blob: 87ca628ab7061f94c3212f86bf6d8d9dfce16b63 [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 Vasko8dadf782016-01-15 10:29:36 +010052API NC_STATUS
53nc_session_get_status(const struct nc_session *session)
54{
Michal Vasko7f1c78b2016-01-19 09:52:14 +010055 if (!session) {
56 ERRARG;
57 return 0;
58 }
59
Michal Vasko8dadf782016-01-15 10:29:36 +010060 return session->status;
61}
62
63API uint32_t
64nc_session_get_id(const struct nc_session *session)
65{
Michal Vasko7f1c78b2016-01-19 09:52:14 +010066 if (!session) {
67 ERRARG;
68 return 0;
69 }
70
Michal Vasko8dadf782016-01-15 10:29:36 +010071 return session->id;
72}
73
74API NC_TRANSPORT_IMPL
75nc_session_get_ti(const struct nc_session *session)
76{
Michal Vasko7f1c78b2016-01-19 09:52:14 +010077 if (!session) {
78 ERRARG;
79 return 0;
80 }
81
Michal Vasko8dadf782016-01-15 10:29:36 +010082 return session->ti_type;
83}
84
85API const char *
86nc_session_get_username(const struct nc_session *session)
87{
Michal Vasko7f1c78b2016-01-19 09:52:14 +010088 if (!session) {
89 ERRARG;
90 return NULL;
91 }
92
Michal Vasko8dadf782016-01-15 10:29:36 +010093 return session->username;
94}
95
96API const char *
97nc_session_get_host(const struct nc_session *session)
98{
Michal Vasko7f1c78b2016-01-19 09:52:14 +010099 if (!session) {
100 ERRARG;
101 return NULL;
102 }
103
Michal Vasko8dadf782016-01-15 10:29:36 +0100104 return session->host;
105}
106
107API uint16_t
108nc_session_get_port(const struct nc_session *session)
109{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100110 if (!session) {
111 ERRARG;
112 return 0;
113 }
114
Michal Vasko8dadf782016-01-15 10:29:36 +0100115 return session->port;
116}
117
118API const char **
119nc_session_get_cpblts(const struct nc_session *session)
120{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100121 if (!session) {
122 ERRARG;
123 return NULL;
124 }
125
Michal Vasko8dadf782016-01-15 10:29:36 +0100126 return session->cpblts;
127}
128
129API const char *
130nc_session_cpblt(const struct nc_session *session, const char *capab)
131{
132 int i, len;
133
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100134 if (!session || !capab) {
135 ERRARG;
136 return NULL;
137 }
138
Michal Vasko8dadf782016-01-15 10:29:36 +0100139 len = strlen(capab);
140 for (i = 0; session->cpblts[i]; ++i) {
141 if (!strncmp(session->cpblts[i], capab, len)) {
142 return session->cpblts[i];
143 }
144 }
145
146 return NULL;
147}
148
Michal Vasko086311b2016-01-08 09:53:11 +0100149NC_MSG_TYPE
150nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200151{
Michal Vasko086311b2016-01-08 09:53:11 +0100152 int r;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200153
Michal Vasko086311b2016-01-08 09:53:11 +0100154 if (session->ctx != op->schema->module->ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100155 ERR("Session %u: RPC \"%s\" was created in different context than that of the session.",
156 session->id, op->schema->name);
Michal Vasko086311b2016-01-08 09:53:11 +0100157 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100158 }
159
Michal Vasko086311b2016-01-08 09:53:11 +0100160 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
161
162 if (r) {
163 return NC_MSG_ERROR;
164 }
165
166 return NC_MSG_RPC;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100167}
168
Radek Krejci5686ff72015-10-09 13:33:56 +0200169/*
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100170 * @return 1 - success
171 * 0 - timeout
172 * -1 - error
Radek Krejci5686ff72015-10-09 13:33:56 +0200173 */
Michal Vasko086311b2016-01-08 09:53:11 +0100174int
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100175nc_timedlock(pthread_mutex_t *lock, int timeout, int *elapsed)
Radek Krejci206fcd62015-10-07 15:42:48 +0200176{
Michal Vasko58f31552016-01-19 12:39:15 +0100177 int ret;
178 struct timespec ts_timeout, ts_old, ts_new;
Radek Krejci206fcd62015-10-07 15:42:48 +0200179
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100180 if (timeout > 0) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100181 clock_gettime(CLOCK_REALTIME, &ts_timeout);
Michal Vasko58f31552016-01-19 12:39:15 +0100182
Michal Vasko11d142a2016-01-19 15:58:24 +0100183 if (elapsed) {
184 ts_old = ts_timeout;
185 }
186
187 ts_timeout.tv_sec += timeout / 1000;
188 ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
Michal Vasko58f31552016-01-19 12:39:15 +0100189
190 ret = pthread_mutex_timedlock(lock, &ts_timeout);
191
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100192 if (elapsed) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100193 clock_gettime(CLOCK_REALTIME, &ts_new);
194
Michal Vasko58f31552016-01-19 12:39:15 +0100195 *elapsed += (ts_new.tv_sec - ts_old.tv_sec) * 1000;
196 *elapsed += (ts_new.tv_nsec - ts_old.tv_nsec) / 1000000;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100197 }
198 } else if (!timeout) {
199 ret = pthread_mutex_trylock(lock);
200 } else { /* timeout == -1 */
201 ret = pthread_mutex_lock(lock);
Radek Krejci5686ff72015-10-09 13:33:56 +0200202 }
Radek Krejci5686ff72015-10-09 13:33:56 +0200203
Michal Vasko58f31552016-01-19 12:39:15 +0100204 if (ret == ETIMEDOUT) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100205 /* timeout */
206 return 0;
207 } else if (ret) {
208 /* error */
Michal Vaskod083db62016-01-19 10:31:29 +0100209 ERR("Mutex lock failed (%s).", strerror(errno));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100210 return -1;
211 }
212
213 /* ok */
214 return 1;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200215}
216
Radek Krejci695d4fa2015-10-22 13:23:54 +0200217API void
218nc_session_free(struct nc_session *session)
219{
220 int r, i;
Michal Vasko428087d2016-01-14 16:04:28 +0100221 int connected; /* flag to indicate whether the transport socket is still connected */
Michal Vaskob48aa812016-01-18 14:13:09 +0100222 int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200223 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100224 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100225 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200226 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100227 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200228 void *p;
229
Michal Vasko428087d2016-01-14 16:04:28 +0100230 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200231 return;
232 }
233
234 /* mark session for closing */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100235 if (session->ti_lock) {
236 do {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100237 r = nc_timedlock(session->ti_lock, 0, NULL);
238 } while (!r);
239 if (r == -1) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100240 return;
241 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200242 }
243
244 /* stop notifications loop if any */
245 if (session->notif) {
246 pthread_cancel(*session->notif);
247 pthread_join(*session->notif, NULL);
248 }
249
Michal Vasko428087d2016-01-14 16:04:28 +0100250 if ((session->side == NC_CLIENT) && (session->status == NC_STATUS_RUNNING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200251 /* cleanup message queues */
252 /* notifications */
Michal Vaskoad611702015-12-03 13:41:51 +0100253 for (contiter = session->notifs; contiter; ) {
254 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200255
Michal Vaskoad611702015-12-03 13:41:51 +0100256 p = contiter;
257 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200258 free(p);
259 }
260
261 /* rpc replies */
Michal Vaskoad611702015-12-03 13:41:51 +0100262 for (contiter = session->replies; contiter; ) {
263 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200264
Michal Vaskoad611702015-12-03 13:41:51 +0100265 p = contiter;
266 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200267 free(p);
268 }
269
270 /* send closing info to the other side */
271 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
272 if (!ietfnc) {
Michal Vasko428087d2016-01-14 16:04:28 +0100273 WRN("Session %u: missing ietf-netconf schema in context, unable to send <close-session>.", session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200274 } else {
275 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100276 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200277 lyd_free(close_rpc);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100278 switch (nc_read_msg_poll(session, NC_CLOSE_REPLY_TIMEOUT, &rpl)) {
Michal Vaskofad6e912015-10-26 15:37:22 +0100279 case NC_MSG_REPLY:
280 LY_TREE_FOR(rpl->child, child) {
281 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
282 break;
283 }
284 }
285 if (!child) {
Michal Vasko428087d2016-01-14 16:04:28 +0100286 WRN("Session %u: the reply to <close-session> was not <ok> as expected.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100287 }
Michal Vaskoad611702015-12-03 13:41:51 +0100288 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100289 break;
290 case NC_MSG_WOULDBLOCK:
Michal Vasko428087d2016-01-14 16:04:28 +0100291 WRN("Session %u: timeout for receiving a reply to <close-session> elapsed.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100292 break;
293 case NC_MSG_ERROR:
Michal Vaskod083db62016-01-19 10:31:29 +0100294 ERR("Session %u: failed to receive a reply to <close-session>.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100295 break;
296 default:
297 /* cannot happen */
298 break;
299 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200300 }
301
302 /* list of server's capabilities */
303 if (session->cpblts) {
304 for (i = 0; session->cpblts[i]; i++) {
305 lydict_remove(session->ctx, session->cpblts[i]);
306 }
307 free(session->cpblts);
308 }
309 }
310
311 session->status = NC_STATUS_CLOSING;
Michal Vasko428087d2016-01-14 16:04:28 +0100312 connected = nc_session_is_connected(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200313
314 /* transport implementation cleanup */
315 switch (session->ti_type) {
316 case NC_TI_FD:
317 /* nothing needed - file descriptors were provided by caller,
318 * so it is up to the caller to close them correctly
319 * TODO use callbacks
320 */
321 break;
322
Michal Vaskofb2fb762015-10-27 11:44:32 +0100323#ifdef ENABLE_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200324 case NC_TI_LIBSSH:
Michal Vasko428087d2016-01-14 16:04:28 +0100325 if (connected) {
326 ssh_channel_free(session->ti.libssh.channel);
327 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200328 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
329 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
330 * it.
331 */
332 if (!session->ti.libssh.next) {
Michal Vasko428087d2016-01-14 16:04:28 +0100333 if (connected) {
334 ssh_disconnect(session->ti.libssh.session);
335 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200336 ssh_free(session->ti.libssh.session);
337 } else {
338 /* multiple NETCONF sessions on a single SSH session */
339 multisession = 1;
340 /* remove the session from the list */
341 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100342 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200343 /* there will be only one session */
344 siter->ti.libssh.next = NULL;
345 } else {
346 /* there are still multiple sessions, keep the ring list */
347 siter->ti.libssh.next = session->ti.libssh.next;
348 }
349 }
350 break;
351#endif
352
353#ifdef ENABLE_TLS
354 case NC_TI_OPENSSL:
Michal Vasko428087d2016-01-14 16:04:28 +0100355 if (connected) {
356 SSL_shutdown(session->ti.tls);
357 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200358 SSL_free(session->ti.tls);
Michal Vasko06e22432016-01-15 10:30:06 +0100359
360 X509_free(session->tls_cert);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200361 break;
362#endif
Michal Vasko428087d2016-01-14 16:04:28 +0100363 case NC_TI_NONE:
364 ERRINT;
365 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200366 }
Michal Vasko428087d2016-01-14 16:04:28 +0100367
Michal Vasko11d142a2016-01-19 15:58:24 +0100368 if (session->side == NC_SERVER) {
369 nc_ctx_lock(-1, NULL);
370 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200371 lydict_remove(session->ctx, session->username);
372 lydict_remove(session->ctx, session->host);
Michal Vasko11d142a2016-01-19 15:58:24 +0100373 if (session->side == NC_SERVER) {
374 nc_ctx_unlock();
375 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200376
377 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100378 if (session->ti_lock) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100379 pthread_mutex_unlock(session->ti_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100380 if (!multisession) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100381 pthread_mutex_destroy(session->ti_lock);
382 free(session->ti_lock);
383 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200384 }
385
386 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
387 ly_ctx_destroy(session->ctx);
388 }
389
390 free(session);
391}
392
Michal Vasko086311b2016-01-08 09:53:11 +0100393static void
394add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count)
395{
396 if (*count == *size) {
397 *size += 5;
398 *cpblts = realloc(*cpblts, *size * sizeof **cpblts);
399 }
400
401 if (capab) {
402 (*cpblts)[*count] = lydict_insert(ctx, capab, 0);
403 } else {
404 (*cpblts)[*count] = NULL;
405 }
406 ++(*count);
407}
408
409static const char **
410create_cpblts(struct ly_ctx *ctx)
411{
412 struct lyd_node *child, *child2, *yanglib;
413 struct lyd_node_leaf_list **features = NULL, *ns = NULL, *rev = NULL, *name = NULL;
414 const char **cpblts;
415 const struct lys_module *mod;
Michal Vasko11d142a2016-01-19 15:58:24 +0100416 int size = 10, count, feat_count = 0, i, str_len;
Michal Vasko086311b2016-01-08 09:53:11 +0100417 char str[512];
418
Michal Vasko11d142a2016-01-19 15:58:24 +0100419 nc_ctx_lock(-1, NULL);
420
Michal Vasko086311b2016-01-08 09:53:11 +0100421 yanglib = ly_ctx_info(ctx);
422 if (!yanglib) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100423 nc_ctx_unlock();
Michal Vasko086311b2016-01-08 09:53:11 +0100424 return NULL;
425 }
426
427 cpblts = malloc(size * sizeof *cpblts);
428 cpblts[0] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0);
429 cpblts[1] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0);
430 count = 2;
431
432 /* capabilities */
433
434 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
435 if (mod) {
436 if (lys_features_state(mod, "writable-running") == 1) {
437 add_cpblt(ctx, "urn:ietf:params:netconf:writable-running:1.0", &cpblts, &size, &count);
438 }
439 if (lys_features_state(mod, "candidate") == 1) {
440 add_cpblt(ctx, "urn:ietf:params:netconf:candidate:1.0", &cpblts, &size, &count);
441 if (lys_features_state(mod, "confirmed-commit") == 1) {
442 add_cpblt(ctx, "urn:ietf:params:netconf:confirmed-commit:1.1", &cpblts, &size, &count);
443 }
444 }
445 if (lys_features_state(mod, "rollback-on-error") == 1) {
446 add_cpblt(ctx, "urn:ietf:params:netconf:rollback-on-error:1.0", &cpblts, &size, &count);
447 }
448 if (lys_features_state(mod, "validate") == 1) {
449 add_cpblt(ctx, "urn:ietf:params:netconf:validate:1.1", &cpblts, &size, &count);
450 }
451 if (lys_features_state(mod, "startup") == 1) {
452 add_cpblt(ctx, "urn:ietf:params:netconf:startup:1.0", &cpblts, &size, &count);
453 }
454 if (lys_features_state(mod, "url") == 1) {
455 add_cpblt(ctx, "urn:ietf:params:netconf:url:1.0", &cpblts, &size, &count);
456 }
457 if (lys_features_state(mod, "xpath") == 1) {
458 add_cpblt(ctx, "urn:ietf:params:netconf:xpath:1.0", &cpblts, &size, &count);
459 }
460 }
461
462 mod = ly_ctx_get_module(ctx, "ietf-netconf-with-defaults", NULL);
463 if (mod) {
464 if (!server_opts.wd_basic_mode) {
465 VRB("with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
466 } else {
467 strcpy(str, "urn:ietf:params:netconf:with-defaults:1.0");
468 switch (server_opts.wd_basic_mode) {
469 case NC_WD_ALL:
470 strcat(str, "?basic-mode=report-all");
471 break;
472 case NC_WD_TRIM:
473 strcat(str, "?basic-mode=trim");
474 break;
475 case NC_WD_EXPLICIT:
476 strcat(str, "?basic-mode=explicit");
477 break;
478 default:
Michal Vasko9e036d52016-01-08 10:49:26 +0100479 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100480 break;
481 }
482
483 if (server_opts.wd_also_supported) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100484 strcat(str, "&amp;also-supported=");
Michal Vasko086311b2016-01-08 09:53:11 +0100485 if (server_opts.wd_also_supported & NC_WD_ALL) {
486 strcat(str, "report-all,");
487 }
488 if (server_opts.wd_also_supported & NC_WD_ALL_TAG) {
489 strcat(str, "report-all-tagged,");
490 }
491 if (server_opts.wd_also_supported & NC_WD_TRIM) {
492 strcat(str, "trim,");
493 }
494 if (server_opts.wd_also_supported & NC_WD_EXPLICIT) {
495 strcat(str, "explicit,");
496 }
497 str[strlen(str) - 1] = '\0';
498
499 add_cpblt(ctx, str, &cpblts, &size, &count);
500 }
501 }
502 }
503
Michal Vasko1a38c862016-01-15 15:50:07 +0100504 mod = ly_ctx_get_module(ctx, "nc-notifications", NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100505 if (mod) {
506 add_cpblt(ctx, "urn:ietf:params:netconf:notification:1.0", &cpblts, &size, &count);
507 if (server_opts.interleave_capab) {
508 add_cpblt(ctx, "urn:ietf:params:netconf:interleave:1.0", &cpblts, &size, &count);
509 }
510 }
511
512 /* models */
513
514 LY_TREE_FOR(yanglib->child, child) {
515 if (!strcmp(child->schema->name, "module")) {
516 LY_TREE_FOR(child->child, child2) {
517 if (!strcmp(child2->schema->name, "namespace")) {
518 ns = (struct lyd_node_leaf_list *)child2;
519 } else if (!strcmp(child2->schema->name, "name")) {
520 name = (struct lyd_node_leaf_list *)child2;
521 } else if (!strcmp(child2->schema->name, "revision")) {
522 rev = (struct lyd_node_leaf_list *)child2;
523 } else if (!strcmp(child2->schema->name, "feature")) {
524 features = realloc(features, feat_count++ * sizeof *features);
525 features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
526 }
527 }
528
529 if (!ns || !name || !rev) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100530 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100531 continue;
532 }
533
Michal Vasko11d142a2016-01-19 15:58:24 +0100534 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 +0100535 if (feat_count) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100536 strcat(str, "&amp;features=");
Michal Vasko11d142a2016-01-19 15:58:24 +0100537 str_len += 14;
Michal Vasko086311b2016-01-08 09:53:11 +0100538 for (i = 0; i < feat_count; ++i) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100539 if (str_len + 1 + strlen(features[i]->value_str) >= 512) {
540 ERRINT;
541 break;
542 }
Michal Vasko086311b2016-01-08 09:53:11 +0100543 if (i) {
544 strcat(str, ",");
Michal Vasko11d142a2016-01-19 15:58:24 +0100545 ++str_len;
Michal Vasko086311b2016-01-08 09:53:11 +0100546 }
547 strcat(str, features[i]->value_str);
Michal Vasko11d142a2016-01-19 15:58:24 +0100548 str_len += strlen(features[i]->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100549 }
550 }
551
552 add_cpblt(ctx, str, &cpblts, &size, &count);
553
554 ns = NULL;
555 name = NULL;
556 rev = NULL;
557 free(features);
558 features = NULL;
559 feat_count = 0;
560 }
561 }
562
563 lyd_free(yanglib);
564
Michal Vasko11d142a2016-01-19 15:58:24 +0100565 nc_ctx_unlock();
566
Michal Vasko086311b2016-01-08 09:53:11 +0100567 /* ending NULL capability */
568 add_cpblt(ctx, NULL, &cpblts, &size, &count);
569
570 return cpblts;
571}
572
Radek Krejci695d4fa2015-10-22 13:23:54 +0200573static int
574parse_cpblts(struct lyxml_elem *xml, const char ***list)
575{
576 struct lyxml_elem *cpblt;
577 int ver = -1;
578 int i = 0;
579
580 if (list) {
581 /* get the storage for server's capabilities */
582 LY_TREE_FOR(xml->child, cpblt) {
583 i++;
584 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100585 /* last item remains NULL */
586 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200587 if (!*list) {
588 ERRMEM;
589 return -1;
590 }
591 i = 0;
592 }
593
594 LY_TREE_FOR(xml->child, cpblt) {
595 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
596 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
597 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
598 return -1;
599 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
600 continue;
601 }
602
603 /* detect NETCONF version */
604 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
605 ver = 0;
606 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
607 ver = 1;
608 }
609
610 /* store capabilities */
611 if (list) {
612 (*list)[i] = cpblt->content;
613 cpblt->content = NULL;
614 i++;
615 }
616 }
617
618 if (ver == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100619 ERR("Peer does not support a compatible NETCONF version.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200620 }
621
622 return ver;
623}
624
625static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100626nc_send_client_hello(struct nc_session *session)
Michal Vasko086311b2016-01-08 09:53:11 +0100627{
628 int r, i;
629 const char **cpblts;
630
Michal Vasko11d142a2016-01-19 15:58:24 +0100631 /* client side hello - send only NETCONF base capabilities */
632 cpblts = malloc(3 * sizeof *cpblts);
633 cpblts[0] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0);
634 cpblts[1] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0);
635 cpblts[2] = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100636
Michal Vasko11d142a2016-01-19 15:58:24 +0100637 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100638
Michal Vasko086311b2016-01-08 09:53:11 +0100639 for (i = 0; cpblts[i]; ++i) {
640 lydict_remove(session->ctx, cpblts[i]);
641 }
642 free(cpblts);
643
644 if (r) {
645 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100646 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100647
648 return NC_MSG_HELLO;
Michal Vasko086311b2016-01-08 09:53:11 +0100649}
650
651static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100652nc_send_server_hello(struct nc_session *session)
653{
654 int r, i;
655 const char **cpblts;
656
657 cpblts = create_cpblts(session->ctx);
658
659 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, &session->id);
660
661 nc_ctx_lock(-1, NULL);
662 for (i = 0; cpblts[i]; ++i) {
663 lydict_remove(session->ctx, cpblts[i]);
664 }
665 nc_ctx_unlock();
666 free(cpblts);
667
668 if (r) {
669 return NC_MSG_ERROR;
670 }
671
672 return NC_MSG_HELLO;
673}
674
675static NC_MSG_TYPE
676nc_recv_client_hello(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200677{
678 struct lyxml_elem *xml = NULL, *node;
679 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
680 int ver = -1;
681 char *str;
682 long long int id;
683 int flag = 0;
684
Michal Vasko05ba9df2016-01-13 14:40:27 +0100685 msgtype = nc_read_msg_poll(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200686
687 switch(msgtype) {
688 case NC_MSG_HELLO:
689 /* parse <hello> data */
Michal Vasko11d142a2016-01-19 15:58:24 +0100690 LY_TREE_FOR(xml->child, node) {
691 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
692 continue;
693 } else if (!strcmp(node->name, "session-id")) {
694 if (!node->content || !strlen(node->content)) {
695 ERR("No value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200696 goto error;
697 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100698 str = NULL;
699 id = strtoll(node->content, &str, 10);
700 if (*str || id < 1 || id > UINT32_MAX) {
701 ERR("Invalid value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200702 goto error;
703 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100704 session->id = (uint32_t)id;
705 continue;
706 } else if (strcmp(node->name, "capabilities")) {
707 ERR("Unexpected <%s> element in client's <hello>.", node->name);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200708 goto error;
709 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100710
711 if (flag) {
712 /* multiple capabilities elements */
713 ERR("Invalid <hello> message (multiple <capabilities> elements).");
714 goto error;
715 }
716 flag = 1;
717
718 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
719 goto error;
720 }
721 session->version = ver;
722 }
723
724 if (!session->id) {
725 ERR("Missing <session-id> in server's <hello>.");
726 goto error;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200727 }
728 break;
729 case NC_MSG_ERROR:
730 /* nothing special, just pass it out */
731 break;
732 default:
733 ERR("Unexpected message received instead of <hello>.");
734 msgtype = NC_MSG_ERROR;
735 }
736
737 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100738 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200739
740 return msgtype;
741
742error:
743 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100744 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200745
746 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +0200747}
748
Michal Vasko11d142a2016-01-19 15:58:24 +0100749static NC_MSG_TYPE
750nc_recv_server_hello(struct nc_session *session)
751{
752 struct lyxml_elem *xml = NULL, *node;
753 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
754 int ver = -1;
755 int flag = 0;
756
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100757 msgtype = nc_read_msg_poll(session, server_opts.hello_timeout * 1000, &xml);
Michal Vasko11d142a2016-01-19 15:58:24 +0100758
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100759 switch (msgtype) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100760 case NC_MSG_HELLO:
761 /* get know NETCONF version */
762 LY_TREE_FOR(xml->child, node) {
763 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
764 continue;
765 } else if (strcmp(node->name, "capabilities")) {
766 ERR("Unexpected <%s> element in client's <hello>.", node->name);
767 msgtype = NC_MSG_ERROR;
768 goto cleanup;
769 }
770
771 if (flag) {
772 /* multiple capabilities elements */
773 ERR("Invalid <hello> message (multiple <capabilities> elements).");
774 msgtype = NC_MSG_ERROR;
775 goto cleanup;
776 }
777 flag = 1;
778
779 if ((ver = parse_cpblts(node, NULL)) < 0) {
780 msgtype = NC_MSG_ERROR;
781 goto cleanup;
782 }
783 session->version = ver;
784 }
785 break;
786 case NC_MSG_ERROR:
787 /* nothing special, just pass it out */
788 break;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100789 case NC_MSG_WOULDBLOCK:
790 ERR("Client's <hello> timeout elapsed.");
791 msgtype = NC_MSG_ERROR;
792 break;
Michal Vasko11d142a2016-01-19 15:58:24 +0100793 default:
794 ERR("Unexpected message received instead of <hello>.");
795 msgtype = NC_MSG_ERROR;
796 }
797
798cleanup:
799 nc_ctx_lock(-1, NULL);
800 lyxml_free(session->ctx, xml);
801 nc_ctx_unlock();
802
803 return msgtype;
804}
805
Michal Vasko80cad7f2015-12-08 14:42:27 +0100806int
Michal Vasko086311b2016-01-08 09:53:11 +0100807nc_handshake(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100808{
Michal Vasko086311b2016-01-08 09:53:11 +0100809 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100810
Michal Vasko11d142a2016-01-19 15:58:24 +0100811 if (session->side == NC_CLIENT) {
812 type = nc_send_client_hello(session);
813 } else {
814 type = nc_send_server_hello(session);
815 }
816
Michal Vasko086311b2016-01-08 09:53:11 +0100817 if (type != NC_MSG_HELLO) {
818 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100819 }
820
Michal Vasko11d142a2016-01-19 15:58:24 +0100821 if (session->side == NC_CLIENT) {
822 type = nc_recv_client_hello(session);
823 } else {
824 type = nc_recv_server_hello(session);
825 }
826
Michal Vasko086311b2016-01-08 09:53:11 +0100827 if (type != NC_MSG_HELLO) {
828 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100829 }
830
Michal Vasko086311b2016-01-08 09:53:11 +0100831 return 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100832}
Michal Vasko086311b2016-01-08 09:53:11 +0100833
834#ifdef ENABLE_SSH
835
836API void
837nc_ssh_init(void)
838{
839 ssh_threads_set_callbacks(ssh_threads_get_pthread());
840 ssh_init();
841 ssh_set_log_level(verbose_level);
842}
843
844API void
845nc_ssh_destroy(void)
846{
847 ssh_finalize();
848}
849
850#endif /* ENABLE_SSH */
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100851
852#ifdef ENABLE_TLS
853
854static pthread_mutex_t *tls_locks;
855
856static void
Michal Vaskob48aa812016-01-18 14:13:09 +0100857tls_thread_locking_func(int mode, int n, const char *UNUSED(file), int UNUSED(line))
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100858{
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100859 if (mode & CRYPTO_LOCK) {
860 pthread_mutex_lock(tls_locks + n);
861 } else {
862 pthread_mutex_unlock(tls_locks + n);
863 }
864}
865
866static unsigned long
867tls_thread_id_func(void)
868{
869 return (unsigned long)pthread_self();
870}
871
872API void
873nc_tls_init(void)
874{
875 int i;
876
877 SSL_load_error_strings();
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100878 ERR_load_BIO_strings();
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100879 SSL_library_init();
880
881 tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks);
882 for (i = 0; i < CRYPTO_num_locks(); ++i) {
883 pthread_mutex_init(tls_locks + i, NULL);
884 }
885
886 CRYPTO_set_id_callback(tls_thread_id_func);
887 CRYPTO_set_locking_callback(tls_thread_locking_func);
888}
889
890API void
891nc_tls_destroy(void)
892{
893 int i;
894
895 CRYPTO_THREADID crypto_tid;
896
897 EVP_cleanup();
898 CRYPTO_cleanup_all_ex_data();
899 ERR_free_strings();
900 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
901 CRYPTO_THREADID_current(&crypto_tid);
902 ERR_remove_thread_state(&crypto_tid);
903
904 CRYPTO_set_id_callback(NULL);
905 CRYPTO_set_locking_callback(NULL);
906 for (i = 0; i < CRYPTO_num_locks(); ++i) {
907 pthread_mutex_destroy(tls_locks + i);
908 }
909 free(tls_locks);
910}
911
912#endif /* ENABLE_TLS */