blob: 82053329ec5d56d77b84a893100865ff5d3ac685 [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 Vaskoc6b9c7b2016-01-28 11:10:08 +010043nc_server_endpt_lock(const char *name, NC_TRANSPORT_IMPL ti)
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) {
52 if ((server_opts.binds[i].ti == ti) && !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
68 return endpt;
69}
70
71void
72nc_server_endpt_unlock(struct nc_endpt *endpt)
73{
74 /* ENDPT UNLOCK */
75 pthread_mutex_unlock(&endpt->endpt_lock);
76
77 /* READ UNLOCK */
Michal Vasko27562ad2016-02-02 15:50:39 +010078 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010079}
Michal Vasko086311b2016-01-08 09:53:11 +010080
Michal Vasko1a38c862016-01-15 15:50:07 +010081API void
82nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
83{
Michal Vasko45e53ae2016-04-07 11:46:03 +020084 if (!session) {
85 ERRARG("session");
86 return;
87 } else if (!reason) {
88 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +010089 return;
90 }
91
92 session->term_reason = reason;
93}
94
Michal Vasko086311b2016-01-08 09:53:11 +010095int
Michal Vaskof05562c2016-01-20 12:06:43 +010096nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +010097{
98 const int optVal = 1;
99 const socklen_t optLen = sizeof(optVal);
100 int is_ipv4, sock;
101 struct sockaddr_storage saddr;
102
103 struct sockaddr_in *saddr4;
104 struct sockaddr_in6 *saddr6;
105
106
107 if (!strchr(address, ':')) {
108 is_ipv4 = 1;
109 } else {
110 is_ipv4 = 0;
111 }
112
113 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
114 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100115 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100116 goto fail;
117 }
118
119 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100120 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100121 goto fail;
122 }
123
124 bzero(&saddr, sizeof(struct sockaddr_storage));
125 if (is_ipv4) {
126 saddr4 = (struct sockaddr_in *)&saddr;
127
128 saddr4->sin_family = AF_INET;
129 saddr4->sin_port = htons(port);
130
131 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100132 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100133 goto fail;
134 }
135
136 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100137 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100138 goto fail;
139 }
140
141 } else {
142 saddr6 = (struct sockaddr_in6 *)&saddr;
143
144 saddr6->sin6_family = AF_INET6;
145 saddr6->sin6_port = htons(port);
146
147 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100148 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100149 goto fail;
150 }
151
152 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100153 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100154 goto fail;
155 }
156 }
157
Michal Vaskofb89d772016-01-08 12:25:35 +0100158 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100159 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100160 goto fail;
161 }
162
163 return sock;
164
165fail:
166 if (sock > -1) {
167 close(sock);
168 }
169
170 return -1;
171}
172
173int
Michal Vasko3031aae2016-01-27 16:07:18 +0100174nc_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 +0100175{
176 uint16_t i;
177 struct pollfd *pfd;
178 struct sockaddr_storage saddr;
179 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100180 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100181
182 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100183 if (!pfd) {
184 ERRMEM;
185 return -1;
186 }
187
Michal Vasko086311b2016-01-08 09:53:11 +0100188 for (i = 0; i < bind_count; ++i) {
189 pfd[i].fd = binds[i].sock;
190 pfd[i].events = POLLIN;
191 pfd[i].revents = 0;
192 }
193
194 /* poll for a new connection */
195 errno = 0;
196 ret = poll(pfd, bind_count, timeout);
197 if (!ret) {
198 /* we timeouted */
199 free(pfd);
200 return 0;
201 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100202 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100203 free(pfd);
204 return -1;
205 }
206
207 for (i = 0; i < bind_count; ++i) {
208 if (pfd[i].revents & POLLIN) {
209 sock = pfd[i].fd;
210 break;
211 }
212 }
213 free(pfd);
214
215 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100217 return -1;
218 }
219
220 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100221 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100222 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100223 return -1;
224 }
225
Michal Vasko0190bc32016-03-02 15:47:49 +0100226 /* make the socket non-blocking */
227 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
228 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100229 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100230 return -1;
231 }
232
Michal Vasko3031aae2016-01-27 16:07:18 +0100233 if (idx) {
234 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100235 }
236
Michal Vasko086311b2016-01-08 09:53:11 +0100237 /* host was requested */
238 if (host) {
239 if (saddr.ss_family == AF_INET) {
240 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100241 if (*host) {
242 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
243 ERR("inet_ntop failed (%s).", strerror(errno));
244 free(*host);
245 *host = NULL;
246 }
Michal Vasko086311b2016-01-08 09:53:11 +0100247
Michal Vasko4eb3c312016-03-01 14:09:37 +0100248 if (port) {
249 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
250 }
251 } else {
252 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100253 }
254 } else if (saddr.ss_family == AF_INET6) {
255 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100256 if (*host) {
257 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
258 ERR("inet_ntop failed (%s).", strerror(errno));
259 free(*host);
260 *host = NULL;
261 }
Michal Vasko086311b2016-01-08 09:53:11 +0100262
Michal Vasko4eb3c312016-03-01 14:09:37 +0100263 if (port) {
264 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
265 }
266 } else {
267 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100268 }
269 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100270 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100271 }
272 }
273
274 return ret;
275}
276
Michal Vasko05ba9df2016-01-13 14:40:27 +0100277static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100278nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100279{
280 const char *identifier = NULL, *version = NULL, *format = NULL;
281 char *model_data = NULL;
282 const struct lys_module *module;
283 struct nc_server_error *err;
284 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100285 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100286
287 LY_TREE_FOR(rpc->child, child) {
288 if (!strcmp(child->schema->name, "identifier")) {
289 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
290 } else if (!strcmp(child->schema->name, "version")) {
291 version = ((struct lyd_node_leaf_list *)child)->value_str;
292 } else if (!strcmp(child->schema->name, "format")) {
293 format = ((struct lyd_node_leaf_list *)child)->value_str;
294 }
295 }
296
297 /* check version */
298 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100299 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
300 nc_err_set_msg(err, "The requested version is not supported.", "en");
301 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100302 }
303
304 /* check and get module with the name identifier */
305 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
306 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200307 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
308 }
309 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100310 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
311 nc_err_set_msg(err, "The requested schema was not found.", "en");
312 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100313 }
314
315 /* check format */
316 if (!format || !strcmp(format, "yang")) {
317 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
318 } else if (!strcmp(format, "yin")) {
319 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
320 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100321 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
322 nc_err_set_msg(err, "The requested format is not supported.", "en");
323 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100324 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200325 if (!model_data) {
326 ERRINT;
327 return NULL;
328 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100329
Michal Vasko303245c2016-03-24 15:20:03 +0100330 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200331 if (!sdata) {
332 ERRINT;
333 free(model_data);
334 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100335 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200336
337 data = lyd_output_new_anyxml_str(sdata, model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100338 if (!data) {
339 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200340 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100341 return NULL;
342 }
343
344 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
345}
346
347static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100348nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100349{
Michal Vasko428087d2016-01-14 16:04:28 +0100350 session->term_reason = NC_SESSION_TERM_CLOSED;
351 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100352}
353
Michal Vasko086311b2016-01-08 09:53:11 +0100354API int
355nc_server_init(struct ly_ctx *ctx)
356{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100357 const struct lys_node *rpc;
358
Michal Vasko086311b2016-01-08 09:53:11 +0100359 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200360 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100361 return -1;
362 }
363
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100364 nc_init();
365
Michal Vasko05ba9df2016-01-13 14:40:27 +0100366 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100367 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100368 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100369 lys_set_private(rpc, nc_clb_default_get_schema);
370 }
371
372 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100373 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100374 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100375 lys_set_private(rpc, nc_clb_default_close_session);
376 }
377
Michal Vasko086311b2016-01-08 09:53:11 +0100378 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100379
380 server_opts.new_session_id = 1;
381 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
382
Michal Vasko086311b2016-01-08 09:53:11 +0100383 return 0;
384}
385
Michal Vaskob48aa812016-01-18 14:13:09 +0100386API void
387nc_server_destroy(void)
388{
389 pthread_spin_destroy(&server_opts.sid_lock);
390
Radek Krejci53691be2016-02-22 13:58:37 +0100391#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3031aae2016-01-27 16:07:18 +0100392 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100393#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100394 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100395}
396
Michal Vasko086311b2016-01-08 09:53:11 +0100397API int
398nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
399{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200400 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
401 ERRARG("basic_mode");
402 return -1;
403 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
404 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100405 return -1;
406 }
407
408 server_opts.wd_basic_mode = basic_mode;
409 server_opts.wd_also_supported = also_supported;
410 return 0;
411}
412
Michal Vasko1a38c862016-01-15 15:50:07 +0100413API void
Michal Vasko55f03972016-04-13 08:56:01 +0200414nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
415{
416 if (!basic_mode && !also_supported) {
417 ERRARG("basic_mode and also_supported");
418 return;
419 }
420
421 if (basic_mode) {
422 *basic_mode = server_opts.wd_basic_mode;
423 }
424 if (also_supported) {
425 *also_supported = server_opts.wd_also_supported;
426 }
427}
428
429API void
Michal Vasko086311b2016-01-08 09:53:11 +0100430nc_server_set_capab_interleave(int interleave_support)
431{
432 if (interleave_support) {
433 server_opts.interleave_capab = 1;
434 } else {
435 server_opts.interleave_capab = 0;
436 }
Michal Vasko086311b2016-01-08 09:53:11 +0100437}
438
Michal Vasko55f03972016-04-13 08:56:01 +0200439API int
440nc_server_get_capab_interleave(void)
441{
442 return server_opts.interleave_capab;
443}
444
Michal Vasko1a38c862016-01-15 15:50:07 +0100445API void
Michal Vasko086311b2016-01-08 09:53:11 +0100446nc_server_set_hello_timeout(uint16_t hello_timeout)
447{
Michal Vasko086311b2016-01-08 09:53:11 +0100448 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100449}
450
Michal Vasko55f03972016-04-13 08:56:01 +0200451API uint16_t
452nc_server_get_hello_timeout(void)
453{
454 return server_opts.hello_timeout;
455}
456
Michal Vasko1a38c862016-01-15 15:50:07 +0100457API void
Michal Vasko086311b2016-01-08 09:53:11 +0100458nc_server_set_idle_timeout(uint16_t idle_timeout)
459{
Michal Vasko086311b2016-01-08 09:53:11 +0100460 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100461}
462
Michal Vasko55f03972016-04-13 08:56:01 +0200463API uint16_t
464nc_server_get_idle_timeout(void)
465{
466 return server_opts.idle_timeout;
467}
468
Michal Vasko086311b2016-01-08 09:53:11 +0100469API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100470nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100471{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200472 if (!server_opts.ctx) {
473 ERRINIT;
474 return -1;
475 } else if (fdin < 0) {
476 ERRARG("fdin");
477 return -1;
478 } else if (fdout < 0) {
479 ERRARG("fdout");
480 return -1;
481 } else if (!username) {
482 ERRARG("username");
483 return -1;
484 } else if (!session) {
485 ERRARG("session");
Michal Vasko1a38c862016-01-15 15:50:07 +0100486 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100487 }
488
489 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100490 *session = calloc(1, sizeof **session);
491 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100492 ERRMEM;
Michal Vasko1a38c862016-01-15 15:50:07 +0100493 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100494 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100495 (*session)->status = NC_STATUS_STARTING;
496 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100497
498 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100499 (*session)->ti_type = NC_TI_FD;
500 (*session)->ti.fd.in = fdin;
501 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100502
503 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100504 (*session)->flags = NC_SESSION_SHAREDCTX;
505 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100506
Michal Vaskob48aa812016-01-18 14:13:09 +0100507 /* assign new SID atomically */
508 pthread_spin_lock(&server_opts.sid_lock);
509 (*session)->id = server_opts.new_session_id++;
510 pthread_spin_unlock(&server_opts.sid_lock);
511
Michal Vasko086311b2016-01-08 09:53:11 +0100512 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100513 if (nc_handshake(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100514 goto fail;
515 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100516 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100517 (*session)->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100518
Michal Vasko1a38c862016-01-15 15:50:07 +0100519 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100520
521fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100522 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100523 *session = NULL;
524 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100525}
Michal Vasko9e036d52016-01-08 10:49:26 +0100526
Michal Vaskof04a52a2016-04-07 10:52:10 +0200527int
Michal Vaskobe86fe32016-04-07 10:43:03 +0200528nc_ps_lock(struct nc_pollsession *ps)
529{
530 int ret;
531 uint8_t our_id, queue_last;
532 struct timespec ts;
533
534 clock_gettime(CLOCK_REALTIME, &ts);
535 ts.tv_sec += NC_READ_TIMEOUT;
536
537 /* LOCK */
538 ret = pthread_mutex_timedlock(&ps->lock, &ts);
539 if (ret) {
540 ERR("Failed to lock a pollsession (%s).", strerror(ret));
541 return -1;
542 }
543
544 /* get a unique queue value (by adding 1 to the last added value, if any) */
545 if (ps->queue_len) {
546 queue_last = ps->queue_begin + ps->queue_len - 1;
547 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
548 queue_last -= NC_PS_QUEUE_SIZE;
549 }
550 our_id = ps->queue[queue_last] + 1;
551 } else {
552 our_id = 0;
553 }
554
555 /* add ourselves into the queue */
556 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
557 ERR("Pollsession queue too small.");
558 return -1;
559 }
560 ++ps->queue_len;
561 queue_last = ps->queue_begin + ps->queue_len - 1;
562 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
563 queue_last -= NC_PS_QUEUE_SIZE;
564 }
565 ps->queue[queue_last] = our_id;
566
567 /* is it our turn? */
568 while (ps->queue[ps->queue_begin] != our_id) {
569 clock_gettime(CLOCK_REALTIME, &ts);
570 ts.tv_sec += NC_READ_TIMEOUT;
571
572 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
573 if (ret) {
574 ERR("Failed to wait for a pollsession condition (%s).", strerror(ret));
575 /* remove ourselves from the queue */
576 ps->queue_begin = (ps->queue_begin < NC_PS_QUEUE_SIZE - 1 ? ps->queue_begin + 1 : 0);
577 --ps->queue_len;
578 return -1;
579 }
580 }
581
Michal Vaskobe86fe32016-04-07 10:43:03 +0200582 /* UNLOCK */
583 pthread_mutex_unlock(&ps->lock);
584
585 return 0;
586}
587
Michal Vaskof04a52a2016-04-07 10:52:10 +0200588int
Michal Vaskobe86fe32016-04-07 10:43:03 +0200589nc_ps_unlock(struct nc_pollsession *ps)
590{
591 int ret;
592 struct timespec ts;
593
594 clock_gettime(CLOCK_REALTIME, &ts);
595 ts.tv_sec += NC_READ_TIMEOUT;
596
597 /* LOCK */
598 ret = pthread_mutex_timedlock(&ps->lock, &ts);
599 if (ret) {
600 ERR("Failed to lock a pollsession (%s).", strerror(ret));
601 ret = -1;
602 }
603
604 /* remove ourselves from the queue */
605 ps->queue_begin = (ps->queue_begin < NC_PS_QUEUE_SIZE - 1 ? ps->queue_begin + 1 : 0);
606 --ps->queue_len;
607
608 /* broadcast to all other threads that the queue moved */
609 pthread_cond_broadcast(&ps->cond);
610
Michal Vaskobe86fe32016-04-07 10:43:03 +0200611 /* UNLOCK */
612 if (!ret) {
613 pthread_mutex_unlock(&ps->lock);
614 }
615
616 return ret;
617}
618
Michal Vasko428087d2016-01-14 16:04:28 +0100619API struct nc_pollsession *
620nc_ps_new(void)
621{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100622 struct nc_pollsession *ps;
623
624 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100625 if (!ps) {
626 ERRMEM;
627 return NULL;
628 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200629 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100630 pthread_mutex_init(&ps->lock, NULL);
631
632 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100633}
634
635API void
636nc_ps_free(struct nc_pollsession *ps)
637{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100638 if (!ps) {
639 return;
640 }
641
Michal Vaskobe86fe32016-04-07 10:43:03 +0200642 if (ps->queue_len) {
643 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
644 }
645
Michal Vasko3a715132016-01-21 15:40:31 +0100646 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100647 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100648 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200649 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100650
Michal Vasko428087d2016-01-14 16:04:28 +0100651 free(ps);
652}
653
654API int
655nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
656{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200657 if (!ps) {
658 ERRARG("ps");
659 return -1;
660 } else if (!session) {
661 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100662 return -1;
663 }
664
Michal Vasko48a63ed2016-03-01 09:48:21 +0100665 /* LOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200666 if (nc_ps_lock(ps)) {
667 return -1;
668 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100669
Michal Vasko428087d2016-01-14 16:04:28 +0100670 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100671 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
672 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
673 if (!ps->pfds || !ps->sessions) {
674 ERRMEM;
675 /* UNLOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200676 nc_ps_unlock(ps);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100677 return -1;
678 }
Michal Vasko428087d2016-01-14 16:04:28 +0100679
680 switch (session->ti_type) {
681 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100682 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100683 break;
684
Radek Krejci53691be2016-02-22 13:58:37 +0100685#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100686 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100687 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100688 break;
689#endif
690
Radek Krejci53691be2016-02-22 13:58:37 +0100691#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100692 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100693 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100694 break;
695#endif
696
697 default:
698 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100699 /* UNLOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200700 nc_ps_unlock(ps);
Michal Vasko428087d2016-01-14 16:04:28 +0100701 return -1;
702 }
Michal Vasko3a715132016-01-21 15:40:31 +0100703 ps->pfds[ps->session_count - 1].events = POLLIN;
704 ps->pfds[ps->session_count - 1].revents = 0;
705 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100706
Michal Vasko48a63ed2016-03-01 09:48:21 +0100707 /* UNLOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200708 return nc_ps_unlock(ps);
Michal Vasko428087d2016-01-14 16:04:28 +0100709}
710
Michal Vasko48a63ed2016-03-01 09:48:21 +0100711static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100712_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100713{
714 uint16_t i;
715
Radek Krejcid5f978f2016-03-03 13:14:45 +0100716 if (index >= 0) {
717 i = (uint16_t)index;
718 goto remove;
719 }
Michal Vasko428087d2016-01-14 16:04:28 +0100720 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100721 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100722remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100723 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100724 if (i < ps->session_count) {
725 ps->sessions[i] = ps->sessions[ps->session_count];
726 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
727 } else if (!ps->session_count) {
728 free(ps->sessions);
729 ps->sessions = NULL;
730 free(ps->pfds);
731 ps->pfds = NULL;
732 }
Michal Vasko428087d2016-01-14 16:04:28 +0100733 return 0;
734 }
735 }
736
Michal Vaskof0537d82016-01-29 14:42:38 +0100737 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100738}
739
Michal Vasko48a63ed2016-03-01 09:48:21 +0100740API int
741nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
742{
Michal Vaskobe86fe32016-04-07 10:43:03 +0200743 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100744
Michal Vasko45e53ae2016-04-07 11:46:03 +0200745 if (!ps) {
746 ERRARG("ps");
747 return -1;
748 } else if (!session) {
749 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100750 return -1;
751 }
752
753 /* LOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200754 if (nc_ps_lock(ps)) {
755 return -1;
756 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100757
Radek Krejcid5f978f2016-03-03 13:14:45 +0100758 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100759
760 /* UNLOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200761 ret2 = nc_ps_unlock(ps);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100762
Michal Vaskobe86fe32016-04-07 10:43:03 +0200763 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100764}
765
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100766API uint16_t
767nc_ps_session_count(struct nc_pollsession *ps)
768{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100769 uint16_t count;
770
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100771 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200772 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100773 return 0;
774 }
775
Michal Vasko48a63ed2016-03-01 09:48:21 +0100776 /* LOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200777 if (nc_ps_lock(ps)) {
778 return -1;
779 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100780
781 count = ps->session_count;
782
783 /* UNLOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200784 nc_ps_unlock(ps);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100785
786 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100787}
788
Michal Vasko428087d2016-01-14 16:04:28 +0100789/* must be called holding the session lock! */
790static NC_MSG_TYPE
791nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
792{
793 struct lyxml_elem *xml = NULL;
794 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200795 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200796 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100797
Michal Vasko45e53ae2016-04-07 11:46:03 +0200798 if (!session) {
799 ERRARG("session");
800 return NC_MSG_ERROR;
801 } else if (!rpc) {
802 ERRARG("rpc");
Michal Vasko428087d2016-01-14 16:04:28 +0100803 return NC_MSG_ERROR;
804 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100805 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100806 return NC_MSG_ERROR;
807 }
808
809 msgtype = nc_read_msg(session, &xml);
810
811 switch (msgtype) {
812 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200813 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100814 if (!*rpc) {
815 ERRMEM;
816 goto error;
817 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100818
Radek Krejcif93c7d42016-04-06 13:41:15 +0200819 ly_errno = LY_SUCCESS;
Michal Vasko428087d2016-01-14 16:04:28 +0100820 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100821 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200822 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200823 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200824 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200825 nc_server_reply_free(reply);
826 if (ret == -1) {
827 ERR("Session %u: failed to write reply.", session->id);
828 msgtype = NC_MSG_ERROR;
829 } else {
830 msgtype = NC_MSG_NONE;
831 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100832 }
Michal Vasko428087d2016-01-14 16:04:28 +0100833 (*rpc)->root = xml;
834 break;
835 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100836 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100837 goto error;
838 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100839 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100840 goto error;
841 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100842 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100843 goto error;
844 default:
845 /* NC_MSG_ERROR - pass it out;
846 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
847 */
848 break;
849 }
850
851 return msgtype;
852
853error:
854 /* cleanup */
855 lyxml_free(server_opts.ctx, xml);
856
857 return NC_MSG_ERROR;
858}
859
860/* must be called holding the session lock! */
861static NC_MSG_TYPE
862nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
863{
864 nc_rpc_clb clb;
865 struct nc_server_reply *reply;
866 int ret;
867
Michal Vasko4a827e52016-03-03 10:59:00 +0100868 if (!rpc) {
869 ERRINT;
870 return NC_MSG_ERROR;
871 }
872
Michal Vasko428087d2016-01-14 16:04:28 +0100873 /* no callback, reply with a not-implemented error */
Radek Krejcif93c7d42016-04-06 13:41:15 +0200874 if (!rpc->tree->schema->priv) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100875 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100876 } else {
Michal Vaskofd100c92016-03-01 15:23:46 +0100877 clb = (nc_rpc_clb)rpc->tree->schema->priv;
Michal Vasko428087d2016-01-14 16:04:28 +0100878 reply = clb(rpc->tree, session);
879 }
880
881 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100882 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100883 }
884
885 ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
886
887 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
888 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
889 session->status = NC_STATUS_INVALID;
890 }
891
892 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100893 ERR("Session %u: failed to write reply.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100894 nc_server_reply_free(reply);
895 return NC_MSG_ERROR;
896 }
897 nc_server_reply_free(reply);
898
899 return NC_MSG_REPLY;
900}
901
902API int
903nc_ps_poll(struct nc_pollsession *ps, int timeout)
904{
905 int ret;
Michal Vasko3512e402016-01-28 16:22:34 +0100906 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100907 time_t cur_time;
Michal Vasko428087d2016-01-14 16:04:28 +0100908 NC_MSG_TYPE msgtype;
909 struct nc_session *session;
Michal Vasko4a827e52016-03-03 10:59:00 +0100910 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100911
912 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200913 ERRARG("ps");
Michal Vasko428087d2016-01-14 16:04:28 +0100914 return -1;
915 }
916
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100917 cur_time = time(NULL);
918
Michal Vasko48a63ed2016-03-01 09:48:21 +0100919 /* LOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +0200920 if (nc_ps_lock(ps)) {
921 return -1;
922 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100923
Michal Vasko428087d2016-01-14 16:04:28 +0100924 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100925 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
926 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100927 ret = -1;
928 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +0100929 }
Michal Vaskobd8ef262016-01-20 11:09:27 +0100930
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100931 /* TODO invalidate only sessions without subscription */
Michal Vasko3a715132016-01-21 15:40:31 +0100932 if (server_opts.idle_timeout && (ps->sessions[i]->last_rpc + server_opts.idle_timeout >= cur_time)) {
933 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
934 ps->sessions[i]->status = NC_STATUS_INVALID;
935 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100936 ret = 3;
937 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100938 }
939
Michal Vasko3a715132016-01-21 15:40:31 +0100940 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100941 break;
942 }
Michal Vasko428087d2016-01-14 16:04:28 +0100943 }
944
Michal Vaskobd8ef262016-01-20 11:09:27 +0100945 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +0100946#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +0100947retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +0100948#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +0100949 /* no leftover event */
950 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +0100951 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100952 if (ret < 1) {
Michal Vasko48a63ed2016-03-01 09:48:21 +0100953 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100954 }
Michal Vasko428087d2016-01-14 16:04:28 +0100955 }
956
Michal Vaskobd8ef262016-01-20 11:09:27 +0100957 /* find the first fd with POLLIN, we don't care if there are more now */
958 for (; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100959 if (ps->pfds[i].revents & POLLHUP) {
960 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
961 ps->sessions[i]->status = NC_STATUS_INVALID;
962 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100963 ret = 3;
964 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +0100965 } else if (ps->pfds[i].revents & POLLERR) {
966 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
967 ps->sessions[i]->status = NC_STATUS_INVALID;
968 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100969 ret = 3;
970 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +0100971 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +0100972#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +0100973 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +0100974 uint16_t j;
975
Michal Vasko96164bf2016-01-21 15:41:58 +0100976 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +0100977 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100978
979 /* clear POLLIN on sessions sharing this session's SSH session */
980 if ((ret == 1) || (ret >= 4)) {
981 for (j = i + 1; j < ps->session_count; ++j) {
982 if (ps->pfds[j].fd == ps->pfds[i].fd) {
983 ps->pfds[j].revents = 0;
984 }
985 }
986 }
987
988 /* actual event happened */
989 if ((ret <= 0) || (ret >= 3)) {
990 ps->pfds[i].revents = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100991 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +0100992
993 /* event occurred on some other channel */
994 } else if (ret == 2) {
995 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100996 if (i == ps->session_count - 1) {
997 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +0100998 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +0100999 /* ... timeout is 0, so that is it */
Michal Vasko48a63ed2016-03-01 09:48:21 +01001000 ret = 0;
1001 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001002 }
Michal Vasko8c748832016-02-03 15:32:16 +01001003 /* ... retry polling reasonable time apart ... */
1004 usleep(NC_TIMEOUT_STEP);
1005 if (timeout > 0) {
1006 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001007 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001008 }
1009 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001010 }
1011 /* check other sessions */
1012 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001013 }
1014 }
Radek Krejci53691be2016-02-22 13:58:37 +01001015#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001016
Michal Vaskobd8ef262016-01-20 11:09:27 +01001017 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001018 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001019 break;
1020 }
1021 }
1022
1023 if (i == ps->session_count) {
1024 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001025 ret = -1;
1026 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001027 }
1028
1029 /* this is the session with some data available for reading */
Michal Vasko3a715132016-01-21 15:40:31 +01001030 session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +01001031
Michal Vaskobd8ef262016-01-20 11:09:27 +01001032 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001033 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001034 if (ret != 1) {
1035 /* error or timeout */
Michal Vasko48a63ed2016-03-01 09:48:21 +01001036 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001037 }
1038
1039 msgtype = nc_recv_rpc(session, &rpc);
1040 if (msgtype == NC_MSG_ERROR) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001041 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +01001042 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko48a63ed2016-03-01 09:48:21 +01001043 ret = 3;
1044 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001045 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001046 ret = -1;
1047 goto finish;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001048 } else if (msgtype == NC_MSG_NONE) {
1049 /* already processed, just stop further processing */
1050 pthread_mutex_unlock(session->ti_lock);
1051 goto done;
Michal Vasko428087d2016-01-14 16:04:28 +01001052 }
1053
Michal Vaskoca4a2422016-02-02 12:17:14 +01001054 if (msgtype == NC_MSG_RPC) {
1055 session->last_rpc = time(NULL);
1056 }
1057
Michal Vasko428087d2016-01-14 16:04:28 +01001058 /* process RPC */
1059 msgtype = nc_send_reply(session, rpc);
1060
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001061 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +01001062
1063 if (msgtype == NC_MSG_ERROR) {
Michal Vaskoca4a2422016-02-02 12:17:14 +01001064 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001065 ret = -1;
1066 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001067 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001068
1069done:
Michal Vaskoca4a2422016-02-02 12:17:14 +01001070 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001071
Michal Vaskobd8b4e12016-01-22 16:11:20 +01001072 /* status change takes precedence over leftover events (return 2) */
1073 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko48a63ed2016-03-01 09:48:21 +01001074 ret = 3;
1075 goto finish;
Michal Vaskobd8b4e12016-01-22 16:11:20 +01001076 }
1077
Michal Vaskobd8ef262016-01-20 11:09:27 +01001078 /* is there some other socket waiting? */
1079 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001080 if (ps->pfds[i].revents) {
Michal Vasko48a63ed2016-03-01 09:48:21 +01001081 ret = 2;
1082 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001083 }
1084 }
1085
Michal Vasko48a63ed2016-03-01 09:48:21 +01001086 ret = 1;
1087
1088finish:
1089 /* UNLOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +02001090 nc_ps_unlock(ps);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001091 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001092}
1093
Michal Vaskod09eae62016-02-01 10:32:52 +01001094API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001095nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001096{
1097 uint16_t i;
1098 struct nc_session *session;
1099
Michal Vasko9a25e932016-02-01 10:36:42 +01001100 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001101 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001102 return;
1103 }
1104
Michal Vasko48a63ed2016-03-01 09:48:21 +01001105 /* LOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +02001106 if (nc_ps_lock(ps)) {
1107 return;
1108 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001109
Michal Vasko48a63ed2016-03-01 09:48:21 +01001110 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001111 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001112 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001113 }
1114 free(ps->sessions);
1115 ps->sessions = NULL;
1116 free(ps->pfds);
1117 ps->pfds = NULL;
1118 ps->session_count = 0;
1119 } else {
1120 for (i = 0; i < ps->session_count; ) {
1121 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1122 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001123 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001124 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001125 continue;
1126 }
1127
1128 ++i;
1129 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001130 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001131
1132 /* UNLOCK */
Michal Vaskobe86fe32016-04-07 10:43:03 +02001133 nc_ps_unlock(ps);
Michal Vaskod09eae62016-02-01 10:32:52 +01001134}
1135
Radek Krejci53691be2016-02-22 13:58:37 +01001136#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001137
Michal Vasko3031aae2016-01-27 16:07:18 +01001138int
1139nc_server_add_endpt_listen(const char *name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001140{
1141 int sock;
Michal Vasko3031aae2016-01-27 16:07:18 +01001142 uint16_t i;
Radek Krejci53691be2016-02-22 13:58:37 +01001143#ifdef NC_ENABLED_SSH
Michal Vasko08a629a2016-02-02 12:20:47 +01001144 struct nc_server_ssh_opts *ssh_opts;
1145#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001146
Michal Vasko45e53ae2016-04-07 11:46:03 +02001147 if (!name) {
1148 ERRARG("name");
1149 return -1;
1150 } else if (!address) {
1151 ERRARG("address");
1152 return -1;
1153 } else if (!port) {
1154 ERRARG("port");
Michal Vasko9e036d52016-01-08 10:49:26 +01001155 return -1;
1156 }
1157
Michal Vasko51e514d2016-02-02 15:51:52 +01001158 /* WRITE LOCK */
1159 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001160
1161 /* check name uniqueness */
1162 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskod4c03a82016-02-08 15:27:26 +01001163 if ((server_opts.binds[i].ti == ti) && !strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001164 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001165 /* WRITE UNLOCK */
Michal Vasko9faf1c82016-02-01 13:26:19 +01001166 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001167 return -1;
1168 }
1169 }
1170
Michal Vasko9e036d52016-01-08 10:49:26 +01001171 sock = nc_sock_listen(address, port);
1172 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001173 /* WRITE UNLOCK */
1174 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001175 return -1;
1176 }
1177
Michal Vasko3031aae2016-01-27 16:07:18 +01001178 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001179 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
1180 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
1181 if (!server_opts.binds || !server_opts.endpts) {
1182 ERRMEM;
1183 /* WRITE UNLOCK */
1184 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko0f74da52016-03-03 08:52:52 +01001185 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001186 return -1;
1187 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001188
Michal Vasko3031aae2016-01-27 16:07:18 +01001189 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1190 server_opts.binds[server_opts.endpt_count - 1].address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001191 server_opts.binds[server_opts.endpt_count - 1].port = port;
1192 server_opts.binds[server_opts.endpt_count - 1].sock = sock;
1193 server_opts.binds[server_opts.endpt_count - 1].ti = ti;
1194 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001195#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001196 case NC_TI_LIBSSH:
Michal Vasko08a629a2016-02-02 12:20:47 +01001197 ssh_opts = calloc(1, sizeof *ssh_opts);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001198 if (!ssh_opts) {
1199 ERRMEM;
1200 /* WRITE UNLOCK */
1201 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1202 return -1;
1203 }
Michal Vasko08a629a2016-02-02 12:20:47 +01001204 /* set default values */
1205 ssh_opts->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1206 ssh_opts->auth_attempts = 3;
1207 ssh_opts->auth_timeout = 10;
1208
1209 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = ssh_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +01001210 break;
1211#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001212#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001213 case NC_TI_OPENSSL:
1214 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = calloc(1, sizeof(struct nc_server_tls_opts));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001215 if (!server_opts.endpts[server_opts.endpt_count - 1].ti_opts) {
1216 ERRMEM;
1217 /* WRITE UNLOCK */
1218 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1219 return -1;
1220 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001221 break;
1222#endif
1223 default:
1224 ERRINT;
1225 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = NULL;
1226 break;
1227 }
1228 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001229
Michal Vasko3031aae2016-01-27 16:07:18 +01001230 /* WRITE UNLOCK */
1231 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001232
Michal Vasko9e036d52016-01-08 10:49:26 +01001233 return 0;
1234}
1235
Michal Vasko3031aae2016-01-27 16:07:18 +01001236int
Michal Vaskoda514772016-02-01 11:32:01 +01001237nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1238{
1239 struct nc_endpt *endpt;
1240 struct nc_bind *bind = NULL;
1241 uint16_t i;
1242 int sock;
1243
Michal Vasko45e53ae2016-04-07 11:46:03 +02001244 if (!endpt_name) {
1245 ERRARG("endpt_name");
1246 return -1;
1247 } else if ((!address && !port) || (address && port)) {
1248 ERRARG("address and port");
1249 return -1;
1250 } else if (!ti) {
1251 ERRARG("ti");
Michal Vaskoda514772016-02-01 11:32:01 +01001252 return -1;
1253 }
1254
Michal Vasko51e514d2016-02-02 15:51:52 +01001255 /* LOCK */
Michal Vaskoda514772016-02-01 11:32:01 +01001256 endpt = nc_server_endpt_lock(endpt_name, ti);
1257 if (!endpt) {
1258 return -1;
1259 }
1260
1261 /* we need to learn the index, to get the bind :-/ */
1262 for (i = 0; i < server_opts.endpt_count; ++i) {
1263 if (&server_opts.endpts[i] == endpt) {
1264 bind = &server_opts.binds[i];
1265 }
1266 }
1267 if (!bind) {
1268 ERRINT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001269 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001270 }
1271
1272 if (address) {
1273 sock = nc_sock_listen(address, bind->port);
1274 } else {
1275 sock = nc_sock_listen(bind->address, port);
1276 }
1277 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001278 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001279 }
1280
1281 /* close old socket, update parameters */
1282 close(bind->sock);
1283 bind->sock = sock;
1284 if (address) {
1285 lydict_remove(server_opts.ctx, bind->address);
1286 bind->address = lydict_insert(server_opts.ctx, address, 0);
1287 } else {
1288 bind->port = port;
1289 }
1290
Michal Vasko51e514d2016-02-02 15:51:52 +01001291 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001292 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001293 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001294
1295fail:
1296 /* UNLOCK */
1297 nc_server_endpt_unlock(endpt);
1298 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001299}
1300
1301int
Michal Vasko3031aae2016-01-27 16:07:18 +01001302nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001303{
1304 uint32_t i;
1305 int ret = -1;
1306
Michal Vasko3031aae2016-01-27 16:07:18 +01001307 /* WRITE LOCK */
1308 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001309
Michal Vasko3031aae2016-01-27 16:07:18 +01001310 if (!name && !ti) {
1311 /* remove all */
Michal Vasko3031aae2016-01-27 16:07:18 +01001312 for (i = 0; i < server_opts.endpt_count; ++i) {
1313 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001314 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko51e514d2016-02-02 15:51:52 +01001315
Michal Vasko3031aae2016-01-27 16:07:18 +01001316 close(server_opts.binds[i].sock);
1317 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1318 switch (server_opts.binds[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001319#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001320 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001321 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001322 break;
1323#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001324#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001325 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001326 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001327 break;
1328#endif
1329 default:
1330 ERRINT;
1331 break;
1332 }
1333 free(server_opts.endpts[i].ti_opts);
Michal Vasko9e036d52016-01-08 10:49:26 +01001334
Michal Vasko9e036d52016-01-08 10:49:26 +01001335 ret = 0;
1336 }
Michal Vasko7ddc5702016-02-08 15:29:39 +01001337 free(server_opts.binds);
1338 server_opts.binds = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001339 free(server_opts.endpts);
1340 server_opts.endpts = NULL;
1341 server_opts.endpt_count = 0;
1342
Michal Vasko1a38c862016-01-15 15:50:07 +01001343 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001344 /* remove one name endpoint or all ti endpoints */
1345 for (i = 0; i < server_opts.endpt_count; ++i) {
1346 if ((server_opts.binds[i].ti == ti) &&
1347 (!name || !strcmp(server_opts.endpts[i].name, name))) {
1348
Michal Vasko3031aae2016-01-27 16:07:18 +01001349 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001350 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001351 close(server_opts.binds[i].sock);
1352 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1353 switch (server_opts.binds[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001354#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001355 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001356 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001357 break;
1358#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001359#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001360 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001361 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001362 break;
1363#endif
1364 default:
1365 ERRINT;
1366 break;
1367 }
1368 free(server_opts.endpts[i].ti_opts);
Michal Vasko1a38c862016-01-15 15:50:07 +01001369
Michal Vasko3031aae2016-01-27 16:07:18 +01001370 --server_opts.endpt_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001371 if (i < server_opts.endpt_count) {
1372 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1373 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1374 } else if (!server_opts.endpt_count) {
1375 free(server_opts.binds);
1376 server_opts.binds = NULL;
1377 free(server_opts.endpts);
1378 server_opts.endpts = NULL;
1379 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001380
1381 ret = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001382
1383 if (name) {
1384 /* one name endpoint removed, they are unique, we're done */
1385 break;
1386 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001387 }
1388 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001389 }
1390
Michal Vasko3031aae2016-01-27 16:07:18 +01001391 /* WRITE UNLOCK */
1392 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001393
Michal Vasko9e036d52016-01-08 10:49:26 +01001394 return ret;
1395}
1396
Michal Vasko1a38c862016-01-15 15:50:07 +01001397API int
1398nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001399{
Michal Vasko1a38c862016-01-15 15:50:07 +01001400 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001401 char *host = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001402 uint16_t port, idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001403
Michal Vasko45e53ae2016-04-07 11:46:03 +02001404 if (!server_opts.ctx) {
1405 ERRINIT;
1406 return -1;
1407 } else if (!session) {
1408 ERRARG("session");
Michal Vasko1a38c862016-01-15 15:50:07 +01001409 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001410 }
1411
Michal Vasko51e514d2016-02-02 15:51:52 +01001412 /* we have to hold WRITE for the whole time, since there is not
1413 * a way of downgrading the lock to READ */
1414 /* WRITE LOCK */
1415 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
1416
1417 if (!server_opts.endpt_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001418 ERRINIT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001419 /* WRITE UNLOCK */
1420 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1421 return -1;
1422 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001423
Michal Vasko3031aae2016-01-27 16:07:18 +01001424 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
Michal Vaskob48aa812016-01-18 14:13:09 +01001425
Michal Vasko50456e82016-02-02 12:16:08 +01001426 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001427 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001428 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001429 free(host);
Michal Vaskob48aa812016-01-18 14:13:09 +01001430 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001431 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001432 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001433
Michal Vasko1a38c862016-01-15 15:50:07 +01001434 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001435 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001436 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001437 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001438 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001439 ret = -1;
1440 goto fail;
Michal Vasko9e036d52016-01-08 10:49:26 +01001441 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001442 (*session)->status = NC_STATUS_STARTING;
1443 (*session)->side = NC_SERVER;
1444 (*session)->ctx = server_opts.ctx;
1445 (*session)->flags = NC_SESSION_SHAREDCTX;
1446 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1447 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001448
1449 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001450 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1451 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001452 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001453 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001454 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001455 goto fail;
1456 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001457 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001458
Michal Vasko2cc4c682016-03-01 09:16:48 +01001459 (*session)->data = server_opts.endpts[idx].ti_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +01001460
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001461 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001462#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001463 if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001464 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001465 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001466 goto fail;
1467 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001468 } else
1469#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001470#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001471 if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001472 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001473 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001474 goto fail;
1475 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001476 } else
1477#endif
1478 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001479 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001480 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001481 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001482 goto fail;
1483 }
1484
Michal Vasko2cc4c682016-03-01 09:16:48 +01001485 (*session)->data = NULL;
1486
Michal Vasko51e514d2016-02-02 15:51:52 +01001487 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001488 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1489
Michal Vaskob48aa812016-01-18 14:13:09 +01001490 /* assign new SID atomically */
1491 /* LOCK */
1492 pthread_spin_lock(&server_opts.sid_lock);
1493 (*session)->id = server_opts.new_session_id++;
1494 /* UNLOCK */
1495 pthread_spin_unlock(&server_opts.sid_lock);
1496
Michal Vasko9e036d52016-01-08 10:49:26 +01001497 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +01001498 if (nc_handshake(*session)) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001499 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001500 *session = NULL;
1501 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001502 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001503 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001504
Michal Vasko1a38c862016-01-15 15:50:07 +01001505 return 1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001506
1507fail:
Michal Vasko3031aae2016-01-27 16:07:18 +01001508 /* WRITE UNLOCK */
1509 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1510
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001511 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001512 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001513 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001514}
1515
Michal Vasko3031aae2016-01-27 16:07:18 +01001516int
Michal Vasko8f5270d2016-02-29 16:22:25 +01001517nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01001518{
1519 int sock, ret;
1520
Michal Vasko45e53ae2016-04-07 11:46:03 +02001521 if (!host) {
1522 ERRARG("host");
1523 return -1;
1524 } else if (!port) {
1525 ERRARG("port");
1526 return -1;
1527 } else if (!ti) {
1528 ERRARG("ti");
1529 return -1;
1530 } else if (!session) {
1531 ERRARG("session");
Michal Vaskoc61c4492016-01-25 11:13:34 +01001532 return -1;
1533 }
1534
Michal Vaskob05053d2016-01-22 16:12:06 +01001535 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001536 if (sock < 0) {
1537 return -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01001538 }
1539
1540 *session = calloc(1, sizeof **session);
1541 if (!(*session)) {
1542 ERRMEM;
1543 close(sock);
1544 return -1;
1545 }
1546 (*session)->status = NC_STATUS_STARTING;
1547 (*session)->side = NC_SERVER;
1548 (*session)->ctx = server_opts.ctx;
1549 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vaskob05053d2016-01-22 16:12:06 +01001550 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01001551 (*session)->port = port;
1552
1553 /* transport lock */
1554 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1555 if (!(*session)->ti_lock) {
1556 ERRMEM;
1557 close(sock);
1558 ret = -1;
1559 goto fail;
1560 }
1561 pthread_mutex_init((*session)->ti_lock, NULL);
1562
1563 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001564#ifdef NC_ENABLED_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001565 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001566 /* OPTS LOCK */
1567 pthread_mutex_lock(&ssh_ch_opts_lock);
1568
Michal Vasko2cc4c682016-03-01 09:16:48 +01001569 (*session)->data = &ssh_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001570 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001571 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001572
1573 /* OPTS UNLOCK */
1574 pthread_mutex_unlock(&ssh_ch_opts_lock);
1575
Michal Vaskob05053d2016-01-22 16:12:06 +01001576 if (ret < 1) {
1577 goto fail;
1578 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001579 } else
1580#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001581#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001582 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001583 /* OPTS LOCK */
1584 pthread_mutex_lock(&tls_ch_opts_lock);
1585
Michal Vasko2cc4c682016-03-01 09:16:48 +01001586 (*session)->data = &tls_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001587 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001588 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001589
1590 /* OPTS UNLOCK */
1591 pthread_mutex_unlock(&tls_ch_opts_lock);
1592
Michal Vaskob05053d2016-01-22 16:12:06 +01001593 if (ret < 1) {
1594 goto fail;
1595 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001596 } else
1597#endif
1598 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001599 ERRINT;
1600 close(sock);
1601 ret = -1;
1602 goto fail;
1603 }
1604
1605 /* assign new SID atomically */
1606 /* LOCK */
1607 pthread_spin_lock(&server_opts.sid_lock);
1608 (*session)->id = server_opts.new_session_id++;
1609 /* UNLOCK */
1610 pthread_spin_unlock(&server_opts.sid_lock);
1611
1612 /* NETCONF handshake */
1613 if (nc_handshake(*session)) {
1614 ret = -1;
1615 goto fail;
1616 }
1617 (*session)->status = NC_STATUS_RUNNING;
1618
1619 return 1;
1620
1621fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001622 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001623 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001624 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +01001625}
1626
Radek Krejci53691be2016-02-22 13:58:37 +01001627#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */