blob: 64f93e5b4a516bf823c2868890459f1eeb894894 [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 }
289
Michal Vasko0190bc32016-03-02 15:47:49 +0100290 /* make the socket non-blocking */
291 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
292 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100293 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100294 return -1;
295 }
296
Michal Vasko3031aae2016-01-27 16:07:18 +0100297 if (idx) {
298 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100299 }
300
Michal Vasko086311b2016-01-08 09:53:11 +0100301 /* host was requested */
302 if (host) {
303 if (saddr.ss_family == AF_INET) {
304 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100305 if (*host) {
306 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
307 ERR("inet_ntop failed (%s).", strerror(errno));
308 free(*host);
309 *host = NULL;
310 }
Michal Vasko086311b2016-01-08 09:53:11 +0100311
Michal Vasko4eb3c312016-03-01 14:09:37 +0100312 if (port) {
313 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
314 }
315 } else {
316 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100317 }
318 } else if (saddr.ss_family == AF_INET6) {
319 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100320 if (*host) {
321 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
322 ERR("inet_ntop failed (%s).", strerror(errno));
323 free(*host);
324 *host = NULL;
325 }
Michal Vasko086311b2016-01-08 09:53:11 +0100326
Michal Vasko4eb3c312016-03-01 14:09:37 +0100327 if (port) {
328 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
329 }
330 } else {
331 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100332 }
333 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100334 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100335 }
336 }
337
338 return ret;
339}
340
Michal Vasko05ba9df2016-01-13 14:40:27 +0100341static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100342nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100343{
344 const char *identifier = NULL, *version = NULL, *format = NULL;
345 char *model_data = NULL;
346 const struct lys_module *module;
347 struct nc_server_error *err;
348 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100349 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350
351 LY_TREE_FOR(rpc->child, child) {
352 if (!strcmp(child->schema->name, "identifier")) {
353 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
354 } else if (!strcmp(child->schema->name, "version")) {
355 version = ((struct lyd_node_leaf_list *)child)->value_str;
356 } else if (!strcmp(child->schema->name, "format")) {
357 format = ((struct lyd_node_leaf_list *)child)->value_str;
358 }
359 }
360
361 /* check version */
362 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100363 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
364 nc_err_set_msg(err, "The requested version is not supported.", "en");
365 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100366 }
367
368 /* check and get module with the name identifier */
369 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
370 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200371 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
372 }
373 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100374 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
375 nc_err_set_msg(err, "The requested schema was not found.", "en");
376 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377 }
378
379 /* check format */
380 if (!format || !strcmp(format, "yang")) {
381 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
382 } else if (!strcmp(format, "yin")) {
383 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
384 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100385 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
386 nc_err_set_msg(err, "The requested format is not supported.", "en");
387 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100388 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200389 if (!model_data) {
390 ERRINT;
391 return NULL;
392 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100393
Michal Vasko303245c2016-03-24 15:20:03 +0100394 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200395 if (!sdata) {
396 ERRINT;
397 free(model_data);
398 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100399 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200400
Radek Krejci539efb62016-08-24 15:05:16 +0200401 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
402 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100403 if (!data) {
404 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200405 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100406 return NULL;
407 }
408
Radek Krejci36dfdb32016-09-01 16:56:35 +0200409 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100410}
411
412static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100413nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100414{
Michal Vasko428087d2016-01-14 16:04:28 +0100415 session->term_reason = NC_SESSION_TERM_CLOSED;
416 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100417}
418
Michal Vasko086311b2016-01-08 09:53:11 +0100419API int
420nc_server_init(struct ly_ctx *ctx)
421{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422 const struct lys_node *rpc;
423
Michal Vasko086311b2016-01-08 09:53:11 +0100424 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200425 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100426 return -1;
427 }
428
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100429 nc_init();
430
Michal Vasko05ba9df2016-01-13 14:40:27 +0100431 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100432 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100433 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100434 lys_set_private(rpc, nc_clb_default_get_schema);
435 }
436
437 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100438 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100439 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100440 lys_set_private(rpc, nc_clb_default_close_session);
441 }
442
Michal Vasko086311b2016-01-08 09:53:11 +0100443 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100444
445 server_opts.new_session_id = 1;
446 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
447
Michal Vasko086311b2016-01-08 09:53:11 +0100448 return 0;
449}
450
Michal Vaskob48aa812016-01-18 14:13:09 +0100451API void
452nc_server_destroy(void)
453{
454 pthread_spin_destroy(&server_opts.sid_lock);
455
Radek Krejci53691be2016-02-22 13:58:37 +0100456#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200457 nc_server_del_endpt(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100458#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100459 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100460}
461
Michal Vasko086311b2016-01-08 09:53:11 +0100462API int
463nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
464{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200465 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
466 ERRARG("basic_mode");
467 return -1;
468 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
469 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100470 return -1;
471 }
472
473 server_opts.wd_basic_mode = basic_mode;
474 server_opts.wd_also_supported = also_supported;
475 return 0;
476}
477
Michal Vasko1a38c862016-01-15 15:50:07 +0100478API void
Michal Vasko55f03972016-04-13 08:56:01 +0200479nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
480{
481 if (!basic_mode && !also_supported) {
482 ERRARG("basic_mode and also_supported");
483 return;
484 }
485
486 if (basic_mode) {
487 *basic_mode = server_opts.wd_basic_mode;
488 }
489 if (also_supported) {
490 *also_supported = server_opts.wd_also_supported;
491 }
492}
493
494API void
Michal Vasko086311b2016-01-08 09:53:11 +0100495nc_server_set_capab_interleave(int interleave_support)
496{
497 if (interleave_support) {
498 server_opts.interleave_capab = 1;
499 } else {
500 server_opts.interleave_capab = 0;
501 }
Michal Vasko086311b2016-01-08 09:53:11 +0100502}
503
Michal Vasko55f03972016-04-13 08:56:01 +0200504API int
505nc_server_get_capab_interleave(void)
506{
507 return server_opts.interleave_capab;
508}
509
Michal Vasko1a38c862016-01-15 15:50:07 +0100510API void
Michal Vasko086311b2016-01-08 09:53:11 +0100511nc_server_set_hello_timeout(uint16_t hello_timeout)
512{
Michal Vasko086311b2016-01-08 09:53:11 +0100513 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100514}
515
Michal Vasko55f03972016-04-13 08:56:01 +0200516API uint16_t
517nc_server_get_hello_timeout(void)
518{
519 return server_opts.hello_timeout;
520}
521
Michal Vasko1a38c862016-01-15 15:50:07 +0100522API void
Michal Vasko086311b2016-01-08 09:53:11 +0100523nc_server_set_idle_timeout(uint16_t idle_timeout)
524{
Michal Vasko086311b2016-01-08 09:53:11 +0100525 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100526}
527
Michal Vasko55f03972016-04-13 08:56:01 +0200528API uint16_t
529nc_server_get_idle_timeout(void)
530{
531 return server_opts.idle_timeout;
532}
533
Michal Vasko71090fc2016-05-24 16:37:28 +0200534API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100535nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100536{
Michal Vasko71090fc2016-05-24 16:37:28 +0200537 NC_MSG_TYPE msgtype;
538
Michal Vasko45e53ae2016-04-07 11:46:03 +0200539 if (!server_opts.ctx) {
540 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200541 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200542 } else if (fdin < 0) {
543 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200544 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200545 } else if (fdout < 0) {
546 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200547 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200548 } else if (!username) {
549 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200550 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200551 } else if (!session) {
552 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200553 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100554 }
555
556 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100557 *session = calloc(1, sizeof **session);
558 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100559 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200560 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100561 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100562 (*session)->status = NC_STATUS_STARTING;
563 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100564
565 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100566 (*session)->ti_type = NC_TI_FD;
567 (*session)->ti.fd.in = fdin;
568 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100569
570 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100571 (*session)->flags = NC_SESSION_SHAREDCTX;
572 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100573
Michal Vaskob48aa812016-01-18 14:13:09 +0100574 /* assign new SID atomically */
575 pthread_spin_lock(&server_opts.sid_lock);
576 (*session)->id = server_opts.new_session_id++;
577 pthread_spin_unlock(&server_opts.sid_lock);
578
Michal Vasko086311b2016-01-08 09:53:11 +0100579 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200580 msgtype = nc_handshake(*session);
581 if (msgtype != NC_MSG_HELLO) {
582 nc_session_free(*session, NULL);
583 *session = NULL;
584 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100585 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200586 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100587 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100588
Michal Vasko71090fc2016-05-24 16:37:28 +0200589 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100590}
Michal Vasko9e036d52016-01-08 10:49:26 +0100591
Michal Vaskob30b99c2016-07-26 11:35:43 +0200592static void
593nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
594{
595 uint8_t i, found = 0;
596
597 for (i = 0; i < ps->queue_len; ++i) {
598 /* idx round buffer adjust */
599 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
600 i = -ps->queue_begin;
601 }
602
603 if (found) {
604 /* move the value back one place */
605 if (ps->queue[ps->queue_begin + i] == id) {
606 /* another equal value, simply cannot be */
607 ERRINT;
608 }
609
610 if (ps->queue_begin + i == 0) {
611 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
612 } else {
613 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
614 }
615 } else if (ps->queue[ps->queue_begin + i] == id) {
616 /* found our id, there can be no more equal valid values */
617 found = 1;
618 }
619 }
620
621 if (!found) {
622 ERRINT;
623 }
624 --ps->queue_len;
625}
626
Michal Vaskof04a52a2016-04-07 10:52:10 +0200627int
Michal Vasko26043172016-07-26 14:08:59 +0200628nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200629{
630 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200631 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200632 struct timespec ts;
633
Radek Krejci7ac16052016-07-15 11:48:18 +0200634 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200635 ts.tv_sec += NC_READ_TIMEOUT;
636
637 /* LOCK */
638 ret = pthread_mutex_timedlock(&ps->lock, &ts);
639 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200640 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200641 return -1;
642 }
643
644 /* get a unique queue value (by adding 1 to the last added value, if any) */
645 if (ps->queue_len) {
646 queue_last = ps->queue_begin + ps->queue_len - 1;
647 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
648 queue_last -= NC_PS_QUEUE_SIZE;
649 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200650 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200651 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200652 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200653 }
654
655 /* add ourselves into the queue */
656 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200657 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200658 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200659 return -1;
660 }
661 ++ps->queue_len;
662 queue_last = ps->queue_begin + ps->queue_len - 1;
663 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
664 queue_last -= NC_PS_QUEUE_SIZE;
665 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200666 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200667
668 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200669 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200670 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200671 ts.tv_sec += NC_READ_TIMEOUT;
672
673 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
674 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200675 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200676 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200677 nc_ps_queue_remove_id(ps, *id);
678 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200679 return -1;
680 }
681 }
682
Michal Vaskobe86fe32016-04-07 10:43:03 +0200683 /* UNLOCK */
684 pthread_mutex_unlock(&ps->lock);
685
686 return 0;
687}
688
Michal Vaskof04a52a2016-04-07 10:52:10 +0200689int
Michal Vasko26043172016-07-26 14:08:59 +0200690nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200691{
692 int ret;
693 struct timespec ts;
694
Radek Krejci7ac16052016-07-15 11:48:18 +0200695 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200696 ts.tv_sec += NC_READ_TIMEOUT;
697
698 /* LOCK */
699 ret = pthread_mutex_timedlock(&ps->lock, &ts);
700 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200701 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200702 ret = -1;
703 }
704
Michal Vaskob30b99c2016-07-26 11:35:43 +0200705 /* we must be the first, it was our turn after all, right? */
706 if (ps->queue[ps->queue_begin] != id) {
707 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200708 /* UNLOCK */
709 if (!ret) {
710 pthread_mutex_unlock(&ps->lock);
711 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200712 return -1;
713 }
714
Michal Vaskobe86fe32016-04-07 10:43:03 +0200715 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200716 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200717
718 /* broadcast to all other threads that the queue moved */
719 pthread_cond_broadcast(&ps->cond);
720
Michal Vaskobe86fe32016-04-07 10:43:03 +0200721 /* UNLOCK */
722 if (!ret) {
723 pthread_mutex_unlock(&ps->lock);
724 }
725
726 return ret;
727}
728
Michal Vasko428087d2016-01-14 16:04:28 +0100729API struct nc_pollsession *
730nc_ps_new(void)
731{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100732 struct nc_pollsession *ps;
733
734 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100735 if (!ps) {
736 ERRMEM;
737 return NULL;
738 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200739 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100740 pthread_mutex_init(&ps->lock, NULL);
741
742 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100743}
744
745API void
746nc_ps_free(struct nc_pollsession *ps)
747{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100748 if (!ps) {
749 return;
750 }
751
Michal Vaskobe86fe32016-04-07 10:43:03 +0200752 if (ps->queue_len) {
753 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
754 }
755
Michal Vasko3a715132016-01-21 15:40:31 +0100756 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100757 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100758 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200759 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100760
Michal Vasko428087d2016-01-14 16:04:28 +0100761 free(ps);
762}
763
764API int
765nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
766{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200767 uint8_t q_id;
768
Michal Vasko45e53ae2016-04-07 11:46:03 +0200769 if (!ps) {
770 ERRARG("ps");
771 return -1;
772 } else if (!session) {
773 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100774 return -1;
775 }
776
Michal Vasko48a63ed2016-03-01 09:48:21 +0100777 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200778 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200779 return -1;
780 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100781
Michal Vasko428087d2016-01-14 16:04:28 +0100782 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100783 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
784 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
785 if (!ps->pfds || !ps->sessions) {
786 ERRMEM;
787 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200788 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100789 return -1;
790 }
Michal Vasko428087d2016-01-14 16:04:28 +0100791
792 switch (session->ti_type) {
793 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100794 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100795 break;
796
Radek Krejci53691be2016-02-22 13:58:37 +0100797#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100798 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100799 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100800 break;
801#endif
802
Radek Krejci53691be2016-02-22 13:58:37 +0100803#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100804 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100805 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100806 break;
807#endif
808
809 default:
810 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200812 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100813 return -1;
814 }
Michal Vasko3a715132016-01-21 15:40:31 +0100815 ps->pfds[ps->session_count - 1].events = POLLIN;
816 ps->pfds[ps->session_count - 1].revents = 0;
817 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100818
Michal Vasko48a63ed2016-03-01 09:48:21 +0100819 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200820 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100821}
822
Michal Vasko48a63ed2016-03-01 09:48:21 +0100823static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100824_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100825{
826 uint16_t i;
827
Radek Krejcid5f978f2016-03-03 13:14:45 +0100828 if (index >= 0) {
829 i = (uint16_t)index;
830 goto remove;
831 }
Michal Vasko428087d2016-01-14 16:04:28 +0100832 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100833 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100834remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100835 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100836 if (i < ps->session_count) {
837 ps->sessions[i] = ps->sessions[ps->session_count];
838 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
839 } else if (!ps->session_count) {
840 free(ps->sessions);
841 ps->sessions = NULL;
842 free(ps->pfds);
843 ps->pfds = NULL;
844 }
Michal Vasko428087d2016-01-14 16:04:28 +0100845 return 0;
846 }
847 }
848
Michal Vaskof0537d82016-01-29 14:42:38 +0100849 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100850}
851
Michal Vasko48a63ed2016-03-01 09:48:21 +0100852API int
853nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
854{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200855 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200856 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100857
Michal Vasko45e53ae2016-04-07 11:46:03 +0200858 if (!ps) {
859 ERRARG("ps");
860 return -1;
861 } else if (!session) {
862 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100863 return -1;
864 }
865
866 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200867 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200868 return -1;
869 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100870
Radek Krejcid5f978f2016-03-03 13:14:45 +0100871 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100872
873 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200874 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100875
Michal Vaskobe86fe32016-04-07 10:43:03 +0200876 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100877}
878
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100879API uint16_t
880nc_ps_session_count(struct nc_pollsession *ps)
881{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200882 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100883 uint16_t count;
884
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100885 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200886 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100887 return 0;
888 }
889
Michal Vasko48a63ed2016-03-01 09:48:21 +0100890 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200891 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200892 return -1;
893 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100894
895 count = ps->session_count;
896
897 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200898 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100899
900 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100901}
902
Michal Vasko71090fc2016-05-24 16:37:28 +0200903/* must be called holding the session lock!
904 * returns: NC_PSPOLL_ERROR,
905 * NC_PSPOLL_BAD_RPC,
906 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
907 * NC_PSPOLL_RPC
908 */
909static int
Radek Krejci93e80222016-10-03 13:34:25 +0200910nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100911{
912 struct lyxml_elem *xml = NULL;
913 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200914 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200915 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100916
Michal Vasko45e53ae2016-04-07 11:46:03 +0200917 if (!session) {
918 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200919 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200920 } else if (!rpc) {
921 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200922 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100923 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100924 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200925 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100926 }
927
928 msgtype = nc_read_msg(session, &xml);
929
930 switch (msgtype) {
931 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200932 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100933 if (!*rpc) {
934 ERRMEM;
935 goto error;
936 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100937
Radek Krejcif93c7d42016-04-06 13:41:15 +0200938 ly_errno = LY_SUCCESS;
Michal Vasko68b3f292016-09-16 12:00:32 +0200939 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_RPC | LYD_OPT_DESTRUCT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100940 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200941 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200942 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200943 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200944 nc_server_reply_free(reply);
945 if (ret == -1) {
946 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200947 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200948 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
949 } else {
950 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100951 }
Michal Vasko428087d2016-01-14 16:04:28 +0100952 (*rpc)->root = xml;
953 break;
954 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100955 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200956 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100957 goto error;
958 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100959 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200960 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100961 goto error;
962 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100963 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200964 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100965 goto error;
966 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200967 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100968 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
969 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200970 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100971 break;
972 }
973
Michal Vasko71090fc2016-05-24 16:37:28 +0200974 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100975
976error:
977 /* cleanup */
978 lyxml_free(server_opts.ctx, xml);
979
Michal Vasko71090fc2016-05-24 16:37:28 +0200980 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100981}
982
fanchanghu966f2de2016-07-21 02:28:57 -0400983API void
984nc_set_global_rpc_clb(nc_rpc_clb clb)
985{
986 global_rpc_clb = clb;
987}
988
Radek Krejci93e80222016-10-03 13:34:25 +0200989API NC_MSG_TYPE
990nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
991{
992 NC_MSG_TYPE result = NC_MSG_NOTIF;
993 int ret;
994
995 /* check parameters */
996 if (!session) {
997 ERRARG("session");
998 return NC_MSG_ERROR;
999 } else if (!notif || !notif->tree || !notif->eventtime) {
1000 ERRARG("notif");
1001 return NC_MSG_ERROR;
1002 }
1003
1004 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
1005 ret = nc_timedlock(session->ti_lock, timeout, __func__);
1006 if (ret < 0) {
1007 return NC_MSG_ERROR;
1008 } else if (!ret) {
1009 return NC_MSG_WOULDBLOCK;
1010 }
1011
1012 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1013 if (ret == -1) {
1014 ERR("Session %u: failed to write notification.", session->id);
1015 result = NC_MSG_ERROR;
1016 }
1017 pthread_mutex_unlock(session->ti_lock);
1018
1019 return result;
1020}
1021
Michal Vasko71090fc2016-05-24 16:37:28 +02001022/* must be called holding the session lock!
1023 * returns: NC_PSPOLL_ERROR,
1024 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1025 * NC_PSPOLL_REPLY_ERROR,
1026 * 0
1027 */
1028static int
Radek Krejci93e80222016-10-03 13:34:25 +02001029nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001030{
1031 nc_rpc_clb clb;
1032 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001033 struct lys_node *rpc_act = NULL;
1034 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001035 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001036
Michal Vasko4a827e52016-03-03 10:59:00 +01001037 if (!rpc) {
1038 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001039 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001040 }
1041
Michal Vasko90e8e692016-07-13 12:27:57 +02001042 if (rpc->tree->schema->nodetype == LYS_RPC) {
1043 /* RPC */
1044 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001045 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001046 /* action */
1047 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1048 if (elem->schema->nodetype == LYS_ACTION) {
1049 rpc_act = elem->schema;
1050 break;
1051 }
1052 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001053 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001054 if (!rpc_act) {
1055 ERRINT;
1056 return NC_PSPOLL_ERROR;
1057 }
1058 }
1059
1060 if (!rpc_act->priv) {
1061 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001062 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001063 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001064 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001065 reply = clb(rpc->tree, session);
1066 }
1067
1068 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001069 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001070 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001071 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1072 if (reply->type == NC_RPL_ERROR) {
1073 ret |= NC_PSPOLL_REPLY_ERROR;
1074 }
1075 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001076
Michal Vasko71090fc2016-05-24 16:37:28 +02001077 if (r == -1) {
1078 ERR("Session %u: failed to write reply.", session->id);
1079 ret |= NC_PSPOLL_ERROR;
1080 }
Michal Vasko428087d2016-01-14 16:04:28 +01001081
1082 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1083 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1084 session->status = NC_STATUS_INVALID;
1085 }
1086
Michal Vasko71090fc2016-05-24 16:37:28 +02001087 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001088}
1089
1090API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001091nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001092{
1093 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001094 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +01001095 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001096 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +02001097 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001098 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +01001099
1100 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001101 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001102 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001103 }
1104
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001105 cur_time = time(NULL);
1106
Michal Vasko48a63ed2016-03-01 09:48:21 +01001107 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001108 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001109 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001110 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001111
Michal Vasko428087d2016-01-14 16:04:28 +01001112 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001113 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1114 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001115 ret = NC_PSPOLL_ERROR;
1116 if (session) {
1117 *session = ps->sessions[i];
1118 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001119 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001120 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001121
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001122 /* TODO invalidate only sessions without subscription */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001123 if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME) && server_opts.idle_timeout
1124 && (cur_time >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001125 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1126 ps->sessions[i]->status = NC_STATUS_INVALID;
1127 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001128 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1129 if (session) {
1130 *session = ps->sessions[i];
1131 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001132 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001133 }
1134
Michal Vasko3a715132016-01-21 15:40:31 +01001135 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001136 break;
1137 }
Michal Vasko428087d2016-01-14 16:04:28 +01001138 }
1139
Michal Vaskobd8ef262016-01-20 11:09:27 +01001140 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001141#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001142retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001143#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001144 /* no leftover event */
1145 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001146 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001147 if (ret < 0) {
1148 ERR("Poll failed (%s).", strerror(errno));
1149 ret = NC_PSPOLL_ERROR;
1150 goto finish;
1151 } else if (!ret) {
1152 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001153 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001154 }
Michal Vasko428087d2016-01-14 16:04:28 +01001155 }
1156
Michal Vaskobd8ef262016-01-20 11:09:27 +01001157 /* find the first fd with POLLIN, we don't care if there are more now */
1158 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001159 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001160 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1161 ps->sessions[i]->status = NC_STATUS_INVALID;
1162 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001163 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1164 if (session) {
1165 *session = ps->sessions[i];
1166 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001167 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001168 } else if (ps->pfds[i].revents & POLLERR) {
1169 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1170 ps->sessions[i]->status = NC_STATUS_INVALID;
1171 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001172 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1173 if (session) {
1174 *session = ps->sessions[i];
1175 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001176 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001177 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001178#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001179 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001180 uint16_t j;
1181
Michal Vasko96164bf2016-01-21 15:41:58 +01001182 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001183 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001184
1185 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001186 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001187 for (j = i + 1; j < ps->session_count; ++j) {
1188 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1189 ps->pfds[j].revents = 0;
1190 }
1191 }
1192 }
1193
Michal Vasko71090fc2016-05-24 16:37:28 +02001194 /* SSH message only */
1195 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001196 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001197 if (session) {
1198 *session = ps->sessions[i];
1199 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001200 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001201
1202 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001203 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001204 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001205 if (i == ps->session_count - 1) {
1206 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001207 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001208 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001209 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001210 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001211 }
Michal Vasko8c748832016-02-03 15:32:16 +01001212 /* ... retry polling reasonable time apart ... */
1213 usleep(NC_TIMEOUT_STEP);
1214 if (timeout > 0) {
1215 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001216 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001217 }
1218 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001219 }
1220 /* check other sessions */
1221 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001222 }
1223 }
Radek Krejci53691be2016-02-22 13:58:37 +01001224#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001225
Michal Vaskobd8ef262016-01-20 11:09:27 +01001226 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001227 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001228 break;
1229 }
1230 }
1231
1232 if (i == ps->session_count) {
1233 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001234 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001235 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001236 }
1237
1238 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001239 cur_session = ps->sessions[i];
1240 if (session) {
1241 *session = cur_session;
1242 }
Michal Vasko428087d2016-01-14 16:04:28 +01001243
Michal Vaskobd8ef262016-01-20 11:09:27 +01001244 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal vasko953939c2016-10-04 13:46:20 +02001245 ret = nc_timedlock(cur_session->ti_lock, timeout, __func__);
Michal Vasko71090fc2016-05-24 16:37:28 +02001246 if (ret < 0) {
1247 ret = NC_PSPOLL_ERROR;
1248 goto finish;
1249 } else if (!ret) {
1250 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001251 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001252 }
1253
Radek Krejci93e80222016-10-03 13:34:25 +02001254 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001255 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1256 pthread_mutex_unlock(cur_session->ti_lock);
1257 if (cur_session->status != NC_STATUS_RUNNING) {
1258 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001259 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001260 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001261 }
1262
Michal Vasko2e6defd2016-10-07 15:48:15 +02001263 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001264
Michal Vasko428087d2016-01-14 16:04:28 +01001265 /* process RPC */
Radek Krejci93e80222016-10-03 13:34:25 +02001266 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001267
Michal Vasko71090fc2016-05-24 16:37:28 +02001268 pthread_mutex_unlock(cur_session->ti_lock);
1269 if (cur_session->status != NC_STATUS_RUNNING) {
1270 ret |= NC_PSPOLL_SESSION_TERM;
1271 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1272 ret |= NC_PSPOLL_SESSION_ERROR;
1273 }
Michal Vasko428087d2016-01-14 16:04:28 +01001274 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001275
Michal Vaskoca4a2422016-02-02 12:17:14 +01001276 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001277
1278 /* is there some other socket waiting? */
1279 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001280 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001281 ret |= NC_PSPOLL_PENDING;
1282 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001283 }
1284 }
1285
Michal Vasko48a63ed2016-03-01 09:48:21 +01001286finish:
1287 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001288 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001289 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001290}
1291
Michal Vaskod09eae62016-02-01 10:32:52 +01001292API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001293nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001294{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001295 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001296 uint16_t i;
1297 struct nc_session *session;
1298
Michal Vasko9a25e932016-02-01 10:36:42 +01001299 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001300 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001301 return;
1302 }
1303
Michal Vasko48a63ed2016-03-01 09:48:21 +01001304 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001305 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001306 return;
1307 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001308
Michal Vasko48a63ed2016-03-01 09:48:21 +01001309 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001310 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001311 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001312 }
1313 free(ps->sessions);
1314 ps->sessions = NULL;
1315 free(ps->pfds);
1316 ps->pfds = NULL;
1317 ps->session_count = 0;
1318 } else {
1319 for (i = 0; i < ps->session_count; ) {
1320 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1321 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001322 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001323 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001324 continue;
1325 }
1326
1327 ++i;
1328 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001329 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001330
1331 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001332 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001333}
1334
Radek Krejci53691be2016-02-22 13:58:37 +01001335#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001336
Michal Vaskoe2713da2016-08-22 16:06:40 +02001337API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001338nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001339{
Michal Vasko3031aae2016-01-27 16:07:18 +01001340 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +01001341
Michal Vasko45e53ae2016-04-07 11:46:03 +02001342 if (!name) {
1343 ERRARG("name");
1344 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001345 }
1346
Michal Vasko51e514d2016-02-02 15:51:52 +01001347 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001348 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001349
1350 /* check name uniqueness */
1351 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001352 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001353 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001354 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001355 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001356 return -1;
1357 }
1358 }
1359
Michal Vasko3031aae2016-01-27 16:07:18 +01001360 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001361 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001362 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001363 ERRMEM;
1364 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001365 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001366 return -1;
1367 }
1368 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001369 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001370
Michal Vaskoe2713da2016-08-22 16:06:40 +02001371 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001372 if (!server_opts.binds) {
1373 ERRMEM;
1374 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001375 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001376 return -1;
1377 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001378
Michal Vasko2e6defd2016-10-07 15:48:15 +02001379 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1380 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1381 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001382 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001383
1384 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001385#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001386 case NC_TI_LIBSSH:
1387 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1388 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1389 ERRMEM;
1390 /* WRITE UNLOCK */
1391 pthread_rwlock_unlock(&server_opts.endpt_lock);
1392 return -1;
1393 }
1394 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1395 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1396 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1397 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1398 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001399#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001400#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001401 case NC_TI_OPENSSL:
1402 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1403 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1404 ERRMEM;
1405 /* WRITE UNLOCK */
1406 pthread_rwlock_unlock(&server_opts.endpt_lock);
1407 return -1;
1408 }
1409 break;
1410#endif
1411 default:
1412 ERRINT;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001413 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001414 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001415 return -1;
1416 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001417
Michal Vasko2e6defd2016-10-07 15:48:15 +02001418 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001419
Michal Vasko3031aae2016-01-27 16:07:18 +01001420 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001421 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001422
Michal Vasko9e036d52016-01-08 10:49:26 +01001423 return 0;
1424}
1425
Michal Vasko3031aae2016-01-27 16:07:18 +01001426int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001427nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001428{
1429 struct nc_endpt *endpt;
1430 struct nc_bind *bind = NULL;
1431 uint16_t i;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001432 int sock = -1, set_addr;
Michal Vaskoda514772016-02-01 11:32:01 +01001433
Michal Vasko45e53ae2016-04-07 11:46:03 +02001434 if (!endpt_name) {
1435 ERRARG("endpt_name");
1436 return -1;
1437 } else if ((!address && !port) || (address && port)) {
1438 ERRARG("address and port");
1439 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001440 }
1441
Michal Vaskoe2713da2016-08-22 16:06:40 +02001442 if (address) {
1443 set_addr = 1;
1444 } else {
1445 set_addr = 0;
1446 }
1447
Michal Vasko51e514d2016-02-02 15:51:52 +01001448 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001449 endpt = nc_server_endpt_lock(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001450 if (!endpt) {
1451 return -1;
1452 }
1453
Michal Vaskoe2713da2016-08-22 16:06:40 +02001454 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001455
Michal Vaskoe2713da2016-08-22 16:06:40 +02001456 if (set_addr) {
1457 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001458 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001459 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001460 }
1461
Michal Vaskoe2713da2016-08-22 16:06:40 +02001462 /* we have all the information we need to create a listening socket */
1463 if (address && port) {
1464 /* create new socket, close the old one */
1465 sock = nc_sock_listen(address, port);
1466 if (sock == -1) {
1467 goto fail;
1468 }
1469
1470 if (bind->sock > -1) {
1471 close(bind->sock);
1472 }
1473 bind->sock = sock;
1474 } /* else we are just setting address or port */
1475
1476 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001477 lydict_remove(server_opts.ctx, bind->address);
1478 bind->address = lydict_insert(server_opts.ctx, address, 0);
1479 } else {
1480 bind->port = port;
1481 }
1482
Michal Vaskoe2713da2016-08-22 16:06:40 +02001483 if (sock > -1) {
1484#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001485 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001486#elif defined(NC_ENABLED_SSH)
1487 VRB("Listening on %s:%u for SSH connections.", address, port);
1488#else
1489 VRB("Listening on %s:%u for TLS connections.", address, port);
1490#endif
1491 }
1492
Michal Vasko51e514d2016-02-02 15:51:52 +01001493 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001494 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001495 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001496
1497fail:
1498 /* UNLOCK */
1499 nc_server_endpt_unlock(endpt);
1500 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001501}
1502
Michal Vaskoe2713da2016-08-22 16:06:40 +02001503API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001504nc_server_endpt_set_address(const char *endpt_name, const char *address)
1505{
1506 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1507}
1508
1509API int
1510nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1511{
1512 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1513}
1514
1515API int
Michal Vaskoe2713da2016-08-22 16:06:40 +02001516nc_server_del_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001517{
1518 uint32_t i;
1519 int ret = -1;
1520
Michal Vasko3031aae2016-01-27 16:07:18 +01001521 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001522 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001523
Michal Vaskoe2713da2016-08-22 16:06:40 +02001524 if (!name) {
1525 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001526 for (i = 0; i < server_opts.endpt_count; ++i) {
1527 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001528 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1529 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001530#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001531 case NC_TI_LIBSSH:
1532 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1533 free(server_opts.endpts[i].opts.ssh);
1534 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001535#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001536#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001537 case NC_TI_OPENSSL:
1538 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1539 free(server_opts.endpts[i].opts.tls);
1540 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001541#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001542 default:
1543 ERRINT;
1544 /* won't get here ...*/
1545 break;
1546 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001547 ret = 0;
1548 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001549 free(server_opts.endpts);
1550 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001551
1552 /* remove all binds */
1553 for (i = 0; i < server_opts.endpt_count; ++i) {
1554 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1555 if (server_opts.binds[i].sock > -1) {
1556 close(server_opts.binds[i].sock);
1557 }
1558 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001559 free(server_opts.binds);
1560 server_opts.binds = NULL;
1561
Michal Vasko3031aae2016-01-27 16:07:18 +01001562 server_opts.endpt_count = 0;
1563
Michal Vasko1a38c862016-01-15 15:50:07 +01001564 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001565 /* remove one endpoint with bind(s) */
Michal Vasko3031aae2016-01-27 16:07:18 +01001566 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001567 if (!strcmp(server_opts.endpts[i].name, name)) {
1568 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001569 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001570 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1571 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001572#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001573 case NC_TI_LIBSSH:
1574 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1575 free(server_opts.endpts[i].opts.ssh);
1576 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001577#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001578#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001579 case NC_TI_OPENSSL:
1580 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1581 free(server_opts.endpts[i].opts.tls);
1582 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001583#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001584 default:
1585 ERRINT;
1586 break;
1587 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001588
Michal Vaskoe2713da2016-08-22 16:06:40 +02001589 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001590 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1591 if (server_opts.binds[i].sock > -1) {
1592 close(server_opts.binds[i].sock);
1593 }
1594
1595 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001596 --server_opts.endpt_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001597 if (i < server_opts.endpt_count) {
1598 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1599 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001600 } else if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001601 free(server_opts.binds);
1602 server_opts.binds = NULL;
1603 free(server_opts.endpts);
1604 server_opts.endpts = NULL;
1605 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001606
1607 ret = 0;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001608 break;
Michal Vasko1a38c862016-01-15 15:50:07 +01001609 }
1610 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001611 }
1612
Michal Vasko3031aae2016-01-27 16:07:18 +01001613 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001614 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001615
Michal Vasko9e036d52016-01-08 10:49:26 +01001616 return ret;
1617}
1618
Michal Vasko71090fc2016-05-24 16:37:28 +02001619API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001620nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001621{
Michal Vasko71090fc2016-05-24 16:37:28 +02001622 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001623 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001624 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001625 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001626
Michal Vasko45e53ae2016-04-07 11:46:03 +02001627 if (!server_opts.ctx) {
1628 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001629 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001630 } else if (!session) {
1631 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001632 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001633 }
1634
Michal Vasko51e514d2016-02-02 15:51:52 +01001635 /* we have to hold WRITE for the whole time, since there is not
1636 * a way of downgrading the lock to READ */
1637 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001638 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001639
1640 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001641 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001642 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001643 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001644 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001645 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001646
Michal Vaskoe2713da2016-08-22 16:06:40 +02001647 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001648 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001649 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001650 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001651 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001652 if (!ret) {
1653 return NC_MSG_WOULDBLOCK;
1654 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001655 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001656 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001657 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001658
Michal Vasko1a38c862016-01-15 15:50:07 +01001659 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001660 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001661 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001662 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001663 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001664 msgtype = NC_MSG_ERROR;
1665 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001666 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001667 (*session)->status = NC_STATUS_STARTING;
1668 (*session)->side = NC_SERVER;
1669 (*session)->ctx = server_opts.ctx;
1670 (*session)->flags = NC_SESSION_SHAREDCTX;
1671 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1672 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001673
1674 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001675 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1676 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001677 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001678 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001679 msgtype = NC_MSG_ERROR;
1680 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001681 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001682 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001683
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001684 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001685#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001686 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1687 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01001688 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001689 if (ret < 0) {
1690 msgtype = NC_MSG_ERROR;
1691 goto cleanup;
1692 } else if (!ret) {
1693 msgtype = NC_MSG_WOULDBLOCK;
1694 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001695 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001696 } else
1697#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001698#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001699 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1700 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01001701 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001702 if (ret < 0) {
1703 msgtype = NC_MSG_ERROR;
1704 goto cleanup;
1705 } else if (!ret) {
1706 msgtype = NC_MSG_WOULDBLOCK;
1707 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001708 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001709 } else
1710#endif
1711 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001712 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001713 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001714 msgtype = NC_MSG_ERROR;
1715 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001716 }
1717
Michal Vasko2cc4c682016-03-01 09:16:48 +01001718 (*session)->data = NULL;
1719
Michal Vasko51e514d2016-02-02 15:51:52 +01001720 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001721 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001722
Michal Vaskob48aa812016-01-18 14:13:09 +01001723 /* assign new SID atomically */
1724 /* LOCK */
1725 pthread_spin_lock(&server_opts.sid_lock);
1726 (*session)->id = server_opts.new_session_id++;
1727 /* UNLOCK */
1728 pthread_spin_unlock(&server_opts.sid_lock);
1729
Michal Vasko9e036d52016-01-08 10:49:26 +01001730 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001731 msgtype = nc_handshake(*session);
1732 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001733 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001734 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001735 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001736 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001737 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001738 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001739
Michal Vasko71090fc2016-05-24 16:37:28 +02001740 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001741
Michal Vasko71090fc2016-05-24 16:37:28 +02001742cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001743 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001744 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001745
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001746 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001747 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001748 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001749}
1750
Michal Vasko2e6defd2016-10-07 15:48:15 +02001751API int
1752nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1753{
1754 uint16_t i;
1755
1756 if (!name) {
1757 ERRARG("name");
1758 return -1;
1759 } else if (!ti) {
1760 ERRARG("ti");
1761 return -1;
1762 }
1763
1764 /* WRITE LOCK */
1765 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1766
1767 /* check name uniqueness */
1768 for (i = 0; i < server_opts.ch_client_count; ++i) {
1769 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1770 ERR("Call Home client \"%s\" already exists.", name);
1771 /* WRITE UNLOCK */
1772 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1773 return -1;
1774 }
1775 }
1776
1777 ++server_opts.ch_client_count;
1778 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1779 if (!server_opts.ch_clients) {
1780 ERRMEM;
1781 /* WRITE UNLOCK */
1782 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1783 return -1;
1784 }
1785 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1786 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001787 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1788 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001789
1790 switch (ti) {
1791#ifdef NC_ENABLED_SSH
1792 case NC_TI_LIBSSH:
1793 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1794 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1795 ERRMEM;
1796 /* WRITE UNLOCK */
1797 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1798 return -1;
1799 }
1800 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1801 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1802 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1803 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1804 break;
1805#endif
1806#ifdef NC_ENABLED_TLS
1807 case NC_TI_OPENSSL:
1808 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1809 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1810 ERRMEM;
1811 /* WRITE UNLOCK */
1812 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1813 return -1;
1814 }
1815 break;
1816#endif
1817 default:
1818 ERRINT;
1819 /* WRITE UNLOCK */
1820 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1821 return -1;
1822 }
1823
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001824 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
1825
Michal Vasko2e6defd2016-10-07 15:48:15 +02001826 /* set CH default options */
1827 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1828 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1829
1830 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1831
1832 /* WRITE UNLOCK */
1833 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1834
1835 return 0;
1836}
1837
1838API int
1839nc_server_ch_del_client(const char *name)
1840{
1841 uint16_t i, j;
1842 int ret = -1;
1843
1844 /* WRITE LOCK */
1845 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1846
1847 if (!name) {
1848 /* remove all CH clients */
1849 for (i = 0; i < server_opts.ch_client_count; ++i) {
1850 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1851
1852 /* remove all endpoints */
1853 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1854 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1855 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1856 }
1857 free(server_opts.ch_clients[i].ch_endpts);
1858
1859 switch (server_opts.ch_clients[i].ti) {
1860#ifdef NC_ENABLED_SSH
1861 case NC_TI_LIBSSH:
1862 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1863 free(server_opts.ch_clients[i].opts.ssh);
1864 break;
1865#endif
1866#ifdef NC_ENABLED_TLS
1867 case NC_TI_OPENSSL:
1868 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1869 free(server_opts.ch_clients[i].opts.tls);
1870 break;
1871#endif
1872 default:
1873 ERRINT;
1874 /* won't get here ...*/
1875 break;
1876 }
1877
1878 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1879
1880 ret = 0;
1881 }
1882 free(server_opts.ch_clients);
1883 server_opts.ch_clients = NULL;
1884
1885 server_opts.ch_client_count = 0;
1886
1887 } else {
1888 /* remove one client with endpoint(s) */
1889 for (i = 0; i < server_opts.ch_client_count; ++i) {
1890 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1891 /* remove endpt */
1892 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1893
1894 switch (server_opts.ch_clients[i].ti) {
1895#ifdef NC_ENABLED_SSH
1896 case NC_TI_LIBSSH:
1897 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1898 free(server_opts.ch_clients[i].opts.ssh);
1899 break;
1900#endif
1901#ifdef NC_ENABLED_TLS
1902 case NC_TI_OPENSSL:
1903 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1904 free(server_opts.ch_clients[i].opts.tls);
1905 break;
1906#endif
1907 default:
1908 ERRINT;
1909 break;
1910 }
1911
1912 /* remove all endpoints */
1913 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1914 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1915 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1916 }
1917 free(server_opts.ch_clients[i].ch_endpts);
1918
1919 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1920
1921 /* move last client and endpoint(s) to the empty space */
1922 --server_opts.ch_client_count;
1923 if (i < server_opts.ch_client_count) {
1924 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
1925 sizeof *server_opts.ch_clients);
1926 } else if (!server_opts.ch_client_count) {
1927 free(server_opts.ch_clients);
1928 server_opts.ch_clients = NULL;
1929 }
1930
1931 ret = 0;
1932 break;
1933 }
1934 }
1935 }
1936
1937 /* WRITE UNLOCK */
1938 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1939
1940 return ret;
1941}
1942
1943API int
1944nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
1945{
1946 uint16_t i;
1947 struct nc_ch_client *client;
1948
1949 if (!client_name) {
1950 ERRARG("client_name");
1951 return -1;
1952 } else if (!endpt_name) {
1953 ERRARG("endpt_name");
1954 return -1;
1955 }
1956
1957 /* LOCK */
1958 client = nc_server_ch_client_lock(client_name, 0, NULL);
1959 if (!client) {
1960 return -1;
1961 }
1962
1963 for (i = 0; i < client->ch_endpt_count; ++i) {
1964 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
1965 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
1966 /* UNLOCK */
1967 nc_server_ch_client_unlock(client);
1968 return -1;
1969 }
1970 }
1971
1972 ++client->ch_endpt_count;
1973 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
1974 if (!client->ch_endpts) {
1975 ERRMEM;
1976 /* UNLOCK */
1977 nc_server_ch_client_unlock(client);
1978 return -1;
1979 }
1980
1981 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
1982 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
1983 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
1984
1985 /* UNLOCK */
1986 nc_server_ch_client_unlock(client);
1987
1988 return 0;
1989}
1990
1991API int
1992nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
1993{
1994 uint16_t i;
1995 int ret = -1;
1996 struct nc_ch_client *client;
1997
1998 if (!client_name) {
1999 ERRARG("client_name");
2000 return -1;
2001 }
2002
2003 /* LOCK */
2004 client = nc_server_ch_client_lock(client_name, 0, NULL);
2005 if (!client) {
2006 return -1;
2007 }
2008
2009 if (!endpt_name) {
2010 /* remove all endpoints */
2011 for (i = 0; i < client->ch_endpt_count; ++i) {
2012 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2013 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2014 }
2015 free(client->ch_endpts);
2016 client->ch_endpts = NULL;
2017 client->ch_endpt_count = 0;
2018
2019 ret = 0;
2020 } else {
2021 for (i = 0; i < client->ch_endpt_count; ++i) {
2022 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2023 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2024 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2025 }
2026
2027 /* move last endpoint to the empty space */
2028 --client->ch_endpt_count;
2029 if (i < client->ch_endpt_count) {
2030 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2031 } else if (!server_opts.ch_client_count) {
2032 free(server_opts.ch_clients);
2033 server_opts.ch_clients = NULL;
2034 }
2035
2036 ret = 0;
2037 break;
2038 }
2039 }
2040
2041 /* UNLOCK */
2042 nc_server_ch_client_unlock(client);
2043
2044 return ret;
2045}
2046
2047API int
2048nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2049{
2050 uint16_t i;
2051 int ret = -1;
2052 struct nc_ch_client *client;
2053
2054 if (!client_name) {
2055 ERRARG("client_name");
2056 return -1;
2057 } else if (!endpt_name) {
2058 ERRARG("endpt_name");
2059 return -1;
2060 } else if (!address) {
2061 ERRARG("address");
2062 return -1;
2063 }
2064
2065 /* LOCK */
2066 client = nc_server_ch_client_lock(client_name, 0, NULL);
2067 if (!client) {
2068 return -1;
2069 }
2070
2071 for (i = 0; i < client->ch_endpt_count; ++i) {
2072 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2073 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2074 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2075
2076 ret = 0;
2077 break;
2078 }
2079 }
2080
2081 /* UNLOCK */
2082 nc_server_ch_client_unlock(client);
2083
2084 if (ret == -1) {
2085 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2086 }
2087
2088 return ret;
2089}
2090
2091API int
2092nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2093{
2094 uint16_t i;
2095 int ret = -1;
2096 struct nc_ch_client *client;
2097
2098 if (!client_name) {
2099 ERRARG("client_name");
2100 return -1;
2101 } else if (!endpt_name) {
2102 ERRARG("endpt_name");
2103 return -1;
2104 } else if (!port) {
2105 ERRARG("port");
2106 return -1;
2107 }
2108
2109 /* LOCK */
2110 client = nc_server_ch_client_lock(client_name, 0, NULL);
2111 if (!client) {
2112 return -1;
2113 }
2114
2115 for (i = 0; i < client->ch_endpt_count; ++i) {
2116 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2117 client->ch_endpts[i].port = port;
2118
2119 ret = 0;
2120 break;
2121 }
2122 }
2123
2124 /* UNLOCK */
2125 nc_server_ch_client_unlock(client);
2126
2127 if (ret == -1) {
2128 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2129 }
2130
2131 return ret;
2132}
2133
2134API int
2135nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2136{
2137 struct nc_ch_client *client;
2138
2139 if (!client_name) {
2140 ERRARG("client_name");
2141 return -1;
2142 } else if (!conn_type) {
2143 ERRARG("conn_type");
2144 return -1;
2145 }
2146
2147 /* LOCK */
2148 client = nc_server_ch_client_lock(client_name, 0, NULL);
2149 if (!client) {
2150 return -1;
2151 }
2152
2153 if (client->conn_type != conn_type) {
2154 client->conn_type = conn_type;
2155
2156 /* set default options */
2157 switch (conn_type) {
2158 case NC_CH_PERSIST:
2159 client->conn.persist.idle_timeout = 86400;
2160 client->conn.persist.ka_max_wait = 30;
2161 client->conn.persist.ka_max_attempts = 3;
2162 break;
2163 case NC_CH_PERIOD:
2164 client->conn.period.idle_timeout = 300;
2165 client->conn.period.reconnect_timeout = 60;
2166 break;
2167 default:
2168 ERRINT;
2169 break;
2170 }
2171 }
2172
2173 /* UNLOCK */
2174 nc_server_ch_client_unlock(client);
2175
2176 return 0;
2177}
2178
2179API int
2180nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2181{
2182 struct nc_ch_client *client;
2183
2184 if (!client_name) {
2185 ERRARG("client_name");
2186 return -1;
2187 }
2188
2189 /* LOCK */
2190 client = nc_server_ch_client_lock(client_name, 0, NULL);
2191 if (!client) {
2192 return -1;
2193 }
2194
2195 if (client->conn_type != NC_CH_PERSIST) {
2196 ERR("Call Home client \"%s\" is not of persistent connection type.");
2197 /* UNLOCK */
2198 nc_server_ch_client_unlock(client);
2199 return -1;
2200 }
2201
2202 client->conn.persist.idle_timeout = idle_timeout;
2203
2204 /* UNLOCK */
2205 nc_server_ch_client_unlock(client);
2206
2207 return 0;
2208}
2209
2210API int
2211nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2212{
2213 struct nc_ch_client *client;
2214
2215 if (!client_name) {
2216 ERRARG("client_name");
2217 return -1;
2218 } else if (!max_wait) {
2219 ERRARG("max_wait");
2220 return -1;
2221 }
2222
2223 /* LOCK */
2224 client = nc_server_ch_client_lock(client_name, 0, NULL);
2225 if (!client) {
2226 return -1;
2227 }
2228
2229 if (client->conn_type != NC_CH_PERSIST) {
2230 ERR("Call Home client \"%s\" is not of persistent connection type.");
2231 /* UNLOCK */
2232 nc_server_ch_client_unlock(client);
2233 return -1;
2234 }
2235
2236 client->conn.persist.ka_max_wait = max_wait;
2237
2238 /* UNLOCK */
2239 nc_server_ch_client_unlock(client);
2240
2241 return 0;
2242}
2243
2244API int
2245nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2246{
2247 struct nc_ch_client *client;
2248
2249 if (!client_name) {
2250 ERRARG("client_name");
2251 return -1;
2252 }
2253
2254 /* LOCK */
2255 client = nc_server_ch_client_lock(client_name, 0, NULL);
2256 if (!client) {
2257 return -1;
2258 }
2259
2260 if (client->conn_type != NC_CH_PERSIST) {
2261 ERR("Call Home client \"%s\" is not of persistent connection type.");
2262 /* UNLOCK */
2263 nc_server_ch_client_unlock(client);
2264 return -1;
2265 }
2266
2267 client->conn.persist.ka_max_attempts = max_attempts;
2268
2269 /* UNLOCK */
2270 nc_server_ch_client_unlock(client);
2271
2272 return 0;
2273}
2274
2275API int
2276nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2277{
2278 struct nc_ch_client *client;
2279
2280 if (!client_name) {
2281 ERRARG("client_name");
2282 return -1;
2283 }
2284
2285 /* LOCK */
2286 client = nc_server_ch_client_lock(client_name, 0, NULL);
2287 if (!client) {
2288 return -1;
2289 }
2290
2291 if (client->conn_type != NC_CH_PERIOD) {
2292 ERR("Call Home client \"%s\" is not of periodic connection type.");
2293 /* UNLOCK */
2294 nc_server_ch_client_unlock(client);
2295 return -1;
2296 }
2297
2298 client->conn.period.idle_timeout = idle_timeout;
2299
2300 /* UNLOCK */
2301 nc_server_ch_client_unlock(client);
2302
2303 return 0;
2304}
2305
2306API int
2307nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2308{
2309 struct nc_ch_client *client;
2310
2311 if (!client_name) {
2312 ERRARG("client_name");
2313 return -1;
2314 } else if (!reconnect_timeout) {
2315 ERRARG("reconnect_timeout");
2316 return -1;
2317 }
2318
2319 /* LOCK */
2320 client = nc_server_ch_client_lock(client_name, 0, NULL);
2321 if (!client) {
2322 return -1;
2323 }
2324
2325 if (client->conn_type != NC_CH_PERIOD) {
2326 ERR("Call Home client \"%s\" is not of periodic connection type.");
2327 /* UNLOCK */
2328 nc_server_ch_client_unlock(client);
2329 return -1;
2330 }
2331
2332 client->conn.period.reconnect_timeout = reconnect_timeout;
2333
2334 /* UNLOCK */
2335 nc_server_ch_client_unlock(client);
2336
2337 return 0;
2338}
2339
2340API int
2341nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2342{
2343 struct nc_ch_client *client;
2344
2345 if (!client_name) {
2346 ERRARG("client_name");
2347 return -1;
2348 }
2349
2350 /* LOCK */
2351 client = nc_server_ch_client_lock(client_name, 0, NULL);
2352 if (!client) {
2353 return -1;
2354 }
2355
2356 client->start_with = start_with;
2357
2358 /* UNLOCK */
2359 nc_server_ch_client_unlock(client);
2360
2361 return 0;
2362}
2363
2364API int
2365nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2366{
2367 struct nc_ch_client *client;
2368
2369 if (!client_name) {
2370 ERRARG("client_name");
2371 return -1;
2372 } else if (!max_attempts) {
2373 ERRARG("max_attempts");
2374 return -1;
2375 }
2376
2377 /* LOCK */
2378 client = nc_server_ch_client_lock(client_name, 0, NULL);
2379 if (!client) {
2380 return -1;
2381 }
2382
2383 client->max_attempts = max_attempts;
2384
2385 /* UNLOCK */
2386 nc_server_ch_client_unlock(client);
2387
2388 return 0;
2389}
2390
2391/* client lock is expected to be held */
2392static NC_MSG_TYPE
2393nc_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 +01002394{
Michal Vasko71090fc2016-05-24 16:37:28 +02002395 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002396 int sock, ret;
2397
Michal Vasko2e6defd2016-10-07 15:48:15 +02002398 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002399 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002400 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002401 }
2402
2403 *session = calloc(1, sizeof **session);
2404 if (!(*session)) {
2405 ERRMEM;
2406 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002407 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002408 }
2409 (*session)->status = NC_STATUS_STARTING;
2410 (*session)->side = NC_SERVER;
2411 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002412 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002413 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2414 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002415
2416 /* transport lock */
2417 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
2418 if (!(*session)->ti_lock) {
2419 ERRMEM;
2420 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002421 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002422 goto fail;
2423 }
2424 pthread_mutex_init((*session)->ti_lock, NULL);
2425
2426 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002427#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002428 if (client->ti == NC_TI_LIBSSH) {
2429 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002430 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002431 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002432
Michal Vasko71090fc2016-05-24 16:37:28 +02002433 if (ret < 0) {
2434 msgtype = NC_MSG_ERROR;
2435 goto fail;
2436 } else if (!ret) {
2437 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002438 goto fail;
2439 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002440 } else
2441#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002442#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002443 if (client->ti == NC_TI_OPENSSL) {
2444 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002445 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002446 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002447
Michal Vasko71090fc2016-05-24 16:37:28 +02002448 if (ret < 0) {
2449 msgtype = NC_MSG_ERROR;
2450 goto fail;
2451 } else if (!ret) {
2452 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002453 goto fail;
2454 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002455 } else
2456#endif
2457 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002458 ERRINT;
2459 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002460 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002461 goto fail;
2462 }
2463
2464 /* assign new SID atomically */
2465 /* LOCK */
2466 pthread_spin_lock(&server_opts.sid_lock);
2467 (*session)->id = server_opts.new_session_id++;
2468 /* UNLOCK */
2469 pthread_spin_unlock(&server_opts.sid_lock);
2470
2471 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002472 msgtype = nc_handshake(*session);
2473 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002474 goto fail;
2475 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002476 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002477 (*session)->status = NC_STATUS_RUNNING;
2478
Michal Vasko71090fc2016-05-24 16:37:28 +02002479 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002480
2481fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002482 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002483 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002484 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002485}
2486
Michal Vasko2e6defd2016-10-07 15:48:15 +02002487/* ms */
2488#define NC_CH_NO_ENDPT_WAIT 1000
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002489#define NC_CH_ENDPT_FAIL_WAIT 1000
Michal Vasko2e6defd2016-10-07 15:48:15 +02002490
2491struct nc_ch_client_thread_arg {
2492 char *client_name;
2493 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2494};
2495
2496static struct nc_ch_client *
2497nc_server_ch_client_with_endpt_lock(const char *name)
2498{
2499 struct nc_ch_client *client;
2500
2501 while (1) {
2502 /* LOCK */
2503 client = nc_server_ch_client_lock(name, 0, NULL);
2504 if (!client) {
2505 return NULL;
2506 }
2507 if (client->ch_endpt_count) {
2508 return client;
2509 }
2510 /* no endpoints defined yet */
2511
2512 /* UNLOCK */
2513 nc_server_ch_client_unlock(client);
2514
2515 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2516 }
2517
2518 return NULL;
2519}
2520
2521static int
2522nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2523{
2524 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002525 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002526 struct timespec ts;
2527 struct nc_ch_client *client;
2528
2529 /* session created, initialize condition */
2530 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2531 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2532 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2533 ERRMEM;
2534 nc_session_free(session, NULL);
2535 return -1;
2536 }
2537 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2538 pthread_cond_init(session->opts.server.ch_cond, NULL);
2539
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002540 session->flags |= NC_SESSION_CALLHOME;
2541
Michal Vasko2e6defd2016-10-07 15:48:15 +02002542 /* CH LOCK */
2543 pthread_mutex_lock(session->opts.server.ch_lock);
2544
2545 /* give the session to the user */
2546 data->session_clb(data->client_name, session);
2547
2548 do {
2549 nc_gettimespec(&ts);
2550 ts.tv_nsec += NC_CH_NO_ENDPT_WAIT * 1000000L;
2551 if (ts.tv_nsec > 1000000000L) {
2552 ts.tv_sec += ts.tv_nsec / 1000000000L;
2553 ts.tv_nsec %= 1000000000L;
2554 }
2555
2556 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2557 if (ret && (ret != ETIMEDOUT)) {
2558 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2559 goto ch_client_remove;
2560 }
2561
2562 /* check whether the client was not removed */
2563 /* LOCK */
2564 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2565 if (!client) {
2566 /* client was removed, finish thread */
2567 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2568 data->client_name);
2569 goto ch_client_remove;
2570 }
2571
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002572 if (client->conn_type == NC_CH_PERSIST) {
2573 /* TODO keep-alives */
2574 idle_timeout = client->conn.persist.idle_timeout;
2575 } else {
2576 idle_timeout = client->conn.period.idle_timeout;
2577 }
2578
2579 /* TODO only for sessions without subscriptions */
2580 if (idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
2581 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2582 session->status = NC_STATUS_INVALID;
2583 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2584 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002585
2586 /* UNLOCK */
2587 nc_server_ch_client_unlock(client);
2588
2589 } while (session->status == NC_STATUS_RUNNING);
2590
2591 /* CH UNLOCK */
2592 pthread_mutex_unlock(session->opts.server.ch_lock);
2593
2594 return 0;
2595
2596ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002597 /* make the session a standard one */
2598 pthread_cond_destroy(session->opts.server.ch_cond);
2599 free(session->opts.server.ch_cond);
2600 session->opts.server.ch_cond = NULL;
2601
2602 session->flags &= ~NC_SESSION_CALLHOME;
2603
Michal Vasko2e6defd2016-10-07 15:48:15 +02002604 /* CH UNLOCK */
2605 pthread_mutex_unlock(session->opts.server.ch_lock);
2606
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002607 pthread_mutex_destroy(session->opts.server.ch_lock);
2608 free(session->opts.server.ch_lock);
2609 session->opts.server.ch_lock = NULL;
2610
Michal Vasko2e6defd2016-10-07 15:48:15 +02002611 return 1;
2612}
2613
2614static void *
2615nc_ch_client_thread(void *arg)
2616{
2617 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2618 NC_MSG_TYPE msgtype;
2619 uint8_t cur_attempts = 0;
2620 uint16_t i;
2621 char *cur_endpt_name;
2622 struct nc_ch_endpt *cur_endpt;
2623 struct nc_session *session;
2624 struct nc_ch_client *client;
2625
2626 /* LOCK */
2627 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2628 if (!client) {
2629 goto cleanup;
2630 }
2631
2632 cur_endpt = &client->ch_endpts[0];
2633 cur_endpt_name = strdup(cur_endpt->name);
2634
Michal Vasko29af44b2016-10-13 10:59:55 +02002635 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002636 while (1) {
2637 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2638
2639 if (msgtype == NC_MSG_HELLO) {
2640 /* UNLOCK */
2641 nc_server_ch_client_unlock(client);
2642
Michal Vasko29af44b2016-10-13 10:59:55 +02002643 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002644 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2645 goto cleanup;
2646 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002647 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002648
2649 /* LOCK */
2650 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2651 if (!client) {
2652 goto cleanup;
2653 }
2654
2655 /* session changed status -> it was disconnected for whatever reason,
2656 * persistent connection immediately tries to reconnect, periodic waits some first */
2657 if (client->conn_type == NC_CH_PERIOD) {
2658 i = client->conn.period.reconnect_timeout;
2659
2660 /* UNLOCK */
2661 nc_server_ch_client_unlock(client);
2662
2663 /* TODO wake up sometimes to check for new notifications */
2664 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2665
2666 /* LOCK */
2667 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2668 if (!client) {
2669 goto cleanup;
2670 }
2671 }
2672
2673 /* set next endpoint to try */
2674 if (client->start_with == NC_CH_FIRST_LISTED) {
2675 cur_endpt = &client->ch_endpts[0];
2676 free(cur_endpt_name);
2677 cur_endpt_name = strdup(cur_endpt->name);
2678 } /* else we keep the current one */
2679 } else {
2680 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002681 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2682
Michal Vasko2e6defd2016-10-07 15:48:15 +02002683 ++cur_attempts;
2684 if (cur_attempts == client->max_attempts) {
2685 for (i = 0; i < client->ch_endpt_count; ++i) {
2686 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2687 break;
2688 }
2689 }
2690 if (i < client->ch_endpt_count - 1) {
2691 /* just go to the next endpoint */
2692 cur_endpt = &client->ch_endpts[i + 1];
2693 free(cur_endpt_name);
2694 cur_endpt_name = strdup(cur_endpt->name);
2695 } else {
2696 /* cur_endpoint was removed or is the last, either way start with the first one */
2697 cur_endpt = &client->ch_endpts[0];
2698 free(cur_endpt_name);
2699 cur_endpt_name = strdup(cur_endpt->name);
2700 }
2701
2702 cur_attempts = 0;
2703 } /* else we keep the current one */
2704 }
2705 }
2706
2707cleanup:
2708 VRB("Call Home client \"%s\" thread exit.", data->client_name);
2709
2710 free(data->client_name);
2711 free(data);
2712 return NULL;
2713}
2714
2715API int
2716nc_connect_ch_client_dispatch(const char *client_name,
2717 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2718 int ret;
2719 pthread_t tid;
2720 struct nc_ch_client_thread_arg *arg;
2721
2722 if (!client_name) {
2723 ERRARG("client_name");
2724 return -1;
2725 } else if (!session_clb) {
2726 ERRARG("session_clb");
2727 return -1;
2728 }
2729
2730 arg = malloc(sizeof *arg);
2731 if (!arg) {
2732 ERRMEM;
2733 return -1;
2734 }
2735 arg->client_name = strdup(client_name);
2736 if (!arg->client_name) {
2737 ERRMEM;
2738 free(arg);
2739 return -1;
2740 }
2741 arg->session_clb = session_clb;
2742
2743 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2744 if (ret) {
2745 ERR("Creating a new thread failed (%s).", strerror(ret));
2746 free(arg->client_name);
2747 free(arg);
2748 return -1;
2749 }
2750 /* the thread now manages arg */
2751
2752 pthread_detach(tid);
2753
2754 return 0;
2755}
2756
Radek Krejci53691be2016-02-22 13:58:37 +01002757#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002758
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002759API time_t
2760nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002761{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002762 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002763 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002764 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002765 }
2766
Michal Vasko2e6defd2016-10-07 15:48:15 +02002767 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002768}