blob: 3851a1d1f19db11290dac6fa1daf0ccf389bba7e [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 - general session functions
Radek Krejci206fcd62015-10-07 15:42:48 +02005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
Michal Vaskod8a74192023-02-06 15:51:50 +01007 * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
Radek Krejci206fcd62015-10-07 15:42:48 +02008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020014 */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +020015#define _GNU_SOURCE
Radek Krejci206fcd62015-10-07 15:42:48 +020016
Michal Vasko18aeb5d2017-02-17 09:23:56 +010017#include <assert.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020018#include <ctype.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020019#include <errno.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020020#include <libyang/libyang.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020021#include <pthread.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/socket.h>
25#include <sys/time.h>
26#include <sys/types.h>
Michal Vasko58f31552016-01-19 12:39:15 +010027#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020028#include <unistd.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020029
Michal Vasko9e8ac262020-04-07 13:06:45 +020030#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020031#include "config.h"
32#include "log_p.h"
33#include "netconf.h"
34#include "session_p.h"
Michal Vaskob48aa812016-01-18 14:13:09 +010035
roman2eab4742023-06-06 10:00:26 +020036#ifdef NC_ENABLED_SSH_TLS
Radek Krejci206fcd62015-10-07 15:42:48 +020037
roman2eab4742023-06-06 10:00:26 +020038#include <libssh/libssh.h>
39#include <openssl/conf.h>
40#include <openssl/err.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020041
roman2eab4742023-06-06 10:00:26 +020042#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskoc14e3c82016-01-11 16:14:30 +010043
Michal Vasko086311b2016-01-08 09:53:11 +010044/* in seconds */
45#define NC_CLIENT_HELLO_TIMEOUT 60
fanchanghu75888b62017-08-01 19:51:20 +080046#define NC_SERVER_HELLO_TIMEOUT 60
Radek Krejci695d4fa2015-10-22 13:23:54 +020047
Michal Vasko05ba9df2016-01-13 14:40:27 +010048/* in milliseconds */
49#define NC_CLOSE_REPLY_TIMEOUT 200
50
Michal Vasko086311b2016-01-08 09:53:11 +010051extern struct nc_server_opts server_opts;
52
Michal Vaskod8a74192023-02-06 15:51:50 +010053void
54nc_timeouttime_get(struct timespec *ts, uint32_t add_ms)
Radek Krejci7ac16052016-07-15 11:48:18 +020055{
Michal Vaskod8a74192023-02-06 15:51:50 +010056 if (clock_gettime(COMPAT_CLOCK_ID, ts) == -1) {
57 ERR(NULL, "clock_gettime() failed (%s).", strerror(errno));
58 return;
59 }
60
61 if (!add_ms) {
62 return;
63 }
64
65 assert((ts->tv_nsec >= 0) && (ts->tv_nsec < 1000000000L));
66
67 ts->tv_sec += add_ms / 1000;
68 ts->tv_nsec += (add_ms % 1000) * 1000000L;
69
70 if (ts->tv_nsec >= 1000000000L) {
71 ++ts->tv_sec;
72 ts->tv_nsec -= 1000000000L;
73 } else if (ts->tv_nsec < 0) {
74 --ts->tv_sec;
75 ts->tv_nsec += 1000000000L;
76 }
77
78 assert((ts->tv_nsec >= 0) && (ts->tv_nsec < 1000000000L));
79}
80
81int32_t
82nc_timeouttime_cur_diff(const struct timespec *ts)
83{
84 struct timespec cur;
85 int64_t nsec_diff = 0;
86
87 nc_timeouttime_get(&cur, 0);
88
89 nsec_diff += (((int64_t)ts->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L;
90 nsec_diff += ((int64_t)ts->tv_nsec) - ((int64_t)cur.tv_nsec);
91
92 return nsec_diff / 1000000L;
93}
94
95void
96nc_realtime_get(struct timespec *ts)
97{
roman6ece9c52022-06-22 09:29:17 +020098 if (clock_gettime(CLOCK_REALTIME, ts)) {
Michal Vaskod8a74192023-02-06 15:51:50 +010099 ERR(NULL, "clock_gettime() failed (%s).", strerror(errno));
100 return;
roman6ece9c52022-06-22 09:29:17 +0200101 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100102}
103
roman2eab4742023-06-06 10:00:26 +0200104#ifdef NC_ENABLED_SSH_TLS
105
Michal Vaskoddce1212019-05-24 09:58:49 +0200106const char *
roman3f9b65c2023-06-05 14:26:58 +0200107nc_privkey_format_to_str(NC_PRIVKEY_FORMAT format)
Michal Vaskoddce1212019-05-24 09:58:49 +0200108{
roman3f9b65c2023-06-05 14:26:58 +0200109 switch (format) {
110 case NC_PRIVKEY_FORMAT_RSA:
Michal Vaskoddce1212019-05-24 09:58:49 +0200111 return "RSA";
roman3f9b65c2023-06-05 14:26:58 +0200112 case NC_PRIVKEY_FORMAT_EC:
Michal Vaskoddce1212019-05-24 09:58:49 +0200113 return "EC";
roman3f9b65c2023-06-05 14:26:58 +0200114 case NC_PRIVKEY_FORMAT_X509:
roman44600f42023-04-28 15:54:27 +0200115 return NULL;
roman3f9b65c2023-06-05 14:26:58 +0200116 case NC_PRIVKEY_FORMAT_OPENSSH:
117 return "OPENSSH";
Michal Vaskoddce1212019-05-24 09:58:49 +0200118 default:
roman3f9b65c2023-06-05 14:26:58 +0200119 return NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +0200120 }
Michal Vaskoddce1212019-05-24 09:58:49 +0200121}
122
roman2eab4742023-06-06 10:00:26 +0200123#endif /* NC_ENABLED_SSH_TLS */
124
Michal Vaskobe52dc22018-10-17 09:28:17 +0200125int
romanc1d2b092023-02-02 08:58:27 +0100126nc_sock_configure_keepalive(int sock, struct nc_keepalives *ka)
Michal Vaskobe52dc22018-10-17 09:28:17 +0200127{
128 int opt;
129
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200130 opt = ka->enabled;
Michal Vaskobe52dc22018-10-17 09:28:17 +0200131 if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200132 ERR(NULL, "Could not set SO_KEEPALIVE option (%s).", strerror(errno));
Michal Vaskobe52dc22018-10-17 09:28:17 +0200133 return -1;
134 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200135 if (!ka->enabled) {
136 return 0;
137 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200138
139#ifdef TCP_KEEPIDLE
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200140 opt = ka->idle_time;
Michal Vaskobe52dc22018-10-17 09:28:17 +0200141 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200142 ERR(NULL, "Setsockopt failed (%s).", strerror(errno));
Michal Vaskobe52dc22018-10-17 09:28:17 +0200143 return -1;
144 }
145#endif
146
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200147#ifdef TCP_KEEPCNT
148 opt = ka->max_probes;
149 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200150 ERR(NULL, "Setsockopt failed (%s).", strerror(errno));
Michal Vaskobe52dc22018-10-17 09:28:17 +0200151 return -1;
152 }
153#endif
154
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200155#ifdef TCP_KEEPINTVL
156 opt = ka->probe_interval;
157 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200158 ERR(NULL, "Setsockopt failed (%s).", strerror(errno));
Michal Vaskobe52dc22018-10-17 09:28:17 +0200159 return -1;
160 }
161#endif
162
163 return 0;
164}
165
Michal Vaskoade892d2017-02-22 13:40:35 +0100166struct nc_session *
Michal Vasko131120a2018-05-29 15:44:02 +0200167nc_new_session(NC_SIDE side, int shared_ti)
Michal Vaskoade892d2017-02-22 13:40:35 +0100168{
169 struct nc_session *sess;
170
171 sess = calloc(1, sizeof *sess);
172 if (!sess) {
173 return NULL;
174 }
175
Michal Vasko131120a2018-05-29 15:44:02 +0200176 sess->side = side;
177
178 if (side == NC_SERVER) {
Michal Vaskodf68e7e2022-04-21 11:04:00 +0200179 pthread_mutex_init(&sess->opts.server.ntf_status_lock, NULL);
Michal Vaskoacf98472021-02-04 15:33:57 +0100180 pthread_mutex_init(&sess->opts.server.rpc_lock, NULL);
181 pthread_cond_init(&sess->opts.server.rpc_cond, NULL);
Michal Vaskoacf98472021-02-04 15:33:57 +0100182
183 pthread_mutex_init(&sess->opts.server.ch_lock, NULL);
184 pthread_cond_init(&sess->opts.server.ch_cond, NULL);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +0200185 } else {
186 pthread_mutex_init(&sess->opts.client.msgs_lock, NULL);
Michal Vasko131120a2018-05-29 15:44:02 +0200187 }
188
189 if (!shared_ti) {
190 sess->io_lock = malloc(sizeof *sess->io_lock);
191 if (!sess->io_lock) {
192 goto error;
193 }
194 pthread_mutex_init(sess->io_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +0100195 }
196
197 return sess;
Michal Vasko131120a2018-05-29 15:44:02 +0200198
199error:
Michal Vasko131120a2018-05-29 15:44:02 +0200200 free(sess);
201 return NULL;
Michal Vaskoade892d2017-02-22 13:40:35 +0100202}
203
Michal Vasko96164bf2016-01-21 15:41:58 +0100204/*
205 * @return 1 - success
206 * 0 - timeout
207 * -1 - error
208 */
209int
Michal Vasko131120a2018-05-29 15:44:02 +0200210nc_session_rpc_lock(struct nc_session *session, int timeout, const char *func)
Michal Vasko96164bf2016-01-21 15:41:58 +0100211{
212 int ret;
Michal Vasko62be1ce2016-03-03 13:24:52 +0100213 struct timespec ts_timeout;
Michal Vasko96164bf2016-01-21 15:41:58 +0100214
Michal Vasko131120a2018-05-29 15:44:02 +0200215 if (session->side != NC_SERVER) {
216 ERRINT;
217 return -1;
218 }
219
Michal Vasko96164bf2016-01-21 15:41:58 +0100220 if (timeout > 0) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100221 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100222
Michal Vaskoade892d2017-02-22 13:40:35 +0100223 /* LOCK */
Michal Vaskod8a74192023-02-06 15:51:50 +0100224 ret = pthread_mutex_clocklock(&session->opts.server.rpc_lock, COMPAT_CLOCK_ID, &ts_timeout);
Michal Vaskoade892d2017-02-22 13:40:35 +0100225 if (!ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100226 while (session->opts.server.rpc_inuse) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100227 ret = pthread_cond_clockwait(&session->opts.server.rpc_cond, &session->opts.server.rpc_lock,
228 COMPAT_CLOCK_ID, &ts_timeout);
Michal Vaskoade892d2017-02-22 13:40:35 +0100229 if (ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100230 pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100231 break;
232 }
233 }
234 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100235 } else if (!timeout) {
Michal Vaskoade892d2017-02-22 13:40:35 +0100236 /* LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100237 ret = pthread_mutex_trylock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100238 if (!ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100239 if (session->opts.server.rpc_inuse) {
240 pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100241 return 0;
242 }
Michal vasko2f8e4b52016-10-05 13:04:11 +0200243 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100244 } else { /* timeout == -1 */
Michal Vaskoade892d2017-02-22 13:40:35 +0100245 /* LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100246 ret = pthread_mutex_lock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100247 if (!ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100248 while (session->opts.server.rpc_inuse) {
249 ret = pthread_cond_wait(&session->opts.server.rpc_cond, &session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100250 if (ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100251 pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100252 break;
253 }
254 }
255 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100256 }
257
Michal Vaskoade892d2017-02-22 13:40:35 +0100258 if (ret) {
259 if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
260 /* timeout */
261 return 0;
262 }
263
Michal Vasko96164bf2016-01-21 15:41:58 +0100264 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200265 ERR(session, "%s: failed to RPC lock a session (%s).", func, strerror(ret));
Michal Vasko96164bf2016-01-21 15:41:58 +0100266 return -1;
267 }
268
269 /* ok */
Michal Vaskoacf98472021-02-04 15:33:57 +0100270 assert(session->opts.server.rpc_inuse == 0);
271 session->opts.server.rpc_inuse = 1;
Michal Vaskoade892d2017-02-22 13:40:35 +0100272
273 /* UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100274 ret = pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100275 if (ret) {
276 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200277 ERR(session, "%s: failed to RPC unlock a session (%s).", func, strerror(ret));
Michal Vaskoade892d2017-02-22 13:40:35 +0100278 return -1;
279 }
280
281 return 1;
282}
283
284int
Michal Vasko131120a2018-05-29 15:44:02 +0200285nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func)
Michal Vaskoade892d2017-02-22 13:40:35 +0100286{
287 int ret;
288 struct timespec ts_timeout;
289
Michal Vasko131120a2018-05-29 15:44:02 +0200290 if (session->side != NC_SERVER) {
291 ERRINT;
292 return -1;
293 }
294
Michal Vaskoacf98472021-02-04 15:33:57 +0100295 assert(session->opts.server.rpc_inuse);
Michal Vaskoade892d2017-02-22 13:40:35 +0100296
297 if (timeout > 0) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100298 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vaskoade892d2017-02-22 13:40:35 +0100299
300 /* LOCK */
Michal Vaskod8a74192023-02-06 15:51:50 +0100301 ret = pthread_mutex_clocklock(&session->opts.server.rpc_lock, COMPAT_CLOCK_ID, &ts_timeout);
Michal Vaskoade892d2017-02-22 13:40:35 +0100302 } else if (!timeout) {
303 /* LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100304 ret = pthread_mutex_trylock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100305 } else { /* timeout == -1 */
306 /* LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100307 ret = pthread_mutex_lock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100308 }
309
310 if (ret && (ret != EBUSY) && (ret != ETIMEDOUT)) {
311 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200312 ERR(session, "%s: failed to RPC lock a session (%s).", func, strerror(ret));
Michal Vaskoade892d2017-02-22 13:40:35 +0100313 return -1;
314 } else if (ret) {
Michal Vasko69e98752022-12-14 14:20:17 +0100315 WRN(session, "%s: session RPC lock timeout, should not happen.", func);
Michal Vaskoade892d2017-02-22 13:40:35 +0100316 }
317
Michal Vaskoacf98472021-02-04 15:33:57 +0100318 session->opts.server.rpc_inuse = 0;
319 pthread_cond_signal(&session->opts.server.rpc_cond);
Michal Vaskoade892d2017-02-22 13:40:35 +0100320
321 if (!ret) {
322 /* UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100323 ret = pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100324 if (ret) {
325 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200326 ERR(session, "%s: failed to RPC unlock a session (%s).", func, strerror(ret));
Michal Vaskoade892d2017-02-22 13:40:35 +0100327 return -1;
328 }
329 }
330
Michal Vasko96164bf2016-01-21 15:41:58 +0100331 return 1;
332}
333
Michal Vasko131120a2018-05-29 15:44:02 +0200334int
335nc_session_io_lock(struct nc_session *session, int timeout, const char *func)
336{
337 int ret;
338 struct timespec ts_timeout;
339
340 if (timeout > 0) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100341 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko131120a2018-05-29 15:44:02 +0200342
Michal Vaskod8a74192023-02-06 15:51:50 +0100343 ret = pthread_mutex_clocklock(session->io_lock, COMPAT_CLOCK_ID, &ts_timeout);
Michal Vasko131120a2018-05-29 15:44:02 +0200344 } else if (!timeout) {
345 ret = pthread_mutex_trylock(session->io_lock);
346 } else { /* timeout == -1 */
Robin Jarry54ea2962018-10-10 10:33:40 +0200347 ret = pthread_mutex_lock(session->io_lock);
Michal Vasko131120a2018-05-29 15:44:02 +0200348 }
349
350 if (ret) {
351 if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
352 /* timeout */
353 return 0;
354 }
355
356 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200357 ERR(session, "%s: failed to IO lock a session (%s).", func, strerror(ret));
Michal Vasko131120a2018-05-29 15:44:02 +0200358 return -1;
359 }
360
361 return 1;
362}
363
364int
365nc_session_io_unlock(struct nc_session *session, const char *func)
366{
367 int ret;
368
369 ret = pthread_mutex_unlock(session->io_lock);
370 if (ret) {
371 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200372 ERR(session, "%s: failed to IO unlock a session (%s).", func, strerror(ret));
Michal Vasko131120a2018-05-29 15:44:02 +0200373 return -1;
374 }
375
376 return 1;
377}
378
Michal Vasko01130bd2021-08-26 11:47:38 +0200379int
380nc_session_client_msgs_lock(struct nc_session *session, int *timeout, const char *func)
381{
382 int ret;
383 int32_t diff_msec;
roman6ece9c52022-06-22 09:29:17 +0200384 struct timespec ts_timeout, ts_start;
Michal Vasko01130bd2021-08-26 11:47:38 +0200385
386 assert(session->side == NC_CLIENT);
387
388 if (*timeout > 0) {
389 /* get current time */
Michal Vaskod8a74192023-02-06 15:51:50 +0100390 nc_timeouttime_get(&ts_start, 0);
Michal Vasko01130bd2021-08-26 11:47:38 +0200391
Michal Vaskod8a74192023-02-06 15:51:50 +0100392 nc_timeouttime_get(&ts_timeout, *timeout);
Michal Vasko01130bd2021-08-26 11:47:38 +0200393
Michal Vaskod8a74192023-02-06 15:51:50 +0100394 ret = pthread_mutex_clocklock(&session->opts.client.msgs_lock, COMPAT_CLOCK_ID, &ts_timeout);
Michal Vasko01130bd2021-08-26 11:47:38 +0200395 if (!ret) {
396 /* update timeout based on what was elapsed */
Michal Vaskod8a74192023-02-06 15:51:50 +0100397 diff_msec = nc_timeouttime_cur_diff(&ts_start);
Michal Vasko01130bd2021-08-26 11:47:38 +0200398 *timeout -= diff_msec;
399 }
400 } else if (!*timeout) {
401 ret = pthread_mutex_trylock(&session->opts.client.msgs_lock);
402 } else { /* timeout == -1 */
403 ret = pthread_mutex_lock(&session->opts.client.msgs_lock);
404 }
405
406 if (ret) {
407 if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
408 /* timeout */
409 return 0;
410 }
411
412 /* error */
413 ERR(session, "%s: failed to MSGS lock a session (%s).", func, strerror(ret));
414 return -1;
415 }
416
417 return 1;
418}
419
420int
421nc_session_client_msgs_unlock(struct nc_session *session, const char *func)
422{
423 int ret;
424
425 assert(session->side == NC_CLIENT);
426
427 ret = pthread_mutex_unlock(&session->opts.client.msgs_lock);
428 if (ret) {
429 /* error */
430 ERR(session, "%s: failed to MSGS unlock a session (%s).", func, strerror(ret));
431 return -1;
432 }
433
434 return 1;
435}
436
Michal Vasko8dadf782016-01-15 10:29:36 +0100437API NC_STATUS
438nc_session_get_status(const struct nc_session *session)
439{
roman40672412023-05-04 11:10:22 +0200440 NC_CHECK_ARG_RET(session, session, NC_STATUS_ERR);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100441
Michal Vasko8dadf782016-01-15 10:29:36 +0100442 return session->status;
443}
444
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100445API NC_SESSION_TERM_REASON
Michal Vasko142cfea2017-08-07 10:12:11 +0200446nc_session_get_term_reason(const struct nc_session *session)
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100447{
roman40672412023-05-04 11:10:22 +0200448 NC_CHECK_ARG_RET(session, session, NC_SESSION_TERM_ERR);
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100449
450 return session->term_reason;
451}
452
Michal Vasko8dadf782016-01-15 10:29:36 +0100453API uint32_t
Michal Vasko142cfea2017-08-07 10:12:11 +0200454nc_session_get_killed_by(const struct nc_session *session)
455{
roman40672412023-05-04 11:10:22 +0200456 NC_CHECK_ARG_RET(session, session, 0);
Michal Vasko142cfea2017-08-07 10:12:11 +0200457
458 return session->killed_by;
459}
460
461API uint32_t
Michal Vasko8dadf782016-01-15 10:29:36 +0100462nc_session_get_id(const struct nc_session *session)
463{
roman40672412023-05-04 11:10:22 +0200464 NC_CHECK_ARG_RET(session, session, 0);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100465
Michal Vasko8dadf782016-01-15 10:29:36 +0100466 return session->id;
467}
468
Michal Vasko174fe8e2016-02-17 15:38:09 +0100469API int
470nc_session_get_version(const struct nc_session *session)
471{
roman40672412023-05-04 11:10:22 +0200472 NC_CHECK_ARG_RET(session, session, -1);
Michal Vasko174fe8e2016-02-17 15:38:09 +0100473
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200474 return session->version == NC_VERSION_10 ? 0 : 1;
Michal Vasko174fe8e2016-02-17 15:38:09 +0100475}
476
Michal Vasko8dadf782016-01-15 10:29:36 +0100477API NC_TRANSPORT_IMPL
478nc_session_get_ti(const struct nc_session *session)
479{
roman40672412023-05-04 11:10:22 +0200480 NC_CHECK_ARG_RET(session, session, 0);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100481
Michal Vasko8dadf782016-01-15 10:29:36 +0100482 return session->ti_type;
483}
484
485API const char *
486nc_session_get_username(const struct nc_session *session)
487{
roman40672412023-05-04 11:10:22 +0200488 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100489
Michal Vasko8dadf782016-01-15 10:29:36 +0100490 return session->username;
491}
492
493API const char *
494nc_session_get_host(const struct nc_session *session)
495{
roman40672412023-05-04 11:10:22 +0200496 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100497
Michal Vasko8dadf782016-01-15 10:29:36 +0100498 return session->host;
499}
500
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200501API const char *
502nc_session_get_path(const struct nc_session *session)
503{
roman40672412023-05-04 11:10:22 +0200504 NC_CHECK_ARG_RET(session, session, NULL);
505
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200506 if (session->ti_type != NC_TI_UNIX) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200507 return NULL;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200508 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200509
510 return session->path;
511}
512
Michal Vasko8dadf782016-01-15 10:29:36 +0100513API uint16_t
514nc_session_get_port(const struct nc_session *session)
515{
roman40672412023-05-04 11:10:22 +0200516 NC_CHECK_ARG_RET(session, session, 0);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100517
Michal Vasko8dadf782016-01-15 10:29:36 +0100518 return session->port;
519}
520
Michal Vasko93224072021-11-09 12:14:28 +0100521API const struct ly_ctx *
Michal Vasko9a25e932016-02-01 10:36:42 +0100522nc_session_get_ctx(const struct nc_session *session)
523{
roman40672412023-05-04 11:10:22 +0200524 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vasko9a25e932016-02-01 10:36:42 +0100525
526 return session->ctx;
527}
528
Michal Vasko2cc4c682016-03-01 09:16:48 +0100529API void
530nc_session_set_data(struct nc_session *session, void *data)
531{
532 if (!session) {
roman40672412023-05-04 11:10:22 +0200533 ERRARG(NULL, "session");
Michal Vasko2cc4c682016-03-01 09:16:48 +0100534 return;
535 }
536
537 session->data = data;
538}
539
540API void *
541nc_session_get_data(const struct nc_session *session)
542{
roman40672412023-05-04 11:10:22 +0200543 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vasko2cc4c682016-03-01 09:16:48 +0100544
545 return session->data;
546}
547
Michal Vaskodc96bb92023-03-28 08:52:48 +0200548API int
549nc_session_is_callhome(const struct nc_session *session)
550{
roman40672412023-05-04 11:10:22 +0200551 NC_CHECK_ARG_RET(session, session, 0);
Michal Vaskodc96bb92023-03-28 08:52:48 +0200552
553 if (session->flags & NC_SESSION_CALLHOME) {
554 return 1;
555 }
556
557 return 0;
558}
559
Michal Vasko086311b2016-01-08 09:53:11 +0100560NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200561nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200562{
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100563 if (session->ctx != LYD_CTX(op)) {
564 ERR(session, "RPC \"%s\" was created in different context than that of the session.", LYD_NAME(op));
Michal Vasko086311b2016-01-08 09:53:11 +0100565 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100566 }
567
Michal Vasko131120a2018-05-29 15:44:02 +0200568 return nc_write_msg_io(session, io_timeout, NC_MSG_RPC, op, NULL);
Michal Vasko7df39ec2015-12-09 15:26:24 +0100569}
570
Michal Vaskod4da3632022-05-25 11:49:10 +0200571/**
572 * @brief Send \<close-session\> and read the reply on a session.
573 *
574 * @param[in] session Closing NETCONF session.
575 */
576static void
577nc_session_free_close_session(struct nc_session *session)
578{
579 struct ly_in *msg;
580 struct lyd_node *close_rpc, *envp;
581 const struct lys_module *ietfnc;
582
583 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
584 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200585 WRN(session, "Missing ietf-netconf module in context, unable to send <close-session>.");
Michal Vaskod4da3632022-05-25 11:49:10 +0200586 return;
587 }
588 if (lyd_new_inner(NULL, ietfnc, "close-session", 0, &close_rpc)) {
589 WRN(session, "Failed to create <close-session> RPC.");
590 return;
591 }
592
593 /* send the RPC */
594 nc_send_msg_io(session, NC_SESSION_FREE_LOCK_TIMEOUT, close_rpc);
595
596read_msg:
597 switch (nc_read_msg_poll_io(session, NC_CLOSE_REPLY_TIMEOUT, &msg)) {
598 case 1:
599 if (!strncmp(ly_in_memory(msg, NULL), "<notification", 13)) {
600 /* ignore */
601 ly_in_free(msg, 1);
602 goto read_msg;
603 }
604 if (lyd_parse_op(session->ctx, close_rpc, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, &envp, NULL)) {
605 WRN(session, "Failed to parse <close-session> reply.");
606 } else if (!lyd_child(envp) || strcmp(LYD_NAME(lyd_child(envp)), "ok")) {
607 WRN(session, "Reply to <close-session> was not <ok> as expected.");
608 }
609 lyd_free_tree(envp);
610 ly_in_free(msg, 1);
611 break;
612 case 0:
613 WRN(session, "Timeout for receiving a reply to <close-session> elapsed.");
614 break;
615 case -1:
616 ERR(session, "Failed to receive a reply to <close-session>.");
617 break;
618 default:
619 /* cannot happen */
620 break;
621 }
622 lyd_free_tree(close_rpc);
623}
624
Michal Vasko33476c32022-09-09 11:21:40 +0200625/**
626 * @brief Free transport implementation members of a session.
627 *
628 * @param[in] session Session to free.
629 * @param[out] multisession Whether there are other NC sessions on the same SSH sessions.
630 */
631static void
632nc_session_free_transport(struct nc_session *session, int *multisession)
633{
634 int connected; /* flag to indicate whether the transport socket is still connected */
Michal Vaskoe44f2702022-12-12 07:58:06 +0100635 int sock = -1;
Michal Vasko33476c32022-09-09 11:21:40 +0200636 struct nc_session *siter;
637
638 *multisession = 0;
639 connected = nc_session_is_connected(session);
640
641 /* transport implementation cleanup */
642 switch (session->ti_type) {
643 case NC_TI_FD:
644 /* nothing needed - file descriptors were provided by caller,
645 * so it is up to the caller to close them correctly
646 * TODO use callbacks
647 */
648 /* just to avoid compiler warning */
649 (void)connected;
650 (void)siter;
651 break;
652
653 case NC_TI_UNIX:
654 sock = session->ti.unixsock.sock;
655 (void)connected;
656 (void)siter;
657 break;
658
roman2eab4742023-06-06 10:00:26 +0200659#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoe44f2702022-12-12 07:58:06 +0100660 case NC_TI_LIBSSH: {
661 int r;
662
Michal Vasko33476c32022-09-09 11:21:40 +0200663 if (connected) {
Michal Vasko64734402022-09-09 11:22:00 +0200664 ssh_channel_send_eof(session->ti.libssh.channel);
Michal Vasko33476c32022-09-09 11:21:40 +0200665 ssh_channel_free(session->ti.libssh.channel);
666 }
667 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
668 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
669 * it. Also, avoid concurrent free by multiple threads of sessions that share the SSH session.
670 */
671 /* SESSION IO LOCK */
672 r = nc_session_io_lock(session, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
673
674 if (session->ti.libssh.next) {
675 for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
676 if (siter->status != NC_STATUS_STARTING) {
677 *multisession = 1;
678 break;
679 }
680 }
681 }
682
683 if (!*multisession) {
684 /* it's not multisession yet, but we still need to free the starting sessions */
685 if (session->ti.libssh.next) {
686 do {
687 siter = session->ti.libssh.next;
688 session->ti.libssh.next = siter->ti.libssh.next;
689
690 /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */
691 free(siter->username);
692 free(siter->host);
693 if (!(siter->flags & NC_SESSION_SHAREDCTX)) {
694 ly_ctx_destroy((struct ly_ctx *)siter->ctx);
695 }
696
697 free(siter);
698 } while (session->ti.libssh.next != session);
699 }
700 /* remember sock so we can close it */
701 sock = ssh_get_fd(session->ti.libssh.session);
702 if (connected) {
703 ssh_disconnect(session->ti.libssh.session);
704 sock = -1;
705 }
706 ssh_free(session->ti.libssh.session);
707 } else {
708 /* remove the session from the list */
709 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next) {}
710 if (session->ti.libssh.next == siter) {
711 /* there will be only one session */
712 siter->ti.libssh.next = NULL;
713 } else {
714 /* there are still multiple sessions, keep the ring list */
715 siter->ti.libssh.next = session->ti.libssh.next;
716 }
Michal Vasko33476c32022-09-09 11:21:40 +0200717 }
718
719 /* SESSION IO UNLOCK */
720 if (r == 1) {
721 nc_session_io_unlock(session, __func__);
722 }
723 break;
Michal Vaskoe44f2702022-12-12 07:58:06 +0100724 }
Michal Vasko33476c32022-09-09 11:21:40 +0200725 case NC_TI_OPENSSL:
726 /* remember sock so we can close it */
727 sock = SSL_get_fd(session->ti.tls);
728
729 if (connected) {
730 SSL_shutdown(session->ti.tls);
731 }
732 SSL_free(session->ti.tls);
733
734 if (session->side == NC_SERVER) {
735 X509_free(session->opts.server.client_cert);
736 }
737 break;
roman2eab4742023-06-06 10:00:26 +0200738#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko33476c32022-09-09 11:21:40 +0200739 case NC_TI_NONE:
740 break;
741 }
742
743 /* close socket separately */
744 if (sock > -1) {
745 close(sock);
746 }
747}
748
Radek Krejci695d4fa2015-10-22 13:23:54 +0200749API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100750nc_session_free(struct nc_session *session, void (*data_free)(void *))
Radek Krejci695d4fa2015-10-22 13:23:54 +0200751{
Michal Vasko33476c32022-09-09 11:21:40 +0200752 int r, i, rpc_locked = 0, msgs_locked = 0, timeout;
Michal Vaskob48aa812016-01-18 14:13:09 +0100753 int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */
Michal Vaskoad611702015-12-03 13:41:51 +0100754 struct nc_msg_cont *contiter;
Michal Vasko77367452021-02-16 16:32:18 +0100755 struct ly_in *msg;
roman6ece9c52022-06-22 09:29:17 +0200756 struct timespec ts;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200757 void *p;
758
Michal Vasko428087d2016-01-14 16:04:28 +0100759 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200760 return;
761 }
762
Michal Vaskoa8ec54b2022-10-20 09:59:07 +0200763 /* stop notification threads if any */
764 if ((session->side == NC_CLIENT) && ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
765 /* let the threads know they should quit */
766 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200767
Michal Vaskoa8ec54b2022-10-20 09:59:07 +0200768 /* wait for them */
Michal Vaskod8a74192023-02-06 15:51:50 +0100769 nc_timeouttime_get(&ts, NC_SESSION_FREE_LOCK_TIMEOUT);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +0200770 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_count)) {
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200771 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +0100772 if (nc_timeouttime_cur_diff(&ts) < 1) {
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200773 ERR(session, "Waiting for notification thread exit failed (timed out).");
774 break;
775 }
776 }
Michal Vasko86d357c2016-03-11 13:46:38 +0100777 }
778
Michal Vaskoacf98472021-02-04 15:33:57 +0100779 if (session->side == NC_SERVER) {
Michal Vasko131120a2018-05-29 15:44:02 +0200780 r = nc_session_rpc_lock(session, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100781 if (r == -1) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100782 return;
Michal Vasko131120a2018-05-29 15:44:02 +0200783 } else if (r) {
784 rpc_locked = 1;
Michal Vasko96a28a32021-02-04 15:35:20 +0100785 } else {
786 /* else failed to lock it, too bad */
Michal Vasko05532772021-06-03 12:12:38 +0200787 ERR(session, "Freeing a session while an RPC is being processed.");
Michal Vasko96a28a32021-02-04 15:35:20 +0100788 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200789 }
790
Michal Vasko8c247822020-09-07 13:23:23 +0200791 if (session->side == NC_CLIENT) {
Michal Vasko01130bd2021-08-26 11:47:38 +0200792 timeout = NC_SESSION_FREE_LOCK_TIMEOUT;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +0200793
Michal Vasko01130bd2021-08-26 11:47:38 +0200794 /* MSGS LOCK */
795 r = nc_session_client_msgs_lock(session, &timeout, __func__);
796 if (r == -1) {
797 return;
798 } else if (r) {
799 msgs_locked = 1;
800 } else {
801 /* else failed to lock it, too bad */
802 ERR(session, "Freeing a session while messages are being received.");
803 }
804
805 /* cleanup message queue */
tadeas-vintrlik54f142a2021-08-23 10:36:18 +0200806 for (contiter = session->opts.client.msgs; contiter; ) {
Michal Vasko77367452021-02-16 16:32:18 +0100807 ly_in_free(contiter->msg, 1);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200808
Michal Vaskoad611702015-12-03 13:41:51 +0100809 p = contiter;
810 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200811 free(p);
812 }
813
Michal Vasko01130bd2021-08-26 11:47:38 +0200814 if (msgs_locked) {
815 /* MSGS UNLOCK */
816 nc_session_client_msgs_unlock(session, __func__);
817 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200818
Michal Vasko8c247822020-09-07 13:23:23 +0200819 if (session->status == NC_STATUS_RUNNING) {
Michal Vasko9c6d38c2021-09-03 13:02:53 +0200820 /* receive any leftover messages */
821 while (nc_read_msg_poll_io(session, 0, &msg) == 1) {
822 ly_in_free(msg, 1);
823 }
824
Michal Vasko8c247822020-09-07 13:23:23 +0200825 /* send closing info to the other side */
Michal Vaskod4da3632022-05-25 11:49:10 +0200826 nc_session_free_close_session(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200827 }
828
829 /* list of server's capabilities */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200830 if (session->opts.client.cpblts) {
831 for (i = 0; session->opts.client.cpblts[i]; i++) {
Michal Vasko96fc4bb2017-05-23 14:58:34 +0200832 free(session->opts.client.cpblts[i]);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200833 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200834 free(session->opts.client.cpblts);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200835 }
Michal Vasko78939072022-12-12 07:43:18 +0100836
837 /* LY ext data */
roman2eab4742023-06-06 10:00:26 +0200838#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoe44f2702022-12-12 07:58:06 +0100839 struct nc_session *siter;
840
Michal Vasko78939072022-12-12 07:43:18 +0100841 if ((session->flags & NC_SESSION_SHAREDCTX) && session->ti.libssh.next) {
842 for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
843 if (siter->status != NC_STATUS_STARTING) {
844 /* move LY ext data to this session */
845 assert(!siter->opts.client.ext_data);
846 siter->opts.client.ext_data = session->opts.client.ext_data;
847 session->opts.client.ext_data = NULL;
848 break;
849 }
850 }
Michal Vaskoe44f2702022-12-12 07:58:06 +0100851 } else
roman2eab4742023-06-06 10:00:26 +0200852#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskoe44f2702022-12-12 07:58:06 +0100853 {
Michal Vasko78939072022-12-12 07:43:18 +0100854 lyd_free_siblings(session->opts.client.ext_data);
855 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200856 }
857
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100858 if (session->data && data_free) {
859 data_free(session->data);
860 }
861
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200862 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
863 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100864 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200865 }
866
Michal Vasko86d357c2016-03-11 13:46:38 +0100867 /* mark session for closing */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200868 session->status = NC_STATUS_CLOSING;
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200869
Michal Vaskofeccb312022-03-24 15:24:59 +0100870 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CH_THREAD)) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100871 pthread_cond_signal(&session->opts.server.ch_cond);
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200872
Michal Vaskod8a74192023-02-06 15:51:50 +0100873 nc_timeouttime_get(&ts, NC_SESSION_FREE_LOCK_TIMEOUT);
Michal Vasko0db3db52021-03-03 10:45:42 +0100874
875 /* wait for CH thread to actually wake up and terminate */
876 r = 0;
Michal Vaskofeccb312022-03-24 15:24:59 +0100877 while (!r && (session->flags & NC_SESSION_CH_THREAD)) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100878 r = pthread_cond_clockwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, COMPAT_CLOCK_ID, &ts);
Michal Vasko0db3db52021-03-03 10:45:42 +0100879 }
Michal Vasko0db3db52021-03-03 10:45:42 +0100880 if (r) {
Michal Vasko05532772021-06-03 12:12:38 +0200881 ERR(session, "Waiting for Call Home thread failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +0100882 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200883 }
884
Michal Vaskofeccb312022-03-24 15:24:59 +0100885 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
886 /* CH UNLOCK */
887 pthread_mutex_unlock(&session->opts.server.ch_lock);
888 }
889
Radek Krejci695d4fa2015-10-22 13:23:54 +0200890 /* transport implementation cleanup */
Michal Vasko33476c32022-09-09 11:21:40 +0200891 nc_session_free_transport(session, &multisession);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200892
Michal Vasko33476c32022-09-09 11:21:40 +0200893 /* final cleanup */
Michal Vasko93224072021-11-09 12:14:28 +0100894 free(session->username);
895 free(session->host);
896 free(session->path);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200897
Michal Vaskoacf98472021-02-04 15:33:57 +0100898 if (session->side == NC_SERVER) {
Michal Vaskodf68e7e2022-04-21 11:04:00 +0200899 pthread_mutex_destroy(&session->opts.server.ntf_status_lock);
Michal Vasko131120a2018-05-29 15:44:02 +0200900 if (rpc_locked) {
901 nc_session_rpc_unlock(session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko9e99f012016-03-03 13:25:20 +0100902 }
Michal Vaskoacf98472021-02-04 15:33:57 +0100903 pthread_mutex_destroy(&session->opts.server.rpc_lock);
904 pthread_cond_destroy(&session->opts.server.rpc_cond);
Michal Vasko131120a2018-05-29 15:44:02 +0200905 }
906
907 if (session->io_lock && !multisession) {
908 pthread_mutex_destroy(session->io_lock);
909 free(session->io_lock);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200910 }
911
912 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
Michal Vasko93224072021-11-09 12:14:28 +0100913 ly_ctx_destroy((struct ly_ctx *)session->ctx);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200914 }
915
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200916 if (session->side == NC_SERVER) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100917 /* free CH synchronization structures */
918 pthread_cond_destroy(&session->opts.server.ch_cond);
919 pthread_mutex_destroy(&session->opts.server.ch_lock);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +0200920 } else {
921 pthread_mutex_destroy(&session->opts.client.msgs_lock);
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200922 }
923
Radek Krejci695d4fa2015-10-22 13:23:54 +0200924 free(session);
925}
926
Michal Vasko086311b2016-01-08 09:53:11 +0100927static void
Michal Vasko93224072021-11-09 12:14:28 +0100928add_cpblt(const char *capab, char ***cpblts, int *size, int *count)
Michal Vasko086311b2016-01-08 09:53:11 +0100929{
Radek Krejci658782b2016-12-04 22:04:55 +0100930 size_t len;
931 int i;
932 char *p;
933
934 if (capab) {
935 /* check if already present */
936 p = strchr(capab, '?');
937 if (p) {
938 len = p - capab;
939 } else {
940 len = strlen(capab);
941 }
942 for (i = 0; i < *count; i++) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200943 if (!strncmp((*cpblts)[i], capab, len) && (((*cpblts)[i][len] == '\0') || ((*cpblts)[i][len] == '?'))) {
Radek Krejci658782b2016-12-04 22:04:55 +0100944 /* already present, do not duplicate it */
945 return;
946 }
947 }
948 }
949
950 /* add another capability */
Michal Vasko086311b2016-01-08 09:53:11 +0100951 if (*count == *size) {
952 *size += 5;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100953 *cpblts = nc_realloc(*cpblts, *size * sizeof **cpblts);
954 if (!(*cpblts)) {
955 ERRMEM;
956 return;
957 }
Michal Vasko086311b2016-01-08 09:53:11 +0100958 }
959
Michal Vasko93224072021-11-09 12:14:28 +0100960 (*cpblts)[*count] = capab ? strdup(capab) : NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100961 ++(*count);
962}
963
Michal Vasko93224072021-11-09 12:14:28 +0100964API char **
965nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version)
Michal Vasko086311b2016-01-08 09:53:11 +0100966{
Michal Vasko93224072021-11-09 12:14:28 +0100967 char **cpblts;
Michal Vasko77367452021-02-16 16:32:18 +0100968 const struct lys_module *mod;
969 struct lysp_feature *feat;
970 int size = 10, count, features_count = 0, dev_count = 0, str_len, len;
Michal Vasko1440a742021-03-31 11:11:03 +0200971 uint32_t i, u;
Michal Vasko77367452021-02-16 16:32:18 +0100972 LY_ARRAY_COUNT_TYPE v;
Michal Vasko1440a742021-03-31 11:11:03 +0200973 char *yl_content_id;
romanc1d2b092023-02-02 08:58:27 +0100974 uint32_t wd_also_supported;
975 uint32_t wd_basic_mode;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200976
Radek Krejci24a18412018-05-16 15:09:10 +0200977#define NC_CPBLT_BUF_LEN 4096
Michal Vasko2e47ef92016-06-20 10:03:24 +0200978 char str[NC_CPBLT_BUF_LEN];
Michal Vasko086311b2016-01-08 09:53:11 +0100979
roman40672412023-05-04 11:10:22 +0200980 NC_CHECK_ARG_RET(NULL, ctx, NULL);
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200981
Michal Vasko086311b2016-01-08 09:53:11 +0100982 cpblts = malloc(size * sizeof *cpblts);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100983 if (!cpblts) {
984 ERRMEM;
Radek Krejcif906e412017-09-22 14:44:45 +0200985 goto error;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100986 }
Michal Vasko93224072021-11-09 12:14:28 +0100987 cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0");
988 cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1");
Michal Vasko086311b2016-01-08 09:53:11 +0100989 count = 2;
990
991 /* capabilities */
992
Michal Vasko77367452021-02-16 16:32:18 +0100993 mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf");
Michal Vasko086311b2016-01-08 09:53:11 +0100994 if (mod) {
Michal Vasko77367452021-02-16 16:32:18 +0100995 if (lys_feature_value(mod, "writable-running") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +0100996 add_cpblt("urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100997 }
Michal Vasko77367452021-02-16 16:32:18 +0100998 if (lys_feature_value(mod, "candidate") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +0100999 add_cpblt("urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count);
Michal Vasko77367452021-02-16 16:32:18 +01001000 if (lys_feature_value(mod, "confirmed-commit") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001001 add_cpblt("urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001002 }
1003 }
Michal Vasko77367452021-02-16 16:32:18 +01001004 if (lys_feature_value(mod, "rollback-on-error") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001005 add_cpblt("urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001006 }
Michal Vasko77367452021-02-16 16:32:18 +01001007 if (lys_feature_value(mod, "validate") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001008 add_cpblt("urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001009 }
Michal Vasko77367452021-02-16 16:32:18 +01001010 if (lys_feature_value(mod, "startup") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001011 add_cpblt("urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001012 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001013
1014 /* The URL capability must be set manually using nc_server_set_capability()
1015 * because of the need for supported protocols to be included.
1016 * https://tools.ietf.org/html/rfc6241#section-8.8.3
1017 */
Michal Vasko77367452021-02-16 16:32:18 +01001018 // if (lys_feature_value(mod, "url") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001019 // add_cpblt("urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count);
mekleoa8de5e92020-02-13 09:05:56 +01001020 // }
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001021
Michal Vasko77367452021-02-16 16:32:18 +01001022 if (lys_feature_value(mod, "xpath") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001023 add_cpblt("urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001024 }
1025 }
1026
Michal Vasko77367452021-02-16 16:32:18 +01001027 mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01001028 if (mod) {
romanc1d2b092023-02-02 08:58:27 +01001029 wd_basic_mode = ATOMIC_LOAD_RELAXED(server_opts.wd_basic_mode);
1030 if (!wd_basic_mode) {
Michal Vasko05532772021-06-03 12:12:38 +02001031 VRB(NULL, "with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
Michal Vasko086311b2016-01-08 09:53:11 +01001032 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001033 strcpy(str, "urn:ietf:params:netconf:capability:with-defaults:1.0");
romanc1d2b092023-02-02 08:58:27 +01001034 switch (wd_basic_mode) {
Michal Vasko086311b2016-01-08 09:53:11 +01001035 case NC_WD_ALL:
1036 strcat(str, "?basic-mode=report-all");
1037 break;
1038 case NC_WD_TRIM:
1039 strcat(str, "?basic-mode=trim");
1040 break;
1041 case NC_WD_EXPLICIT:
1042 strcat(str, "?basic-mode=explicit");
1043 break;
1044 default:
Michal Vasko9e036d52016-01-08 10:49:26 +01001045 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +01001046 break;
1047 }
1048
romanc1d2b092023-02-02 08:58:27 +01001049 wd_also_supported = ATOMIC_LOAD_RELAXED(server_opts.wd_also_supported);
1050 if (wd_also_supported) {
Michal Vasko2e47ef92016-06-20 10:03:24 +02001051 strcat(str, "&also-supported=");
romanc1d2b092023-02-02 08:58:27 +01001052 if (wd_also_supported & NC_WD_ALL) {
Michal Vasko086311b2016-01-08 09:53:11 +01001053 strcat(str, "report-all,");
1054 }
romanc1d2b092023-02-02 08:58:27 +01001055 if (wd_also_supported & NC_WD_ALL_TAG) {
Michal Vasko086311b2016-01-08 09:53:11 +01001056 strcat(str, "report-all-tagged,");
1057 }
romanc1d2b092023-02-02 08:58:27 +01001058 if (wd_also_supported & NC_WD_TRIM) {
Michal Vasko086311b2016-01-08 09:53:11 +01001059 strcat(str, "trim,");
1060 }
romanc1d2b092023-02-02 08:58:27 +01001061 if (wd_also_supported & NC_WD_EXPLICIT) {
Michal Vasko086311b2016-01-08 09:53:11 +01001062 strcat(str, "explicit,");
1063 }
1064 str[strlen(str) - 1] = '\0';
1065
Michal Vasko93224072021-11-09 12:14:28 +01001066 add_cpblt(str, &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001067 }
1068 }
1069 }
1070
Radek Krejci658782b2016-12-04 22:04:55 +01001071 /* other capabilities */
1072 for (u = 0; u < server_opts.capabilities_count; u++) {
Michal Vasko93224072021-11-09 12:14:28 +01001073 add_cpblt(server_opts.capabilities[u], &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001074 }
1075
1076 /* models */
Michal Vasko1440a742021-03-31 11:11:03 +02001077 u = 0;
Radek Krejci24a18412018-05-16 15:09:10 +02001078 while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
Radek Krejci24a18412018-05-16 15:09:10 +02001079 if (!strcmp(mod->name, "ietf-yang-library")) {
Michal Vasko77367452021-02-16 16:32:18 +01001080 if (!mod->revision || (strcmp(mod->revision, "2016-06-21") && strcmp(mod->revision, "2019-01-04"))) {
Michal Vasko05532772021-06-03 12:12:38 +02001081 ERR(NULL, "Unknown \"ietf-yang-library\" revision, only 2016-06-21 and 2019-01-04 are supported.");
Michal Vaskod5ada122020-03-19 18:28:06 +01001082 goto error;
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001083 }
Michal Vaskod5ada122020-03-19 18:28:06 +01001084
Michal Vasko1440a742021-03-31 11:11:03 +02001085 /* get content-id */
1086 if (server_opts.content_id_clb) {
1087 yl_content_id = server_opts.content_id_clb(server_opts.content_id_data);
1088 if (!yl_content_id) {
1089 ERRMEM;
1090 goto error;
1091 }
1092 } else {
1093 yl_content_id = malloc(11);
1094 if (!yl_content_id) {
1095 ERRMEM;
1096 goto error;
1097 }
1098 sprintf(yl_content_id, "%u", ly_ctx_get_change_count(ctx));
1099 }
1100
Michal Vasko77367452021-02-16 16:32:18 +01001101 if (!strcmp(mod->revision, "2019-01-04")) {
Michal Vasko7b5e3d92020-04-08 14:40:31 +02001102 /* new one (capab defined in RFC 8526 section 2) */
Michal Vasko1440a742021-03-31 11:11:03 +02001103 sprintf(str, "urn:ietf:params:netconf:capability:yang-library:1.1?revision=%s&content-id=%s",
1104 mod->revision, yl_content_id);
Michal Vasko93224072021-11-09 12:14:28 +01001105 add_cpblt(str, &cpblts, &size, &count);
Michal Vasko7b5e3d92020-04-08 14:40:31 +02001106 } else {
1107 /* old one (capab defined in RFC 7950 section 5.6.4) */
Michal Vasko1440a742021-03-31 11:11:03 +02001108 sprintf(str, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s",
1109 mod->revision, yl_content_id);
Michal Vasko93224072021-11-09 12:14:28 +01001110 add_cpblt(str, &cpblts, &size, &count);
Michal Vaskod5ada122020-03-19 18:28:06 +01001111 }
Michal Vasko1440a742021-03-31 11:11:03 +02001112 free(yl_content_id);
Radek Krejci24a18412018-05-16 15:09:10 +02001113 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001114 } else if ((version == LYS_VERSION_1_0) && (mod->parsed->version > version)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001115 /* skip YANG 1.1 modules */
Radek Krejci24a18412018-05-16 15:09:10 +02001116 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001117 } else if ((version == LYS_VERSION_1_1) && (mod->parsed->version != version)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001118 /* skip YANG 1.0 modules */
Radek Krejci24a18412018-05-16 15:09:10 +02001119 continue;
1120 }
Michal Vasko086311b2016-01-08 09:53:11 +01001121
Michal Vasko77367452021-02-16 16:32:18 +01001122 str_len = sprintf(str, "%s?module=%s%s%s", mod->ns, mod->name, mod->revision ? "&revision=" : "",
1123 mod->revision ? mod->revision : "");
Radek Krejci24a18412018-05-16 15:09:10 +02001124
Michal Vaskodafdc742020-03-11 16:15:59 +01001125 features_count = 0;
1126 i = 0;
1127 feat = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001128 while ((feat = lysp_feature_next(feat, mod->parsed, &i))) {
Michal Vaskodafdc742020-03-11 16:15:59 +01001129 if (!(feat->flags & LYS_FENABLED)) {
1130 continue;
Michal Vaskoe90e4d12016-06-20 10:05:01 +02001131 }
Michal Vaskodafdc742020-03-11 16:15:59 +01001132 if (!features_count) {
1133 strcat(str, "&features=");
1134 str_len += 10;
1135 }
1136 len = strlen(feat->name);
1137 if (str_len + 1 + len >= NC_CPBLT_BUF_LEN) {
1138 ERRINT;
1139 break;
1140 }
1141 if (features_count) {
1142 strcat(str, ",");
1143 ++str_len;
1144 }
1145 strcat(str, feat->name);
1146 str_len += len;
1147 features_count++;
Michal Vasko086311b2016-01-08 09:53:11 +01001148 }
Michal Vasko086311b2016-01-08 09:53:11 +01001149
Michal Vasko77367452021-02-16 16:32:18 +01001150 if (mod->deviated_by) {
Radek Krejci24a18412018-05-16 15:09:10 +02001151 strcat(str, "&deviations=");
1152 str_len += 12;
1153 dev_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +01001154 LY_ARRAY_FOR(mod->deviated_by, v) {
1155 len = strlen(mod->deviated_by[v]->name);
1156 if (str_len + 1 + len >= NC_CPBLT_BUF_LEN) {
1157 ERRINT;
1158 break;
Radek Krejci24a18412018-05-16 15:09:10 +02001159 }
Michal Vasko77367452021-02-16 16:32:18 +01001160 if (dev_count) {
1161 strcat(str, ",");
1162 ++str_len;
Radek Krejci24a18412018-05-16 15:09:10 +02001163 }
Michal Vasko77367452021-02-16 16:32:18 +01001164 strcat(str, mod->deviated_by[v]->name);
1165 str_len += len;
1166 dev_count++;
Radek Krejci24a18412018-05-16 15:09:10 +02001167 }
1168 }
1169
Michal Vasko93224072021-11-09 12:14:28 +01001170 add_cpblt(str, &cpblts, &size, &count);
Radek Krejci24a18412018-05-16 15:09:10 +02001171 }
Michal Vasko086311b2016-01-08 09:53:11 +01001172
1173 /* ending NULL capability */
Michal Vasko93224072021-11-09 12:14:28 +01001174 add_cpblt(NULL, &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001175
1176 return cpblts;
Radek Krejcif906e412017-09-22 14:44:45 +02001177
1178error:
Radek Krejcif906e412017-09-22 14:44:45 +02001179 free(cpblts);
Radek Krejcif906e412017-09-22 14:44:45 +02001180 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001181}
1182
Michal Vasko93224072021-11-09 12:14:28 +01001183API char **
1184nc_server_get_cpblts(const struct ly_ctx *ctx)
Radek Krejci24a18412018-05-16 15:09:10 +02001185{
1186 return nc_server_get_cpblts_version(ctx, LYS_VERSION_UNDEF);
1187}
1188
Radek Krejci695d4fa2015-10-22 13:23:54 +02001189static int
Michal Vasko77367452021-02-16 16:32:18 +01001190parse_cpblts(struct lyd_node *capabilities, char ***list)
Radek Krejci695d4fa2015-10-22 13:23:54 +02001191{
Michal Vasko77367452021-02-16 16:32:18 +01001192 struct lyd_node *iter;
1193 struct lyd_node_opaq *cpblt;
Michal Vasko156d3272017-04-11 11:46:49 +02001194 int ver = -1, i = 0;
1195 const char *cpb_start, *cpb_end;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001196
1197 if (list) {
1198 /* get the storage for server's capabilities */
Michal Vasko77367452021-02-16 16:32:18 +01001199 LY_LIST_FOR(lyd_child(capabilities), iter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +02001200 i++;
1201 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001202 /* last item remains NULL */
1203 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001204 if (!*list) {
1205 ERRMEM;
1206 return -1;
1207 }
1208 i = 0;
1209 }
1210
Michal Vasko77367452021-02-16 16:32:18 +01001211 LY_LIST_FOR(lyd_child(capabilities), iter) {
1212 cpblt = (struct lyd_node_opaq *)iter;
1213
1214 if (strcmp(cpblt->name.name, "capability") || !cpblt->name.module_ns || strcmp(cpblt->name.module_ns, NC_NS_BASE)) {
Michal Vasko05532772021-06-03 12:12:38 +02001215 ERR(NULL, "Unexpected <%s> element in client's <hello>.", cpblt->name.name);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001216 return -1;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001217 }
1218
Michal Vasko156d3272017-04-11 11:46:49 +02001219 /* skip leading/trailing whitespaces */
Michal Vasko77367452021-02-16 16:32:18 +01001220 for (cpb_start = cpblt->value; isspace(cpb_start[0]); ++cpb_start) {}
1221 for (cpb_end = cpblt->value + strlen(cpblt->value); (cpb_end > cpblt->value) && isspace(cpb_end[-1]); --cpb_end) {}
1222 if (!cpb_start[0] || (cpb_end == cpblt->value)) {
Michal Vasko05532772021-06-03 12:12:38 +02001223 ERR(NULL, "Empty capability \"%s\" received.", cpblt->value);
Michal Vasko156d3272017-04-11 11:46:49 +02001224 return -1;
1225 }
1226
Radek Krejci695d4fa2015-10-22 13:23:54 +02001227 /* detect NETCONF version */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001228 if ((ver < 0) && !strncmp(cpb_start, "urn:ietf:params:netconf:base:1.0", cpb_end - cpb_start)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +02001229 ver = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001230 } else if ((ver < 1) && !strncmp(cpb_start, "urn:ietf:params:netconf:base:1.1", cpb_end - cpb_start)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +02001231 ver = 1;
1232 }
1233
1234 /* store capabilities */
1235 if (list) {
Michal Vasko156d3272017-04-11 11:46:49 +02001236 (*list)[i] = strndup(cpb_start, cpb_end - cpb_start);
1237 if (!(*list)[i]) {
1238 ERRMEM;
1239 return -1;
1240 }
Radek Krejci695d4fa2015-10-22 13:23:54 +02001241 i++;
1242 }
1243 }
1244
1245 if (ver == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001246 ERR(NULL, "Peer does not support a compatible NETCONF version.");
Radek Krejci695d4fa2015-10-22 13:23:54 +02001247 }
1248
1249 return ver;
1250}
1251
1252static NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +02001253nc_send_hello_io(struct nc_session *session)
Michal Vasko086311b2016-01-08 09:53:11 +01001254{
Michal Vasko131120a2018-05-29 15:44:02 +02001255 NC_MSG_TYPE ret;
1256 int i, io_timeout;
Michal Vasko93224072021-11-09 12:14:28 +01001257 char **cpblts;
Michal Vasko131120a2018-05-29 15:44:02 +02001258 uint32_t *sid;
Michal Vasko086311b2016-01-08 09:53:11 +01001259
Michal Vasko131120a2018-05-29 15:44:02 +02001260 if (session->side == NC_CLIENT) {
1261 /* client side hello - send only NETCONF base capabilities */
1262 cpblts = malloc(3 * sizeof *cpblts);
1263 if (!cpblts) {
1264 ERRMEM;
1265 return NC_MSG_ERROR;
1266 }
Michal Vasko93224072021-11-09 12:14:28 +01001267 cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0");
1268 cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1");
Michal Vasko131120a2018-05-29 15:44:02 +02001269 cpblts[2] = NULL;
1270
1271 io_timeout = NC_CLIENT_HELLO_TIMEOUT * 1000;
1272 sid = NULL;
1273 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001274 cpblts = nc_server_get_cpblts_version(session->ctx, LYS_VERSION_1_0);
Michal Vasko5b24b6b2020-12-04 09:29:47 +01001275 if (!cpblts) {
1276 return NC_MSG_ERROR;
1277 }
Michal Vasko131120a2018-05-29 15:44:02 +02001278
1279 io_timeout = NC_SERVER_HELLO_TIMEOUT * 1000;
1280 sid = &session->id;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001281 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001282
Michal Vasko131120a2018-05-29 15:44:02 +02001283 ret = nc_write_msg_io(session, io_timeout, NC_MSG_HELLO, cpblts, sid);
Michal Vasko086311b2016-01-08 09:53:11 +01001284
Michal Vasko086311b2016-01-08 09:53:11 +01001285 for (i = 0; cpblts[i]; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01001286 free(cpblts[i]);
Michal Vasko086311b2016-01-08 09:53:11 +01001287 }
1288 free(cpblts);
1289
Michal Vasko131120a2018-05-29 15:44:02 +02001290 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001291}
1292
1293static NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +02001294nc_recv_client_hello_io(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +02001295{
Michal Vasko77367452021-02-16 16:32:18 +01001296 struct ly_in *msg;
1297 struct lyd_node *hello = NULL, *iter;
1298 struct lyd_node_opaq *node;
1299 int r, ver = -1, flag = 0;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001300 char *str;
Michal Vasko292c5542023-02-01 14:33:17 +01001301 long long id;
Michal Vasko77367452021-02-16 16:32:18 +01001302 NC_MSG_TYPE rc = NC_MSG_HELLO;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001303
Michal Vasko77367452021-02-16 16:32:18 +01001304 r = nc_read_msg_poll_io(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &msg);
1305 switch (r) {
1306 case 1:
Radek Krejci695d4fa2015-10-22 13:23:54 +02001307 /* parse <hello> data */
Michal Vasko77367452021-02-16 16:32:18 +01001308 if (lyd_parse_data(session->ctx, NULL, msg, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_OPAQ, 0, &hello)) {
Michal Vasko05532772021-06-03 12:12:38 +02001309 ERR(session, "Failed to parse server <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001310 rc = NC_MSG_ERROR;
1311 goto cleanup;
1312 }
1313
1314 LY_LIST_FOR(lyd_child(hello), iter) {
1315 node = (struct lyd_node_opaq *)iter;
1316
1317 if (!node->name.module_ns || strcmp(node->name.module_ns, NC_NS_BASE)) {
Michal Vasko11d142a2016-01-19 15:58:24 +01001318 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001319 } else if (!strcmp(node->name.name, "session-id")) {
1320 if (!node->value || !strlen(node->value)) {
Michal Vasko05532772021-06-03 12:12:38 +02001321 ERR(session, "No value of <session-id> element in server <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001322 rc = NC_MSG_ERROR;
1323 goto cleanup;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001324 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001325 str = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001326 id = strtoll(node->value, &str, 10);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001327 if (*str || (id < 1) || (id > UINT32_MAX)) {
Michal Vasko05532772021-06-03 12:12:38 +02001328 ERR(session, "Invalid value of <session-id> element in server <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001329 rc = NC_MSG_ERROR;
1330 goto cleanup;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001331 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001332 session->id = (uint32_t)id;
1333 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001334 } else if (strcmp(node->name.name, "capabilities")) {
Michal Vasko05532772021-06-03 12:12:38 +02001335 ERR(session, "Unexpected <%s> element in server <hello>.", node->name.name);
Michal Vasko77367452021-02-16 16:32:18 +01001336 rc = NC_MSG_ERROR;
1337 goto cleanup;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001338 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001339
1340 if (flag) {
1341 /* multiple capabilities elements */
Michal Vasko05532772021-06-03 12:12:38 +02001342 ERR(session, "Invalid <hello> message (multiple <capabilities> elements).");
Michal Vasko77367452021-02-16 16:32:18 +01001343 rc = NC_MSG_ERROR;
1344 goto cleanup;
Michal Vasko11d142a2016-01-19 15:58:24 +01001345 }
1346 flag = 1;
1347
Michal Vasko77367452021-02-16 16:32:18 +01001348 if ((ver = parse_cpblts(&node->node, &session->opts.client.cpblts)) < 0) {
1349 rc = NC_MSG_ERROR;
1350 goto cleanup;
Michal Vasko11d142a2016-01-19 15:58:24 +01001351 }
1352 session->version = ver;
1353 }
1354
1355 if (!session->id) {
Michal Vasko05532772021-06-03 12:12:38 +02001356 ERR(session, "Missing <session-id> in server <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001357 rc = NC_MSG_ERROR;
1358 goto cleanup;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001359 }
1360 break;
Michal Vasko77367452021-02-16 16:32:18 +01001361 case 0:
Michal Vasko05532772021-06-03 12:12:38 +02001362 ERR(session, "Server <hello> timeout elapsed.");
Michal Vasko77367452021-02-16 16:32:18 +01001363 rc = NC_MSG_WOULDBLOCK;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001364 break;
1365 default:
Michal Vasko77367452021-02-16 16:32:18 +01001366 rc = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +02001367 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001368 }
1369
Michal Vasko77367452021-02-16 16:32:18 +01001370cleanup:
1371 ly_in_free(msg, 1);
1372 lyd_free_tree(hello);
1373 return rc;
Radek Krejci5686ff72015-10-09 13:33:56 +02001374}
1375
Michal Vasko11d142a2016-01-19 15:58:24 +01001376static NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +02001377nc_recv_server_hello_io(struct nc_session *session)
Michal Vasko11d142a2016-01-19 15:58:24 +01001378{
Michal Vasko77367452021-02-16 16:32:18 +01001379 struct ly_in *msg;
1380 struct lyd_node *hello = NULL, *iter;
1381 struct lyd_node_opaq *node;
1382 NC_MSG_TYPE rc = NC_MSG_HELLO;
1383 int r, ver = -1, flag = 0, timeout_io;
Michal Vasko11d142a2016-01-19 15:58:24 +01001384
Michal Vasko77367452021-02-16 16:32:18 +01001385 timeout_io = server_opts.hello_timeout ? server_opts.hello_timeout * 1000 : NC_SERVER_HELLO_TIMEOUT * 1000;
1386 r = nc_read_msg_poll_io(session, timeout_io, &msg);
1387 switch (r) {
1388 case 1:
1389 /* parse <hello> data */
1390 if (lyd_parse_data(session->ctx, NULL, msg, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_OPAQ, 0, &hello)) {
Michal Vasko05532772021-06-03 12:12:38 +02001391 ERR(session, "Failed to parse client <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001392 rc = NC_MSG_ERROR;
1393 goto cleanup;
1394 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001395
Michal Vasko77367452021-02-16 16:32:18 +01001396 /* learn NETCONF version */
1397 LY_LIST_FOR(lyd_child(hello), iter) {
1398 node = (struct lyd_node_opaq *)iter;
1399
1400 if (!node->name.module_ns || strcmp(node->name.module_ns, NC_NS_BASE)) {
Michal Vasko11d142a2016-01-19 15:58:24 +01001401 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001402 } else if (strcmp(node->name.name, "capabilities")) {
Michal Vasko05532772021-06-03 12:12:38 +02001403 ERR(session, "Unexpected <%s> element in client <hello>.", node->name.name);
Michal Vasko77367452021-02-16 16:32:18 +01001404 rc = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001405 goto cleanup;
1406 }
1407
1408 if (flag) {
1409 /* multiple capabilities elements */
Michal Vasko05532772021-06-03 12:12:38 +02001410 ERR(session, "Invalid <hello> message (multiple <capabilities> elements).");
Michal Vasko77367452021-02-16 16:32:18 +01001411 rc = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001412 goto cleanup;
1413 }
1414 flag = 1;
1415
Michal Vasko77367452021-02-16 16:32:18 +01001416 if ((ver = parse_cpblts(&node->node, NULL)) < 0) {
1417 rc = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001418 goto cleanup;
1419 }
1420 session->version = ver;
1421 }
1422 break;
Michal Vasko77367452021-02-16 16:32:18 +01001423 case 0:
Michal Vasko05532772021-06-03 12:12:38 +02001424 ERR(session, "Client <hello> timeout elapsed.");
Michal Vasko77367452021-02-16 16:32:18 +01001425 rc = NC_MSG_WOULDBLOCK;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001426 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001427 default:
Michal Vasko77367452021-02-16 16:32:18 +01001428 rc = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +02001429 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001430 }
1431
1432cleanup:
Michal Vasko77367452021-02-16 16:32:18 +01001433 ly_in_free(msg, 1);
1434 lyd_free_tree(hello);
1435 return rc;
Michal Vasko11d142a2016-01-19 15:58:24 +01001436}
1437
Michal Vasko71090fc2016-05-24 16:37:28 +02001438NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +02001439nc_handshake_io(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001440{
Michal Vasko086311b2016-01-08 09:53:11 +01001441 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001442
Michal Vasko131120a2018-05-29 15:44:02 +02001443 type = nc_send_hello_io(session);
Michal Vasko086311b2016-01-08 09:53:11 +01001444 if (type != NC_MSG_HELLO) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001445 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001446 }
1447
Michal Vasko11d142a2016-01-19 15:58:24 +01001448 if (session->side == NC_CLIENT) {
Michal Vasko131120a2018-05-29 15:44:02 +02001449 type = nc_recv_client_hello_io(session);
Michal Vasko11d142a2016-01-19 15:58:24 +01001450 } else {
Michal Vasko131120a2018-05-29 15:44:02 +02001451 type = nc_recv_server_hello_io(session);
Michal Vasko11d142a2016-01-19 15:58:24 +01001452 }
1453
Michal Vasko71090fc2016-05-24 16:37:28 +02001454 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001455}