blob: 1d60dcd67d7da6b52f4c9937473e970ade10bc20 [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 Vasko3031aae2016-01-27 16:07:18 +010033 .endpt_array_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010034};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010035
Michal Vasko3031aae2016-01-27 16:07:18 +010036extern struct nc_server_ssh_opts ssh_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010037extern pthread_mutex_t ssh_ch_opts_lock;
38
Michal Vasko3031aae2016-01-27 16:07:18 +010039extern struct nc_server_tls_opts tls_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010040extern pthread_mutex_t tls_ch_opts_lock;
Michal Vasko3031aae2016-01-27 16:07:18 +010041
42struct nc_endpt *
Michal Vaskoe2713da2016-08-22 16:06:40 +020043nc_server_endpt_lock(const char *name, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010044{
45 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010046 struct nc_endpt *endpt = NULL;
47
48 /* READ LOCK */
49 pthread_rwlock_rdlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010050
51 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +020052 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010053 endpt = &server_opts.endpts[i];
54 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010055 }
56 }
57
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010058 if (!endpt) {
59 ERR("Endpoint \"%s\" was not found.", name);
60 /* READ UNLOCK */
61 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
62 return NULL;
63 }
64
65 /* ENDPT LOCK */
66 pthread_mutex_lock(&endpt->endpt_lock);
67
Michal Vaskoe2713da2016-08-22 16:06:40 +020068 if (idx) {
69 *idx = i;
70 }
71
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010072 return endpt;
73}
74
75void
76nc_server_endpt_unlock(struct nc_endpt *endpt)
77{
78 /* ENDPT UNLOCK */
79 pthread_mutex_unlock(&endpt->endpt_lock);
80
81 /* READ UNLOCK */
Michal Vasko27562ad2016-02-02 15:50:39 +010082 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010083}
Michal Vasko086311b2016-01-08 09:53:11 +010084
Michal Vasko1a38c862016-01-15 15:50:07 +010085API void
86nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
87{
Michal Vasko45e53ae2016-04-07 11:46:03 +020088 if (!session) {
89 ERRARG("session");
90 return;
91 } else if (!reason) {
92 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +010093 return;
94 }
95
96 session->term_reason = reason;
97}
98
Michal Vasko086311b2016-01-08 09:53:11 +010099int
Michal Vaskof05562c2016-01-20 12:06:43 +0100100nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100101{
102 const int optVal = 1;
103 const socklen_t optLen = sizeof(optVal);
104 int is_ipv4, sock;
105 struct sockaddr_storage saddr;
106
107 struct sockaddr_in *saddr4;
108 struct sockaddr_in6 *saddr6;
109
110
111 if (!strchr(address, ':')) {
112 is_ipv4 = 1;
113 } else {
114 is_ipv4 = 0;
115 }
116
117 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
118 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100119 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100120 goto fail;
121 }
122
123 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100124 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100125 goto fail;
126 }
127
128 bzero(&saddr, sizeof(struct sockaddr_storage));
129 if (is_ipv4) {
130 saddr4 = (struct sockaddr_in *)&saddr;
131
132 saddr4->sin_family = AF_INET;
133 saddr4->sin_port = htons(port);
134
135 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100136 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100137 goto fail;
138 }
139
140 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100141 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100142 goto fail;
143 }
144
145 } else {
146 saddr6 = (struct sockaddr_in6 *)&saddr;
147
148 saddr6->sin6_family = AF_INET6;
149 saddr6->sin6_port = htons(port);
150
151 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100152 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100153 goto fail;
154 }
155
156 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100157 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100158 goto fail;
159 }
160 }
161
Michal Vaskofb89d772016-01-08 12:25:35 +0100162 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100163 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100164 goto fail;
165 }
166
167 return sock;
168
169fail:
170 if (sock > -1) {
171 close(sock);
172 }
173
174 return -1;
175}
176
177int
Michal Vasko3031aae2016-01-27 16:07:18 +0100178nc_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 +0100179{
180 uint16_t i;
181 struct pollfd *pfd;
182 struct sockaddr_storage saddr;
183 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100184 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100185
186 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100187 if (!pfd) {
188 ERRMEM;
189 return -1;
190 }
191
Michal Vasko94acafc2016-09-23 13:40:10 +0200192 i = 0;
193 while (i < bind_count) {
194 if (binds[i].sock < 0) {
195 /* invalid socket */
196 --bind_count;
197 continue;
198 }
Michal Vasko086311b2016-01-08 09:53:11 +0100199 pfd[i].fd = binds[i].sock;
200 pfd[i].events = POLLIN;
201 pfd[i].revents = 0;
Michal Vasko94acafc2016-09-23 13:40:10 +0200202
203 ++i;
Michal Vasko086311b2016-01-08 09:53:11 +0100204 }
205
206 /* poll for a new connection */
207 errno = 0;
208 ret = poll(pfd, bind_count, timeout);
209 if (!ret) {
210 /* we timeouted */
211 free(pfd);
212 return 0;
213 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100214 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100215 free(pfd);
216 return -1;
217 }
218
219 for (i = 0; i < bind_count; ++i) {
220 if (pfd[i].revents & POLLIN) {
221 sock = pfd[i].fd;
222 break;
223 }
224 }
225 free(pfd);
226
227 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100228 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100229 return -1;
230 }
231
232 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100233 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100234 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100235 return -1;
236 }
237
Michal Vasko0190bc32016-03-02 15:47:49 +0100238 /* make the socket non-blocking */
239 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
240 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100241 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100242 return -1;
243 }
244
Michal Vasko3031aae2016-01-27 16:07:18 +0100245 if (idx) {
246 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100247 }
248
Michal Vasko086311b2016-01-08 09:53:11 +0100249 /* host was requested */
250 if (host) {
251 if (saddr.ss_family == AF_INET) {
252 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100253 if (*host) {
254 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
255 ERR("inet_ntop failed (%s).", strerror(errno));
256 free(*host);
257 *host = NULL;
258 }
Michal Vasko086311b2016-01-08 09:53:11 +0100259
Michal Vasko4eb3c312016-03-01 14:09:37 +0100260 if (port) {
261 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
262 }
263 } else {
264 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100265 }
266 } else if (saddr.ss_family == AF_INET6) {
267 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100268 if (*host) {
269 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
270 ERR("inet_ntop failed (%s).", strerror(errno));
271 free(*host);
272 *host = NULL;
273 }
Michal Vasko086311b2016-01-08 09:53:11 +0100274
Michal Vasko4eb3c312016-03-01 14:09:37 +0100275 if (port) {
276 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
277 }
278 } else {
279 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100280 }
281 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100282 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100283 }
284 }
285
286 return ret;
287}
288
Michal Vasko05ba9df2016-01-13 14:40:27 +0100289static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100290nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100291{
292 const char *identifier = NULL, *version = NULL, *format = NULL;
293 char *model_data = NULL;
294 const struct lys_module *module;
295 struct nc_server_error *err;
296 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100297 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100298
299 LY_TREE_FOR(rpc->child, child) {
300 if (!strcmp(child->schema->name, "identifier")) {
301 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
302 } else if (!strcmp(child->schema->name, "version")) {
303 version = ((struct lyd_node_leaf_list *)child)->value_str;
304 } else if (!strcmp(child->schema->name, "format")) {
305 format = ((struct lyd_node_leaf_list *)child)->value_str;
306 }
307 }
308
309 /* check version */
310 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100311 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
312 nc_err_set_msg(err, "The requested version is not supported.", "en");
313 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100314 }
315
316 /* check and get module with the name identifier */
317 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
318 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200319 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
320 }
321 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100322 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
323 nc_err_set_msg(err, "The requested schema was not found.", "en");
324 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100325 }
326
327 /* check format */
328 if (!format || !strcmp(format, "yang")) {
329 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
330 } else if (!strcmp(format, "yin")) {
331 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
332 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100333 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
334 nc_err_set_msg(err, "The requested format is not supported.", "en");
335 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100336 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200337 if (!model_data) {
338 ERRINT;
339 return NULL;
340 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100341
Michal Vasko303245c2016-03-24 15:20:03 +0100342 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200343 if (!sdata) {
344 ERRINT;
345 free(model_data);
346 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100347 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200348
Radek Krejci539efb62016-08-24 15:05:16 +0200349 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
350 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100351 if (!data) {
352 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200353 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100354 return NULL;
355 }
356
Radek Krejci36dfdb32016-09-01 16:56:35 +0200357 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100358}
359
360static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100361nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100362{
Michal Vasko428087d2016-01-14 16:04:28 +0100363 session->term_reason = NC_SESSION_TERM_CLOSED;
364 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100365}
366
Michal Vasko086311b2016-01-08 09:53:11 +0100367API int
368nc_server_init(struct ly_ctx *ctx)
369{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100370 const struct lys_node *rpc;
371
Michal Vasko086311b2016-01-08 09:53:11 +0100372 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200373 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100374 return -1;
375 }
376
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100377 nc_init();
378
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100380 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100381 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100382 lys_set_private(rpc, nc_clb_default_get_schema);
383 }
384
385 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100386 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100387 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100388 lys_set_private(rpc, nc_clb_default_close_session);
389 }
390
Michal Vasko086311b2016-01-08 09:53:11 +0100391 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100392
393 server_opts.new_session_id = 1;
394 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
395
Michal Vasko086311b2016-01-08 09:53:11 +0100396 return 0;
397}
398
Michal Vaskob48aa812016-01-18 14:13:09 +0100399API void
400nc_server_destroy(void)
401{
402 pthread_spin_destroy(&server_opts.sid_lock);
403
Radek Krejci53691be2016-02-22 13:58:37 +0100404#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200405 nc_server_del_endpt(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100406#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100407 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100408}
409
Michal Vasko086311b2016-01-08 09:53:11 +0100410API int
411nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
412{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200413 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
414 ERRARG("basic_mode");
415 return -1;
416 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
417 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100418 return -1;
419 }
420
421 server_opts.wd_basic_mode = basic_mode;
422 server_opts.wd_also_supported = also_supported;
423 return 0;
424}
425
Michal Vasko1a38c862016-01-15 15:50:07 +0100426API void
Michal Vasko55f03972016-04-13 08:56:01 +0200427nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
428{
429 if (!basic_mode && !also_supported) {
430 ERRARG("basic_mode and also_supported");
431 return;
432 }
433
434 if (basic_mode) {
435 *basic_mode = server_opts.wd_basic_mode;
436 }
437 if (also_supported) {
438 *also_supported = server_opts.wd_also_supported;
439 }
440}
441
442API void
Michal Vasko086311b2016-01-08 09:53:11 +0100443nc_server_set_capab_interleave(int interleave_support)
444{
445 if (interleave_support) {
446 server_opts.interleave_capab = 1;
447 } else {
448 server_opts.interleave_capab = 0;
449 }
Michal Vasko086311b2016-01-08 09:53:11 +0100450}
451
Michal Vasko55f03972016-04-13 08:56:01 +0200452API int
453nc_server_get_capab_interleave(void)
454{
455 return server_opts.interleave_capab;
456}
457
Michal Vasko1a38c862016-01-15 15:50:07 +0100458API void
Michal Vasko086311b2016-01-08 09:53:11 +0100459nc_server_set_hello_timeout(uint16_t hello_timeout)
460{
Michal Vasko086311b2016-01-08 09:53:11 +0100461 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100462}
463
Michal Vasko55f03972016-04-13 08:56:01 +0200464API uint16_t
465nc_server_get_hello_timeout(void)
466{
467 return server_opts.hello_timeout;
468}
469
Michal Vasko1a38c862016-01-15 15:50:07 +0100470API void
Michal Vasko086311b2016-01-08 09:53:11 +0100471nc_server_set_idle_timeout(uint16_t idle_timeout)
472{
Michal Vasko086311b2016-01-08 09:53:11 +0100473 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100474}
475
Michal Vasko55f03972016-04-13 08:56:01 +0200476API uint16_t
477nc_server_get_idle_timeout(void)
478{
479 return server_opts.idle_timeout;
480}
481
Michal Vasko71090fc2016-05-24 16:37:28 +0200482API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100483nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100484{
Michal Vasko71090fc2016-05-24 16:37:28 +0200485 NC_MSG_TYPE msgtype;
486
Michal Vasko45e53ae2016-04-07 11:46:03 +0200487 if (!server_opts.ctx) {
488 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200489 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200490 } else if (fdin < 0) {
491 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200492 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200493 } else if (fdout < 0) {
494 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200495 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200496 } else if (!username) {
497 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200498 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200499 } else if (!session) {
500 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200501 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100502 }
503
504 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100505 *session = calloc(1, sizeof **session);
506 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100507 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200508 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100509 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100510 (*session)->status = NC_STATUS_STARTING;
511 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100512
513 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100514 (*session)->ti_type = NC_TI_FD;
515 (*session)->ti.fd.in = fdin;
516 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100517
518 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100519 (*session)->flags = NC_SESSION_SHAREDCTX;
520 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100521
Michal Vaskob48aa812016-01-18 14:13:09 +0100522 /* assign new SID atomically */
523 pthread_spin_lock(&server_opts.sid_lock);
524 (*session)->id = server_opts.new_session_id++;
525 pthread_spin_unlock(&server_opts.sid_lock);
526
Michal Vasko086311b2016-01-08 09:53:11 +0100527 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200528 msgtype = nc_handshake(*session);
529 if (msgtype != NC_MSG_HELLO) {
530 nc_session_free(*session, NULL);
531 *session = NULL;
532 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100533 }
Michal Vaskof8352352016-05-24 09:11:36 +0200534 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100535 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100536
Michal Vasko71090fc2016-05-24 16:37:28 +0200537 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100538}
Michal Vasko9e036d52016-01-08 10:49:26 +0100539
Michal Vaskobdcf2362016-07-26 11:35:43 +0200540static void
541nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
542{
543 uint8_t i, found = 0;
544
545 for (i = 0; i < ps->queue_len; ++i) {
546 /* idx round buffer adjust */
547 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
548 i = -ps->queue_begin;
549 }
550
551 if (found) {
552 /* move the value back one place */
553 if (ps->queue[ps->queue_begin + i] == id) {
554 /* another equal value, simply cannot be */
555 ERRINT;
556 }
557
558 if (ps->queue_begin + i == 0) {
559 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
560 } else {
561 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
562 }
563 } else if (ps->queue[ps->queue_begin + i] == id) {
564 /* found our id, there can be no more equal valid values */
565 found = 1;
566 }
567 }
568
569 if (!found) {
570 ERRINT;
571 }
572 --ps->queue_len;
573}
574
Michal Vaskof04a52a2016-04-07 10:52:10 +0200575int
Michal Vasko227f8ff2016-07-26 14:08:59 +0200576nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200577{
578 int ret;
Michal Vaskobdcf2362016-07-26 11:35:43 +0200579 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200580 struct timespec ts;
581
Radek Krejci7ac16052016-07-15 11:48:18 +0200582 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200583 ts.tv_sec += NC_READ_TIMEOUT;
584
585 /* LOCK */
586 ret = pthread_mutex_timedlock(&ps->lock, &ts);
587 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200588 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200589 return -1;
590 }
591
592 /* get a unique queue value (by adding 1 to the last added value, if any) */
593 if (ps->queue_len) {
594 queue_last = ps->queue_begin + ps->queue_len - 1;
595 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
596 queue_last -= NC_PS_QUEUE_SIZE;
597 }
Michal Vaskobdcf2362016-07-26 11:35:43 +0200598 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200599 } else {
Michal Vaskobdcf2362016-07-26 11:35:43 +0200600 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200601 }
602
603 /* add ourselves into the queue */
604 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200605 ERR("%s: pollsession queue too small.", func);
Michal Vasko8c242532016-07-26 12:23:24 +0200606 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200607 return -1;
608 }
609 ++ps->queue_len;
610 queue_last = ps->queue_begin + ps->queue_len - 1;
611 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
612 queue_last -= NC_PS_QUEUE_SIZE;
613 }
Michal Vaskobdcf2362016-07-26 11:35:43 +0200614 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200615
616 /* is it our turn? */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200617 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200618 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200619 ts.tv_sec += NC_READ_TIMEOUT;
620
621 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
622 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200623 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200624 /* remove ourselves from the queue */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200625 nc_ps_queue_remove_id(ps, *id);
626 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200627 return -1;
628 }
629 }
630
Michal Vaskobe86fe32016-04-07 10:43:03 +0200631 /* UNLOCK */
632 pthread_mutex_unlock(&ps->lock);
633
634 return 0;
635}
636
Michal Vaskof04a52a2016-04-07 10:52:10 +0200637int
Michal Vasko227f8ff2016-07-26 14:08:59 +0200638nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200639{
640 int ret;
641 struct timespec ts;
642
Radek Krejci7ac16052016-07-15 11:48:18 +0200643 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200644 ts.tv_sec += NC_READ_TIMEOUT;
645
646 /* LOCK */
647 ret = pthread_mutex_timedlock(&ps->lock, &ts);
648 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200649 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200650 ret = -1;
651 }
652
Michal Vaskobdcf2362016-07-26 11:35:43 +0200653 /* we must be the first, it was our turn after all, right? */
654 if (ps->queue[ps->queue_begin] != id) {
655 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200656 /* UNLOCK */
657 if (!ret) {
658 pthread_mutex_unlock(&ps->lock);
659 }
Michal Vaskobdcf2362016-07-26 11:35:43 +0200660 return -1;
661 }
662
Michal Vaskobe86fe32016-04-07 10:43:03 +0200663 /* remove ourselves from the queue */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200664 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200665
666 /* broadcast to all other threads that the queue moved */
667 pthread_cond_broadcast(&ps->cond);
668
Michal Vaskobe86fe32016-04-07 10:43:03 +0200669 /* UNLOCK */
670 if (!ret) {
671 pthread_mutex_unlock(&ps->lock);
672 }
673
674 return ret;
675}
676
Michal Vasko428087d2016-01-14 16:04:28 +0100677API struct nc_pollsession *
678nc_ps_new(void)
679{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100680 struct nc_pollsession *ps;
681
682 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100683 if (!ps) {
684 ERRMEM;
685 return NULL;
686 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200687 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100688 pthread_mutex_init(&ps->lock, NULL);
689
690 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100691}
692
693API void
694nc_ps_free(struct nc_pollsession *ps)
695{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100696 if (!ps) {
697 return;
698 }
699
Michal Vaskobe86fe32016-04-07 10:43:03 +0200700 if (ps->queue_len) {
701 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
702 }
703
Michal Vasko3a715132016-01-21 15:40:31 +0100704 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100705 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100706 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200707 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100708
Michal Vasko428087d2016-01-14 16:04:28 +0100709 free(ps);
710}
711
712API int
713nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
714{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200715 uint8_t q_id;
716
Michal Vasko45e53ae2016-04-07 11:46:03 +0200717 if (!ps) {
718 ERRARG("ps");
719 return -1;
720 } else if (!session) {
721 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100722 return -1;
723 }
724
Michal Vasko48a63ed2016-03-01 09:48:21 +0100725 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200726 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200727 return -1;
728 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100729
Michal Vasko428087d2016-01-14 16:04:28 +0100730 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100731 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
732 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
733 if (!ps->pfds || !ps->sessions) {
734 ERRMEM;
735 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200736 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100737 return -1;
738 }
Michal Vasko428087d2016-01-14 16:04:28 +0100739
740 switch (session->ti_type) {
741 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100742 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100743 break;
744
Radek Krejci53691be2016-02-22 13:58:37 +0100745#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100746 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100747 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100748 break;
749#endif
750
Radek Krejci53691be2016-02-22 13:58:37 +0100751#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100752 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100753 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100754 break;
755#endif
756
757 default:
758 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100759 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200760 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100761 return -1;
762 }
Michal Vasko3a715132016-01-21 15:40:31 +0100763 ps->pfds[ps->session_count - 1].events = POLLIN;
764 ps->pfds[ps->session_count - 1].revents = 0;
765 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100766
Michal Vasko48a63ed2016-03-01 09:48:21 +0100767 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200768 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100769}
770
Michal Vasko48a63ed2016-03-01 09:48:21 +0100771static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100772_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100773{
774 uint16_t i;
775
Radek Krejcid5f978f2016-03-03 13:14:45 +0100776 if (index >= 0) {
777 i = (uint16_t)index;
778 goto remove;
779 }
Michal Vasko428087d2016-01-14 16:04:28 +0100780 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100781 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100782remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100783 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100784 if (i < ps->session_count) {
785 ps->sessions[i] = ps->sessions[ps->session_count];
786 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
787 } else if (!ps->session_count) {
788 free(ps->sessions);
789 ps->sessions = NULL;
790 free(ps->pfds);
791 ps->pfds = NULL;
792 }
Michal Vasko428087d2016-01-14 16:04:28 +0100793 return 0;
794 }
795 }
796
Michal Vaskof0537d82016-01-29 14:42:38 +0100797 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100798}
799
Michal Vasko48a63ed2016-03-01 09:48:21 +0100800API int
801nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
802{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200803 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200804 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100805
Michal Vasko45e53ae2016-04-07 11:46:03 +0200806 if (!ps) {
807 ERRARG("ps");
808 return -1;
809 } else if (!session) {
810 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811 return -1;
812 }
813
814 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200815 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200816 return -1;
817 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100818
Radek Krejcid5f978f2016-03-03 13:14:45 +0100819 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100820
821 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200822 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100823
Michal Vaskobe86fe32016-04-07 10:43:03 +0200824 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100825}
826
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100827API uint16_t
828nc_ps_session_count(struct nc_pollsession *ps)
829{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200830 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100831 uint16_t count;
832
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100833 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200834 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100835 return 0;
836 }
837
Michal Vasko48a63ed2016-03-01 09:48:21 +0100838 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200839 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200840 return -1;
841 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100842
843 count = ps->session_count;
844
845 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200846 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100847
848 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100849}
850
Michal Vasko71090fc2016-05-24 16:37:28 +0200851/* must be called holding the session lock!
852 * returns: NC_PSPOLL_ERROR,
853 * NC_PSPOLL_BAD_RPC,
854 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
855 * NC_PSPOLL_RPC
856 */
857static int
Michal Vasko428087d2016-01-14 16:04:28 +0100858nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
859{
860 struct lyxml_elem *xml = NULL;
861 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200862 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200863 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100864
Michal Vasko45e53ae2016-04-07 11:46:03 +0200865 if (!session) {
866 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200867 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200868 } else if (!rpc) {
869 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200870 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100871 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100872 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200873 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100874 }
875
876 msgtype = nc_read_msg(session, &xml);
877
878 switch (msgtype) {
879 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200880 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100881 if (!*rpc) {
882 ERRMEM;
883 goto error;
884 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100885
Radek Krejcif93c7d42016-04-06 13:41:15 +0200886 ly_errno = LY_SUCCESS;
Michal Vasko68b3f292016-09-16 12:00:32 +0200887 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_RPC | LYD_OPT_DESTRUCT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100888 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200889 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200890 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200891 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200892 nc_server_reply_free(reply);
893 if (ret == -1) {
894 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200895 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200896 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
897 } else {
898 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100899 }
Michal Vasko428087d2016-01-14 16:04:28 +0100900 (*rpc)->root = xml;
901 break;
902 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100903 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200904 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100905 goto error;
906 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100907 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200908 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100909 goto error;
910 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100911 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200912 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100913 goto error;
914 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200915 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100916 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
917 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200918 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100919 break;
920 }
921
Michal Vasko71090fc2016-05-24 16:37:28 +0200922 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100923
924error:
925 /* cleanup */
926 lyxml_free(server_opts.ctx, xml);
927
Michal Vasko71090fc2016-05-24 16:37:28 +0200928 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100929}
930
Michal Vasko71090fc2016-05-24 16:37:28 +0200931/* must be called holding the session lock!
932 * returns: NC_PSPOLL_ERROR,
933 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
934 * NC_PSPOLL_REPLY_ERROR,
935 * 0
936 */
937static int
Michal Vasko428087d2016-01-14 16:04:28 +0100938nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
939{
940 nc_rpc_clb clb;
941 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +0200942 struct lys_node *rpc_act = NULL;
943 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +0200944 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +0100945
Michal Vasko4a827e52016-03-03 10:59:00 +0100946 if (!rpc) {
947 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200948 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +0100949 }
950
Michal Vasko90e8e692016-07-13 12:27:57 +0200951 if (rpc->tree->schema->nodetype == LYS_RPC) {
952 /* RPC */
953 rpc_act = rpc->tree->schema;
954 } else {
955 /* action */
956 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
957 if (elem->schema->nodetype == LYS_ACTION) {
958 rpc_act = elem->schema;
959 break;
960 }
961 LY_TREE_DFS_END(rpc->tree, next, elem);
962 }
963 if (!rpc_act) {
964 ERRINT;
965 return NC_PSPOLL_ERROR;
966 }
967 }
968
969 if (!rpc_act->priv) {
970 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +0100971 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100972 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +0200973 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +0100974 reply = clb(rpc->tree, session);
975 }
976
977 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100978 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100979 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200980 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
981 if (reply->type == NC_RPL_ERROR) {
982 ret |= NC_PSPOLL_REPLY_ERROR;
983 }
984 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +0100985
Michal Vasko71090fc2016-05-24 16:37:28 +0200986 if (r == -1) {
987 ERR("Session %u: failed to write reply.", session->id);
988 ret |= NC_PSPOLL_ERROR;
989 }
Michal Vasko428087d2016-01-14 16:04:28 +0100990
991 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
992 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
993 session->status = NC_STATUS_INVALID;
994 }
995
Michal Vasko71090fc2016-05-24 16:37:28 +0200996 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100997}
998
999API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001000nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001001{
1002 int ret;
Michal Vaskobdcf2362016-07-26 11:35:43 +02001003 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +01001004 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001005 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +02001006 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001007 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +01001008
1009 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001010 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001011 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001012 }
1013
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001014 cur_time = time(NULL);
1015
Michal Vasko48a63ed2016-03-01 09:48:21 +01001016 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001017 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001018 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001019 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001020
Michal Vasko428087d2016-01-14 16:04:28 +01001021 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001022 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1023 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001024 ret = NC_PSPOLL_ERROR;
1025 if (session) {
1026 *session = ps->sessions[i];
1027 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001028 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001029 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001030
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001031 /* TODO invalidate only sessions without subscription */
Michal Vasko15dfe722016-07-28 15:47:25 +02001032 if (server_opts.idle_timeout && (cur_time >= ps->sessions[i]->last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001033 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1034 ps->sessions[i]->status = NC_STATUS_INVALID;
1035 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001036 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1037 if (session) {
1038 *session = ps->sessions[i];
1039 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001040 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001041 }
1042
Michal Vasko3a715132016-01-21 15:40:31 +01001043 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001044 break;
1045 }
Michal Vasko428087d2016-01-14 16:04:28 +01001046 }
1047
Michal Vaskobd8ef262016-01-20 11:09:27 +01001048 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001049#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001050retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001051#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001052 /* no leftover event */
1053 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001054 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001055 if (ret < 0) {
1056 ERR("Poll failed (%s).", strerror(errno));
1057 ret = NC_PSPOLL_ERROR;
1058 goto finish;
1059 } else if (!ret) {
1060 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001061 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001062 }
Michal Vasko428087d2016-01-14 16:04:28 +01001063 }
1064
Michal Vaskobd8ef262016-01-20 11:09:27 +01001065 /* find the first fd with POLLIN, we don't care if there are more now */
1066 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001067 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001068 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1069 ps->sessions[i]->status = NC_STATUS_INVALID;
1070 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001071 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1072 if (session) {
1073 *session = ps->sessions[i];
1074 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001075 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001076 } else if (ps->pfds[i].revents & POLLERR) {
1077 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1078 ps->sessions[i]->status = NC_STATUS_INVALID;
1079 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001080 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1081 if (session) {
1082 *session = ps->sessions[i];
1083 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001084 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001085 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001086#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001087 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001088 uint16_t j;
1089
Michal Vasko96164bf2016-01-21 15:41:58 +01001090 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001091 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001092
1093 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001094 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001095 for (j = i + 1; j < ps->session_count; ++j) {
1096 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1097 ps->pfds[j].revents = 0;
1098 }
1099 }
1100 }
1101
Michal Vasko71090fc2016-05-24 16:37:28 +02001102 /* SSH message only */
1103 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001104 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001105 if (session) {
1106 *session = ps->sessions[i];
1107 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001108 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001109
1110 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001111 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001112 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001113 if (i == ps->session_count - 1) {
1114 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001115 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001116 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001117 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001118 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001119 }
Michal Vasko8c748832016-02-03 15:32:16 +01001120 /* ... retry polling reasonable time apart ... */
1121 usleep(NC_TIMEOUT_STEP);
1122 if (timeout > 0) {
1123 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001124 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001125 }
1126 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001127 }
1128 /* check other sessions */
1129 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001130 }
1131 }
Radek Krejci53691be2016-02-22 13:58:37 +01001132#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001133
Michal Vaskobd8ef262016-01-20 11:09:27 +01001134 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001135 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001136 break;
1137 }
1138 }
1139
1140 if (i == ps->session_count) {
1141 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001142 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001143 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001144 }
1145
1146 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001147 cur_session = ps->sessions[i];
1148 if (session) {
1149 *session = cur_session;
1150 }
Michal Vasko428087d2016-01-14 16:04:28 +01001151
Michal Vaskobd8ef262016-01-20 11:09:27 +01001152 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal vasko50cc94f2016-10-04 13:46:20 +02001153 ret = nc_timedlock(cur_session->ti_lock, timeout, __func__);
Michal Vasko71090fc2016-05-24 16:37:28 +02001154 if (ret < 0) {
1155 ret = NC_PSPOLL_ERROR;
1156 goto finish;
1157 } else if (!ret) {
1158 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001159 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001160 }
1161
Michal Vasko71090fc2016-05-24 16:37:28 +02001162 ret = nc_recv_rpc(cur_session, &rpc);
1163 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1164 pthread_mutex_unlock(cur_session->ti_lock);
1165 if (cur_session->status != NC_STATUS_RUNNING) {
1166 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001167 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001168 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001169 }
1170
Michal Vasko71090fc2016-05-24 16:37:28 +02001171 cur_session->last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001172
Michal Vasko428087d2016-01-14 16:04:28 +01001173 /* process RPC */
Michal Vasko71090fc2016-05-24 16:37:28 +02001174 ret |= nc_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001175
Michal Vasko71090fc2016-05-24 16:37:28 +02001176 pthread_mutex_unlock(cur_session->ti_lock);
1177 if (cur_session->status != NC_STATUS_RUNNING) {
1178 ret |= NC_PSPOLL_SESSION_TERM;
1179 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1180 ret |= NC_PSPOLL_SESSION_ERROR;
1181 }
Michal Vasko428087d2016-01-14 16:04:28 +01001182 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001183
Michal Vaskoca4a2422016-02-02 12:17:14 +01001184 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001185
1186 /* is there some other socket waiting? */
1187 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001188 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001189 ret |= NC_PSPOLL_PENDING;
1190 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001191 }
1192 }
1193
Michal Vasko48a63ed2016-03-01 09:48:21 +01001194finish:
1195 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001196 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001197 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001198}
1199
Michal Vaskod09eae62016-02-01 10:32:52 +01001200API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001201nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001202{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001203 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001204 uint16_t i;
1205 struct nc_session *session;
1206
Michal Vasko9a25e932016-02-01 10:36:42 +01001207 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001208 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001209 return;
1210 }
1211
Michal Vasko48a63ed2016-03-01 09:48:21 +01001212 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001213 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001214 return;
1215 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001216
Michal Vasko48a63ed2016-03-01 09:48:21 +01001217 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001218 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001219 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001220 }
1221 free(ps->sessions);
1222 ps->sessions = NULL;
1223 free(ps->pfds);
1224 ps->pfds = NULL;
1225 ps->session_count = 0;
1226 } else {
1227 for (i = 0; i < ps->session_count; ) {
1228 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1229 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001230 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001231 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001232 continue;
1233 }
1234
1235 ++i;
1236 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001237 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001238
1239 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001240 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001241}
1242
Radek Krejci53691be2016-02-22 13:58:37 +01001243#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001244
Michal Vaskoe2713da2016-08-22 16:06:40 +02001245API int
1246nc_server_add_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001247{
Michal Vasko3031aae2016-01-27 16:07:18 +01001248 uint16_t i;
Radek Krejci53691be2016-02-22 13:58:37 +01001249#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001250 uint16_t bind_ssh_idx;
1251#endif
1252#ifdef NC_ENABLED_TLS
1253 uint16_t bind_tls_idx;
Michal Vasko08a629a2016-02-02 12:20:47 +01001254#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001255
Michal Vasko45e53ae2016-04-07 11:46:03 +02001256 if (!name) {
1257 ERRARG("name");
1258 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001259 }
1260
Michal Vasko51e514d2016-02-02 15:51:52 +01001261 /* WRITE LOCK */
1262 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001263
1264 /* check name uniqueness */
1265 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001266 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001267 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001268 /* WRITE UNLOCK */
Michal Vasko9faf1c82016-02-01 13:26:19 +01001269 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001270 return -1;
1271 }
1272 }
1273
Michal Vasko3031aae2016-01-27 16:07:18 +01001274 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001275 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001276 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001277 ERRMEM;
1278 /* WRITE UNLOCK */
1279 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001280 return -1;
1281 }
1282 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1283
1284#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1285 server_opts.binds = nc_realloc(server_opts.binds, 2 * server_opts.endpt_count * sizeof *server_opts.binds);
1286 bind_ssh_idx = (server_opts.endpt_count - 1) * 2;
1287 bind_tls_idx = (server_opts.endpt_count - 1) * 2 + 1;
1288#else
1289 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
1290# ifdef NC_ENABLED_SSH
1291 bind_ssh_idx = server_opts.endpt_count - 1;
1292# endif
1293# ifdef NC_ENABLED_TLS
1294 bind_tls_idx = server_opts.endpt_count - 1;
1295# endif
1296#endif
1297 if (!server_opts.binds) {
1298 ERRMEM;
1299 /* WRITE UNLOCK */
1300 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001301 return -1;
1302 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001303
Radek Krejci53691be2016-02-22 13:58:37 +01001304#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001305 server_opts.binds[bind_ssh_idx].address = NULL;
1306 server_opts.binds[bind_ssh_idx].port = 0;
1307 server_opts.binds[bind_ssh_idx].sock = -1;
1308 server_opts.binds[bind_ssh_idx].ti = NC_TI_LIBSSH;
Michal Vasko08a629a2016-02-02 12:20:47 +01001309
Michal Vaskoe2713da2016-08-22 16:06:40 +02001310 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts = calloc(1, sizeof(struct nc_server_ssh_opts));
1311 if (!server_opts.endpts[server_opts.endpt_count - 1].ssh_opts) {
1312 ERRMEM;
1313 /* WRITE UNLOCK */
1314 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1315 return -1;
Michal Vasko3031aae2016-01-27 16:07:18 +01001316 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001317 /* set default values */
1318 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_methods =
1319 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1320 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_attempts = 3;
1321 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_timeout = 10;
1322#endif
1323
1324#ifdef NC_ENABLED_TLS
1325 server_opts.binds[bind_tls_idx].address = NULL;
1326 server_opts.binds[bind_tls_idx].port = 0;
1327 server_opts.binds[bind_tls_idx].sock = -1;
1328 server_opts.binds[bind_tls_idx].ti = NC_TI_OPENSSL;
1329
1330 server_opts.endpts[server_opts.endpt_count - 1].tls_opts = calloc(1, sizeof(struct nc_server_tls_opts));
1331 if (!server_opts.endpts[server_opts.endpt_count - 1].tls_opts) {
1332 ERRMEM;
1333 /* WRITE UNLOCK */
1334 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1335 return -1;
1336 }
1337#endif
1338
Michal Vasko3031aae2016-01-27 16:07:18 +01001339 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001340
Michal Vasko3031aae2016-01-27 16:07:18 +01001341 /* WRITE UNLOCK */
1342 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001343
Michal Vasko9e036d52016-01-08 10:49:26 +01001344 return 0;
1345}
1346
Michal Vasko3031aae2016-01-27 16:07:18 +01001347int
Michal Vaskoda514772016-02-01 11:32:01 +01001348nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1349{
1350 struct nc_endpt *endpt;
1351 struct nc_bind *bind = NULL;
1352 uint16_t i;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001353 int sock = -1, set_addr;
Michal Vaskoda514772016-02-01 11:32:01 +01001354
Michal Vasko45e53ae2016-04-07 11:46:03 +02001355 if (!endpt_name) {
1356 ERRARG("endpt_name");
1357 return -1;
1358 } else if ((!address && !port) || (address && port)) {
1359 ERRARG("address and port");
1360 return -1;
1361 } else if (!ti) {
1362 ERRARG("ti");
Michal Vaskoda514772016-02-01 11:32:01 +01001363 return -1;
1364 }
1365
Michal Vaskoe2713da2016-08-22 16:06:40 +02001366 if (address) {
1367 set_addr = 1;
1368 } else {
1369 set_addr = 0;
1370 }
1371
Michal Vasko51e514d2016-02-02 15:51:52 +01001372 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001373 endpt = nc_server_endpt_lock(endpt_name, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001374 if (!endpt) {
1375 return -1;
1376 }
1377
Michal Vaskoe2713da2016-08-22 16:06:40 +02001378#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1379 if (ti == NC_TI_LIBSSH) {
1380 bind = &server_opts.binds[2 * i];
1381 } else {
1382 bind = &server_opts.binds[2 * i + 1];
Michal Vaskoda514772016-02-01 11:32:01 +01001383 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001384#else
1385 bind = &server_opts.binds[i];
Michal Vasko5e391372016-08-22 16:20:58 +02001386 if (bind->ti != ti) {
1387 ERRINT;
1388 goto fail;
1389 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001390#endif
Michal Vaskoda514772016-02-01 11:32:01 +01001391 if (!bind) {
1392 ERRINT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001393 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001394 }
1395
Michal Vaskoe2713da2016-08-22 16:06:40 +02001396 if (set_addr) {
1397 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001398 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001399 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001400 }
1401
Michal Vaskoe2713da2016-08-22 16:06:40 +02001402 /* we have all the information we need to create a listening socket */
1403 if (address && port) {
1404 /* create new socket, close the old one */
1405 sock = nc_sock_listen(address, port);
1406 if (sock == -1) {
1407 goto fail;
1408 }
1409
1410 if (bind->sock > -1) {
1411 close(bind->sock);
1412 }
1413 bind->sock = sock;
1414 } /* else we are just setting address or port */
1415
1416 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001417 lydict_remove(server_opts.ctx, bind->address);
1418 bind->address = lydict_insert(server_opts.ctx, address, 0);
1419 } else {
1420 bind->port = port;
1421 }
1422
Michal Vaskoe2713da2016-08-22 16:06:40 +02001423 if (sock > -1) {
1424#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1425 VRB("Listening on %s:%u for %s connections.", address, port, (ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
1426#elif defined(NC_ENABLED_SSH)
1427 VRB("Listening on %s:%u for SSH connections.", address, port);
1428#else
1429 VRB("Listening on %s:%u for TLS connections.", address, port);
1430#endif
1431 }
1432
Michal Vasko51e514d2016-02-02 15:51:52 +01001433 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001434 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001435 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001436
1437fail:
1438 /* UNLOCK */
1439 nc_server_endpt_unlock(endpt);
1440 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001441}
1442
Michal Vaskoe2713da2016-08-22 16:06:40 +02001443API int
1444nc_server_del_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001445{
1446 uint32_t i;
1447 int ret = -1;
1448
Michal Vasko3031aae2016-01-27 16:07:18 +01001449 /* WRITE LOCK */
1450 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001451
Michal Vaskoe2713da2016-08-22 16:06:40 +02001452 if (!name) {
1453 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001454 for (i = 0; i < server_opts.endpt_count; ++i) {
1455 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko3031aae2016-01-27 16:07:18 +01001456 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
Radek Krejci53691be2016-02-22 13:58:37 +01001457#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001458 nc_server_ssh_clear_opts(server_opts.endpts[i].ssh_opts);
1459 free(server_opts.endpts[i].ssh_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001460#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001461#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001462 nc_server_tls_clear_opts(server_opts.endpts[i].tls_opts);
1463 free(server_opts.endpts[i].tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001464#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001465 ret = 0;
1466 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001467 free(server_opts.endpts);
1468 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001469
1470 /* remove all binds */
1471 for (i = 0; i < server_opts.endpt_count; ++i) {
1472 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1473 if (server_opts.binds[i].sock > -1) {
1474 close(server_opts.binds[i].sock);
1475 }
1476 }
1477#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1478 for (; i < 2 * server_opts.endpt_count; ++i) {
1479 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1480 if (server_opts.binds[i].sock > -1) {
1481 close(server_opts.binds[i].sock);
1482 }
1483 }
1484#endif
1485 free(server_opts.binds);
1486 server_opts.binds = NULL;
1487
Michal Vasko3031aae2016-01-27 16:07:18 +01001488 server_opts.endpt_count = 0;
1489
Michal Vasko1a38c862016-01-15 15:50:07 +01001490 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001491 /* remove one endpoint with bind(s) */
Michal Vasko3031aae2016-01-27 16:07:18 +01001492 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001493 if (!strcmp(server_opts.endpts[i].name, name)) {
1494 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001495 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko3031aae2016-01-27 16:07:18 +01001496 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
Radek Krejci53691be2016-02-22 13:58:37 +01001497#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001498 nc_server_ssh_clear_opts(server_opts.endpts[i].ssh_opts);
1499 free(server_opts.endpts[i].ssh_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001500#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001501#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001502 nc_server_tls_clear_opts(server_opts.endpts[i].tls_opts);
1503 free(server_opts.endpts[i].tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001504#endif
Michal Vasko1a38c862016-01-15 15:50:07 +01001505
Michal Vaskoe2713da2016-08-22 16:06:40 +02001506 /* remove bind(s) */
1507#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1508 i *= 2;
1509 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1510 if (server_opts.binds[i].sock > -1) {
1511 close(server_opts.binds[i].sock);
1512 }
1513 ++i;
1514#endif
1515 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1516 if (server_opts.binds[i].sock > -1) {
1517 close(server_opts.binds[i].sock);
1518 }
1519
1520 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001521 --server_opts.endpt_count;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001522#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1523 --i;
1524 i /= 2;
1525 if (i < server_opts.endpt_count) {
1526 memcpy(&server_opts.binds[2 * i], &server_opts.binds[2 * server_opts.endpt_count], 2 * sizeof *server_opts.binds);
1527 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1528 }
1529#else
Michal Vaskoc0256492016-02-02 12:19:06 +01001530 if (i < server_opts.endpt_count) {
1531 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1532 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001533 }
1534#endif
1535 else if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001536 free(server_opts.binds);
1537 server_opts.binds = NULL;
1538 free(server_opts.endpts);
1539 server_opts.endpts = NULL;
1540 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001541
1542 ret = 0;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001543 break;
Michal Vasko1a38c862016-01-15 15:50:07 +01001544 }
1545 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001546 }
1547
Michal Vasko3031aae2016-01-27 16:07:18 +01001548 /* WRITE UNLOCK */
1549 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001550
Michal Vasko9e036d52016-01-08 10:49:26 +01001551 return ret;
1552}
1553
Michal Vasko71090fc2016-05-24 16:37:28 +02001554API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001555nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001556{
Michal Vasko71090fc2016-05-24 16:37:28 +02001557 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001558 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001559 char *host = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001560 uint16_t port, endpt_idx, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001561
Michal Vasko45e53ae2016-04-07 11:46:03 +02001562 if (!server_opts.ctx) {
1563 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001564 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001565 } else if (!session) {
1566 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001567 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001568 }
1569
Michal Vasko51e514d2016-02-02 15:51:52 +01001570 /* we have to hold WRITE for the whole time, since there is not
1571 * a way of downgrading the lock to READ */
1572 /* WRITE LOCK */
1573 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
1574
1575 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001576 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001577 /* WRITE UNLOCK */
1578 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001579 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001580 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001581
Michal Vasko94acafc2016-09-23 13:40:10 +02001582#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1583 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count * 2, timeout, &host, &port, &bind_idx);
1584#else
Michal Vaskoe2713da2016-08-22 16:06:40 +02001585 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko94acafc2016-09-23 13:40:10 +02001586#endif
Michal Vaskob48aa812016-01-18 14:13:09 +01001587
Michal Vasko50456e82016-02-02 12:16:08 +01001588 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001589 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001590 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001591 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001592 if (!ret) {
1593 return NC_MSG_WOULDBLOCK;
1594 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001595 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001596 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001597 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001598
Michal Vasko1a38c862016-01-15 15:50:07 +01001599 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001600 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001601 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001602 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001603 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001604 msgtype = NC_MSG_ERROR;
1605 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001606 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001607 (*session)->status = NC_STATUS_STARTING;
1608 (*session)->side = NC_SERVER;
1609 (*session)->ctx = server_opts.ctx;
1610 (*session)->flags = NC_SESSION_SHAREDCTX;
1611 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1612 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001613
1614 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001615 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1616 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001617 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001618 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001619 msgtype = NC_MSG_ERROR;
1620 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001621 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001622 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001623
Michal Vaskoe2713da2016-08-22 16:06:40 +02001624 endpt_idx = bind_idx;
1625 /* transform index as needed */
1626#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1627 if (server_opts.binds[bind_idx].ti == NC_TI_OPENSSL) {
1628 --endpt_idx;
1629 }
1630 endpt_idx /= 2;
1631#endif
Michal Vasko3031aae2016-01-27 16:07:18 +01001632
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001633 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001634#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001635 if (server_opts.binds[bind_idx].ti == NC_TI_LIBSSH) {
1636 (*session)->data = server_opts.endpts[endpt_idx].ssh_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001637 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001638 if (ret < 0) {
1639 msgtype = NC_MSG_ERROR;
1640 goto cleanup;
1641 } else if (!ret) {
1642 msgtype = NC_MSG_WOULDBLOCK;
1643 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001644 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001645 } else
1646#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001647#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001648 if (server_opts.binds[bind_idx].ti == NC_TI_OPENSSL) {
1649 (*session)->data = server_opts.endpts[endpt_idx].tls_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001650 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001651 if (ret < 0) {
1652 msgtype = NC_MSG_ERROR;
1653 goto cleanup;
1654 } else if (!ret) {
1655 msgtype = NC_MSG_WOULDBLOCK;
1656 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001657 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001658 } else
1659#endif
1660 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001661 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001662 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001663 msgtype = NC_MSG_ERROR;
1664 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001665 }
1666
Michal Vasko2cc4c682016-03-01 09:16:48 +01001667 (*session)->data = NULL;
1668
Michal Vasko51e514d2016-02-02 15:51:52 +01001669 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001670 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1671
Michal Vaskob48aa812016-01-18 14:13:09 +01001672 /* assign new SID atomically */
1673 /* LOCK */
1674 pthread_spin_lock(&server_opts.sid_lock);
1675 (*session)->id = server_opts.new_session_id++;
1676 /* UNLOCK */
1677 pthread_spin_unlock(&server_opts.sid_lock);
1678
Michal Vasko9e036d52016-01-08 10:49:26 +01001679 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001680 msgtype = nc_handshake(*session);
1681 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001682 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001683 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001684 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001685 }
Michal Vasko2f975cf2016-07-28 15:47:00 +02001686 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001687 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001688
Michal Vasko71090fc2016-05-24 16:37:28 +02001689 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001690
Michal Vasko71090fc2016-05-24 16:37:28 +02001691cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001692 /* WRITE UNLOCK */
1693 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1694
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001695 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001696 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001697 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001698}
1699
Michal Vasko71090fc2016-05-24 16:37:28 +02001700NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001701nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01001702{
Michal Vasko71090fc2016-05-24 16:37:28 +02001703 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001704 int sock, ret;
1705
Michal Vasko45e53ae2016-04-07 11:46:03 +02001706 if (!host) {
1707 ERRARG("host");
Michal Vasko71090fc2016-05-24 16:37:28 +02001708 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001709 } else if (!port) {
1710 ERRARG("port");
Michal Vasko71090fc2016-05-24 16:37:28 +02001711 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001712 } else if (!ti) {
1713 ERRARG("ti");
Michal Vasko71090fc2016-05-24 16:37:28 +02001714 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001715 } else if (!session) {
1716 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001717 return NC_MSG_ERROR;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001718 }
1719
Michal Vaskob05053d2016-01-22 16:12:06 +01001720 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001721 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001722 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001723 }
1724
1725 *session = calloc(1, sizeof **session);
1726 if (!(*session)) {
1727 ERRMEM;
1728 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001729 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001730 }
1731 (*session)->status = NC_STATUS_STARTING;
1732 (*session)->side = NC_SERVER;
1733 (*session)->ctx = server_opts.ctx;
1734 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vaskob05053d2016-01-22 16:12:06 +01001735 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01001736 (*session)->port = port;
1737
1738 /* transport lock */
1739 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1740 if (!(*session)->ti_lock) {
1741 ERRMEM;
1742 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001743 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001744 goto fail;
1745 }
1746 pthread_mutex_init((*session)->ti_lock, NULL);
1747
1748 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001749#ifdef NC_ENABLED_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001750 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001751 /* OPTS LOCK */
1752 pthread_mutex_lock(&ssh_ch_opts_lock);
1753
Michal Vasko2cc4c682016-03-01 09:16:48 +01001754 (*session)->data = &ssh_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001755 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001756 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001757
1758 /* OPTS UNLOCK */
1759 pthread_mutex_unlock(&ssh_ch_opts_lock);
1760
Michal Vasko71090fc2016-05-24 16:37:28 +02001761 if (ret < 0) {
1762 msgtype = NC_MSG_ERROR;
1763 goto fail;
1764 } else if (!ret) {
1765 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001766 goto fail;
1767 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001768 } else
1769#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001770#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001771 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001772 /* OPTS LOCK */
1773 pthread_mutex_lock(&tls_ch_opts_lock);
1774
Michal Vasko2cc4c682016-03-01 09:16:48 +01001775 (*session)->data = &tls_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001776 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001777 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001778
1779 /* OPTS UNLOCK */
1780 pthread_mutex_unlock(&tls_ch_opts_lock);
1781
Michal Vasko71090fc2016-05-24 16:37:28 +02001782 if (ret < 0) {
1783 msgtype = NC_MSG_ERROR;
1784 goto fail;
1785 } else if (!ret) {
1786 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001787 goto fail;
1788 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001789 } else
1790#endif
1791 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001792 ERRINT;
1793 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001794 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001795 goto fail;
1796 }
1797
1798 /* assign new SID atomically */
1799 /* LOCK */
1800 pthread_spin_lock(&server_opts.sid_lock);
1801 (*session)->id = server_opts.new_session_id++;
1802 /* UNLOCK */
1803 pthread_spin_unlock(&server_opts.sid_lock);
1804
1805 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001806 msgtype = nc_handshake(*session);
1807 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01001808 goto fail;
1809 }
Michal Vasko2f975cf2016-07-28 15:47:00 +02001810 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001811 (*session)->status = NC_STATUS_RUNNING;
1812
Michal Vasko71090fc2016-05-24 16:37:28 +02001813 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001814
1815fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001816 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001817 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001818 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001819}
1820
Radek Krejci53691be2016-02-22 13:58:37 +01001821#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02001822
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001823API time_t
1824nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02001825{
1826 if (!session) {
1827 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001828 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02001829 }
1830
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001831 return session->session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02001832}