blob: 387cf5c635a9f1ac1e1dc03140e81c8503b407f4 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file session.c
Michal Vasko086311b2016-01-08 09:53:11 +01003 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 - general session functions
Radek Krejci206fcd62015-10-07 15:42:48 +02005 *
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
Radek Krejci206fcd62015-10-07 15:42:48 +020023#include <errno.h>
Radek Krejci952eb862016-01-08 14:22:55 +010024#include <stdlib.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020025#include <pthread.h>
Michal Vasko58f31552016-01-19 12:39:15 +010026#include <time.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020027#include <libyang/libyang.h>
28
Michal Vaskob48aa812016-01-18 14:13:09 +010029#include "libnetconf.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010030#include "session_server.h"
Michal Vaskob48aa812016-01-18 14:13:09 +010031
Michal Vasko086311b2016-01-08 09:53:11 +010032#ifdef ENABLE_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020033
Michal Vasko086311b2016-01-08 09:53:11 +010034# include <libssh/libssh.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020035
Michal Vasko086311b2016-01-08 09:53:11 +010036#endif /* ENABLE_SSH */
Radek Krejci695d4fa2015-10-22 13:23:54 +020037
Michal Vaskoc14e3c82016-01-11 16:14:30 +010038#ifdef ENABLE_TLS
39
40# include <openssl/err.h>
41
42#endif /* ENABLE_TLS */
43
Michal Vasko086311b2016-01-08 09:53:11 +010044/* in seconds */
45#define NC_CLIENT_HELLO_TIMEOUT 60
Radek Krejci695d4fa2015-10-22 13:23:54 +020046
Michal Vasko05ba9df2016-01-13 14:40:27 +010047/* in milliseconds */
48#define NC_CLOSE_REPLY_TIMEOUT 200
49
Michal Vasko086311b2016-01-08 09:53:11 +010050extern struct nc_server_opts server_opts;
51
Michal Vasko96164bf2016-01-21 15:41:58 +010052/*
53 * @return 1 - success
54 * 0 - timeout
55 * -1 - error
56 */
57int
58nc_timedlock(pthread_mutex_t *lock, int timeout, int *elapsed)
59{
60 int ret;
61 struct timespec ts_timeout, ts_old, ts_new;
62
63 if (timeout > 0) {
64 clock_gettime(CLOCK_REALTIME, &ts_timeout);
65
66 if (elapsed) {
67 ts_old = ts_timeout;
68 }
69
70 ts_timeout.tv_sec += timeout / 1000;
71 ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
72
73 ret = pthread_mutex_timedlock(lock, &ts_timeout);
74
75 if (elapsed) {
76 clock_gettime(CLOCK_REALTIME, &ts_new);
77
78 *elapsed += (ts_new.tv_sec - ts_old.tv_sec) * 1000;
79 *elapsed += (ts_new.tv_nsec - ts_old.tv_nsec) / 1000000;
80 }
81 } else if (!timeout) {
82 ret = pthread_mutex_trylock(lock);
83 } else { /* timeout == -1 */
84 ret = pthread_mutex_lock(lock);
85 }
86
87 if (ret == ETIMEDOUT) {
88 /* timeout */
89 return 0;
90 } else if (ret) {
91 /* error */
92 ERR("Mutex lock failed (%s).", strerror(errno));
93 return -1;
94 }
95
96 /* ok */
97 return 1;
98}
99
100void
101nc_subtract_elapsed(int *timeout, struct timespec *old_ts)
102{
103 struct timespec new_ts;
104
105 clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
106
107 *timeout -= (new_ts.tv_sec - old_ts->tv_sec) * 1000;
108 *timeout -= (new_ts.tv_nsec - old_ts->tv_nsec) / 1000000;
109
110 *old_ts = new_ts;
111}
112
Michal Vasko8dadf782016-01-15 10:29:36 +0100113API NC_STATUS
114nc_session_get_status(const struct nc_session *session)
115{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100116 if (!session) {
117 ERRARG;
118 return 0;
119 }
120
Michal Vasko8dadf782016-01-15 10:29:36 +0100121 return session->status;
122}
123
124API uint32_t
125nc_session_get_id(const struct nc_session *session)
126{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100127 if (!session) {
128 ERRARG;
129 return 0;
130 }
131
Michal Vasko8dadf782016-01-15 10:29:36 +0100132 return session->id;
133}
134
135API NC_TRANSPORT_IMPL
136nc_session_get_ti(const struct nc_session *session)
137{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100138 if (!session) {
139 ERRARG;
140 return 0;
141 }
142
Michal Vasko8dadf782016-01-15 10:29:36 +0100143 return session->ti_type;
144}
145
146API const char *
147nc_session_get_username(const struct nc_session *session)
148{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100149 if (!session) {
150 ERRARG;
151 return NULL;
152 }
153
Michal Vasko8dadf782016-01-15 10:29:36 +0100154 return session->username;
155}
156
157API const char *
158nc_session_get_host(const struct nc_session *session)
159{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100160 if (!session) {
161 ERRARG;
162 return NULL;
163 }
164
Michal Vasko8dadf782016-01-15 10:29:36 +0100165 return session->host;
166}
167
168API uint16_t
169nc_session_get_port(const struct nc_session *session)
170{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100171 if (!session) {
172 ERRARG;
173 return 0;
174 }
175
Michal Vasko8dadf782016-01-15 10:29:36 +0100176 return session->port;
177}
178
179API const char **
180nc_session_get_cpblts(const struct nc_session *session)
181{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100182 if (!session) {
183 ERRARG;
184 return NULL;
185 }
186
Michal Vasko8dadf782016-01-15 10:29:36 +0100187 return session->cpblts;
188}
189
190API const char *
191nc_session_cpblt(const struct nc_session *session, const char *capab)
192{
193 int i, len;
194
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100195 if (!session || !capab) {
196 ERRARG;
197 return NULL;
198 }
199
Michal Vasko8dadf782016-01-15 10:29:36 +0100200 len = strlen(capab);
201 for (i = 0; session->cpblts[i]; ++i) {
202 if (!strncmp(session->cpblts[i], capab, len)) {
203 return session->cpblts[i];
204 }
205 }
206
207 return NULL;
208}
209
Michal Vasko086311b2016-01-08 09:53:11 +0100210NC_MSG_TYPE
211nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200212{
Michal Vasko086311b2016-01-08 09:53:11 +0100213 int r;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200214
Michal Vasko086311b2016-01-08 09:53:11 +0100215 if (session->ctx != op->schema->module->ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERR("Session %u: RPC \"%s\" was created in different context than that of the session.",
217 session->id, op->schema->name);
Michal Vasko086311b2016-01-08 09:53:11 +0100218 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100219 }
220
Michal Vasko086311b2016-01-08 09:53:11 +0100221 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
222
223 if (r) {
224 return NC_MSG_ERROR;
225 }
226
227 return NC_MSG_RPC;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100228}
229
Radek Krejci695d4fa2015-10-22 13:23:54 +0200230API void
231nc_session_free(struct nc_session *session)
232{
233 int r, i;
Michal Vasko428087d2016-01-14 16:04:28 +0100234 int connected; /* flag to indicate whether the transport socket is still connected */
Michal Vaskob48aa812016-01-18 14:13:09 +0100235 int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */
Michal Vaskoa8ad4482016-01-28 14:25:54 +0100236 pthread_t tid;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200237 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100238 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100239 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200240 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100241 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200242 void *p;
243
Michal Vasko428087d2016-01-14 16:04:28 +0100244 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200245 return;
246 }
247
248 /* mark session for closing */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100249 if (session->ti_lock) {
250 do {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100251 r = nc_timedlock(session->ti_lock, 0, NULL);
252 } while (!r);
253 if (r == -1) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100254 return;
255 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200256 }
257
258 /* stop notifications loop if any */
Michal Vaskoa8ad4482016-01-28 14:25:54 +0100259 if (session->ntf_tid) {
260 tid = *session->ntf_tid;
261 free((pthread_t *)session->ntf_tid);
262 session->ntf_tid = NULL;
263 /* the thread now knows it should quit */
264
265 pthread_join(tid, NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200266 }
267
Michal Vasko428087d2016-01-14 16:04:28 +0100268 if ((session->side == NC_CLIENT) && (session->status == NC_STATUS_RUNNING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200269 /* cleanup message queues */
270 /* notifications */
Michal Vaskoad611702015-12-03 13:41:51 +0100271 for (contiter = session->notifs; contiter; ) {
272 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200273
Michal Vaskoad611702015-12-03 13:41:51 +0100274 p = contiter;
275 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200276 free(p);
277 }
278
279 /* rpc replies */
Michal Vaskoad611702015-12-03 13:41:51 +0100280 for (contiter = session->replies; contiter; ) {
281 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200282
Michal Vaskoad611702015-12-03 13:41:51 +0100283 p = contiter;
284 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200285 free(p);
286 }
287
288 /* send closing info to the other side */
289 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
290 if (!ietfnc) {
Michal Vasko428087d2016-01-14 16:04:28 +0100291 WRN("Session %u: missing ietf-netconf schema in context, unable to send <close-session>.", session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200292 } else {
293 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100294 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200295 lyd_free(close_rpc);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100296 switch (nc_read_msg_poll(session, NC_CLOSE_REPLY_TIMEOUT, &rpl)) {
Michal Vaskofad6e912015-10-26 15:37:22 +0100297 case NC_MSG_REPLY:
298 LY_TREE_FOR(rpl->child, child) {
299 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
300 break;
301 }
302 }
303 if (!child) {
Michal Vasko428087d2016-01-14 16:04:28 +0100304 WRN("Session %u: the reply to <close-session> was not <ok> as expected.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100305 }
Michal Vaskoad611702015-12-03 13:41:51 +0100306 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100307 break;
308 case NC_MSG_WOULDBLOCK:
Michal Vasko428087d2016-01-14 16:04:28 +0100309 WRN("Session %u: timeout for receiving a reply to <close-session> elapsed.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100310 break;
311 case NC_MSG_ERROR:
Michal Vaskod083db62016-01-19 10:31:29 +0100312 ERR("Session %u: failed to receive a reply to <close-session>.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100313 break;
314 default:
315 /* cannot happen */
316 break;
317 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200318 }
319
320 /* list of server's capabilities */
321 if (session->cpblts) {
322 for (i = 0; session->cpblts[i]; i++) {
323 lydict_remove(session->ctx, session->cpblts[i]);
324 }
325 free(session->cpblts);
326 }
327 }
328
329 session->status = NC_STATUS_CLOSING;
Michal Vasko428087d2016-01-14 16:04:28 +0100330 connected = nc_session_is_connected(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200331
332 /* transport implementation cleanup */
333 switch (session->ti_type) {
334 case NC_TI_FD:
335 /* nothing needed - file descriptors were provided by caller,
336 * so it is up to the caller to close them correctly
337 * TODO use callbacks
338 */
339 break;
340
Michal Vaskofb2fb762015-10-27 11:44:32 +0100341#ifdef ENABLE_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200342 case NC_TI_LIBSSH:
Michal Vasko428087d2016-01-14 16:04:28 +0100343 if (connected) {
344 ssh_channel_free(session->ti.libssh.channel);
345 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200346 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
347 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
348 * it.
349 */
Michal Vasko96164bf2016-01-21 15:41:58 +0100350 multisession = 0;
351 if (session->ti.libssh.next) {
352 for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
353 if (siter->status != NC_STATUS_STARTING) {
354 multisession = 1;
355 break;
356 }
357 }
358 }
359
360 if (!multisession) {
361 /* it's not multisession yet, but we still need to free the starting sessions */
362 if (session->ti.libssh.next) {
363 do {
364 siter = session->ti.libssh.next;
365 session->ti.libssh.next = siter->ti.libssh.next;
366
367 /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */
368 if (session->side == NC_SERVER) {
369 nc_ctx_lock(-1, NULL);
370 }
371 lydict_remove(session->ctx, session->username);
372 lydict_remove(session->ctx, session->host);
373 if (session->side == NC_SERVER) {
374 nc_ctx_unlock();
375 }
376 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
377 ly_ctx_destroy(session->ctx);
378 }
379
380 free(siter);
381 } while (session->ti.libssh.next != session);
382 }
Michal Vasko428087d2016-01-14 16:04:28 +0100383 if (connected) {
384 ssh_disconnect(session->ti.libssh.session);
385 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200386 ssh_free(session->ti.libssh.session);
387 } else {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200388 /* remove the session from the list */
389 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100390 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200391 /* there will be only one session */
392 siter->ti.libssh.next = NULL;
393 } else {
394 /* there are still multiple sessions, keep the ring list */
395 siter->ti.libssh.next = session->ti.libssh.next;
396 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100397 /* change nc_sshcb_msg() argument, we need a RUNNING session and this one will be freed */
398 if (session->flags & NC_SESSION_SSH_MSG_CB) {
399 for (siter = session->ti.libssh.next; siter->status != NC_STATUS_RUNNING; siter = siter->ti.libssh.next) {
400 if (siter->ti.libssh.next == session) {
401 ERRINT;
402 break;
403 }
404 }
405 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, siter);
406 siter->flags |= NC_SESSION_SSH_MSG_CB;
407 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200408 }
409 break;
410#endif
411
412#ifdef ENABLE_TLS
413 case NC_TI_OPENSSL:
Michal Vasko428087d2016-01-14 16:04:28 +0100414 if (connected) {
415 SSL_shutdown(session->ti.tls);
416 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200417 SSL_free(session->ti.tls);
Michal Vasko06e22432016-01-15 10:30:06 +0100418
419 X509_free(session->tls_cert);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200420 break;
421#endif
Michal Vasko428087d2016-01-14 16:04:28 +0100422 case NC_TI_NONE:
423 ERRINT;
424 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200425 }
Michal Vasko428087d2016-01-14 16:04:28 +0100426
Michal Vasko11d142a2016-01-19 15:58:24 +0100427 if (session->side == NC_SERVER) {
428 nc_ctx_lock(-1, NULL);
429 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200430 lydict_remove(session->ctx, session->username);
431 lydict_remove(session->ctx, session->host);
Michal Vasko11d142a2016-01-19 15:58:24 +0100432 if (session->side == NC_SERVER) {
433 nc_ctx_unlock();
434 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200435
436 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100437 if (session->ti_lock) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100438 pthread_mutex_unlock(session->ti_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100439 if (!multisession) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100440 pthread_mutex_destroy(session->ti_lock);
441 free(session->ti_lock);
442 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200443 }
444
445 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
446 ly_ctx_destroy(session->ctx);
447 }
448
449 free(session);
450}
451
Michal Vasko086311b2016-01-08 09:53:11 +0100452static void
453add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count)
454{
455 if (*count == *size) {
456 *size += 5;
457 *cpblts = realloc(*cpblts, *size * sizeof **cpblts);
458 }
459
460 if (capab) {
461 (*cpblts)[*count] = lydict_insert(ctx, capab, 0);
462 } else {
463 (*cpblts)[*count] = NULL;
464 }
465 ++(*count);
466}
467
468static const char **
469create_cpblts(struct ly_ctx *ctx)
470{
471 struct lyd_node *child, *child2, *yanglib;
472 struct lyd_node_leaf_list **features = NULL, *ns = NULL, *rev = NULL, *name = NULL;
473 const char **cpblts;
474 const struct lys_module *mod;
Michal Vasko11d142a2016-01-19 15:58:24 +0100475 int size = 10, count, feat_count = 0, i, str_len;
Michal Vasko086311b2016-01-08 09:53:11 +0100476 char str[512];
477
Michal Vasko11d142a2016-01-19 15:58:24 +0100478 nc_ctx_lock(-1, NULL);
479
Michal Vasko086311b2016-01-08 09:53:11 +0100480 yanglib = ly_ctx_info(ctx);
481 if (!yanglib) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100482 nc_ctx_unlock();
Michal Vasko086311b2016-01-08 09:53:11 +0100483 return NULL;
484 }
485
486 cpblts = malloc(size * sizeof *cpblts);
487 cpblts[0] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0);
488 cpblts[1] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0);
489 count = 2;
490
491 /* capabilities */
492
493 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
494 if (mod) {
495 if (lys_features_state(mod, "writable-running") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100496 add_cpblt(ctx, "urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100497 }
498 if (lys_features_state(mod, "candidate") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100499 add_cpblt(ctx, "urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100500 if (lys_features_state(mod, "confirmed-commit") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100501 add_cpblt(ctx, "urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100502 }
503 }
504 if (lys_features_state(mod, "rollback-on-error") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100505 add_cpblt(ctx, "urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100506 }
507 if (lys_features_state(mod, "validate") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100508 add_cpblt(ctx, "urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100509 }
510 if (lys_features_state(mod, "startup") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100511 add_cpblt(ctx, "urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100512 }
513 if (lys_features_state(mod, "url") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100514 add_cpblt(ctx, "urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100515 }
516 if (lys_features_state(mod, "xpath") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100517 add_cpblt(ctx, "urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100518 }
519 }
520
521 mod = ly_ctx_get_module(ctx, "ietf-netconf-with-defaults", NULL);
522 if (mod) {
523 if (!server_opts.wd_basic_mode) {
524 VRB("with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
525 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100526 strcpy(str, "urn:ietf:params:netconf:capability:with-defaults:1.0");
Michal Vasko086311b2016-01-08 09:53:11 +0100527 switch (server_opts.wd_basic_mode) {
528 case NC_WD_ALL:
529 strcat(str, "?basic-mode=report-all");
530 break;
531 case NC_WD_TRIM:
532 strcat(str, "?basic-mode=trim");
533 break;
534 case NC_WD_EXPLICIT:
535 strcat(str, "?basic-mode=explicit");
536 break;
537 default:
Michal Vasko9e036d52016-01-08 10:49:26 +0100538 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100539 break;
540 }
541
542 if (server_opts.wd_also_supported) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100543 strcat(str, "&amp;also-supported=");
Michal Vasko086311b2016-01-08 09:53:11 +0100544 if (server_opts.wd_also_supported & NC_WD_ALL) {
545 strcat(str, "report-all,");
546 }
547 if (server_opts.wd_also_supported & NC_WD_ALL_TAG) {
548 strcat(str, "report-all-tagged,");
549 }
550 if (server_opts.wd_also_supported & NC_WD_TRIM) {
551 strcat(str, "trim,");
552 }
553 if (server_opts.wd_also_supported & NC_WD_EXPLICIT) {
554 strcat(str, "explicit,");
555 }
556 str[strlen(str) - 1] = '\0';
557
558 add_cpblt(ctx, str, &cpblts, &size, &count);
559 }
560 }
561 }
562
Michal Vasko1a38c862016-01-15 15:50:07 +0100563 mod = ly_ctx_get_module(ctx, "nc-notifications", NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100564 if (mod) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100565 add_cpblt(ctx, "urn:ietf:params:netconf:capability:notification:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100566 if (server_opts.interleave_capab) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100567 add_cpblt(ctx, "urn:ietf:params:netconf:capability:interleave:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100568 }
569 }
570
571 /* models */
572
573 LY_TREE_FOR(yanglib->child, child) {
574 if (!strcmp(child->schema->name, "module")) {
575 LY_TREE_FOR(child->child, child2) {
576 if (!strcmp(child2->schema->name, "namespace")) {
577 ns = (struct lyd_node_leaf_list *)child2;
578 } else if (!strcmp(child2->schema->name, "name")) {
579 name = (struct lyd_node_leaf_list *)child2;
580 } else if (!strcmp(child2->schema->name, "revision")) {
581 rev = (struct lyd_node_leaf_list *)child2;
582 } else if (!strcmp(child2->schema->name, "feature")) {
583 features = realloc(features, feat_count++ * sizeof *features);
584 features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
585 }
586 }
587
588 if (!ns || !name || !rev) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100589 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100590 continue;
591 }
592
Michal Vasko11d142a2016-01-19 15:58:24 +0100593 str_len = sprintf(str, "%s?module=%s&amp;revision=%s", ns->value_str, name->value_str, rev->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100594 if (feat_count) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100595 strcat(str, "&amp;features=");
Michal Vasko11d142a2016-01-19 15:58:24 +0100596 str_len += 14;
Michal Vasko086311b2016-01-08 09:53:11 +0100597 for (i = 0; i < feat_count; ++i) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100598 if (str_len + 1 + strlen(features[i]->value_str) >= 512) {
599 ERRINT;
600 break;
601 }
Michal Vasko086311b2016-01-08 09:53:11 +0100602 if (i) {
603 strcat(str, ",");
Michal Vasko11d142a2016-01-19 15:58:24 +0100604 ++str_len;
Michal Vasko086311b2016-01-08 09:53:11 +0100605 }
606 strcat(str, features[i]->value_str);
Michal Vasko11d142a2016-01-19 15:58:24 +0100607 str_len += strlen(features[i]->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100608 }
609 }
610
611 add_cpblt(ctx, str, &cpblts, &size, &count);
612
613 ns = NULL;
614 name = NULL;
615 rev = NULL;
616 free(features);
617 features = NULL;
618 feat_count = 0;
619 }
620 }
621
622 lyd_free(yanglib);
623
Michal Vasko11d142a2016-01-19 15:58:24 +0100624 nc_ctx_unlock();
625
Michal Vasko086311b2016-01-08 09:53:11 +0100626 /* ending NULL capability */
627 add_cpblt(ctx, NULL, &cpblts, &size, &count);
628
629 return cpblts;
630}
631
Radek Krejci695d4fa2015-10-22 13:23:54 +0200632static int
633parse_cpblts(struct lyxml_elem *xml, const char ***list)
634{
635 struct lyxml_elem *cpblt;
636 int ver = -1;
637 int i = 0;
638
639 if (list) {
640 /* get the storage for server's capabilities */
641 LY_TREE_FOR(xml->child, cpblt) {
642 i++;
643 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100644 /* last item remains NULL */
645 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200646 if (!*list) {
647 ERRMEM;
648 return -1;
649 }
650 i = 0;
651 }
652
653 LY_TREE_FOR(xml->child, cpblt) {
654 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
655 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
656 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
657 return -1;
658 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
659 continue;
660 }
661
662 /* detect NETCONF version */
663 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
664 ver = 0;
665 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
666 ver = 1;
667 }
668
669 /* store capabilities */
670 if (list) {
671 (*list)[i] = cpblt->content;
672 cpblt->content = NULL;
673 i++;
674 }
675 }
676
677 if (ver == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100678 ERR("Peer does not support a compatible NETCONF version.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200679 }
680
681 return ver;
682}
683
684static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100685nc_send_client_hello(struct nc_session *session)
Michal Vasko086311b2016-01-08 09:53:11 +0100686{
687 int r, i;
688 const char **cpblts;
689
Michal Vasko11d142a2016-01-19 15:58:24 +0100690 /* client side hello - send only NETCONF base capabilities */
691 cpblts = malloc(3 * sizeof *cpblts);
692 cpblts[0] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0);
693 cpblts[1] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0);
694 cpblts[2] = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100695
Michal Vasko11d142a2016-01-19 15:58:24 +0100696 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100697
Michal Vasko086311b2016-01-08 09:53:11 +0100698 for (i = 0; cpblts[i]; ++i) {
699 lydict_remove(session->ctx, cpblts[i]);
700 }
701 free(cpblts);
702
703 if (r) {
704 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100705 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100706
707 return NC_MSG_HELLO;
Michal Vasko086311b2016-01-08 09:53:11 +0100708}
709
710static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100711nc_send_server_hello(struct nc_session *session)
712{
713 int r, i;
714 const char **cpblts;
715
716 cpblts = create_cpblts(session->ctx);
717
718 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, &session->id);
719
720 nc_ctx_lock(-1, NULL);
721 for (i = 0; cpblts[i]; ++i) {
722 lydict_remove(session->ctx, cpblts[i]);
723 }
724 nc_ctx_unlock();
725 free(cpblts);
726
727 if (r) {
728 return NC_MSG_ERROR;
729 }
730
731 return NC_MSG_HELLO;
732}
733
734static NC_MSG_TYPE
735nc_recv_client_hello(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200736{
737 struct lyxml_elem *xml = NULL, *node;
738 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
739 int ver = -1;
740 char *str;
741 long long int id;
742 int flag = 0;
743
Michal Vasko05ba9df2016-01-13 14:40:27 +0100744 msgtype = nc_read_msg_poll(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200745
746 switch(msgtype) {
747 case NC_MSG_HELLO:
748 /* parse <hello> data */
Michal Vasko11d142a2016-01-19 15:58:24 +0100749 LY_TREE_FOR(xml->child, node) {
750 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
751 continue;
752 } else if (!strcmp(node->name, "session-id")) {
753 if (!node->content || !strlen(node->content)) {
754 ERR("No value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200755 goto error;
756 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100757 str = NULL;
758 id = strtoll(node->content, &str, 10);
759 if (*str || id < 1 || id > UINT32_MAX) {
760 ERR("Invalid value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200761 goto error;
762 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100763 session->id = (uint32_t)id;
764 continue;
765 } else if (strcmp(node->name, "capabilities")) {
766 ERR("Unexpected <%s> element in client's <hello>.", node->name);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200767 goto error;
768 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100769
770 if (flag) {
771 /* multiple capabilities elements */
772 ERR("Invalid <hello> message (multiple <capabilities> elements).");
773 goto error;
774 }
775 flag = 1;
776
777 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
778 goto error;
779 }
780 session->version = ver;
781 }
782
783 if (!session->id) {
784 ERR("Missing <session-id> in server's <hello>.");
785 goto error;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200786 }
787 break;
788 case NC_MSG_ERROR:
789 /* nothing special, just pass it out */
790 break;
791 default:
792 ERR("Unexpected message received instead of <hello>.");
793 msgtype = NC_MSG_ERROR;
794 }
795
796 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100797 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200798
799 return msgtype;
800
801error:
802 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100803 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200804
805 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +0200806}
807
Michal Vasko11d142a2016-01-19 15:58:24 +0100808static NC_MSG_TYPE
809nc_recv_server_hello(struct nc_session *session)
810{
811 struct lyxml_elem *xml = NULL, *node;
812 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
813 int ver = -1;
814 int flag = 0;
815
Michal Vaskoadb850e2016-01-20 14:06:32 +0100816 msgtype = nc_read_msg_poll(session, (server_opts.hello_timeout ? server_opts.hello_timeout * 1000 : -1), &xml);
Michal Vasko11d142a2016-01-19 15:58:24 +0100817
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100818 switch (msgtype) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100819 case NC_MSG_HELLO:
820 /* get know NETCONF version */
821 LY_TREE_FOR(xml->child, node) {
822 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
823 continue;
824 } else if (strcmp(node->name, "capabilities")) {
825 ERR("Unexpected <%s> element in client's <hello>.", node->name);
826 msgtype = NC_MSG_ERROR;
827 goto cleanup;
828 }
829
830 if (flag) {
831 /* multiple capabilities elements */
832 ERR("Invalid <hello> message (multiple <capabilities> elements).");
833 msgtype = NC_MSG_ERROR;
834 goto cleanup;
835 }
836 flag = 1;
837
838 if ((ver = parse_cpblts(node, NULL)) < 0) {
839 msgtype = NC_MSG_ERROR;
840 goto cleanup;
841 }
842 session->version = ver;
843 }
844 break;
845 case NC_MSG_ERROR:
846 /* nothing special, just pass it out */
847 break;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100848 case NC_MSG_WOULDBLOCK:
849 ERR("Client's <hello> timeout elapsed.");
850 msgtype = NC_MSG_ERROR;
851 break;
Michal Vasko11d142a2016-01-19 15:58:24 +0100852 default:
853 ERR("Unexpected message received instead of <hello>.");
854 msgtype = NC_MSG_ERROR;
855 }
856
857cleanup:
858 nc_ctx_lock(-1, NULL);
859 lyxml_free(session->ctx, xml);
860 nc_ctx_unlock();
861
862 return msgtype;
863}
864
Michal Vasko80cad7f2015-12-08 14:42:27 +0100865int
Michal Vasko086311b2016-01-08 09:53:11 +0100866nc_handshake(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100867{
Michal Vasko086311b2016-01-08 09:53:11 +0100868 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100869
Michal Vasko11d142a2016-01-19 15:58:24 +0100870 if (session->side == NC_CLIENT) {
871 type = nc_send_client_hello(session);
872 } else {
873 type = nc_send_server_hello(session);
874 }
875
Michal Vasko086311b2016-01-08 09:53:11 +0100876 if (type != NC_MSG_HELLO) {
877 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100878 }
879
Michal Vasko11d142a2016-01-19 15:58:24 +0100880 if (session->side == NC_CLIENT) {
881 type = nc_recv_client_hello(session);
882 } else {
883 type = nc_recv_server_hello(session);
884 }
885
Michal Vasko086311b2016-01-08 09:53:11 +0100886 if (type != NC_MSG_HELLO) {
887 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100888 }
889
Michal Vasko086311b2016-01-08 09:53:11 +0100890 return 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100891}
Michal Vasko086311b2016-01-08 09:53:11 +0100892
893#ifdef ENABLE_SSH
894
895API void
896nc_ssh_init(void)
897{
898 ssh_threads_set_callbacks(ssh_threads_get_pthread());
899 ssh_init();
900 ssh_set_log_level(verbose_level);
901}
902
903API void
904nc_ssh_destroy(void)
905{
906 ssh_finalize();
907}
908
909#endif /* ENABLE_SSH */
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100910
911#ifdef ENABLE_TLS
912
913static pthread_mutex_t *tls_locks;
914
915static void
Michal Vaskob48aa812016-01-18 14:13:09 +0100916tls_thread_locking_func(int mode, int n, const char *UNUSED(file), int UNUSED(line))
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100917{
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100918 if (mode & CRYPTO_LOCK) {
919 pthread_mutex_lock(tls_locks + n);
920 } else {
921 pthread_mutex_unlock(tls_locks + n);
922 }
923}
924
925static unsigned long
926tls_thread_id_func(void)
927{
928 return (unsigned long)pthread_self();
929}
930
931API void
932nc_tls_init(void)
933{
934 int i;
935
936 SSL_load_error_strings();
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100937 ERR_load_BIO_strings();
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100938 SSL_library_init();
939
940 tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks);
941 for (i = 0; i < CRYPTO_num_locks(); ++i) {
942 pthread_mutex_init(tls_locks + i, NULL);
943 }
944
945 CRYPTO_set_id_callback(tls_thread_id_func);
946 CRYPTO_set_locking_callback(tls_thread_locking_func);
947}
948
949API void
950nc_tls_destroy(void)
951{
952 int i;
953
954 CRYPTO_THREADID crypto_tid;
955
956 EVP_cleanup();
957 CRYPTO_cleanup_all_ex_data();
958 ERR_free_strings();
959 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
960 CRYPTO_THREADID_current(&crypto_tid);
961 ERR_remove_thread_state(&crypto_tid);
962
963 CRYPTO_set_id_callback(NULL);
964 CRYPTO_set_locking_callback(NULL);
965 for (i = 0; i < CRYPTO_num_locks(); ++i) {
966 pthread_mutex_destroy(tls_locks + i);
967 }
968 free(tls_locks);
969}
970
971#endif /* ENABLE_TLS */