blob: 3c899f8cbf5cef0227310e2596e0c9fb00aed466 [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 Vasko3a715132016-01-21 15:40:31 +0100772 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100773 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100774 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200775 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100776
Michal Vasko428087d2016-01-14 16:04:28 +0100777 free(ps);
778}
779
780API int
781nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
782{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200783 uint8_t q_id;
784
Michal Vasko45e53ae2016-04-07 11:46:03 +0200785 if (!ps) {
786 ERRARG("ps");
787 return -1;
788 } else if (!session) {
789 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100790 return -1;
791 }
792
Michal Vasko48a63ed2016-03-01 09:48:21 +0100793 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200794 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200795 return -1;
796 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100797
Michal Vasko428087d2016-01-14 16:04:28 +0100798 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100799 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
800 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
801 if (!ps->pfds || !ps->sessions) {
802 ERRMEM;
803 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200804 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100805 return -1;
806 }
Michal Vasko428087d2016-01-14 16:04:28 +0100807
808 switch (session->ti_type) {
809 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100810 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100811 break;
812
Radek Krejci53691be2016-02-22 13:58:37 +0100813#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100814 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100815 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100816 break;
817#endif
818
Radek Krejci53691be2016-02-22 13:58:37 +0100819#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100820 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100821 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100822 break;
823#endif
824
825 default:
826 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100827 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200828 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100829 return -1;
830 }
Michal Vasko3a715132016-01-21 15:40:31 +0100831 ps->pfds[ps->session_count - 1].events = POLLIN;
832 ps->pfds[ps->session_count - 1].revents = 0;
833 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100834
Michal Vasko48a63ed2016-03-01 09:48:21 +0100835 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200836 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100837}
838
Michal Vasko48a63ed2016-03-01 09:48:21 +0100839static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100840_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100841{
842 uint16_t i;
843
Radek Krejcid5f978f2016-03-03 13:14:45 +0100844 if (index >= 0) {
845 i = (uint16_t)index;
846 goto remove;
847 }
Michal Vasko428087d2016-01-14 16:04:28 +0100848 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100849 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100850remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100851 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100852 if (i < ps->session_count) {
853 ps->sessions[i] = ps->sessions[ps->session_count];
854 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
855 } else if (!ps->session_count) {
856 free(ps->sessions);
857 ps->sessions = NULL;
858 free(ps->pfds);
859 ps->pfds = NULL;
860 }
Michal Vasko428087d2016-01-14 16:04:28 +0100861 return 0;
862 }
863 }
864
Michal Vaskof0537d82016-01-29 14:42:38 +0100865 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100866}
867
Michal Vasko48a63ed2016-03-01 09:48:21 +0100868API int
869nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
870{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200871 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200872 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100873
Michal Vasko45e53ae2016-04-07 11:46:03 +0200874 if (!ps) {
875 ERRARG("ps");
876 return -1;
877 } else if (!session) {
878 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100879 return -1;
880 }
881
882 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200883 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200884 return -1;
885 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100886
Radek Krejcid5f978f2016-03-03 13:14:45 +0100887 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100888
889 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200890 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100891
Michal Vaskobe86fe32016-04-07 10:43:03 +0200892 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100893}
894
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100895API uint16_t
896nc_ps_session_count(struct nc_pollsession *ps)
897{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200898 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100899 uint16_t count;
900
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100901 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200902 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100903 return 0;
904 }
905
Michal Vasko48a63ed2016-03-01 09:48:21 +0100906 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200907 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200908 return -1;
909 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100910
911 count = ps->session_count;
912
913 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200914 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100915
916 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100917}
918
Michal Vasko71090fc2016-05-24 16:37:28 +0200919/* must be called holding the session lock!
920 * returns: NC_PSPOLL_ERROR,
921 * NC_PSPOLL_BAD_RPC,
922 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
923 * NC_PSPOLL_RPC
924 */
925static int
Radek Krejci93e80222016-10-03 13:34:25 +0200926nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100927{
928 struct lyxml_elem *xml = NULL;
929 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200930 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200931 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100932
Michal Vasko45e53ae2016-04-07 11:46:03 +0200933 if (!session) {
934 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200935 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200936 } else if (!rpc) {
937 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200938 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100939 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100940 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200941 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100942 }
943
944 msgtype = nc_read_msg(session, &xml);
945
946 switch (msgtype) {
947 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200948 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100949 if (!*rpc) {
950 ERRMEM;
951 goto error;
952 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100953
Radek Krejcif93c7d42016-04-06 13:41:15 +0200954 ly_errno = LY_SUCCESS;
Michal Vasko0a5ae9a2016-11-15 11:54:47 +0100955 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100956 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200957 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200958 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200959 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200960 nc_server_reply_free(reply);
961 if (ret == -1) {
962 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200963 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200964 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
965 } else {
966 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100967 }
Michal Vasko428087d2016-01-14 16:04:28 +0100968 (*rpc)->root = xml;
969 break;
970 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100971 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200972 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100973 goto error;
974 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100975 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200976 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100977 goto error;
978 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100979 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200980 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100981 goto error;
982 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200983 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100984 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
985 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200986 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100987 break;
988 }
989
Michal Vasko71090fc2016-05-24 16:37:28 +0200990 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100991
992error:
993 /* cleanup */
994 lyxml_free(server_opts.ctx, xml);
995
Michal Vasko71090fc2016-05-24 16:37:28 +0200996 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100997}
998
fanchanghu966f2de2016-07-21 02:28:57 -0400999API void
1000nc_set_global_rpc_clb(nc_rpc_clb clb)
1001{
1002 global_rpc_clb = clb;
1003}
1004
Radek Krejci93e80222016-10-03 13:34:25 +02001005API NC_MSG_TYPE
1006nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1007{
1008 NC_MSG_TYPE result = NC_MSG_NOTIF;
1009 int ret;
1010
1011 /* check parameters */
1012 if (!session) {
1013 ERRARG("session");
1014 return NC_MSG_ERROR;
1015 } else if (!notif || !notif->tree || !notif->eventtime) {
1016 ERRARG("notif");
1017 return NC_MSG_ERROR;
1018 }
1019
1020 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
1021 ret = nc_timedlock(session->ti_lock, timeout, __func__);
1022 if (ret < 0) {
1023 return NC_MSG_ERROR;
1024 } else if (!ret) {
1025 return NC_MSG_WOULDBLOCK;
1026 }
1027
1028 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1029 if (ret == -1) {
1030 ERR("Session %u: failed to write notification.", session->id);
1031 result = NC_MSG_ERROR;
1032 }
1033 pthread_mutex_unlock(session->ti_lock);
1034
1035 return result;
1036}
1037
Michal Vasko71090fc2016-05-24 16:37:28 +02001038/* must be called holding the session lock!
1039 * returns: NC_PSPOLL_ERROR,
1040 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1041 * NC_PSPOLL_REPLY_ERROR,
1042 * 0
1043 */
1044static int
Radek Krejci93e80222016-10-03 13:34:25 +02001045nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001046{
1047 nc_rpc_clb clb;
1048 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001049 struct lys_node *rpc_act = NULL;
1050 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001051 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001052
Michal Vasko4a827e52016-03-03 10:59:00 +01001053 if (!rpc) {
1054 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001055 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001056 }
1057
Michal Vasko90e8e692016-07-13 12:27:57 +02001058 if (rpc->tree->schema->nodetype == LYS_RPC) {
1059 /* RPC */
1060 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001061 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001062 /* action */
1063 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1064 if (elem->schema->nodetype == LYS_ACTION) {
1065 rpc_act = elem->schema;
1066 break;
1067 }
1068 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001069 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001070 if (!rpc_act) {
1071 ERRINT;
1072 return NC_PSPOLL_ERROR;
1073 }
1074 }
1075
1076 if (!rpc_act->priv) {
1077 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001078 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001079 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001080 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001081 reply = clb(rpc->tree, session);
1082 }
1083
1084 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001085 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001086 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001087 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1088 if (reply->type == NC_RPL_ERROR) {
1089 ret |= NC_PSPOLL_REPLY_ERROR;
1090 }
1091 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001092
Michal Vasko71090fc2016-05-24 16:37:28 +02001093 if (r == -1) {
1094 ERR("Session %u: failed to write reply.", session->id);
1095 ret |= NC_PSPOLL_ERROR;
1096 }
Michal Vasko428087d2016-01-14 16:04:28 +01001097
1098 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1099 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1100 session->status = NC_STATUS_INVALID;
1101 }
1102
Michal Vasko71090fc2016-05-24 16:37:28 +02001103 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001104}
1105
1106API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001107nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001108{
1109 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001110 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +01001111 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001112 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +02001113 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001114 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +01001115
1116 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001117 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001118 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001119 }
1120
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001121 cur_time = time(NULL);
1122
Michal Vasko48a63ed2016-03-01 09:48:21 +01001123 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001124 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001125 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001126 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001127
Michal Vasko428087d2016-01-14 16:04:28 +01001128 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001129 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1130 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001131 ret = NC_PSPOLL_ERROR;
1132 if (session) {
1133 *session = ps->sessions[i];
1134 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001135 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001136 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001137
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001138 /* TODO invalidate only sessions without subscription */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001139 if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME) && server_opts.idle_timeout
1140 && (cur_time >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001141 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1142 ps->sessions[i]->status = NC_STATUS_INVALID;
1143 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001144 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1145 if (session) {
1146 *session = ps->sessions[i];
1147 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001148 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001149 }
1150
Michal Vasko3a715132016-01-21 15:40:31 +01001151 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001152 break;
1153 }
Michal Vasko428087d2016-01-14 16:04:28 +01001154 }
1155
Michal Vaskobd8ef262016-01-20 11:09:27 +01001156 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001157#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001158retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001159#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001160 /* no leftover event */
1161 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001162 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001163 if (ret < 0) {
1164 ERR("Poll failed (%s).", strerror(errno));
1165 ret = NC_PSPOLL_ERROR;
1166 goto finish;
1167 } else if (!ret) {
1168 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001169 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001170 }
Michal Vasko428087d2016-01-14 16:04:28 +01001171 }
1172
Michal Vaskobd8ef262016-01-20 11:09:27 +01001173 /* find the first fd with POLLIN, we don't care if there are more now */
1174 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001175 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001176 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1177 ps->sessions[i]->status = NC_STATUS_INVALID;
1178 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001179 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1180 if (session) {
1181 *session = ps->sessions[i];
1182 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001183 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001184 } else if (ps->pfds[i].revents & POLLERR) {
1185 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1186 ps->sessions[i]->status = NC_STATUS_INVALID;
1187 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001188 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1189 if (session) {
1190 *session = ps->sessions[i];
1191 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001192 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001193 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001194#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001195 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001196 uint16_t j;
1197
Michal Vasko96164bf2016-01-21 15:41:58 +01001198 /* things are not that simple with SSH... */
Michal Vasko625ffa52016-12-08 12:01:56 +01001199 ret = nc_ssh_pollin(ps->sessions[i], timeout, 1);
Michal Vasko96164bf2016-01-21 15:41:58 +01001200
1201 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001202 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001203 for (j = i + 1; j < ps->session_count; ++j) {
1204 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1205 ps->pfds[j].revents = 0;
1206 }
1207 }
1208 }
1209
Michal Vasko71090fc2016-05-24 16:37:28 +02001210 /* SSH message only */
1211 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001212 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001213 if (session) {
1214 *session = ps->sessions[i];
1215 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001216 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001217
1218 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001219 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001220 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001221 if (i == ps->session_count - 1) {
1222 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001223 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001224 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001225 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001226 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001227 }
Michal Vasko8c748832016-02-03 15:32:16 +01001228 /* ... retry polling reasonable time apart ... */
1229 usleep(NC_TIMEOUT_STEP);
1230 if (timeout > 0) {
1231 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001232 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001233 }
1234 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001235 }
1236 /* check other sessions */
1237 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001238 }
1239 }
Radek Krejci53691be2016-02-22 13:58:37 +01001240#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001241
Michal Vaskobd8ef262016-01-20 11:09:27 +01001242 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001243 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001244 break;
1245 }
1246 }
1247
1248 if (i == ps->session_count) {
1249 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001250 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001251 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001252 }
1253
1254 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001255 cur_session = ps->sessions[i];
1256 if (session) {
1257 *session = cur_session;
1258 }
Michal Vasko428087d2016-01-14 16:04:28 +01001259
Michal Vaskobd8ef262016-01-20 11:09:27 +01001260 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal vasko953939c2016-10-04 13:46:20 +02001261 ret = nc_timedlock(cur_session->ti_lock, timeout, __func__);
Michal Vasko71090fc2016-05-24 16:37:28 +02001262 if (ret < 0) {
1263 ret = NC_PSPOLL_ERROR;
1264 goto finish;
1265 } else if (!ret) {
1266 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001267 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001268 }
1269
Radek Krejci93e80222016-10-03 13:34:25 +02001270 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001271 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1272 pthread_mutex_unlock(cur_session->ti_lock);
1273 if (cur_session->status != NC_STATUS_RUNNING) {
1274 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001275 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001276 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001277 }
1278
Michal Vasko2e6defd2016-10-07 15:48:15 +02001279 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001280
Michal Vasko428087d2016-01-14 16:04:28 +01001281 /* process RPC */
Radek Krejci93e80222016-10-03 13:34:25 +02001282 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001283
Michal Vasko71090fc2016-05-24 16:37:28 +02001284 pthread_mutex_unlock(cur_session->ti_lock);
1285 if (cur_session->status != NC_STATUS_RUNNING) {
1286 ret |= NC_PSPOLL_SESSION_TERM;
1287 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1288 ret |= NC_PSPOLL_SESSION_ERROR;
1289 }
Michal Vasko428087d2016-01-14 16:04:28 +01001290 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001291
Michal Vaskoca4a2422016-02-02 12:17:14 +01001292 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001293
Michal Vasko625ffa52016-12-08 12:01:56 +01001294#ifdef NC_ENABLED_SSH
1295 /* is there any data received but not processed? */
1296 if (cur_session->ti_type == NC_TI_LIBSSH) {
1297 /* ignore errors */
1298 if (nc_ssh_pollin(cur_session, 0, 0) == NC_PSPOLL_RPC) {
1299 ps->pfds[i].revents = POLLIN;
1300 ret |= NC_PSPOLL_PENDING;
1301 }
1302 }
1303#endif /* NC_ENABLED_SSH */
1304
Michal Vaskobd8ef262016-01-20 11:09:27 +01001305 /* is there some other socket waiting? */
1306 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001307 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001308 ret |= NC_PSPOLL_PENDING;
1309 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001310 }
1311 }
1312
Michal Vasko48a63ed2016-03-01 09:48:21 +01001313finish:
1314 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001315 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001316 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001317}
1318
Michal Vaskod09eae62016-02-01 10:32:52 +01001319API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001320nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001321{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001322 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001323 uint16_t i;
1324 struct nc_session *session;
1325
Michal Vasko9a25e932016-02-01 10:36:42 +01001326 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001327 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001328 return;
1329 }
1330
Michal Vasko48a63ed2016-03-01 09:48:21 +01001331 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001332 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001333 return;
1334 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001335
Michal Vasko48a63ed2016-03-01 09:48:21 +01001336 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001337 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001338 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001339 }
1340 free(ps->sessions);
1341 ps->sessions = NULL;
1342 free(ps->pfds);
1343 ps->pfds = NULL;
1344 ps->session_count = 0;
1345 } else {
1346 for (i = 0; i < ps->session_count; ) {
1347 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1348 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001349 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001350 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001351 continue;
1352 }
1353
1354 ++i;
1355 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001356 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001357
1358 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001359 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001360}
1361
Radek Krejci53691be2016-02-22 13:58:37 +01001362#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001363
Michal Vaskoe2713da2016-08-22 16:06:40 +02001364API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001365nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001366{
Michal Vasko3031aae2016-01-27 16:07:18 +01001367 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +01001368
Michal Vasko45e53ae2016-04-07 11:46:03 +02001369 if (!name) {
1370 ERRARG("name");
1371 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001372 }
1373
Michal Vasko51e514d2016-02-02 15:51:52 +01001374 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001375 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001376
1377 /* check name uniqueness */
1378 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001379 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001380 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001381 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001382 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001383 return -1;
1384 }
1385 }
1386
Michal Vasko3031aae2016-01-27 16:07:18 +01001387 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001388 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001389 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001390 ERRMEM;
1391 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001392 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001393 return -1;
1394 }
1395 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001396 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001397
Michal Vaskoe2713da2016-08-22 16:06:40 +02001398 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001399 if (!server_opts.binds) {
1400 ERRMEM;
1401 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001402 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001403 return -1;
1404 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001405
Michal Vasko2e6defd2016-10-07 15:48:15 +02001406 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1407 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1408 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001409 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001410
1411 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001412#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001413 case NC_TI_LIBSSH:
1414 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1415 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1416 ERRMEM;
1417 /* WRITE UNLOCK */
1418 pthread_rwlock_unlock(&server_opts.endpt_lock);
1419 return -1;
1420 }
1421 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1422 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1423 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1424 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1425 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001426#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001427#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001428 case NC_TI_OPENSSL:
1429 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1430 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1431 ERRMEM;
1432 /* WRITE UNLOCK */
1433 pthread_rwlock_unlock(&server_opts.endpt_lock);
1434 return -1;
1435 }
1436 break;
1437#endif
1438 default:
1439 ERRINT;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001440 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001441 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001442 return -1;
1443 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001444
Michal Vasko2e6defd2016-10-07 15:48:15 +02001445 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001446
Michal Vasko3031aae2016-01-27 16:07:18 +01001447 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001448 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001449
Michal Vasko9e036d52016-01-08 10:49:26 +01001450 return 0;
1451}
1452
Michal Vasko3031aae2016-01-27 16:07:18 +01001453int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001454nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001455{
1456 struct nc_endpt *endpt;
1457 struct nc_bind *bind = NULL;
1458 uint16_t i;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001459 int sock = -1, set_addr;
Michal Vaskoda514772016-02-01 11:32:01 +01001460
Michal Vasko45e53ae2016-04-07 11:46:03 +02001461 if (!endpt_name) {
1462 ERRARG("endpt_name");
1463 return -1;
1464 } else if ((!address && !port) || (address && port)) {
1465 ERRARG("address and port");
1466 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001467 }
1468
Michal Vaskoe2713da2016-08-22 16:06:40 +02001469 if (address) {
1470 set_addr = 1;
1471 } else {
1472 set_addr = 0;
1473 }
1474
Michal Vasko51e514d2016-02-02 15:51:52 +01001475 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001476 endpt = nc_server_endpt_lock(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001477 if (!endpt) {
1478 return -1;
1479 }
1480
Michal Vaskoe2713da2016-08-22 16:06:40 +02001481 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001482
Michal Vaskoe2713da2016-08-22 16:06:40 +02001483 if (set_addr) {
1484 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001485 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001486 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001487 }
1488
Michal Vaskoe2713da2016-08-22 16:06:40 +02001489 /* we have all the information we need to create a listening socket */
1490 if (address && port) {
1491 /* create new socket, close the old one */
1492 sock = nc_sock_listen(address, port);
1493 if (sock == -1) {
1494 goto fail;
1495 }
1496
1497 if (bind->sock > -1) {
1498 close(bind->sock);
1499 }
1500 bind->sock = sock;
1501 } /* else we are just setting address or port */
1502
1503 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001504 lydict_remove(server_opts.ctx, bind->address);
1505 bind->address = lydict_insert(server_opts.ctx, address, 0);
1506 } else {
1507 bind->port = port;
1508 }
1509
Michal Vaskoe2713da2016-08-22 16:06:40 +02001510 if (sock > -1) {
1511#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001512 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001513#elif defined(NC_ENABLED_SSH)
1514 VRB("Listening on %s:%u for SSH connections.", address, port);
1515#else
1516 VRB("Listening on %s:%u for TLS connections.", address, port);
1517#endif
1518 }
1519
Michal Vasko51e514d2016-02-02 15:51:52 +01001520 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001521 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001522 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001523
1524fail:
1525 /* UNLOCK */
1526 nc_server_endpt_unlock(endpt);
1527 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001528}
1529
Michal Vaskoe2713da2016-08-22 16:06:40 +02001530API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001531nc_server_endpt_set_address(const char *endpt_name, const char *address)
1532{
1533 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1534}
1535
1536API int
1537nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1538{
1539 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1540}
1541
1542API int
Michal Vasko59050372016-11-22 14:33:55 +01001543nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001544{
1545 uint32_t i;
1546 int ret = -1;
1547
Michal Vasko3031aae2016-01-27 16:07:18 +01001548 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001549 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001550
Michal Vasko59050372016-11-22 14:33:55 +01001551 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001552 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001553 for (i = 0; i < server_opts.endpt_count; ++i) {
1554 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001555 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1556 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001557#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001558 case NC_TI_LIBSSH:
1559 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1560 free(server_opts.endpts[i].opts.ssh);
1561 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001562#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001563#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001564 case NC_TI_OPENSSL:
1565 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1566 free(server_opts.endpts[i].opts.tls);
1567 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001568#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001569 default:
1570 ERRINT;
1571 /* won't get here ...*/
1572 break;
1573 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001574 ret = 0;
1575 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001576 free(server_opts.endpts);
1577 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001578
1579 /* remove all binds */
1580 for (i = 0; i < server_opts.endpt_count; ++i) {
1581 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1582 if (server_opts.binds[i].sock > -1) {
1583 close(server_opts.binds[i].sock);
1584 }
1585 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001586 free(server_opts.binds);
1587 server_opts.binds = NULL;
1588
Michal Vasko3031aae2016-01-27 16:07:18 +01001589 server_opts.endpt_count = 0;
1590
Michal Vasko1a38c862016-01-15 15:50:07 +01001591 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001592 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001593 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001594 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001595 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001596 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001597 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1598 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001599#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001600 case NC_TI_LIBSSH:
1601 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1602 free(server_opts.endpts[i].opts.ssh);
1603 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001604#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001605#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001606 case NC_TI_OPENSSL:
1607 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1608 free(server_opts.endpts[i].opts.tls);
1609 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001610#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001611 default:
1612 ERRINT;
1613 break;
1614 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001615
Michal Vaskoe2713da2016-08-22 16:06:40 +02001616 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001617 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1618 if (server_opts.binds[i].sock > -1) {
1619 close(server_opts.binds[i].sock);
1620 }
1621
1622 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001623 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001624 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001625 free(server_opts.binds);
1626 server_opts.binds = NULL;
1627 free(server_opts.endpts);
1628 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001629 } else if (i < server_opts.endpt_count) {
1630 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1631 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001632 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001633
1634 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001635 if (name) {
1636 break;
1637 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001638 }
1639 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001640 }
1641
Michal Vasko3031aae2016-01-27 16:07:18 +01001642 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001643 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001644
Michal Vasko9e036d52016-01-08 10:49:26 +01001645 return ret;
1646}
1647
Michal Vasko71090fc2016-05-24 16:37:28 +02001648API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001649nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001650{
Michal Vasko71090fc2016-05-24 16:37:28 +02001651 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001652 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001653 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001654 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001655
Michal Vasko45e53ae2016-04-07 11:46:03 +02001656 if (!server_opts.ctx) {
1657 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001658 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001659 } else if (!session) {
1660 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001661 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001662 }
1663
Michal Vasko51e514d2016-02-02 15:51:52 +01001664 /* we have to hold WRITE for the whole time, since there is not
1665 * a way of downgrading the lock to READ */
1666 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001667 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001668
1669 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001670 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001671 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001672 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001673 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001674 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001675
Michal Vaskoe2713da2016-08-22 16:06:40 +02001676 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001677 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001678 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001679 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001680 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001681 if (!ret) {
1682 return NC_MSG_WOULDBLOCK;
1683 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001684 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001685 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001686 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001687
Michal Vasko1a38c862016-01-15 15:50:07 +01001688 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001689 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001690 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001691 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001692 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001693 msgtype = NC_MSG_ERROR;
1694 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001695 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001696 (*session)->status = NC_STATUS_STARTING;
1697 (*session)->side = NC_SERVER;
1698 (*session)->ctx = server_opts.ctx;
1699 (*session)->flags = NC_SESSION_SHAREDCTX;
1700 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1701 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001702
1703 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001704 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1705 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001706 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001707 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001708 msgtype = NC_MSG_ERROR;
1709 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001710 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001711 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001712
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001713 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001714#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001715 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1716 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01001717 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001718 if (ret < 0) {
1719 msgtype = NC_MSG_ERROR;
1720 goto cleanup;
1721 } else if (!ret) {
1722 msgtype = NC_MSG_WOULDBLOCK;
1723 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001724 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001725 } else
1726#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001727#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001728 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1729 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01001730 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001731 if (ret < 0) {
1732 msgtype = NC_MSG_ERROR;
1733 goto cleanup;
1734 } else if (!ret) {
1735 msgtype = NC_MSG_WOULDBLOCK;
1736 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001737 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001738 } else
1739#endif
1740 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001741 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001742 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001743 msgtype = NC_MSG_ERROR;
1744 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001745 }
1746
Michal Vasko2cc4c682016-03-01 09:16:48 +01001747 (*session)->data = NULL;
1748
Michal Vasko51e514d2016-02-02 15:51:52 +01001749 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001750 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001751
Michal Vaskob48aa812016-01-18 14:13:09 +01001752 /* assign new SID atomically */
1753 /* LOCK */
1754 pthread_spin_lock(&server_opts.sid_lock);
1755 (*session)->id = server_opts.new_session_id++;
1756 /* UNLOCK */
1757 pthread_spin_unlock(&server_opts.sid_lock);
1758
Michal Vasko9e036d52016-01-08 10:49:26 +01001759 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001760 msgtype = nc_handshake(*session);
1761 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001762 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001763 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001764 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001765 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001766 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001767 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001768
Michal Vasko71090fc2016-05-24 16:37:28 +02001769 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001770
Michal Vasko71090fc2016-05-24 16:37:28 +02001771cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001772 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001773 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001774
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001775 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001776 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001777 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001778}
1779
Michal Vasko2e6defd2016-10-07 15:48:15 +02001780API int
1781nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1782{
1783 uint16_t i;
1784
1785 if (!name) {
1786 ERRARG("name");
1787 return -1;
1788 } else if (!ti) {
1789 ERRARG("ti");
1790 return -1;
1791 }
1792
1793 /* WRITE LOCK */
1794 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1795
1796 /* check name uniqueness */
1797 for (i = 0; i < server_opts.ch_client_count; ++i) {
1798 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1799 ERR("Call Home client \"%s\" already exists.", name);
1800 /* WRITE UNLOCK */
1801 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1802 return -1;
1803 }
1804 }
1805
1806 ++server_opts.ch_client_count;
1807 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1808 if (!server_opts.ch_clients) {
1809 ERRMEM;
1810 /* WRITE UNLOCK */
1811 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1812 return -1;
1813 }
1814 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1815 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001816 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1817 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001818
1819 switch (ti) {
1820#ifdef NC_ENABLED_SSH
1821 case NC_TI_LIBSSH:
1822 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1823 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1824 ERRMEM;
1825 /* WRITE UNLOCK */
1826 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1827 return -1;
1828 }
1829 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1830 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1831 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1832 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1833 break;
1834#endif
1835#ifdef NC_ENABLED_TLS
1836 case NC_TI_OPENSSL:
1837 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1838 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1839 ERRMEM;
1840 /* WRITE UNLOCK */
1841 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1842 return -1;
1843 }
1844 break;
1845#endif
1846 default:
1847 ERRINT;
1848 /* WRITE UNLOCK */
1849 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1850 return -1;
1851 }
1852
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001853 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
1854
Michal Vasko2e6defd2016-10-07 15:48:15 +02001855 /* set CH default options */
1856 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1857 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1858
1859 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1860
1861 /* WRITE UNLOCK */
1862 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1863
1864 return 0;
1865}
1866
1867API int
Michal Vasko59050372016-11-22 14:33:55 +01001868nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001869{
1870 uint16_t i, j;
1871 int ret = -1;
1872
1873 /* WRITE LOCK */
1874 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1875
Michal Vasko59050372016-11-22 14:33:55 +01001876 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001877 /* remove all CH clients */
1878 for (i = 0; i < server_opts.ch_client_count; ++i) {
1879 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1880
1881 /* remove all endpoints */
1882 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1883 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1884 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1885 }
1886 free(server_opts.ch_clients[i].ch_endpts);
1887
1888 switch (server_opts.ch_clients[i].ti) {
1889#ifdef NC_ENABLED_SSH
1890 case NC_TI_LIBSSH:
1891 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1892 free(server_opts.ch_clients[i].opts.ssh);
1893 break;
1894#endif
1895#ifdef NC_ENABLED_TLS
1896 case NC_TI_OPENSSL:
1897 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1898 free(server_opts.ch_clients[i].opts.tls);
1899 break;
1900#endif
1901 default:
1902 ERRINT;
1903 /* won't get here ...*/
1904 break;
1905 }
1906
1907 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1908
1909 ret = 0;
1910 }
1911 free(server_opts.ch_clients);
1912 server_opts.ch_clients = NULL;
1913
1914 server_opts.ch_client_count = 0;
1915
1916 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001917 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001918 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001919 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 +02001920 /* remove endpt */
1921 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1922
1923 switch (server_opts.ch_clients[i].ti) {
1924#ifdef NC_ENABLED_SSH
1925 case NC_TI_LIBSSH:
1926 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1927 free(server_opts.ch_clients[i].opts.ssh);
1928 break;
1929#endif
1930#ifdef NC_ENABLED_TLS
1931 case NC_TI_OPENSSL:
1932 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1933 free(server_opts.ch_clients[i].opts.tls);
1934 break;
1935#endif
1936 default:
1937 ERRINT;
1938 break;
1939 }
1940
1941 /* remove all endpoints */
1942 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1943 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1944 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1945 }
1946 free(server_opts.ch_clients[i].ch_endpts);
1947
1948 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1949
1950 /* move last client and endpoint(s) to the empty space */
1951 --server_opts.ch_client_count;
1952 if (i < server_opts.ch_client_count) {
1953 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
1954 sizeof *server_opts.ch_clients);
1955 } else if (!server_opts.ch_client_count) {
1956 free(server_opts.ch_clients);
1957 server_opts.ch_clients = NULL;
1958 }
1959
1960 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001961 if (name) {
1962 break;
1963 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001964 }
1965 }
1966 }
1967
1968 /* WRITE UNLOCK */
1969 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1970
1971 return ret;
1972}
1973
1974API int
1975nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
1976{
1977 uint16_t i;
1978 struct nc_ch_client *client;
1979
1980 if (!client_name) {
1981 ERRARG("client_name");
1982 return -1;
1983 } else if (!endpt_name) {
1984 ERRARG("endpt_name");
1985 return -1;
1986 }
1987
1988 /* LOCK */
1989 client = nc_server_ch_client_lock(client_name, 0, NULL);
1990 if (!client) {
1991 return -1;
1992 }
1993
1994 for (i = 0; i < client->ch_endpt_count; ++i) {
1995 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
1996 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
1997 /* UNLOCK */
1998 nc_server_ch_client_unlock(client);
1999 return -1;
2000 }
2001 }
2002
2003 ++client->ch_endpt_count;
2004 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2005 if (!client->ch_endpts) {
2006 ERRMEM;
2007 /* UNLOCK */
2008 nc_server_ch_client_unlock(client);
2009 return -1;
2010 }
2011
2012 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2013 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2014 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2015
2016 /* UNLOCK */
2017 nc_server_ch_client_unlock(client);
2018
2019 return 0;
2020}
2021
2022API int
2023nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2024{
2025 uint16_t i;
2026 int ret = -1;
2027 struct nc_ch_client *client;
2028
2029 if (!client_name) {
2030 ERRARG("client_name");
2031 return -1;
2032 }
2033
2034 /* LOCK */
2035 client = nc_server_ch_client_lock(client_name, 0, NULL);
2036 if (!client) {
2037 return -1;
2038 }
2039
2040 if (!endpt_name) {
2041 /* remove all endpoints */
2042 for (i = 0; i < client->ch_endpt_count; ++i) {
2043 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2044 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2045 }
2046 free(client->ch_endpts);
2047 client->ch_endpts = NULL;
2048 client->ch_endpt_count = 0;
2049
2050 ret = 0;
2051 } else {
2052 for (i = 0; i < client->ch_endpt_count; ++i) {
2053 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2054 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2055 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002056
Michal Vasko4f921012016-10-20 14:07:45 +02002057 /* move last endpoint to the empty space */
2058 --client->ch_endpt_count;
2059 if (i < client->ch_endpt_count) {
2060 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2061 } else if (!server_opts.ch_client_count) {
2062 free(server_opts.ch_clients);
2063 server_opts.ch_clients = NULL;
2064 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002065
Michal Vasko4f921012016-10-20 14:07:45 +02002066 ret = 0;
2067 break;
2068 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002069 }
2070 }
2071
2072 /* UNLOCK */
2073 nc_server_ch_client_unlock(client);
2074
2075 return ret;
2076}
2077
2078API int
2079nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2080{
2081 uint16_t i;
2082 int ret = -1;
2083 struct nc_ch_client *client;
2084
2085 if (!client_name) {
2086 ERRARG("client_name");
2087 return -1;
2088 } else if (!endpt_name) {
2089 ERRARG("endpt_name");
2090 return -1;
2091 } else if (!address) {
2092 ERRARG("address");
2093 return -1;
2094 }
2095
2096 /* LOCK */
2097 client = nc_server_ch_client_lock(client_name, 0, NULL);
2098 if (!client) {
2099 return -1;
2100 }
2101
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].address);
2105 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2106
2107 ret = 0;
2108 break;
2109 }
2110 }
2111
2112 /* UNLOCK */
2113 nc_server_ch_client_unlock(client);
2114
2115 if (ret == -1) {
2116 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2117 }
2118
2119 return ret;
2120}
2121
2122API int
2123nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2124{
2125 uint16_t i;
2126 int ret = -1;
2127 struct nc_ch_client *client;
2128
2129 if (!client_name) {
2130 ERRARG("client_name");
2131 return -1;
2132 } else if (!endpt_name) {
2133 ERRARG("endpt_name");
2134 return -1;
2135 } else if (!port) {
2136 ERRARG("port");
2137 return -1;
2138 }
2139
2140 /* LOCK */
2141 client = nc_server_ch_client_lock(client_name, 0, NULL);
2142 if (!client) {
2143 return -1;
2144 }
2145
2146 for (i = 0; i < client->ch_endpt_count; ++i) {
2147 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2148 client->ch_endpts[i].port = port;
2149
2150 ret = 0;
2151 break;
2152 }
2153 }
2154
2155 /* UNLOCK */
2156 nc_server_ch_client_unlock(client);
2157
2158 if (ret == -1) {
2159 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2160 }
2161
2162 return ret;
2163}
2164
2165API int
2166nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2167{
2168 struct nc_ch_client *client;
2169
2170 if (!client_name) {
2171 ERRARG("client_name");
2172 return -1;
2173 } else if (!conn_type) {
2174 ERRARG("conn_type");
2175 return -1;
2176 }
2177
2178 /* LOCK */
2179 client = nc_server_ch_client_lock(client_name, 0, NULL);
2180 if (!client) {
2181 return -1;
2182 }
2183
2184 if (client->conn_type != conn_type) {
2185 client->conn_type = conn_type;
2186
2187 /* set default options */
2188 switch (conn_type) {
2189 case NC_CH_PERSIST:
2190 client->conn.persist.idle_timeout = 86400;
2191 client->conn.persist.ka_max_wait = 30;
2192 client->conn.persist.ka_max_attempts = 3;
2193 break;
2194 case NC_CH_PERIOD:
2195 client->conn.period.idle_timeout = 300;
2196 client->conn.period.reconnect_timeout = 60;
2197 break;
2198 default:
2199 ERRINT;
2200 break;
2201 }
2202 }
2203
2204 /* UNLOCK */
2205 nc_server_ch_client_unlock(client);
2206
2207 return 0;
2208}
2209
2210API int
2211nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2212{
2213 struct nc_ch_client *client;
2214
2215 if (!client_name) {
2216 ERRARG("client_name");
2217 return -1;
2218 }
2219
2220 /* LOCK */
2221 client = nc_server_ch_client_lock(client_name, 0, NULL);
2222 if (!client) {
2223 return -1;
2224 }
2225
2226 if (client->conn_type != NC_CH_PERSIST) {
2227 ERR("Call Home client \"%s\" is not of persistent connection type.");
2228 /* UNLOCK */
2229 nc_server_ch_client_unlock(client);
2230 return -1;
2231 }
2232
2233 client->conn.persist.idle_timeout = idle_timeout;
2234
2235 /* UNLOCK */
2236 nc_server_ch_client_unlock(client);
2237
2238 return 0;
2239}
2240
2241API int
2242nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2243{
2244 struct nc_ch_client *client;
2245
2246 if (!client_name) {
2247 ERRARG("client_name");
2248 return -1;
2249 } else if (!max_wait) {
2250 ERRARG("max_wait");
2251 return -1;
2252 }
2253
2254 /* LOCK */
2255 client = nc_server_ch_client_lock(client_name, 0, NULL);
2256 if (!client) {
2257 return -1;
2258 }
2259
2260 if (client->conn_type != NC_CH_PERSIST) {
2261 ERR("Call Home client \"%s\" is not of persistent connection type.");
2262 /* UNLOCK */
2263 nc_server_ch_client_unlock(client);
2264 return -1;
2265 }
2266
2267 client->conn.persist.ka_max_wait = max_wait;
2268
2269 /* UNLOCK */
2270 nc_server_ch_client_unlock(client);
2271
2272 return 0;
2273}
2274
2275API int
2276nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2277{
2278 struct nc_ch_client *client;
2279
2280 if (!client_name) {
2281 ERRARG("client_name");
2282 return -1;
2283 }
2284
2285 /* LOCK */
2286 client = nc_server_ch_client_lock(client_name, 0, NULL);
2287 if (!client) {
2288 return -1;
2289 }
2290
2291 if (client->conn_type != NC_CH_PERSIST) {
2292 ERR("Call Home client \"%s\" is not of persistent connection type.");
2293 /* UNLOCK */
2294 nc_server_ch_client_unlock(client);
2295 return -1;
2296 }
2297
2298 client->conn.persist.ka_max_attempts = max_attempts;
2299
2300 /* UNLOCK */
2301 nc_server_ch_client_unlock(client);
2302
2303 return 0;
2304}
2305
2306API int
2307nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2308{
2309 struct nc_ch_client *client;
2310
2311 if (!client_name) {
2312 ERRARG("client_name");
2313 return -1;
2314 }
2315
2316 /* LOCK */
2317 client = nc_server_ch_client_lock(client_name, 0, NULL);
2318 if (!client) {
2319 return -1;
2320 }
2321
2322 if (client->conn_type != NC_CH_PERIOD) {
2323 ERR("Call Home client \"%s\" is not of periodic connection type.");
2324 /* UNLOCK */
2325 nc_server_ch_client_unlock(client);
2326 return -1;
2327 }
2328
2329 client->conn.period.idle_timeout = idle_timeout;
2330
2331 /* UNLOCK */
2332 nc_server_ch_client_unlock(client);
2333
2334 return 0;
2335}
2336
2337API int
2338nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2339{
2340 struct nc_ch_client *client;
2341
2342 if (!client_name) {
2343 ERRARG("client_name");
2344 return -1;
2345 } else if (!reconnect_timeout) {
2346 ERRARG("reconnect_timeout");
2347 return -1;
2348 }
2349
2350 /* LOCK */
2351 client = nc_server_ch_client_lock(client_name, 0, NULL);
2352 if (!client) {
2353 return -1;
2354 }
2355
2356 if (client->conn_type != NC_CH_PERIOD) {
2357 ERR("Call Home client \"%s\" is not of periodic connection type.");
2358 /* UNLOCK */
2359 nc_server_ch_client_unlock(client);
2360 return -1;
2361 }
2362
2363 client->conn.period.reconnect_timeout = reconnect_timeout;
2364
2365 /* UNLOCK */
2366 nc_server_ch_client_unlock(client);
2367
2368 return 0;
2369}
2370
2371API int
2372nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2373{
2374 struct nc_ch_client *client;
2375
2376 if (!client_name) {
2377 ERRARG("client_name");
2378 return -1;
2379 }
2380
2381 /* LOCK */
2382 client = nc_server_ch_client_lock(client_name, 0, NULL);
2383 if (!client) {
2384 return -1;
2385 }
2386
2387 client->start_with = start_with;
2388
2389 /* UNLOCK */
2390 nc_server_ch_client_unlock(client);
2391
2392 return 0;
2393}
2394
2395API int
2396nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2397{
2398 struct nc_ch_client *client;
2399
2400 if (!client_name) {
2401 ERRARG("client_name");
2402 return -1;
2403 } else if (!max_attempts) {
2404 ERRARG("max_attempts");
2405 return -1;
2406 }
2407
2408 /* LOCK */
2409 client = nc_server_ch_client_lock(client_name, 0, NULL);
2410 if (!client) {
2411 return -1;
2412 }
2413
2414 client->max_attempts = max_attempts;
2415
2416 /* UNLOCK */
2417 nc_server_ch_client_unlock(client);
2418
2419 return 0;
2420}
2421
2422/* client lock is expected to be held */
2423static NC_MSG_TYPE
2424nc_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 +01002425{
Michal Vasko71090fc2016-05-24 16:37:28 +02002426 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002427 int sock, ret;
2428
Michal Vasko2e6defd2016-10-07 15:48:15 +02002429 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002430 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002431 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002432 }
2433
2434 *session = calloc(1, sizeof **session);
2435 if (!(*session)) {
2436 ERRMEM;
2437 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002438 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002439 }
2440 (*session)->status = NC_STATUS_STARTING;
2441 (*session)->side = NC_SERVER;
2442 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002443 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002444 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2445 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002446
2447 /* transport lock */
2448 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
2449 if (!(*session)->ti_lock) {
2450 ERRMEM;
2451 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002452 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002453 goto fail;
2454 }
2455 pthread_mutex_init((*session)->ti_lock, NULL);
2456
2457 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002458#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002459 if (client->ti == NC_TI_LIBSSH) {
2460 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002461 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002462 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002463
Michal Vasko71090fc2016-05-24 16:37:28 +02002464 if (ret < 0) {
2465 msgtype = NC_MSG_ERROR;
2466 goto fail;
2467 } else if (!ret) {
2468 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002469 goto fail;
2470 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002471 } else
2472#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002473#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002474 if (client->ti == NC_TI_OPENSSL) {
2475 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002476 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002477 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002478
Michal Vasko71090fc2016-05-24 16:37:28 +02002479 if (ret < 0) {
2480 msgtype = NC_MSG_ERROR;
2481 goto fail;
2482 } else if (!ret) {
2483 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002484 goto fail;
2485 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002486 } else
2487#endif
2488 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002489 ERRINT;
2490 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002491 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002492 goto fail;
2493 }
2494
2495 /* assign new SID atomically */
2496 /* LOCK */
2497 pthread_spin_lock(&server_opts.sid_lock);
2498 (*session)->id = server_opts.new_session_id++;
2499 /* UNLOCK */
2500 pthread_spin_unlock(&server_opts.sid_lock);
2501
2502 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002503 msgtype = nc_handshake(*session);
2504 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002505 goto fail;
2506 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002507 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002508 (*session)->status = NC_STATUS_RUNNING;
2509
Michal Vasko71090fc2016-05-24 16:37:28 +02002510 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002511
2512fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002513 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002514 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002515 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002516}
2517
Michal Vasko2e6defd2016-10-07 15:48:15 +02002518/* ms */
2519#define NC_CH_NO_ENDPT_WAIT 1000
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002520#define NC_CH_ENDPT_FAIL_WAIT 1000
Michal Vasko2e6defd2016-10-07 15:48:15 +02002521
2522struct nc_ch_client_thread_arg {
2523 char *client_name;
2524 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2525};
2526
2527static struct nc_ch_client *
2528nc_server_ch_client_with_endpt_lock(const char *name)
2529{
2530 struct nc_ch_client *client;
2531
2532 while (1) {
2533 /* LOCK */
2534 client = nc_server_ch_client_lock(name, 0, NULL);
2535 if (!client) {
2536 return NULL;
2537 }
2538 if (client->ch_endpt_count) {
2539 return client;
2540 }
2541 /* no endpoints defined yet */
2542
2543 /* UNLOCK */
2544 nc_server_ch_client_unlock(client);
2545
2546 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2547 }
2548
2549 return NULL;
2550}
2551
2552static int
2553nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2554{
2555 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002556 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002557 struct timespec ts;
2558 struct nc_ch_client *client;
2559
2560 /* session created, initialize condition */
2561 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2562 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2563 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2564 ERRMEM;
2565 nc_session_free(session, NULL);
2566 return -1;
2567 }
2568 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2569 pthread_cond_init(session->opts.server.ch_cond, NULL);
2570
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002571 session->flags |= NC_SESSION_CALLHOME;
2572
Michal Vasko2e6defd2016-10-07 15:48:15 +02002573 /* CH LOCK */
2574 pthread_mutex_lock(session->opts.server.ch_lock);
2575
2576 /* give the session to the user */
2577 data->session_clb(data->client_name, session);
2578
2579 do {
2580 nc_gettimespec(&ts);
2581 ts.tv_nsec += NC_CH_NO_ENDPT_WAIT * 1000000L;
2582 if (ts.tv_nsec > 1000000000L) {
2583 ts.tv_sec += ts.tv_nsec / 1000000000L;
2584 ts.tv_nsec %= 1000000000L;
2585 }
2586
2587 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2588 if (ret && (ret != ETIMEDOUT)) {
2589 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2590 goto ch_client_remove;
2591 }
2592
2593 /* check whether the client was not removed */
2594 /* LOCK */
2595 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2596 if (!client) {
2597 /* client was removed, finish thread */
2598 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2599 data->client_name);
2600 goto ch_client_remove;
2601 }
2602
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002603 if (client->conn_type == NC_CH_PERSIST) {
2604 /* TODO keep-alives */
2605 idle_timeout = client->conn.persist.idle_timeout;
2606 } else {
2607 idle_timeout = client->conn.period.idle_timeout;
2608 }
2609
2610 /* TODO only for sessions without subscriptions */
2611 if (idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
2612 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2613 session->status = NC_STATUS_INVALID;
2614 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2615 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002616
2617 /* UNLOCK */
2618 nc_server_ch_client_unlock(client);
2619
2620 } while (session->status == NC_STATUS_RUNNING);
2621
2622 /* CH UNLOCK */
2623 pthread_mutex_unlock(session->opts.server.ch_lock);
2624
2625 return 0;
2626
2627ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002628 /* make the session a standard one */
2629 pthread_cond_destroy(session->opts.server.ch_cond);
2630 free(session->opts.server.ch_cond);
2631 session->opts.server.ch_cond = NULL;
2632
2633 session->flags &= ~NC_SESSION_CALLHOME;
2634
Michal Vasko2e6defd2016-10-07 15:48:15 +02002635 /* CH UNLOCK */
2636 pthread_mutex_unlock(session->opts.server.ch_lock);
2637
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002638 pthread_mutex_destroy(session->opts.server.ch_lock);
2639 free(session->opts.server.ch_lock);
2640 session->opts.server.ch_lock = NULL;
2641
Michal Vasko2e6defd2016-10-07 15:48:15 +02002642 return 1;
2643}
2644
2645static void *
2646nc_ch_client_thread(void *arg)
2647{
2648 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2649 NC_MSG_TYPE msgtype;
2650 uint8_t cur_attempts = 0;
2651 uint16_t i;
2652 char *cur_endpt_name;
2653 struct nc_ch_endpt *cur_endpt;
2654 struct nc_session *session;
2655 struct nc_ch_client *client;
2656
2657 /* LOCK */
2658 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2659 if (!client) {
2660 goto cleanup;
2661 }
2662
2663 cur_endpt = &client->ch_endpts[0];
2664 cur_endpt_name = strdup(cur_endpt->name);
2665
Michal Vasko29af44b2016-10-13 10:59:55 +02002666 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002667 while (1) {
2668 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2669
2670 if (msgtype == NC_MSG_HELLO) {
2671 /* UNLOCK */
2672 nc_server_ch_client_unlock(client);
2673
Michal Vasko29af44b2016-10-13 10:59:55 +02002674 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002675 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2676 goto cleanup;
2677 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002678 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002679
2680 /* LOCK */
2681 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2682 if (!client) {
2683 goto cleanup;
2684 }
2685
2686 /* session changed status -> it was disconnected for whatever reason,
2687 * persistent connection immediately tries to reconnect, periodic waits some first */
2688 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002689 /* UNLOCK */
2690 nc_server_ch_client_unlock(client);
2691
2692 /* TODO wake up sometimes to check for new notifications */
2693 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2694
2695 /* LOCK */
2696 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2697 if (!client) {
2698 goto cleanup;
2699 }
2700 }
2701
2702 /* set next endpoint to try */
2703 if (client->start_with == NC_CH_FIRST_LISTED) {
2704 cur_endpt = &client->ch_endpts[0];
2705 free(cur_endpt_name);
2706 cur_endpt_name = strdup(cur_endpt->name);
2707 } /* else we keep the current one */
2708 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002709 /* UNLOCK */
2710 nc_server_ch_client_unlock(client);
2711
Michal Vasko2e6defd2016-10-07 15:48:15 +02002712 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002713 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2714
Michal Vasko6bb116b2016-10-26 13:53:46 +02002715 /* LOCK */
2716 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2717 if (!client) {
2718 goto cleanup;
2719 }
2720
Michal Vasko2e6defd2016-10-07 15:48:15 +02002721 ++cur_attempts;
2722 if (cur_attempts == client->max_attempts) {
2723 for (i = 0; i < client->ch_endpt_count; ++i) {
2724 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2725 break;
2726 }
2727 }
2728 if (i < client->ch_endpt_count - 1) {
2729 /* just go to the next endpoint */
2730 cur_endpt = &client->ch_endpts[i + 1];
2731 free(cur_endpt_name);
2732 cur_endpt_name = strdup(cur_endpt->name);
2733 } else {
2734 /* cur_endpoint was removed or is the last, either way start with the first one */
2735 cur_endpt = &client->ch_endpts[0];
2736 free(cur_endpt_name);
2737 cur_endpt_name = strdup(cur_endpt->name);
2738 }
2739
2740 cur_attempts = 0;
2741 } /* else we keep the current one */
2742 }
2743 }
2744
2745cleanup:
2746 VRB("Call Home client \"%s\" thread exit.", data->client_name);
2747
2748 free(data->client_name);
2749 free(data);
2750 return NULL;
2751}
2752
2753API int
2754nc_connect_ch_client_dispatch(const char *client_name,
2755 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2756 int ret;
2757 pthread_t tid;
2758 struct nc_ch_client_thread_arg *arg;
2759
2760 if (!client_name) {
2761 ERRARG("client_name");
2762 return -1;
2763 } else if (!session_clb) {
2764 ERRARG("session_clb");
2765 return -1;
2766 }
2767
2768 arg = malloc(sizeof *arg);
2769 if (!arg) {
2770 ERRMEM;
2771 return -1;
2772 }
2773 arg->client_name = strdup(client_name);
2774 if (!arg->client_name) {
2775 ERRMEM;
2776 free(arg);
2777 return -1;
2778 }
2779 arg->session_clb = session_clb;
2780
2781 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2782 if (ret) {
2783 ERR("Creating a new thread failed (%s).", strerror(ret));
2784 free(arg->client_name);
2785 free(arg);
2786 return -1;
2787 }
2788 /* the thread now manages arg */
2789
2790 pthread_detach(tid);
2791
2792 return 0;
2793}
2794
Radek Krejci53691be2016-02-22 13:58:37 +01002795#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002796
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002797API time_t
2798nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002799{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002800 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002801 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002802 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002803 }
2804
Michal Vasko2e6defd2016-10-07 15:48:15 +02002805 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002806}