blob: b12c5fd56eecaec7e5a00ef155081a01588f3965 [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
fanchanghu966f2de2016-07-21 02:28:57 -040036static nc_rpc_clb global_rpc_clb = NULL;
37
Michal Vasko3031aae2016-01-27 16:07:18 +010038extern struct nc_server_ssh_opts ssh_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010039extern pthread_mutex_t ssh_ch_opts_lock;
40
Michal Vasko3031aae2016-01-27 16:07:18 +010041extern struct nc_server_tls_opts tls_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042extern pthread_mutex_t tls_ch_opts_lock;
Michal Vasko3031aae2016-01-27 16:07:18 +010043
44struct nc_endpt *
Michal Vaskoe2713da2016-08-22 16:06:40 +020045nc_server_endpt_lock(const char *name, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010046{
47 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010048 struct nc_endpt *endpt = NULL;
49
50 /* READ LOCK */
51 pthread_rwlock_rdlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010052
53 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +020054 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010055 endpt = &server_opts.endpts[i];
56 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010057 }
58 }
59
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010060 if (!endpt) {
61 ERR("Endpoint \"%s\" was not found.", name);
62 /* READ UNLOCK */
63 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
64 return NULL;
65 }
66
67 /* ENDPT LOCK */
68 pthread_mutex_lock(&endpt->endpt_lock);
69
Michal Vaskoe2713da2016-08-22 16:06:40 +020070 if (idx) {
71 *idx = i;
72 }
73
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010074 return endpt;
75}
76
77void
78nc_server_endpt_unlock(struct nc_endpt *endpt)
79{
80 /* ENDPT UNLOCK */
81 pthread_mutex_unlock(&endpt->endpt_lock);
82
83 /* READ UNLOCK */
Michal Vasko27562ad2016-02-02 15:50:39 +010084 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010085}
Michal Vasko086311b2016-01-08 09:53:11 +010086
Michal Vasko1a38c862016-01-15 15:50:07 +010087API void
88nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
89{
Michal Vasko45e53ae2016-04-07 11:46:03 +020090 if (!session) {
91 ERRARG("session");
92 return;
93 } else if (!reason) {
94 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +010095 return;
96 }
97
98 session->term_reason = reason;
99}
100
Michal Vasko086311b2016-01-08 09:53:11 +0100101int
Michal Vaskof05562c2016-01-20 12:06:43 +0100102nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100103{
104 const int optVal = 1;
105 const socklen_t optLen = sizeof(optVal);
106 int is_ipv4, sock;
107 struct sockaddr_storage saddr;
108
109 struct sockaddr_in *saddr4;
110 struct sockaddr_in6 *saddr6;
111
112
113 if (!strchr(address, ':')) {
114 is_ipv4 = 1;
115 } else {
116 is_ipv4 = 0;
117 }
118
119 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
120 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100121 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100122 goto fail;
123 }
124
125 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100126 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100127 goto fail;
128 }
129
130 bzero(&saddr, sizeof(struct sockaddr_storage));
131 if (is_ipv4) {
132 saddr4 = (struct sockaddr_in *)&saddr;
133
134 saddr4->sin_family = AF_INET;
135 saddr4->sin_port = htons(port);
136
137 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100138 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100139 goto fail;
140 }
141
142 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100143 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100144 goto fail;
145 }
146
147 } else {
148 saddr6 = (struct sockaddr_in6 *)&saddr;
149
150 saddr6->sin6_family = AF_INET6;
151 saddr6->sin6_port = htons(port);
152
153 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100154 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100155 goto fail;
156 }
157
158 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100159 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100160 goto fail;
161 }
162 }
163
Michal Vaskofb89d772016-01-08 12:25:35 +0100164 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100165 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100166 goto fail;
167 }
168
169 return sock;
170
171fail:
172 if (sock > -1) {
173 close(sock);
174 }
175
176 return -1;
177}
178
179int
Michal Vasko3031aae2016-01-27 16:07:18 +0100180nc_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 +0100181{
182 uint16_t i;
183 struct pollfd *pfd;
184 struct sockaddr_storage saddr;
185 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100186 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100187
188 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100189 if (!pfd) {
190 ERRMEM;
191 return -1;
192 }
193
Michal Vasko94acafc2016-09-23 13:40:10 +0200194 i = 0;
195 while (i < bind_count) {
196 if (binds[i].sock < 0) {
197 /* invalid socket */
198 --bind_count;
199 continue;
200 }
Michal Vasko086311b2016-01-08 09:53:11 +0100201 pfd[i].fd = binds[i].sock;
202 pfd[i].events = POLLIN;
203 pfd[i].revents = 0;
Michal Vasko94acafc2016-09-23 13:40:10 +0200204
205 ++i;
Michal Vasko086311b2016-01-08 09:53:11 +0100206 }
207
208 /* poll for a new connection */
209 errno = 0;
210 ret = poll(pfd, bind_count, timeout);
211 if (!ret) {
212 /* we timeouted */
213 free(pfd);
214 return 0;
215 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100217 free(pfd);
218 return -1;
219 }
220
221 for (i = 0; i < bind_count; ++i) {
222 if (pfd[i].revents & POLLIN) {
223 sock = pfd[i].fd;
224 break;
225 }
226 }
227 free(pfd);
228
229 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100230 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100231 return -1;
232 }
233
234 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100235 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100236 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100237 return -1;
238 }
239
Michal Vasko0190bc32016-03-02 15:47:49 +0100240 /* make the socket non-blocking */
241 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
242 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100243 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100244 return -1;
245 }
246
Michal Vasko3031aae2016-01-27 16:07:18 +0100247 if (idx) {
248 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100249 }
250
Michal Vasko086311b2016-01-08 09:53:11 +0100251 /* host was requested */
252 if (host) {
253 if (saddr.ss_family == AF_INET) {
254 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100255 if (*host) {
256 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
257 ERR("inet_ntop failed (%s).", strerror(errno));
258 free(*host);
259 *host = NULL;
260 }
Michal Vasko086311b2016-01-08 09:53:11 +0100261
Michal Vasko4eb3c312016-03-01 14:09:37 +0100262 if (port) {
263 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
264 }
265 } else {
266 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100267 }
268 } else if (saddr.ss_family == AF_INET6) {
269 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100270 if (*host) {
271 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
272 ERR("inet_ntop failed (%s).", strerror(errno));
273 free(*host);
274 *host = NULL;
275 }
Michal Vasko086311b2016-01-08 09:53:11 +0100276
Michal Vasko4eb3c312016-03-01 14:09:37 +0100277 if (port) {
278 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
279 }
280 } else {
281 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100282 }
283 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100284 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100285 }
286 }
287
288 return ret;
289}
290
Michal Vasko05ba9df2016-01-13 14:40:27 +0100291static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100292nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100293{
294 const char *identifier = NULL, *version = NULL, *format = NULL;
295 char *model_data = NULL;
296 const struct lys_module *module;
297 struct nc_server_error *err;
298 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100299 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100300
301 LY_TREE_FOR(rpc->child, child) {
302 if (!strcmp(child->schema->name, "identifier")) {
303 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
304 } else if (!strcmp(child->schema->name, "version")) {
305 version = ((struct lyd_node_leaf_list *)child)->value_str;
306 } else if (!strcmp(child->schema->name, "format")) {
307 format = ((struct lyd_node_leaf_list *)child)->value_str;
308 }
309 }
310
311 /* check version */
312 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100313 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
314 nc_err_set_msg(err, "The requested version is not supported.", "en");
315 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100316 }
317
318 /* check and get module with the name identifier */
319 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
320 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200321 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
322 }
323 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100324 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
325 nc_err_set_msg(err, "The requested schema was not found.", "en");
326 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327 }
328
329 /* check format */
330 if (!format || !strcmp(format, "yang")) {
331 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
332 } else if (!strcmp(format, "yin")) {
333 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
334 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100335 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
336 nc_err_set_msg(err, "The requested format is not supported.", "en");
337 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100338 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200339 if (!model_data) {
340 ERRINT;
341 return NULL;
342 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100343
Michal Vasko303245c2016-03-24 15:20:03 +0100344 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200345 if (!sdata) {
346 ERRINT;
347 free(model_data);
348 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100349 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200350
Radek Krejci539efb62016-08-24 15:05:16 +0200351 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
352 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100353 if (!data) {
354 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200355 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100356 return NULL;
357 }
358
Radek Krejci36dfdb32016-09-01 16:56:35 +0200359 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100360}
361
362static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100363nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100364{
Michal Vasko428087d2016-01-14 16:04:28 +0100365 session->term_reason = NC_SESSION_TERM_CLOSED;
366 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100367}
368
Michal Vasko086311b2016-01-08 09:53:11 +0100369API int
370nc_server_init(struct ly_ctx *ctx)
371{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100372 const struct lys_node *rpc;
373
Michal Vasko086311b2016-01-08 09:53:11 +0100374 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200375 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100376 return -1;
377 }
378
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100379 nc_init();
380
Michal Vasko05ba9df2016-01-13 14:40:27 +0100381 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100382 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100383 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 lys_set_private(rpc, nc_clb_default_get_schema);
385 }
386
387 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100388 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100389 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100390 lys_set_private(rpc, nc_clb_default_close_session);
391 }
392
Michal Vasko086311b2016-01-08 09:53:11 +0100393 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100394
395 server_opts.new_session_id = 1;
396 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
397
Michal Vasko086311b2016-01-08 09:53:11 +0100398 return 0;
399}
400
Michal Vaskob48aa812016-01-18 14:13:09 +0100401API void
402nc_server_destroy(void)
403{
404 pthread_spin_destroy(&server_opts.sid_lock);
405
Radek Krejci53691be2016-02-22 13:58:37 +0100406#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200407 nc_server_del_endpt(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100408#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100409 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100410}
411
Michal Vasko086311b2016-01-08 09:53:11 +0100412API int
413nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
414{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200415 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
416 ERRARG("basic_mode");
417 return -1;
418 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
419 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100420 return -1;
421 }
422
423 server_opts.wd_basic_mode = basic_mode;
424 server_opts.wd_also_supported = also_supported;
425 return 0;
426}
427
Michal Vasko1a38c862016-01-15 15:50:07 +0100428API void
Michal Vasko55f03972016-04-13 08:56:01 +0200429nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
430{
431 if (!basic_mode && !also_supported) {
432 ERRARG("basic_mode and also_supported");
433 return;
434 }
435
436 if (basic_mode) {
437 *basic_mode = server_opts.wd_basic_mode;
438 }
439 if (also_supported) {
440 *also_supported = server_opts.wd_also_supported;
441 }
442}
443
444API void
Michal Vasko086311b2016-01-08 09:53:11 +0100445nc_server_set_capab_interleave(int interleave_support)
446{
447 if (interleave_support) {
448 server_opts.interleave_capab = 1;
449 } else {
450 server_opts.interleave_capab = 0;
451 }
Michal Vasko086311b2016-01-08 09:53:11 +0100452}
453
Michal Vasko55f03972016-04-13 08:56:01 +0200454API int
455nc_server_get_capab_interleave(void)
456{
457 return server_opts.interleave_capab;
458}
459
Michal Vasko1a38c862016-01-15 15:50:07 +0100460API void
Michal Vasko086311b2016-01-08 09:53:11 +0100461nc_server_set_hello_timeout(uint16_t hello_timeout)
462{
Michal Vasko086311b2016-01-08 09:53:11 +0100463 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100464}
465
Michal Vasko55f03972016-04-13 08:56:01 +0200466API uint16_t
467nc_server_get_hello_timeout(void)
468{
469 return server_opts.hello_timeout;
470}
471
Michal Vasko1a38c862016-01-15 15:50:07 +0100472API void
Michal Vasko086311b2016-01-08 09:53:11 +0100473nc_server_set_idle_timeout(uint16_t idle_timeout)
474{
Michal Vasko086311b2016-01-08 09:53:11 +0100475 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100476}
477
Michal Vasko55f03972016-04-13 08:56:01 +0200478API uint16_t
479nc_server_get_idle_timeout(void)
480{
481 return server_opts.idle_timeout;
482}
483
Michal Vasko71090fc2016-05-24 16:37:28 +0200484API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100485nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100486{
Michal Vasko71090fc2016-05-24 16:37:28 +0200487 NC_MSG_TYPE msgtype;
488
Michal Vasko45e53ae2016-04-07 11:46:03 +0200489 if (!server_opts.ctx) {
490 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200491 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200492 } else if (fdin < 0) {
493 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200494 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200495 } else if (fdout < 0) {
496 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200497 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200498 } else if (!username) {
499 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200500 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200501 } else if (!session) {
502 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200503 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100504 }
505
506 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100507 *session = calloc(1, sizeof **session);
508 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100509 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200510 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100511 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100512 (*session)->status = NC_STATUS_STARTING;
513 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100514
515 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100516 (*session)->ti_type = NC_TI_FD;
517 (*session)->ti.fd.in = fdin;
518 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100519
520 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100521 (*session)->flags = NC_SESSION_SHAREDCTX;
522 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100523
Michal Vaskob48aa812016-01-18 14:13:09 +0100524 /* assign new SID atomically */
525 pthread_spin_lock(&server_opts.sid_lock);
526 (*session)->id = server_opts.new_session_id++;
527 pthread_spin_unlock(&server_opts.sid_lock);
528
Michal Vasko086311b2016-01-08 09:53:11 +0100529 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200530 msgtype = nc_handshake(*session);
531 if (msgtype != NC_MSG_HELLO) {
532 nc_session_free(*session, NULL);
533 *session = NULL;
534 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100535 }
Michal Vaskof8352352016-05-24 09:11:36 +0200536 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100537 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100538
Michal Vasko71090fc2016-05-24 16:37:28 +0200539 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100540}
Michal Vasko9e036d52016-01-08 10:49:26 +0100541
Michal Vaskob30b99c2016-07-26 11:35:43 +0200542static void
543nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
544{
545 uint8_t i, found = 0;
546
547 for (i = 0; i < ps->queue_len; ++i) {
548 /* idx round buffer adjust */
549 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
550 i = -ps->queue_begin;
551 }
552
553 if (found) {
554 /* move the value back one place */
555 if (ps->queue[ps->queue_begin + i] == id) {
556 /* another equal value, simply cannot be */
557 ERRINT;
558 }
559
560 if (ps->queue_begin + i == 0) {
561 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
562 } else {
563 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
564 }
565 } else if (ps->queue[ps->queue_begin + i] == id) {
566 /* found our id, there can be no more equal valid values */
567 found = 1;
568 }
569 }
570
571 if (!found) {
572 ERRINT;
573 }
574 --ps->queue_len;
575}
576
Michal Vaskof04a52a2016-04-07 10:52:10 +0200577int
Michal Vasko26043172016-07-26 14:08:59 +0200578nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200579{
580 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200581 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200582 struct timespec ts;
583
Radek Krejci7ac16052016-07-15 11:48:18 +0200584 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200585 ts.tv_sec += NC_READ_TIMEOUT;
586
587 /* LOCK */
588 ret = pthread_mutex_timedlock(&ps->lock, &ts);
589 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200590 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200591 return -1;
592 }
593
594 /* get a unique queue value (by adding 1 to the last added value, if any) */
595 if (ps->queue_len) {
596 queue_last = ps->queue_begin + ps->queue_len - 1;
597 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
598 queue_last -= NC_PS_QUEUE_SIZE;
599 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200600 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200601 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200602 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200603 }
604
605 /* add ourselves into the queue */
606 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200607 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200608 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200609 return -1;
610 }
611 ++ps->queue_len;
612 queue_last = ps->queue_begin + ps->queue_len - 1;
613 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
614 queue_last -= NC_PS_QUEUE_SIZE;
615 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200616 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200617
618 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200619 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200620 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200621 ts.tv_sec += NC_READ_TIMEOUT;
622
623 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
624 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200625 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200626 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200627 nc_ps_queue_remove_id(ps, *id);
628 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200629 return -1;
630 }
631 }
632
Michal Vaskobe86fe32016-04-07 10:43:03 +0200633 /* UNLOCK */
634 pthread_mutex_unlock(&ps->lock);
635
636 return 0;
637}
638
Michal Vaskof04a52a2016-04-07 10:52:10 +0200639int
Michal Vasko26043172016-07-26 14:08:59 +0200640nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200641{
642 int ret;
643 struct timespec ts;
644
Radek Krejci7ac16052016-07-15 11:48:18 +0200645 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200646 ts.tv_sec += NC_READ_TIMEOUT;
647
648 /* LOCK */
649 ret = pthread_mutex_timedlock(&ps->lock, &ts);
650 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200651 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200652 ret = -1;
653 }
654
Michal Vaskob30b99c2016-07-26 11:35:43 +0200655 /* we must be the first, it was our turn after all, right? */
656 if (ps->queue[ps->queue_begin] != id) {
657 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200658 /* UNLOCK */
659 if (!ret) {
660 pthread_mutex_unlock(&ps->lock);
661 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200662 return -1;
663 }
664
Michal Vaskobe86fe32016-04-07 10:43:03 +0200665 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200666 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200667
668 /* broadcast to all other threads that the queue moved */
669 pthread_cond_broadcast(&ps->cond);
670
Michal Vaskobe86fe32016-04-07 10:43:03 +0200671 /* UNLOCK */
672 if (!ret) {
673 pthread_mutex_unlock(&ps->lock);
674 }
675
676 return ret;
677}
678
Michal Vasko428087d2016-01-14 16:04:28 +0100679API struct nc_pollsession *
680nc_ps_new(void)
681{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100682 struct nc_pollsession *ps;
683
684 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100685 if (!ps) {
686 ERRMEM;
687 return NULL;
688 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200689 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100690 pthread_mutex_init(&ps->lock, NULL);
691
692 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100693}
694
695API void
696nc_ps_free(struct nc_pollsession *ps)
697{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100698 if (!ps) {
699 return;
700 }
701
Michal Vaskobe86fe32016-04-07 10:43:03 +0200702 if (ps->queue_len) {
703 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
704 }
705
Michal Vasko3a715132016-01-21 15:40:31 +0100706 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100707 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100708 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200709 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100710
Michal Vasko428087d2016-01-14 16:04:28 +0100711 free(ps);
712}
713
714API int
715nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
716{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200717 uint8_t q_id;
718
Michal Vasko45e53ae2016-04-07 11:46:03 +0200719 if (!ps) {
720 ERRARG("ps");
721 return -1;
722 } else if (!session) {
723 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100724 return -1;
725 }
726
Michal Vasko48a63ed2016-03-01 09:48:21 +0100727 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200728 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200729 return -1;
730 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100731
Michal Vasko428087d2016-01-14 16:04:28 +0100732 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100733 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
734 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
735 if (!ps->pfds || !ps->sessions) {
736 ERRMEM;
737 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200738 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100739 return -1;
740 }
Michal Vasko428087d2016-01-14 16:04:28 +0100741
742 switch (session->ti_type) {
743 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100744 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100745 break;
746
Radek Krejci53691be2016-02-22 13:58:37 +0100747#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100748 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100749 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100750 break;
751#endif
752
Radek Krejci53691be2016-02-22 13:58:37 +0100753#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100754 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100755 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100756 break;
757#endif
758
759 default:
760 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100761 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200762 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100763 return -1;
764 }
Michal Vasko3a715132016-01-21 15:40:31 +0100765 ps->pfds[ps->session_count - 1].events = POLLIN;
766 ps->pfds[ps->session_count - 1].revents = 0;
767 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100768
Michal Vasko48a63ed2016-03-01 09:48:21 +0100769 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200770 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100771}
772
Michal Vasko48a63ed2016-03-01 09:48:21 +0100773static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100774_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100775{
776 uint16_t i;
777
Radek Krejcid5f978f2016-03-03 13:14:45 +0100778 if (index >= 0) {
779 i = (uint16_t)index;
780 goto remove;
781 }
Michal Vasko428087d2016-01-14 16:04:28 +0100782 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100783 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100784remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100785 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100786 if (i < ps->session_count) {
787 ps->sessions[i] = ps->sessions[ps->session_count];
788 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
789 } else if (!ps->session_count) {
790 free(ps->sessions);
791 ps->sessions = NULL;
792 free(ps->pfds);
793 ps->pfds = NULL;
794 }
Michal Vasko428087d2016-01-14 16:04:28 +0100795 return 0;
796 }
797 }
798
Michal Vaskof0537d82016-01-29 14:42:38 +0100799 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100800}
801
Michal Vasko48a63ed2016-03-01 09:48:21 +0100802API int
803nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
804{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200805 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200806 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100807
Michal Vasko45e53ae2016-04-07 11:46:03 +0200808 if (!ps) {
809 ERRARG("ps");
810 return -1;
811 } else if (!session) {
812 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100813 return -1;
814 }
815
816 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200817 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200818 return -1;
819 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100820
Radek Krejcid5f978f2016-03-03 13:14:45 +0100821 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100822
823 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200824 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100825
Michal Vaskobe86fe32016-04-07 10:43:03 +0200826 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100827}
828
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100829API uint16_t
830nc_ps_session_count(struct nc_pollsession *ps)
831{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200832 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100833 uint16_t count;
834
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100835 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200836 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100837 return 0;
838 }
839
Michal Vasko48a63ed2016-03-01 09:48:21 +0100840 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200841 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200842 return -1;
843 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100844
845 count = ps->session_count;
846
847 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200848 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100849
850 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100851}
852
Michal Vasko71090fc2016-05-24 16:37:28 +0200853/* must be called holding the session lock!
854 * returns: NC_PSPOLL_ERROR,
855 * NC_PSPOLL_BAD_RPC,
856 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
857 * NC_PSPOLL_RPC
858 */
859static int
Radek Krejci93e80222016-10-03 13:34:25 +0200860nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100861{
862 struct lyxml_elem *xml = NULL;
863 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200864 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200865 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100866
Michal Vasko45e53ae2016-04-07 11:46:03 +0200867 if (!session) {
868 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200869 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200870 } else if (!rpc) {
871 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200872 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100873 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100874 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200875 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100876 }
877
878 msgtype = nc_read_msg(session, &xml);
879
880 switch (msgtype) {
881 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200882 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100883 if (!*rpc) {
884 ERRMEM;
885 goto error;
886 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100887
Radek Krejcif93c7d42016-04-06 13:41:15 +0200888 ly_errno = LY_SUCCESS;
Michal Vasko68b3f292016-09-16 12:00:32 +0200889 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_RPC | LYD_OPT_DESTRUCT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100890 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200891 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200892 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200893 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200894 nc_server_reply_free(reply);
895 if (ret == -1) {
896 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200897 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200898 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
899 } else {
900 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100901 }
Michal Vasko428087d2016-01-14 16:04:28 +0100902 (*rpc)->root = xml;
903 break;
904 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100905 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200906 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100907 goto error;
908 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100909 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200910 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100911 goto error;
912 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100913 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200914 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100915 goto error;
916 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200917 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100918 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
919 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200920 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100921 break;
922 }
923
Michal Vasko71090fc2016-05-24 16:37:28 +0200924 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100925
926error:
927 /* cleanup */
928 lyxml_free(server_opts.ctx, xml);
929
Michal Vasko71090fc2016-05-24 16:37:28 +0200930 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100931}
932
fanchanghu966f2de2016-07-21 02:28:57 -0400933API void
934nc_set_global_rpc_clb(nc_rpc_clb clb)
935{
936 global_rpc_clb = clb;
937}
938
Radek Krejci93e80222016-10-03 13:34:25 +0200939API NC_MSG_TYPE
940nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
941{
942 NC_MSG_TYPE result = NC_MSG_NOTIF;
943 int ret;
944
945 /* check parameters */
946 if (!session) {
947 ERRARG("session");
948 return NC_MSG_ERROR;
949 } else if (!notif || !notif->tree || !notif->eventtime) {
950 ERRARG("notif");
951 return NC_MSG_ERROR;
952 }
953
954 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
955 ret = nc_timedlock(session->ti_lock, timeout, __func__);
956 if (ret < 0) {
957 return NC_MSG_ERROR;
958 } else if (!ret) {
959 return NC_MSG_WOULDBLOCK;
960 }
961
962 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
963 if (ret == -1) {
964 ERR("Session %u: failed to write notification.", session->id);
965 result = NC_MSG_ERROR;
966 }
967 pthread_mutex_unlock(session->ti_lock);
968
969 return result;
970}
971
Michal Vasko71090fc2016-05-24 16:37:28 +0200972/* must be called holding the session lock!
973 * returns: NC_PSPOLL_ERROR,
974 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
975 * NC_PSPOLL_REPLY_ERROR,
976 * 0
977 */
978static int
Radek Krejci93e80222016-10-03 13:34:25 +0200979nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100980{
981 nc_rpc_clb clb;
982 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +0200983 struct lys_node *rpc_act = NULL;
984 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +0200985 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +0100986
Michal Vasko4a827e52016-03-03 10:59:00 +0100987 if (!rpc) {
988 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200989 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +0100990 }
991
Michal Vasko90e8e692016-07-13 12:27:57 +0200992 if (rpc->tree->schema->nodetype == LYS_RPC) {
993 /* RPC */
994 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -0400995 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +0200996 /* action */
997 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
998 if (elem->schema->nodetype == LYS_ACTION) {
999 rpc_act = elem->schema;
1000 break;
1001 }
1002 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001003 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001004 if (!rpc_act) {
1005 ERRINT;
1006 return NC_PSPOLL_ERROR;
1007 }
1008 }
1009
1010 if (!rpc_act->priv) {
1011 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001012 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001013 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001014 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001015 reply = clb(rpc->tree, session);
1016 }
1017
1018 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001019 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001020 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001021 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1022 if (reply->type == NC_RPL_ERROR) {
1023 ret |= NC_PSPOLL_REPLY_ERROR;
1024 }
1025 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001026
Michal Vasko71090fc2016-05-24 16:37:28 +02001027 if (r == -1) {
1028 ERR("Session %u: failed to write reply.", session->id);
1029 ret |= NC_PSPOLL_ERROR;
1030 }
Michal Vasko428087d2016-01-14 16:04:28 +01001031
1032 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1033 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1034 session->status = NC_STATUS_INVALID;
1035 }
1036
Michal Vasko71090fc2016-05-24 16:37:28 +02001037 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001038}
1039
1040API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001041nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001042{
1043 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001044 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +01001045 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001046 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +02001047 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001048 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +01001049
1050 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001051 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001052 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001053 }
1054
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001055 cur_time = time(NULL);
1056
Michal Vasko48a63ed2016-03-01 09:48:21 +01001057 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001058 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001059 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001060 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001061
Michal Vasko428087d2016-01-14 16:04:28 +01001062 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001063 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1064 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001065 ret = NC_PSPOLL_ERROR;
1066 if (session) {
1067 *session = ps->sessions[i];
1068 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001069 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001070 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001071
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001072 /* TODO invalidate only sessions without subscription */
Michal Vaskobae4ca32016-07-28 15:47:25 +02001073 if (server_opts.idle_timeout && (cur_time >= ps->sessions[i]->last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001074 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1075 ps->sessions[i]->status = NC_STATUS_INVALID;
1076 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001077 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1078 if (session) {
1079 *session = ps->sessions[i];
1080 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001081 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001082 }
1083
Michal Vasko3a715132016-01-21 15:40:31 +01001084 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001085 break;
1086 }
Michal Vasko428087d2016-01-14 16:04:28 +01001087 }
1088
Michal Vaskobd8ef262016-01-20 11:09:27 +01001089 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001090#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001091retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001092#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001093 /* no leftover event */
1094 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001095 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001096 if (ret < 0) {
1097 ERR("Poll failed (%s).", strerror(errno));
1098 ret = NC_PSPOLL_ERROR;
1099 goto finish;
1100 } else if (!ret) {
1101 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001102 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001103 }
Michal Vasko428087d2016-01-14 16:04:28 +01001104 }
1105
Michal Vaskobd8ef262016-01-20 11:09:27 +01001106 /* find the first fd with POLLIN, we don't care if there are more now */
1107 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001108 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001109 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1110 ps->sessions[i]->status = NC_STATUS_INVALID;
1111 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001112 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1113 if (session) {
1114 *session = ps->sessions[i];
1115 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001116 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001117 } else if (ps->pfds[i].revents & POLLERR) {
1118 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1119 ps->sessions[i]->status = NC_STATUS_INVALID;
1120 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001121 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1122 if (session) {
1123 *session = ps->sessions[i];
1124 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001125 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001126 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001127#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001128 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001129 uint16_t j;
1130
Michal Vasko96164bf2016-01-21 15:41:58 +01001131 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001132 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001133
1134 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001135 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001136 for (j = i + 1; j < ps->session_count; ++j) {
1137 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1138 ps->pfds[j].revents = 0;
1139 }
1140 }
1141 }
1142
Michal Vasko71090fc2016-05-24 16:37:28 +02001143 /* SSH message only */
1144 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001145 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001146 if (session) {
1147 *session = ps->sessions[i];
1148 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001149 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001150
1151 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001152 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001153 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001154 if (i == ps->session_count - 1) {
1155 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001156 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001157 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001158 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001159 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001160 }
Michal Vasko8c748832016-02-03 15:32:16 +01001161 /* ... retry polling reasonable time apart ... */
1162 usleep(NC_TIMEOUT_STEP);
1163 if (timeout > 0) {
1164 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001165 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001166 }
1167 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001168 }
1169 /* check other sessions */
1170 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001171 }
1172 }
Radek Krejci53691be2016-02-22 13:58:37 +01001173#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001174
Michal Vaskobd8ef262016-01-20 11:09:27 +01001175 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001176 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001177 break;
1178 }
1179 }
1180
1181 if (i == ps->session_count) {
1182 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001183 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001184 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001185 }
1186
1187 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001188 cur_session = ps->sessions[i];
1189 if (session) {
1190 *session = cur_session;
1191 }
Michal Vasko428087d2016-01-14 16:04:28 +01001192
Michal Vaskobd8ef262016-01-20 11:09:27 +01001193 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal vasko953939c2016-10-04 13:46:20 +02001194 ret = nc_timedlock(cur_session->ti_lock, timeout, __func__);
Michal Vasko71090fc2016-05-24 16:37:28 +02001195 if (ret < 0) {
1196 ret = NC_PSPOLL_ERROR;
1197 goto finish;
1198 } else if (!ret) {
1199 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001200 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001201 }
1202
Radek Krejci93e80222016-10-03 13:34:25 +02001203 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001204 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1205 pthread_mutex_unlock(cur_session->ti_lock);
1206 if (cur_session->status != NC_STATUS_RUNNING) {
1207 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001208 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001209 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001210 }
1211
Michal Vasko71090fc2016-05-24 16:37:28 +02001212 cur_session->last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001213
Michal Vasko428087d2016-01-14 16:04:28 +01001214 /* process RPC */
Radek Krejci93e80222016-10-03 13:34:25 +02001215 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001216
Michal Vasko71090fc2016-05-24 16:37:28 +02001217 pthread_mutex_unlock(cur_session->ti_lock);
1218 if (cur_session->status != NC_STATUS_RUNNING) {
1219 ret |= NC_PSPOLL_SESSION_TERM;
1220 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1221 ret |= NC_PSPOLL_SESSION_ERROR;
1222 }
Michal Vasko428087d2016-01-14 16:04:28 +01001223 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001224
Michal Vaskoca4a2422016-02-02 12:17:14 +01001225 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001226
1227 /* is there some other socket waiting? */
1228 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001229 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001230 ret |= NC_PSPOLL_PENDING;
1231 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001232 }
1233 }
1234
Michal Vasko48a63ed2016-03-01 09:48:21 +01001235finish:
1236 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001237 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001238 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001239}
1240
Michal Vaskod09eae62016-02-01 10:32:52 +01001241API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001242nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001243{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001244 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001245 uint16_t i;
1246 struct nc_session *session;
1247
Michal Vasko9a25e932016-02-01 10:36:42 +01001248 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001249 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001250 return;
1251 }
1252
Michal Vasko48a63ed2016-03-01 09:48:21 +01001253 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001254 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001255 return;
1256 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001257
Michal Vasko48a63ed2016-03-01 09:48:21 +01001258 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001259 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001260 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001261 }
1262 free(ps->sessions);
1263 ps->sessions = NULL;
1264 free(ps->pfds);
1265 ps->pfds = NULL;
1266 ps->session_count = 0;
1267 } else {
1268 for (i = 0; i < ps->session_count; ) {
1269 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1270 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001271 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001272 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001273 continue;
1274 }
1275
1276 ++i;
1277 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001278 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001279
1280 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001281 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001282}
1283
Radek Krejci53691be2016-02-22 13:58:37 +01001284#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001285
Michal Vaskoe2713da2016-08-22 16:06:40 +02001286API int
1287nc_server_add_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001288{
Michal Vasko3031aae2016-01-27 16:07:18 +01001289 uint16_t i;
Radek Krejci53691be2016-02-22 13:58:37 +01001290#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001291 uint16_t bind_ssh_idx;
1292#endif
1293#ifdef NC_ENABLED_TLS
1294 uint16_t bind_tls_idx;
Michal Vasko08a629a2016-02-02 12:20:47 +01001295#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001296
Michal Vasko45e53ae2016-04-07 11:46:03 +02001297 if (!name) {
1298 ERRARG("name");
1299 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001300 }
1301
Michal Vasko51e514d2016-02-02 15:51:52 +01001302 /* WRITE LOCK */
1303 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001304
1305 /* check name uniqueness */
1306 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001307 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001308 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001309 /* WRITE UNLOCK */
Michal Vasko9faf1c82016-02-01 13:26:19 +01001310 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001311 return -1;
1312 }
1313 }
1314
Michal Vasko3031aae2016-01-27 16:07:18 +01001315 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001316 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001317 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001318 ERRMEM;
1319 /* WRITE UNLOCK */
1320 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001321 return -1;
1322 }
1323 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1324
1325#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1326 server_opts.binds = nc_realloc(server_opts.binds, 2 * server_opts.endpt_count * sizeof *server_opts.binds);
1327 bind_ssh_idx = (server_opts.endpt_count - 1) * 2;
1328 bind_tls_idx = (server_opts.endpt_count - 1) * 2 + 1;
1329#else
1330 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
1331# ifdef NC_ENABLED_SSH
1332 bind_ssh_idx = server_opts.endpt_count - 1;
1333# endif
1334# ifdef NC_ENABLED_TLS
1335 bind_tls_idx = server_opts.endpt_count - 1;
1336# endif
1337#endif
1338 if (!server_opts.binds) {
1339 ERRMEM;
1340 /* WRITE UNLOCK */
1341 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001342 return -1;
1343 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001344
Radek Krejci53691be2016-02-22 13:58:37 +01001345#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001346 server_opts.binds[bind_ssh_idx].address = NULL;
1347 server_opts.binds[bind_ssh_idx].port = 0;
1348 server_opts.binds[bind_ssh_idx].sock = -1;
1349 server_opts.binds[bind_ssh_idx].ti = NC_TI_LIBSSH;
Michal Vasko08a629a2016-02-02 12:20:47 +01001350
Michal Vaskoe2713da2016-08-22 16:06:40 +02001351 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts = calloc(1, sizeof(struct nc_server_ssh_opts));
1352 if (!server_opts.endpts[server_opts.endpt_count - 1].ssh_opts) {
1353 ERRMEM;
1354 /* WRITE UNLOCK */
1355 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1356 return -1;
Michal Vasko3031aae2016-01-27 16:07:18 +01001357 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001358 /* set default values */
1359 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_methods =
1360 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1361 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_attempts = 3;
1362 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_timeout = 10;
1363#endif
1364
1365#ifdef NC_ENABLED_TLS
1366 server_opts.binds[bind_tls_idx].address = NULL;
1367 server_opts.binds[bind_tls_idx].port = 0;
1368 server_opts.binds[bind_tls_idx].sock = -1;
1369 server_opts.binds[bind_tls_idx].ti = NC_TI_OPENSSL;
1370
1371 server_opts.endpts[server_opts.endpt_count - 1].tls_opts = calloc(1, sizeof(struct nc_server_tls_opts));
1372 if (!server_opts.endpts[server_opts.endpt_count - 1].tls_opts) {
1373 ERRMEM;
1374 /* WRITE UNLOCK */
1375 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1376 return -1;
1377 }
1378#endif
1379
Michal Vasko3031aae2016-01-27 16:07:18 +01001380 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001381
Michal Vasko3031aae2016-01-27 16:07:18 +01001382 /* WRITE UNLOCK */
1383 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001384
Michal Vasko9e036d52016-01-08 10:49:26 +01001385 return 0;
1386}
1387
Michal Vasko3031aae2016-01-27 16:07:18 +01001388int
Michal Vaskoda514772016-02-01 11:32:01 +01001389nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1390{
1391 struct nc_endpt *endpt;
1392 struct nc_bind *bind = NULL;
1393 uint16_t i;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001394 int sock = -1, set_addr;
Michal Vaskoda514772016-02-01 11:32:01 +01001395
Michal Vasko45e53ae2016-04-07 11:46:03 +02001396 if (!endpt_name) {
1397 ERRARG("endpt_name");
1398 return -1;
1399 } else if ((!address && !port) || (address && port)) {
1400 ERRARG("address and port");
1401 return -1;
1402 } else if (!ti) {
1403 ERRARG("ti");
Michal Vaskoda514772016-02-01 11:32:01 +01001404 return -1;
1405 }
1406
Michal Vaskoe2713da2016-08-22 16:06:40 +02001407 if (address) {
1408 set_addr = 1;
1409 } else {
1410 set_addr = 0;
1411 }
1412
Michal Vasko51e514d2016-02-02 15:51:52 +01001413 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001414 endpt = nc_server_endpt_lock(endpt_name, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001415 if (!endpt) {
1416 return -1;
1417 }
1418
Michal Vaskoe2713da2016-08-22 16:06:40 +02001419#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1420 if (ti == NC_TI_LIBSSH) {
1421 bind = &server_opts.binds[2 * i];
1422 } else {
1423 bind = &server_opts.binds[2 * i + 1];
Michal Vaskoda514772016-02-01 11:32:01 +01001424 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001425#else
1426 bind = &server_opts.binds[i];
Michal Vasko5e391372016-08-22 16:20:58 +02001427 if (bind->ti != ti) {
Michal Vaskoda514772016-02-01 11:32:01 +01001428 ERRINT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001429 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001430 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001431#endif
Michal Vaskoda514772016-02-01 11:32:01 +01001432
Michal Vaskoe2713da2016-08-22 16:06:40 +02001433 if (set_addr) {
1434 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001435 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001436 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001437 }
1438
Michal Vaskoe2713da2016-08-22 16:06:40 +02001439 /* we have all the information we need to create a listening socket */
1440 if (address && port) {
1441 /* create new socket, close the old one */
1442 sock = nc_sock_listen(address, port);
1443 if (sock == -1) {
1444 goto fail;
1445 }
1446
1447 if (bind->sock > -1) {
1448 close(bind->sock);
1449 }
1450 bind->sock = sock;
1451 } /* else we are just setting address or port */
1452
1453 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001454 lydict_remove(server_opts.ctx, bind->address);
1455 bind->address = lydict_insert(server_opts.ctx, address, 0);
1456 } else {
1457 bind->port = port;
1458 }
1459
Michal Vaskoe2713da2016-08-22 16:06:40 +02001460 if (sock > -1) {
1461#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1462 VRB("Listening on %s:%u for %s connections.", address, port, (ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
1463#elif defined(NC_ENABLED_SSH)
1464 VRB("Listening on %s:%u for SSH connections.", address, port);
1465#else
1466 VRB("Listening on %s:%u for TLS connections.", address, port);
1467#endif
1468 }
1469
Michal Vasko51e514d2016-02-02 15:51:52 +01001470 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001471 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001472 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001473
1474fail:
1475 /* UNLOCK */
1476 nc_server_endpt_unlock(endpt);
1477 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001478}
1479
Michal Vaskoe2713da2016-08-22 16:06:40 +02001480API int
1481nc_server_del_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001482{
1483 uint32_t i;
1484 int ret = -1;
1485
Michal Vasko3031aae2016-01-27 16:07:18 +01001486 /* WRITE LOCK */
1487 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001488
Michal Vaskoe2713da2016-08-22 16:06:40 +02001489 if (!name) {
1490 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001491 for (i = 0; i < server_opts.endpt_count; ++i) {
1492 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko3031aae2016-01-27 16:07:18 +01001493 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
Radek Krejci53691be2016-02-22 13:58:37 +01001494#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001495 nc_server_ssh_clear_opts(server_opts.endpts[i].ssh_opts);
1496 free(server_opts.endpts[i].ssh_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001497#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001498#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001499 nc_server_tls_clear_opts(server_opts.endpts[i].tls_opts);
1500 free(server_opts.endpts[i].tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001501#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001502 ret = 0;
1503 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001504 free(server_opts.endpts);
1505 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001506
1507 /* remove all binds */
1508 for (i = 0; i < server_opts.endpt_count; ++i) {
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 }
1514#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1515 for (; i < 2 * server_opts.endpt_count; ++i) {
1516 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1517 if (server_opts.binds[i].sock > -1) {
1518 close(server_opts.binds[i].sock);
1519 }
1520 }
1521#endif
1522 free(server_opts.binds);
1523 server_opts.binds = NULL;
1524
Michal Vasko3031aae2016-01-27 16:07:18 +01001525 server_opts.endpt_count = 0;
1526
Michal Vasko1a38c862016-01-15 15:50:07 +01001527 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001528 /* remove one endpoint with bind(s) */
Michal Vasko3031aae2016-01-27 16:07:18 +01001529 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001530 if (!strcmp(server_opts.endpts[i].name, name)) {
1531 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001532 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko3031aae2016-01-27 16:07:18 +01001533 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
Radek Krejci53691be2016-02-22 13:58:37 +01001534#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001535 nc_server_ssh_clear_opts(server_opts.endpts[i].ssh_opts);
1536 free(server_opts.endpts[i].ssh_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001537#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001538#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001539 nc_server_tls_clear_opts(server_opts.endpts[i].tls_opts);
1540 free(server_opts.endpts[i].tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001541#endif
Michal Vasko1a38c862016-01-15 15:50:07 +01001542
Michal Vaskoe2713da2016-08-22 16:06:40 +02001543 /* remove bind(s) */
1544#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1545 i *= 2;
1546 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1547 if (server_opts.binds[i].sock > -1) {
1548 close(server_opts.binds[i].sock);
1549 }
1550 ++i;
1551#endif
1552 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1553 if (server_opts.binds[i].sock > -1) {
1554 close(server_opts.binds[i].sock);
1555 }
1556
1557 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001558 --server_opts.endpt_count;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001559#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1560 --i;
1561 i /= 2;
1562 if (i < server_opts.endpt_count) {
1563 memcpy(&server_opts.binds[2 * i], &server_opts.binds[2 * server_opts.endpt_count], 2 * sizeof *server_opts.binds);
1564 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1565 }
1566#else
Michal Vaskoc0256492016-02-02 12:19:06 +01001567 if (i < server_opts.endpt_count) {
1568 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1569 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001570 }
1571#endif
1572 else if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001573 free(server_opts.binds);
1574 server_opts.binds = NULL;
1575 free(server_opts.endpts);
1576 server_opts.endpts = NULL;
1577 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001578
1579 ret = 0;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001580 break;
Michal Vasko1a38c862016-01-15 15:50:07 +01001581 }
1582 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001583 }
1584
Michal Vasko3031aae2016-01-27 16:07:18 +01001585 /* WRITE UNLOCK */
1586 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001587
Michal Vasko9e036d52016-01-08 10:49:26 +01001588 return ret;
1589}
1590
Michal Vasko71090fc2016-05-24 16:37:28 +02001591API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001592nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001593{
Michal Vasko71090fc2016-05-24 16:37:28 +02001594 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001595 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001596 char *host = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001597 uint16_t port, endpt_idx, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001598
Michal Vasko45e53ae2016-04-07 11:46:03 +02001599 if (!server_opts.ctx) {
1600 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001601 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001602 } else if (!session) {
1603 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001604 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001605 }
1606
Michal Vasko51e514d2016-02-02 15:51:52 +01001607 /* we have to hold WRITE for the whole time, since there is not
1608 * a way of downgrading the lock to READ */
1609 /* WRITE LOCK */
1610 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
1611
1612 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001613 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001614 /* WRITE UNLOCK */
1615 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001616 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001617 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001618
Michal Vasko94acafc2016-09-23 13:40:10 +02001619#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1620 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count * 2, timeout, &host, &port, &bind_idx);
1621#else
Michal Vaskoe2713da2016-08-22 16:06:40 +02001622 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko94acafc2016-09-23 13:40:10 +02001623#endif
Michal Vaskob48aa812016-01-18 14:13:09 +01001624
Michal Vasko50456e82016-02-02 12:16:08 +01001625 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001626 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001627 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001628 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001629 if (!ret) {
1630 return NC_MSG_WOULDBLOCK;
1631 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001632 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001633 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001634 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001635
Michal Vasko1a38c862016-01-15 15:50:07 +01001636 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001637 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001638 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001639 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001640 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001641 msgtype = NC_MSG_ERROR;
1642 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001643 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001644 (*session)->status = NC_STATUS_STARTING;
1645 (*session)->side = NC_SERVER;
1646 (*session)->ctx = server_opts.ctx;
1647 (*session)->flags = NC_SESSION_SHAREDCTX;
1648 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1649 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001650
1651 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001652 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1653 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001654 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001655 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001656 msgtype = NC_MSG_ERROR;
1657 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001658 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001659 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001660
Michal Vaskoe2713da2016-08-22 16:06:40 +02001661 endpt_idx = bind_idx;
1662 /* transform index as needed */
1663#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1664 if (server_opts.binds[bind_idx].ti == NC_TI_OPENSSL) {
1665 --endpt_idx;
1666 }
1667 endpt_idx /= 2;
1668#endif
Michal Vasko3031aae2016-01-27 16:07:18 +01001669
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001670 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001671#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001672 if (server_opts.binds[bind_idx].ti == NC_TI_LIBSSH) {
1673 (*session)->data = server_opts.endpts[endpt_idx].ssh_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001674 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001675 if (ret < 0) {
1676 msgtype = NC_MSG_ERROR;
1677 goto cleanup;
1678 } else if (!ret) {
1679 msgtype = NC_MSG_WOULDBLOCK;
1680 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001681 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001682 } else
1683#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001684#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001685 if (server_opts.binds[bind_idx].ti == NC_TI_OPENSSL) {
1686 (*session)->data = server_opts.endpts[endpt_idx].tls_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001687 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001688 if (ret < 0) {
1689 msgtype = NC_MSG_ERROR;
1690 goto cleanup;
1691 } else if (!ret) {
1692 msgtype = NC_MSG_WOULDBLOCK;
1693 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001694 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001695 } else
1696#endif
1697 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001698 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001699 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001700 msgtype = NC_MSG_ERROR;
1701 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001702 }
1703
Michal Vasko2cc4c682016-03-01 09:16:48 +01001704 (*session)->data = NULL;
1705
Michal Vasko51e514d2016-02-02 15:51:52 +01001706 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001707 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1708
Michal Vaskob48aa812016-01-18 14:13:09 +01001709 /* assign new SID atomically */
1710 /* LOCK */
1711 pthread_spin_lock(&server_opts.sid_lock);
1712 (*session)->id = server_opts.new_session_id++;
1713 /* UNLOCK */
1714 pthread_spin_unlock(&server_opts.sid_lock);
1715
Michal Vasko9e036d52016-01-08 10:49:26 +01001716 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001717 msgtype = nc_handshake(*session);
1718 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001719 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001720 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001721 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001722 }
Michal Vaskof802fdc2016-07-28 15:47:00 +02001723 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001724 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001725
Michal Vasko71090fc2016-05-24 16:37:28 +02001726 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001727
Michal Vasko71090fc2016-05-24 16:37:28 +02001728cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001729 /* WRITE UNLOCK */
1730 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1731
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001732 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001733 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001734 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001735}
1736
Michal Vasko71090fc2016-05-24 16:37:28 +02001737NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001738nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01001739{
Michal Vasko71090fc2016-05-24 16:37:28 +02001740 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001741 int sock, ret;
1742
Michal Vasko45e53ae2016-04-07 11:46:03 +02001743 if (!host) {
1744 ERRARG("host");
Michal Vasko71090fc2016-05-24 16:37:28 +02001745 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001746 } else if (!port) {
1747 ERRARG("port");
Michal Vasko71090fc2016-05-24 16:37:28 +02001748 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001749 } else if (!ti) {
1750 ERRARG("ti");
Michal Vasko71090fc2016-05-24 16:37:28 +02001751 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001752 } else if (!session) {
1753 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001754 return NC_MSG_ERROR;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001755 }
1756
Michal Vaskob05053d2016-01-22 16:12:06 +01001757 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001758 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001759 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001760 }
1761
1762 *session = calloc(1, sizeof **session);
1763 if (!(*session)) {
1764 ERRMEM;
1765 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001766 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001767 }
1768 (*session)->status = NC_STATUS_STARTING;
1769 (*session)->side = NC_SERVER;
1770 (*session)->ctx = server_opts.ctx;
1771 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vaskob05053d2016-01-22 16:12:06 +01001772 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01001773 (*session)->port = port;
1774
1775 /* transport lock */
1776 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1777 if (!(*session)->ti_lock) {
1778 ERRMEM;
1779 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001780 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001781 goto fail;
1782 }
1783 pthread_mutex_init((*session)->ti_lock, NULL);
1784
1785 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001786#ifdef NC_ENABLED_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001787 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001788 /* OPTS LOCK */
1789 pthread_mutex_lock(&ssh_ch_opts_lock);
1790
Michal Vasko2cc4c682016-03-01 09:16:48 +01001791 (*session)->data = &ssh_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001792 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001793 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001794
1795 /* OPTS UNLOCK */
1796 pthread_mutex_unlock(&ssh_ch_opts_lock);
1797
Michal Vasko71090fc2016-05-24 16:37:28 +02001798 if (ret < 0) {
1799 msgtype = NC_MSG_ERROR;
1800 goto fail;
1801 } else if (!ret) {
1802 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001803 goto fail;
1804 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001805 } else
1806#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001807#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001808 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001809 /* OPTS LOCK */
1810 pthread_mutex_lock(&tls_ch_opts_lock);
1811
Michal Vasko2cc4c682016-03-01 09:16:48 +01001812 (*session)->data = &tls_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001813 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001814 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001815
1816 /* OPTS UNLOCK */
1817 pthread_mutex_unlock(&tls_ch_opts_lock);
1818
Michal Vasko71090fc2016-05-24 16:37:28 +02001819 if (ret < 0) {
1820 msgtype = NC_MSG_ERROR;
1821 goto fail;
1822 } else if (!ret) {
1823 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001824 goto fail;
1825 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001826 } else
1827#endif
1828 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001829 ERRINT;
1830 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001831 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001832 goto fail;
1833 }
1834
1835 /* assign new SID atomically */
1836 /* LOCK */
1837 pthread_spin_lock(&server_opts.sid_lock);
1838 (*session)->id = server_opts.new_session_id++;
1839 /* UNLOCK */
1840 pthread_spin_unlock(&server_opts.sid_lock);
1841
1842 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001843 msgtype = nc_handshake(*session);
1844 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01001845 goto fail;
1846 }
Michal Vaskof802fdc2016-07-28 15:47:00 +02001847 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001848 (*session)->status = NC_STATUS_RUNNING;
1849
Michal Vasko71090fc2016-05-24 16:37:28 +02001850 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001851
1852fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001853 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001854 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001855 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001856}
1857
Radek Krejci53691be2016-02-22 13:58:37 +01001858#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02001859
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001860API time_t
1861nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02001862{
1863 if (!session) {
1864 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001865 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02001866 }
1867
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001868 return session->session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02001869}