blob: ce65748335437b21e48441e78fe1aca1df6d0326 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
15#include <stdint.h>
16#include <stdlib.h>
17#include <errno.h>
18#include <string.h>
19#include <poll.h>
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010025#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010026#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010027#include <time.h>
Michal Vasko086311b2016-01-08 09:53:11 +010028
Michal Vasko1a38c862016-01-15 15:50:07 +010029#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010030#include "session_server.h"
31
Michal Vaskob48aa812016-01-18 14:13:09 +010032struct nc_server_opts server_opts = {
Michal Vasko2e6defd2016-10-07 15:48:15 +020033 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
34 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010035};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010036
fanchanghu966f2de2016-07-21 02:28:57 -040037static nc_rpc_clb global_rpc_clb = NULL;
38
Michal Vasko3031aae2016-01-27 16:07:18 +010039struct nc_endpt *
Michal Vasko2e6defd2016-10-07 15:48:15 +020040nc_server_endpt_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010041{
42 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010043 struct nc_endpt *endpt = NULL;
44
45 /* READ LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020046 pthread_rwlock_rdlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010047
48 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020049 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010050 endpt = &server_opts.endpts[i];
51 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010052 }
53 }
54
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010055 if (!endpt) {
56 ERR("Endpoint \"%s\" was not found.", name);
57 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020058 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010059 return NULL;
60 }
61
62 /* ENDPT LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020063 pthread_mutex_lock(&endpt->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010064
Michal Vaskoe2713da2016-08-22 16:06:40 +020065 if (idx) {
66 *idx = i;
67 }
68
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010069 return endpt;
70}
71
72void
73nc_server_endpt_unlock(struct nc_endpt *endpt)
74{
75 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020076 pthread_mutex_unlock(&endpt->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077
78 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020079 pthread_rwlock_unlock(&server_opts.endpt_lock);
80}
81
82struct nc_ch_client *
83nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
84{
85 uint16_t i;
86 struct nc_ch_client *client = NULL;
87
88 /* READ LOCK */
89 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
90
91 for (i = 0; i < server_opts.ch_client_count; ++i) {
92 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
93 client = &server_opts.ch_clients[i];
94 break;
95 }
96 }
97
98 if (!client) {
99 ERR("Call Home client \"%s\" was not found.", name);
100 /* READ UNLOCK */
101 pthread_rwlock_unlock(&server_opts.ch_client_lock);
102 return NULL;
103 }
104
105 /* CH CLIENT LOCK */
106 pthread_mutex_lock(&client->lock);
107
108 if (idx) {
109 *idx = i;
110 }
111
112 return client;
113}
114
115void
116nc_server_ch_client_unlock(struct nc_ch_client *client)
117{
118 /* CH CLIENT UNLOCK */
119 pthread_mutex_unlock(&client->lock);
120
121 /* READ UNLOCK */
122 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100123}
Michal Vasko086311b2016-01-08 09:53:11 +0100124
Michal Vasko1a38c862016-01-15 15:50:07 +0100125API void
126nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
127{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200128 if (!session) {
129 ERRARG("session");
130 return;
131 } else if (!reason) {
132 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100133 return;
134 }
135
136 session->term_reason = reason;
137}
138
Michal Vasko086311b2016-01-08 09:53:11 +0100139int
Michal Vaskof05562c2016-01-20 12:06:43 +0100140nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100141{
142 const int optVal = 1;
143 const socklen_t optLen = sizeof(optVal);
144 int is_ipv4, sock;
145 struct sockaddr_storage saddr;
146
147 struct sockaddr_in *saddr4;
148 struct sockaddr_in6 *saddr6;
149
150
151 if (!strchr(address, ':')) {
152 is_ipv4 = 1;
153 } else {
154 is_ipv4 = 0;
155 }
156
157 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
158 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100159 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100160 goto fail;
161 }
162
163 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100164 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100165 goto fail;
166 }
167
168 bzero(&saddr, sizeof(struct sockaddr_storage));
169 if (is_ipv4) {
170 saddr4 = (struct sockaddr_in *)&saddr;
171
172 saddr4->sin_family = AF_INET;
173 saddr4->sin_port = htons(port);
174
175 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100176 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100177 goto fail;
178 }
179
180 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100181 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100182 goto fail;
183 }
184
185 } else {
186 saddr6 = (struct sockaddr_in6 *)&saddr;
187
188 saddr6->sin6_family = AF_INET6;
189 saddr6->sin6_port = htons(port);
190
191 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100192 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100193 goto fail;
194 }
195
196 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100197 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100198 goto fail;
199 }
200 }
201
Michal Vaskofb89d772016-01-08 12:25:35 +0100202 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100203 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100204 goto fail;
205 }
206
207 return sock;
208
209fail:
210 if (sock > -1) {
211 close(sock);
212 }
213
214 return -1;
215}
216
217int
Michal Vasko3031aae2016-01-27 16:07:18 +0100218nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100219{
220 uint16_t i;
221 struct pollfd *pfd;
222 struct sockaddr_storage saddr;
223 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100224 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100225
226 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100227 if (!pfd) {
228 ERRMEM;
229 return -1;
230 }
231
Michal Vasko0a3f3752016-10-13 14:58:38 +0200232 for (i = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200233 if (binds[i].sock < 0) {
234 /* invalid socket */
235 --bind_count;
236 continue;
237 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200238 if (binds[i].pollin) {
239 binds[i].pollin = 0;
240 /* leftover pollin */
241 sock = binds[i].sock;
242 break;
243 }
Michal Vasko086311b2016-01-08 09:53:11 +0100244 pfd[i].fd = binds[i].sock;
245 pfd[i].events = POLLIN;
246 pfd[i].revents = 0;
247 }
248
Michal Vasko0a3f3752016-10-13 14:58:38 +0200249 if (sock == -1) {
250 /* poll for a new connection */
251 ret = poll(pfd, bind_count, timeout);
252 if (!ret) {
253 /* we timeouted */
254 free(pfd);
255 return 0;
256 } else if (ret == -1) {
257 ERR("Poll failed (%s).", strerror(errno));
258 free(pfd);
259 return -1;
260 }
Michal Vasko086311b2016-01-08 09:53:11 +0100261
Michal Vasko0a3f3752016-10-13 14:58:38 +0200262 for (i = 0; i < bind_count; ++i) {
263 if (pfd[i].revents & POLLIN) {
264 --ret;
265
266 if (!ret) {
267 /* the last socket with an event, use it */
268 sock = pfd[i].fd;
269 break;
270 } else {
271 /* just remember the event for next time */
272 binds[i].pollin = 1;
273 }
274 }
Michal Vasko086311b2016-01-08 09:53:11 +0100275 }
276 }
277 free(pfd);
278
279 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100280 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100281 return -1;
282 }
283
284 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100285 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100286 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100287 return -1;
288 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200289 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100290
Michal Vasko0190bc32016-03-02 15:47:49 +0100291 /* make the socket non-blocking */
292 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
293 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100294 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100295 return -1;
296 }
297
Michal Vasko3031aae2016-01-27 16:07:18 +0100298 if (idx) {
299 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100300 }
301
Michal Vasko086311b2016-01-08 09:53:11 +0100302 /* host was requested */
303 if (host) {
304 if (saddr.ss_family == AF_INET) {
305 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100306 if (*host) {
307 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
308 ERR("inet_ntop failed (%s).", strerror(errno));
309 free(*host);
310 *host = NULL;
311 }
Michal Vasko086311b2016-01-08 09:53:11 +0100312
Michal Vasko4eb3c312016-03-01 14:09:37 +0100313 if (port) {
314 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
315 }
316 } else {
317 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100318 }
319 } else if (saddr.ss_family == AF_INET6) {
320 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100321 if (*host) {
322 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
323 ERR("inet_ntop failed (%s).", strerror(errno));
324 free(*host);
325 *host = NULL;
326 }
Michal Vasko086311b2016-01-08 09:53:11 +0100327
Michal Vasko4eb3c312016-03-01 14:09:37 +0100328 if (port) {
329 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
330 }
331 } else {
332 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100333 }
334 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100335 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100336 }
337 }
338
339 return ret;
340}
341
Michal Vasko05ba9df2016-01-13 14:40:27 +0100342static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100343nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100344{
345 const char *identifier = NULL, *version = NULL, *format = NULL;
346 char *model_data = NULL;
347 const struct lys_module *module;
348 struct nc_server_error *err;
349 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100350 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100351
352 LY_TREE_FOR(rpc->child, child) {
353 if (!strcmp(child->schema->name, "identifier")) {
354 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
355 } else if (!strcmp(child->schema->name, "version")) {
356 version = ((struct lyd_node_leaf_list *)child)->value_str;
357 } else if (!strcmp(child->schema->name, "format")) {
358 format = ((struct lyd_node_leaf_list *)child)->value_str;
359 }
360 }
361
362 /* check version */
363 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100364 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
365 nc_err_set_msg(err, "The requested version is not supported.", "en");
366 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100367 }
368
369 /* check and get module with the name identifier */
370 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
371 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200372 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
373 }
374 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100375 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
376 nc_err_set_msg(err, "The requested schema was not found.", "en");
377 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100378 }
379
380 /* check format */
381 if (!format || !strcmp(format, "yang")) {
382 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
383 } else if (!strcmp(format, "yin")) {
384 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
385 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100386 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
387 nc_err_set_msg(err, "The requested format is not supported.", "en");
388 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200390 if (!model_data) {
391 ERRINT;
392 return NULL;
393 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100394
Michal Vasko303245c2016-03-24 15:20:03 +0100395 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200396 if (!sdata) {
397 ERRINT;
398 free(model_data);
399 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100400 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200401
Radek Krejci539efb62016-08-24 15:05:16 +0200402 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
403 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100404 if (!data) {
405 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200406 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100407 return NULL;
408 }
409
Radek Krejci36dfdb32016-09-01 16:56:35 +0200410 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100411}
412
413static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100414nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100415{
Michal Vasko428087d2016-01-14 16:04:28 +0100416 session->term_reason = NC_SESSION_TERM_CLOSED;
417 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100418}
419
Michal Vasko086311b2016-01-08 09:53:11 +0100420API int
421nc_server_init(struct ly_ctx *ctx)
422{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100423 const struct lys_node *rpc;
424
Michal Vasko086311b2016-01-08 09:53:11 +0100425 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200426 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100427 return -1;
428 }
429
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100430 nc_init();
431
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100433 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100434 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100435 lys_set_private(rpc, nc_clb_default_get_schema);
436 }
437
438 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100439 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100440 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100441 lys_set_private(rpc, nc_clb_default_close_session);
442 }
443
Michal Vasko086311b2016-01-08 09:53:11 +0100444 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100445
446 server_opts.new_session_id = 1;
447 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
448
Michal Vasko086311b2016-01-08 09:53:11 +0100449 return 0;
450}
451
Michal Vaskob48aa812016-01-18 14:13:09 +0100452API void
453nc_server_destroy(void)
454{
455 pthread_spin_destroy(&server_opts.sid_lock);
456
Radek Krejci53691be2016-02-22 13:58:37 +0100457#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200458 nc_server_del_endpt(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100459#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100460 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100461}
462
Michal Vasko086311b2016-01-08 09:53:11 +0100463API int
464nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
465{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200466 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
467 ERRARG("basic_mode");
468 return -1;
469 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
470 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100471 return -1;
472 }
473
474 server_opts.wd_basic_mode = basic_mode;
475 server_opts.wd_also_supported = also_supported;
476 return 0;
477}
478
Michal Vasko1a38c862016-01-15 15:50:07 +0100479API void
Michal Vasko55f03972016-04-13 08:56:01 +0200480nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
481{
482 if (!basic_mode && !also_supported) {
483 ERRARG("basic_mode and also_supported");
484 return;
485 }
486
487 if (basic_mode) {
488 *basic_mode = server_opts.wd_basic_mode;
489 }
490 if (also_supported) {
491 *also_supported = server_opts.wd_also_supported;
492 }
493}
494
495API void
Michal Vasko086311b2016-01-08 09:53:11 +0100496nc_server_set_capab_interleave(int interleave_support)
497{
498 if (interleave_support) {
499 server_opts.interleave_capab = 1;
500 } else {
501 server_opts.interleave_capab = 0;
502 }
Michal Vasko086311b2016-01-08 09:53:11 +0100503}
504
Michal Vasko55f03972016-04-13 08:56:01 +0200505API int
506nc_server_get_capab_interleave(void)
507{
508 return server_opts.interleave_capab;
509}
510
Michal Vasko1a38c862016-01-15 15:50:07 +0100511API void
Michal Vasko086311b2016-01-08 09:53:11 +0100512nc_server_set_hello_timeout(uint16_t hello_timeout)
513{
Michal Vasko086311b2016-01-08 09:53:11 +0100514 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100515}
516
Michal Vasko55f03972016-04-13 08:56:01 +0200517API uint16_t
518nc_server_get_hello_timeout(void)
519{
520 return server_opts.hello_timeout;
521}
522
Michal Vasko1a38c862016-01-15 15:50:07 +0100523API void
Michal Vasko086311b2016-01-08 09:53:11 +0100524nc_server_set_idle_timeout(uint16_t idle_timeout)
525{
Michal Vasko086311b2016-01-08 09:53:11 +0100526 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100527}
528
Michal Vasko55f03972016-04-13 08:56:01 +0200529API uint16_t
530nc_server_get_idle_timeout(void)
531{
532 return server_opts.idle_timeout;
533}
534
Michal Vasko71090fc2016-05-24 16:37:28 +0200535API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100536nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100537{
Michal Vasko71090fc2016-05-24 16:37:28 +0200538 NC_MSG_TYPE msgtype;
539
Michal Vasko45e53ae2016-04-07 11:46:03 +0200540 if (!server_opts.ctx) {
541 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200542 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200543 } else if (fdin < 0) {
544 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200545 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200546 } else if (fdout < 0) {
547 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200548 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200549 } else if (!username) {
550 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200551 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200552 } else if (!session) {
553 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200554 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100555 }
556
557 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100558 *session = calloc(1, sizeof **session);
559 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100560 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200561 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100562 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100563 (*session)->status = NC_STATUS_STARTING;
564 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100565
566 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100567 (*session)->ti_type = NC_TI_FD;
568 (*session)->ti.fd.in = fdin;
569 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100570
571 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100572 (*session)->flags = NC_SESSION_SHAREDCTX;
573 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100574
Michal Vaskob48aa812016-01-18 14:13:09 +0100575 /* assign new SID atomically */
576 pthread_spin_lock(&server_opts.sid_lock);
577 (*session)->id = server_opts.new_session_id++;
578 pthread_spin_unlock(&server_opts.sid_lock);
579
Michal Vasko086311b2016-01-08 09:53:11 +0100580 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200581 msgtype = nc_handshake(*session);
582 if (msgtype != NC_MSG_HELLO) {
583 nc_session_free(*session, NULL);
584 *session = NULL;
585 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100586 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200587 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100588 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100589
Michal Vasko71090fc2016-05-24 16:37:28 +0200590 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100591}
Michal Vasko9e036d52016-01-08 10:49:26 +0100592
Michal Vaskob30b99c2016-07-26 11:35:43 +0200593static void
594nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
595{
596 uint8_t i, found = 0;
597
598 for (i = 0; i < ps->queue_len; ++i) {
599 /* idx round buffer adjust */
600 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
601 i = -ps->queue_begin;
602 }
603
604 if (found) {
605 /* move the value back one place */
606 if (ps->queue[ps->queue_begin + i] == id) {
607 /* another equal value, simply cannot be */
608 ERRINT;
609 }
610
611 if (ps->queue_begin + i == 0) {
612 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
613 } else {
614 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
615 }
616 } else if (ps->queue[ps->queue_begin + i] == id) {
617 /* found our id, there can be no more equal valid values */
618 found = 1;
619 }
620 }
621
622 if (!found) {
623 ERRINT;
624 }
625 --ps->queue_len;
626}
627
Michal Vaskof04a52a2016-04-07 10:52:10 +0200628int
Michal Vasko26043172016-07-26 14:08:59 +0200629nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200630{
631 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200632 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200633 struct timespec ts;
634
Radek Krejci7ac16052016-07-15 11:48:18 +0200635 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200636 ts.tv_sec += NC_READ_TIMEOUT;
637
638 /* LOCK */
639 ret = pthread_mutex_timedlock(&ps->lock, &ts);
640 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200641 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200642 return -1;
643 }
644
645 /* get a unique queue value (by adding 1 to the last added value, if any) */
646 if (ps->queue_len) {
647 queue_last = ps->queue_begin + ps->queue_len - 1;
648 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
649 queue_last -= NC_PS_QUEUE_SIZE;
650 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200651 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200652 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200653 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200654 }
655
656 /* add ourselves into the queue */
657 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200658 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200659 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200660 return -1;
661 }
662 ++ps->queue_len;
663 queue_last = ps->queue_begin + ps->queue_len - 1;
664 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
665 queue_last -= NC_PS_QUEUE_SIZE;
666 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200667 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200668
669 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200670 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200671 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200672 ts.tv_sec += NC_READ_TIMEOUT;
673
674 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
675 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200676 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200677 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200678 nc_ps_queue_remove_id(ps, *id);
679 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200680 return -1;
681 }
682 }
683
Michal Vaskobe86fe32016-04-07 10:43:03 +0200684 /* UNLOCK */
685 pthread_mutex_unlock(&ps->lock);
686
687 return 0;
688}
689
Michal Vaskof04a52a2016-04-07 10:52:10 +0200690int
Michal Vasko26043172016-07-26 14:08:59 +0200691nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200692{
693 int ret;
694 struct timespec ts;
695
Radek Krejci7ac16052016-07-15 11:48:18 +0200696 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200697 ts.tv_sec += NC_READ_TIMEOUT;
698
699 /* LOCK */
700 ret = pthread_mutex_timedlock(&ps->lock, &ts);
701 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200702 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200703 ret = -1;
704 }
705
Michal Vaskob30b99c2016-07-26 11:35:43 +0200706 /* we must be the first, it was our turn after all, right? */
707 if (ps->queue[ps->queue_begin] != id) {
708 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200709 /* UNLOCK */
710 if (!ret) {
711 pthread_mutex_unlock(&ps->lock);
712 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200713 return -1;
714 }
715
Michal Vaskobe86fe32016-04-07 10:43:03 +0200716 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200717 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200718
719 /* broadcast to all other threads that the queue moved */
720 pthread_cond_broadcast(&ps->cond);
721
Michal Vaskobe86fe32016-04-07 10:43:03 +0200722 /* UNLOCK */
723 if (!ret) {
724 pthread_mutex_unlock(&ps->lock);
725 }
726
727 return ret;
728}
729
Michal Vasko428087d2016-01-14 16:04:28 +0100730API struct nc_pollsession *
731nc_ps_new(void)
732{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100733 struct nc_pollsession *ps;
734
735 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100736 if (!ps) {
737 ERRMEM;
738 return NULL;
739 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200740 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100741 pthread_mutex_init(&ps->lock, NULL);
742
743 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100744}
745
746API void
747nc_ps_free(struct nc_pollsession *ps)
748{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100749 if (!ps) {
750 return;
751 }
752
Michal Vaskobe86fe32016-04-07 10:43:03 +0200753 if (ps->queue_len) {
754 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
755 }
756
Michal Vasko3a715132016-01-21 15:40:31 +0100757 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100758 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100759 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200760 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100761
Michal Vasko428087d2016-01-14 16:04:28 +0100762 free(ps);
763}
764
765API int
766nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
767{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200768 uint8_t q_id;
769
Michal Vasko45e53ae2016-04-07 11:46:03 +0200770 if (!ps) {
771 ERRARG("ps");
772 return -1;
773 } else if (!session) {
774 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100775 return -1;
776 }
777
Michal Vasko48a63ed2016-03-01 09:48:21 +0100778 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200779 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200780 return -1;
781 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100782
Michal Vasko428087d2016-01-14 16:04:28 +0100783 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100784 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
785 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
786 if (!ps->pfds || !ps->sessions) {
787 ERRMEM;
788 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200789 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100790 return -1;
791 }
Michal Vasko428087d2016-01-14 16:04:28 +0100792
793 switch (session->ti_type) {
794 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100795 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100796 break;
797
Radek Krejci53691be2016-02-22 13:58:37 +0100798#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100799 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100800 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100801 break;
802#endif
803
Radek Krejci53691be2016-02-22 13:58:37 +0100804#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100805 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100806 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100807 break;
808#endif
809
810 default:
811 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100812 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200813 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100814 return -1;
815 }
Michal Vasko3a715132016-01-21 15:40:31 +0100816 ps->pfds[ps->session_count - 1].events = POLLIN;
817 ps->pfds[ps->session_count - 1].revents = 0;
818 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100819
Michal Vasko48a63ed2016-03-01 09:48:21 +0100820 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200821 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100822}
823
Michal Vasko48a63ed2016-03-01 09:48:21 +0100824static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100825_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100826{
827 uint16_t i;
828
Radek Krejcid5f978f2016-03-03 13:14:45 +0100829 if (index >= 0) {
830 i = (uint16_t)index;
831 goto remove;
832 }
Michal Vasko428087d2016-01-14 16:04:28 +0100833 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100834 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100835remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100836 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100837 if (i < ps->session_count) {
838 ps->sessions[i] = ps->sessions[ps->session_count];
839 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
840 } else if (!ps->session_count) {
841 free(ps->sessions);
842 ps->sessions = NULL;
843 free(ps->pfds);
844 ps->pfds = NULL;
845 }
Michal Vasko428087d2016-01-14 16:04:28 +0100846 return 0;
847 }
848 }
849
Michal Vaskof0537d82016-01-29 14:42:38 +0100850 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100851}
852
Michal Vasko48a63ed2016-03-01 09:48:21 +0100853API int
854nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
855{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200856 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200857 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100858
Michal Vasko45e53ae2016-04-07 11:46:03 +0200859 if (!ps) {
860 ERRARG("ps");
861 return -1;
862 } else if (!session) {
863 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100864 return -1;
865 }
866
867 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200868 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200869 return -1;
870 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100871
Radek Krejcid5f978f2016-03-03 13:14:45 +0100872 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100873
874 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200875 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100876
Michal Vaskobe86fe32016-04-07 10:43:03 +0200877 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100878}
879
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100880API uint16_t
881nc_ps_session_count(struct nc_pollsession *ps)
882{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200883 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100884 uint16_t count;
885
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100886 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200887 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100888 return 0;
889 }
890
Michal Vasko48a63ed2016-03-01 09:48:21 +0100891 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200892 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200893 return -1;
894 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100895
896 count = ps->session_count;
897
898 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200899 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100900
901 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100902}
903
Michal Vasko71090fc2016-05-24 16:37:28 +0200904/* must be called holding the session lock!
905 * returns: NC_PSPOLL_ERROR,
906 * NC_PSPOLL_BAD_RPC,
907 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
908 * NC_PSPOLL_RPC
909 */
910static int
Radek Krejci93e80222016-10-03 13:34:25 +0200911nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100912{
913 struct lyxml_elem *xml = NULL;
914 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200915 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200916 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100917
Michal Vasko45e53ae2016-04-07 11:46:03 +0200918 if (!session) {
919 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200920 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200921 } else if (!rpc) {
922 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200923 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100924 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100925 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200926 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100927 }
928
929 msgtype = nc_read_msg(session, &xml);
930
931 switch (msgtype) {
932 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200933 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100934 if (!*rpc) {
935 ERRMEM;
936 goto error;
937 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100938
Radek Krejcif93c7d42016-04-06 13:41:15 +0200939 ly_errno = LY_SUCCESS;
Michal Vasko68b3f292016-09-16 12:00:32 +0200940 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_RPC | LYD_OPT_DESTRUCT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100941 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200942 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200943 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200944 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200945 nc_server_reply_free(reply);
946 if (ret == -1) {
947 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200948 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200949 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
950 } else {
951 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100952 }
Michal Vasko428087d2016-01-14 16:04:28 +0100953 (*rpc)->root = xml;
954 break;
955 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100956 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200957 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100958 goto error;
959 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100960 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200961 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100962 goto error;
963 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100964 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200965 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100966 goto error;
967 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200968 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100969 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
970 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200971 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100972 break;
973 }
974
Michal Vasko71090fc2016-05-24 16:37:28 +0200975 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100976
977error:
978 /* cleanup */
979 lyxml_free(server_opts.ctx, xml);
980
Michal Vasko71090fc2016-05-24 16:37:28 +0200981 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100982}
983
fanchanghu966f2de2016-07-21 02:28:57 -0400984API void
985nc_set_global_rpc_clb(nc_rpc_clb clb)
986{
987 global_rpc_clb = clb;
988}
989
Radek Krejci93e80222016-10-03 13:34:25 +0200990API NC_MSG_TYPE
991nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
992{
993 NC_MSG_TYPE result = NC_MSG_NOTIF;
994 int ret;
995
996 /* check parameters */
997 if (!session) {
998 ERRARG("session");
999 return NC_MSG_ERROR;
1000 } else if (!notif || !notif->tree || !notif->eventtime) {
1001 ERRARG("notif");
1002 return NC_MSG_ERROR;
1003 }
1004
1005 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
1006 ret = nc_timedlock(session->ti_lock, timeout, __func__);
1007 if (ret < 0) {
1008 return NC_MSG_ERROR;
1009 } else if (!ret) {
1010 return NC_MSG_WOULDBLOCK;
1011 }
1012
1013 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1014 if (ret == -1) {
1015 ERR("Session %u: failed to write notification.", session->id);
1016 result = NC_MSG_ERROR;
1017 }
1018 pthread_mutex_unlock(session->ti_lock);
1019
1020 return result;
1021}
1022
Michal Vasko71090fc2016-05-24 16:37:28 +02001023/* must be called holding the session lock!
1024 * returns: NC_PSPOLL_ERROR,
1025 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1026 * NC_PSPOLL_REPLY_ERROR,
1027 * 0
1028 */
1029static int
Radek Krejci93e80222016-10-03 13:34:25 +02001030nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001031{
1032 nc_rpc_clb clb;
1033 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001034 struct lys_node *rpc_act = NULL;
1035 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001036 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001037
Michal Vasko4a827e52016-03-03 10:59:00 +01001038 if (!rpc) {
1039 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001040 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001041 }
1042
Michal Vasko90e8e692016-07-13 12:27:57 +02001043 if (rpc->tree->schema->nodetype == LYS_RPC) {
1044 /* RPC */
1045 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001046 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001047 /* action */
1048 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1049 if (elem->schema->nodetype == LYS_ACTION) {
1050 rpc_act = elem->schema;
1051 break;
1052 }
1053 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001054 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001055 if (!rpc_act) {
1056 ERRINT;
1057 return NC_PSPOLL_ERROR;
1058 }
1059 }
1060
1061 if (!rpc_act->priv) {
1062 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001063 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001064 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001065 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001066 reply = clb(rpc->tree, session);
1067 }
1068
1069 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001070 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001071 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001072 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1073 if (reply->type == NC_RPL_ERROR) {
1074 ret |= NC_PSPOLL_REPLY_ERROR;
1075 }
1076 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001077
Michal Vasko71090fc2016-05-24 16:37:28 +02001078 if (r == -1) {
1079 ERR("Session %u: failed to write reply.", session->id);
1080 ret |= NC_PSPOLL_ERROR;
1081 }
Michal Vasko428087d2016-01-14 16:04:28 +01001082
1083 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1084 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1085 session->status = NC_STATUS_INVALID;
1086 }
1087
Michal Vasko71090fc2016-05-24 16:37:28 +02001088 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001089}
1090
1091API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001092nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001093{
1094 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001095 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +01001096 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001097 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +02001098 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001099 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +01001100
1101 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001102 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001103 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001104 }
1105
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001106 cur_time = time(NULL);
1107
Michal Vasko48a63ed2016-03-01 09:48:21 +01001108 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001109 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001110 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001111 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001112
Michal Vasko428087d2016-01-14 16:04:28 +01001113 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001114 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1115 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001116 ret = NC_PSPOLL_ERROR;
1117 if (session) {
1118 *session = ps->sessions[i];
1119 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001120 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001121 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001122
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001123 /* TODO invalidate only sessions without subscription */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001124 if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME) && server_opts.idle_timeout
1125 && (cur_time >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001126 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1127 ps->sessions[i]->status = NC_STATUS_INVALID;
1128 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001129 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1130 if (session) {
1131 *session = ps->sessions[i];
1132 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001133 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001134 }
1135
Michal Vasko3a715132016-01-21 15:40:31 +01001136 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001137 break;
1138 }
Michal Vasko428087d2016-01-14 16:04:28 +01001139 }
1140
Michal Vaskobd8ef262016-01-20 11:09:27 +01001141 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001142#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001143retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001144#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001145 /* no leftover event */
1146 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001147 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001148 if (ret < 0) {
1149 ERR("Poll failed (%s).", strerror(errno));
1150 ret = NC_PSPOLL_ERROR;
1151 goto finish;
1152 } else if (!ret) {
1153 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001154 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001155 }
Michal Vasko428087d2016-01-14 16:04:28 +01001156 }
1157
Michal Vaskobd8ef262016-01-20 11:09:27 +01001158 /* find the first fd with POLLIN, we don't care if there are more now */
1159 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001160 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001161 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1162 ps->sessions[i]->status = NC_STATUS_INVALID;
1163 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001164 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1165 if (session) {
1166 *session = ps->sessions[i];
1167 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001168 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001169 } else if (ps->pfds[i].revents & POLLERR) {
1170 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1171 ps->sessions[i]->status = NC_STATUS_INVALID;
1172 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001173 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1174 if (session) {
1175 *session = ps->sessions[i];
1176 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001177 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001178 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001179#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001180 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001181 uint16_t j;
1182
Michal Vasko96164bf2016-01-21 15:41:58 +01001183 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001184 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001185
1186 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001187 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001188 for (j = i + 1; j < ps->session_count; ++j) {
1189 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1190 ps->pfds[j].revents = 0;
1191 }
1192 }
1193 }
1194
Michal Vasko71090fc2016-05-24 16:37:28 +02001195 /* SSH message only */
1196 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001197 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001198 if (session) {
1199 *session = ps->sessions[i];
1200 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001201 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001202
1203 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001204 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001205 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001206 if (i == ps->session_count - 1) {
1207 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001208 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001209 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001210 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001211 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001212 }
Michal Vasko8c748832016-02-03 15:32:16 +01001213 /* ... retry polling reasonable time apart ... */
1214 usleep(NC_TIMEOUT_STEP);
1215 if (timeout > 0) {
1216 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001217 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001218 }
1219 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001220 }
1221 /* check other sessions */
1222 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001223 }
1224 }
Radek Krejci53691be2016-02-22 13:58:37 +01001225#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001226
Michal Vaskobd8ef262016-01-20 11:09:27 +01001227 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001228 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001229 break;
1230 }
1231 }
1232
1233 if (i == ps->session_count) {
1234 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001235 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001236 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001237 }
1238
1239 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001240 cur_session = ps->sessions[i];
1241 if (session) {
1242 *session = cur_session;
1243 }
Michal Vasko428087d2016-01-14 16:04:28 +01001244
Michal Vaskobd8ef262016-01-20 11:09:27 +01001245 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal vasko953939c2016-10-04 13:46:20 +02001246 ret = nc_timedlock(cur_session->ti_lock, timeout, __func__);
Michal Vasko71090fc2016-05-24 16:37:28 +02001247 if (ret < 0) {
1248 ret = NC_PSPOLL_ERROR;
1249 goto finish;
1250 } else if (!ret) {
1251 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001252 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001253 }
1254
Radek Krejci93e80222016-10-03 13:34:25 +02001255 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001256 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1257 pthread_mutex_unlock(cur_session->ti_lock);
1258 if (cur_session->status != NC_STATUS_RUNNING) {
1259 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001260 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001261 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001262 }
1263
Michal Vasko2e6defd2016-10-07 15:48:15 +02001264 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001265
Michal Vasko428087d2016-01-14 16:04:28 +01001266 /* process RPC */
Radek Krejci93e80222016-10-03 13:34:25 +02001267 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001268
Michal Vasko71090fc2016-05-24 16:37:28 +02001269 pthread_mutex_unlock(cur_session->ti_lock);
1270 if (cur_session->status != NC_STATUS_RUNNING) {
1271 ret |= NC_PSPOLL_SESSION_TERM;
1272 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1273 ret |= NC_PSPOLL_SESSION_ERROR;
1274 }
Michal Vasko428087d2016-01-14 16:04:28 +01001275 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001276
Michal Vaskoca4a2422016-02-02 12:17:14 +01001277 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001278
1279 /* is there some other socket waiting? */
1280 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001281 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001282 ret |= NC_PSPOLL_PENDING;
1283 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001284 }
1285 }
1286
Michal Vasko48a63ed2016-03-01 09:48:21 +01001287finish:
1288 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001289 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001290 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001291}
1292
Michal Vaskod09eae62016-02-01 10:32:52 +01001293API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001294nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001295{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001296 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001297 uint16_t i;
1298 struct nc_session *session;
1299
Michal Vasko9a25e932016-02-01 10:36:42 +01001300 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001301 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001302 return;
1303 }
1304
Michal Vasko48a63ed2016-03-01 09:48:21 +01001305 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001306 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001307 return;
1308 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001309
Michal Vasko48a63ed2016-03-01 09:48:21 +01001310 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001311 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001312 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001313 }
1314 free(ps->sessions);
1315 ps->sessions = NULL;
1316 free(ps->pfds);
1317 ps->pfds = NULL;
1318 ps->session_count = 0;
1319 } else {
1320 for (i = 0; i < ps->session_count; ) {
1321 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1322 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001323 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001324 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001325 continue;
1326 }
1327
1328 ++i;
1329 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001330 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001331
1332 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001333 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001334}
1335
Radek Krejci53691be2016-02-22 13:58:37 +01001336#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001337
Michal Vaskoe2713da2016-08-22 16:06:40 +02001338API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001339nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001340{
Michal Vasko3031aae2016-01-27 16:07:18 +01001341 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +01001342
Michal Vasko45e53ae2016-04-07 11:46:03 +02001343 if (!name) {
1344 ERRARG("name");
1345 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001346 }
1347
Michal Vasko51e514d2016-02-02 15:51:52 +01001348 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001349 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001350
1351 /* check name uniqueness */
1352 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001353 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001354 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001355 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001356 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001357 return -1;
1358 }
1359 }
1360
Michal Vasko3031aae2016-01-27 16:07:18 +01001361 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001362 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001363 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001364 ERRMEM;
1365 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001366 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001367 return -1;
1368 }
1369 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001370 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001371
Michal Vaskoe2713da2016-08-22 16:06:40 +02001372 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001373 if (!server_opts.binds) {
1374 ERRMEM;
1375 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001376 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001377 return -1;
1378 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001379
Michal Vasko2e6defd2016-10-07 15:48:15 +02001380 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1381 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1382 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001383 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001384
1385 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001386#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001387 case NC_TI_LIBSSH:
1388 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1389 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1390 ERRMEM;
1391 /* WRITE UNLOCK */
1392 pthread_rwlock_unlock(&server_opts.endpt_lock);
1393 return -1;
1394 }
1395 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1396 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1397 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1398 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1399 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001400#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001401#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001402 case NC_TI_OPENSSL:
1403 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1404 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1405 ERRMEM;
1406 /* WRITE UNLOCK */
1407 pthread_rwlock_unlock(&server_opts.endpt_lock);
1408 return -1;
1409 }
1410 break;
1411#endif
1412 default:
1413 ERRINT;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001414 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001415 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001416 return -1;
1417 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001418
Michal Vasko2e6defd2016-10-07 15:48:15 +02001419 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001420
Michal Vasko3031aae2016-01-27 16:07:18 +01001421 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001422 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001423
Michal Vasko9e036d52016-01-08 10:49:26 +01001424 return 0;
1425}
1426
Michal Vasko3031aae2016-01-27 16:07:18 +01001427int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001428nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001429{
1430 struct nc_endpt *endpt;
1431 struct nc_bind *bind = NULL;
1432 uint16_t i;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001433 int sock = -1, set_addr;
Michal Vaskoda514772016-02-01 11:32:01 +01001434
Michal Vasko45e53ae2016-04-07 11:46:03 +02001435 if (!endpt_name) {
1436 ERRARG("endpt_name");
1437 return -1;
1438 } else if ((!address && !port) || (address && port)) {
1439 ERRARG("address and port");
1440 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001441 }
1442
Michal Vaskoe2713da2016-08-22 16:06:40 +02001443 if (address) {
1444 set_addr = 1;
1445 } else {
1446 set_addr = 0;
1447 }
1448
Michal Vasko51e514d2016-02-02 15:51:52 +01001449 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001450 endpt = nc_server_endpt_lock(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001451 if (!endpt) {
1452 return -1;
1453 }
1454
Michal Vaskoe2713da2016-08-22 16:06:40 +02001455 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001456
Michal Vaskoe2713da2016-08-22 16:06:40 +02001457 if (set_addr) {
1458 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001459 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001460 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001461 }
1462
Michal Vaskoe2713da2016-08-22 16:06:40 +02001463 /* we have all the information we need to create a listening socket */
1464 if (address && port) {
1465 /* create new socket, close the old one */
1466 sock = nc_sock_listen(address, port);
1467 if (sock == -1) {
1468 goto fail;
1469 }
1470
1471 if (bind->sock > -1) {
1472 close(bind->sock);
1473 }
1474 bind->sock = sock;
1475 } /* else we are just setting address or port */
1476
1477 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001478 lydict_remove(server_opts.ctx, bind->address);
1479 bind->address = lydict_insert(server_opts.ctx, address, 0);
1480 } else {
1481 bind->port = port;
1482 }
1483
Michal Vaskoe2713da2016-08-22 16:06:40 +02001484 if (sock > -1) {
1485#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001486 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001487#elif defined(NC_ENABLED_SSH)
1488 VRB("Listening on %s:%u for SSH connections.", address, port);
1489#else
1490 VRB("Listening on %s:%u for TLS connections.", address, port);
1491#endif
1492 }
1493
Michal Vasko51e514d2016-02-02 15:51:52 +01001494 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001495 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001496 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001497
1498fail:
1499 /* UNLOCK */
1500 nc_server_endpt_unlock(endpt);
1501 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001502}
1503
Michal Vaskoe2713da2016-08-22 16:06:40 +02001504API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001505nc_server_endpt_set_address(const char *endpt_name, const char *address)
1506{
1507 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1508}
1509
1510API int
1511nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1512{
1513 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1514}
1515
1516API int
Michal Vaskoe2713da2016-08-22 16:06:40 +02001517nc_server_del_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001518{
1519 uint32_t i;
1520 int ret = -1;
1521
Michal Vasko3031aae2016-01-27 16:07:18 +01001522 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001523 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001524
Michal Vaskoe2713da2016-08-22 16:06:40 +02001525 if (!name) {
1526 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001527 for (i = 0; i < server_opts.endpt_count; ++i) {
1528 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001529 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1530 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001531#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001532 case NC_TI_LIBSSH:
1533 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1534 free(server_opts.endpts[i].opts.ssh);
1535 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001536#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001537#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001538 case NC_TI_OPENSSL:
1539 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1540 free(server_opts.endpts[i].opts.tls);
1541 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001542#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001543 default:
1544 ERRINT;
1545 /* won't get here ...*/
1546 break;
1547 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001548 ret = 0;
1549 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001550 free(server_opts.endpts);
1551 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001552
1553 /* remove all binds */
1554 for (i = 0; i < server_opts.endpt_count; ++i) {
1555 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1556 if (server_opts.binds[i].sock > -1) {
1557 close(server_opts.binds[i].sock);
1558 }
1559 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001560 free(server_opts.binds);
1561 server_opts.binds = NULL;
1562
Michal Vasko3031aae2016-01-27 16:07:18 +01001563 server_opts.endpt_count = 0;
1564
Michal Vasko1a38c862016-01-15 15:50:07 +01001565 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001566 /* remove one endpoint with bind(s) */
Michal Vasko3031aae2016-01-27 16:07:18 +01001567 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001568 if (!strcmp(server_opts.endpts[i].name, name)) {
1569 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001570 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001571 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1572 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001573#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001574 case NC_TI_LIBSSH:
1575 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1576 free(server_opts.endpts[i].opts.ssh);
1577 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001578#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001579#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001580 case NC_TI_OPENSSL:
1581 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1582 free(server_opts.endpts[i].opts.tls);
1583 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001584#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001585 default:
1586 ERRINT;
1587 break;
1588 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001589
Michal Vaskoe2713da2016-08-22 16:06:40 +02001590 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001591 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1592 if (server_opts.binds[i].sock > -1) {
1593 close(server_opts.binds[i].sock);
1594 }
1595
1596 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001597 --server_opts.endpt_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001598 if (i < server_opts.endpt_count) {
1599 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1600 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001601 } else if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001602 free(server_opts.binds);
1603 server_opts.binds = NULL;
1604 free(server_opts.endpts);
1605 server_opts.endpts = NULL;
1606 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001607
1608 ret = 0;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001609 break;
Michal Vasko1a38c862016-01-15 15:50:07 +01001610 }
1611 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001612 }
1613
Michal Vasko3031aae2016-01-27 16:07:18 +01001614 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001615 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001616
Michal Vasko9e036d52016-01-08 10:49:26 +01001617 return ret;
1618}
1619
Michal Vasko71090fc2016-05-24 16:37:28 +02001620API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001621nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001622{
Michal Vasko71090fc2016-05-24 16:37:28 +02001623 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001624 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001625 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001626 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001627
Michal Vasko45e53ae2016-04-07 11:46:03 +02001628 if (!server_opts.ctx) {
1629 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001630 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001631 } else if (!session) {
1632 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001633 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001634 }
1635
Michal Vasko51e514d2016-02-02 15:51:52 +01001636 /* we have to hold WRITE for the whole time, since there is not
1637 * a way of downgrading the lock to READ */
1638 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001639 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001640
1641 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001642 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001643 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001644 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001645 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001646 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001647
Michal Vaskoe2713da2016-08-22 16:06:40 +02001648 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001649 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001650 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001651 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001652 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001653 if (!ret) {
1654 return NC_MSG_WOULDBLOCK;
1655 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001656 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001657 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001658 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001659
Michal Vasko1a38c862016-01-15 15:50:07 +01001660 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001661 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001662 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001663 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001664 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001665 msgtype = NC_MSG_ERROR;
1666 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001667 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001668 (*session)->status = NC_STATUS_STARTING;
1669 (*session)->side = NC_SERVER;
1670 (*session)->ctx = server_opts.ctx;
1671 (*session)->flags = NC_SESSION_SHAREDCTX;
1672 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1673 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001674
1675 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001676 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1677 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001678 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001679 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001680 msgtype = NC_MSG_ERROR;
1681 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001682 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001683 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001684
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001685 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001686#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001687 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1688 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01001689 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001690 if (ret < 0) {
1691 msgtype = NC_MSG_ERROR;
1692 goto cleanup;
1693 } else if (!ret) {
1694 msgtype = NC_MSG_WOULDBLOCK;
1695 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001696 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001697 } else
1698#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001699#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001700 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1701 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01001702 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001703 if (ret < 0) {
1704 msgtype = NC_MSG_ERROR;
1705 goto cleanup;
1706 } else if (!ret) {
1707 msgtype = NC_MSG_WOULDBLOCK;
1708 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001709 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001710 } else
1711#endif
1712 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001713 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001714 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001715 msgtype = NC_MSG_ERROR;
1716 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001717 }
1718
Michal Vasko2cc4c682016-03-01 09:16:48 +01001719 (*session)->data = NULL;
1720
Michal Vasko51e514d2016-02-02 15:51:52 +01001721 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001722 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001723
Michal Vaskob48aa812016-01-18 14:13:09 +01001724 /* assign new SID atomically */
1725 /* LOCK */
1726 pthread_spin_lock(&server_opts.sid_lock);
1727 (*session)->id = server_opts.new_session_id++;
1728 /* UNLOCK */
1729 pthread_spin_unlock(&server_opts.sid_lock);
1730
Michal Vasko9e036d52016-01-08 10:49:26 +01001731 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001732 msgtype = nc_handshake(*session);
1733 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001734 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001735 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001736 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001737 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001738 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001739 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001740
Michal Vasko71090fc2016-05-24 16:37:28 +02001741 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001742
Michal Vasko71090fc2016-05-24 16:37:28 +02001743cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001744 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001745 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001746
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001747 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001748 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001749 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001750}
1751
Michal Vasko2e6defd2016-10-07 15:48:15 +02001752API int
1753nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1754{
1755 uint16_t i;
1756
1757 if (!name) {
1758 ERRARG("name");
1759 return -1;
1760 } else if (!ti) {
1761 ERRARG("ti");
1762 return -1;
1763 }
1764
1765 /* WRITE LOCK */
1766 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1767
1768 /* check name uniqueness */
1769 for (i = 0; i < server_opts.ch_client_count; ++i) {
1770 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1771 ERR("Call Home client \"%s\" already exists.", name);
1772 /* WRITE UNLOCK */
1773 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1774 return -1;
1775 }
1776 }
1777
1778 ++server_opts.ch_client_count;
1779 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1780 if (!server_opts.ch_clients) {
1781 ERRMEM;
1782 /* WRITE UNLOCK */
1783 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1784 return -1;
1785 }
1786 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1787 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001788 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1789 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001790
1791 switch (ti) {
1792#ifdef NC_ENABLED_SSH
1793 case NC_TI_LIBSSH:
1794 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1795 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1796 ERRMEM;
1797 /* WRITE UNLOCK */
1798 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1799 return -1;
1800 }
1801 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1802 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1803 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1804 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1805 break;
1806#endif
1807#ifdef NC_ENABLED_TLS
1808 case NC_TI_OPENSSL:
1809 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1810 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1811 ERRMEM;
1812 /* WRITE UNLOCK */
1813 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1814 return -1;
1815 }
1816 break;
1817#endif
1818 default:
1819 ERRINT;
1820 /* WRITE UNLOCK */
1821 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1822 return -1;
1823 }
1824
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001825 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
1826
Michal Vasko2e6defd2016-10-07 15:48:15 +02001827 /* set CH default options */
1828 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1829 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1830
1831 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1832
1833 /* WRITE UNLOCK */
1834 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1835
1836 return 0;
1837}
1838
1839API int
1840nc_server_ch_del_client(const char *name)
1841{
1842 uint16_t i, j;
1843 int ret = -1;
1844
1845 /* WRITE LOCK */
1846 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1847
1848 if (!name) {
1849 /* remove all CH clients */
1850 for (i = 0; i < server_opts.ch_client_count; ++i) {
1851 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1852
1853 /* remove all endpoints */
1854 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1855 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1856 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1857 }
1858 free(server_opts.ch_clients[i].ch_endpts);
1859
1860 switch (server_opts.ch_clients[i].ti) {
1861#ifdef NC_ENABLED_SSH
1862 case NC_TI_LIBSSH:
1863 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1864 free(server_opts.ch_clients[i].opts.ssh);
1865 break;
1866#endif
1867#ifdef NC_ENABLED_TLS
1868 case NC_TI_OPENSSL:
1869 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1870 free(server_opts.ch_clients[i].opts.tls);
1871 break;
1872#endif
1873 default:
1874 ERRINT;
1875 /* won't get here ...*/
1876 break;
1877 }
1878
1879 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1880
1881 ret = 0;
1882 }
1883 free(server_opts.ch_clients);
1884 server_opts.ch_clients = NULL;
1885
1886 server_opts.ch_client_count = 0;
1887
1888 } else {
1889 /* remove one client with endpoint(s) */
1890 for (i = 0; i < server_opts.ch_client_count; ++i) {
1891 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1892 /* remove endpt */
1893 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1894
1895 switch (server_opts.ch_clients[i].ti) {
1896#ifdef NC_ENABLED_SSH
1897 case NC_TI_LIBSSH:
1898 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1899 free(server_opts.ch_clients[i].opts.ssh);
1900 break;
1901#endif
1902#ifdef NC_ENABLED_TLS
1903 case NC_TI_OPENSSL:
1904 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1905 free(server_opts.ch_clients[i].opts.tls);
1906 break;
1907#endif
1908 default:
1909 ERRINT;
1910 break;
1911 }
1912
1913 /* remove all endpoints */
1914 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1915 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1916 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1917 }
1918 free(server_opts.ch_clients[i].ch_endpts);
1919
1920 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1921
1922 /* move last client and endpoint(s) to the empty space */
1923 --server_opts.ch_client_count;
1924 if (i < server_opts.ch_client_count) {
1925 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
1926 sizeof *server_opts.ch_clients);
1927 } else if (!server_opts.ch_client_count) {
1928 free(server_opts.ch_clients);
1929 server_opts.ch_clients = NULL;
1930 }
1931
1932 ret = 0;
1933 break;
1934 }
1935 }
1936 }
1937
1938 /* WRITE UNLOCK */
1939 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1940
1941 return ret;
1942}
1943
1944API int
1945nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
1946{
1947 uint16_t i;
1948 struct nc_ch_client *client;
1949
1950 if (!client_name) {
1951 ERRARG("client_name");
1952 return -1;
1953 } else if (!endpt_name) {
1954 ERRARG("endpt_name");
1955 return -1;
1956 }
1957
1958 /* LOCK */
1959 client = nc_server_ch_client_lock(client_name, 0, NULL);
1960 if (!client) {
1961 return -1;
1962 }
1963
1964 for (i = 0; i < client->ch_endpt_count; ++i) {
1965 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
1966 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
1967 /* UNLOCK */
1968 nc_server_ch_client_unlock(client);
1969 return -1;
1970 }
1971 }
1972
1973 ++client->ch_endpt_count;
1974 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
1975 if (!client->ch_endpts) {
1976 ERRMEM;
1977 /* UNLOCK */
1978 nc_server_ch_client_unlock(client);
1979 return -1;
1980 }
1981
1982 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
1983 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
1984 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
1985
1986 /* UNLOCK */
1987 nc_server_ch_client_unlock(client);
1988
1989 return 0;
1990}
1991
1992API int
1993nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
1994{
1995 uint16_t i;
1996 int ret = -1;
1997 struct nc_ch_client *client;
1998
1999 if (!client_name) {
2000 ERRARG("client_name");
2001 return -1;
2002 }
2003
2004 /* LOCK */
2005 client = nc_server_ch_client_lock(client_name, 0, NULL);
2006 if (!client) {
2007 return -1;
2008 }
2009
2010 if (!endpt_name) {
2011 /* remove all endpoints */
2012 for (i = 0; i < client->ch_endpt_count; ++i) {
2013 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2014 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2015 }
2016 free(client->ch_endpts);
2017 client->ch_endpts = NULL;
2018 client->ch_endpt_count = 0;
2019
2020 ret = 0;
2021 } else {
2022 for (i = 0; i < client->ch_endpt_count; ++i) {
2023 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2024 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2025 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2026 }
2027
2028 /* move last endpoint to the empty space */
2029 --client->ch_endpt_count;
2030 if (i < client->ch_endpt_count) {
2031 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2032 } else if (!server_opts.ch_client_count) {
2033 free(server_opts.ch_clients);
2034 server_opts.ch_clients = NULL;
2035 }
2036
2037 ret = 0;
2038 break;
2039 }
2040 }
2041
2042 /* UNLOCK */
2043 nc_server_ch_client_unlock(client);
2044
2045 return ret;
2046}
2047
2048API int
2049nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2050{
2051 uint16_t i;
2052 int ret = -1;
2053 struct nc_ch_client *client;
2054
2055 if (!client_name) {
2056 ERRARG("client_name");
2057 return -1;
2058 } else if (!endpt_name) {
2059 ERRARG("endpt_name");
2060 return -1;
2061 } else if (!address) {
2062 ERRARG("address");
2063 return -1;
2064 }
2065
2066 /* LOCK */
2067 client = nc_server_ch_client_lock(client_name, 0, NULL);
2068 if (!client) {
2069 return -1;
2070 }
2071
2072 for (i = 0; i < client->ch_endpt_count; ++i) {
2073 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2074 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2075 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2076
2077 ret = 0;
2078 break;
2079 }
2080 }
2081
2082 /* UNLOCK */
2083 nc_server_ch_client_unlock(client);
2084
2085 if (ret == -1) {
2086 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2087 }
2088
2089 return ret;
2090}
2091
2092API int
2093nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2094{
2095 uint16_t i;
2096 int ret = -1;
2097 struct nc_ch_client *client;
2098
2099 if (!client_name) {
2100 ERRARG("client_name");
2101 return -1;
2102 } else if (!endpt_name) {
2103 ERRARG("endpt_name");
2104 return -1;
2105 } else if (!port) {
2106 ERRARG("port");
2107 return -1;
2108 }
2109
2110 /* LOCK */
2111 client = nc_server_ch_client_lock(client_name, 0, NULL);
2112 if (!client) {
2113 return -1;
2114 }
2115
2116 for (i = 0; i < client->ch_endpt_count; ++i) {
2117 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2118 client->ch_endpts[i].port = port;
2119
2120 ret = 0;
2121 break;
2122 }
2123 }
2124
2125 /* UNLOCK */
2126 nc_server_ch_client_unlock(client);
2127
2128 if (ret == -1) {
2129 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2130 }
2131
2132 return ret;
2133}
2134
2135API int
2136nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2137{
2138 struct nc_ch_client *client;
2139
2140 if (!client_name) {
2141 ERRARG("client_name");
2142 return -1;
2143 } else if (!conn_type) {
2144 ERRARG("conn_type");
2145 return -1;
2146 }
2147
2148 /* LOCK */
2149 client = nc_server_ch_client_lock(client_name, 0, NULL);
2150 if (!client) {
2151 return -1;
2152 }
2153
2154 if (client->conn_type != conn_type) {
2155 client->conn_type = conn_type;
2156
2157 /* set default options */
2158 switch (conn_type) {
2159 case NC_CH_PERSIST:
2160 client->conn.persist.idle_timeout = 86400;
2161 client->conn.persist.ka_max_wait = 30;
2162 client->conn.persist.ka_max_attempts = 3;
2163 break;
2164 case NC_CH_PERIOD:
2165 client->conn.period.idle_timeout = 300;
2166 client->conn.period.reconnect_timeout = 60;
2167 break;
2168 default:
2169 ERRINT;
2170 break;
2171 }
2172 }
2173
2174 /* UNLOCK */
2175 nc_server_ch_client_unlock(client);
2176
2177 return 0;
2178}
2179
2180API int
2181nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2182{
2183 struct nc_ch_client *client;
2184
2185 if (!client_name) {
2186 ERRARG("client_name");
2187 return -1;
2188 }
2189
2190 /* LOCK */
2191 client = nc_server_ch_client_lock(client_name, 0, NULL);
2192 if (!client) {
2193 return -1;
2194 }
2195
2196 if (client->conn_type != NC_CH_PERSIST) {
2197 ERR("Call Home client \"%s\" is not of persistent connection type.");
2198 /* UNLOCK */
2199 nc_server_ch_client_unlock(client);
2200 return -1;
2201 }
2202
2203 client->conn.persist.idle_timeout = idle_timeout;
2204
2205 /* UNLOCK */
2206 nc_server_ch_client_unlock(client);
2207
2208 return 0;
2209}
2210
2211API int
2212nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2213{
2214 struct nc_ch_client *client;
2215
2216 if (!client_name) {
2217 ERRARG("client_name");
2218 return -1;
2219 } else if (!max_wait) {
2220 ERRARG("max_wait");
2221 return -1;
2222 }
2223
2224 /* LOCK */
2225 client = nc_server_ch_client_lock(client_name, 0, NULL);
2226 if (!client) {
2227 return -1;
2228 }
2229
2230 if (client->conn_type != NC_CH_PERSIST) {
2231 ERR("Call Home client \"%s\" is not of persistent connection type.");
2232 /* UNLOCK */
2233 nc_server_ch_client_unlock(client);
2234 return -1;
2235 }
2236
2237 client->conn.persist.ka_max_wait = max_wait;
2238
2239 /* UNLOCK */
2240 nc_server_ch_client_unlock(client);
2241
2242 return 0;
2243}
2244
2245API int
2246nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2247{
2248 struct nc_ch_client *client;
2249
2250 if (!client_name) {
2251 ERRARG("client_name");
2252 return -1;
2253 }
2254
2255 /* LOCK */
2256 client = nc_server_ch_client_lock(client_name, 0, NULL);
2257 if (!client) {
2258 return -1;
2259 }
2260
2261 if (client->conn_type != NC_CH_PERSIST) {
2262 ERR("Call Home client \"%s\" is not of persistent connection type.");
2263 /* UNLOCK */
2264 nc_server_ch_client_unlock(client);
2265 return -1;
2266 }
2267
2268 client->conn.persist.ka_max_attempts = max_attempts;
2269
2270 /* UNLOCK */
2271 nc_server_ch_client_unlock(client);
2272
2273 return 0;
2274}
2275
2276API int
2277nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_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_PERIOD) {
2293 ERR("Call Home client \"%s\" is not of periodic connection type.");
2294 /* UNLOCK */
2295 nc_server_ch_client_unlock(client);
2296 return -1;
2297 }
2298
2299 client->conn.period.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_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2309{
2310 struct nc_ch_client *client;
2311
2312 if (!client_name) {
2313 ERRARG("client_name");
2314 return -1;
2315 } else if (!reconnect_timeout) {
2316 ERRARG("reconnect_timeout");
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_PERIOD) {
2327 ERR("Call Home client \"%s\" is not of periodic connection type.");
2328 /* UNLOCK */
2329 nc_server_ch_client_unlock(client);
2330 return -1;
2331 }
2332
2333 client->conn.period.reconnect_timeout = reconnect_timeout;
2334
2335 /* UNLOCK */
2336 nc_server_ch_client_unlock(client);
2337
2338 return 0;
2339}
2340
2341API int
2342nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
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 client->start_with = start_with;
2358
2359 /* UNLOCK */
2360 nc_server_ch_client_unlock(client);
2361
2362 return 0;
2363}
2364
2365API int
2366nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2367{
2368 struct nc_ch_client *client;
2369
2370 if (!client_name) {
2371 ERRARG("client_name");
2372 return -1;
2373 } else if (!max_attempts) {
2374 ERRARG("max_attempts");
2375 return -1;
2376 }
2377
2378 /* LOCK */
2379 client = nc_server_ch_client_lock(client_name, 0, NULL);
2380 if (!client) {
2381 return -1;
2382 }
2383
2384 client->max_attempts = max_attempts;
2385
2386 /* UNLOCK */
2387 nc_server_ch_client_unlock(client);
2388
2389 return 0;
2390}
2391
2392/* client lock is expected to be held */
2393static NC_MSG_TYPE
2394nc_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 +01002395{
Michal Vasko71090fc2016-05-24 16:37:28 +02002396 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002397 int sock, ret;
2398
Michal Vasko2e6defd2016-10-07 15:48:15 +02002399 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002400 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002401 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002402 }
2403
2404 *session = calloc(1, sizeof **session);
2405 if (!(*session)) {
2406 ERRMEM;
2407 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002408 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002409 }
2410 (*session)->status = NC_STATUS_STARTING;
2411 (*session)->side = NC_SERVER;
2412 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002413 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002414 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2415 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002416
2417 /* transport lock */
2418 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
2419 if (!(*session)->ti_lock) {
2420 ERRMEM;
2421 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002422 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002423 goto fail;
2424 }
2425 pthread_mutex_init((*session)->ti_lock, NULL);
2426
2427 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002428#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002429 if (client->ti == NC_TI_LIBSSH) {
2430 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002431 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002432 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002433
Michal Vasko71090fc2016-05-24 16:37:28 +02002434 if (ret < 0) {
2435 msgtype = NC_MSG_ERROR;
2436 goto fail;
2437 } else if (!ret) {
2438 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002439 goto fail;
2440 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002441 } else
2442#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002443#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002444 if (client->ti == NC_TI_OPENSSL) {
2445 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002446 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002447 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002448
Michal Vasko71090fc2016-05-24 16:37:28 +02002449 if (ret < 0) {
2450 msgtype = NC_MSG_ERROR;
2451 goto fail;
2452 } else if (!ret) {
2453 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002454 goto fail;
2455 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002456 } else
2457#endif
2458 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002459 ERRINT;
2460 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002461 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002462 goto fail;
2463 }
2464
2465 /* assign new SID atomically */
2466 /* LOCK */
2467 pthread_spin_lock(&server_opts.sid_lock);
2468 (*session)->id = server_opts.new_session_id++;
2469 /* UNLOCK */
2470 pthread_spin_unlock(&server_opts.sid_lock);
2471
2472 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002473 msgtype = nc_handshake(*session);
2474 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002475 goto fail;
2476 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002477 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002478 (*session)->status = NC_STATUS_RUNNING;
2479
Michal Vasko71090fc2016-05-24 16:37:28 +02002480 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002481
2482fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002483 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002484 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002485 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002486}
2487
Michal Vasko2e6defd2016-10-07 15:48:15 +02002488/* ms */
2489#define NC_CH_NO_ENDPT_WAIT 1000
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002490#define NC_CH_ENDPT_FAIL_WAIT 1000
Michal Vasko2e6defd2016-10-07 15:48:15 +02002491
2492struct nc_ch_client_thread_arg {
2493 char *client_name;
2494 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2495};
2496
2497static struct nc_ch_client *
2498nc_server_ch_client_with_endpt_lock(const char *name)
2499{
2500 struct nc_ch_client *client;
2501
2502 while (1) {
2503 /* LOCK */
2504 client = nc_server_ch_client_lock(name, 0, NULL);
2505 if (!client) {
2506 return NULL;
2507 }
2508 if (client->ch_endpt_count) {
2509 return client;
2510 }
2511 /* no endpoints defined yet */
2512
2513 /* UNLOCK */
2514 nc_server_ch_client_unlock(client);
2515
2516 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2517 }
2518
2519 return NULL;
2520}
2521
2522static int
2523nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2524{
2525 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002526 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002527 struct timespec ts;
2528 struct nc_ch_client *client;
2529
2530 /* session created, initialize condition */
2531 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2532 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2533 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2534 ERRMEM;
2535 nc_session_free(session, NULL);
2536 return -1;
2537 }
2538 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2539 pthread_cond_init(session->opts.server.ch_cond, NULL);
2540
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002541 session->flags |= NC_SESSION_CALLHOME;
2542
Michal Vasko2e6defd2016-10-07 15:48:15 +02002543 /* CH LOCK */
2544 pthread_mutex_lock(session->opts.server.ch_lock);
2545
2546 /* give the session to the user */
2547 data->session_clb(data->client_name, session);
2548
2549 do {
2550 nc_gettimespec(&ts);
2551 ts.tv_nsec += NC_CH_NO_ENDPT_WAIT * 1000000L;
2552 if (ts.tv_nsec > 1000000000L) {
2553 ts.tv_sec += ts.tv_nsec / 1000000000L;
2554 ts.tv_nsec %= 1000000000L;
2555 }
2556
2557 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2558 if (ret && (ret != ETIMEDOUT)) {
2559 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2560 goto ch_client_remove;
2561 }
2562
2563 /* check whether the client was not removed */
2564 /* LOCK */
2565 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2566 if (!client) {
2567 /* client was removed, finish thread */
2568 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2569 data->client_name);
2570 goto ch_client_remove;
2571 }
2572
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002573 if (client->conn_type == NC_CH_PERSIST) {
2574 /* TODO keep-alives */
2575 idle_timeout = client->conn.persist.idle_timeout;
2576 } else {
2577 idle_timeout = client->conn.period.idle_timeout;
2578 }
2579
2580 /* TODO only for sessions without subscriptions */
2581 if (idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
2582 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2583 session->status = NC_STATUS_INVALID;
2584 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2585 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002586
2587 /* UNLOCK */
2588 nc_server_ch_client_unlock(client);
2589
2590 } while (session->status == NC_STATUS_RUNNING);
2591
2592 /* CH UNLOCK */
2593 pthread_mutex_unlock(session->opts.server.ch_lock);
2594
2595 return 0;
2596
2597ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002598 /* make the session a standard one */
2599 pthread_cond_destroy(session->opts.server.ch_cond);
2600 free(session->opts.server.ch_cond);
2601 session->opts.server.ch_cond = NULL;
2602
2603 session->flags &= ~NC_SESSION_CALLHOME;
2604
Michal Vasko2e6defd2016-10-07 15:48:15 +02002605 /* CH UNLOCK */
2606 pthread_mutex_unlock(session->opts.server.ch_lock);
2607
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002608 pthread_mutex_destroy(session->opts.server.ch_lock);
2609 free(session->opts.server.ch_lock);
2610 session->opts.server.ch_lock = NULL;
2611
Michal Vasko2e6defd2016-10-07 15:48:15 +02002612 return 1;
2613}
2614
2615static void *
2616nc_ch_client_thread(void *arg)
2617{
2618 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2619 NC_MSG_TYPE msgtype;
2620 uint8_t cur_attempts = 0;
2621 uint16_t i;
2622 char *cur_endpt_name;
2623 struct nc_ch_endpt *cur_endpt;
2624 struct nc_session *session;
2625 struct nc_ch_client *client;
2626
2627 /* LOCK */
2628 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2629 if (!client) {
2630 goto cleanup;
2631 }
2632
2633 cur_endpt = &client->ch_endpts[0];
2634 cur_endpt_name = strdup(cur_endpt->name);
2635
Michal Vasko29af44b2016-10-13 10:59:55 +02002636 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002637 while (1) {
2638 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2639
2640 if (msgtype == NC_MSG_HELLO) {
2641 /* UNLOCK */
2642 nc_server_ch_client_unlock(client);
2643
Michal Vasko29af44b2016-10-13 10:59:55 +02002644 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002645 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2646 goto cleanup;
2647 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002648 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002649
2650 /* LOCK */
2651 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2652 if (!client) {
2653 goto cleanup;
2654 }
2655
2656 /* session changed status -> it was disconnected for whatever reason,
2657 * persistent connection immediately tries to reconnect, periodic waits some first */
2658 if (client->conn_type == NC_CH_PERIOD) {
2659 i = client->conn.period.reconnect_timeout;
2660
2661 /* UNLOCK */
2662 nc_server_ch_client_unlock(client);
2663
2664 /* TODO wake up sometimes to check for new notifications */
2665 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2666
2667 /* LOCK */
2668 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2669 if (!client) {
2670 goto cleanup;
2671 }
2672 }
2673
2674 /* set next endpoint to try */
2675 if (client->start_with == NC_CH_FIRST_LISTED) {
2676 cur_endpt = &client->ch_endpts[0];
2677 free(cur_endpt_name);
2678 cur_endpt_name = strdup(cur_endpt->name);
2679 } /* else we keep the current one */
2680 } else {
2681 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002682 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2683
Michal Vasko2e6defd2016-10-07 15:48:15 +02002684 ++cur_attempts;
2685 if (cur_attempts == client->max_attempts) {
2686 for (i = 0; i < client->ch_endpt_count; ++i) {
2687 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2688 break;
2689 }
2690 }
2691 if (i < client->ch_endpt_count - 1) {
2692 /* just go to the next endpoint */
2693 cur_endpt = &client->ch_endpts[i + 1];
2694 free(cur_endpt_name);
2695 cur_endpt_name = strdup(cur_endpt->name);
2696 } else {
2697 /* cur_endpoint was removed or is the last, either way start with the first one */
2698 cur_endpt = &client->ch_endpts[0];
2699 free(cur_endpt_name);
2700 cur_endpt_name = strdup(cur_endpt->name);
2701 }
2702
2703 cur_attempts = 0;
2704 } /* else we keep the current one */
2705 }
2706 }
2707
2708cleanup:
2709 VRB("Call Home client \"%s\" thread exit.", data->client_name);
2710
2711 free(data->client_name);
2712 free(data);
2713 return NULL;
2714}
2715
2716API int
2717nc_connect_ch_client_dispatch(const char *client_name,
2718 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2719 int ret;
2720 pthread_t tid;
2721 struct nc_ch_client_thread_arg *arg;
2722
2723 if (!client_name) {
2724 ERRARG("client_name");
2725 return -1;
2726 } else if (!session_clb) {
2727 ERRARG("session_clb");
2728 return -1;
2729 }
2730
2731 arg = malloc(sizeof *arg);
2732 if (!arg) {
2733 ERRMEM;
2734 return -1;
2735 }
2736 arg->client_name = strdup(client_name);
2737 if (!arg->client_name) {
2738 ERRMEM;
2739 free(arg);
2740 return -1;
2741 }
2742 arg->session_clb = session_clb;
2743
2744 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2745 if (ret) {
2746 ERR("Creating a new thread failed (%s).", strerror(ret));
2747 free(arg->client_name);
2748 free(arg);
2749 return -1;
2750 }
2751 /* the thread now manages arg */
2752
2753 pthread_detach(tid);
2754
2755 return 0;
2756}
2757
Radek Krejci53691be2016-02-22 13:58:37 +01002758#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002759
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002760API time_t
2761nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002762{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002763 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002764 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002765 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002766 }
2767
Michal Vasko2e6defd2016-10-07 15:48:15 +02002768 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002769}