blob: 59a1600238c8baa18e73bb651c7c9008f8ff1f06 [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);
468#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100469 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100470}
471
Michal Vasko086311b2016-01-08 09:53:11 +0100472API int
473nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
474{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200475 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
476 ERRARG("basic_mode");
477 return -1;
478 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
479 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100480 return -1;
481 }
482
483 server_opts.wd_basic_mode = basic_mode;
484 server_opts.wd_also_supported = also_supported;
485 return 0;
486}
487
Michal Vasko1a38c862016-01-15 15:50:07 +0100488API void
Michal Vasko55f03972016-04-13 08:56:01 +0200489nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
490{
491 if (!basic_mode && !also_supported) {
492 ERRARG("basic_mode and also_supported");
493 return;
494 }
495
496 if (basic_mode) {
497 *basic_mode = server_opts.wd_basic_mode;
498 }
499 if (also_supported) {
500 *also_supported = server_opts.wd_also_supported;
501 }
502}
503
Michal Vasko55f03972016-04-13 08:56:01 +0200504API int
Radek Krejci658782b2016-12-04 22:04:55 +0100505nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200506{
Radek Krejci658782b2016-12-04 22:04:55 +0100507 const char **new;
508
509 if (!value || !value[0]) {
510 ERRARG("value must not be empty");
511 return EXIT_FAILURE;
512 }
513
514 server_opts.capabilities_count++;
515 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
516 if (!new) {
517 ERRMEM;
518 return EXIT_FAILURE;
519 }
520 server_opts.capabilities = new;
521 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
522
523 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200524}
525
Michal Vasko1a38c862016-01-15 15:50:07 +0100526API void
Michal Vasko086311b2016-01-08 09:53:11 +0100527nc_server_set_hello_timeout(uint16_t hello_timeout)
528{
Michal Vasko086311b2016-01-08 09:53:11 +0100529 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100530}
531
Michal Vasko55f03972016-04-13 08:56:01 +0200532API uint16_t
533nc_server_get_hello_timeout(void)
534{
535 return server_opts.hello_timeout;
536}
537
Michal Vasko1a38c862016-01-15 15:50:07 +0100538API void
Michal Vasko086311b2016-01-08 09:53:11 +0100539nc_server_set_idle_timeout(uint16_t idle_timeout)
540{
Michal Vasko086311b2016-01-08 09:53:11 +0100541 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100542}
543
Michal Vasko55f03972016-04-13 08:56:01 +0200544API uint16_t
545nc_server_get_idle_timeout(void)
546{
547 return server_opts.idle_timeout;
548}
549
Michal Vasko71090fc2016-05-24 16:37:28 +0200550API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100551nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100552{
Michal Vasko71090fc2016-05-24 16:37:28 +0200553 NC_MSG_TYPE msgtype;
554
Michal Vasko45e53ae2016-04-07 11:46:03 +0200555 if (!server_opts.ctx) {
556 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200557 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200558 } else if (fdin < 0) {
559 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200560 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200561 } else if (fdout < 0) {
562 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200563 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200564 } else if (!username) {
565 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200566 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200567 } else if (!session) {
568 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200569 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100570 }
571
572 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100573 *session = calloc(1, sizeof **session);
574 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100575 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200576 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100577 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100578 (*session)->status = NC_STATUS_STARTING;
579 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100580
581 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100582 (*session)->ti_type = NC_TI_FD;
583 (*session)->ti.fd.in = fdin;
584 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100585
586 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100587 (*session)->flags = NC_SESSION_SHAREDCTX;
588 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100589
Michal Vaskob48aa812016-01-18 14:13:09 +0100590 /* assign new SID atomically */
591 pthread_spin_lock(&server_opts.sid_lock);
592 (*session)->id = server_opts.new_session_id++;
593 pthread_spin_unlock(&server_opts.sid_lock);
594
Michal Vasko086311b2016-01-08 09:53:11 +0100595 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200596 msgtype = nc_handshake(*session);
597 if (msgtype != NC_MSG_HELLO) {
598 nc_session_free(*session, NULL);
599 *session = NULL;
600 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100601 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200602 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100603 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100604
Michal Vasko71090fc2016-05-24 16:37:28 +0200605 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100606}
Michal Vasko9e036d52016-01-08 10:49:26 +0100607
Michal Vaskob30b99c2016-07-26 11:35:43 +0200608static void
609nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
610{
611 uint8_t i, found = 0;
612
613 for (i = 0; i < ps->queue_len; ++i) {
614 /* idx round buffer adjust */
615 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
616 i = -ps->queue_begin;
617 }
618
619 if (found) {
620 /* move the value back one place */
621 if (ps->queue[ps->queue_begin + i] == id) {
622 /* another equal value, simply cannot be */
623 ERRINT;
624 }
625
626 if (ps->queue_begin + i == 0) {
627 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
628 } else {
629 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
630 }
631 } else if (ps->queue[ps->queue_begin + i] == id) {
632 /* found our id, there can be no more equal valid values */
633 found = 1;
634 }
635 }
636
637 if (!found) {
638 ERRINT;
639 }
640 --ps->queue_len;
641}
642
Michal Vaskof04a52a2016-04-07 10:52:10 +0200643int
Michal Vasko26043172016-07-26 14:08:59 +0200644nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200645{
646 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200647 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200648 struct timespec ts;
649
Radek Krejci7ac16052016-07-15 11:48:18 +0200650 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200651 ts.tv_sec += NC_READ_TIMEOUT;
652
653 /* LOCK */
654 ret = pthread_mutex_timedlock(&ps->lock, &ts);
655 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200656 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200657 return -1;
658 }
659
660 /* get a unique queue value (by adding 1 to the last added value, if any) */
661 if (ps->queue_len) {
662 queue_last = ps->queue_begin + ps->queue_len - 1;
663 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
664 queue_last -= NC_PS_QUEUE_SIZE;
665 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200666 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200667 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200668 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200669 }
670
671 /* add ourselves into the queue */
672 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200673 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200674 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200675 return -1;
676 }
677 ++ps->queue_len;
678 queue_last = ps->queue_begin + ps->queue_len - 1;
679 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
680 queue_last -= NC_PS_QUEUE_SIZE;
681 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200682 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200683
684 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200685 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200686 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200687 ts.tv_sec += NC_READ_TIMEOUT;
688
689 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
690 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200691 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200692 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200693 nc_ps_queue_remove_id(ps, *id);
694 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200695 return -1;
696 }
697 }
698
Michal Vaskobe86fe32016-04-07 10:43:03 +0200699 /* UNLOCK */
700 pthread_mutex_unlock(&ps->lock);
701
702 return 0;
703}
704
Michal Vaskof04a52a2016-04-07 10:52:10 +0200705int
Michal Vasko26043172016-07-26 14:08:59 +0200706nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200707{
708 int ret;
709 struct timespec ts;
710
Radek Krejci7ac16052016-07-15 11:48:18 +0200711 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200712 ts.tv_sec += NC_READ_TIMEOUT;
713
714 /* LOCK */
715 ret = pthread_mutex_timedlock(&ps->lock, &ts);
716 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200717 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200718 ret = -1;
719 }
720
Michal Vaskob30b99c2016-07-26 11:35:43 +0200721 /* we must be the first, it was our turn after all, right? */
722 if (ps->queue[ps->queue_begin] != id) {
723 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200724 /* UNLOCK */
725 if (!ret) {
726 pthread_mutex_unlock(&ps->lock);
727 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200728 return -1;
729 }
730
Michal Vaskobe86fe32016-04-07 10:43:03 +0200731 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200732 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200733
734 /* broadcast to all other threads that the queue moved */
735 pthread_cond_broadcast(&ps->cond);
736
Michal Vaskobe86fe32016-04-07 10:43:03 +0200737 /* UNLOCK */
738 if (!ret) {
739 pthread_mutex_unlock(&ps->lock);
740 }
741
742 return ret;
743}
744
Michal Vasko428087d2016-01-14 16:04:28 +0100745API struct nc_pollsession *
746nc_ps_new(void)
747{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100748 struct nc_pollsession *ps;
749
750 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100751 if (!ps) {
752 ERRMEM;
753 return NULL;
754 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200755 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100756 pthread_mutex_init(&ps->lock, NULL);
757
758 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100759}
760
761API void
762nc_ps_free(struct nc_pollsession *ps)
763{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100764 if (!ps) {
765 return;
766 }
767
Michal Vaskobe86fe32016-04-07 10:43:03 +0200768 if (ps->queue_len) {
769 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
770 }
771
Michal Vasko428087d2016-01-14 16:04:28 +0100772 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100773 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200774 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100775
Michal Vasko428087d2016-01-14 16:04:28 +0100776 free(ps);
777}
778
779API int
780nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
781{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200782 uint8_t q_id;
783
Michal Vasko45e53ae2016-04-07 11:46:03 +0200784 if (!ps) {
785 ERRARG("ps");
786 return -1;
787 } else if (!session) {
788 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100789 return -1;
790 }
791
Michal Vasko48a63ed2016-03-01 09:48:21 +0100792 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200793 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200794 return -1;
795 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100796
Michal Vasko428087d2016-01-14 16:04:28 +0100797 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100798 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko99f251b2017-01-11 11:31:46 +0100799 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100800 ERRMEM;
801 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200802 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100803 return -1;
804 }
Michal Vasko3a715132016-01-21 15:40:31 +0100805 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100806
Michal Vasko48a63ed2016-03-01 09:48:21 +0100807 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200808 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100809}
810
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100812_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100813{
814 uint16_t i;
815
Radek Krejcid5f978f2016-03-03 13:14:45 +0100816 if (index >= 0) {
817 i = (uint16_t)index;
818 goto remove;
819 }
Michal Vasko428087d2016-01-14 16:04:28 +0100820 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100821 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100822remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100823 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100824 if (i < ps->session_count) {
825 ps->sessions[i] = ps->sessions[ps->session_count];
Michal Vasko99f251b2017-01-11 11:31:46 +0100826 if (ps->last_event_session == i) {
827 ps->last_event_session = 0;
828 }
Michal Vasko58005732016-02-02 15:50:52 +0100829 } else if (!ps->session_count) {
830 free(ps->sessions);
831 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100832 }
Michal Vasko428087d2016-01-14 16:04:28 +0100833 return 0;
834 }
835 }
836
Michal Vaskof0537d82016-01-29 14:42:38 +0100837 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100838}
839
Michal Vasko48a63ed2016-03-01 09:48:21 +0100840API int
841nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
842{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200843 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200844 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100845
Michal Vasko45e53ae2016-04-07 11:46:03 +0200846 if (!ps) {
847 ERRARG("ps");
848 return -1;
849 } else if (!session) {
850 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100851 return -1;
852 }
853
854 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200855 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200856 return -1;
857 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100858
Radek Krejcid5f978f2016-03-03 13:14:45 +0100859 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100860
861 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200862 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100863
Michal Vaskobe86fe32016-04-07 10:43:03 +0200864 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100865}
866
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100867API uint16_t
868nc_ps_session_count(struct nc_pollsession *ps)
869{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200870 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100871 uint16_t count;
872
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100873 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200874 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100875 return 0;
876 }
877
Michal Vasko48a63ed2016-03-01 09:48:21 +0100878 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200879 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200880 return -1;
881 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100882
883 count = ps->session_count;
884
885 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200886 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100887
888 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100889}
890
Michal Vasko71090fc2016-05-24 16:37:28 +0200891/* must be called holding the session lock!
892 * returns: NC_PSPOLL_ERROR,
893 * NC_PSPOLL_BAD_RPC,
894 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
895 * NC_PSPOLL_RPC
896 */
897static int
Radek Krejci93e80222016-10-03 13:34:25 +0200898nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100899{
900 struct lyxml_elem *xml = NULL;
901 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200902 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200903 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100904
Michal Vasko45e53ae2016-04-07 11:46:03 +0200905 if (!session) {
906 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200907 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200908 } else if (!rpc) {
909 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200910 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100911 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100912 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200913 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100914 }
915
916 msgtype = nc_read_msg(session, &xml);
917
918 switch (msgtype) {
919 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200920 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100921 if (!*rpc) {
922 ERRMEM;
923 goto error;
924 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100925
Radek Krejcif93c7d42016-04-06 13:41:15 +0200926 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +0100927 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
928 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100929 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200930 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200931 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200932 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200933 nc_server_reply_free(reply);
934 if (ret == -1) {
935 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200936 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200937 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
938 } else {
939 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100940 }
Michal Vasko428087d2016-01-14 16:04:28 +0100941 (*rpc)->root = xml;
942 break;
943 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100944 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200945 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100946 goto error;
947 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100948 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200949 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100950 goto error;
951 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100952 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200953 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100954 goto error;
955 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200956 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100957 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
958 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200959 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100960 break;
961 }
962
Michal Vasko71090fc2016-05-24 16:37:28 +0200963 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100964
965error:
966 /* cleanup */
967 lyxml_free(server_opts.ctx, xml);
968
Michal Vasko71090fc2016-05-24 16:37:28 +0200969 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100970}
971
fanchanghu966f2de2016-07-21 02:28:57 -0400972API void
973nc_set_global_rpc_clb(nc_rpc_clb clb)
974{
975 global_rpc_clb = clb;
976}
977
Radek Krejci93e80222016-10-03 13:34:25 +0200978API NC_MSG_TYPE
979nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
980{
981 NC_MSG_TYPE result = NC_MSG_NOTIF;
982 int ret;
983
984 /* check parameters */
985 if (!session) {
986 ERRARG("session");
987 return NC_MSG_ERROR;
988 } else if (!notif || !notif->tree || !notif->eventtime) {
989 ERRARG("notif");
990 return NC_MSG_ERROR;
991 }
992
993 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
994 ret = nc_timedlock(session->ti_lock, timeout, __func__);
995 if (ret < 0) {
996 return NC_MSG_ERROR;
997 } else if (!ret) {
998 return NC_MSG_WOULDBLOCK;
999 }
1000
1001 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1002 if (ret == -1) {
1003 ERR("Session %u: failed to write notification.", session->id);
1004 result = NC_MSG_ERROR;
1005 }
1006 pthread_mutex_unlock(session->ti_lock);
1007
1008 return result;
1009}
1010
Michal Vasko71090fc2016-05-24 16:37:28 +02001011/* must be called holding the session lock!
1012 * returns: NC_PSPOLL_ERROR,
1013 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1014 * NC_PSPOLL_REPLY_ERROR,
1015 * 0
1016 */
1017static int
Radek Krejci93e80222016-10-03 13:34:25 +02001018nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001019{
1020 nc_rpc_clb clb;
1021 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001022 struct lys_node *rpc_act = NULL;
1023 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001024 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001025
Michal Vasko4a827e52016-03-03 10:59:00 +01001026 if (!rpc) {
1027 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001028 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001029 }
1030
Michal Vasko90e8e692016-07-13 12:27:57 +02001031 if (rpc->tree->schema->nodetype == LYS_RPC) {
1032 /* RPC */
1033 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001034 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001035 /* action */
1036 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1037 if (elem->schema->nodetype == LYS_ACTION) {
1038 rpc_act = elem->schema;
1039 break;
1040 }
1041 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001042 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001043 if (!rpc_act) {
1044 ERRINT;
1045 return NC_PSPOLL_ERROR;
1046 }
1047 }
1048
1049 if (!rpc_act->priv) {
1050 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001051 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001052 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001053 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001054 reply = clb(rpc->tree, session);
1055 }
1056
1057 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001058 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001059 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001060 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1061 if (reply->type == NC_RPL_ERROR) {
1062 ret |= NC_PSPOLL_REPLY_ERROR;
1063 }
1064 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001065
Michal Vasko71090fc2016-05-24 16:37:28 +02001066 if (r == -1) {
1067 ERR("Session %u: failed to write reply.", session->id);
1068 ret |= NC_PSPOLL_ERROR;
1069 }
Michal Vasko428087d2016-01-14 16:04:28 +01001070
1071 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1072 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1073 session->status = NC_STATUS_INVALID;
1074 }
1075
Michal Vasko71090fc2016-05-24 16:37:28 +02001076 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001077}
1078
1079API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001080nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001081{
Michal Vasko99f251b2017-01-11 11:31:46 +01001082 int ret, r, poll_ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001083 uint8_t q_id;
Michal Vasko99f251b2017-01-11 11:31:46 +01001084 uint16_t i, j;
1085 char msg[256];
1086 NC_SESSION_TERM_REASON term_reason;
1087 struct pollfd pfd;
1088 struct timespec begin_ts, cur_ts;
Michal Vasko71090fc2016-05-24 16:37:28 +02001089 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001090 struct nc_server_rpc *rpc = NULL;
Michal Vasko99f251b2017-01-11 11:31:46 +01001091#ifdef NC_ENABLED_SSH
1092 struct nc_session *new;
1093#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001094
1095 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001096 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001097 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001098 }
1099
Michal Vasko99f251b2017-01-11 11:31:46 +01001100 nc_gettimespec(&begin_ts);
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001101
Michal Vasko48a63ed2016-03-01 09:48:21 +01001102 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001103 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001104 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001105 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001106
Michal Vasko99f251b2017-01-11 11:31:46 +01001107 /* check that all session are fine */
Michal Vasko428087d2016-01-14 16:04:28 +01001108 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001109 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1110 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001111 ret = NC_PSPOLL_ERROR;
1112 if (session) {
1113 *session = ps->sessions[i];
1114 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001115 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001116 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001117
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001118 /* TODO invalidate only sessions without subscription */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001119 if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME) && server_opts.idle_timeout
Michal Vasko99f251b2017-01-11 11:31:46 +01001120 && (begin_ts.tv_sec >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001121 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1122 ps->sessions[i]->status = NC_STATUS_INVALID;
1123 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001124 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1125 if (session) {
1126 *session = ps->sessions[i];
1127 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001128 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001129 }
Michal Vasko428087d2016-01-14 16:04:28 +01001130 }
1131
Michal Vasko99f251b2017-01-11 11:31:46 +01001132 /* poll on all the sessions one-by-one */
1133 do {
1134 /* loop from i to j */
1135 if (ps->last_event_session == ps->session_count - 1) {
1136 i = j = 0;
1137 } else {
1138 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001139 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001140 do {
1141 cur_session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +01001142
Michal Vasko99f251b2017-01-11 11:31:46 +01001143 switch (cur_session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +01001144#ifdef NC_ENABLED_SSH
Michal Vasko99f251b2017-01-11 11:31:46 +01001145 case NC_TI_LIBSSH:
1146 r = ssh_channel_poll_timeout(cur_session->ti.libssh.channel, 0, 0);
1147 if (r < 1) {
1148 if (r == SSH_EOF) {
1149 sprintf(msg, "SSH channel unexpectedly closed");
1150 term_reason = NC_SESSION_TERM_DROPPED;
1151 poll_ret = -2;
1152 } else if (r == SSH_ERROR) {
1153 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(cur_session->ti.libssh.session));
Michal Vasko76be6752017-01-19 12:14:48 +01001154 term_reason = NC_SESSION_TERM_OTHER;
1155 poll_ret = -2;
Michal Vasko99f251b2017-01-11 11:31:46 +01001156 } else {
1157 poll_ret = 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001158 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001159 break;
Michal Vasko96164bf2016-01-21 15:41:58 +01001160 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001161 /* we have some data, but it may be just an SSH message */
Michal Vasko96164bf2016-01-21 15:41:58 +01001162
Michal Vasko99f251b2017-01-11 11:31:46 +01001163 r = nc_timedlock(cur_session->ti_lock, timeout, __func__);
1164 if (r < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001165 if (session) {
Michal Vasko99f251b2017-01-11 11:31:46 +01001166 *session = cur_session;
Michal Vasko71090fc2016-05-24 16:37:28 +02001167 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001168 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001169 goto finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001170 } else if (!r) {
1171 if (session) {
1172 *session = cur_session;
Michal Vasko428087d2016-01-14 16:04:28 +01001173 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001174 ret = NC_PSPOLL_TIMEOUT;
1175 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001176 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001177 r = ssh_execute_message_callbacks(cur_session->ti.libssh.session);
1178 pthread_mutex_unlock(cur_session->ti_lock);
1179
1180 if (r != SSH_OK) {
1181 sprintf(msg, "failed to receive SSH messages (%s)", ssh_get_error(cur_session->ti.libssh.session));
1182 term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko76be6752017-01-19 12:14:48 +01001183 poll_ret = -2;
Michal Vasko99f251b2017-01-11 11:31:46 +01001184 } else if (cur_session->flags & NC_SESSION_SSH_NEW_MSG) {
1185 /* new SSH message */
1186 cur_session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1187 if (cur_session->ti.libssh.next) {
1188 for (new = cur_session->ti.libssh.next; new != cur_session; new = new->ti.libssh.next) {
1189 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1190 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1191 /* new NETCONF SSH channel */
1192 if (session) {
1193 *session = cur_session;
1194 }
1195 ret = NC_PSPOLL_SSH_CHANNEL;
1196 goto finish;
1197 }
1198 }
1199 }
1200
1201 /* just some SSH message */
1202 if (session) {
1203 *session = cur_session;
1204 }
1205 ret = NC_PSPOLL_SSH_MSG;
1206 goto finish;
1207 } else {
1208 /* we have some application data */
1209 poll_ret = 1;
1210 }
1211 break;
1212#endif
1213#ifdef NC_ENABLED_TLS
1214 case NC_TI_OPENSSL:
1215 r = SSL_pending(cur_session->ti.tls);
1216 if (!r) {
1217 /* no data pending in the SSL buffer, poll fd */
1218 pfd.fd = SSL_get_rfd(cur_session->ti.tls);
1219 if (pfd.fd < 0) {
1220 ERRINT;
1221 ret = NC_PSPOLL_ERROR;
1222 goto finish;
1223 }
1224 pfd.events = POLLIN;
1225 pfd.revents = 0;
1226 r = poll(&pfd, 1, 0);
1227
1228 if (r < 0) {
1229 sprintf(msg, "poll failed (%s)", strerror(errno));
1230 poll_ret = -1;
1231 } else if (r > 0) {
1232 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1233 sprintf(msg, "communication socket unexpectedly closed");
1234 term_reason = NC_SESSION_TERM_DROPPED;
1235 poll_ret = -2;
1236 } else if (pfd.revents & POLLERR) {
1237 sprintf(msg, "communication socket error");
1238 term_reason = NC_SESSION_TERM_OTHER;
1239 poll_ret = -2;
1240 } else {
1241 poll_ret = 1;
1242 }
1243 } else {
1244 poll_ret = 0;
1245 }
1246 } else {
1247 poll_ret = 1;
1248 }
1249 break;
1250#endif
1251 case NC_TI_FD:
1252 pfd.fd = cur_session->ti.fd.in;
1253 pfd.events = POLLIN;
1254 pfd.revents = 0;
1255 r = poll(&pfd, 1, 0);
1256
1257 if (r < 0) {
1258 sprintf(msg, "poll failed (%s)", strerror(errno));
Michal Vasko76be6752017-01-19 12:14:48 +01001259 poll_ret = -1;
Michal Vasko99f251b2017-01-11 11:31:46 +01001260 } else if (r > 0) {
1261 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1262 sprintf(msg, "communication socket unexpectedly closed");
1263 term_reason = NC_SESSION_TERM_DROPPED;
1264 poll_ret = -2;
1265 } else if (pfd.revents & POLLERR) {
1266 sprintf(msg, "communication socket error");
1267 term_reason = NC_SESSION_TERM_OTHER;
1268 poll_ret = -2;
1269 } else {
1270 poll_ret = 1;
1271 }
1272 } else {
1273 poll_ret = 0;
1274 }
1275 break;
1276 case NC_TI_NONE:
1277 ERRINT;
1278 ret = NC_PSPOLL_ERROR;
1279 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001280 }
Michal Vasko428087d2016-01-14 16:04:28 +01001281
Michal Vasko99f251b2017-01-11 11:31:46 +01001282 /* here: poll_ret == -2 - session error, session terminated,
Michal Vasko76be6752017-01-19 12:14:48 +01001283 * poll_ret == -1 - generic error,
1284 * poll_ret == 0 - nothing to read,
1285 * poll_ret > 0 - data available */
Michal Vasko99f251b2017-01-11 11:31:46 +01001286 if (poll_ret == -2) {
1287 ERR("Session %u: %s.", cur_session->id, msg);
1288 cur_session->status = NC_STATUS_INVALID;
1289 cur_session->term_reason = term_reason;
1290 if (session) {
1291 *session = cur_session;
1292 }
1293 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1294 goto finish;
1295 } else if (poll_ret == -1) {
1296 ERR("Session %u: %s.", cur_session->id, msg);
1297 ret = NC_PSPOLL_ERROR;
1298 goto finish;
1299 } else if (poll_ret > 0) {
1300 break;
1301 }
1302
1303 /* next iteration */
1304 if (i == ps->session_count - 1) {
1305 i = 0;
1306 } else {
1307 ++i;
1308 }
1309 } while (i != j);
1310
1311 /* no event */
1312 if (!poll_ret && (timeout > -1)) {
1313 usleep(NC_TIMEOUT_STEP);
1314
1315 nc_gettimespec(&cur_ts);
1316 /* final timeout */
1317 if (nc_difftimespec(&begin_ts, &cur_ts) >= (unsigned)timeout) {
1318 ret = NC_PSPOLL_TIMEOUT;
1319 goto finish;
1320 }
Michal Vasko428087d2016-01-14 16:04:28 +01001321 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001322 } while (!poll_ret);
Michal Vasko428087d2016-01-14 16:04:28 +01001323
1324 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001325 if (session) {
1326 *session = cur_session;
1327 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001328 ps->last_event_session = i;
Michal Vasko428087d2016-01-14 16:04:28 +01001329
Michal Vaskobd8ef262016-01-20 11:09:27 +01001330 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko99f251b2017-01-11 11:31:46 +01001331 r = nc_timedlock(cur_session->ti_lock, timeout, __func__);
1332 if (r < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001333 ret = NC_PSPOLL_ERROR;
1334 goto finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001335 } else if (!r) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001336 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001337 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001338 }
1339
Radek Krejci93e80222016-10-03 13:34:25 +02001340 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001341 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1342 pthread_mutex_unlock(cur_session->ti_lock);
1343 if (cur_session->status != NC_STATUS_RUNNING) {
1344 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001345 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001346 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001347 }
1348
Michal Vasko2e6defd2016-10-07 15:48:15 +02001349 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001350
Michal Vasko428087d2016-01-14 16:04:28 +01001351 /* process RPC */
Radek Krejci93e80222016-10-03 13:34:25 +02001352 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001353
Michal Vasko71090fc2016-05-24 16:37:28 +02001354 pthread_mutex_unlock(cur_session->ti_lock);
1355 if (cur_session->status != NC_STATUS_RUNNING) {
1356 ret |= NC_PSPOLL_SESSION_TERM;
1357 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1358 ret |= NC_PSPOLL_SESSION_ERROR;
1359 }
Michal Vasko428087d2016-01-14 16:04:28 +01001360 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001361
Michal Vaskoca4a2422016-02-02 12:17:14 +01001362 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001363
Michal Vasko48a63ed2016-03-01 09:48:21 +01001364finish:
1365 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001366 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001367 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001368}
1369
Michal Vaskod09eae62016-02-01 10:32:52 +01001370API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001371nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001372{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001373 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001374 uint16_t i;
1375 struct nc_session *session;
1376
Michal Vasko9a25e932016-02-01 10:36:42 +01001377 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001378 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001379 return;
1380 }
1381
Michal Vasko48a63ed2016-03-01 09:48:21 +01001382 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001383 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001384 return;
1385 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001386
Michal Vasko48a63ed2016-03-01 09:48:21 +01001387 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001388 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001389 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001390 }
1391 free(ps->sessions);
1392 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001393 ps->session_count = 0;
Michal Vasko99f251b2017-01-11 11:31:46 +01001394 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001395 } else {
1396 for (i = 0; i < ps->session_count; ) {
1397 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1398 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001399 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001400 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001401 continue;
1402 }
1403
1404 ++i;
1405 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001406 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001407
1408 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001409 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001410}
1411
Radek Krejci53691be2016-02-22 13:58:37 +01001412#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001413
Michal Vaskoe2713da2016-08-22 16:06:40 +02001414API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001415nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001416{
Michal Vasko3031aae2016-01-27 16:07:18 +01001417 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +01001418
Michal Vasko45e53ae2016-04-07 11:46:03 +02001419 if (!name) {
1420 ERRARG("name");
1421 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001422 }
1423
Michal Vasko51e514d2016-02-02 15:51:52 +01001424 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001425 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001426
1427 /* check name uniqueness */
1428 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001429 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001430 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001431 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001432 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001433 return -1;
1434 }
1435 }
1436
Michal Vasko3031aae2016-01-27 16:07:18 +01001437 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001438 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001439 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001440 ERRMEM;
1441 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001442 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001443 return -1;
1444 }
1445 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001446 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001447
Michal Vaskoe2713da2016-08-22 16:06:40 +02001448 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001449 if (!server_opts.binds) {
1450 ERRMEM;
1451 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001452 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001453 return -1;
1454 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001455
Michal Vasko2e6defd2016-10-07 15:48:15 +02001456 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1457 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1458 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001459 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001460
1461 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001462#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001463 case NC_TI_LIBSSH:
1464 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1465 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1466 ERRMEM;
1467 /* WRITE UNLOCK */
1468 pthread_rwlock_unlock(&server_opts.endpt_lock);
1469 return -1;
1470 }
1471 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1472 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1473 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1474 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1475 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001476#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001477#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001478 case NC_TI_OPENSSL:
1479 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1480 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1481 ERRMEM;
1482 /* WRITE UNLOCK */
1483 pthread_rwlock_unlock(&server_opts.endpt_lock);
1484 return -1;
1485 }
1486 break;
1487#endif
1488 default:
1489 ERRINT;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001490 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001491 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001492 return -1;
1493 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001494
Michal Vasko2e6defd2016-10-07 15:48:15 +02001495 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001496
Michal Vasko3031aae2016-01-27 16:07:18 +01001497 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001498 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001499
Michal Vasko9e036d52016-01-08 10:49:26 +01001500 return 0;
1501}
1502
Michal Vasko3031aae2016-01-27 16:07:18 +01001503int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001504nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001505{
1506 struct nc_endpt *endpt;
1507 struct nc_bind *bind = NULL;
1508 uint16_t i;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001509 int sock = -1, set_addr;
Michal Vaskoda514772016-02-01 11:32:01 +01001510
Michal Vasko45e53ae2016-04-07 11:46:03 +02001511 if (!endpt_name) {
1512 ERRARG("endpt_name");
1513 return -1;
1514 } else if ((!address && !port) || (address && port)) {
1515 ERRARG("address and port");
1516 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001517 }
1518
Michal Vaskoe2713da2016-08-22 16:06:40 +02001519 if (address) {
1520 set_addr = 1;
1521 } else {
1522 set_addr = 0;
1523 }
1524
Michal Vasko51e514d2016-02-02 15:51:52 +01001525 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001526 endpt = nc_server_endpt_lock(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001527 if (!endpt) {
1528 return -1;
1529 }
1530
Michal Vaskoe2713da2016-08-22 16:06:40 +02001531 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001532
Michal Vaskoe2713da2016-08-22 16:06:40 +02001533 if (set_addr) {
1534 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001535 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001536 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001537 }
1538
Michal Vaskoe2713da2016-08-22 16:06:40 +02001539 /* we have all the information we need to create a listening socket */
1540 if (address && port) {
1541 /* create new socket, close the old one */
1542 sock = nc_sock_listen(address, port);
1543 if (sock == -1) {
1544 goto fail;
1545 }
1546
1547 if (bind->sock > -1) {
1548 close(bind->sock);
1549 }
1550 bind->sock = sock;
1551 } /* else we are just setting address or port */
1552
1553 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001554 lydict_remove(server_opts.ctx, bind->address);
1555 bind->address = lydict_insert(server_opts.ctx, address, 0);
1556 } else {
1557 bind->port = port;
1558 }
1559
Michal Vaskoe2713da2016-08-22 16:06:40 +02001560 if (sock > -1) {
1561#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001562 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001563#elif defined(NC_ENABLED_SSH)
1564 VRB("Listening on %s:%u for SSH connections.", address, port);
1565#else
1566 VRB("Listening on %s:%u for TLS connections.", address, port);
1567#endif
1568 }
1569
Michal Vasko51e514d2016-02-02 15:51:52 +01001570 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001571 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001572 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001573
1574fail:
1575 /* UNLOCK */
1576 nc_server_endpt_unlock(endpt);
1577 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001578}
1579
Michal Vaskoe2713da2016-08-22 16:06:40 +02001580API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001581nc_server_endpt_set_address(const char *endpt_name, const char *address)
1582{
1583 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1584}
1585
1586API int
1587nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1588{
1589 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1590}
1591
1592API int
Michal Vasko59050372016-11-22 14:33:55 +01001593nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001594{
1595 uint32_t i;
1596 int ret = -1;
1597
Michal Vasko3031aae2016-01-27 16:07:18 +01001598 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001599 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001600
Michal Vasko59050372016-11-22 14:33:55 +01001601 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001602 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001603 for (i = 0; i < server_opts.endpt_count; ++i) {
1604 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001605 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1606 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001607#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001608 case NC_TI_LIBSSH:
1609 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1610 free(server_opts.endpts[i].opts.ssh);
1611 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001612#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001613#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001614 case NC_TI_OPENSSL:
1615 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1616 free(server_opts.endpts[i].opts.tls);
1617 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001618#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001619 default:
1620 ERRINT;
1621 /* won't get here ...*/
1622 break;
1623 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001624 ret = 0;
1625 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001626 free(server_opts.endpts);
1627 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001628
1629 /* remove all binds */
1630 for (i = 0; i < server_opts.endpt_count; ++i) {
1631 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1632 if (server_opts.binds[i].sock > -1) {
1633 close(server_opts.binds[i].sock);
1634 }
1635 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001636 free(server_opts.binds);
1637 server_opts.binds = NULL;
1638
Michal Vasko3031aae2016-01-27 16:07:18 +01001639 server_opts.endpt_count = 0;
1640
Michal Vasko1a38c862016-01-15 15:50:07 +01001641 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001642 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001643 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001644 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001645 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001646 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001647 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1648 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001649#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001650 case NC_TI_LIBSSH:
1651 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1652 free(server_opts.endpts[i].opts.ssh);
1653 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001654#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001655#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001656 case NC_TI_OPENSSL:
1657 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1658 free(server_opts.endpts[i].opts.tls);
1659 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001660#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001661 default:
1662 ERRINT;
1663 break;
1664 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001665
Michal Vaskoe2713da2016-08-22 16:06:40 +02001666 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001667 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1668 if (server_opts.binds[i].sock > -1) {
1669 close(server_opts.binds[i].sock);
1670 }
1671
1672 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001673 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001674 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001675 free(server_opts.binds);
1676 server_opts.binds = NULL;
1677 free(server_opts.endpts);
1678 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001679 } else if (i < server_opts.endpt_count) {
1680 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1681 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001682 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001683
1684 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001685 if (name) {
1686 break;
1687 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001688 }
1689 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001690 }
1691
Michal Vasko3031aae2016-01-27 16:07:18 +01001692 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001693 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001694
Michal Vasko9e036d52016-01-08 10:49:26 +01001695 return ret;
1696}
1697
Michal Vasko71090fc2016-05-24 16:37:28 +02001698API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001699nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001700{
Michal Vasko71090fc2016-05-24 16:37:28 +02001701 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001702 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001703 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001704 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001705
Michal Vasko45e53ae2016-04-07 11:46:03 +02001706 if (!server_opts.ctx) {
1707 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001708 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001709 } else if (!session) {
1710 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001711 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001712 }
1713
Michal Vasko51e514d2016-02-02 15:51:52 +01001714 /* we have to hold WRITE for the whole time, since there is not
1715 * a way of downgrading the lock to READ */
1716 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001717 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001718
1719 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001720 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001721 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001722 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001723 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001724 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001725
Michal Vaskoe2713da2016-08-22 16:06:40 +02001726 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001727 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001728 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001729 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001730 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001731 if (!ret) {
1732 return NC_MSG_WOULDBLOCK;
1733 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001734 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001735 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001736 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001737
Michal Vasko1a38c862016-01-15 15:50:07 +01001738 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001739 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001740 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001741 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001742 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001743 msgtype = NC_MSG_ERROR;
1744 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001745 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001746 (*session)->status = NC_STATUS_STARTING;
1747 (*session)->side = NC_SERVER;
1748 (*session)->ctx = server_opts.ctx;
1749 (*session)->flags = NC_SESSION_SHAREDCTX;
1750 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1751 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001752
1753 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001754 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1755 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001756 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001757 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001758 msgtype = NC_MSG_ERROR;
1759 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001760 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001761 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001762
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001763 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001764#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001765 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1766 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01001767 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001768 if (ret < 0) {
1769 msgtype = NC_MSG_ERROR;
1770 goto cleanup;
1771 } else if (!ret) {
1772 msgtype = NC_MSG_WOULDBLOCK;
1773 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001774 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001775 } else
1776#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001777#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001778 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1779 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01001780 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001781 if (ret < 0) {
1782 msgtype = NC_MSG_ERROR;
1783 goto cleanup;
1784 } else if (!ret) {
1785 msgtype = NC_MSG_WOULDBLOCK;
1786 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001787 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001788 } else
1789#endif
1790 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001791 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001792 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001793 msgtype = NC_MSG_ERROR;
1794 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001795 }
1796
Michal Vasko2cc4c682016-03-01 09:16:48 +01001797 (*session)->data = NULL;
1798
Michal Vasko51e514d2016-02-02 15:51:52 +01001799 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001800 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001801
Michal Vaskob48aa812016-01-18 14:13:09 +01001802 /* assign new SID atomically */
1803 /* LOCK */
1804 pthread_spin_lock(&server_opts.sid_lock);
1805 (*session)->id = server_opts.new_session_id++;
1806 /* UNLOCK */
1807 pthread_spin_unlock(&server_opts.sid_lock);
1808
Michal Vasko9e036d52016-01-08 10:49:26 +01001809 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001810 msgtype = nc_handshake(*session);
1811 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001812 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001813 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001814 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001815 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001816 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001817 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001818
Michal Vasko71090fc2016-05-24 16:37:28 +02001819 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001820
Michal Vasko71090fc2016-05-24 16:37:28 +02001821cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001822 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001823 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001824
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001825 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001826 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001827 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001828}
1829
Michal Vasko2e6defd2016-10-07 15:48:15 +02001830API int
1831nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1832{
1833 uint16_t i;
1834
1835 if (!name) {
1836 ERRARG("name");
1837 return -1;
1838 } else if (!ti) {
1839 ERRARG("ti");
1840 return -1;
1841 }
1842
1843 /* WRITE LOCK */
1844 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1845
1846 /* check name uniqueness */
1847 for (i = 0; i < server_opts.ch_client_count; ++i) {
1848 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1849 ERR("Call Home client \"%s\" already exists.", name);
1850 /* WRITE UNLOCK */
1851 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1852 return -1;
1853 }
1854 }
1855
1856 ++server_opts.ch_client_count;
1857 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1858 if (!server_opts.ch_clients) {
1859 ERRMEM;
1860 /* WRITE UNLOCK */
1861 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1862 return -1;
1863 }
1864 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1865 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001866 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1867 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001868
1869 switch (ti) {
1870#ifdef NC_ENABLED_SSH
1871 case NC_TI_LIBSSH:
1872 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1873 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1874 ERRMEM;
1875 /* WRITE UNLOCK */
1876 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1877 return -1;
1878 }
1879 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1880 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1881 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1882 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1883 break;
1884#endif
1885#ifdef NC_ENABLED_TLS
1886 case NC_TI_OPENSSL:
1887 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1888 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1889 ERRMEM;
1890 /* WRITE UNLOCK */
1891 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1892 return -1;
1893 }
1894 break;
1895#endif
1896 default:
1897 ERRINT;
1898 /* WRITE UNLOCK */
1899 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1900 return -1;
1901 }
1902
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001903 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
1904
Michal Vasko2e6defd2016-10-07 15:48:15 +02001905 /* set CH default options */
1906 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1907 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1908
1909 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1910
1911 /* WRITE UNLOCK */
1912 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1913
1914 return 0;
1915}
1916
1917API int
Michal Vasko59050372016-11-22 14:33:55 +01001918nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001919{
1920 uint16_t i, j;
1921 int ret = -1;
1922
1923 /* WRITE LOCK */
1924 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1925
Michal Vasko59050372016-11-22 14:33:55 +01001926 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001927 /* remove all CH clients */
1928 for (i = 0; i < server_opts.ch_client_count; ++i) {
1929 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1930
1931 /* remove all endpoints */
1932 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1933 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1934 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1935 }
1936 free(server_opts.ch_clients[i].ch_endpts);
1937
1938 switch (server_opts.ch_clients[i].ti) {
1939#ifdef NC_ENABLED_SSH
1940 case NC_TI_LIBSSH:
1941 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1942 free(server_opts.ch_clients[i].opts.ssh);
1943 break;
1944#endif
1945#ifdef NC_ENABLED_TLS
1946 case NC_TI_OPENSSL:
1947 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1948 free(server_opts.ch_clients[i].opts.tls);
1949 break;
1950#endif
1951 default:
1952 ERRINT;
1953 /* won't get here ...*/
1954 break;
1955 }
1956
1957 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1958
1959 ret = 0;
1960 }
1961 free(server_opts.ch_clients);
1962 server_opts.ch_clients = NULL;
1963
1964 server_opts.ch_client_count = 0;
1965
1966 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001967 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001968 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001969 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 +02001970 /* remove endpt */
1971 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1972
1973 switch (server_opts.ch_clients[i].ti) {
1974#ifdef NC_ENABLED_SSH
1975 case NC_TI_LIBSSH:
1976 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1977 free(server_opts.ch_clients[i].opts.ssh);
1978 break;
1979#endif
1980#ifdef NC_ENABLED_TLS
1981 case NC_TI_OPENSSL:
1982 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1983 free(server_opts.ch_clients[i].opts.tls);
1984 break;
1985#endif
1986 default:
1987 ERRINT;
1988 break;
1989 }
1990
1991 /* remove all endpoints */
1992 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1993 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1994 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1995 }
1996 free(server_opts.ch_clients[i].ch_endpts);
1997
1998 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1999
2000 /* move last client and endpoint(s) to the empty space */
2001 --server_opts.ch_client_count;
2002 if (i < server_opts.ch_client_count) {
2003 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2004 sizeof *server_opts.ch_clients);
2005 } else if (!server_opts.ch_client_count) {
2006 free(server_opts.ch_clients);
2007 server_opts.ch_clients = NULL;
2008 }
2009
2010 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002011 if (name) {
2012 break;
2013 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002014 }
2015 }
2016 }
2017
2018 /* WRITE UNLOCK */
2019 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2020
2021 return ret;
2022}
2023
2024API int
2025nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2026{
2027 uint16_t i;
2028 struct nc_ch_client *client;
2029
2030 if (!client_name) {
2031 ERRARG("client_name");
2032 return -1;
2033 } else if (!endpt_name) {
2034 ERRARG("endpt_name");
2035 return -1;
2036 }
2037
2038 /* LOCK */
2039 client = nc_server_ch_client_lock(client_name, 0, NULL);
2040 if (!client) {
2041 return -1;
2042 }
2043
2044 for (i = 0; i < client->ch_endpt_count; ++i) {
2045 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2046 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2047 /* UNLOCK */
2048 nc_server_ch_client_unlock(client);
2049 return -1;
2050 }
2051 }
2052
2053 ++client->ch_endpt_count;
2054 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2055 if (!client->ch_endpts) {
2056 ERRMEM;
2057 /* UNLOCK */
2058 nc_server_ch_client_unlock(client);
2059 return -1;
2060 }
2061
2062 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2063 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2064 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2065
2066 /* UNLOCK */
2067 nc_server_ch_client_unlock(client);
2068
2069 return 0;
2070}
2071
2072API int
2073nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2074{
2075 uint16_t i;
2076 int ret = -1;
2077 struct nc_ch_client *client;
2078
2079 if (!client_name) {
2080 ERRARG("client_name");
2081 return -1;
2082 }
2083
2084 /* LOCK */
2085 client = nc_server_ch_client_lock(client_name, 0, NULL);
2086 if (!client) {
2087 return -1;
2088 }
2089
2090 if (!endpt_name) {
2091 /* remove all endpoints */
2092 for (i = 0; i < client->ch_endpt_count; ++i) {
2093 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2094 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2095 }
2096 free(client->ch_endpts);
2097 client->ch_endpts = NULL;
2098 client->ch_endpt_count = 0;
2099
2100 ret = 0;
2101 } else {
2102 for (i = 0; i < client->ch_endpt_count; ++i) {
2103 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2104 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2105 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002106
Michal Vasko4f921012016-10-20 14:07:45 +02002107 /* move last endpoint to the empty space */
2108 --client->ch_endpt_count;
2109 if (i < client->ch_endpt_count) {
2110 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2111 } else if (!server_opts.ch_client_count) {
2112 free(server_opts.ch_clients);
2113 server_opts.ch_clients = NULL;
2114 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002115
Michal Vasko4f921012016-10-20 14:07:45 +02002116 ret = 0;
2117 break;
2118 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002119 }
2120 }
2121
2122 /* UNLOCK */
2123 nc_server_ch_client_unlock(client);
2124
2125 return ret;
2126}
2127
2128API int
2129nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2130{
2131 uint16_t i;
2132 int ret = -1;
2133 struct nc_ch_client *client;
2134
2135 if (!client_name) {
2136 ERRARG("client_name");
2137 return -1;
2138 } else if (!endpt_name) {
2139 ERRARG("endpt_name");
2140 return -1;
2141 } else if (!address) {
2142 ERRARG("address");
2143 return -1;
2144 }
2145
2146 /* LOCK */
2147 client = nc_server_ch_client_lock(client_name, 0, NULL);
2148 if (!client) {
2149 return -1;
2150 }
2151
2152 for (i = 0; i < client->ch_endpt_count; ++i) {
2153 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2154 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2155 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2156
2157 ret = 0;
2158 break;
2159 }
2160 }
2161
2162 /* UNLOCK */
2163 nc_server_ch_client_unlock(client);
2164
2165 if (ret == -1) {
2166 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2167 }
2168
2169 return ret;
2170}
2171
2172API int
2173nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2174{
2175 uint16_t i;
2176 int ret = -1;
2177 struct nc_ch_client *client;
2178
2179 if (!client_name) {
2180 ERRARG("client_name");
2181 return -1;
2182 } else if (!endpt_name) {
2183 ERRARG("endpt_name");
2184 return -1;
2185 } else if (!port) {
2186 ERRARG("port");
2187 return -1;
2188 }
2189
2190 /* LOCK */
2191 client = nc_server_ch_client_lock(client_name, 0, NULL);
2192 if (!client) {
2193 return -1;
2194 }
2195
2196 for (i = 0; i < client->ch_endpt_count; ++i) {
2197 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2198 client->ch_endpts[i].port = port;
2199
2200 ret = 0;
2201 break;
2202 }
2203 }
2204
2205 /* UNLOCK */
2206 nc_server_ch_client_unlock(client);
2207
2208 if (ret == -1) {
2209 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2210 }
2211
2212 return ret;
2213}
2214
2215API int
2216nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2217{
2218 struct nc_ch_client *client;
2219
2220 if (!client_name) {
2221 ERRARG("client_name");
2222 return -1;
2223 } else if (!conn_type) {
2224 ERRARG("conn_type");
2225 return -1;
2226 }
2227
2228 /* LOCK */
2229 client = nc_server_ch_client_lock(client_name, 0, NULL);
2230 if (!client) {
2231 return -1;
2232 }
2233
2234 if (client->conn_type != conn_type) {
2235 client->conn_type = conn_type;
2236
2237 /* set default options */
2238 switch (conn_type) {
2239 case NC_CH_PERSIST:
2240 client->conn.persist.idle_timeout = 86400;
2241 client->conn.persist.ka_max_wait = 30;
2242 client->conn.persist.ka_max_attempts = 3;
2243 break;
2244 case NC_CH_PERIOD:
2245 client->conn.period.idle_timeout = 300;
2246 client->conn.period.reconnect_timeout = 60;
2247 break;
2248 default:
2249 ERRINT;
2250 break;
2251 }
2252 }
2253
2254 /* UNLOCK */
2255 nc_server_ch_client_unlock(client);
2256
2257 return 0;
2258}
2259
2260API int
2261nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2262{
2263 struct nc_ch_client *client;
2264
2265 if (!client_name) {
2266 ERRARG("client_name");
2267 return -1;
2268 }
2269
2270 /* LOCK */
2271 client = nc_server_ch_client_lock(client_name, 0, NULL);
2272 if (!client) {
2273 return -1;
2274 }
2275
2276 if (client->conn_type != NC_CH_PERSIST) {
2277 ERR("Call Home client \"%s\" is not of persistent connection type.");
2278 /* UNLOCK */
2279 nc_server_ch_client_unlock(client);
2280 return -1;
2281 }
2282
2283 client->conn.persist.idle_timeout = idle_timeout;
2284
2285 /* UNLOCK */
2286 nc_server_ch_client_unlock(client);
2287
2288 return 0;
2289}
2290
2291API int
2292nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2293{
2294 struct nc_ch_client *client;
2295
2296 if (!client_name) {
2297 ERRARG("client_name");
2298 return -1;
2299 } else if (!max_wait) {
2300 ERRARG("max_wait");
2301 return -1;
2302 }
2303
2304 /* LOCK */
2305 client = nc_server_ch_client_lock(client_name, 0, NULL);
2306 if (!client) {
2307 return -1;
2308 }
2309
2310 if (client->conn_type != NC_CH_PERSIST) {
2311 ERR("Call Home client \"%s\" is not of persistent connection type.");
2312 /* UNLOCK */
2313 nc_server_ch_client_unlock(client);
2314 return -1;
2315 }
2316
2317 client->conn.persist.ka_max_wait = max_wait;
2318
2319 /* UNLOCK */
2320 nc_server_ch_client_unlock(client);
2321
2322 return 0;
2323}
2324
2325API int
2326nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2327{
2328 struct nc_ch_client *client;
2329
2330 if (!client_name) {
2331 ERRARG("client_name");
2332 return -1;
2333 }
2334
2335 /* LOCK */
2336 client = nc_server_ch_client_lock(client_name, 0, NULL);
2337 if (!client) {
2338 return -1;
2339 }
2340
2341 if (client->conn_type != NC_CH_PERSIST) {
2342 ERR("Call Home client \"%s\" is not of persistent connection type.");
2343 /* UNLOCK */
2344 nc_server_ch_client_unlock(client);
2345 return -1;
2346 }
2347
2348 client->conn.persist.ka_max_attempts = max_attempts;
2349
2350 /* UNLOCK */
2351 nc_server_ch_client_unlock(client);
2352
2353 return 0;
2354}
2355
2356API int
2357nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2358{
2359 struct nc_ch_client *client;
2360
2361 if (!client_name) {
2362 ERRARG("client_name");
2363 return -1;
2364 }
2365
2366 /* LOCK */
2367 client = nc_server_ch_client_lock(client_name, 0, NULL);
2368 if (!client) {
2369 return -1;
2370 }
2371
2372 if (client->conn_type != NC_CH_PERIOD) {
2373 ERR("Call Home client \"%s\" is not of periodic connection type.");
2374 /* UNLOCK */
2375 nc_server_ch_client_unlock(client);
2376 return -1;
2377 }
2378
2379 client->conn.period.idle_timeout = idle_timeout;
2380
2381 /* UNLOCK */
2382 nc_server_ch_client_unlock(client);
2383
2384 return 0;
2385}
2386
2387API int
2388nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2389{
2390 struct nc_ch_client *client;
2391
2392 if (!client_name) {
2393 ERRARG("client_name");
2394 return -1;
2395 } else if (!reconnect_timeout) {
2396 ERRARG("reconnect_timeout");
2397 return -1;
2398 }
2399
2400 /* LOCK */
2401 client = nc_server_ch_client_lock(client_name, 0, NULL);
2402 if (!client) {
2403 return -1;
2404 }
2405
2406 if (client->conn_type != NC_CH_PERIOD) {
2407 ERR("Call Home client \"%s\" is not of periodic connection type.");
2408 /* UNLOCK */
2409 nc_server_ch_client_unlock(client);
2410 return -1;
2411 }
2412
2413 client->conn.period.reconnect_timeout = reconnect_timeout;
2414
2415 /* UNLOCK */
2416 nc_server_ch_client_unlock(client);
2417
2418 return 0;
2419}
2420
2421API int
2422nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2423{
2424 struct nc_ch_client *client;
2425
2426 if (!client_name) {
2427 ERRARG("client_name");
2428 return -1;
2429 }
2430
2431 /* LOCK */
2432 client = nc_server_ch_client_lock(client_name, 0, NULL);
2433 if (!client) {
2434 return -1;
2435 }
2436
2437 client->start_with = start_with;
2438
2439 /* UNLOCK */
2440 nc_server_ch_client_unlock(client);
2441
2442 return 0;
2443}
2444
2445API int
2446nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2447{
2448 struct nc_ch_client *client;
2449
2450 if (!client_name) {
2451 ERRARG("client_name");
2452 return -1;
2453 } else if (!max_attempts) {
2454 ERRARG("max_attempts");
2455 return -1;
2456 }
2457
2458 /* LOCK */
2459 client = nc_server_ch_client_lock(client_name, 0, NULL);
2460 if (!client) {
2461 return -1;
2462 }
2463
2464 client->max_attempts = max_attempts;
2465
2466 /* UNLOCK */
2467 nc_server_ch_client_unlock(client);
2468
2469 return 0;
2470}
2471
2472/* client lock is expected to be held */
2473static NC_MSG_TYPE
2474nc_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 +01002475{
Michal Vasko71090fc2016-05-24 16:37:28 +02002476 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002477 int sock, ret;
2478
Michal Vasko2e6defd2016-10-07 15:48:15 +02002479 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002480 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002481 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002482 }
2483
2484 *session = calloc(1, sizeof **session);
2485 if (!(*session)) {
2486 ERRMEM;
2487 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002488 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002489 }
2490 (*session)->status = NC_STATUS_STARTING;
2491 (*session)->side = NC_SERVER;
2492 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002493 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002494 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2495 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002496
2497 /* transport lock */
2498 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
2499 if (!(*session)->ti_lock) {
2500 ERRMEM;
2501 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002502 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002503 goto fail;
2504 }
2505 pthread_mutex_init((*session)->ti_lock, NULL);
2506
2507 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002508#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002509 if (client->ti == NC_TI_LIBSSH) {
2510 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002511 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002512 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002513
Michal Vasko71090fc2016-05-24 16:37:28 +02002514 if (ret < 0) {
2515 msgtype = NC_MSG_ERROR;
2516 goto fail;
2517 } else if (!ret) {
2518 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002519 goto fail;
2520 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002521 } else
2522#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002523#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002524 if (client->ti == NC_TI_OPENSSL) {
2525 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002526 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002527 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002528
Michal Vasko71090fc2016-05-24 16:37:28 +02002529 if (ret < 0) {
2530 msgtype = NC_MSG_ERROR;
2531 goto fail;
2532 } else if (!ret) {
2533 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002534 goto fail;
2535 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002536 } else
2537#endif
2538 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002539 ERRINT;
2540 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002541 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002542 goto fail;
2543 }
2544
2545 /* assign new SID atomically */
2546 /* LOCK */
2547 pthread_spin_lock(&server_opts.sid_lock);
2548 (*session)->id = server_opts.new_session_id++;
2549 /* UNLOCK */
2550 pthread_spin_unlock(&server_opts.sid_lock);
2551
2552 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002553 msgtype = nc_handshake(*session);
2554 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002555 goto fail;
2556 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002557 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002558 (*session)->status = NC_STATUS_RUNNING;
2559
Michal Vasko71090fc2016-05-24 16:37:28 +02002560 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002561
2562fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002563 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002564 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002565 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002566}
2567
Michal Vasko2e6defd2016-10-07 15:48:15 +02002568/* ms */
2569#define NC_CH_NO_ENDPT_WAIT 1000
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002570#define NC_CH_ENDPT_FAIL_WAIT 1000
Michal Vasko2e6defd2016-10-07 15:48:15 +02002571
2572struct nc_ch_client_thread_arg {
2573 char *client_name;
2574 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2575};
2576
2577static struct nc_ch_client *
2578nc_server_ch_client_with_endpt_lock(const char *name)
2579{
2580 struct nc_ch_client *client;
2581
2582 while (1) {
2583 /* LOCK */
2584 client = nc_server_ch_client_lock(name, 0, NULL);
2585 if (!client) {
2586 return NULL;
2587 }
2588 if (client->ch_endpt_count) {
2589 return client;
2590 }
2591 /* no endpoints defined yet */
2592
2593 /* UNLOCK */
2594 nc_server_ch_client_unlock(client);
2595
2596 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2597 }
2598
2599 return NULL;
2600}
2601
2602static int
2603nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2604{
2605 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002606 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002607 struct timespec ts;
2608 struct nc_ch_client *client;
2609
2610 /* session created, initialize condition */
2611 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2612 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2613 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2614 ERRMEM;
2615 nc_session_free(session, NULL);
2616 return -1;
2617 }
2618 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2619 pthread_cond_init(session->opts.server.ch_cond, NULL);
2620
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002621 session->flags |= NC_SESSION_CALLHOME;
2622
Michal Vasko2e6defd2016-10-07 15:48:15 +02002623 /* CH LOCK */
2624 pthread_mutex_lock(session->opts.server.ch_lock);
2625
2626 /* give the session to the user */
2627 data->session_clb(data->client_name, session);
2628
2629 do {
2630 nc_gettimespec(&ts);
2631 ts.tv_nsec += NC_CH_NO_ENDPT_WAIT * 1000000L;
2632 if (ts.tv_nsec > 1000000000L) {
2633 ts.tv_sec += ts.tv_nsec / 1000000000L;
2634 ts.tv_nsec %= 1000000000L;
2635 }
2636
2637 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2638 if (ret && (ret != ETIMEDOUT)) {
2639 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2640 goto ch_client_remove;
2641 }
2642
2643 /* check whether the client was not removed */
2644 /* LOCK */
2645 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2646 if (!client) {
2647 /* client was removed, finish thread */
2648 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2649 data->client_name);
2650 goto ch_client_remove;
2651 }
2652
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002653 if (client->conn_type == NC_CH_PERSIST) {
2654 /* TODO keep-alives */
2655 idle_timeout = client->conn.persist.idle_timeout;
2656 } else {
2657 idle_timeout = client->conn.period.idle_timeout;
2658 }
2659
2660 /* TODO only for sessions without subscriptions */
2661 if (idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
2662 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2663 session->status = NC_STATUS_INVALID;
2664 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2665 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002666
2667 /* UNLOCK */
2668 nc_server_ch_client_unlock(client);
2669
2670 } while (session->status == NC_STATUS_RUNNING);
2671
2672 /* CH UNLOCK */
2673 pthread_mutex_unlock(session->opts.server.ch_lock);
2674
2675 return 0;
2676
2677ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002678 /* make the session a standard one */
2679 pthread_cond_destroy(session->opts.server.ch_cond);
2680 free(session->opts.server.ch_cond);
2681 session->opts.server.ch_cond = NULL;
2682
2683 session->flags &= ~NC_SESSION_CALLHOME;
2684
Michal Vasko2e6defd2016-10-07 15:48:15 +02002685 /* CH UNLOCK */
2686 pthread_mutex_unlock(session->opts.server.ch_lock);
2687
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002688 pthread_mutex_destroy(session->opts.server.ch_lock);
2689 free(session->opts.server.ch_lock);
2690 session->opts.server.ch_lock = NULL;
2691
Michal Vasko2e6defd2016-10-07 15:48:15 +02002692 return 1;
2693}
2694
2695static void *
2696nc_ch_client_thread(void *arg)
2697{
2698 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2699 NC_MSG_TYPE msgtype;
2700 uint8_t cur_attempts = 0;
2701 uint16_t i;
2702 char *cur_endpt_name;
2703 struct nc_ch_endpt *cur_endpt;
2704 struct nc_session *session;
2705 struct nc_ch_client *client;
2706
2707 /* LOCK */
2708 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2709 if (!client) {
2710 goto cleanup;
2711 }
2712
2713 cur_endpt = &client->ch_endpts[0];
2714 cur_endpt_name = strdup(cur_endpt->name);
2715
Michal Vasko29af44b2016-10-13 10:59:55 +02002716 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002717 while (1) {
2718 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2719
2720 if (msgtype == NC_MSG_HELLO) {
2721 /* UNLOCK */
2722 nc_server_ch_client_unlock(client);
2723
Michal Vasko29af44b2016-10-13 10:59:55 +02002724 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002725 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2726 goto cleanup;
2727 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002728 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002729
2730 /* LOCK */
2731 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2732 if (!client) {
2733 goto cleanup;
2734 }
2735
2736 /* session changed status -> it was disconnected for whatever reason,
2737 * persistent connection immediately tries to reconnect, periodic waits some first */
2738 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002739 /* UNLOCK */
2740 nc_server_ch_client_unlock(client);
2741
2742 /* TODO wake up sometimes to check for new notifications */
2743 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2744
2745 /* LOCK */
2746 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2747 if (!client) {
2748 goto cleanup;
2749 }
2750 }
2751
2752 /* set next endpoint to try */
2753 if (client->start_with == NC_CH_FIRST_LISTED) {
2754 cur_endpt = &client->ch_endpts[0];
2755 free(cur_endpt_name);
2756 cur_endpt_name = strdup(cur_endpt->name);
2757 } /* else we keep the current one */
2758 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002759 /* UNLOCK */
2760 nc_server_ch_client_unlock(client);
2761
Michal Vasko2e6defd2016-10-07 15:48:15 +02002762 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002763 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2764
Michal Vasko6bb116b2016-10-26 13:53:46 +02002765 /* LOCK */
2766 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2767 if (!client) {
2768 goto cleanup;
2769 }
2770
Michal Vasko2e6defd2016-10-07 15:48:15 +02002771 ++cur_attempts;
2772 if (cur_attempts == client->max_attempts) {
2773 for (i = 0; i < client->ch_endpt_count; ++i) {
2774 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2775 break;
2776 }
2777 }
2778 if (i < client->ch_endpt_count - 1) {
2779 /* just go to the next endpoint */
2780 cur_endpt = &client->ch_endpts[i + 1];
2781 free(cur_endpt_name);
2782 cur_endpt_name = strdup(cur_endpt->name);
2783 } else {
2784 /* cur_endpoint was removed or is the last, either way start with the first one */
2785 cur_endpt = &client->ch_endpts[0];
2786 free(cur_endpt_name);
2787 cur_endpt_name = strdup(cur_endpt->name);
2788 }
2789
2790 cur_attempts = 0;
2791 } /* else we keep the current one */
2792 }
2793 }
2794
2795cleanup:
2796 VRB("Call Home client \"%s\" thread exit.", data->client_name);
2797
2798 free(data->client_name);
2799 free(data);
2800 return NULL;
2801}
2802
2803API int
2804nc_connect_ch_client_dispatch(const char *client_name,
2805 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2806 int ret;
2807 pthread_t tid;
2808 struct nc_ch_client_thread_arg *arg;
2809
2810 if (!client_name) {
2811 ERRARG("client_name");
2812 return -1;
2813 } else if (!session_clb) {
2814 ERRARG("session_clb");
2815 return -1;
2816 }
2817
2818 arg = malloc(sizeof *arg);
2819 if (!arg) {
2820 ERRMEM;
2821 return -1;
2822 }
2823 arg->client_name = strdup(client_name);
2824 if (!arg->client_name) {
2825 ERRMEM;
2826 free(arg);
2827 return -1;
2828 }
2829 arg->session_clb = session_clb;
2830
2831 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2832 if (ret) {
2833 ERR("Creating a new thread failed (%s).", strerror(ret));
2834 free(arg->client_name);
2835 free(arg);
2836 return -1;
2837 }
2838 /* the thread now manages arg */
2839
2840 pthread_detach(tid);
2841
2842 return 0;
2843}
2844
Radek Krejci53691be2016-02-22 13:58:37 +01002845#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002846
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002847API time_t
2848nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002849{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002850 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002851 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002852 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002853 }
2854
Michal Vasko2e6defd2016-10-07 15:48:15 +02002855 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002856}