blob: 2b3902cfe9a05f68a8069c362f4ee5d8c519c7a3 [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{
Michal Vaskoac2f6182017-01-30 14:32:03 +0100220 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100221 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 Vaskoac2f6182017-01-30 14:32:03 +0100232 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200233 if (binds[i].sock < 0) {
234 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200235 continue;
236 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200237 if (binds[i].pollin) {
238 binds[i].pollin = 0;
239 /* leftover pollin */
240 sock = binds[i].sock;
241 break;
242 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100243 pfd[pfd_count].fd = binds[i].sock;
244 pfd[pfd_count].events = POLLIN;
245 pfd[pfd_count].revents = 0;
246
247 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100248 }
249
Michal Vasko0a3f3752016-10-13 14:58:38 +0200250 if (sock == -1) {
251 /* poll for a new connection */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100252 ret = poll(pfd, pfd_count, timeout);
Michal Vasko0a3f3752016-10-13 14:58:38 +0200253 if (!ret) {
254 /* we timeouted */
255 free(pfd);
256 return 0;
257 } else if (ret == -1) {
258 ERR("Poll failed (%s).", strerror(errno));
259 free(pfd);
260 return -1;
261 }
Michal Vasko086311b2016-01-08 09:53:11 +0100262
Michal Vaskoac2f6182017-01-30 14:32:03 +0100263 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
264 /* adjust i so that indices in binds and pfd always match */
265 while (binds[i].sock != pfd[j].fd) {
266 ++i;
267 }
268
269 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200270 --ret;
271
272 if (!ret) {
273 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100274 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200275 break;
276 } else {
277 /* just remember the event for next time */
278 binds[i].pollin = 1;
279 }
280 }
Michal Vasko086311b2016-01-08 09:53:11 +0100281 }
282 }
283 free(pfd);
284
285 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100286 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100287 return -1;
288 }
289
290 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100291 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100292 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100293 return -1;
294 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200295 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100296
Michal Vasko0190bc32016-03-02 15:47:49 +0100297 /* make the socket non-blocking */
298 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
299 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100300 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100301 return -1;
302 }
303
Michal Vasko3031aae2016-01-27 16:07:18 +0100304 if (idx) {
305 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100306 }
307
Michal Vasko086311b2016-01-08 09:53:11 +0100308 /* host was requested */
309 if (host) {
310 if (saddr.ss_family == AF_INET) {
311 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100312 if (*host) {
313 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
314 ERR("inet_ntop failed (%s).", strerror(errno));
315 free(*host);
316 *host = NULL;
317 }
Michal Vasko086311b2016-01-08 09:53:11 +0100318
Michal Vasko4eb3c312016-03-01 14:09:37 +0100319 if (port) {
320 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
321 }
322 } else {
323 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100324 }
325 } else if (saddr.ss_family == AF_INET6) {
326 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100327 if (*host) {
328 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
329 ERR("inet_ntop failed (%s).", strerror(errno));
330 free(*host);
331 *host = NULL;
332 }
Michal Vasko086311b2016-01-08 09:53:11 +0100333
Michal Vasko4eb3c312016-03-01 14:09:37 +0100334 if (port) {
335 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
336 }
337 } else {
338 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100339 }
340 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100341 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100342 }
343 }
344
345 return ret;
346}
347
Michal Vasko05ba9df2016-01-13 14:40:27 +0100348static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100349nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350{
351 const char *identifier = NULL, *version = NULL, *format = NULL;
352 char *model_data = NULL;
353 const struct lys_module *module;
354 struct nc_server_error *err;
355 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100356 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100357
358 LY_TREE_FOR(rpc->child, child) {
359 if (!strcmp(child->schema->name, "identifier")) {
360 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
361 } else if (!strcmp(child->schema->name, "version")) {
362 version = ((struct lyd_node_leaf_list *)child)->value_str;
363 } else if (!strcmp(child->schema->name, "format")) {
364 format = ((struct lyd_node_leaf_list *)child)->value_str;
365 }
366 }
367
368 /* check version */
369 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100370 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
371 nc_err_set_msg(err, "The requested version is not supported.", "en");
372 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100373 }
374
375 /* check and get module with the name identifier */
376 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
377 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200378 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
379 }
380 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100381 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
382 nc_err_set_msg(err, "The requested schema was not found.", "en");
383 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 }
385
386 /* check format */
Radek Krejci89c34452016-12-07 15:59:45 +0100387 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100388 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
Radek Krejci89c34452016-12-07 15:59:45 +0100389 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100390 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
391 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100392 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
393 nc_err_set_msg(err, "The requested format is not supported.", "en");
394 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100395 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200396 if (!model_data) {
397 ERRINT;
398 return NULL;
399 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100400
Michal Vasko303245c2016-03-24 15:20:03 +0100401 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200402 if (!sdata) {
403 ERRINT;
404 free(model_data);
405 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100406 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200407
Radek Krejci539efb62016-08-24 15:05:16 +0200408 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
409 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100410 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100411 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200412 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100413 return NULL;
414 }
415
Radek Krejci36dfdb32016-09-01 16:56:35 +0200416 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100417}
418
419static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100420nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100421{
Michal Vasko428087d2016-01-14 16:04:28 +0100422 session->term_reason = NC_SESSION_TERM_CLOSED;
423 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100424}
425
Michal Vasko086311b2016-01-08 09:53:11 +0100426API int
427nc_server_init(struct ly_ctx *ctx)
428{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100429 const struct lys_node *rpc;
430
Michal Vasko086311b2016-01-08 09:53:11 +0100431 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200432 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100433 return -1;
434 }
435
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100436 nc_init();
437
Michal Vasko05ba9df2016-01-13 14:40:27 +0100438 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100439 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
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_get_schema);
442 }
443
444 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100445 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100446 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100447 lys_set_private(rpc, nc_clb_default_close_session);
448 }
449
Michal Vasko086311b2016-01-08 09:53:11 +0100450 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100451
452 server_opts.new_session_id = 1;
453 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
454
Michal Vasko086311b2016-01-08 09:53:11 +0100455 return 0;
456}
457
Michal Vaskob48aa812016-01-18 14:13:09 +0100458API void
459nc_server_destroy(void)
460{
Radek Krejci658782b2016-12-04 22:04:55 +0100461 unsigned int i;
462
463 for (i = 0; i < server_opts.capabilities_count; i++) {
464 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
465 }
466 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100467 pthread_spin_destroy(&server_opts.sid_lock);
468
Radek Krejci53691be2016-02-22 13:58:37 +0100469#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100470 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100471#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100472#ifdef NC_ENABLED_SSH
473 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100474
475 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
476 server_opts.hostkey_data_free(server_opts.hostkey_data);
477 }
478#endif
479#ifdef NC_ENABLED_TLS
480 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
481 server_opts.server_cert_data_free(server_opts.server_cert_data);
482 }
483 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
484 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
485 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100486#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100487 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100488}
489
Michal Vasko086311b2016-01-08 09:53:11 +0100490API int
491nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
492{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200493 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
494 ERRARG("basic_mode");
495 return -1;
496 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
497 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100498 return -1;
499 }
500
501 server_opts.wd_basic_mode = basic_mode;
502 server_opts.wd_also_supported = also_supported;
503 return 0;
504}
505
Michal Vasko1a38c862016-01-15 15:50:07 +0100506API void
Michal Vasko55f03972016-04-13 08:56:01 +0200507nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
508{
509 if (!basic_mode && !also_supported) {
510 ERRARG("basic_mode and also_supported");
511 return;
512 }
513
514 if (basic_mode) {
515 *basic_mode = server_opts.wd_basic_mode;
516 }
517 if (also_supported) {
518 *also_supported = server_opts.wd_also_supported;
519 }
520}
521
Michal Vasko55f03972016-04-13 08:56:01 +0200522API int
Radek Krejci658782b2016-12-04 22:04:55 +0100523nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200524{
Radek Krejci658782b2016-12-04 22:04:55 +0100525 const char **new;
526
527 if (!value || !value[0]) {
528 ERRARG("value must not be empty");
529 return EXIT_FAILURE;
530 }
531
532 server_opts.capabilities_count++;
533 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
534 if (!new) {
535 ERRMEM;
536 return EXIT_FAILURE;
537 }
538 server_opts.capabilities = new;
539 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
540
541 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200542}
543
Michal Vasko1a38c862016-01-15 15:50:07 +0100544API void
Michal Vasko086311b2016-01-08 09:53:11 +0100545nc_server_set_hello_timeout(uint16_t hello_timeout)
546{
Michal Vasko086311b2016-01-08 09:53:11 +0100547 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100548}
549
Michal Vasko55f03972016-04-13 08:56:01 +0200550API uint16_t
551nc_server_get_hello_timeout(void)
552{
553 return server_opts.hello_timeout;
554}
555
Michal Vasko1a38c862016-01-15 15:50:07 +0100556API void
Michal Vasko086311b2016-01-08 09:53:11 +0100557nc_server_set_idle_timeout(uint16_t idle_timeout)
558{
Michal Vasko086311b2016-01-08 09:53:11 +0100559 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100560}
561
Michal Vasko55f03972016-04-13 08:56:01 +0200562API uint16_t
563nc_server_get_idle_timeout(void)
564{
565 return server_opts.idle_timeout;
566}
567
Michal Vasko71090fc2016-05-24 16:37:28 +0200568API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100569nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100570{
Michal Vasko71090fc2016-05-24 16:37:28 +0200571 NC_MSG_TYPE msgtype;
572
Michal Vasko45e53ae2016-04-07 11:46:03 +0200573 if (!server_opts.ctx) {
574 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200575 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200576 } else if (fdin < 0) {
577 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200578 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200579 } else if (fdout < 0) {
580 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200581 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200582 } else if (!username) {
583 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200584 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200585 } else if (!session) {
586 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200587 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100588 }
589
590 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100591 *session = calloc(1, sizeof **session);
592 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100593 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200594 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100595 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100596 (*session)->status = NC_STATUS_STARTING;
597 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100598
599 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100600 (*session)->ti_type = NC_TI_FD;
601 (*session)->ti.fd.in = fdin;
602 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100603
604 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100605 (*session)->flags = NC_SESSION_SHAREDCTX;
606 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100607
Michal Vaskob48aa812016-01-18 14:13:09 +0100608 /* assign new SID atomically */
609 pthread_spin_lock(&server_opts.sid_lock);
610 (*session)->id = server_opts.new_session_id++;
611 pthread_spin_unlock(&server_opts.sid_lock);
612
Michal Vasko086311b2016-01-08 09:53:11 +0100613 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200614 msgtype = nc_handshake(*session);
615 if (msgtype != NC_MSG_HELLO) {
616 nc_session_free(*session, NULL);
617 *session = NULL;
618 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100619 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200620 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100621 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100622
Michal Vasko71090fc2016-05-24 16:37:28 +0200623 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100624}
Michal Vasko9e036d52016-01-08 10:49:26 +0100625
Michal Vaskob30b99c2016-07-26 11:35:43 +0200626static void
627nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
628{
629 uint8_t i, found = 0;
630
631 for (i = 0; i < ps->queue_len; ++i) {
632 /* idx round buffer adjust */
633 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
634 i = -ps->queue_begin;
635 }
636
637 if (found) {
638 /* move the value back one place */
639 if (ps->queue[ps->queue_begin + i] == id) {
640 /* another equal value, simply cannot be */
641 ERRINT;
642 }
643
644 if (ps->queue_begin + i == 0) {
645 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
646 } else {
647 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
648 }
649 } else if (ps->queue[ps->queue_begin + i] == id) {
650 /* found our id, there can be no more equal valid values */
651 found = 1;
652 }
653 }
654
655 if (!found) {
656 ERRINT;
657 }
658 --ps->queue_len;
659}
660
Michal Vaskof04a52a2016-04-07 10:52:10 +0200661int
Michal Vasko26043172016-07-26 14:08:59 +0200662nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200663{
664 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200665 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200666 struct timespec ts;
667
Radek Krejci7ac16052016-07-15 11:48:18 +0200668 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200669 ts.tv_sec += NC_READ_TIMEOUT;
670
671 /* LOCK */
672 ret = pthread_mutex_timedlock(&ps->lock, &ts);
673 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200674 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200675 return -1;
676 }
677
678 /* get a unique queue value (by adding 1 to the last added value, if any) */
679 if (ps->queue_len) {
680 queue_last = ps->queue_begin + ps->queue_len - 1;
681 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
682 queue_last -= NC_PS_QUEUE_SIZE;
683 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200684 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200685 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200686 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200687 }
688
689 /* add ourselves into the queue */
690 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200691 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200692 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200693 return -1;
694 }
695 ++ps->queue_len;
696 queue_last = ps->queue_begin + ps->queue_len - 1;
697 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
698 queue_last -= NC_PS_QUEUE_SIZE;
699 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200700 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200701
702 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200703 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200704 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200705 ts.tv_sec += NC_READ_TIMEOUT;
706
707 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
708 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200709 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200710 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200711 nc_ps_queue_remove_id(ps, *id);
712 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200713 return -1;
714 }
715 }
716
Michal Vaskobe86fe32016-04-07 10:43:03 +0200717 /* UNLOCK */
718 pthread_mutex_unlock(&ps->lock);
719
720 return 0;
721}
722
Michal Vaskof04a52a2016-04-07 10:52:10 +0200723int
Michal Vasko26043172016-07-26 14:08:59 +0200724nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200725{
726 int ret;
727 struct timespec ts;
728
Radek Krejci7ac16052016-07-15 11:48:18 +0200729 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200730 ts.tv_sec += NC_READ_TIMEOUT;
731
732 /* LOCK */
733 ret = pthread_mutex_timedlock(&ps->lock, &ts);
734 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200735 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200736 ret = -1;
737 }
738
Michal Vaskob30b99c2016-07-26 11:35:43 +0200739 /* we must be the first, it was our turn after all, right? */
740 if (ps->queue[ps->queue_begin] != id) {
741 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200742 /* UNLOCK */
743 if (!ret) {
744 pthread_mutex_unlock(&ps->lock);
745 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200746 return -1;
747 }
748
Michal Vaskobe86fe32016-04-07 10:43:03 +0200749 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200750 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200751
752 /* broadcast to all other threads that the queue moved */
753 pthread_cond_broadcast(&ps->cond);
754
Michal Vaskobe86fe32016-04-07 10:43:03 +0200755 /* UNLOCK */
756 if (!ret) {
757 pthread_mutex_unlock(&ps->lock);
758 }
759
760 return ret;
761}
762
Michal Vasko428087d2016-01-14 16:04:28 +0100763API struct nc_pollsession *
764nc_ps_new(void)
765{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100766 struct nc_pollsession *ps;
767
768 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100769 if (!ps) {
770 ERRMEM;
771 return NULL;
772 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200773 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100774 pthread_mutex_init(&ps->lock, NULL);
775
776 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100777}
778
779API void
780nc_ps_free(struct nc_pollsession *ps)
781{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100782 if (!ps) {
783 return;
784 }
785
Michal Vaskobe86fe32016-04-07 10:43:03 +0200786 if (ps->queue_len) {
787 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
788 }
789
Michal Vasko428087d2016-01-14 16:04:28 +0100790 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100791 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200792 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100793
Michal Vasko428087d2016-01-14 16:04:28 +0100794 free(ps);
795}
796
797API int
798nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
799{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200800 uint8_t q_id;
801
Michal Vasko45e53ae2016-04-07 11:46:03 +0200802 if (!ps) {
803 ERRARG("ps");
804 return -1;
805 } else if (!session) {
806 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100807 return -1;
808 }
809
Michal Vasko48a63ed2016-03-01 09:48:21 +0100810 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200811 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200812 return -1;
813 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100814
Michal Vasko428087d2016-01-14 16:04:28 +0100815 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100816 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko99f251b2017-01-11 11:31:46 +0100817 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100818 ERRMEM;
819 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200820 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100821 return -1;
822 }
Michal Vasko3a715132016-01-21 15:40:31 +0100823 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100824
Michal Vasko48a63ed2016-03-01 09:48:21 +0100825 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200826 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100827}
828
Michal Vasko48a63ed2016-03-01 09:48:21 +0100829static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100830_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100831{
832 uint16_t i;
833
Radek Krejcid5f978f2016-03-03 13:14:45 +0100834 if (index >= 0) {
835 i = (uint16_t)index;
836 goto remove;
837 }
Michal Vasko428087d2016-01-14 16:04:28 +0100838 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100839 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100840remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100841 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100842 if (i < ps->session_count) {
843 ps->sessions[i] = ps->sessions[ps->session_count];
Michal Vasko99f251b2017-01-11 11:31:46 +0100844 if (ps->last_event_session == i) {
845 ps->last_event_session = 0;
846 }
Michal Vasko58005732016-02-02 15:50:52 +0100847 } else if (!ps->session_count) {
848 free(ps->sessions);
849 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100850 }
Michal Vasko428087d2016-01-14 16:04:28 +0100851 return 0;
852 }
853 }
854
Michal Vaskof0537d82016-01-29 14:42:38 +0100855 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100856}
857
Michal Vasko48a63ed2016-03-01 09:48:21 +0100858API int
859nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
860{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200861 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200862 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100863
Michal Vasko45e53ae2016-04-07 11:46:03 +0200864 if (!ps) {
865 ERRARG("ps");
866 return -1;
867 } else if (!session) {
868 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100869 return -1;
870 }
871
872 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200873 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200874 return -1;
875 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100876
Radek Krejcid5f978f2016-03-03 13:14:45 +0100877 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100878
879 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200880 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100881
Michal Vaskobe86fe32016-04-07 10:43:03 +0200882 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100883}
884
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100885API uint16_t
886nc_ps_session_count(struct nc_pollsession *ps)
887{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200888 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100889 uint16_t count;
890
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100891 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200892 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100893 return 0;
894 }
895
Michal Vasko48a63ed2016-03-01 09:48:21 +0100896 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200897 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200898 return -1;
899 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100900
901 count = ps->session_count;
902
903 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200904 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100905
906 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100907}
908
Michal Vasko71090fc2016-05-24 16:37:28 +0200909/* must be called holding the session lock!
910 * returns: NC_PSPOLL_ERROR,
911 * NC_PSPOLL_BAD_RPC,
912 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
913 * NC_PSPOLL_RPC
914 */
915static int
Radek Krejci93e80222016-10-03 13:34:25 +0200916nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100917{
918 struct lyxml_elem *xml = NULL;
919 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200920 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200921 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100922
Michal Vasko45e53ae2016-04-07 11:46:03 +0200923 if (!session) {
924 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200925 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200926 } else if (!rpc) {
927 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200928 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100929 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100930 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200931 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100932 }
933
934 msgtype = nc_read_msg(session, &xml);
935
936 switch (msgtype) {
937 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200938 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100939 if (!*rpc) {
940 ERRMEM;
941 goto error;
942 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100943
Radek Krejcif93c7d42016-04-06 13:41:15 +0200944 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +0100945 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
946 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100947 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200948 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200949 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200950 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200951 nc_server_reply_free(reply);
952 if (ret == -1) {
953 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200954 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200955 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
956 } else {
957 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100958 }
Michal Vasko428087d2016-01-14 16:04:28 +0100959 (*rpc)->root = xml;
960 break;
961 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100962 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200963 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100964 goto error;
965 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100966 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200967 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100968 goto error;
969 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100970 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200971 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100972 goto error;
973 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200974 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100975 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
976 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200977 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100978 break;
979 }
980
Michal Vasko71090fc2016-05-24 16:37:28 +0200981 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100982
983error:
984 /* cleanup */
985 lyxml_free(server_opts.ctx, xml);
986
Michal Vasko71090fc2016-05-24 16:37:28 +0200987 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100988}
989
fanchanghu966f2de2016-07-21 02:28:57 -0400990API void
991nc_set_global_rpc_clb(nc_rpc_clb clb)
992{
993 global_rpc_clb = clb;
994}
995
Radek Krejci93e80222016-10-03 13:34:25 +0200996API NC_MSG_TYPE
997nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
998{
999 NC_MSG_TYPE result = NC_MSG_NOTIF;
1000 int ret;
1001
1002 /* check parameters */
1003 if (!session) {
1004 ERRARG("session");
1005 return NC_MSG_ERROR;
1006 } else if (!notif || !notif->tree || !notif->eventtime) {
1007 ERRARG("notif");
1008 return NC_MSG_ERROR;
1009 }
1010
1011 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
1012 ret = nc_timedlock(session->ti_lock, timeout, __func__);
1013 if (ret < 0) {
1014 return NC_MSG_ERROR;
1015 } else if (!ret) {
1016 return NC_MSG_WOULDBLOCK;
1017 }
1018
1019 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1020 if (ret == -1) {
1021 ERR("Session %u: failed to write notification.", session->id);
1022 result = NC_MSG_ERROR;
1023 }
1024 pthread_mutex_unlock(session->ti_lock);
1025
1026 return result;
1027}
1028
Michal Vasko71090fc2016-05-24 16:37:28 +02001029/* must be called holding the session lock!
1030 * returns: NC_PSPOLL_ERROR,
1031 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1032 * NC_PSPOLL_REPLY_ERROR,
1033 * 0
1034 */
1035static int
Radek Krejci93e80222016-10-03 13:34:25 +02001036nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001037{
1038 nc_rpc_clb clb;
1039 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001040 struct lys_node *rpc_act = NULL;
1041 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001042 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001043
Michal Vasko4a827e52016-03-03 10:59:00 +01001044 if (!rpc) {
1045 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001046 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001047 }
1048
Michal Vasko90e8e692016-07-13 12:27:57 +02001049 if (rpc->tree->schema->nodetype == LYS_RPC) {
1050 /* RPC */
1051 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001052 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001053 /* action */
1054 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1055 if (elem->schema->nodetype == LYS_ACTION) {
1056 rpc_act = elem->schema;
1057 break;
1058 }
1059 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001060 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001061 if (!rpc_act) {
1062 ERRINT;
1063 return NC_PSPOLL_ERROR;
1064 }
1065 }
1066
1067 if (!rpc_act->priv) {
1068 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001069 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001070 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001071 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001072 reply = clb(rpc->tree, session);
1073 }
1074
1075 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001076 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001077 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001078 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1079 if (reply->type == NC_RPL_ERROR) {
1080 ret |= NC_PSPOLL_REPLY_ERROR;
1081 }
1082 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001083
Michal Vasko71090fc2016-05-24 16:37:28 +02001084 if (r == -1) {
1085 ERR("Session %u: failed to write reply.", session->id);
1086 ret |= NC_PSPOLL_ERROR;
1087 }
Michal Vasko428087d2016-01-14 16:04:28 +01001088
1089 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1090 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1091 session->status = NC_STATUS_INVALID;
1092 }
1093
Michal Vasko71090fc2016-05-24 16:37:28 +02001094 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001095}
1096
1097API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001098nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001099{
Michal Vasko99f251b2017-01-11 11:31:46 +01001100 int ret, r, poll_ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001101 uint8_t q_id;
Michal Vasko99f251b2017-01-11 11:31:46 +01001102 uint16_t i, j;
1103 char msg[256];
1104 NC_SESSION_TERM_REASON term_reason;
1105 struct pollfd pfd;
1106 struct timespec begin_ts, cur_ts;
Michal Vasko71090fc2016-05-24 16:37:28 +02001107 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001108 struct nc_server_rpc *rpc = NULL;
Michal Vasko99f251b2017-01-11 11:31:46 +01001109#ifdef NC_ENABLED_SSH
1110 struct nc_session *new;
1111#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001112
1113 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001114 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001115 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001116 }
1117
Michal Vasko99f251b2017-01-11 11:31:46 +01001118 nc_gettimespec(&begin_ts);
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001119
Michal Vasko48a63ed2016-03-01 09:48:21 +01001120 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001121 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001122 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001123 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001124
Michal Vasko99f251b2017-01-11 11:31:46 +01001125 /* check that all session are fine */
Michal Vasko428087d2016-01-14 16:04:28 +01001126 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001127 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1128 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001129 ret = NC_PSPOLL_ERROR;
1130 if (session) {
1131 *session = ps->sessions[i];
1132 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001133 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001134 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001135
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001136 /* TODO invalidate only sessions without subscription */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001137 if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME) && server_opts.idle_timeout
Michal Vasko99f251b2017-01-11 11:31:46 +01001138 && (begin_ts.tv_sec >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001139 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1140 ps->sessions[i]->status = NC_STATUS_INVALID;
1141 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001142 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1143 if (session) {
1144 *session = ps->sessions[i];
1145 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001146 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001147 }
Michal Vasko428087d2016-01-14 16:04:28 +01001148 }
1149
Michal Vasko99f251b2017-01-11 11:31:46 +01001150 /* poll on all the sessions one-by-one */
1151 do {
1152 /* loop from i to j */
1153 if (ps->last_event_session == ps->session_count - 1) {
1154 i = j = 0;
1155 } else {
1156 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001157 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001158 do {
1159 cur_session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +01001160
Michal Vasko99f251b2017-01-11 11:31:46 +01001161 switch (cur_session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +01001162#ifdef NC_ENABLED_SSH
Michal Vasko99f251b2017-01-11 11:31:46 +01001163 case NC_TI_LIBSSH:
1164 r = ssh_channel_poll_timeout(cur_session->ti.libssh.channel, 0, 0);
1165 if (r < 1) {
1166 if (r == SSH_EOF) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +01001167 sprintf(msg, "SSH channel unexpected EOF");
Michal Vasko99f251b2017-01-11 11:31:46 +01001168 term_reason = NC_SESSION_TERM_DROPPED;
1169 poll_ret = -2;
1170 } else if (r == SSH_ERROR) {
1171 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(cur_session->ti.libssh.session));
Michal Vasko76be6752017-01-19 12:14:48 +01001172 term_reason = NC_SESSION_TERM_OTHER;
1173 poll_ret = -2;
Michal Vasko99f251b2017-01-11 11:31:46 +01001174 } else {
1175 poll_ret = 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001176 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001177 break;
Michal Vasko96164bf2016-01-21 15:41:58 +01001178 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001179 /* we have some data, but it may be just an SSH message */
Michal Vasko96164bf2016-01-21 15:41:58 +01001180
Michal Vasko99f251b2017-01-11 11:31:46 +01001181 r = nc_timedlock(cur_session->ti_lock, timeout, __func__);
1182 if (r < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001183 if (session) {
Michal Vasko99f251b2017-01-11 11:31:46 +01001184 *session = cur_session;
Michal Vasko71090fc2016-05-24 16:37:28 +02001185 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001186 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001187 goto finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001188 } else if (!r) {
1189 if (session) {
1190 *session = cur_session;
Michal Vasko428087d2016-01-14 16:04:28 +01001191 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001192 ret = NC_PSPOLL_TIMEOUT;
1193 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001194 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001195 r = ssh_execute_message_callbacks(cur_session->ti.libssh.session);
1196 pthread_mutex_unlock(cur_session->ti_lock);
1197
1198 if (r != SSH_OK) {
1199 sprintf(msg, "failed to receive SSH messages (%s)", ssh_get_error(cur_session->ti.libssh.session));
1200 term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko76be6752017-01-19 12:14:48 +01001201 poll_ret = -2;
Michal Vasko99f251b2017-01-11 11:31:46 +01001202 } else if (cur_session->flags & NC_SESSION_SSH_NEW_MSG) {
1203 /* new SSH message */
1204 cur_session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1205 if (cur_session->ti.libssh.next) {
1206 for (new = cur_session->ti.libssh.next; new != cur_session; new = new->ti.libssh.next) {
1207 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1208 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1209 /* new NETCONF SSH channel */
1210 if (session) {
1211 *session = cur_session;
1212 }
1213 ret = NC_PSPOLL_SSH_CHANNEL;
1214 goto finish;
1215 }
1216 }
1217 }
1218
1219 /* just some SSH message */
1220 if (session) {
1221 *session = cur_session;
1222 }
1223 ret = NC_PSPOLL_SSH_MSG;
1224 goto finish;
1225 } else {
1226 /* we have some application data */
1227 poll_ret = 1;
1228 }
1229 break;
1230#endif
1231#ifdef NC_ENABLED_TLS
1232 case NC_TI_OPENSSL:
1233 r = SSL_pending(cur_session->ti.tls);
1234 if (!r) {
1235 /* no data pending in the SSL buffer, poll fd */
1236 pfd.fd = SSL_get_rfd(cur_session->ti.tls);
1237 if (pfd.fd < 0) {
1238 ERRINT;
1239 ret = NC_PSPOLL_ERROR;
1240 goto finish;
1241 }
1242 pfd.events = POLLIN;
1243 pfd.revents = 0;
1244 r = poll(&pfd, 1, 0);
1245
1246 if (r < 0) {
1247 sprintf(msg, "poll failed (%s)", strerror(errno));
1248 poll_ret = -1;
1249 } else if (r > 0) {
1250 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1251 sprintf(msg, "communication socket unexpectedly closed");
1252 term_reason = NC_SESSION_TERM_DROPPED;
1253 poll_ret = -2;
1254 } else if (pfd.revents & POLLERR) {
1255 sprintf(msg, "communication socket error");
1256 term_reason = NC_SESSION_TERM_OTHER;
1257 poll_ret = -2;
1258 } else {
1259 poll_ret = 1;
1260 }
1261 } else {
1262 poll_ret = 0;
1263 }
1264 } else {
1265 poll_ret = 1;
1266 }
1267 break;
1268#endif
1269 case NC_TI_FD:
1270 pfd.fd = cur_session->ti.fd.in;
1271 pfd.events = POLLIN;
1272 pfd.revents = 0;
1273 r = poll(&pfd, 1, 0);
1274
1275 if (r < 0) {
1276 sprintf(msg, "poll failed (%s)", strerror(errno));
Michal Vasko76be6752017-01-19 12:14:48 +01001277 poll_ret = -1;
Michal Vasko99f251b2017-01-11 11:31:46 +01001278 } else if (r > 0) {
1279 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1280 sprintf(msg, "communication socket unexpectedly closed");
1281 term_reason = NC_SESSION_TERM_DROPPED;
1282 poll_ret = -2;
1283 } else if (pfd.revents & POLLERR) {
1284 sprintf(msg, "communication socket error");
1285 term_reason = NC_SESSION_TERM_OTHER;
1286 poll_ret = -2;
1287 } else {
1288 poll_ret = 1;
1289 }
1290 } else {
1291 poll_ret = 0;
1292 }
1293 break;
1294 case NC_TI_NONE:
1295 ERRINT;
1296 ret = NC_PSPOLL_ERROR;
1297 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001298 }
Michal Vasko428087d2016-01-14 16:04:28 +01001299
Michal Vasko99f251b2017-01-11 11:31:46 +01001300 /* here: poll_ret == -2 - session error, session terminated,
Michal Vasko76be6752017-01-19 12:14:48 +01001301 * poll_ret == -1 - generic error,
1302 * poll_ret == 0 - nothing to read,
1303 * poll_ret > 0 - data available */
Michal Vasko99f251b2017-01-11 11:31:46 +01001304 if (poll_ret == -2) {
1305 ERR("Session %u: %s.", cur_session->id, msg);
1306 cur_session->status = NC_STATUS_INVALID;
1307 cur_session->term_reason = term_reason;
1308 if (session) {
1309 *session = cur_session;
1310 }
1311 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1312 goto finish;
1313 } else if (poll_ret == -1) {
1314 ERR("Session %u: %s.", cur_session->id, msg);
1315 ret = NC_PSPOLL_ERROR;
1316 goto finish;
1317 } else if (poll_ret > 0) {
1318 break;
1319 }
1320
1321 /* next iteration */
1322 if (i == ps->session_count - 1) {
1323 i = 0;
1324 } else {
1325 ++i;
1326 }
1327 } while (i != j);
1328
1329 /* no event */
1330 if (!poll_ret && (timeout > -1)) {
1331 usleep(NC_TIMEOUT_STEP);
1332
1333 nc_gettimespec(&cur_ts);
1334 /* final timeout */
1335 if (nc_difftimespec(&begin_ts, &cur_ts) >= (unsigned)timeout) {
1336 ret = NC_PSPOLL_TIMEOUT;
1337 goto finish;
1338 }
Michal Vasko428087d2016-01-14 16:04:28 +01001339 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001340 } while (!poll_ret);
Michal Vasko428087d2016-01-14 16:04:28 +01001341
1342 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001343 if (session) {
1344 *session = cur_session;
1345 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001346 ps->last_event_session = i;
Michal Vasko428087d2016-01-14 16:04:28 +01001347
Michal Vaskobd8ef262016-01-20 11:09:27 +01001348 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko99f251b2017-01-11 11:31:46 +01001349 r = nc_timedlock(cur_session->ti_lock, timeout, __func__);
1350 if (r < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001351 ret = NC_PSPOLL_ERROR;
1352 goto finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001353 } else if (!r) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001354 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001355 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001356 }
1357
Radek Krejci93e80222016-10-03 13:34:25 +02001358 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001359 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1360 pthread_mutex_unlock(cur_session->ti_lock);
1361 if (cur_session->status != NC_STATUS_RUNNING) {
1362 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001363 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001364 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001365 }
1366
Michal Vasko2e6defd2016-10-07 15:48:15 +02001367 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001368
Michal Vasko428087d2016-01-14 16:04:28 +01001369 /* process RPC */
Radek Krejci93e80222016-10-03 13:34:25 +02001370 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001371
Michal Vasko71090fc2016-05-24 16:37:28 +02001372 pthread_mutex_unlock(cur_session->ti_lock);
1373 if (cur_session->status != NC_STATUS_RUNNING) {
1374 ret |= NC_PSPOLL_SESSION_TERM;
1375 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1376 ret |= NC_PSPOLL_SESSION_ERROR;
1377 }
Michal Vasko428087d2016-01-14 16:04:28 +01001378 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001379
Michal Vaskoca4a2422016-02-02 12:17:14 +01001380 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001381
Michal Vasko48a63ed2016-03-01 09:48:21 +01001382finish:
1383 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001384 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001385 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001386}
1387
Michal Vaskod09eae62016-02-01 10:32:52 +01001388API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001389nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001390{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001391 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001392 uint16_t i;
1393 struct nc_session *session;
1394
Michal Vasko9a25e932016-02-01 10:36:42 +01001395 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001396 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001397 return;
1398 }
1399
Michal Vasko48a63ed2016-03-01 09:48:21 +01001400 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001401 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001402 return;
1403 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001404
Michal Vasko48a63ed2016-03-01 09:48:21 +01001405 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001406 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001407 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001408 }
1409 free(ps->sessions);
1410 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001411 ps->session_count = 0;
Michal Vasko99f251b2017-01-11 11:31:46 +01001412 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001413 } else {
1414 for (i = 0; i < ps->session_count; ) {
1415 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1416 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001417 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001418 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001419 continue;
1420 }
1421
1422 ++i;
1423 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001424 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001425
1426 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001427 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001428}
1429
Radek Krejci53691be2016-02-22 13:58:37 +01001430#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001431
Michal Vaskoe2713da2016-08-22 16:06:40 +02001432API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001433nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001434{
Michal Vasko3031aae2016-01-27 16:07:18 +01001435 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +01001436
Michal Vasko45e53ae2016-04-07 11:46:03 +02001437 if (!name) {
1438 ERRARG("name");
1439 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001440 }
1441
Michal Vasko51e514d2016-02-02 15:51:52 +01001442 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001443 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001444
1445 /* check name uniqueness */
1446 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001447 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001448 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001449 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001450 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001451 return -1;
1452 }
1453 }
1454
Michal Vasko3031aae2016-01-27 16:07:18 +01001455 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001456 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001457 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001458 ERRMEM;
1459 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001460 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001461 return -1;
1462 }
1463 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001464 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001465
Michal Vasko001224d2017-01-30 14:32:28 +01001466 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].lock, NULL);
1467
Michal Vaskoe2713da2016-08-22 16:06:40 +02001468 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001469 if (!server_opts.binds) {
1470 ERRMEM;
1471 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001472 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001473 return -1;
1474 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001475
Michal Vasko2e6defd2016-10-07 15:48:15 +02001476 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1477 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1478 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001479 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001480
1481 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001482#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001483 case NC_TI_LIBSSH:
1484 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1485 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1486 ERRMEM;
1487 /* WRITE UNLOCK */
1488 pthread_rwlock_unlock(&server_opts.endpt_lock);
1489 return -1;
1490 }
1491 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1492 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1493 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1494 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1495 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001496#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001497#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001498 case NC_TI_OPENSSL:
1499 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1500 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1501 ERRMEM;
1502 /* WRITE UNLOCK */
1503 pthread_rwlock_unlock(&server_opts.endpt_lock);
1504 return -1;
1505 }
1506 break;
1507#endif
1508 default:
1509 ERRINT;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001510 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001511 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001512 return -1;
1513 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001514
Michal Vasko3031aae2016-01-27 16:07:18 +01001515 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001516 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001517
Michal Vasko9e036d52016-01-08 10:49:26 +01001518 return 0;
1519}
1520
Michal Vasko3031aae2016-01-27 16:07:18 +01001521int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001522nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001523{
1524 struct nc_endpt *endpt;
1525 struct nc_bind *bind = NULL;
1526 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001527 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001528
Michal Vasko45e53ae2016-04-07 11:46:03 +02001529 if (!endpt_name) {
1530 ERRARG("endpt_name");
1531 return -1;
1532 } else if ((!address && !port) || (address && port)) {
1533 ERRARG("address and port");
1534 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001535 }
1536
Michal Vaskoe2713da2016-08-22 16:06:40 +02001537 if (address) {
1538 set_addr = 1;
1539 } else {
1540 set_addr = 0;
1541 }
1542
Michal Vasko51e514d2016-02-02 15:51:52 +01001543 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001544 endpt = nc_server_endpt_lock(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001545 if (!endpt) {
1546 return -1;
1547 }
1548
Michal Vaskoe2713da2016-08-22 16:06:40 +02001549 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001550
Michal Vaskoe2713da2016-08-22 16:06:40 +02001551 if (set_addr) {
1552 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001553 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001554 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001555 }
1556
Michal Vaskoe2713da2016-08-22 16:06:40 +02001557 /* we have all the information we need to create a listening socket */
1558 if (address && port) {
1559 /* create new socket, close the old one */
1560 sock = nc_sock_listen(address, port);
1561 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001562 ret = -1;
1563 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001564 }
1565
1566 if (bind->sock > -1) {
1567 close(bind->sock);
1568 }
1569 bind->sock = sock;
1570 } /* else we are just setting address or port */
1571
1572 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001573 lydict_remove(server_opts.ctx, bind->address);
1574 bind->address = lydict_insert(server_opts.ctx, address, 0);
1575 } else {
1576 bind->port = port;
1577 }
1578
Michal Vaskoe2713da2016-08-22 16:06:40 +02001579 if (sock > -1) {
1580#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001581 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001582#elif defined(NC_ENABLED_SSH)
1583 VRB("Listening on %s:%u for SSH connections.", address, port);
1584#else
1585 VRB("Listening on %s:%u for TLS connections.", address, port);
1586#endif
1587 }
1588
Michal Vasko4c1fb492017-01-30 14:31:07 +01001589cleanup:
Michal Vasko51e514d2016-02-02 15:51:52 +01001590 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001591 nc_server_endpt_unlock(endpt);
Michal Vasko51e514d2016-02-02 15:51:52 +01001592
Michal Vasko4c1fb492017-01-30 14:31:07 +01001593 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001594}
1595
Michal Vaskoe2713da2016-08-22 16:06:40 +02001596API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001597nc_server_endpt_set_address(const char *endpt_name, const char *address)
1598{
1599 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1600}
1601
1602API int
1603nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1604{
1605 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1606}
1607
1608API int
Michal Vasko59050372016-11-22 14:33:55 +01001609nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001610{
1611 uint32_t i;
1612 int ret = -1;
1613
Michal Vasko3031aae2016-01-27 16:07:18 +01001614 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001615 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001616
Michal Vasko59050372016-11-22 14:33:55 +01001617 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001618 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001619 for (i = 0; i < server_opts.endpt_count; ++i) {
1620 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001621 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1622 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001623#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001624 case NC_TI_LIBSSH:
1625 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1626 free(server_opts.endpts[i].opts.ssh);
1627 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001628#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001629#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001630 case NC_TI_OPENSSL:
1631 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1632 free(server_opts.endpts[i].opts.tls);
1633 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001634#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001635 default:
1636 ERRINT;
1637 /* won't get here ...*/
1638 break;
1639 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001640 ret = 0;
1641 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001642 free(server_opts.endpts);
1643 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001644
1645 /* remove all binds */
1646 for (i = 0; i < server_opts.endpt_count; ++i) {
1647 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1648 if (server_opts.binds[i].sock > -1) {
1649 close(server_opts.binds[i].sock);
1650 }
1651 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001652 free(server_opts.binds);
1653 server_opts.binds = NULL;
1654
Michal Vasko3031aae2016-01-27 16:07:18 +01001655 server_opts.endpt_count = 0;
1656
Michal Vasko1a38c862016-01-15 15:50:07 +01001657 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001658 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001659 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001660 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001661 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001662 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001663 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1664 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001665#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001666 case NC_TI_LIBSSH:
1667 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1668 free(server_opts.endpts[i].opts.ssh);
1669 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001670#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001671#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001672 case NC_TI_OPENSSL:
1673 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1674 free(server_opts.endpts[i].opts.tls);
1675 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001676#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001677 default:
1678 ERRINT;
1679 break;
1680 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001681
Michal Vaskoe2713da2016-08-22 16:06:40 +02001682 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001683 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1684 if (server_opts.binds[i].sock > -1) {
1685 close(server_opts.binds[i].sock);
1686 }
1687
1688 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001689 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001690 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001691 free(server_opts.binds);
1692 server_opts.binds = NULL;
1693 free(server_opts.endpts);
1694 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001695 } else if (i < server_opts.endpt_count) {
1696 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1697 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001698 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001699
1700 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001701 if (name) {
1702 break;
1703 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001704 }
1705 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001706 }
1707
Michal Vasko3031aae2016-01-27 16:07:18 +01001708 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001709 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001710
Michal Vasko9e036d52016-01-08 10:49:26 +01001711 return ret;
1712}
1713
Michal Vasko71090fc2016-05-24 16:37:28 +02001714API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001715nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001716{
Michal Vasko71090fc2016-05-24 16:37:28 +02001717 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001718 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001719 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001720 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001721
Michal Vasko45e53ae2016-04-07 11:46:03 +02001722 if (!server_opts.ctx) {
1723 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001724 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001725 } else if (!session) {
1726 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001727 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001728 }
1729
Michal Vasko51e514d2016-02-02 15:51:52 +01001730 /* we have to hold WRITE for the whole time, since there is not
1731 * a way of downgrading the lock to READ */
1732 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001733 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001734
1735 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001736 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001737 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001738 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001739 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001740 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001741
Michal Vaskoe2713da2016-08-22 16:06:40 +02001742 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001743 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001744 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001745 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001746 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001747 if (!ret) {
1748 return NC_MSG_WOULDBLOCK;
1749 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001750 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001751 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001752 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001753
Michal Vasko1a38c862016-01-15 15:50:07 +01001754 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001755 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001756 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001757 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001758 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001759 msgtype = NC_MSG_ERROR;
1760 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001761 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001762 (*session)->status = NC_STATUS_STARTING;
1763 (*session)->side = NC_SERVER;
1764 (*session)->ctx = server_opts.ctx;
1765 (*session)->flags = NC_SESSION_SHAREDCTX;
1766 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1767 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001768
1769 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001770 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1771 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001772 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001773 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001774 msgtype = NC_MSG_ERROR;
1775 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001776 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001777 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001778
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001779 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001780#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001781 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1782 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01001783 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001784 if (ret < 0) {
1785 msgtype = NC_MSG_ERROR;
1786 goto cleanup;
1787 } else if (!ret) {
1788 msgtype = NC_MSG_WOULDBLOCK;
1789 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001790 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001791 } else
1792#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001793#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001794 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1795 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01001796 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001797 if (ret < 0) {
1798 msgtype = NC_MSG_ERROR;
1799 goto cleanup;
1800 } else if (!ret) {
1801 msgtype = NC_MSG_WOULDBLOCK;
1802 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001803 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001804 } else
1805#endif
1806 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001807 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001808 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001809 msgtype = NC_MSG_ERROR;
1810 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001811 }
1812
Michal Vasko2cc4c682016-03-01 09:16:48 +01001813 (*session)->data = NULL;
1814
Michal Vasko51e514d2016-02-02 15:51:52 +01001815 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001816 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001817
Michal Vaskob48aa812016-01-18 14:13:09 +01001818 /* assign new SID atomically */
1819 /* LOCK */
1820 pthread_spin_lock(&server_opts.sid_lock);
1821 (*session)->id = server_opts.new_session_id++;
1822 /* UNLOCK */
1823 pthread_spin_unlock(&server_opts.sid_lock);
1824
Michal Vasko9e036d52016-01-08 10:49:26 +01001825 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001826 msgtype = nc_handshake(*session);
1827 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001828 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001829 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001830 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001831 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001832 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001833 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001834
Michal Vasko71090fc2016-05-24 16:37:28 +02001835 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001836
Michal Vasko71090fc2016-05-24 16:37:28 +02001837cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001838 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001839 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001840
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001841 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001842 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001843 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001844}
1845
Michal Vasko2e6defd2016-10-07 15:48:15 +02001846API int
1847nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1848{
1849 uint16_t i;
1850
1851 if (!name) {
1852 ERRARG("name");
1853 return -1;
1854 } else if (!ti) {
1855 ERRARG("ti");
1856 return -1;
1857 }
1858
1859 /* WRITE LOCK */
1860 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1861
1862 /* check name uniqueness */
1863 for (i = 0; i < server_opts.ch_client_count; ++i) {
1864 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1865 ERR("Call Home client \"%s\" already exists.", name);
1866 /* WRITE UNLOCK */
1867 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1868 return -1;
1869 }
1870 }
1871
1872 ++server_opts.ch_client_count;
1873 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1874 if (!server_opts.ch_clients) {
1875 ERRMEM;
1876 /* WRITE UNLOCK */
1877 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1878 return -1;
1879 }
1880 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1881 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001882 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1883 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001884
1885 switch (ti) {
1886#ifdef NC_ENABLED_SSH
1887 case NC_TI_LIBSSH:
1888 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1889 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1890 ERRMEM;
1891 /* WRITE UNLOCK */
1892 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1893 return -1;
1894 }
1895 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1896 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1897 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1898 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1899 break;
1900#endif
1901#ifdef NC_ENABLED_TLS
1902 case NC_TI_OPENSSL:
1903 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1904 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1905 ERRMEM;
1906 /* WRITE UNLOCK */
1907 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1908 return -1;
1909 }
1910 break;
1911#endif
1912 default:
1913 ERRINT;
1914 /* WRITE UNLOCK */
1915 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1916 return -1;
1917 }
1918
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001919 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
1920
Michal Vasko2e6defd2016-10-07 15:48:15 +02001921 /* set CH default options */
1922 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1923 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1924
1925 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1926
1927 /* WRITE UNLOCK */
1928 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1929
1930 return 0;
1931}
1932
1933API int
Michal Vasko59050372016-11-22 14:33:55 +01001934nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001935{
1936 uint16_t i, j;
1937 int ret = -1;
1938
1939 /* WRITE LOCK */
1940 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1941
Michal Vasko59050372016-11-22 14:33:55 +01001942 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001943 /* remove all CH clients */
1944 for (i = 0; i < server_opts.ch_client_count; ++i) {
1945 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1946
1947 /* remove all endpoints */
1948 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1949 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1950 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1951 }
1952 free(server_opts.ch_clients[i].ch_endpts);
1953
1954 switch (server_opts.ch_clients[i].ti) {
1955#ifdef NC_ENABLED_SSH
1956 case NC_TI_LIBSSH:
1957 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1958 free(server_opts.ch_clients[i].opts.ssh);
1959 break;
1960#endif
1961#ifdef NC_ENABLED_TLS
1962 case NC_TI_OPENSSL:
1963 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1964 free(server_opts.ch_clients[i].opts.tls);
1965 break;
1966#endif
1967 default:
1968 ERRINT;
1969 /* won't get here ...*/
1970 break;
1971 }
1972
1973 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1974
1975 ret = 0;
1976 }
1977 free(server_opts.ch_clients);
1978 server_opts.ch_clients = NULL;
1979
1980 server_opts.ch_client_count = 0;
1981
1982 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001983 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001984 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001985 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 +02001986 /* remove endpt */
1987 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1988
1989 switch (server_opts.ch_clients[i].ti) {
1990#ifdef NC_ENABLED_SSH
1991 case NC_TI_LIBSSH:
1992 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1993 free(server_opts.ch_clients[i].opts.ssh);
1994 break;
1995#endif
1996#ifdef NC_ENABLED_TLS
1997 case NC_TI_OPENSSL:
1998 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1999 free(server_opts.ch_clients[i].opts.tls);
2000 break;
2001#endif
2002 default:
2003 ERRINT;
2004 break;
2005 }
2006
2007 /* remove all endpoints */
2008 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2009 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2010 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2011 }
2012 free(server_opts.ch_clients[i].ch_endpts);
2013
2014 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2015
2016 /* move last client and endpoint(s) to the empty space */
2017 --server_opts.ch_client_count;
2018 if (i < server_opts.ch_client_count) {
2019 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2020 sizeof *server_opts.ch_clients);
2021 } else if (!server_opts.ch_client_count) {
2022 free(server_opts.ch_clients);
2023 server_opts.ch_clients = NULL;
2024 }
2025
2026 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002027 if (name) {
2028 break;
2029 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002030 }
2031 }
2032 }
2033
2034 /* WRITE UNLOCK */
2035 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2036
2037 return ret;
2038}
2039
2040API int
2041nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2042{
2043 uint16_t i;
2044 struct nc_ch_client *client;
2045
2046 if (!client_name) {
2047 ERRARG("client_name");
2048 return -1;
2049 } else if (!endpt_name) {
2050 ERRARG("endpt_name");
2051 return -1;
2052 }
2053
2054 /* LOCK */
2055 client = nc_server_ch_client_lock(client_name, 0, NULL);
2056 if (!client) {
2057 return -1;
2058 }
2059
2060 for (i = 0; i < client->ch_endpt_count; ++i) {
2061 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2062 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2063 /* UNLOCK */
2064 nc_server_ch_client_unlock(client);
2065 return -1;
2066 }
2067 }
2068
2069 ++client->ch_endpt_count;
2070 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2071 if (!client->ch_endpts) {
2072 ERRMEM;
2073 /* UNLOCK */
2074 nc_server_ch_client_unlock(client);
2075 return -1;
2076 }
2077
2078 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2079 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2080 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2081
2082 /* UNLOCK */
2083 nc_server_ch_client_unlock(client);
2084
2085 return 0;
2086}
2087
2088API int
2089nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2090{
2091 uint16_t i;
2092 int ret = -1;
2093 struct nc_ch_client *client;
2094
2095 if (!client_name) {
2096 ERRARG("client_name");
2097 return -1;
2098 }
2099
2100 /* LOCK */
2101 client = nc_server_ch_client_lock(client_name, 0, NULL);
2102 if (!client) {
2103 return -1;
2104 }
2105
2106 if (!endpt_name) {
2107 /* remove all endpoints */
2108 for (i = 0; i < client->ch_endpt_count; ++i) {
2109 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2110 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2111 }
2112 free(client->ch_endpts);
2113 client->ch_endpts = NULL;
2114 client->ch_endpt_count = 0;
2115
2116 ret = 0;
2117 } else {
2118 for (i = 0; i < client->ch_endpt_count; ++i) {
2119 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2120 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2121 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002122
Michal Vasko4f921012016-10-20 14:07:45 +02002123 /* move last endpoint to the empty space */
2124 --client->ch_endpt_count;
2125 if (i < client->ch_endpt_count) {
2126 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2127 } else if (!server_opts.ch_client_count) {
2128 free(server_opts.ch_clients);
2129 server_opts.ch_clients = NULL;
2130 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002131
Michal Vasko4f921012016-10-20 14:07:45 +02002132 ret = 0;
2133 break;
2134 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002135 }
2136 }
2137
2138 /* UNLOCK */
2139 nc_server_ch_client_unlock(client);
2140
2141 return ret;
2142}
2143
2144API int
2145nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2146{
2147 uint16_t i;
2148 int ret = -1;
2149 struct nc_ch_client *client;
2150
2151 if (!client_name) {
2152 ERRARG("client_name");
2153 return -1;
2154 } else if (!endpt_name) {
2155 ERRARG("endpt_name");
2156 return -1;
2157 } else if (!address) {
2158 ERRARG("address");
2159 return -1;
2160 }
2161
2162 /* LOCK */
2163 client = nc_server_ch_client_lock(client_name, 0, NULL);
2164 if (!client) {
2165 return -1;
2166 }
2167
2168 for (i = 0; i < client->ch_endpt_count; ++i) {
2169 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2170 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2171 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2172
2173 ret = 0;
2174 break;
2175 }
2176 }
2177
2178 /* UNLOCK */
2179 nc_server_ch_client_unlock(client);
2180
2181 if (ret == -1) {
2182 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2183 }
2184
2185 return ret;
2186}
2187
2188API int
2189nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2190{
2191 uint16_t i;
2192 int ret = -1;
2193 struct nc_ch_client *client;
2194
2195 if (!client_name) {
2196 ERRARG("client_name");
2197 return -1;
2198 } else if (!endpt_name) {
2199 ERRARG("endpt_name");
2200 return -1;
2201 } else if (!port) {
2202 ERRARG("port");
2203 return -1;
2204 }
2205
2206 /* LOCK */
2207 client = nc_server_ch_client_lock(client_name, 0, NULL);
2208 if (!client) {
2209 return -1;
2210 }
2211
2212 for (i = 0; i < client->ch_endpt_count; ++i) {
2213 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2214 client->ch_endpts[i].port = port;
2215
2216 ret = 0;
2217 break;
2218 }
2219 }
2220
2221 /* UNLOCK */
2222 nc_server_ch_client_unlock(client);
2223
2224 if (ret == -1) {
2225 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2226 }
2227
2228 return ret;
2229}
2230
2231API int
2232nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2233{
2234 struct nc_ch_client *client;
2235
2236 if (!client_name) {
2237 ERRARG("client_name");
2238 return -1;
2239 } else if (!conn_type) {
2240 ERRARG("conn_type");
2241 return -1;
2242 }
2243
2244 /* LOCK */
2245 client = nc_server_ch_client_lock(client_name, 0, NULL);
2246 if (!client) {
2247 return -1;
2248 }
2249
2250 if (client->conn_type != conn_type) {
2251 client->conn_type = conn_type;
2252
2253 /* set default options */
2254 switch (conn_type) {
2255 case NC_CH_PERSIST:
2256 client->conn.persist.idle_timeout = 86400;
2257 client->conn.persist.ka_max_wait = 30;
2258 client->conn.persist.ka_max_attempts = 3;
2259 break;
2260 case NC_CH_PERIOD:
2261 client->conn.period.idle_timeout = 300;
2262 client->conn.period.reconnect_timeout = 60;
2263 break;
2264 default:
2265 ERRINT;
2266 break;
2267 }
2268 }
2269
2270 /* UNLOCK */
2271 nc_server_ch_client_unlock(client);
2272
2273 return 0;
2274}
2275
2276API int
2277nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2278{
2279 struct nc_ch_client *client;
2280
2281 if (!client_name) {
2282 ERRARG("client_name");
2283 return -1;
2284 }
2285
2286 /* LOCK */
2287 client = nc_server_ch_client_lock(client_name, 0, NULL);
2288 if (!client) {
2289 return -1;
2290 }
2291
2292 if (client->conn_type != NC_CH_PERSIST) {
2293 ERR("Call Home client \"%s\" is not of persistent connection type.");
2294 /* UNLOCK */
2295 nc_server_ch_client_unlock(client);
2296 return -1;
2297 }
2298
2299 client->conn.persist.idle_timeout = idle_timeout;
2300
2301 /* UNLOCK */
2302 nc_server_ch_client_unlock(client);
2303
2304 return 0;
2305}
2306
2307API int
2308nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2309{
2310 struct nc_ch_client *client;
2311
2312 if (!client_name) {
2313 ERRARG("client_name");
2314 return -1;
2315 } else if (!max_wait) {
2316 ERRARG("max_wait");
2317 return -1;
2318 }
2319
2320 /* LOCK */
2321 client = nc_server_ch_client_lock(client_name, 0, NULL);
2322 if (!client) {
2323 return -1;
2324 }
2325
2326 if (client->conn_type != NC_CH_PERSIST) {
2327 ERR("Call Home client \"%s\" is not of persistent connection type.");
2328 /* UNLOCK */
2329 nc_server_ch_client_unlock(client);
2330 return -1;
2331 }
2332
2333 client->conn.persist.ka_max_wait = max_wait;
2334
2335 /* UNLOCK */
2336 nc_server_ch_client_unlock(client);
2337
2338 return 0;
2339}
2340
2341API int
2342nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2343{
2344 struct nc_ch_client *client;
2345
2346 if (!client_name) {
2347 ERRARG("client_name");
2348 return -1;
2349 }
2350
2351 /* LOCK */
2352 client = nc_server_ch_client_lock(client_name, 0, NULL);
2353 if (!client) {
2354 return -1;
2355 }
2356
2357 if (client->conn_type != NC_CH_PERSIST) {
2358 ERR("Call Home client \"%s\" is not of persistent connection type.");
2359 /* UNLOCK */
2360 nc_server_ch_client_unlock(client);
2361 return -1;
2362 }
2363
2364 client->conn.persist.ka_max_attempts = max_attempts;
2365
2366 /* UNLOCK */
2367 nc_server_ch_client_unlock(client);
2368
2369 return 0;
2370}
2371
2372API int
2373nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2374{
2375 struct nc_ch_client *client;
2376
2377 if (!client_name) {
2378 ERRARG("client_name");
2379 return -1;
2380 }
2381
2382 /* LOCK */
2383 client = nc_server_ch_client_lock(client_name, 0, NULL);
2384 if (!client) {
2385 return -1;
2386 }
2387
2388 if (client->conn_type != NC_CH_PERIOD) {
2389 ERR("Call Home client \"%s\" is not of periodic connection type.");
2390 /* UNLOCK */
2391 nc_server_ch_client_unlock(client);
2392 return -1;
2393 }
2394
2395 client->conn.period.idle_timeout = idle_timeout;
2396
2397 /* UNLOCK */
2398 nc_server_ch_client_unlock(client);
2399
2400 return 0;
2401}
2402
2403API int
2404nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2405{
2406 struct nc_ch_client *client;
2407
2408 if (!client_name) {
2409 ERRARG("client_name");
2410 return -1;
2411 } else if (!reconnect_timeout) {
2412 ERRARG("reconnect_timeout");
2413 return -1;
2414 }
2415
2416 /* LOCK */
2417 client = nc_server_ch_client_lock(client_name, 0, NULL);
2418 if (!client) {
2419 return -1;
2420 }
2421
2422 if (client->conn_type != NC_CH_PERIOD) {
2423 ERR("Call Home client \"%s\" is not of periodic connection type.");
2424 /* UNLOCK */
2425 nc_server_ch_client_unlock(client);
2426 return -1;
2427 }
2428
2429 client->conn.period.reconnect_timeout = reconnect_timeout;
2430
2431 /* UNLOCK */
2432 nc_server_ch_client_unlock(client);
2433
2434 return 0;
2435}
2436
2437API int
2438nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2439{
2440 struct nc_ch_client *client;
2441
2442 if (!client_name) {
2443 ERRARG("client_name");
2444 return -1;
2445 }
2446
2447 /* LOCK */
2448 client = nc_server_ch_client_lock(client_name, 0, NULL);
2449 if (!client) {
2450 return -1;
2451 }
2452
2453 client->start_with = start_with;
2454
2455 /* UNLOCK */
2456 nc_server_ch_client_unlock(client);
2457
2458 return 0;
2459}
2460
2461API int
2462nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2463{
2464 struct nc_ch_client *client;
2465
2466 if (!client_name) {
2467 ERRARG("client_name");
2468 return -1;
2469 } else if (!max_attempts) {
2470 ERRARG("max_attempts");
2471 return -1;
2472 }
2473
2474 /* LOCK */
2475 client = nc_server_ch_client_lock(client_name, 0, NULL);
2476 if (!client) {
2477 return -1;
2478 }
2479
2480 client->max_attempts = max_attempts;
2481
2482 /* UNLOCK */
2483 nc_server_ch_client_unlock(client);
2484
2485 return 0;
2486}
2487
2488/* client lock is expected to be held */
2489static NC_MSG_TYPE
2490nc_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 +01002491{
Michal Vasko71090fc2016-05-24 16:37:28 +02002492 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002493 int sock, ret;
2494
Michal Vasko2e6defd2016-10-07 15:48:15 +02002495 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002496 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002497 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002498 }
2499
2500 *session = calloc(1, sizeof **session);
2501 if (!(*session)) {
2502 ERRMEM;
2503 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002504 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002505 }
2506 (*session)->status = NC_STATUS_STARTING;
2507 (*session)->side = NC_SERVER;
2508 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002509 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002510 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2511 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002512
2513 /* transport lock */
2514 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
2515 if (!(*session)->ti_lock) {
2516 ERRMEM;
2517 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002518 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002519 goto fail;
2520 }
2521 pthread_mutex_init((*session)->ti_lock, NULL);
2522
2523 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002524#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002525 if (client->ti == NC_TI_LIBSSH) {
2526 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002527 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002528 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002529
Michal Vasko71090fc2016-05-24 16:37:28 +02002530 if (ret < 0) {
2531 msgtype = NC_MSG_ERROR;
2532 goto fail;
2533 } else if (!ret) {
2534 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002535 goto fail;
2536 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002537 } else
2538#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002539#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002540 if (client->ti == NC_TI_OPENSSL) {
2541 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002542 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002543 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002544
Michal Vasko71090fc2016-05-24 16:37:28 +02002545 if (ret < 0) {
2546 msgtype = NC_MSG_ERROR;
2547 goto fail;
2548 } else if (!ret) {
2549 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002550 goto fail;
2551 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002552 } else
2553#endif
2554 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002555 ERRINT;
2556 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002557 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002558 goto fail;
2559 }
2560
2561 /* assign new SID atomically */
2562 /* LOCK */
2563 pthread_spin_lock(&server_opts.sid_lock);
2564 (*session)->id = server_opts.new_session_id++;
2565 /* UNLOCK */
2566 pthread_spin_unlock(&server_opts.sid_lock);
2567
2568 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002569 msgtype = nc_handshake(*session);
2570 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002571 goto fail;
2572 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002573 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002574 (*session)->status = NC_STATUS_RUNNING;
2575
Michal Vasko71090fc2016-05-24 16:37:28 +02002576 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002577
2578fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002579 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002580 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002581 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002582}
2583
Michal Vasko2e6defd2016-10-07 15:48:15 +02002584/* ms */
2585#define NC_CH_NO_ENDPT_WAIT 1000
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002586#define NC_CH_ENDPT_FAIL_WAIT 1000
Michal Vasko2e6defd2016-10-07 15:48:15 +02002587
2588struct nc_ch_client_thread_arg {
2589 char *client_name;
2590 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2591};
2592
2593static struct nc_ch_client *
2594nc_server_ch_client_with_endpt_lock(const char *name)
2595{
2596 struct nc_ch_client *client;
2597
2598 while (1) {
2599 /* LOCK */
2600 client = nc_server_ch_client_lock(name, 0, NULL);
2601 if (!client) {
2602 return NULL;
2603 }
2604 if (client->ch_endpt_count) {
2605 return client;
2606 }
2607 /* no endpoints defined yet */
2608
2609 /* UNLOCK */
2610 nc_server_ch_client_unlock(client);
2611
2612 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2613 }
2614
2615 return NULL;
2616}
2617
2618static int
2619nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2620{
2621 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002622 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002623 struct timespec ts;
2624 struct nc_ch_client *client;
2625
2626 /* session created, initialize condition */
2627 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2628 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2629 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2630 ERRMEM;
2631 nc_session_free(session, NULL);
2632 return -1;
2633 }
2634 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2635 pthread_cond_init(session->opts.server.ch_cond, NULL);
2636
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002637 session->flags |= NC_SESSION_CALLHOME;
2638
Michal Vasko2e6defd2016-10-07 15:48:15 +02002639 /* CH LOCK */
2640 pthread_mutex_lock(session->opts.server.ch_lock);
2641
2642 /* give the session to the user */
2643 data->session_clb(data->client_name, session);
2644
2645 do {
2646 nc_gettimespec(&ts);
2647 ts.tv_nsec += NC_CH_NO_ENDPT_WAIT * 1000000L;
2648 if (ts.tv_nsec > 1000000000L) {
2649 ts.tv_sec += ts.tv_nsec / 1000000000L;
2650 ts.tv_nsec %= 1000000000L;
2651 }
2652
2653 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2654 if (ret && (ret != ETIMEDOUT)) {
2655 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2656 goto ch_client_remove;
2657 }
2658
2659 /* check whether the client was not removed */
2660 /* LOCK */
2661 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2662 if (!client) {
2663 /* client was removed, finish thread */
2664 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2665 data->client_name);
2666 goto ch_client_remove;
2667 }
2668
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002669 if (client->conn_type == NC_CH_PERSIST) {
2670 /* TODO keep-alives */
2671 idle_timeout = client->conn.persist.idle_timeout;
2672 } else {
2673 idle_timeout = client->conn.period.idle_timeout;
2674 }
2675
2676 /* TODO only for sessions without subscriptions */
2677 if (idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
2678 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2679 session->status = NC_STATUS_INVALID;
2680 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2681 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002682
2683 /* UNLOCK */
2684 nc_server_ch_client_unlock(client);
2685
2686 } while (session->status == NC_STATUS_RUNNING);
2687
2688 /* CH UNLOCK */
2689 pthread_mutex_unlock(session->opts.server.ch_lock);
2690
2691 return 0;
2692
2693ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002694 /* make the session a standard one */
2695 pthread_cond_destroy(session->opts.server.ch_cond);
2696 free(session->opts.server.ch_cond);
2697 session->opts.server.ch_cond = NULL;
2698
2699 session->flags &= ~NC_SESSION_CALLHOME;
2700
Michal Vasko2e6defd2016-10-07 15:48:15 +02002701 /* CH UNLOCK */
2702 pthread_mutex_unlock(session->opts.server.ch_lock);
2703
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002704 pthread_mutex_destroy(session->opts.server.ch_lock);
2705 free(session->opts.server.ch_lock);
2706 session->opts.server.ch_lock = NULL;
2707
Michal Vasko2e6defd2016-10-07 15:48:15 +02002708 return 1;
2709}
2710
2711static void *
2712nc_ch_client_thread(void *arg)
2713{
2714 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2715 NC_MSG_TYPE msgtype;
2716 uint8_t cur_attempts = 0;
2717 uint16_t i;
2718 char *cur_endpt_name;
2719 struct nc_ch_endpt *cur_endpt;
2720 struct nc_session *session;
2721 struct nc_ch_client *client;
2722
2723 /* LOCK */
2724 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2725 if (!client) {
2726 goto cleanup;
2727 }
2728
2729 cur_endpt = &client->ch_endpts[0];
2730 cur_endpt_name = strdup(cur_endpt->name);
2731
Michal Vasko29af44b2016-10-13 10:59:55 +02002732 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002733 while (1) {
2734 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2735
2736 if (msgtype == NC_MSG_HELLO) {
2737 /* UNLOCK */
2738 nc_server_ch_client_unlock(client);
2739
Michal Vasko29af44b2016-10-13 10:59:55 +02002740 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002741 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2742 goto cleanup;
2743 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002744 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002745
2746 /* LOCK */
2747 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2748 if (!client) {
2749 goto cleanup;
2750 }
2751
2752 /* session changed status -> it was disconnected for whatever reason,
2753 * persistent connection immediately tries to reconnect, periodic waits some first */
2754 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002755 /* UNLOCK */
2756 nc_server_ch_client_unlock(client);
2757
2758 /* TODO wake up sometimes to check for new notifications */
2759 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2760
2761 /* LOCK */
2762 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2763 if (!client) {
2764 goto cleanup;
2765 }
2766 }
2767
2768 /* set next endpoint to try */
2769 if (client->start_with == NC_CH_FIRST_LISTED) {
2770 cur_endpt = &client->ch_endpts[0];
2771 free(cur_endpt_name);
2772 cur_endpt_name = strdup(cur_endpt->name);
2773 } /* else we keep the current one */
2774 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002775 /* UNLOCK */
2776 nc_server_ch_client_unlock(client);
2777
Michal Vasko2e6defd2016-10-07 15:48:15 +02002778 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002779 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2780
Michal Vasko6bb116b2016-10-26 13:53:46 +02002781 /* LOCK */
2782 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2783 if (!client) {
2784 goto cleanup;
2785 }
2786
Michal Vasko2e6defd2016-10-07 15:48:15 +02002787 ++cur_attempts;
2788 if (cur_attempts == client->max_attempts) {
2789 for (i = 0; i < client->ch_endpt_count; ++i) {
2790 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2791 break;
2792 }
2793 }
2794 if (i < client->ch_endpt_count - 1) {
2795 /* just go to the next endpoint */
2796 cur_endpt = &client->ch_endpts[i + 1];
2797 free(cur_endpt_name);
2798 cur_endpt_name = strdup(cur_endpt->name);
2799 } else {
2800 /* cur_endpoint was removed or is the last, either way start with the first one */
2801 cur_endpt = &client->ch_endpts[0];
2802 free(cur_endpt_name);
2803 cur_endpt_name = strdup(cur_endpt->name);
2804 }
2805
2806 cur_attempts = 0;
2807 } /* else we keep the current one */
2808 }
2809 }
2810
2811cleanup:
2812 VRB("Call Home client \"%s\" thread exit.", data->client_name);
2813
2814 free(data->client_name);
2815 free(data);
2816 return NULL;
2817}
2818
2819API int
2820nc_connect_ch_client_dispatch(const char *client_name,
2821 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2822 int ret;
2823 pthread_t tid;
2824 struct nc_ch_client_thread_arg *arg;
2825
2826 if (!client_name) {
2827 ERRARG("client_name");
2828 return -1;
2829 } else if (!session_clb) {
2830 ERRARG("session_clb");
2831 return -1;
2832 }
2833
2834 arg = malloc(sizeof *arg);
2835 if (!arg) {
2836 ERRMEM;
2837 return -1;
2838 }
2839 arg->client_name = strdup(client_name);
2840 if (!arg->client_name) {
2841 ERRMEM;
2842 free(arg);
2843 return -1;
2844 }
2845 arg->session_clb = session_clb;
2846
2847 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2848 if (ret) {
2849 ERR("Creating a new thread failed (%s).", strerror(ret));
2850 free(arg->client_name);
2851 free(arg);
2852 return -1;
2853 }
2854 /* the thread now manages arg */
2855
2856 pthread_detach(tid);
2857
2858 return 0;
2859}
2860
Radek Krejci53691be2016-02-22 13:58:37 +01002861#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002862
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002863API time_t
2864nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002865{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002866 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002867 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002868 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002869 }
2870
Michal Vasko2e6defd2016-10-07 15:48:15 +02002871 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002872}