blob: f83422f008b718fd586ce9126dc3d2d3751bae71 [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 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
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 */
14
15#include <stdint.h>
16#include <stdlib.h>
17#include <errno.h>
18#include <string.h>
19#include <poll.h>
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010025#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010026#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010027#include <time.h>
Michal Vasko086311b2016-01-08 09:53:11 +010028
Michal Vasko1a38c862016-01-15 15:50:07 +010029#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010030#include "session_server.h"
31
Michal Vaskob48aa812016-01-18 14:13:09 +010032struct nc_server_opts server_opts = {
Michal Vasko2e6defd2016-10-07 15:48:15 +020033 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
34 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010035};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010036
fanchanghu966f2de2016-07-21 02:28:57 -040037static nc_rpc_clb global_rpc_clb = NULL;
38
Michal Vasko3031aae2016-01-27 16:07:18 +010039struct nc_endpt *
Michal Vasko2e6defd2016-10-07 15:48:15 +020040nc_server_endpt_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010041{
42 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010043 struct nc_endpt *endpt = NULL;
44
45 /* READ LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020046 pthread_rwlock_rdlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010047
48 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020049 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010050 endpt = &server_opts.endpts[i];
51 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010052 }
53 }
54
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010055 if (!endpt) {
56 ERR("Endpoint \"%s\" was not found.", name);
57 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020058 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010059 return NULL;
60 }
61
62 /* ENDPT LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020063 pthread_mutex_lock(&endpt->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010064
Michal Vaskoe2713da2016-08-22 16:06:40 +020065 if (idx) {
66 *idx = i;
67 }
68
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010069 return endpt;
70}
71
72void
73nc_server_endpt_unlock(struct nc_endpt *endpt)
74{
75 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020076 pthread_mutex_unlock(&endpt->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077
78 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020079 pthread_rwlock_unlock(&server_opts.endpt_lock);
80}
81
82struct nc_ch_client *
83nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
84{
85 uint16_t i;
86 struct nc_ch_client *client = NULL;
87
88 /* READ LOCK */
89 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
90
91 for (i = 0; i < server_opts.ch_client_count; ++i) {
92 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
93 client = &server_opts.ch_clients[i];
94 break;
95 }
96 }
97
98 if (!client) {
99 ERR("Call Home client \"%s\" was not found.", name);
100 /* READ UNLOCK */
101 pthread_rwlock_unlock(&server_opts.ch_client_lock);
102 return NULL;
103 }
104
105 /* CH CLIENT LOCK */
106 pthread_mutex_lock(&client->lock);
107
108 if (idx) {
109 *idx = i;
110 }
111
112 return client;
113}
114
115void
116nc_server_ch_client_unlock(struct nc_ch_client *client)
117{
118 /* CH CLIENT UNLOCK */
119 pthread_mutex_unlock(&client->lock);
120
121 /* READ UNLOCK */
122 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100123}
Michal Vasko086311b2016-01-08 09:53:11 +0100124
Michal Vasko1a38c862016-01-15 15:50:07 +0100125API void
126nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
127{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200128 if (!session) {
129 ERRARG("session");
130 return;
131 } else if (!reason) {
132 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100133 return;
134 }
135
136 session->term_reason = reason;
137}
138
Michal Vasko086311b2016-01-08 09:53:11 +0100139int
Michal Vaskof05562c2016-01-20 12:06:43 +0100140nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100141{
142 const int optVal = 1;
143 const socklen_t optLen = sizeof(optVal);
144 int is_ipv4, sock;
145 struct sockaddr_storage saddr;
146
147 struct sockaddr_in *saddr4;
148 struct sockaddr_in6 *saddr6;
149
150
151 if (!strchr(address, ':')) {
152 is_ipv4 = 1;
153 } else {
154 is_ipv4 = 0;
155 }
156
157 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
158 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100159 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100160 goto fail;
161 }
162
163 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100164 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100165 goto fail;
166 }
167
168 bzero(&saddr, sizeof(struct sockaddr_storage));
169 if (is_ipv4) {
170 saddr4 = (struct sockaddr_in *)&saddr;
171
172 saddr4->sin_family = AF_INET;
173 saddr4->sin_port = htons(port);
174
175 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100176 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100177 goto fail;
178 }
179
180 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100181 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100182 goto fail;
183 }
184
185 } else {
186 saddr6 = (struct sockaddr_in6 *)&saddr;
187
188 saddr6->sin6_family = AF_INET6;
189 saddr6->sin6_port = htons(port);
190
191 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100192 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100193 goto fail;
194 }
195
196 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100197 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100198 goto fail;
199 }
200 }
201
Michal Vaskofb89d772016-01-08 12:25:35 +0100202 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100203 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100204 goto fail;
205 }
206
207 return sock;
208
209fail:
210 if (sock > -1) {
211 close(sock);
212 }
213
214 return -1;
215}
216
217int
Michal Vasko3031aae2016-01-27 16:07:18 +0100218nc_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 +0100219{
220 uint16_t i;
221 struct pollfd *pfd;
222 struct sockaddr_storage saddr;
223 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100224 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100225
226 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100227 if (!pfd) {
228 ERRMEM;
229 return -1;
230 }
231
Michal Vasko0a3f3752016-10-13 14:58:38 +0200232 for (i = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200233 if (binds[i].sock < 0) {
234 /* invalid socket */
235 --bind_count;
236 continue;
237 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200238 if (binds[i].pollin) {
239 binds[i].pollin = 0;
240 /* leftover pollin */
241 sock = binds[i].sock;
242 break;
243 }
Michal Vasko086311b2016-01-08 09:53:11 +0100244 pfd[i].fd = binds[i].sock;
245 pfd[i].events = POLLIN;
246 pfd[i].revents = 0;
247 }
248
Michal Vasko0a3f3752016-10-13 14:58:38 +0200249 if (sock == -1) {
250 /* poll for a new connection */
251 ret = poll(pfd, bind_count, timeout);
252 if (!ret) {
253 /* we timeouted */
254 free(pfd);
255 return 0;
256 } else if (ret == -1) {
257 ERR("Poll failed (%s).", strerror(errno));
258 free(pfd);
259 return -1;
260 }
Michal Vasko086311b2016-01-08 09:53:11 +0100261
Michal Vasko0a3f3752016-10-13 14:58:38 +0200262 for (i = 0; i < bind_count; ++i) {
263 if (pfd[i].revents & POLLIN) {
264 --ret;
265
266 if (!ret) {
267 /* the last socket with an event, use it */
268 sock = pfd[i].fd;
269 break;
270 } else {
271 /* just remember the event for next time */
272 binds[i].pollin = 1;
273 }
274 }
Michal Vasko086311b2016-01-08 09:53:11 +0100275 }
276 }
277 free(pfd);
278
279 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100280 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100281 return -1;
282 }
283
284 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100285 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100286 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100287 return -1;
288 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200289 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100290
Michal Vasko0190bc32016-03-02 15:47:49 +0100291 /* make the socket non-blocking */
292 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
293 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100294 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100295 return -1;
296 }
297
Michal Vasko3031aae2016-01-27 16:07:18 +0100298 if (idx) {
299 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100300 }
301
Michal Vasko086311b2016-01-08 09:53:11 +0100302 /* host was requested */
303 if (host) {
304 if (saddr.ss_family == AF_INET) {
305 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100306 if (*host) {
307 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
308 ERR("inet_ntop failed (%s).", strerror(errno));
309 free(*host);
310 *host = NULL;
311 }
Michal Vasko086311b2016-01-08 09:53:11 +0100312
Michal Vasko4eb3c312016-03-01 14:09:37 +0100313 if (port) {
314 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
315 }
316 } else {
317 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100318 }
319 } else if (saddr.ss_family == AF_INET6) {
320 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100321 if (*host) {
322 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
323 ERR("inet_ntop failed (%s).", strerror(errno));
324 free(*host);
325 *host = NULL;
326 }
Michal Vasko086311b2016-01-08 09:53:11 +0100327
Michal Vasko4eb3c312016-03-01 14:09:37 +0100328 if (port) {
329 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
330 }
331 } else {
332 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100333 }
334 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100335 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100336 }
337 }
338
339 return ret;
340}
341
Michal Vasko05ba9df2016-01-13 14:40:27 +0100342static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100343nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100344{
345 const char *identifier = NULL, *version = NULL, *format = NULL;
346 char *model_data = NULL;
347 const struct lys_module *module;
348 struct nc_server_error *err;
349 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100350 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100351
352 LY_TREE_FOR(rpc->child, child) {
353 if (!strcmp(child->schema->name, "identifier")) {
354 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
355 } else if (!strcmp(child->schema->name, "version")) {
356 version = ((struct lyd_node_leaf_list *)child)->value_str;
357 } else if (!strcmp(child->schema->name, "format")) {
358 format = ((struct lyd_node_leaf_list *)child)->value_str;
359 }
360 }
361
362 /* check version */
363 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100364 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
365 nc_err_set_msg(err, "The requested version is not supported.", "en");
366 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100367 }
368
369 /* check and get module with the name identifier */
370 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
371 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200372 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
373 }
374 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100375 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
376 nc_err_set_msg(err, "The requested schema was not found.", "en");
377 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100378 }
379
380 /* check format */
Radek Krejci89c34452016-12-07 15:59:45 +0100381 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100382 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
Radek Krejci89c34452016-12-07 15:59:45 +0100383 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
385 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100386 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
387 nc_err_set_msg(err, "The requested format is not supported.", "en");
388 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200390 if (!model_data) {
391 ERRINT;
392 return NULL;
393 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100394
Michal Vasko303245c2016-03-24 15:20:03 +0100395 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200396 if (!sdata) {
397 ERRINT;
398 free(model_data);
399 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100400 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200401
Radek Krejci539efb62016-08-24 15:05:16 +0200402 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
403 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100404 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100405 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200406 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100407 return NULL;
408 }
409
Radek Krejci36dfdb32016-09-01 16:56:35 +0200410 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100411}
412
413static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100414nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100415{
Michal Vasko428087d2016-01-14 16:04:28 +0100416 session->term_reason = NC_SESSION_TERM_CLOSED;
417 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100418}
419
Michal Vasko086311b2016-01-08 09:53:11 +0100420API int
421nc_server_init(struct ly_ctx *ctx)
422{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100423 const struct lys_node *rpc;
424
Michal Vasko086311b2016-01-08 09:53:11 +0100425 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200426 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100427 return -1;
428 }
429
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100430 nc_init();
431
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100433 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100434 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100435 lys_set_private(rpc, nc_clb_default_get_schema);
436 }
437
438 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100439 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100440 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100441 lys_set_private(rpc, nc_clb_default_close_session);
442 }
443
Michal Vasko086311b2016-01-08 09:53:11 +0100444 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100445
446 server_opts.new_session_id = 1;
447 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
448
Michal Vasko086311b2016-01-08 09:53:11 +0100449 return 0;
450}
451
Michal Vaskob48aa812016-01-18 14:13:09 +0100452API void
453nc_server_destroy(void)
454{
Radek Krejci658782b2016-12-04 22:04:55 +0100455 unsigned int i;
456
457 for (i = 0; i < server_opts.capabilities_count; i++) {
458 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
459 }
460 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100461 pthread_spin_destroy(&server_opts.sid_lock);
462
Radek Krejci53691be2016-02-22 13:58:37 +0100463#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100464 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100465#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100466#ifdef NC_ENABLED_SSH
467 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100468
469 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
470 server_opts.hostkey_data_free(server_opts.hostkey_data);
471 }
472#endif
473#ifdef NC_ENABLED_TLS
474 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
475 server_opts.server_cert_data_free(server_opts.server_cert_data);
476 }
477 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
478 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
479 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100480#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100481 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100482}
483
Michal Vasko086311b2016-01-08 09:53:11 +0100484API int
485nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
486{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200487 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
488 ERRARG("basic_mode");
489 return -1;
490 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
491 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100492 return -1;
493 }
494
495 server_opts.wd_basic_mode = basic_mode;
496 server_opts.wd_also_supported = also_supported;
497 return 0;
498}
499
Michal Vasko1a38c862016-01-15 15:50:07 +0100500API void
Michal Vasko55f03972016-04-13 08:56:01 +0200501nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
502{
503 if (!basic_mode && !also_supported) {
504 ERRARG("basic_mode and also_supported");
505 return;
506 }
507
508 if (basic_mode) {
509 *basic_mode = server_opts.wd_basic_mode;
510 }
511 if (also_supported) {
512 *also_supported = server_opts.wd_also_supported;
513 }
514}
515
Michal Vasko55f03972016-04-13 08:56:01 +0200516API int
Radek Krejci658782b2016-12-04 22:04:55 +0100517nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200518{
Radek Krejci658782b2016-12-04 22:04:55 +0100519 const char **new;
520
521 if (!value || !value[0]) {
522 ERRARG("value must not be empty");
523 return EXIT_FAILURE;
524 }
525
526 server_opts.capabilities_count++;
527 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
528 if (!new) {
529 ERRMEM;
530 return EXIT_FAILURE;
531 }
532 server_opts.capabilities = new;
533 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
534
535 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200536}
537
Michal Vasko1a38c862016-01-15 15:50:07 +0100538API void
Michal Vasko086311b2016-01-08 09:53:11 +0100539nc_server_set_hello_timeout(uint16_t hello_timeout)
540{
Michal Vasko086311b2016-01-08 09:53:11 +0100541 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100542}
543
Michal Vasko55f03972016-04-13 08:56:01 +0200544API uint16_t
545nc_server_get_hello_timeout(void)
546{
547 return server_opts.hello_timeout;
548}
549
Michal Vasko1a38c862016-01-15 15:50:07 +0100550API void
Michal Vasko086311b2016-01-08 09:53:11 +0100551nc_server_set_idle_timeout(uint16_t idle_timeout)
552{
Michal Vasko086311b2016-01-08 09:53:11 +0100553 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100554}
555
Michal Vasko55f03972016-04-13 08:56:01 +0200556API uint16_t
557nc_server_get_idle_timeout(void)
558{
559 return server_opts.idle_timeout;
560}
561
Michal Vasko71090fc2016-05-24 16:37:28 +0200562API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100563nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100564{
Michal Vasko71090fc2016-05-24 16:37:28 +0200565 NC_MSG_TYPE msgtype;
566
Michal Vasko45e53ae2016-04-07 11:46:03 +0200567 if (!server_opts.ctx) {
568 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200569 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200570 } else if (fdin < 0) {
571 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200572 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200573 } else if (fdout < 0) {
574 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200575 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200576 } else if (!username) {
577 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200578 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200579 } else if (!session) {
580 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200581 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100582 }
583
584 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100585 *session = calloc(1, sizeof **session);
586 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100587 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200588 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100589 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100590 (*session)->status = NC_STATUS_STARTING;
591 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100592
593 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100594 (*session)->ti_type = NC_TI_FD;
595 (*session)->ti.fd.in = fdin;
596 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100597
598 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100599 (*session)->flags = NC_SESSION_SHAREDCTX;
600 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100601
Michal Vaskob48aa812016-01-18 14:13:09 +0100602 /* assign new SID atomically */
603 pthread_spin_lock(&server_opts.sid_lock);
604 (*session)->id = server_opts.new_session_id++;
605 pthread_spin_unlock(&server_opts.sid_lock);
606
Michal Vasko086311b2016-01-08 09:53:11 +0100607 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200608 msgtype = nc_handshake(*session);
609 if (msgtype != NC_MSG_HELLO) {
610 nc_session_free(*session, NULL);
611 *session = NULL;
612 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100613 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200614 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100615 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100616
Michal Vasko71090fc2016-05-24 16:37:28 +0200617 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100618}
Michal Vasko9e036d52016-01-08 10:49:26 +0100619
Michal Vaskob30b99c2016-07-26 11:35:43 +0200620static void
621nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
622{
623 uint8_t i, found = 0;
624
625 for (i = 0; i < ps->queue_len; ++i) {
626 /* idx round buffer adjust */
627 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
628 i = -ps->queue_begin;
629 }
630
631 if (found) {
632 /* move the value back one place */
633 if (ps->queue[ps->queue_begin + i] == id) {
634 /* another equal value, simply cannot be */
635 ERRINT;
636 }
637
638 if (ps->queue_begin + i == 0) {
639 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
640 } else {
641 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
642 }
643 } else if (ps->queue[ps->queue_begin + i] == id) {
644 /* found our id, there can be no more equal valid values */
645 found = 1;
646 }
647 }
648
649 if (!found) {
650 ERRINT;
651 }
652 --ps->queue_len;
653}
654
Michal Vaskof04a52a2016-04-07 10:52:10 +0200655int
Michal Vasko26043172016-07-26 14:08:59 +0200656nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200657{
658 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200659 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200660 struct timespec ts;
661
Radek Krejci7ac16052016-07-15 11:48:18 +0200662 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200663 ts.tv_sec += NC_READ_TIMEOUT;
664
665 /* LOCK */
666 ret = pthread_mutex_timedlock(&ps->lock, &ts);
667 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200668 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200669 return -1;
670 }
671
672 /* get a unique queue value (by adding 1 to the last added value, if any) */
673 if (ps->queue_len) {
674 queue_last = ps->queue_begin + ps->queue_len - 1;
675 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
676 queue_last -= NC_PS_QUEUE_SIZE;
677 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200678 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200679 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200680 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200681 }
682
683 /* add ourselves into the queue */
684 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200685 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200686 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200687 return -1;
688 }
689 ++ps->queue_len;
690 queue_last = ps->queue_begin + ps->queue_len - 1;
691 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
692 queue_last -= NC_PS_QUEUE_SIZE;
693 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200694 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200695
696 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200697 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200698 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200699 ts.tv_sec += NC_READ_TIMEOUT;
700
701 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
702 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200703 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200704 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200705 nc_ps_queue_remove_id(ps, *id);
706 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200707 return -1;
708 }
709 }
710
Michal Vaskobe86fe32016-04-07 10:43:03 +0200711 /* UNLOCK */
712 pthread_mutex_unlock(&ps->lock);
713
714 return 0;
715}
716
Michal Vaskof04a52a2016-04-07 10:52:10 +0200717int
Michal Vasko26043172016-07-26 14:08:59 +0200718nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200719{
720 int ret;
721 struct timespec ts;
722
Radek Krejci7ac16052016-07-15 11:48:18 +0200723 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200724 ts.tv_sec += NC_READ_TIMEOUT;
725
726 /* LOCK */
727 ret = pthread_mutex_timedlock(&ps->lock, &ts);
728 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200729 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200730 ret = -1;
731 }
732
Michal Vaskob30b99c2016-07-26 11:35:43 +0200733 /* we must be the first, it was our turn after all, right? */
734 if (ps->queue[ps->queue_begin] != id) {
735 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200736 /* UNLOCK */
737 if (!ret) {
738 pthread_mutex_unlock(&ps->lock);
739 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200740 return -1;
741 }
742
Michal Vaskobe86fe32016-04-07 10:43:03 +0200743 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200744 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200745
746 /* broadcast to all other threads that the queue moved */
747 pthread_cond_broadcast(&ps->cond);
748
Michal Vaskobe86fe32016-04-07 10:43:03 +0200749 /* UNLOCK */
750 if (!ret) {
751 pthread_mutex_unlock(&ps->lock);
752 }
753
754 return ret;
755}
756
Michal Vasko428087d2016-01-14 16:04:28 +0100757API struct nc_pollsession *
758nc_ps_new(void)
759{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100760 struct nc_pollsession *ps;
761
762 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100763 if (!ps) {
764 ERRMEM;
765 return NULL;
766 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200767 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100768 pthread_mutex_init(&ps->lock, NULL);
769
770 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100771}
772
773API void
774nc_ps_free(struct nc_pollsession *ps)
775{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100776 if (!ps) {
777 return;
778 }
779
Michal Vaskobe86fe32016-04-07 10:43:03 +0200780 if (ps->queue_len) {
781 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
782 }
783
Michal Vasko428087d2016-01-14 16:04:28 +0100784 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100785 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200786 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100787
Michal Vasko428087d2016-01-14 16:04:28 +0100788 free(ps);
789}
790
791API int
792nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
793{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200794 uint8_t q_id;
795
Michal Vasko45e53ae2016-04-07 11:46:03 +0200796 if (!ps) {
797 ERRARG("ps");
798 return -1;
799 } else if (!session) {
800 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100801 return -1;
802 }
803
Michal Vasko48a63ed2016-03-01 09:48:21 +0100804 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200805 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200806 return -1;
807 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100808
Michal Vasko428087d2016-01-14 16:04:28 +0100809 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100810 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko99f251b2017-01-11 11:31:46 +0100811 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100812 ERRMEM;
813 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200814 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100815 return -1;
816 }
Michal Vasko3a715132016-01-21 15:40:31 +0100817 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100818
Michal Vasko48a63ed2016-03-01 09:48:21 +0100819 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200820 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100821}
822
Michal Vasko48a63ed2016-03-01 09:48:21 +0100823static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100824_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100825{
826 uint16_t i;
827
Radek Krejcid5f978f2016-03-03 13:14:45 +0100828 if (index >= 0) {
829 i = (uint16_t)index;
830 goto remove;
831 }
Michal Vasko428087d2016-01-14 16:04:28 +0100832 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100833 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100834remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100835 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100836 if (i < ps->session_count) {
837 ps->sessions[i] = ps->sessions[ps->session_count];
Michal Vasko99f251b2017-01-11 11:31:46 +0100838 if (ps->last_event_session == i) {
839 ps->last_event_session = 0;
840 }
Michal Vasko58005732016-02-02 15:50:52 +0100841 } else if (!ps->session_count) {
842 free(ps->sessions);
843 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100844 }
Michal Vasko428087d2016-01-14 16:04:28 +0100845 return 0;
846 }
847 }
848
Michal Vaskof0537d82016-01-29 14:42:38 +0100849 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100850}
851
Michal Vasko48a63ed2016-03-01 09:48:21 +0100852API int
853nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
854{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200855 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200856 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100857
Michal Vasko45e53ae2016-04-07 11:46:03 +0200858 if (!ps) {
859 ERRARG("ps");
860 return -1;
861 } else if (!session) {
862 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100863 return -1;
864 }
865
866 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200867 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200868 return -1;
869 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100870
Radek Krejcid5f978f2016-03-03 13:14:45 +0100871 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100872
873 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200874 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100875
Michal Vaskobe86fe32016-04-07 10:43:03 +0200876 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100877}
878
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100879API uint16_t
880nc_ps_session_count(struct nc_pollsession *ps)
881{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200882 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100883 uint16_t count;
884
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100885 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200886 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100887 return 0;
888 }
889
Michal Vasko48a63ed2016-03-01 09:48:21 +0100890 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200891 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200892 return -1;
893 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100894
895 count = ps->session_count;
896
897 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200898 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100899
900 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100901}
902
Michal Vasko71090fc2016-05-24 16:37:28 +0200903/* must be called holding the session lock!
904 * returns: NC_PSPOLL_ERROR,
905 * NC_PSPOLL_BAD_RPC,
906 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
907 * NC_PSPOLL_RPC
908 */
909static int
Radek Krejci93e80222016-10-03 13:34:25 +0200910nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100911{
912 struct lyxml_elem *xml = NULL;
913 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200914 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200915 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100916
Michal Vasko45e53ae2016-04-07 11:46:03 +0200917 if (!session) {
918 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200919 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200920 } else if (!rpc) {
921 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200922 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100923 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100924 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200925 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100926 }
927
928 msgtype = nc_read_msg(session, &xml);
929
930 switch (msgtype) {
931 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200932 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100933 if (!*rpc) {
934 ERRMEM;
935 goto error;
936 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100937
Radek Krejcif93c7d42016-04-06 13:41:15 +0200938 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +0100939 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
940 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100941 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200942 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200943 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200944 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200945 nc_server_reply_free(reply);
946 if (ret == -1) {
947 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200948 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200949 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
950 } else {
951 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100952 }
Michal Vasko428087d2016-01-14 16:04:28 +0100953 (*rpc)->root = xml;
954 break;
955 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100956 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200957 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100958 goto error;
959 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100960 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200961 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100962 goto error;
963 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100964 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200965 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100966 goto error;
967 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200968 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100969 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
970 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200971 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100972 break;
973 }
974
Michal Vasko71090fc2016-05-24 16:37:28 +0200975 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100976
977error:
978 /* cleanup */
979 lyxml_free(server_opts.ctx, xml);
980
Michal Vasko71090fc2016-05-24 16:37:28 +0200981 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100982}
983
fanchanghu966f2de2016-07-21 02:28:57 -0400984API void
985nc_set_global_rpc_clb(nc_rpc_clb clb)
986{
987 global_rpc_clb = clb;
988}
989
Radek Krejci93e80222016-10-03 13:34:25 +0200990API NC_MSG_TYPE
991nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
992{
993 NC_MSG_TYPE result = NC_MSG_NOTIF;
994 int ret;
995
996 /* check parameters */
997 if (!session) {
998 ERRARG("session");
999 return NC_MSG_ERROR;
1000 } else if (!notif || !notif->tree || !notif->eventtime) {
1001 ERRARG("notif");
1002 return NC_MSG_ERROR;
1003 }
1004
1005 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
1006 ret = nc_timedlock(session->ti_lock, timeout, __func__);
1007 if (ret < 0) {
1008 return NC_MSG_ERROR;
1009 } else if (!ret) {
1010 return NC_MSG_WOULDBLOCK;
1011 }
1012
1013 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1014 if (ret == -1) {
1015 ERR("Session %u: failed to write notification.", session->id);
1016 result = NC_MSG_ERROR;
1017 }
1018 pthread_mutex_unlock(session->ti_lock);
1019
1020 return result;
1021}
1022
Michal Vasko71090fc2016-05-24 16:37:28 +02001023/* must be called holding the session lock!
1024 * returns: NC_PSPOLL_ERROR,
1025 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1026 * NC_PSPOLL_REPLY_ERROR,
1027 * 0
1028 */
1029static int
Radek Krejci93e80222016-10-03 13:34:25 +02001030nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001031{
1032 nc_rpc_clb clb;
1033 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001034 struct lys_node *rpc_act = NULL;
1035 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001036 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001037
Michal Vasko4a827e52016-03-03 10:59:00 +01001038 if (!rpc) {
1039 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001040 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001041 }
1042
Michal Vasko90e8e692016-07-13 12:27:57 +02001043 if (rpc->tree->schema->nodetype == LYS_RPC) {
1044 /* RPC */
1045 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001046 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001047 /* action */
1048 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1049 if (elem->schema->nodetype == LYS_ACTION) {
1050 rpc_act = elem->schema;
1051 break;
1052 }
1053 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001054 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001055 if (!rpc_act) {
1056 ERRINT;
1057 return NC_PSPOLL_ERROR;
1058 }
1059 }
1060
1061 if (!rpc_act->priv) {
1062 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001063 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001064 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001065 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001066 reply = clb(rpc->tree, session);
1067 }
1068
1069 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001070 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001071 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001072 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1073 if (reply->type == NC_RPL_ERROR) {
1074 ret |= NC_PSPOLL_REPLY_ERROR;
1075 }
1076 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001077
Michal Vasko71090fc2016-05-24 16:37:28 +02001078 if (r == -1) {
1079 ERR("Session %u: failed to write reply.", session->id);
1080 ret |= NC_PSPOLL_ERROR;
1081 }
Michal Vasko428087d2016-01-14 16:04:28 +01001082
1083 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1084 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1085 session->status = NC_STATUS_INVALID;
1086 }
1087
Michal Vasko71090fc2016-05-24 16:37:28 +02001088 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001089}
1090
1091API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001092nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001093{
Michal Vasko99f251b2017-01-11 11:31:46 +01001094 int ret, r, poll_ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001095 uint8_t q_id;
Michal Vasko99f251b2017-01-11 11:31:46 +01001096 uint16_t i, j;
1097 char msg[256];
1098 NC_SESSION_TERM_REASON term_reason;
1099 struct pollfd pfd;
1100 struct timespec begin_ts, cur_ts;
Michal Vasko71090fc2016-05-24 16:37:28 +02001101 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001102 struct nc_server_rpc *rpc = NULL;
Michal Vasko99f251b2017-01-11 11:31:46 +01001103#ifdef NC_ENABLED_SSH
1104 struct nc_session *new;
1105#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001106
1107 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001108 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001109 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001110 }
1111
Michal Vasko99f251b2017-01-11 11:31:46 +01001112 nc_gettimespec(&begin_ts);
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001113
Michal Vasko48a63ed2016-03-01 09:48:21 +01001114 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001115 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001116 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001117 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001118
Michal Vasko99f251b2017-01-11 11:31:46 +01001119 /* check that all session are fine */
Michal Vasko428087d2016-01-14 16:04:28 +01001120 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001121 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1122 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001123 ret = NC_PSPOLL_ERROR;
1124 if (session) {
1125 *session = ps->sessions[i];
1126 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001127 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001128 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001129
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001130 /* TODO invalidate only sessions without subscription */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001131 if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME) && server_opts.idle_timeout
Michal Vasko99f251b2017-01-11 11:31:46 +01001132 && (begin_ts.tv_sec >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001133 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1134 ps->sessions[i]->status = NC_STATUS_INVALID;
1135 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001136 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1137 if (session) {
1138 *session = ps->sessions[i];
1139 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001140 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001141 }
Michal Vasko428087d2016-01-14 16:04:28 +01001142 }
1143
Michal Vasko99f251b2017-01-11 11:31:46 +01001144 /* poll on all the sessions one-by-one */
1145 do {
1146 /* loop from i to j */
1147 if (ps->last_event_session == ps->session_count - 1) {
1148 i = j = 0;
1149 } else {
1150 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001151 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001152 do {
1153 cur_session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +01001154
Michal Vasko99f251b2017-01-11 11:31:46 +01001155 switch (cur_session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +01001156#ifdef NC_ENABLED_SSH
Michal Vasko99f251b2017-01-11 11:31:46 +01001157 case NC_TI_LIBSSH:
1158 r = ssh_channel_poll_timeout(cur_session->ti.libssh.channel, 0, 0);
1159 if (r < 1) {
1160 if (r == SSH_EOF) {
1161 sprintf(msg, "SSH channel unexpectedly closed");
1162 term_reason = NC_SESSION_TERM_DROPPED;
1163 poll_ret = -2;
1164 } else if (r == SSH_ERROR) {
1165 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(cur_session->ti.libssh.session));
Michal Vasko76be6752017-01-19 12:14:48 +01001166 term_reason = NC_SESSION_TERM_OTHER;
1167 poll_ret = -2;
Michal Vasko99f251b2017-01-11 11:31:46 +01001168 } else {
1169 poll_ret = 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001170 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001171 break;
Michal Vasko96164bf2016-01-21 15:41:58 +01001172 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001173 /* we have some data, but it may be just an SSH message */
Michal Vasko96164bf2016-01-21 15:41:58 +01001174
Michal Vasko99f251b2017-01-11 11:31:46 +01001175 r = nc_timedlock(cur_session->ti_lock, timeout, __func__);
1176 if (r < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001177 if (session) {
Michal Vasko99f251b2017-01-11 11:31:46 +01001178 *session = cur_session;
Michal Vasko71090fc2016-05-24 16:37:28 +02001179 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001180 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001181 goto finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001182 } else if (!r) {
1183 if (session) {
1184 *session = cur_session;
Michal Vasko428087d2016-01-14 16:04:28 +01001185 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001186 ret = NC_PSPOLL_TIMEOUT;
1187 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001188 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001189 r = ssh_execute_message_callbacks(cur_session->ti.libssh.session);
1190 pthread_mutex_unlock(cur_session->ti_lock);
1191
1192 if (r != SSH_OK) {
1193 sprintf(msg, "failed to receive SSH messages (%s)", ssh_get_error(cur_session->ti.libssh.session));
1194 term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko76be6752017-01-19 12:14:48 +01001195 poll_ret = -2;
Michal Vasko99f251b2017-01-11 11:31:46 +01001196 } else if (cur_session->flags & NC_SESSION_SSH_NEW_MSG) {
1197 /* new SSH message */
1198 cur_session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1199 if (cur_session->ti.libssh.next) {
1200 for (new = cur_session->ti.libssh.next; new != cur_session; new = new->ti.libssh.next) {
1201 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1202 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1203 /* new NETCONF SSH channel */
1204 if (session) {
1205 *session = cur_session;
1206 }
1207 ret = NC_PSPOLL_SSH_CHANNEL;
1208 goto finish;
1209 }
1210 }
1211 }
1212
1213 /* just some SSH message */
1214 if (session) {
1215 *session = cur_session;
1216 }
1217 ret = NC_PSPOLL_SSH_MSG;
1218 goto finish;
1219 } else {
1220 /* we have some application data */
1221 poll_ret = 1;
1222 }
1223 break;
1224#endif
1225#ifdef NC_ENABLED_TLS
1226 case NC_TI_OPENSSL:
1227 r = SSL_pending(cur_session->ti.tls);
1228 if (!r) {
1229 /* no data pending in the SSL buffer, poll fd */
1230 pfd.fd = SSL_get_rfd(cur_session->ti.tls);
1231 if (pfd.fd < 0) {
1232 ERRINT;
1233 ret = NC_PSPOLL_ERROR;
1234 goto finish;
1235 }
1236 pfd.events = POLLIN;
1237 pfd.revents = 0;
1238 r = poll(&pfd, 1, 0);
1239
1240 if (r < 0) {
1241 sprintf(msg, "poll failed (%s)", strerror(errno));
1242 poll_ret = -1;
1243 } else if (r > 0) {
1244 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1245 sprintf(msg, "communication socket unexpectedly closed");
1246 term_reason = NC_SESSION_TERM_DROPPED;
1247 poll_ret = -2;
1248 } else if (pfd.revents & POLLERR) {
1249 sprintf(msg, "communication socket error");
1250 term_reason = NC_SESSION_TERM_OTHER;
1251 poll_ret = -2;
1252 } else {
1253 poll_ret = 1;
1254 }
1255 } else {
1256 poll_ret = 0;
1257 }
1258 } else {
1259 poll_ret = 1;
1260 }
1261 break;
1262#endif
1263 case NC_TI_FD:
1264 pfd.fd = cur_session->ti.fd.in;
1265 pfd.events = POLLIN;
1266 pfd.revents = 0;
1267 r = poll(&pfd, 1, 0);
1268
1269 if (r < 0) {
1270 sprintf(msg, "poll failed (%s)", strerror(errno));
Michal Vasko76be6752017-01-19 12:14:48 +01001271 poll_ret = -1;
Michal Vasko99f251b2017-01-11 11:31:46 +01001272 } else if (r > 0) {
1273 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1274 sprintf(msg, "communication socket unexpectedly closed");
1275 term_reason = NC_SESSION_TERM_DROPPED;
1276 poll_ret = -2;
1277 } else if (pfd.revents & POLLERR) {
1278 sprintf(msg, "communication socket error");
1279 term_reason = NC_SESSION_TERM_OTHER;
1280 poll_ret = -2;
1281 } else {
1282 poll_ret = 1;
1283 }
1284 } else {
1285 poll_ret = 0;
1286 }
1287 break;
1288 case NC_TI_NONE:
1289 ERRINT;
1290 ret = NC_PSPOLL_ERROR;
1291 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001292 }
Michal Vasko428087d2016-01-14 16:04:28 +01001293
Michal Vasko99f251b2017-01-11 11:31:46 +01001294 /* here: poll_ret == -2 - session error, session terminated,
Michal Vasko76be6752017-01-19 12:14:48 +01001295 * poll_ret == -1 - generic error,
1296 * poll_ret == 0 - nothing to read,
1297 * poll_ret > 0 - data available */
Michal Vasko99f251b2017-01-11 11:31:46 +01001298 if (poll_ret == -2) {
1299 ERR("Session %u: %s.", cur_session->id, msg);
1300 cur_session->status = NC_STATUS_INVALID;
1301 cur_session->term_reason = term_reason;
1302 if (session) {
1303 *session = cur_session;
1304 }
1305 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1306 goto finish;
1307 } else if (poll_ret == -1) {
1308 ERR("Session %u: %s.", cur_session->id, msg);
1309 ret = NC_PSPOLL_ERROR;
1310 goto finish;
1311 } else if (poll_ret > 0) {
1312 break;
1313 }
1314
1315 /* next iteration */
1316 if (i == ps->session_count - 1) {
1317 i = 0;
1318 } else {
1319 ++i;
1320 }
1321 } while (i != j);
1322
1323 /* no event */
1324 if (!poll_ret && (timeout > -1)) {
1325 usleep(NC_TIMEOUT_STEP);
1326
1327 nc_gettimespec(&cur_ts);
1328 /* final timeout */
1329 if (nc_difftimespec(&begin_ts, &cur_ts) >= (unsigned)timeout) {
1330 ret = NC_PSPOLL_TIMEOUT;
1331 goto finish;
1332 }
Michal Vasko428087d2016-01-14 16:04:28 +01001333 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001334 } while (!poll_ret);
Michal Vasko428087d2016-01-14 16:04:28 +01001335
1336 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001337 if (session) {
1338 *session = cur_session;
1339 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001340 ps->last_event_session = i;
Michal Vasko428087d2016-01-14 16:04:28 +01001341
Michal Vaskobd8ef262016-01-20 11:09:27 +01001342 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko99f251b2017-01-11 11:31:46 +01001343 r = nc_timedlock(cur_session->ti_lock, timeout, __func__);
1344 if (r < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001345 ret = NC_PSPOLL_ERROR;
1346 goto finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001347 } else if (!r) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001348 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001349 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001350 }
1351
Radek Krejci93e80222016-10-03 13:34:25 +02001352 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001353 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1354 pthread_mutex_unlock(cur_session->ti_lock);
1355 if (cur_session->status != NC_STATUS_RUNNING) {
1356 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001357 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001358 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001359 }
1360
Michal Vasko2e6defd2016-10-07 15:48:15 +02001361 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001362
Michal Vasko428087d2016-01-14 16:04:28 +01001363 /* process RPC */
Radek Krejci93e80222016-10-03 13:34:25 +02001364 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001365
Michal Vasko71090fc2016-05-24 16:37:28 +02001366 pthread_mutex_unlock(cur_session->ti_lock);
1367 if (cur_session->status != NC_STATUS_RUNNING) {
1368 ret |= NC_PSPOLL_SESSION_TERM;
1369 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1370 ret |= NC_PSPOLL_SESSION_ERROR;
1371 }
Michal Vasko428087d2016-01-14 16:04:28 +01001372 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001373
Michal Vaskoca4a2422016-02-02 12:17:14 +01001374 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001375
Michal Vasko48a63ed2016-03-01 09:48:21 +01001376finish:
1377 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001378 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001379 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001380}
1381
Michal Vaskod09eae62016-02-01 10:32:52 +01001382API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001383nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001384{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001385 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001386 uint16_t i;
1387 struct nc_session *session;
1388
Michal Vasko9a25e932016-02-01 10:36:42 +01001389 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001390 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001391 return;
1392 }
1393
Michal Vasko48a63ed2016-03-01 09:48:21 +01001394 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001395 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001396 return;
1397 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001398
Michal Vasko48a63ed2016-03-01 09:48:21 +01001399 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001400 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001401 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001402 }
1403 free(ps->sessions);
1404 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001405 ps->session_count = 0;
Michal Vasko99f251b2017-01-11 11:31:46 +01001406 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001407 } else {
1408 for (i = 0; i < ps->session_count; ) {
1409 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1410 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001411 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001412 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001413 continue;
1414 }
1415
1416 ++i;
1417 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001418 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001419
1420 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001421 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001422}
1423
Radek Krejci53691be2016-02-22 13:58:37 +01001424#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001425
Michal Vaskoe2713da2016-08-22 16:06:40 +02001426API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001427nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001428{
Michal Vasko3031aae2016-01-27 16:07:18 +01001429 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +01001430
Michal Vasko45e53ae2016-04-07 11:46:03 +02001431 if (!name) {
1432 ERRARG("name");
1433 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001434 }
1435
Michal Vasko51e514d2016-02-02 15:51:52 +01001436 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001437 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001438
1439 /* check name uniqueness */
1440 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001441 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001442 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001443 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001444 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001445 return -1;
1446 }
1447 }
1448
Michal Vasko3031aae2016-01-27 16:07:18 +01001449 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001450 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001451 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001452 ERRMEM;
1453 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001454 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001455 return -1;
1456 }
1457 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001458 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001459
Michal Vaskoe2713da2016-08-22 16:06:40 +02001460 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001461 if (!server_opts.binds) {
1462 ERRMEM;
1463 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001464 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001465 return -1;
1466 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001467
Michal Vasko2e6defd2016-10-07 15:48:15 +02001468 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1469 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1470 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001471 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001472
1473 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001474#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001475 case NC_TI_LIBSSH:
1476 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1477 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1478 ERRMEM;
1479 /* WRITE UNLOCK */
1480 pthread_rwlock_unlock(&server_opts.endpt_lock);
1481 return -1;
1482 }
1483 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1484 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1485 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1486 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1487 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001488#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001489#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001490 case NC_TI_OPENSSL:
1491 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1492 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1493 ERRMEM;
1494 /* WRITE UNLOCK */
1495 pthread_rwlock_unlock(&server_opts.endpt_lock);
1496 return -1;
1497 }
1498 break;
1499#endif
1500 default:
1501 ERRINT;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001502 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001503 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001504 return -1;
1505 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001506
Michal Vasko2e6defd2016-10-07 15:48:15 +02001507 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001508
Michal Vasko3031aae2016-01-27 16:07:18 +01001509 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001510 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001511
Michal Vasko9e036d52016-01-08 10:49:26 +01001512 return 0;
1513}
1514
Michal Vasko3031aae2016-01-27 16:07:18 +01001515int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001516nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001517{
1518 struct nc_endpt *endpt;
1519 struct nc_bind *bind = NULL;
1520 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001521 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001522
Michal Vasko45e53ae2016-04-07 11:46:03 +02001523 if (!endpt_name) {
1524 ERRARG("endpt_name");
1525 return -1;
1526 } else if ((!address && !port) || (address && port)) {
1527 ERRARG("address and port");
1528 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001529 }
1530
Michal Vaskoe2713da2016-08-22 16:06:40 +02001531 if (address) {
1532 set_addr = 1;
1533 } else {
1534 set_addr = 0;
1535 }
1536
Michal Vasko51e514d2016-02-02 15:51:52 +01001537 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001538 endpt = nc_server_endpt_lock(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001539 if (!endpt) {
1540 return -1;
1541 }
1542
Michal Vaskoe2713da2016-08-22 16:06:40 +02001543 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001544
Michal Vaskoe2713da2016-08-22 16:06:40 +02001545 if (set_addr) {
1546 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001547 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001548 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001549 }
1550
Michal Vaskoe2713da2016-08-22 16:06:40 +02001551 /* we have all the information we need to create a listening socket */
1552 if (address && port) {
1553 /* create new socket, close the old one */
1554 sock = nc_sock_listen(address, port);
1555 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001556 ret = -1;
1557 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001558 }
1559
1560 if (bind->sock > -1) {
1561 close(bind->sock);
1562 }
1563 bind->sock = sock;
1564 } /* else we are just setting address or port */
1565
1566 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001567 lydict_remove(server_opts.ctx, bind->address);
1568 bind->address = lydict_insert(server_opts.ctx, address, 0);
1569 } else {
1570 bind->port = port;
1571 }
1572
Michal Vaskoe2713da2016-08-22 16:06:40 +02001573 if (sock > -1) {
1574#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001575 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001576#elif defined(NC_ENABLED_SSH)
1577 VRB("Listening on %s:%u for SSH connections.", address, port);
1578#else
1579 VRB("Listening on %s:%u for TLS connections.", address, port);
1580#endif
1581 }
1582
Michal Vasko4c1fb492017-01-30 14:31:07 +01001583cleanup:
Michal Vasko51e514d2016-02-02 15:51:52 +01001584 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001585 nc_server_endpt_unlock(endpt);
Michal Vasko51e514d2016-02-02 15:51:52 +01001586
Michal Vasko4c1fb492017-01-30 14:31:07 +01001587 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001588}
1589
Michal Vaskoe2713da2016-08-22 16:06:40 +02001590API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001591nc_server_endpt_set_address(const char *endpt_name, const char *address)
1592{
1593 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1594}
1595
1596API int
1597nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1598{
1599 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1600}
1601
1602API int
Michal Vasko59050372016-11-22 14:33:55 +01001603nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001604{
1605 uint32_t i;
1606 int ret = -1;
1607
Michal Vasko3031aae2016-01-27 16:07:18 +01001608 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001609 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001610
Michal Vasko59050372016-11-22 14:33:55 +01001611 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001612 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001613 for (i = 0; i < server_opts.endpt_count; ++i) {
1614 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001615 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1616 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001617#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001618 case NC_TI_LIBSSH:
1619 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1620 free(server_opts.endpts[i].opts.ssh);
1621 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001622#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001623#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001624 case NC_TI_OPENSSL:
1625 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1626 free(server_opts.endpts[i].opts.tls);
1627 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001628#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001629 default:
1630 ERRINT;
1631 /* won't get here ...*/
1632 break;
1633 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001634 ret = 0;
1635 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001636 free(server_opts.endpts);
1637 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001638
1639 /* remove all binds */
1640 for (i = 0; i < server_opts.endpt_count; ++i) {
1641 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1642 if (server_opts.binds[i].sock > -1) {
1643 close(server_opts.binds[i].sock);
1644 }
1645 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001646 free(server_opts.binds);
1647 server_opts.binds = NULL;
1648
Michal Vasko3031aae2016-01-27 16:07:18 +01001649 server_opts.endpt_count = 0;
1650
Michal Vasko1a38c862016-01-15 15:50:07 +01001651 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001652 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001653 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001654 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001655 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001656 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001657 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1658 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001659#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001660 case NC_TI_LIBSSH:
1661 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1662 free(server_opts.endpts[i].opts.ssh);
1663 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001664#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001665#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001666 case NC_TI_OPENSSL:
1667 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1668 free(server_opts.endpts[i].opts.tls);
1669 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001670#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001671 default:
1672 ERRINT;
1673 break;
1674 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001675
Michal Vaskoe2713da2016-08-22 16:06:40 +02001676 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001677 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1678 if (server_opts.binds[i].sock > -1) {
1679 close(server_opts.binds[i].sock);
1680 }
1681
1682 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001683 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001684 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001685 free(server_opts.binds);
1686 server_opts.binds = NULL;
1687 free(server_opts.endpts);
1688 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001689 } else if (i < server_opts.endpt_count) {
1690 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1691 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001692 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001693
1694 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001695 if (name) {
1696 break;
1697 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001698 }
1699 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001700 }
1701
Michal Vasko3031aae2016-01-27 16:07:18 +01001702 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001703 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001704
Michal Vasko9e036d52016-01-08 10:49:26 +01001705 return ret;
1706}
1707
Michal Vasko71090fc2016-05-24 16:37:28 +02001708API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001709nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001710{
Michal Vasko71090fc2016-05-24 16:37:28 +02001711 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001712 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001713 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001714 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001715
Michal Vasko45e53ae2016-04-07 11:46:03 +02001716 if (!server_opts.ctx) {
1717 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001718 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001719 } else if (!session) {
1720 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001721 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001722 }
1723
Michal Vasko51e514d2016-02-02 15:51:52 +01001724 /* we have to hold WRITE for the whole time, since there is not
1725 * a way of downgrading the lock to READ */
1726 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001727 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001728
1729 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001730 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001731 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001732 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001733 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001734 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001735
Michal Vaskoe2713da2016-08-22 16:06:40 +02001736 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001737 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001738 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001739 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001740 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001741 if (!ret) {
1742 return NC_MSG_WOULDBLOCK;
1743 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001744 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001745 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001746 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001747
Michal Vasko1a38c862016-01-15 15:50:07 +01001748 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001749 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001750 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001751 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001752 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001753 msgtype = NC_MSG_ERROR;
1754 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001755 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001756 (*session)->status = NC_STATUS_STARTING;
1757 (*session)->side = NC_SERVER;
1758 (*session)->ctx = server_opts.ctx;
1759 (*session)->flags = NC_SESSION_SHAREDCTX;
1760 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1761 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001762
1763 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001764 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1765 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001766 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001767 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001768 msgtype = NC_MSG_ERROR;
1769 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001770 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001771 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001772
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001773 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001774#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001775 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1776 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01001777 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001778 if (ret < 0) {
1779 msgtype = NC_MSG_ERROR;
1780 goto cleanup;
1781 } else if (!ret) {
1782 msgtype = NC_MSG_WOULDBLOCK;
1783 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001784 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001785 } else
1786#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001787#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001788 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1789 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01001790 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001791 if (ret < 0) {
1792 msgtype = NC_MSG_ERROR;
1793 goto cleanup;
1794 } else if (!ret) {
1795 msgtype = NC_MSG_WOULDBLOCK;
1796 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001797 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001798 } else
1799#endif
1800 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001801 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001802 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001803 msgtype = NC_MSG_ERROR;
1804 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001805 }
1806
Michal Vasko2cc4c682016-03-01 09:16:48 +01001807 (*session)->data = NULL;
1808
Michal Vasko51e514d2016-02-02 15:51:52 +01001809 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001810 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001811
Michal Vaskob48aa812016-01-18 14:13:09 +01001812 /* assign new SID atomically */
1813 /* LOCK */
1814 pthread_spin_lock(&server_opts.sid_lock);
1815 (*session)->id = server_opts.new_session_id++;
1816 /* UNLOCK */
1817 pthread_spin_unlock(&server_opts.sid_lock);
1818
Michal Vasko9e036d52016-01-08 10:49:26 +01001819 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001820 msgtype = nc_handshake(*session);
1821 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001822 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001823 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001824 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001825 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001826 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001827 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001828
Michal Vasko71090fc2016-05-24 16:37:28 +02001829 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001830
Michal Vasko71090fc2016-05-24 16:37:28 +02001831cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001832 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001833 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001834
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001835 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001836 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001837 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001838}
1839
Michal Vasko2e6defd2016-10-07 15:48:15 +02001840API int
1841nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1842{
1843 uint16_t i;
1844
1845 if (!name) {
1846 ERRARG("name");
1847 return -1;
1848 } else if (!ti) {
1849 ERRARG("ti");
1850 return -1;
1851 }
1852
1853 /* WRITE LOCK */
1854 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1855
1856 /* check name uniqueness */
1857 for (i = 0; i < server_opts.ch_client_count; ++i) {
1858 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1859 ERR("Call Home client \"%s\" already exists.", name);
1860 /* WRITE UNLOCK */
1861 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1862 return -1;
1863 }
1864 }
1865
1866 ++server_opts.ch_client_count;
1867 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1868 if (!server_opts.ch_clients) {
1869 ERRMEM;
1870 /* WRITE UNLOCK */
1871 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1872 return -1;
1873 }
1874 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1875 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001876 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1877 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001878
1879 switch (ti) {
1880#ifdef NC_ENABLED_SSH
1881 case NC_TI_LIBSSH:
1882 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1883 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1884 ERRMEM;
1885 /* WRITE UNLOCK */
1886 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1887 return -1;
1888 }
1889 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1890 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1891 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1892 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1893 break;
1894#endif
1895#ifdef NC_ENABLED_TLS
1896 case NC_TI_OPENSSL:
1897 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1898 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1899 ERRMEM;
1900 /* WRITE UNLOCK */
1901 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1902 return -1;
1903 }
1904 break;
1905#endif
1906 default:
1907 ERRINT;
1908 /* WRITE UNLOCK */
1909 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1910 return -1;
1911 }
1912
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001913 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
1914
Michal Vasko2e6defd2016-10-07 15:48:15 +02001915 /* set CH default options */
1916 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1917 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1918
1919 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1920
1921 /* WRITE UNLOCK */
1922 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1923
1924 return 0;
1925}
1926
1927API int
Michal Vasko59050372016-11-22 14:33:55 +01001928nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001929{
1930 uint16_t i, j;
1931 int ret = -1;
1932
1933 /* WRITE LOCK */
1934 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1935
Michal Vasko59050372016-11-22 14:33:55 +01001936 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001937 /* remove all CH clients */
1938 for (i = 0; i < server_opts.ch_client_count; ++i) {
1939 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1940
1941 /* remove all endpoints */
1942 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1943 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1944 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1945 }
1946 free(server_opts.ch_clients[i].ch_endpts);
1947
1948 switch (server_opts.ch_clients[i].ti) {
1949#ifdef NC_ENABLED_SSH
1950 case NC_TI_LIBSSH:
1951 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1952 free(server_opts.ch_clients[i].opts.ssh);
1953 break;
1954#endif
1955#ifdef NC_ENABLED_TLS
1956 case NC_TI_OPENSSL:
1957 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1958 free(server_opts.ch_clients[i].opts.tls);
1959 break;
1960#endif
1961 default:
1962 ERRINT;
1963 /* won't get here ...*/
1964 break;
1965 }
1966
1967 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1968
1969 ret = 0;
1970 }
1971 free(server_opts.ch_clients);
1972 server_opts.ch_clients = NULL;
1973
1974 server_opts.ch_client_count = 0;
1975
1976 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001977 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001978 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001979 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 +02001980 /* remove endpt */
1981 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1982
1983 switch (server_opts.ch_clients[i].ti) {
1984#ifdef NC_ENABLED_SSH
1985 case NC_TI_LIBSSH:
1986 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1987 free(server_opts.ch_clients[i].opts.ssh);
1988 break;
1989#endif
1990#ifdef NC_ENABLED_TLS
1991 case NC_TI_OPENSSL:
1992 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1993 free(server_opts.ch_clients[i].opts.tls);
1994 break;
1995#endif
1996 default:
1997 ERRINT;
1998 break;
1999 }
2000
2001 /* remove all endpoints */
2002 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2003 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2004 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2005 }
2006 free(server_opts.ch_clients[i].ch_endpts);
2007
2008 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2009
2010 /* move last client and endpoint(s) to the empty space */
2011 --server_opts.ch_client_count;
2012 if (i < server_opts.ch_client_count) {
2013 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2014 sizeof *server_opts.ch_clients);
2015 } else if (!server_opts.ch_client_count) {
2016 free(server_opts.ch_clients);
2017 server_opts.ch_clients = NULL;
2018 }
2019
2020 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002021 if (name) {
2022 break;
2023 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002024 }
2025 }
2026 }
2027
2028 /* WRITE UNLOCK */
2029 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2030
2031 return ret;
2032}
2033
2034API int
2035nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2036{
2037 uint16_t i;
2038 struct nc_ch_client *client;
2039
2040 if (!client_name) {
2041 ERRARG("client_name");
2042 return -1;
2043 } else if (!endpt_name) {
2044 ERRARG("endpt_name");
2045 return -1;
2046 }
2047
2048 /* LOCK */
2049 client = nc_server_ch_client_lock(client_name, 0, NULL);
2050 if (!client) {
2051 return -1;
2052 }
2053
2054 for (i = 0; i < client->ch_endpt_count; ++i) {
2055 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2056 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2057 /* UNLOCK */
2058 nc_server_ch_client_unlock(client);
2059 return -1;
2060 }
2061 }
2062
2063 ++client->ch_endpt_count;
2064 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2065 if (!client->ch_endpts) {
2066 ERRMEM;
2067 /* UNLOCK */
2068 nc_server_ch_client_unlock(client);
2069 return -1;
2070 }
2071
2072 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2073 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2074 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2075
2076 /* UNLOCK */
2077 nc_server_ch_client_unlock(client);
2078
2079 return 0;
2080}
2081
2082API int
2083nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2084{
2085 uint16_t i;
2086 int ret = -1;
2087 struct nc_ch_client *client;
2088
2089 if (!client_name) {
2090 ERRARG("client_name");
2091 return -1;
2092 }
2093
2094 /* LOCK */
2095 client = nc_server_ch_client_lock(client_name, 0, NULL);
2096 if (!client) {
2097 return -1;
2098 }
2099
2100 if (!endpt_name) {
2101 /* remove all endpoints */
2102 for (i = 0; i < client->ch_endpt_count; ++i) {
2103 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2104 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2105 }
2106 free(client->ch_endpts);
2107 client->ch_endpts = NULL;
2108 client->ch_endpt_count = 0;
2109
2110 ret = 0;
2111 } else {
2112 for (i = 0; i < client->ch_endpt_count; ++i) {
2113 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2114 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2115 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002116
Michal Vasko4f921012016-10-20 14:07:45 +02002117 /* move last endpoint to the empty space */
2118 --client->ch_endpt_count;
2119 if (i < client->ch_endpt_count) {
2120 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2121 } else if (!server_opts.ch_client_count) {
2122 free(server_opts.ch_clients);
2123 server_opts.ch_clients = NULL;
2124 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002125
Michal Vasko4f921012016-10-20 14:07:45 +02002126 ret = 0;
2127 break;
2128 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002129 }
2130 }
2131
2132 /* UNLOCK */
2133 nc_server_ch_client_unlock(client);
2134
2135 return ret;
2136}
2137
2138API int
2139nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2140{
2141 uint16_t i;
2142 int ret = -1;
2143 struct nc_ch_client *client;
2144
2145 if (!client_name) {
2146 ERRARG("client_name");
2147 return -1;
2148 } else if (!endpt_name) {
2149 ERRARG("endpt_name");
2150 return -1;
2151 } else if (!address) {
2152 ERRARG("address");
2153 return -1;
2154 }
2155
2156 /* LOCK */
2157 client = nc_server_ch_client_lock(client_name, 0, NULL);
2158 if (!client) {
2159 return -1;
2160 }
2161
2162 for (i = 0; i < client->ch_endpt_count; ++i) {
2163 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2164 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2165 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2166
2167 ret = 0;
2168 break;
2169 }
2170 }
2171
2172 /* UNLOCK */
2173 nc_server_ch_client_unlock(client);
2174
2175 if (ret == -1) {
2176 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2177 }
2178
2179 return ret;
2180}
2181
2182API int
2183nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2184{
2185 uint16_t i;
2186 int ret = -1;
2187 struct nc_ch_client *client;
2188
2189 if (!client_name) {
2190 ERRARG("client_name");
2191 return -1;
2192 } else if (!endpt_name) {
2193 ERRARG("endpt_name");
2194 return -1;
2195 } else if (!port) {
2196 ERRARG("port");
2197 return -1;
2198 }
2199
2200 /* LOCK */
2201 client = nc_server_ch_client_lock(client_name, 0, NULL);
2202 if (!client) {
2203 return -1;
2204 }
2205
2206 for (i = 0; i < client->ch_endpt_count; ++i) {
2207 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2208 client->ch_endpts[i].port = port;
2209
2210 ret = 0;
2211 break;
2212 }
2213 }
2214
2215 /* UNLOCK */
2216 nc_server_ch_client_unlock(client);
2217
2218 if (ret == -1) {
2219 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2220 }
2221
2222 return ret;
2223}
2224
2225API int
2226nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2227{
2228 struct nc_ch_client *client;
2229
2230 if (!client_name) {
2231 ERRARG("client_name");
2232 return -1;
2233 } else if (!conn_type) {
2234 ERRARG("conn_type");
2235 return -1;
2236 }
2237
2238 /* LOCK */
2239 client = nc_server_ch_client_lock(client_name, 0, NULL);
2240 if (!client) {
2241 return -1;
2242 }
2243
2244 if (client->conn_type != conn_type) {
2245 client->conn_type = conn_type;
2246
2247 /* set default options */
2248 switch (conn_type) {
2249 case NC_CH_PERSIST:
2250 client->conn.persist.idle_timeout = 86400;
2251 client->conn.persist.ka_max_wait = 30;
2252 client->conn.persist.ka_max_attempts = 3;
2253 break;
2254 case NC_CH_PERIOD:
2255 client->conn.period.idle_timeout = 300;
2256 client->conn.period.reconnect_timeout = 60;
2257 break;
2258 default:
2259 ERRINT;
2260 break;
2261 }
2262 }
2263
2264 /* UNLOCK */
2265 nc_server_ch_client_unlock(client);
2266
2267 return 0;
2268}
2269
2270API int
2271nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2272{
2273 struct nc_ch_client *client;
2274
2275 if (!client_name) {
2276 ERRARG("client_name");
2277 return -1;
2278 }
2279
2280 /* LOCK */
2281 client = nc_server_ch_client_lock(client_name, 0, NULL);
2282 if (!client) {
2283 return -1;
2284 }
2285
2286 if (client->conn_type != NC_CH_PERSIST) {
2287 ERR("Call Home client \"%s\" is not of persistent connection type.");
2288 /* UNLOCK */
2289 nc_server_ch_client_unlock(client);
2290 return -1;
2291 }
2292
2293 client->conn.persist.idle_timeout = idle_timeout;
2294
2295 /* UNLOCK */
2296 nc_server_ch_client_unlock(client);
2297
2298 return 0;
2299}
2300
2301API int
2302nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2303{
2304 struct nc_ch_client *client;
2305
2306 if (!client_name) {
2307 ERRARG("client_name");
2308 return -1;
2309 } else if (!max_wait) {
2310 ERRARG("max_wait");
2311 return -1;
2312 }
2313
2314 /* LOCK */
2315 client = nc_server_ch_client_lock(client_name, 0, NULL);
2316 if (!client) {
2317 return -1;
2318 }
2319
2320 if (client->conn_type != NC_CH_PERSIST) {
2321 ERR("Call Home client \"%s\" is not of persistent connection type.");
2322 /* UNLOCK */
2323 nc_server_ch_client_unlock(client);
2324 return -1;
2325 }
2326
2327 client->conn.persist.ka_max_wait = max_wait;
2328
2329 /* UNLOCK */
2330 nc_server_ch_client_unlock(client);
2331
2332 return 0;
2333}
2334
2335API int
2336nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2337{
2338 struct nc_ch_client *client;
2339
2340 if (!client_name) {
2341 ERRARG("client_name");
2342 return -1;
2343 }
2344
2345 /* LOCK */
2346 client = nc_server_ch_client_lock(client_name, 0, NULL);
2347 if (!client) {
2348 return -1;
2349 }
2350
2351 if (client->conn_type != NC_CH_PERSIST) {
2352 ERR("Call Home client \"%s\" is not of persistent connection type.");
2353 /* UNLOCK */
2354 nc_server_ch_client_unlock(client);
2355 return -1;
2356 }
2357
2358 client->conn.persist.ka_max_attempts = max_attempts;
2359
2360 /* UNLOCK */
2361 nc_server_ch_client_unlock(client);
2362
2363 return 0;
2364}
2365
2366API int
2367nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2368{
2369 struct nc_ch_client *client;
2370
2371 if (!client_name) {
2372 ERRARG("client_name");
2373 return -1;
2374 }
2375
2376 /* LOCK */
2377 client = nc_server_ch_client_lock(client_name, 0, NULL);
2378 if (!client) {
2379 return -1;
2380 }
2381
2382 if (client->conn_type != NC_CH_PERIOD) {
2383 ERR("Call Home client \"%s\" is not of periodic connection type.");
2384 /* UNLOCK */
2385 nc_server_ch_client_unlock(client);
2386 return -1;
2387 }
2388
2389 client->conn.period.idle_timeout = idle_timeout;
2390
2391 /* UNLOCK */
2392 nc_server_ch_client_unlock(client);
2393
2394 return 0;
2395}
2396
2397API int
2398nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2399{
2400 struct nc_ch_client *client;
2401
2402 if (!client_name) {
2403 ERRARG("client_name");
2404 return -1;
2405 } else if (!reconnect_timeout) {
2406 ERRARG("reconnect_timeout");
2407 return -1;
2408 }
2409
2410 /* LOCK */
2411 client = nc_server_ch_client_lock(client_name, 0, NULL);
2412 if (!client) {
2413 return -1;
2414 }
2415
2416 if (client->conn_type != NC_CH_PERIOD) {
2417 ERR("Call Home client \"%s\" is not of periodic connection type.");
2418 /* UNLOCK */
2419 nc_server_ch_client_unlock(client);
2420 return -1;
2421 }
2422
2423 client->conn.period.reconnect_timeout = reconnect_timeout;
2424
2425 /* UNLOCK */
2426 nc_server_ch_client_unlock(client);
2427
2428 return 0;
2429}
2430
2431API int
2432nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2433{
2434 struct nc_ch_client *client;
2435
2436 if (!client_name) {
2437 ERRARG("client_name");
2438 return -1;
2439 }
2440
2441 /* LOCK */
2442 client = nc_server_ch_client_lock(client_name, 0, NULL);
2443 if (!client) {
2444 return -1;
2445 }
2446
2447 client->start_with = start_with;
2448
2449 /* UNLOCK */
2450 nc_server_ch_client_unlock(client);
2451
2452 return 0;
2453}
2454
2455API int
2456nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2457{
2458 struct nc_ch_client *client;
2459
2460 if (!client_name) {
2461 ERRARG("client_name");
2462 return -1;
2463 } else if (!max_attempts) {
2464 ERRARG("max_attempts");
2465 return -1;
2466 }
2467
2468 /* LOCK */
2469 client = nc_server_ch_client_lock(client_name, 0, NULL);
2470 if (!client) {
2471 return -1;
2472 }
2473
2474 client->max_attempts = max_attempts;
2475
2476 /* UNLOCK */
2477 nc_server_ch_client_unlock(client);
2478
2479 return 0;
2480}
2481
2482/* client lock is expected to be held */
2483static NC_MSG_TYPE
2484nc_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 +01002485{
Michal Vasko71090fc2016-05-24 16:37:28 +02002486 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002487 int sock, ret;
2488
Michal Vasko2e6defd2016-10-07 15:48:15 +02002489 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002490 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002491 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002492 }
2493
2494 *session = calloc(1, sizeof **session);
2495 if (!(*session)) {
2496 ERRMEM;
2497 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002498 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002499 }
2500 (*session)->status = NC_STATUS_STARTING;
2501 (*session)->side = NC_SERVER;
2502 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002503 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002504 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2505 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002506
2507 /* transport lock */
2508 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
2509 if (!(*session)->ti_lock) {
2510 ERRMEM;
2511 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002512 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002513 goto fail;
2514 }
2515 pthread_mutex_init((*session)->ti_lock, NULL);
2516
2517 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002518#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002519 if (client->ti == NC_TI_LIBSSH) {
2520 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002521 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002522 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002523
Michal Vasko71090fc2016-05-24 16:37:28 +02002524 if (ret < 0) {
2525 msgtype = NC_MSG_ERROR;
2526 goto fail;
2527 } else if (!ret) {
2528 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002529 goto fail;
2530 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002531 } else
2532#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002533#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002534 if (client->ti == NC_TI_OPENSSL) {
2535 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002536 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002537 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002538
Michal Vasko71090fc2016-05-24 16:37:28 +02002539 if (ret < 0) {
2540 msgtype = NC_MSG_ERROR;
2541 goto fail;
2542 } else if (!ret) {
2543 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002544 goto fail;
2545 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002546 } else
2547#endif
2548 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002549 ERRINT;
2550 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002551 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002552 goto fail;
2553 }
2554
2555 /* assign new SID atomically */
2556 /* LOCK */
2557 pthread_spin_lock(&server_opts.sid_lock);
2558 (*session)->id = server_opts.new_session_id++;
2559 /* UNLOCK */
2560 pthread_spin_unlock(&server_opts.sid_lock);
2561
2562 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002563 msgtype = nc_handshake(*session);
2564 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002565 goto fail;
2566 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002567 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002568 (*session)->status = NC_STATUS_RUNNING;
2569
Michal Vasko71090fc2016-05-24 16:37:28 +02002570 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002571
2572fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002573 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002574 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002575 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002576}
2577
Michal Vasko2e6defd2016-10-07 15:48:15 +02002578/* ms */
2579#define NC_CH_NO_ENDPT_WAIT 1000
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002580#define NC_CH_ENDPT_FAIL_WAIT 1000
Michal Vasko2e6defd2016-10-07 15:48:15 +02002581
2582struct nc_ch_client_thread_arg {
2583 char *client_name;
2584 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2585};
2586
2587static struct nc_ch_client *
2588nc_server_ch_client_with_endpt_lock(const char *name)
2589{
2590 struct nc_ch_client *client;
2591
2592 while (1) {
2593 /* LOCK */
2594 client = nc_server_ch_client_lock(name, 0, NULL);
2595 if (!client) {
2596 return NULL;
2597 }
2598 if (client->ch_endpt_count) {
2599 return client;
2600 }
2601 /* no endpoints defined yet */
2602
2603 /* UNLOCK */
2604 nc_server_ch_client_unlock(client);
2605
2606 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2607 }
2608
2609 return NULL;
2610}
2611
2612static int
2613nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2614{
2615 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002616 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002617 struct timespec ts;
2618 struct nc_ch_client *client;
2619
2620 /* session created, initialize condition */
2621 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2622 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2623 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2624 ERRMEM;
2625 nc_session_free(session, NULL);
2626 return -1;
2627 }
2628 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2629 pthread_cond_init(session->opts.server.ch_cond, NULL);
2630
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002631 session->flags |= NC_SESSION_CALLHOME;
2632
Michal Vasko2e6defd2016-10-07 15:48:15 +02002633 /* CH LOCK */
2634 pthread_mutex_lock(session->opts.server.ch_lock);
2635
2636 /* give the session to the user */
2637 data->session_clb(data->client_name, session);
2638
2639 do {
2640 nc_gettimespec(&ts);
2641 ts.tv_nsec += NC_CH_NO_ENDPT_WAIT * 1000000L;
2642 if (ts.tv_nsec > 1000000000L) {
2643 ts.tv_sec += ts.tv_nsec / 1000000000L;
2644 ts.tv_nsec %= 1000000000L;
2645 }
2646
2647 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2648 if (ret && (ret != ETIMEDOUT)) {
2649 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2650 goto ch_client_remove;
2651 }
2652
2653 /* check whether the client was not removed */
2654 /* LOCK */
2655 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2656 if (!client) {
2657 /* client was removed, finish thread */
2658 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2659 data->client_name);
2660 goto ch_client_remove;
2661 }
2662
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002663 if (client->conn_type == NC_CH_PERSIST) {
2664 /* TODO keep-alives */
2665 idle_timeout = client->conn.persist.idle_timeout;
2666 } else {
2667 idle_timeout = client->conn.period.idle_timeout;
2668 }
2669
2670 /* TODO only for sessions without subscriptions */
2671 if (idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
2672 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2673 session->status = NC_STATUS_INVALID;
2674 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2675 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002676
2677 /* UNLOCK */
2678 nc_server_ch_client_unlock(client);
2679
2680 } while (session->status == NC_STATUS_RUNNING);
2681
2682 /* CH UNLOCK */
2683 pthread_mutex_unlock(session->opts.server.ch_lock);
2684
2685 return 0;
2686
2687ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002688 /* make the session a standard one */
2689 pthread_cond_destroy(session->opts.server.ch_cond);
2690 free(session->opts.server.ch_cond);
2691 session->opts.server.ch_cond = NULL;
2692
2693 session->flags &= ~NC_SESSION_CALLHOME;
2694
Michal Vasko2e6defd2016-10-07 15:48:15 +02002695 /* CH UNLOCK */
2696 pthread_mutex_unlock(session->opts.server.ch_lock);
2697
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002698 pthread_mutex_destroy(session->opts.server.ch_lock);
2699 free(session->opts.server.ch_lock);
2700 session->opts.server.ch_lock = NULL;
2701
Michal Vasko2e6defd2016-10-07 15:48:15 +02002702 return 1;
2703}
2704
2705static void *
2706nc_ch_client_thread(void *arg)
2707{
2708 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2709 NC_MSG_TYPE msgtype;
2710 uint8_t cur_attempts = 0;
2711 uint16_t i;
2712 char *cur_endpt_name;
2713 struct nc_ch_endpt *cur_endpt;
2714 struct nc_session *session;
2715 struct nc_ch_client *client;
2716
2717 /* LOCK */
2718 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2719 if (!client) {
2720 goto cleanup;
2721 }
2722
2723 cur_endpt = &client->ch_endpts[0];
2724 cur_endpt_name = strdup(cur_endpt->name);
2725
Michal Vasko29af44b2016-10-13 10:59:55 +02002726 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002727 while (1) {
2728 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2729
2730 if (msgtype == NC_MSG_HELLO) {
2731 /* UNLOCK */
2732 nc_server_ch_client_unlock(client);
2733
Michal Vasko29af44b2016-10-13 10:59:55 +02002734 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002735 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2736 goto cleanup;
2737 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002738 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002739
2740 /* LOCK */
2741 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2742 if (!client) {
2743 goto cleanup;
2744 }
2745
2746 /* session changed status -> it was disconnected for whatever reason,
2747 * persistent connection immediately tries to reconnect, periodic waits some first */
2748 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002749 /* UNLOCK */
2750 nc_server_ch_client_unlock(client);
2751
2752 /* TODO wake up sometimes to check for new notifications */
2753 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2754
2755 /* LOCK */
2756 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2757 if (!client) {
2758 goto cleanup;
2759 }
2760 }
2761
2762 /* set next endpoint to try */
2763 if (client->start_with == NC_CH_FIRST_LISTED) {
2764 cur_endpt = &client->ch_endpts[0];
2765 free(cur_endpt_name);
2766 cur_endpt_name = strdup(cur_endpt->name);
2767 } /* else we keep the current one */
2768 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002769 /* UNLOCK */
2770 nc_server_ch_client_unlock(client);
2771
Michal Vasko2e6defd2016-10-07 15:48:15 +02002772 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002773 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2774
Michal Vasko6bb116b2016-10-26 13:53:46 +02002775 /* LOCK */
2776 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2777 if (!client) {
2778 goto cleanup;
2779 }
2780
Michal Vasko2e6defd2016-10-07 15:48:15 +02002781 ++cur_attempts;
2782 if (cur_attempts == client->max_attempts) {
2783 for (i = 0; i < client->ch_endpt_count; ++i) {
2784 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2785 break;
2786 }
2787 }
2788 if (i < client->ch_endpt_count - 1) {
2789 /* just go to the next endpoint */
2790 cur_endpt = &client->ch_endpts[i + 1];
2791 free(cur_endpt_name);
2792 cur_endpt_name = strdup(cur_endpt->name);
2793 } else {
2794 /* cur_endpoint was removed or is the last, either way start with the first one */
2795 cur_endpt = &client->ch_endpts[0];
2796 free(cur_endpt_name);
2797 cur_endpt_name = strdup(cur_endpt->name);
2798 }
2799
2800 cur_attempts = 0;
2801 } /* else we keep the current one */
2802 }
2803 }
2804
2805cleanup:
2806 VRB("Call Home client \"%s\" thread exit.", data->client_name);
2807
2808 free(data->client_name);
2809 free(data);
2810 return NULL;
2811}
2812
2813API int
2814nc_connect_ch_client_dispatch(const char *client_name,
2815 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2816 int ret;
2817 pthread_t tid;
2818 struct nc_ch_client_thread_arg *arg;
2819
2820 if (!client_name) {
2821 ERRARG("client_name");
2822 return -1;
2823 } else if (!session_clb) {
2824 ERRARG("session_clb");
2825 return -1;
2826 }
2827
2828 arg = malloc(sizeof *arg);
2829 if (!arg) {
2830 ERRMEM;
2831 return -1;
2832 }
2833 arg->client_name = strdup(client_name);
2834 if (!arg->client_name) {
2835 ERRMEM;
2836 free(arg);
2837 return -1;
2838 }
2839 arg->session_clb = session_clb;
2840
2841 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2842 if (ret) {
2843 ERR("Creating a new thread failed (%s).", strerror(ret));
2844 free(arg->client_name);
2845 free(arg);
2846 return -1;
2847 }
2848 /* the thread now manages arg */
2849
2850 pthread_detach(tid);
2851
2852 return 0;
2853}
2854
Radek Krejci53691be2016-02-22 13:58:37 +01002855#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002856
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002857API time_t
2858nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002859{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002860 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002861 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002862 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002863 }
2864
Michal Vasko2e6defd2016-10-07 15:48:15 +02002865 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002866}