blob: 494a8e0f7663a67e6d1afc657e1da25842ac4d6a [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 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Radek Krejci206fcd62015-10-07 15:42:48 +02007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020013 */
14
Michal Vasko18aeb5d2017-02-17 09:23:56 +010015#include <assert.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020016#include <errno.h>
Radek Krejci952eb862016-01-08 14:22:55 +010017#include <stdlib.h>
Michal Vasko3512e402016-01-28 16:22:34 +010018#include <string.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020019#include <pthread.h>
Radek Krejcid6b73e12016-07-15 12:00:23 +020020#include <sys/time.h>
Michal Vasko58f31552016-01-19 12:39:15 +010021#include <time.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020022#include <libyang/libyang.h>
23
Michal Vasko5e228792016-02-03 15:30:24 +010024#include "session.h"
Michal Vaskob48aa812016-01-18 14:13:09 +010025#include "libnetconf.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010026#include "session_server.h"
Michal Vaskob48aa812016-01-18 14:13:09 +010027
Radek Krejci53691be2016-02-22 13:58:37 +010028#ifdef NC_ENABLED_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020029
Michal Vasko086311b2016-01-08 09:53:11 +010030# include <libssh/libssh.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020031
Radek Krejci53691be2016-02-22 13:58:37 +010032#endif /* NC_ENABLED_SSH */
Radek Krejci695d4fa2015-10-22 13:23:54 +020033
Radek Krejci53691be2016-02-22 13:58:37 +010034#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoc14e3c82016-01-11 16:14:30 +010035
Michal Vasko5e228792016-02-03 15:30:24 +010036# include <openssl/engine.h>
37# include <openssl/conf.h>
Michal Vaskoc14e3c82016-01-11 16:14:30 +010038# include <openssl/err.h>
39
Radek Krejci53691be2016-02-22 13:58:37 +010040#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskoc14e3c82016-01-11 16:14:30 +010041
Michal Vasko086311b2016-01-08 09:53:11 +010042/* in seconds */
43#define NC_CLIENT_HELLO_TIMEOUT 60
Radek Krejci695d4fa2015-10-22 13:23:54 +020044
Michal Vasko05ba9df2016-01-13 14:40:27 +010045/* in milliseconds */
46#define NC_CLOSE_REPLY_TIMEOUT 200
47
Michal Vasko086311b2016-01-08 09:53:11 +010048extern struct nc_server_opts server_opts;
49
Radek Krejci7ac16052016-07-15 11:48:18 +020050int
51nc_gettimespec(struct timespec *ts)
52{
Michal Vasko0ef46df2016-09-21 14:03:18 +020053#ifdef CLOCK_REALTIME
Radek Krejci7ac16052016-07-15 11:48:18 +020054 return clock_gettime(CLOCK_REALTIME, ts);
55#else
56 int rc;
57 struct timeval tv;
58
59 rc = gettimeofday(&tv, NULL);
60 if (!rc) {
61 ts->tv_sec = (time_t)tv.tv_sec;
62 ts->tv_nsec = 1000L * (long)tv.tv_usec;
63 }
64 return rc;
65#endif
66}
67
Michal Vasko36c7be82017-02-22 13:37:59 +010068/* ts1 < ts2 -> +, ts1 > ts2 -> -, returns milliseconds */
69int32_t
Michal Vasko99f251b2017-01-11 11:31:46 +010070nc_difftimespec(struct timespec *ts1, struct timespec *ts2)
71{
Michal Vasko36c7be82017-02-22 13:37:59 +010072 int64_t nsec_diff = 0;
Michal Vasko99f251b2017-01-11 11:31:46 +010073
Michal Vaskoa82bd202017-03-03 14:07:29 +010074 nsec_diff += (((int64_t)ts2->tv_sec) - ((int64_t)ts1->tv_sec)) * 1000000000L;
75 nsec_diff += ((int64_t)ts2->tv_nsec) - ((int64_t)ts1->tv_nsec);
Michal Vasko99f251b2017-01-11 11:31:46 +010076
77 return (nsec_diff ? nsec_diff / 1000000L : 0);
78}
79
Michal Vasko36c7be82017-02-22 13:37:59 +010080void
81nc_addtimespec(struct timespec *ts, uint32_t msec)
82{
Michal Vasko0dfcd332017-03-03 13:14:39 +010083 ts->tv_sec += msec / 1000;
84 ts->tv_nsec += (msec % 1000) * 1000000L;
Michal Vasko36c7be82017-02-22 13:37:59 +010085
86 if (ts->tv_nsec > 1000000000L) {
87 ts->tv_sec += ts->tv_nsec / 1000000000L;
88 ts->tv_nsec %= 1000000000L;
89 }
90}
91
Radek Krejci28472922016-07-15 11:51:16 +020092#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
93int
94pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
95{
96 int rc;
97 struct timespec cur, dur;
98
99 /* Try to acquire the lock and, if we fail, sleep for 5ms. */
100 while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
101 nc_gettimespec(&cur);
102
103 if ((cur.tv_sec > abstime->tv_sec) || ((cur.tv_sec == abstime->tv_sec) && (cur.tv_nsec >= abstime->tv_nsec))) {
104 break;
105 }
106
107 dur.tv_sec = abstime->tv_sec - cur.tv_sec;
108 dur.tv_nsec = abstime->tv_nsec - cur.tv_nsec;
109 if (dur.tv_nsec < 0) {
110 dur.tv_sec--;
111 dur.tv_nsec += 1000000000;
112 }
113
114 if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000)) {
115 dur.tv_sec = 0;
116 dur.tv_nsec = 5000000;
117 }
118
119 nanosleep(&dur, NULL);
120 }
121
122 return rc;
123}
124#endif
125
Michal Vaskoade892d2017-02-22 13:40:35 +0100126struct nc_session *
127nc_new_session(int not_allocate_ti)
128{
129 struct nc_session *sess;
130
131 sess = calloc(1, sizeof *sess);
132 if (!sess) {
133 return NULL;
134 }
135
136 if (!not_allocate_ti) {
137 sess->ti_lock = malloc(sizeof *sess->ti_lock);
138 sess->ti_cond = malloc(sizeof *sess->ti_cond);
139 sess->ti_inuse = malloc(sizeof *sess->ti_inuse);
140 if (!sess->ti_lock || !sess->ti_cond || !sess->ti_inuse) {
141 free(sess->ti_lock);
142 free(sess->ti_cond);
143 free((int *)sess->ti_inuse);
144 free(sess);
145 return NULL;
146 }
147 }
148
149 return sess;
150}
151
Michal Vasko96164bf2016-01-21 15:41:58 +0100152/*
153 * @return 1 - success
154 * 0 - timeout
155 * -1 - error
156 */
157int
Michal Vaskoade892d2017-02-22 13:40:35 +0100158nc_session_lock(struct nc_session *session, int timeout, const char *func)
Michal Vasko96164bf2016-01-21 15:41:58 +0100159{
160 int ret;
Michal Vasko62be1ce2016-03-03 13:24:52 +0100161 struct timespec ts_timeout;
Michal Vasko96164bf2016-01-21 15:41:58 +0100162
163 if (timeout > 0) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200164 nc_gettimespec(&ts_timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100165
Michal Vasko96164bf2016-01-21 15:41:58 +0100166 ts_timeout.tv_sec += timeout / 1000;
167 ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
168
Michal Vaskoade892d2017-02-22 13:40:35 +0100169 /* LOCK */
170 ret = pthread_mutex_timedlock(session->ti_lock, &ts_timeout);
171 if (!ret) {
172 while (*session->ti_inuse) {
173 ret = pthread_cond_timedwait(session->ti_cond, session->ti_lock, &ts_timeout);
174 if (ret) {
175 pthread_mutex_unlock(session->ti_lock);
176 break;
177 }
178 }
179 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100180 } else if (!timeout) {
Michal Vaskoade892d2017-02-22 13:40:35 +0100181 if (*session->ti_inuse) {
182 /* immediate timeout */
183 return 0;
184 }
185
186 /* LOCK */
187 ret = pthread_mutex_trylock(session->ti_lock);
188 if (!ret) {
189 /* be extra careful, someone could have been faster */
190 if (*session->ti_inuse) {
191 pthread_mutex_unlock(session->ti_lock);
192 return 0;
193 }
Michal vasko2f8e4b52016-10-05 13:04:11 +0200194 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100195 } else { /* timeout == -1 */
Michal Vaskoade892d2017-02-22 13:40:35 +0100196 /* LOCK */
197 ret = pthread_mutex_lock(session->ti_lock);
198 if (!ret) {
199 while (*session->ti_inuse) {
200 ret = pthread_cond_wait(session->ti_cond, session->ti_lock);
201 if (ret) {
202 pthread_mutex_unlock(session->ti_lock);
203 break;
204 }
205 }
206 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100207 }
208
Michal Vaskoade892d2017-02-22 13:40:35 +0100209 if (ret) {
210 if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
211 /* timeout */
212 return 0;
213 }
214
Michal Vasko96164bf2016-01-21 15:41:58 +0100215 /* error */
Michal Vaskoade892d2017-02-22 13:40:35 +0100216 ERR("%s: failed to lock a session (%s).", func, strerror(ret));
Michal Vasko96164bf2016-01-21 15:41:58 +0100217 return -1;
218 }
219
220 /* ok */
Michal Vaskoade892d2017-02-22 13:40:35 +0100221 assert(*session->ti_inuse == 0);
222 *session->ti_inuse = 1;
223
224 /* UNLOCK */
225 ret = pthread_mutex_unlock(session->ti_lock);
226 if (ret) {
227 /* error */
228 ERR("%s: faile to unlock a session (%s).", func, strerror(ret));
229 return -1;
230 }
231
232 return 1;
233}
234
235int
236nc_session_unlock(struct nc_session *session, int timeout, const char *func)
237{
238 int ret;
239 struct timespec ts_timeout;
240
241 assert(*session->ti_inuse);
242
243 if (timeout > 0) {
244 nc_gettimespec(&ts_timeout);
245
246 ts_timeout.tv_sec += timeout / 1000;
247 ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
248
249 /* LOCK */
250 ret = pthread_mutex_timedlock(session->ti_lock, &ts_timeout);
251 } else if (!timeout) {
252 /* LOCK */
253 ret = pthread_mutex_trylock(session->ti_lock);
254 } else { /* timeout == -1 */
255 /* LOCK */
256 ret = pthread_mutex_lock(session->ti_lock);
257 }
258
259 if (ret && (ret != EBUSY) && (ret != ETIMEDOUT)) {
260 /* error */
261 ERR("%s: failed to lock a session (%s).", func, strerror(ret));
262 return -1;
263 } else if (ret) {
264 WRN("%s: session lock timeout, should not happen.");
265 }
266
267 *session->ti_inuse = 0;
268 pthread_cond_signal(session->ti_cond);
269
270 if (!ret) {
271 /* UNLOCK */
272 ret = pthread_mutex_unlock(session->ti_lock);
273 if (ret) {
274 /* error */
275 ERR("%s: failed to unlock a session (%s).", func, strerror(ret));
276 return -1;
277 }
278 }
279
Michal Vasko96164bf2016-01-21 15:41:58 +0100280 return 1;
281}
282
Michal Vasko8dadf782016-01-15 10:29:36 +0100283API NC_STATUS
284nc_session_get_status(const struct nc_session *session)
285{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100286 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200287 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100288 return 0;
289 }
290
Michal Vasko8dadf782016-01-15 10:29:36 +0100291 return session->status;
292}
293
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100294API NC_SESSION_TERM_REASON
295nc_session_get_termreason(const struct nc_session *session)
296{
297 if (!session) {
298 ERRARG("session");
299 return 0;
300 }
301
302 return session->term_reason;
303}
304
Michal Vasko8dadf782016-01-15 10:29:36 +0100305API uint32_t
306nc_session_get_id(const struct nc_session *session)
307{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100308 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200309 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100310 return 0;
311 }
312
Michal Vasko8dadf782016-01-15 10:29:36 +0100313 return session->id;
314}
315
Michal Vasko174fe8e2016-02-17 15:38:09 +0100316API int
317nc_session_get_version(const struct nc_session *session)
318{
319 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200320 ERRARG("session");
Michal Vasko174fe8e2016-02-17 15:38:09 +0100321 return -1;
322 }
323
324 return (session->version == NC_VERSION_10 ? 0 : 1);
325}
326
Michal Vasko8dadf782016-01-15 10:29:36 +0100327API NC_TRANSPORT_IMPL
328nc_session_get_ti(const struct nc_session *session)
329{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100330 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200331 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100332 return 0;
333 }
334
Michal Vasko8dadf782016-01-15 10:29:36 +0100335 return session->ti_type;
336}
337
338API const char *
339nc_session_get_username(const struct nc_session *session)
340{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100341 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200342 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100343 return NULL;
344 }
345
Michal Vasko8dadf782016-01-15 10:29:36 +0100346 return session->username;
347}
348
349API const char *
350nc_session_get_host(const struct nc_session *session)
351{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100352 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200353 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100354 return NULL;
355 }
356
Michal Vasko8dadf782016-01-15 10:29:36 +0100357 return session->host;
358}
359
360API uint16_t
361nc_session_get_port(const struct nc_session *session)
362{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100363 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200364 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100365 return 0;
366 }
367
Michal Vasko8dadf782016-01-15 10:29:36 +0100368 return session->port;
369}
370
Michal Vasko9a25e932016-02-01 10:36:42 +0100371API struct ly_ctx *
372nc_session_get_ctx(const struct nc_session *session)
373{
374 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200375 ERRARG("session");
Michal Vasko9a25e932016-02-01 10:36:42 +0100376 return NULL;
377 }
378
379 return session->ctx;
380}
381
Michal Vasko2cc4c682016-03-01 09:16:48 +0100382API void
383nc_session_set_data(struct nc_session *session, void *data)
384{
385 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200386 ERRARG("session");
Michal Vasko2cc4c682016-03-01 09:16:48 +0100387 return;
388 }
389
390 session->data = data;
391}
392
393API void *
394nc_session_get_data(const struct nc_session *session)
395{
396 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200397 ERRARG("session");
Michal Vasko2cc4c682016-03-01 09:16:48 +0100398 return NULL;
399 }
400
401 return session->data;
402}
403
Michal Vasko086311b2016-01-08 09:53:11 +0100404NC_MSG_TYPE
405nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200406{
Michal Vasko086311b2016-01-08 09:53:11 +0100407 int r;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200408
Michal Vasko086311b2016-01-08 09:53:11 +0100409 if (session->ctx != op->schema->module->ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100410 ERR("Session %u: RPC \"%s\" was created in different context than that of the session.",
411 session->id, op->schema->name);
Michal Vasko086311b2016-01-08 09:53:11 +0100412 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100413 }
414
Michal Vasko086311b2016-01-08 09:53:11 +0100415 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
416
417 if (r) {
418 return NC_MSG_ERROR;
419 }
420
421 return NC_MSG_RPC;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100422}
423
Radek Krejci695d4fa2015-10-22 13:23:54 +0200424API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100425nc_session_free(struct nc_session *session, void (*data_free)(void *))
Radek Krejci695d4fa2015-10-22 13:23:54 +0200426{
Michal Vasko9e99f012016-03-03 13:25:20 +0100427 int r, i, locked;
Michal Vasko428087d2016-01-14 16:04:28 +0100428 int connected; /* flag to indicate whether the transport socket is still connected */
Michal Vaskob48aa812016-01-18 14:13:09 +0100429 int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */
Michal Vaskoa8ad4482016-01-28 14:25:54 +0100430 pthread_t tid;
Michal Vasko4589bbe2016-01-29 09:41:30 +0100431 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100432 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100433 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200434 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100435 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200436 void *p;
437
Michal Vasko428087d2016-01-14 16:04:28 +0100438 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200439 return;
440 }
441
Michal Vasko86d357c2016-03-11 13:46:38 +0100442 /* stop notifications loop if any */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200443 if ((session->side == NC_CLIENT) && session->opts.client.ntf_tid) {
444 tid = *session->opts.client.ntf_tid;
445 free((pthread_t *)session->opts.client.ntf_tid);
446 session->opts.client.ntf_tid = NULL;
Michal Vasko86d357c2016-03-11 13:46:38 +0100447 /* the thread now knows it should quit */
448
449 pthread_join(tid, NULL);
450 }
451
Michal Vaskoadd4c792015-10-26 15:36:58 +0100452 if (session->ti_lock) {
Michal Vaskoade892d2017-02-22 13:40:35 +0100453 r = nc_session_lock(session, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100454 if (r == -1) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100455 return;
Michal Vasko9e99f012016-03-03 13:25:20 +0100456 } else if (!r) {
457 /* we failed to lock it, too bad */
458 locked = 0;
459 } else {
460 locked = 1;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100461 }
Michal Vasko5ecbf8c2016-03-29 16:05:22 +0200462 } else {
463 ERRINT;
464 return;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200465 }
466
Michal Vasko9e99f012016-03-03 13:25:20 +0100467 if ((session->side == NC_CLIENT) && (session->status == NC_STATUS_RUNNING) && locked) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200468 /* cleanup message queues */
469 /* notifications */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200470 for (contiter = session->opts.client.notifs; contiter; ) {
Michal Vaskoad611702015-12-03 13:41:51 +0100471 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200472
Michal Vaskoad611702015-12-03 13:41:51 +0100473 p = contiter;
474 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200475 free(p);
476 }
477
478 /* rpc replies */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200479 for (contiter = session->opts.client.replies; contiter; ) {
Michal Vaskoad611702015-12-03 13:41:51 +0100480 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200481
Michal Vaskoad611702015-12-03 13:41:51 +0100482 p = contiter;
483 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200484 free(p);
485 }
486
487 /* send closing info to the other side */
488 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
489 if (!ietfnc) {
Michal Vasko428087d2016-01-14 16:04:28 +0100490 WRN("Session %u: missing ietf-netconf schema in context, unable to send <close-session>.", session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200491 } else {
492 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100493 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200494 lyd_free(close_rpc);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100495 switch (nc_read_msg_poll(session, NC_CLOSE_REPLY_TIMEOUT, &rpl)) {
Michal Vaskofad6e912015-10-26 15:37:22 +0100496 case NC_MSG_REPLY:
497 LY_TREE_FOR(rpl->child, child) {
498 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
499 break;
500 }
501 }
502 if (!child) {
Michal Vasko428087d2016-01-14 16:04:28 +0100503 WRN("Session %u: the reply to <close-session> was not <ok> as expected.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100504 }
Michal Vaskoad611702015-12-03 13:41:51 +0100505 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100506 break;
507 case NC_MSG_WOULDBLOCK:
Michal Vasko428087d2016-01-14 16:04:28 +0100508 WRN("Session %u: timeout for receiving a reply to <close-session> elapsed.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100509 break;
510 case NC_MSG_ERROR:
Michal Vaskod083db62016-01-19 10:31:29 +0100511 ERR("Session %u: failed to receive a reply to <close-session>.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100512 break;
513 default:
514 /* cannot happen */
515 break;
516 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200517 }
518
519 /* list of server's capabilities */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200520 if (session->opts.client.cpblts) {
521 for (i = 0; session->opts.client.cpblts[i]; i++) {
522 lydict_remove(session->ctx, session->opts.client.cpblts[i]);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200523 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200524 free(session->opts.client.cpblts);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200525 }
526 }
527
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100528 if (session->data && data_free) {
529 data_free(session->data);
530 }
531
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200532 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
533 /* CH LOCK */
534 pthread_mutex_lock(session->opts.server.ch_lock);
535 }
536
Michal Vasko86d357c2016-03-11 13:46:38 +0100537 /* mark session for closing */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200538 session->status = NC_STATUS_CLOSING;
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200539
540 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
541 pthread_cond_signal(session->opts.server.ch_cond);
542
543 /* CH UNLOCK */
544 pthread_mutex_unlock(session->opts.server.ch_lock);
545 }
546
Michal Vasko428087d2016-01-14 16:04:28 +0100547 connected = nc_session_is_connected(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200548
549 /* transport implementation cleanup */
550 switch (session->ti_type) {
551 case NC_TI_FD:
552 /* nothing needed - file descriptors were provided by caller,
553 * so it is up to the caller to close them correctly
554 * TODO use callbacks
555 */
Michal Vasko3512e402016-01-28 16:22:34 +0100556 /* just to avoid compiler warning */
557 (void)connected;
Michal Vasko4589bbe2016-01-29 09:41:30 +0100558 (void)siter;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200559 break;
560
Radek Krejci53691be2016-02-22 13:58:37 +0100561#ifdef NC_ENABLED_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200562 case NC_TI_LIBSSH:
Michal Vasko428087d2016-01-14 16:04:28 +0100563 if (connected) {
564 ssh_channel_free(session->ti.libssh.channel);
565 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200566 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
567 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
568 * it.
569 */
Michal Vasko96164bf2016-01-21 15:41:58 +0100570 multisession = 0;
571 if (session->ti.libssh.next) {
572 for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
573 if (siter->status != NC_STATUS_STARTING) {
574 multisession = 1;
575 break;
576 }
577 }
578 }
579
580 if (!multisession) {
581 /* it's not multisession yet, but we still need to free the starting sessions */
582 if (session->ti.libssh.next) {
583 do {
584 siter = session->ti.libssh.next;
585 session->ti.libssh.next = siter->ti.libssh.next;
586
587 /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */
Michal Vasko96164bf2016-01-21 15:41:58 +0100588 lydict_remove(session->ctx, session->username);
589 lydict_remove(session->ctx, session->host);
Michal Vasko96164bf2016-01-21 15:41:58 +0100590 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
Radek Krejci4ba285b2016-02-04 17:34:06 +0100591 ly_ctx_destroy(session->ctx, NULL);
Michal Vasko96164bf2016-01-21 15:41:58 +0100592 }
593
594 free(siter);
595 } while (session->ti.libssh.next != session);
596 }
Michal Vasko428087d2016-01-14 16:04:28 +0100597 if (connected) {
598 ssh_disconnect(session->ti.libssh.session);
599 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200600 ssh_free(session->ti.libssh.session);
601 } else {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200602 /* remove the session from the list */
603 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100604 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200605 /* there will be only one session */
606 siter->ti.libssh.next = NULL;
607 } else {
608 /* there are still multiple sessions, keep the ring list */
609 siter->ti.libssh.next = session->ti.libssh.next;
610 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100611 /* change nc_sshcb_msg() argument, we need a RUNNING session and this one will be freed */
612 if (session->flags & NC_SESSION_SSH_MSG_CB) {
613 for (siter = session->ti.libssh.next; siter->status != NC_STATUS_RUNNING; siter = siter->ti.libssh.next) {
614 if (siter->ti.libssh.next == session) {
615 ERRINT;
616 break;
617 }
618 }
619 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, siter);
620 siter->flags |= NC_SESSION_SSH_MSG_CB;
621 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200622 }
623 break;
624#endif
625
Radek Krejci53691be2016-02-22 13:58:37 +0100626#ifdef NC_ENABLED_TLS
Radek Krejci695d4fa2015-10-22 13:23:54 +0200627 case NC_TI_OPENSSL:
Michal Vasko428087d2016-01-14 16:04:28 +0100628 if (connected) {
629 SSL_shutdown(session->ti.tls);
630 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200631 SSL_free(session->ti.tls);
Michal Vasko06e22432016-01-15 10:30:06 +0100632
Michal Vasko2e6defd2016-10-07 15:48:15 +0200633 if (session->side == NC_SERVER) {
634 X509_free(session->opts.server.client_cert);
635 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200636 break;
637#endif
Michal Vasko428087d2016-01-14 16:04:28 +0100638 case NC_TI_NONE:
639 ERRINT;
640 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200641 }
Michal Vasko428087d2016-01-14 16:04:28 +0100642
Radek Krejciac6d3472015-10-22 15:47:18 +0200643 lydict_remove(session->ctx, session->username);
644 lydict_remove(session->ctx, session->host);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200645
646 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100647 if (session->ti_lock) {
Michal Vasko9e99f012016-03-03 13:25:20 +0100648 if (locked) {
Michal Vaskoade892d2017-02-22 13:40:35 +0100649 nc_session_unlock(session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko9e99f012016-03-03 13:25:20 +0100650 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100651 if (!multisession) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100652 pthread_mutex_destroy(session->ti_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100653 pthread_cond_destroy(session->ti_cond);
Michal Vaskoadd4c792015-10-26 15:36:58 +0100654 free(session->ti_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100655 free(session->ti_cond);
656 free((int *)session->ti_inuse);
Michal Vaskoadd4c792015-10-26 15:36:58 +0100657 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200658 }
659
660 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
Radek Krejci4ba285b2016-02-04 17:34:06 +0100661 ly_ctx_destroy(session->ctx, NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200662 }
663
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200664 if (session->side == NC_SERVER) {
665 if (session->opts.server.ch_cond) {
666 pthread_cond_destroy(session->opts.server.ch_cond);
667 free(session->opts.server.ch_cond);
668 }
669 if (session->opts.server.ch_lock) {
670 pthread_mutex_destroy(session->opts.server.ch_lock);
671 free(session->opts.server.ch_lock);
672 }
673 }
674
Radek Krejci695d4fa2015-10-22 13:23:54 +0200675 free(session);
676}
677
Michal Vasko086311b2016-01-08 09:53:11 +0100678static void
679add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count)
680{
Radek Krejci658782b2016-12-04 22:04:55 +0100681 size_t len;
682 int i;
683 char *p;
684
685 if (capab) {
686 /* check if already present */
687 p = strchr(capab, '?');
688 if (p) {
689 len = p - capab;
690 } else {
691 len = strlen(capab);
692 }
693 for (i = 0; i < *count; i++) {
fanchanghu5244bf42017-03-13 16:27:48 +0800694 if (!strncmp((*cpblts)[i], capab, len) && ((*cpblts)[i][len] == '\0' || (*cpblts)[i][len] == '?')) {
Radek Krejci658782b2016-12-04 22:04:55 +0100695 /* already present, do not duplicate it */
696 return;
697 }
698 }
699 }
700
701 /* add another capability */
Michal Vasko086311b2016-01-08 09:53:11 +0100702 if (*count == *size) {
703 *size += 5;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100704 *cpblts = nc_realloc(*cpblts, *size * sizeof **cpblts);
705 if (!(*cpblts)) {
706 ERRMEM;
707 return;
708 }
Michal Vasko086311b2016-01-08 09:53:11 +0100709 }
710
711 if (capab) {
712 (*cpblts)[*count] = lydict_insert(ctx, capab, 0);
713 } else {
714 (*cpblts)[*count] = NULL;
715 }
716 ++(*count);
717}
718
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200719API const char **
720nc_server_get_cpblts(struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100721{
722 struct lyd_node *child, *child2, *yanglib;
Michal Vaskodd9fe652016-09-14 09:24:32 +0200723 struct lyd_node_leaf_list **features = NULL, **deviations = NULL, *ns = NULL, *rev = NULL, *name = NULL, *module_set_id = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100724 const char **cpblts;
725 const struct lys_module *mod;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200726 int size = 10, count, feat_count = 0, dev_count = 0, i, str_len;
Radek Krejci658782b2016-12-04 22:04:55 +0100727 unsigned int u;
Michal Vasko2e47ef92016-06-20 10:03:24 +0200728#define NC_CPBLT_BUF_LEN 512
729 char str[NC_CPBLT_BUF_LEN];
Michal Vasko086311b2016-01-08 09:53:11 +0100730
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200731 if (!ctx) {
732 ERRARG("ctx");
733 return NULL;
734 }
735
Michal Vasko086311b2016-01-08 09:53:11 +0100736 yanglib = ly_ctx_info(ctx);
737 if (!yanglib) {
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200738 ERR("Failed to get ietf-yang-library data from the context.");
Michal Vasko086311b2016-01-08 09:53:11 +0100739 return NULL;
740 }
741
742 cpblts = malloc(size * sizeof *cpblts);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100743 if (!cpblts) {
744 ERRMEM;
745 return NULL;
746 }
Michal Vasko086311b2016-01-08 09:53:11 +0100747 cpblts[0] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0);
748 cpblts[1] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0);
749 count = 2;
750
751 /* capabilities */
752
753 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
754 if (mod) {
755 if (lys_features_state(mod, "writable-running") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100756 add_cpblt(ctx, "urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100757 }
758 if (lys_features_state(mod, "candidate") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100759 add_cpblt(ctx, "urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100760 if (lys_features_state(mod, "confirmed-commit") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100761 add_cpblt(ctx, "urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100762 }
763 }
764 if (lys_features_state(mod, "rollback-on-error") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100765 add_cpblt(ctx, "urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100766 }
767 if (lys_features_state(mod, "validate") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100768 add_cpblt(ctx, "urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100769 }
770 if (lys_features_state(mod, "startup") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100771 add_cpblt(ctx, "urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100772 }
773 if (lys_features_state(mod, "url") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100774 add_cpblt(ctx, "urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100775 }
776 if (lys_features_state(mod, "xpath") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100777 add_cpblt(ctx, "urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100778 }
779 }
780
781 mod = ly_ctx_get_module(ctx, "ietf-netconf-with-defaults", NULL);
782 if (mod) {
783 if (!server_opts.wd_basic_mode) {
784 VRB("with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
785 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100786 strcpy(str, "urn:ietf:params:netconf:capability:with-defaults:1.0");
Michal Vasko086311b2016-01-08 09:53:11 +0100787 switch (server_opts.wd_basic_mode) {
788 case NC_WD_ALL:
789 strcat(str, "?basic-mode=report-all");
790 break;
791 case NC_WD_TRIM:
792 strcat(str, "?basic-mode=trim");
793 break;
794 case NC_WD_EXPLICIT:
795 strcat(str, "?basic-mode=explicit");
796 break;
797 default:
Michal Vasko9e036d52016-01-08 10:49:26 +0100798 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100799 break;
800 }
801
802 if (server_opts.wd_also_supported) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200803 strcat(str, "&also-supported=");
Michal Vasko086311b2016-01-08 09:53:11 +0100804 if (server_opts.wd_also_supported & NC_WD_ALL) {
805 strcat(str, "report-all,");
806 }
807 if (server_opts.wd_also_supported & NC_WD_ALL_TAG) {
808 strcat(str, "report-all-tagged,");
809 }
810 if (server_opts.wd_also_supported & NC_WD_TRIM) {
811 strcat(str, "trim,");
812 }
813 if (server_opts.wd_also_supported & NC_WD_EXPLICIT) {
814 strcat(str, "explicit,");
815 }
816 str[strlen(str) - 1] = '\0';
817
818 add_cpblt(ctx, str, &cpblts, &size, &count);
819 }
820 }
821 }
822
Radek Krejci658782b2016-12-04 22:04:55 +0100823 /* other capabilities */
824 for (u = 0; u < server_opts.capabilities_count; u++) {
825 add_cpblt(ctx, server_opts.capabilities[u], &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100826 }
827
828 /* models */
Michal Vasko086311b2016-01-08 09:53:11 +0100829 LY_TREE_FOR(yanglib->child, child) {
Michal Vaskodd9fe652016-09-14 09:24:32 +0200830 if (!module_set_id) {
831 if (strcmp(child->prev->schema->name, "module-set-id")) {
832 ERRINT;
Michal Vaskoa8a66b62016-10-05 14:14:19 +0200833 free(cpblts);
834 free(deviations);
Michal Vaskodd9fe652016-09-14 09:24:32 +0200835 return NULL;
836 }
837 module_set_id = (struct lyd_node_leaf_list *)child->prev;
838 }
Michal Vasko086311b2016-01-08 09:53:11 +0100839 if (!strcmp(child->schema->name, "module")) {
840 LY_TREE_FOR(child->child, child2) {
841 if (!strcmp(child2->schema->name, "namespace")) {
842 ns = (struct lyd_node_leaf_list *)child2;
843 } else if (!strcmp(child2->schema->name, "name")) {
844 name = (struct lyd_node_leaf_list *)child2;
845 } else if (!strcmp(child2->schema->name, "revision")) {
846 rev = (struct lyd_node_leaf_list *)child2;
847 } else if (!strcmp(child2->schema->name, "feature")) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100848 features = nc_realloc(features, ++feat_count * sizeof *features);
849 if (!features) {
850 ERRMEM;
851 free(cpblts);
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200852 free(deviations);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100853 return NULL;
854 }
Michal Vasko086311b2016-01-08 09:53:11 +0100855 features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200856 } else if (!strcmp(child2->schema->name, "deviation")) {
857 deviations = nc_realloc(deviations, ++dev_count * sizeof *deviations);
858 if (!deviations) {
859 ERRMEM;
860 free(cpblts);
861 free(features);
862 return NULL;
863 }
Michal Vasko086311b2016-01-08 09:53:11 +0100864 }
865 }
866
867 if (!ns || !name || !rev) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100868 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100869 continue;
870 }
871
Radek Krejcidf7ba522016-03-01 16:05:25 +0100872 str_len = sprintf(str, "%s?module=%s%s%s", ns->value_str, name->value_str,
Michal Vasko2e47ef92016-06-20 10:03:24 +0200873 rev->value_str[0] ? "&revision=" : "", rev->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100874 if (feat_count) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200875 strcat(str, "&features=");
876 str_len += 10;
Michal Vasko086311b2016-01-08 09:53:11 +0100877 for (i = 0; i < feat_count; ++i) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200878 if (str_len + 1 + strlen(features[i]->value_str) >= NC_CPBLT_BUF_LEN) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100879 ERRINT;
880 break;
881 }
Michal Vasko086311b2016-01-08 09:53:11 +0100882 if (i) {
883 strcat(str, ",");
Michal Vasko11d142a2016-01-19 15:58:24 +0100884 ++str_len;
Michal Vasko086311b2016-01-08 09:53:11 +0100885 }
886 strcat(str, features[i]->value_str);
Michal Vasko11d142a2016-01-19 15:58:24 +0100887 str_len += strlen(features[i]->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100888 }
889 }
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200890 if (dev_count) {
891 strcat(str, "&deviations=");
892 str_len += 12;
893 for (i = 0; i < dev_count; ++i) {
894 if (str_len + 1 + strlen(deviations[i]->value_str) >= NC_CPBLT_BUF_LEN) {
895 ERRINT;
896 break;
897 }
898 if (i) {
899 strcat(str, ",");
900 ++str_len;
901 }
902 strcat(str, deviations[i]->value_str);
903 str_len += strlen(deviations[i]->value_str);
904 }
905 }
Michal Vaskodd9fe652016-09-14 09:24:32 +0200906 if (!strcmp(name->value_str, "ietf-yang-library")) {
907 str_len += sprintf(str + str_len, "&module-set-id=%s", module_set_id->value_str);
908 }
Michal Vasko086311b2016-01-08 09:53:11 +0100909
910 add_cpblt(ctx, str, &cpblts, &size, &count);
911
912 ns = NULL;
913 name = NULL;
914 rev = NULL;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200915 if (features || feat_count) {
916 free(features);
917 features = NULL;
918 feat_count = 0;
919 }
920 if (deviations || dev_count) {
921 free(deviations);
922 deviations = NULL;
923 dev_count = 0;
924 }
Michal Vasko086311b2016-01-08 09:53:11 +0100925 }
926 }
927
928 lyd_free(yanglib);
929
930 /* ending NULL capability */
931 add_cpblt(ctx, NULL, &cpblts, &size, &count);
932
933 return cpblts;
934}
935
Radek Krejci695d4fa2015-10-22 13:23:54 +0200936static int
937parse_cpblts(struct lyxml_elem *xml, const char ***list)
938{
939 struct lyxml_elem *cpblt;
940 int ver = -1;
941 int i = 0;
942
943 if (list) {
944 /* get the storage for server's capabilities */
945 LY_TREE_FOR(xml->child, cpblt) {
946 i++;
947 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100948 /* last item remains NULL */
949 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200950 if (!*list) {
951 ERRMEM;
952 return -1;
953 }
954 i = 0;
955 }
956
957 LY_TREE_FOR(xml->child, cpblt) {
958 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
959 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
960 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
961 return -1;
962 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
963 continue;
964 }
965
966 /* detect NETCONF version */
967 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
968 ver = 0;
969 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
970 ver = 1;
971 }
972
973 /* store capabilities */
974 if (list) {
975 (*list)[i] = cpblt->content;
976 cpblt->content = NULL;
977 i++;
978 }
979 }
980
981 if (ver == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100982 ERR("Peer does not support a compatible NETCONF version.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200983 }
984
985 return ver;
986}
987
988static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100989nc_send_client_hello(struct nc_session *session)
Michal Vasko086311b2016-01-08 09:53:11 +0100990{
991 int r, i;
992 const char **cpblts;
993
Michal Vasko11d142a2016-01-19 15:58:24 +0100994 /* client side hello - send only NETCONF base capabilities */
995 cpblts = malloc(3 * sizeof *cpblts);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100996 if (!cpblts) {
997 ERRMEM;
998 return NC_MSG_ERROR;
999 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001000 cpblts[0] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0);
1001 cpblts[1] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0);
1002 cpblts[2] = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001003
Michal Vasko11d142a2016-01-19 15:58:24 +01001004 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001005
Michal Vasko086311b2016-01-08 09:53:11 +01001006 for (i = 0; cpblts[i]; ++i) {
1007 lydict_remove(session->ctx, cpblts[i]);
1008 }
1009 free(cpblts);
1010
1011 if (r) {
1012 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001013 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001014
1015 return NC_MSG_HELLO;
Michal Vasko086311b2016-01-08 09:53:11 +01001016}
1017
1018static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +01001019nc_send_server_hello(struct nc_session *session)
1020{
1021 int r, i;
1022 const char **cpblts;
1023
Michal Vasko4ffa3b22016-05-24 16:36:25 +02001024 cpblts = nc_server_get_cpblts(session->ctx);
Michal Vasko11d142a2016-01-19 15:58:24 +01001025
1026 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, &session->id);
1027
Michal Vasko11d142a2016-01-19 15:58:24 +01001028 for (i = 0; cpblts[i]; ++i) {
1029 lydict_remove(session->ctx, cpblts[i]);
1030 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001031 free(cpblts);
1032
1033 if (r) {
1034 return NC_MSG_ERROR;
1035 }
1036
1037 return NC_MSG_HELLO;
1038}
1039
1040static NC_MSG_TYPE
1041nc_recv_client_hello(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +02001042{
1043 struct lyxml_elem *xml = NULL, *node;
1044 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1045 int ver = -1;
1046 char *str;
1047 long long int id;
1048 int flag = 0;
1049
Michal Vasko05ba9df2016-01-13 14:40:27 +01001050 msgtype = nc_read_msg_poll(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001051
1052 switch(msgtype) {
1053 case NC_MSG_HELLO:
1054 /* parse <hello> data */
Michal Vasko11d142a2016-01-19 15:58:24 +01001055 LY_TREE_FOR(xml->child, node) {
1056 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
1057 continue;
1058 } else if (!strcmp(node->name, "session-id")) {
1059 if (!node->content || !strlen(node->content)) {
1060 ERR("No value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +02001061 goto error;
1062 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001063 str = NULL;
1064 id = strtoll(node->content, &str, 10);
1065 if (*str || id < 1 || id > UINT32_MAX) {
1066 ERR("Invalid value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +02001067 goto error;
1068 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001069 session->id = (uint32_t)id;
1070 continue;
1071 } else if (strcmp(node->name, "capabilities")) {
1072 ERR("Unexpected <%s> element in client's <hello>.", node->name);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001073 goto error;
1074 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001075
1076 if (flag) {
1077 /* multiple capabilities elements */
1078 ERR("Invalid <hello> message (multiple <capabilities> elements).");
1079 goto error;
1080 }
1081 flag = 1;
1082
Michal Vasko2e6defd2016-10-07 15:48:15 +02001083 if ((ver = parse_cpblts(node, &session->opts.client.cpblts)) < 0) {
Michal Vasko11d142a2016-01-19 15:58:24 +01001084 goto error;
1085 }
1086 session->version = ver;
1087 }
1088
1089 if (!session->id) {
1090 ERR("Missing <session-id> in server's <hello>.");
1091 goto error;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001092 }
1093 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001094 case NC_MSG_WOULDBLOCK:
1095 ERR("Server's <hello> timeout elapsed.");
1096 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001097 case NC_MSG_ERROR:
1098 /* nothing special, just pass it out */
1099 break;
1100 default:
1101 ERR("Unexpected message received instead of <hello>.");
1102 msgtype = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +02001103 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001104 }
1105
1106 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +01001107 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001108
1109 return msgtype;
1110
1111error:
1112 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +01001113 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001114
1115 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +02001116}
1117
Michal Vasko11d142a2016-01-19 15:58:24 +01001118static NC_MSG_TYPE
1119nc_recv_server_hello(struct nc_session *session)
1120{
1121 struct lyxml_elem *xml = NULL, *node;
Michal Vasko71090fc2016-05-24 16:37:28 +02001122 NC_MSG_TYPE msgtype;
Michal Vasko11d142a2016-01-19 15:58:24 +01001123 int ver = -1;
1124 int flag = 0;
1125
Michal Vaskoadb850e2016-01-20 14:06:32 +01001126 msgtype = nc_read_msg_poll(session, (server_opts.hello_timeout ? server_opts.hello_timeout * 1000 : -1), &xml);
Michal Vasko11d142a2016-01-19 15:58:24 +01001127
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001128 switch (msgtype) {
Michal Vasko11d142a2016-01-19 15:58:24 +01001129 case NC_MSG_HELLO:
1130 /* get know NETCONF version */
1131 LY_TREE_FOR(xml->child, node) {
1132 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
1133 continue;
1134 } else if (strcmp(node->name, "capabilities")) {
1135 ERR("Unexpected <%s> element in client's <hello>.", node->name);
Michal Vasko71090fc2016-05-24 16:37:28 +02001136 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001137 goto cleanup;
1138 }
1139
1140 if (flag) {
1141 /* multiple capabilities elements */
1142 ERR("Invalid <hello> message (multiple <capabilities> elements).");
Michal Vasko71090fc2016-05-24 16:37:28 +02001143 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001144 goto cleanup;
1145 }
1146 flag = 1;
1147
1148 if ((ver = parse_cpblts(node, NULL)) < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001149 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001150 goto cleanup;
1151 }
1152 session->version = ver;
1153 }
1154 break;
1155 case NC_MSG_ERROR:
1156 /* nothing special, just pass it out */
1157 break;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001158 case NC_MSG_WOULDBLOCK:
1159 ERR("Client's <hello> timeout elapsed.");
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001160 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001161 default:
1162 ERR("Unexpected message received instead of <hello>.");
1163 msgtype = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +02001164 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001165 }
1166
1167cleanup:
Michal Vasko11d142a2016-01-19 15:58:24 +01001168 lyxml_free(session->ctx, xml);
Michal Vasko11d142a2016-01-19 15:58:24 +01001169
1170 return msgtype;
1171}
1172
Michal Vasko71090fc2016-05-24 16:37:28 +02001173NC_MSG_TYPE
Michal Vasko086311b2016-01-08 09:53:11 +01001174nc_handshake(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001175{
Michal Vasko086311b2016-01-08 09:53:11 +01001176 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001177
Michal Vasko11d142a2016-01-19 15:58:24 +01001178 if (session->side == NC_CLIENT) {
1179 type = nc_send_client_hello(session);
1180 } else {
1181 type = nc_send_server_hello(session);
1182 }
1183
Michal Vasko086311b2016-01-08 09:53:11 +01001184 if (type != NC_MSG_HELLO) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001185 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001186 }
1187
Michal Vasko11d142a2016-01-19 15:58:24 +01001188 if (session->side == NC_CLIENT) {
1189 type = nc_recv_client_hello(session);
1190 } else {
1191 type = nc_recv_server_hello(session);
1192 }
1193
Michal Vasko71090fc2016-05-24 16:37:28 +02001194 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001195}
Michal Vasko086311b2016-01-08 09:53:11 +01001196
Radek Krejci53691be2016-02-22 13:58:37 +01001197#ifdef NC_ENABLED_SSH
Michal Vasko086311b2016-01-08 09:53:11 +01001198
Michal Vasko8f0c0282016-02-29 10:17:14 +01001199static void
Michal Vasko086311b2016-01-08 09:53:11 +01001200nc_ssh_init(void)
1201{
1202 ssh_threads_set_callbacks(ssh_threads_get_pthread());
1203 ssh_init();
Michal Vasko086311b2016-01-08 09:53:11 +01001204}
1205
Michal Vasko8f0c0282016-02-29 10:17:14 +01001206static void
Michal Vasko086311b2016-01-08 09:53:11 +01001207nc_ssh_destroy(void)
1208{
Michal Vasko8f0c0282016-02-29 10:17:14 +01001209 FIPS_mode_set(0);
Michal Vasko5e228792016-02-03 15:30:24 +01001210 ENGINE_cleanup();
1211 CONF_modules_unload(1);
Michal Vaskob6e37262016-02-25 14:49:00 +01001212 nc_thread_destroy();
Michal Vasko086311b2016-01-08 09:53:11 +01001213 ssh_finalize();
1214}
1215
Radek Krejci53691be2016-02-22 13:58:37 +01001216#endif /* NC_ENABLED_SSH */
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001217
Radek Krejci53691be2016-02-22 13:58:37 +01001218#ifdef NC_ENABLED_TLS
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001219
Michal Vasko770b4362017-02-17 14:44:18 +01001220#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
1221
Michal Vaskof0c92c02016-01-29 09:41:45 +01001222struct CRYPTO_dynlock_value {
1223 pthread_mutex_t lock;
1224};
1225
Michal Vaskof0c92c02016-01-29 09:41:45 +01001226static struct CRYPTO_dynlock_value *
1227tls_dyn_create_func(const char *UNUSED(file), int UNUSED(line))
1228{
1229 struct CRYPTO_dynlock_value *value;
1230
1231 value = malloc(sizeof *value);
1232 if (!value) {
1233 ERRMEM;
1234 return NULL;
1235 }
1236 pthread_mutex_init(&value->lock, NULL);
1237
1238 return value;
1239}
1240
1241static void
1242tls_dyn_lock_func(int mode, struct CRYPTO_dynlock_value *l, const char *UNUSED(file), int UNUSED(line))
1243{
1244 /* mode can also be CRYPTO_READ or CRYPTO_WRITE, but all the examples
1245 * I found ignored this fact, what do I know... */
1246 if (mode & CRYPTO_LOCK) {
1247 pthread_mutex_lock(&l->lock);
1248 } else {
1249 pthread_mutex_unlock(&l->lock);
1250 }
1251}
1252
1253static void
1254tls_dyn_destroy_func(struct CRYPTO_dynlock_value *l, const char *UNUSED(file), int UNUSED(line))
1255{
1256 pthread_mutex_destroy(&l->lock);
1257 free(l);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001258}
Michal Vasko770b4362017-02-17 14:44:18 +01001259#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001260
Michal Vasko8f0c0282016-02-29 10:17:14 +01001261#endif /* NC_ENABLED_TLS */
1262
1263#if defined(NC_ENABLED_TLS) && !defined(NC_ENABLED_SSH)
1264
Michal Vasko770b4362017-02-17 14:44:18 +01001265#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vasko8f0c0282016-02-29 10:17:14 +01001266static pthread_mutex_t *tls_locks;
1267
1268static void
1269tls_thread_locking_func(int mode, int n, const char *UNUSED(file), int UNUSED(line))
1270{
1271 if (mode & CRYPTO_LOCK) {
1272 pthread_mutex_lock(tls_locks + n);
1273 } else {
1274 pthread_mutex_unlock(tls_locks + n);
1275 }
1276}
1277
1278static void
1279tls_thread_id_func(CRYPTO_THREADID *tid)
1280{
1281 CRYPTO_THREADID_set_numeric(tid, (unsigned long)pthread_self());
1282}
Michal Vasko770b4362017-02-17 14:44:18 +01001283#endif
Michal Vasko8f0c0282016-02-29 10:17:14 +01001284
1285static void
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001286nc_tls_init(void)
1287{
1288 int i;
1289
1290 SSL_load_error_strings();
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001291 ERR_load_BIO_strings();
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001292 SSL_library_init();
1293
Michal Vasko770b4362017-02-17 14:44:18 +01001294#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001295 tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001296 if (!tls_locks) {
1297 ERRMEM;
1298 return;
1299 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001300 for (i = 0; i < CRYPTO_num_locks(); ++i) {
1301 pthread_mutex_init(tls_locks + i, NULL);
1302 }
1303
Michal Vaskof0c92c02016-01-29 09:41:45 +01001304 CRYPTO_THREADID_set_callback(tls_thread_id_func);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001305 CRYPTO_set_locking_callback(tls_thread_locking_func);
Michal Vaskof0c92c02016-01-29 09:41:45 +01001306
1307 CRYPTO_set_dynlock_create_callback(tls_dyn_create_func);
1308 CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_func);
1309 CRYPTO_set_dynlock_destroy_callback(tls_dyn_destroy_func);
Michal Vasko770b4362017-02-17 14:44:18 +01001310#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001311}
1312
Michal Vasko8f0c0282016-02-29 10:17:14 +01001313static void
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001314nc_tls_destroy(void)
1315{
1316 int i;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001317
Michal Vasko8f0c0282016-02-29 10:17:14 +01001318 FIPS_mode_set(0);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001319 CRYPTO_cleanup_all_ex_data();
Michal Vaskob6e37262016-02-25 14:49:00 +01001320 nc_thread_destroy();
Michal Vasko5e228792016-02-03 15:30:24 +01001321 EVP_cleanup();
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001322 ERR_free_strings();
Michal Vasko770b4362017-02-17 14:44:18 +01001323#if OPENSSL_VERSION_NUMBER < 0x10002000L // < 1.0.2
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001324 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
Michal Vasko770b4362017-02-17 14:44:18 +01001325#elif OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
1326 SSL_COMP_free_compression_methods();
Jan Kundrát47e9e1a2016-11-21 09:41:59 +01001327#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001328
Michal Vasko770b4362017-02-17 14:44:18 +01001329#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vaskob6e37262016-02-25 14:49:00 +01001330 CRYPTO_THREADID_set_callback(NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001331 CRYPTO_set_locking_callback(NULL);
1332 for (i = 0; i < CRYPTO_num_locks(); ++i) {
1333 pthread_mutex_destroy(tls_locks + i);
1334 }
1335 free(tls_locks);
Michal Vaskof0c92c02016-01-29 09:41:45 +01001336
1337 CRYPTO_set_dynlock_create_callback(NULL);
1338 CRYPTO_set_dynlock_lock_callback(NULL);
1339 CRYPTO_set_dynlock_destroy_callback(NULL);
Michal Vasko770b4362017-02-17 14:44:18 +01001340#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001341}
1342
Michal Vasko8f0c0282016-02-29 10:17:14 +01001343#endif /* NC_ENABLED_TLS && !NC_ENABLED_SSH */
Michal Vasko5e228792016-02-03 15:30:24 +01001344
Radek Krejci53691be2016-02-22 13:58:37 +01001345#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko5e228792016-02-03 15:30:24 +01001346
Michal Vasko8f0c0282016-02-29 10:17:14 +01001347static void
Michal Vasko5e228792016-02-03 15:30:24 +01001348nc_ssh_tls_init(void)
1349{
1350 SSL_load_error_strings();
1351 ERR_load_BIO_strings();
1352 SSL_library_init();
1353
1354 nc_ssh_init();
1355
Michal Vasko770b4362017-02-17 14:44:18 +01001356#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vasko5e228792016-02-03 15:30:24 +01001357 CRYPTO_set_dynlock_create_callback(tls_dyn_create_func);
1358 CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_func);
1359 CRYPTO_set_dynlock_destroy_callback(tls_dyn_destroy_func);
Michal Vasko770b4362017-02-17 14:44:18 +01001360#endif
Michal Vasko5e228792016-02-03 15:30:24 +01001361}
1362
Michal Vasko8f0c0282016-02-29 10:17:14 +01001363static void
Michal Vasko5e228792016-02-03 15:30:24 +01001364nc_ssh_tls_destroy(void)
1365{
1366 ERR_free_strings();
Michal Vasko770b4362017-02-17 14:44:18 +01001367#if OPENSSL_VERSION_NUMBER < 0x10002000L // < 1.0.2
Michal Vasko5e228792016-02-03 15:30:24 +01001368 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
Michal Vasko770b4362017-02-17 14:44:18 +01001369#elif OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
1370 SSL_COMP_free_compression_methods();
Jan Kundrát47e9e1a2016-11-21 09:41:59 +01001371#endif
Michal Vasko5e228792016-02-03 15:30:24 +01001372
1373 nc_ssh_destroy();
1374
Michal Vasko770b4362017-02-17 14:44:18 +01001375#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vasko5e228792016-02-03 15:30:24 +01001376 CRYPTO_set_dynlock_create_callback(NULL);
1377 CRYPTO_set_dynlock_lock_callback(NULL);
1378 CRYPTO_set_dynlock_destroy_callback(NULL);
Michal Vasko770b4362017-02-17 14:44:18 +01001379#endif
Michal Vasko5e228792016-02-03 15:30:24 +01001380}
1381
Radek Krejci53691be2016-02-22 13:58:37 +01001382#endif /* NC_ENABLED_SSH && NC_ENABLED_TLS */
Michal Vasko8f0c0282016-02-29 10:17:14 +01001383
1384#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
1385
1386API void
1387nc_thread_destroy(void)
1388{
Michal Vasko8f0c0282016-02-29 10:17:14 +01001389 /* caused data-races and seems not neccessary for avoiding valgrind reachable memory */
1390 //CRYPTO_cleanup_all_ex_data();
1391
Michal Vasko770b4362017-02-17 14:44:18 +01001392#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
1393 CRYPTO_THREADID crypto_tid;
1394
Michal Vasko8f0c0282016-02-29 10:17:14 +01001395 CRYPTO_THREADID_current(&crypto_tid);
1396 ERR_remove_thread_state(&crypto_tid);
Michal Vasko770b4362017-02-17 14:44:18 +01001397#endif
Michal Vasko8f0c0282016-02-29 10:17:14 +01001398}
1399
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001400#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
1401
1402void
Michal Vasko8f0c0282016-02-29 10:17:14 +01001403nc_init(void)
1404{
1405#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1406 nc_ssh_tls_init();
1407#elif defined(NC_ENABLED_SSH)
1408 nc_ssh_init();
1409#elif defined(NC_ENABLED_TLS)
1410 nc_tls_init();
1411#endif
1412}
1413
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001414void
Michal Vasko8f0c0282016-02-29 10:17:14 +01001415nc_destroy(void)
1416{
1417#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1418 nc_ssh_tls_destroy();
1419#elif defined(NC_ENABLED_SSH)
1420 nc_ssh_destroy();
1421#elif defined(NC_ENABLED_TLS)
1422 nc_tls_destroy();
1423#endif
1424}