blob: 4a5ce44cce9df27c43c70e23b4a87036d7044c61 [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 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
23#include <stdint.h>
24#include <stdlib.h>
25#include <errno.h>
26#include <string.h>
27#include <poll.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <arpa/inet.h>
32#include <unistd.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010033#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010034#include <time.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035
Michal Vasko1a38c862016-01-15 15:50:07 +010036#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010037#include "session_server.h"
38
Michal Vaskob48aa812016-01-18 14:13:09 +010039struct nc_server_opts server_opts = {
Michal Vasko11d142a2016-01-19 15:58:24 +010040 .ctx_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vaskob48aa812016-01-18 14:13:09 +010041 .bind_lock = PTHREAD_MUTEX_INITIALIZER
42};
Michal Vasko086311b2016-01-08 09:53:11 +010043
Michal Vasko1a38c862016-01-15 15:50:07 +010044API void
45nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
46{
47 if (!session || !reason) {
48 ERRARG;
49 return;
50 }
51
52 session->term_reason = reason;
53}
54
Michal Vasko086311b2016-01-08 09:53:11 +010055int
Michal Vaskof05562c2016-01-20 12:06:43 +010056nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +010057{
58 const int optVal = 1;
59 const socklen_t optLen = sizeof(optVal);
60 int is_ipv4, sock;
61 struct sockaddr_storage saddr;
62
63 struct sockaddr_in *saddr4;
64 struct sockaddr_in6 *saddr6;
65
66
67 if (!strchr(address, ':')) {
68 is_ipv4 = 1;
69 } else {
70 is_ipv4 = 0;
71 }
72
73 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
74 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +010075 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +010076 goto fail;
77 }
78
79 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +010080 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +010081 goto fail;
82 }
83
84 bzero(&saddr, sizeof(struct sockaddr_storage));
85 if (is_ipv4) {
86 saddr4 = (struct sockaddr_in *)&saddr;
87
88 saddr4->sin_family = AF_INET;
89 saddr4->sin_port = htons(port);
90
91 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +010092 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +010093 goto fail;
94 }
95
96 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +010097 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +010098 goto fail;
99 }
100
101 } else {
102 saddr6 = (struct sockaddr_in6 *)&saddr;
103
104 saddr6->sin6_family = AF_INET6;
105 saddr6->sin6_port = htons(port);
106
107 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100108 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100109 goto fail;
110 }
111
112 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100113 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100114 goto fail;
115 }
116 }
117
Michal Vaskofb89d772016-01-08 12:25:35 +0100118 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100119 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100120 goto fail;
121 }
122
123 return sock;
124
125fail:
126 if (sock > -1) {
127 close(sock);
128 }
129
130 return -1;
131}
132
133int
Michal Vaskof05562c2016-01-20 12:06:43 +0100134nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, NC_TRANSPORT_IMPL *ti, char **host, uint16_t *port)
Michal Vasko086311b2016-01-08 09:53:11 +0100135{
136 uint16_t i;
137 struct pollfd *pfd;
138 struct sockaddr_storage saddr;
139 socklen_t saddr_len = sizeof(saddr);
140 int ret, sock = -1;
141
142 pfd = malloc(bind_count * sizeof *pfd);
143 for (i = 0; i < bind_count; ++i) {
144 pfd[i].fd = binds[i].sock;
145 pfd[i].events = POLLIN;
146 pfd[i].revents = 0;
147 }
148
149 /* poll for a new connection */
150 errno = 0;
151 ret = poll(pfd, bind_count, timeout);
152 if (!ret) {
153 /* we timeouted */
154 free(pfd);
155 return 0;
156 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100157 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100158 free(pfd);
159 return -1;
160 }
161
162 for (i = 0; i < bind_count; ++i) {
163 if (pfd[i].revents & POLLIN) {
164 sock = pfd[i].fd;
165 break;
166 }
167 }
168 free(pfd);
169
170 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100171 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100172 return -1;
173 }
174
175 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
176 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100177 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100178 return -1;
179 }
180
Michal Vasko9e036d52016-01-08 10:49:26 +0100181 if (ti) {
182 *ti = binds[i].ti;
183 }
184
Michal Vasko086311b2016-01-08 09:53:11 +0100185 /* host was requested */
186 if (host) {
187 if (saddr.ss_family == AF_INET) {
188 *host = malloc(15);
189 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100190 ERR("inet_ntop failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100191 free(*host);
192 *host = NULL;
193 }
194
195 if (port) {
196 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
197 }
198 } else if (saddr.ss_family == AF_INET6) {
199 *host = malloc(40);
200 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100201 ERR("inet_ntop failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100202 free(*host);
203 *host = NULL;
204 }
205
206 if (port) {
207 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
208 }
209 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100210 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100211 }
212 }
213
214 return ret;
215}
216
Michal Vasko05ba9df2016-01-13 14:40:27 +0100217static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100218nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100219{
220 const char *identifier = NULL, *version = NULL, *format = NULL;
221 char *model_data = NULL;
222 const struct lys_module *module;
223 struct nc_server_error *err;
224 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100225 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100226
227 LY_TREE_FOR(rpc->child, child) {
228 if (!strcmp(child->schema->name, "identifier")) {
229 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
230 } else if (!strcmp(child->schema->name, "version")) {
231 version = ((struct lyd_node_leaf_list *)child)->value_str;
232 } else if (!strcmp(child->schema->name, "format")) {
233 format = ((struct lyd_node_leaf_list *)child)->value_str;
234 }
235 }
236
237 /* check version */
238 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100239 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
240 nc_err_set_msg(err, "The requested version is not supported.", "en");
241 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100242 }
243
244 /* check and get module with the name identifier */
245 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
246 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100247 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
248 nc_err_set_msg(err, "The requested schema was not found.", "en");
249 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100250 }
251
252 /* check format */
253 if (!format || !strcmp(format, "yang")) {
254 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
255 } else if (!strcmp(format, "yin")) {
256 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
257 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100258 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
259 nc_err_set_msg(err, "The requested format is not supported.", "en");
260 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100261 }
262
Michal Vasko0473c4c2016-01-19 10:40:06 +0100263 module = ly_ctx_get_module(server_opts.ctx, "ietf-netconf-monitoring", NULL);
264 if (module) {
265 sdata = lys_get_node(module, "/get-schema/output/data");
266 }
267 if (model_data && sdata) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100268 nc_ctx_lock(-1, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100269 data = lyd_output_new_anyxml(sdata, model_data);
Michal Vasko11d142a2016-01-19 15:58:24 +0100270 nc_ctx_unlock();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100271 }
272 free(model_data);
273 if (!data) {
274 ERRINT;
275 return NULL;
276 }
277
278 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
279}
280
281static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100282nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100283{
Michal Vasko428087d2016-01-14 16:04:28 +0100284 session->term_reason = NC_SESSION_TERM_CLOSED;
285 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100286}
287
Michal Vasko086311b2016-01-08 09:53:11 +0100288API int
289nc_server_init(struct ly_ctx *ctx)
290{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100291 const struct lys_node *rpc;
Michal Vasko0473c4c2016-01-19 10:40:06 +0100292 const struct lys_module *mod;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100293
Michal Vasko086311b2016-01-08 09:53:11 +0100294 if (!ctx) {
295 ERRARG;
296 return -1;
297 }
298
Michal Vasko05ba9df2016-01-13 14:40:27 +0100299 /* set default <get-schema> callback if not specified */
Michal Vasko0473c4c2016-01-19 10:40:06 +0100300 rpc = NULL;
301 mod = ly_ctx_get_module(ctx, "ietf-netconf-monitoring", NULL);
302 if (mod) {
303 rpc = lys_get_node(mod, "/get-schema");
304 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100305 if (rpc && !rpc->private) {
306 lys_set_private(rpc, nc_clb_default_get_schema);
307 }
308
309 /* set default <close-session> callback if not specififed */
Michal Vasko0473c4c2016-01-19 10:40:06 +0100310 rpc = NULL;
311 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
312 if (mod) {
313 rpc = lys_get_node(mod, "/close-session");
314 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100315 if (rpc && !rpc->private) {
316 lys_set_private(rpc, nc_clb_default_close_session);
317 }
318
Michal Vasko086311b2016-01-08 09:53:11 +0100319 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100320
321 server_opts.new_session_id = 1;
322 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
323
Michal Vasko086311b2016-01-08 09:53:11 +0100324 return 0;
325}
326
Michal Vaskob48aa812016-01-18 14:13:09 +0100327API void
328nc_server_destroy(void)
329{
330 pthread_spin_destroy(&server_opts.sid_lock);
331
332#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
333 nc_server_del_bind(NULL, 0, 0);
334#endif
335}
336
Michal Vasko086311b2016-01-08 09:53:11 +0100337API int
338nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
339{
340 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)
341 || (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT)))) {
342 ERRARG;
343 return -1;
344 }
345
346 server_opts.wd_basic_mode = basic_mode;
347 server_opts.wd_also_supported = also_supported;
348 return 0;
349}
350
Michal Vasko1a38c862016-01-15 15:50:07 +0100351API void
Michal Vasko086311b2016-01-08 09:53:11 +0100352nc_server_set_capab_interleave(int interleave_support)
353{
354 if (interleave_support) {
355 server_opts.interleave_capab = 1;
356 } else {
357 server_opts.interleave_capab = 0;
358 }
Michal Vasko086311b2016-01-08 09:53:11 +0100359}
360
Michal Vasko1a38c862016-01-15 15:50:07 +0100361API void
Michal Vasko086311b2016-01-08 09:53:11 +0100362nc_server_set_hello_timeout(uint16_t hello_timeout)
363{
Michal Vasko086311b2016-01-08 09:53:11 +0100364 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100365}
366
Michal Vasko1a38c862016-01-15 15:50:07 +0100367API void
Michal Vasko086311b2016-01-08 09:53:11 +0100368nc_server_set_idle_timeout(uint16_t idle_timeout)
369{
Michal Vasko086311b2016-01-08 09:53:11 +0100370 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100371}
372
373API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100374nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100375{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100376 if (!server_opts.ctx || (fdin < 0) || (fdout < 0) || !username || !session) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100377 ERRARG;
378 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100379 }
380
381 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100382 *session = calloc(1, sizeof **session);
383 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100384 ERRMEM;
Michal Vasko1a38c862016-01-15 15:50:07 +0100385 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100386 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100387 (*session)->status = NC_STATUS_STARTING;
388 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100389
390 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100391 (*session)->ti_type = NC_TI_FD;
392 (*session)->ti.fd.in = fdin;
393 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100394
395 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100396 (*session)->flags = NC_SESSION_SHAREDCTX;
397 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100398
Michal Vaskob48aa812016-01-18 14:13:09 +0100399 /* assign new SID atomically */
400 pthread_spin_lock(&server_opts.sid_lock);
401 (*session)->id = server_opts.new_session_id++;
402 pthread_spin_unlock(&server_opts.sid_lock);
403
Michal Vasko086311b2016-01-08 09:53:11 +0100404 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100405 if (nc_handshake(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100406 goto fail;
407 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100408 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100409 (*session)->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100410
Michal Vasko1a38c862016-01-15 15:50:07 +0100411 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100412
413fail:
Michal Vasko1a38c862016-01-15 15:50:07 +0100414 nc_session_free(*session);
415 *session = NULL;
416 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100417}
Michal Vasko9e036d52016-01-08 10:49:26 +0100418
Michal Vasko428087d2016-01-14 16:04:28 +0100419API struct nc_pollsession *
420nc_ps_new(void)
421{
422 return calloc(1, sizeof(struct nc_pollsession));
423}
424
425API void
426nc_ps_free(struct nc_pollsession *ps)
427{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100428 if (!ps) {
429 return;
430 }
431
Michal Vasko3a715132016-01-21 15:40:31 +0100432 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100433 free(ps->sessions);
434 free(ps);
435}
436
437API int
438nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
439{
440 if (!ps || !session) {
441 ERRARG;
442 return -1;
443 }
444
445 ++ps->session_count;
Michal Vasko3a715132016-01-21 15:40:31 +0100446 ps->pfds = realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100447 ps->sessions = realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
448
449 switch (session->ti_type) {
450 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100451 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100452 break;
453
454#ifdef ENABLE_SSH
455 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100456 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100457 break;
458#endif
459
460#ifdef ENABLE_TLS
461 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100462 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100463 break;
464#endif
465
466 default:
467 ERRINT;
468 return -1;
469 }
Michal Vasko3a715132016-01-21 15:40:31 +0100470 ps->pfds[ps->session_count - 1].events = POLLIN;
471 ps->pfds[ps->session_count - 1].revents = 0;
472 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100473
474 return 0;
475}
476
477API int
478nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
479{
480 uint16_t i;
481
482 if (!ps || !session) {
483 ERRARG;
484 return -1;
485 }
486
487 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100488 if (ps->sessions[i] == session) {
Michal Vasko428087d2016-01-14 16:04:28 +0100489 --ps->session_count;
Michal Vasko3a715132016-01-21 15:40:31 +0100490 ps->sessions[i] = ps->sessions[ps->session_count];
491 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100492 return 0;
493 }
494 }
495
496 return 1;
497}
498
499/* must be called holding the session lock! */
500static NC_MSG_TYPE
501nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
502{
503 struct lyxml_elem *xml = NULL;
504 NC_MSG_TYPE msgtype;
505
506 if (!session || !rpc) {
507 ERRARG;
508 return NC_MSG_ERROR;
509 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100510 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100511 return NC_MSG_ERROR;
512 }
513
514 msgtype = nc_read_msg(session, &xml);
515
516 switch (msgtype) {
517 case NC_MSG_RPC:
518 *rpc = malloc(sizeof **rpc);
Michal Vasko11d142a2016-01-19 15:58:24 +0100519 nc_ctx_lock(-1, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100520 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
Michal Vasko11d142a2016-01-19 15:58:24 +0100521 nc_ctx_unlock();
Michal Vasko428087d2016-01-14 16:04:28 +0100522 (*rpc)->root = xml;
523 break;
524 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100525 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100526 goto error;
527 case NC_MSG_REPLY:
Michal Vaskod083db62016-01-19 10:31:29 +0100528 ERR("Session %u: received <rpc-reply> from NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100529 goto error;
530 case NC_MSG_NOTIF:
Michal Vaskod083db62016-01-19 10:31:29 +0100531 ERR("Session %u: received <notification> from NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100532 goto error;
533 default:
534 /* NC_MSG_ERROR - pass it out;
535 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
536 */
537 break;
538 }
539
540 return msgtype;
541
542error:
543 /* cleanup */
544 lyxml_free(server_opts.ctx, xml);
545
546 return NC_MSG_ERROR;
547}
548
549/* must be called holding the session lock! */
550static NC_MSG_TYPE
551nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
552{
553 nc_rpc_clb clb;
554 struct nc_server_reply *reply;
555 int ret;
556
557 /* no callback, reply with a not-implemented error */
558 if (!rpc->tree->schema->private) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100559 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100560 } else {
561 clb = (nc_rpc_clb)rpc->tree->schema->private;
562 reply = clb(rpc->tree, session);
563 }
564
565 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100566 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100567 }
568
569 ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
570
571 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
572 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
573 session->status = NC_STATUS_INVALID;
574 }
575
576 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100577 ERR("Session %u: failed to write reply.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100578 nc_server_reply_free(reply);
579 return NC_MSG_ERROR;
580 }
581 nc_server_reply_free(reply);
582
583 return NC_MSG_REPLY;
584}
585
586API int
587nc_ps_poll(struct nc_pollsession *ps, int timeout)
588{
589 int ret;
590 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100591 time_t cur_time;
Michal Vasko428087d2016-01-14 16:04:28 +0100592 NC_MSG_TYPE msgtype;
593 struct nc_session *session;
594 struct nc_server_rpc *rpc;
595 struct timespec old_ts, new_ts;
596
597 if (!ps || !ps->session_count) {
598 ERRARG;
599 return -1;
600 }
601
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100602 cur_time = time(NULL);
603
Michal Vasko428087d2016-01-14 16:04:28 +0100604 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100605 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
606 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100607 return -1;
608 }
Michal Vaskobd8ef262016-01-20 11:09:27 +0100609
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100610 /* TODO invalidate only sessions without subscription */
Michal Vasko3a715132016-01-21 15:40:31 +0100611 if (server_opts.idle_timeout && (ps->sessions[i]->last_rpc + server_opts.idle_timeout >= cur_time)) {
612 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
613 ps->sessions[i]->status = NC_STATUS_INVALID;
614 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100615 return 3;
616 }
617
Michal Vasko3a715132016-01-21 15:40:31 +0100618 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100619 break;
620 }
Michal Vasko428087d2016-01-14 16:04:28 +0100621 }
622
623 if (timeout > 0) {
624 clock_gettime(CLOCK_MONOTONIC_RAW, &old_ts);
625 }
626
Michal Vaskobd8ef262016-01-20 11:09:27 +0100627 if (i == ps->session_count) {
Michal Vasko3a715132016-01-21 15:40:31 +0100628retry_poll:
Michal Vaskobd8ef262016-01-20 11:09:27 +0100629 /* no leftover event */
630 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +0100631 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100632 if (ret < 1) {
633 return ret;
634 }
Michal Vasko428087d2016-01-14 16:04:28 +0100635 }
636
Michal Vaskobd8ef262016-01-20 11:09:27 +0100637 /* find the first fd with POLLIN, we don't care if there are more now */
638 for (; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100639 if (ps->pfds[i].revents & POLLHUP) {
640 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
641 ps->sessions[i]->status = NC_STATUS_INVALID;
642 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100643 return 3;
Michal Vasko3a715132016-01-21 15:40:31 +0100644 } else if (ps->pfds[i].revents & POLLERR) {
645 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
646 ps->sessions[i]->status = NC_STATUS_INVALID;
647 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100648 return 3;
Michal Vasko3a715132016-01-21 15:40:31 +0100649 } else if (ps->pfds[i].revents & POLLIN) {
Michal Vasko428087d2016-01-14 16:04:28 +0100650#ifdef ENABLE_SSH
651 if (ps->sessions[i].session->ti_type == NC_TI_LIBSSH) {
652 /* things are not that simple with SSH, we need to check the channel */
653 ret = ssh_channel_poll_timeout(ps->sessions[i].session->ti.libssh.channel, 0, 0);
654 /* not this one */
655 if (!ret) {
656 if (i == ps->session_count - 1) {
657 /* last session and it is not the right channel, ... */
658 if (timeout > 0) {
659 /* ... decrease timeout, wait it all out and try again, last time */
660 clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
661
662 timeout -= (new_ts.tv_sec - old_ts.tv_sec) * 1000;
663 timeout -= (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
664 if (timeout < 0) {
665 ERRINT;
666 return -1;
667 }
668
669 old_ts = new_ts;
670 } else if (!timeout) {
671 /* ... timeout is 0, so that is it */
672 return 0;
673 } else {
674 /* ... retry polling reasonable time apart */
675 usleep(NC_TIMEOUT_STEP);
676 goto retry_poll;
677 }
678 }
679 /* check other sessions */
Michal Vaskobd8ef262016-01-20 11:09:27 +0100680 ps->sessions[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100681 continue;
682 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100683 ERR("Session %u: SSH channel error (%s).", ps->sessions[i].session->id,
Michal Vasko428087d2016-01-14 16:04:28 +0100684 ssh_get_error(ps->sessions[i].session->ti.libssh.session));
685 ps->sessions[i].session->status = NC_STATUS_INVALID;
686 ps->sessions[i].session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100687 return 3;
Michal Vasko428087d2016-01-14 16:04:28 +0100688 } else if (ret == SSH_EOF) {
Michal Vaskod083db62016-01-19 10:31:29 +0100689 ERR("Session %u: communication channel unexpectedly closed (libssh).",
690 ps->sessions[i].session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100691 ps->sessions[i].session->status = NC_STATUS_INVALID;
692 ps->sessions[i].session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100693 return 3;
Michal Vasko428087d2016-01-14 16:04:28 +0100694 }
695 }
696#endif /* ENABLE_SSH */
697
Michal Vaskobd8ef262016-01-20 11:09:27 +0100698 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +0100699 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100700 break;
701 }
702 }
703
704 if (i == ps->session_count) {
705 ERRINT;
706 return -1;
707 }
708
709 /* this is the session with some data available for reading */
Michal Vasko3a715132016-01-21 15:40:31 +0100710 session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +0100711
712 if (timeout > 0) {
713 clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
714
715 /* subtract elapsed time */
716 timeout -= (new_ts.tv_sec - old_ts.tv_sec) * 1000;
717 timeout -= (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
718 if (timeout < 0) {
719 ERRINT;
720 return -1;
721 }
722 }
723
Michal Vaskobd8ef262016-01-20 11:09:27 +0100724 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100725 ret = nc_timedlock(session->ti_lock, timeout, NULL);
726 if (ret != 1) {
727 /* error or timeout */
728 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100729 }
730
731 msgtype = nc_recv_rpc(session, &rpc);
732 if (msgtype == NC_MSG_ERROR) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100733 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100734 if (session->status != NC_STATUS_RUNNING) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100735 return 3;
Michal Vasko428087d2016-01-14 16:04:28 +0100736 }
737 return -1;
738 }
739
740 /* process RPC */
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100741 session->last_rpc = time(NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100742 msgtype = nc_send_reply(session, rpc);
743
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100744 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100745
746 if (msgtype == NC_MSG_ERROR) {
747 nc_server_rpc_free(rpc);
748 if (session->status != NC_STATUS_RUNNING) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100749 return 3;
Michal Vasko428087d2016-01-14 16:04:28 +0100750 }
751 return -1;
752 }
753
754 nc_server_rpc_free(rpc);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100755
756 /* is there some other socket waiting? */
757 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100758 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100759 return 2;
760 }
761 }
762
Michal Vasko428087d2016-01-14 16:04:28 +0100763 return 1;
764}
765
Michal Vasko11d142a2016-01-19 15:58:24 +0100766API int
767nc_ctx_lock(int timeout, int *elapsed)
768{
769 return nc_timedlock(&server_opts.ctx_lock, timeout, elapsed);
770}
771
772API int
773nc_ctx_unlock(void)
774{
775 int ret;
776
777 ret = pthread_mutex_unlock(&server_opts.ctx_lock);
778
779 if (ret) {
780 ERR("Mutex unlock failed (%s).", strerror(ret));
781 return -1;
782 }
783
784 return 0;
785}
786
Michal Vasko9e036d52016-01-08 10:49:26 +0100787#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
788
789API int
790nc_server_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
791{
792 int sock;
793
794 if (!address || !port || ((ti != NC_TI_LIBSSH) && (ti != NC_TI_OPENSSL))) {
795 ERRARG;
796 return -1;
797 }
798
799 sock = nc_sock_listen(address, port);
800 if (sock == -1) {
801 return -1;
802 }
803
Michal Vaskob48aa812016-01-18 14:13:09 +0100804 /* LOCK */
805 pthread_mutex_lock(&server_opts.bind_lock);
806
Michal Vasko9e036d52016-01-08 10:49:26 +0100807 ++server_opts.bind_count;
808 server_opts.binds = realloc(server_opts.binds, server_opts.bind_count * sizeof *server_opts.binds);
809
Michal Vasko11d142a2016-01-19 15:58:24 +0100810 nc_ctx_lock(-1, NULL);
811 server_opts.binds[server_opts.bind_count - 1].address = lydict_insert(server_opts.ctx, address, 0);
812 nc_ctx_unlock();
Michal Vasko9e036d52016-01-08 10:49:26 +0100813 server_opts.binds[server_opts.bind_count - 1].port = port;
814 server_opts.binds[server_opts.bind_count - 1].sock = sock;
815 server_opts.binds[server_opts.bind_count - 1].ti = ti;
816
Michal Vaskob48aa812016-01-18 14:13:09 +0100817 /* UNLOCK */
818 pthread_mutex_unlock(&server_opts.bind_lock);
819
Michal Vasko9e036d52016-01-08 10:49:26 +0100820 return 0;
821}
822
823API int
824nc_server_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
825{
826 uint32_t i;
827 int ret = -1;
828
Michal Vaskob48aa812016-01-18 14:13:09 +0100829 /* LOCK */
830 pthread_mutex_lock(&server_opts.bind_lock);
831
Michal Vasko1a38c862016-01-15 15:50:07 +0100832 if (!address && !port && !ti) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100833 nc_ctx_lock(-1, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100834 for (i = 0; i < server_opts.bind_count; ++i) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100835 close(server_opts.binds[i].sock);
Michal Vasko11d142a2016-01-19 15:58:24 +0100836 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko9e036d52016-01-08 10:49:26 +0100837
Michal Vasko9e036d52016-01-08 10:49:26 +0100838 ret = 0;
839 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100840 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +0100841 free(server_opts.binds);
842 server_opts.binds = NULL;
843 server_opts.bind_count = 0;
844 } else {
845 for (i = 0; i < server_opts.bind_count; ++i) {
846 if ((!address || !strcmp(server_opts.binds[i].address, address))
847 && (!port || (server_opts.binds[i].port == port))
848 && (!ti || (server_opts.binds[i].ti == ti))) {
849 close(server_opts.binds[i].sock);
Michal Vasko11d142a2016-01-19 15:58:24 +0100850 nc_ctx_lock(-1, NULL);
851 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
852 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +0100853
854 --server_opts.bind_count;
Michal Vasko5b003bf2016-01-19 10:56:19 +0100855 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.bind_count], sizeof *server_opts.binds);
Michal Vasko1a38c862016-01-15 15:50:07 +0100856
857 ret = 0;
858 }
859 }
Michal Vasko9e036d52016-01-08 10:49:26 +0100860 }
861
Michal Vaskob48aa812016-01-18 14:13:09 +0100862 /* UNLOCK */
863 pthread_mutex_unlock(&server_opts.bind_lock);
864
Michal Vasko9e036d52016-01-08 10:49:26 +0100865 return ret;
866}
867
Michal Vasko1a38c862016-01-15 15:50:07 +0100868API int
869nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +0100870{
871 NC_TRANSPORT_IMPL ti;
Michal Vasko1a38c862016-01-15 15:50:07 +0100872 int sock, ret;
Michal Vasko9e036d52016-01-08 10:49:26 +0100873 char *host;
874 uint16_t port;
Michal Vasko9e036d52016-01-08 10:49:26 +0100875
Michal Vasko1a38c862016-01-15 15:50:07 +0100876 if (!server_opts.ctx || !server_opts.binds || !session) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100877 ERRARG;
Michal Vasko1a38c862016-01-15 15:50:07 +0100878 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100879 }
880
Michal Vaskob48aa812016-01-18 14:13:09 +0100881 /* LOCK */
882 pthread_mutex_lock(&server_opts.bind_lock);
883
Michal Vaskof05562c2016-01-20 12:06:43 +0100884 ret = nc_sock_accept_binds(server_opts.binds, server_opts.bind_count, timeout, &ti, &host, &port);
Michal Vaskob48aa812016-01-18 14:13:09 +0100885
886 /* UNLOCK */
887 pthread_mutex_unlock(&server_opts.bind_lock);
888
Michal Vasko11d142a2016-01-19 15:58:24 +0100889 if (ret < 0) {
Michal Vaskob48aa812016-01-18 14:13:09 +0100890 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +0100891 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100892 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +0100893
Michal Vasko1a38c862016-01-15 15:50:07 +0100894 *session = calloc(1, sizeof **session);
Michal Vasko9e036d52016-01-08 10:49:26 +0100895 if (!session) {
896 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100897 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +0100898 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100899 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100900 (*session)->status = NC_STATUS_STARTING;
901 (*session)->side = NC_SERVER;
902 (*session)->ctx = server_opts.ctx;
903 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko11d142a2016-01-19 15:58:24 +0100904 nc_ctx_lock(-1, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100905 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
Michal Vasko11d142a2016-01-19 15:58:24 +0100906 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +0100907 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +0100908
909 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +0100910 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
911 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100912 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100913 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +0100914 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100915 goto fail;
916 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100917 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +0100918
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100919 /* sock gets assigned to session or closed */
Michal Vasko9e036d52016-01-08 10:49:26 +0100920 if (ti == NC_TI_LIBSSH) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100921 ret = nc_accept_ssh_session(*session, sock, timeout);
922 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100923 goto fail;
924 }
925 } else if (ti == NC_TI_OPENSSL) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100926 ret = nc_accept_tls_session(*session, sock, timeout);
927 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100928 goto fail;
929 }
930 } else {
931 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100932 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +0100933 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100934 goto fail;
935 }
936
Michal Vaskob48aa812016-01-18 14:13:09 +0100937 /* assign new SID atomically */
938 /* LOCK */
939 pthread_spin_lock(&server_opts.sid_lock);
940 (*session)->id = server_opts.new_session_id++;
941 /* UNLOCK */
942 pthread_spin_unlock(&server_opts.sid_lock);
943
Michal Vasko9e036d52016-01-08 10:49:26 +0100944 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100945 if (nc_handshake(*session)) {
946 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100947 goto fail;
948 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100949 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +0100950
Michal Vasko1a38c862016-01-15 15:50:07 +0100951 return 1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100952
953fail:
Michal Vasko1a38c862016-01-15 15:50:07 +0100954 nc_session_free(*session);
955 *session = NULL;
956 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100957}
958
959#endif /* ENABLE_SSH || ENABLE_TLS */