blob: f45785ff551432f28c669e4785b54b9e804b9635 [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>
roman13145912023-08-17 15:36:54 +020039#include <openssl/bio.h>
roman2eab4742023-06-06 10:00:26 +020040#include <openssl/conf.h>
41#include <openssl/err.h>
roman51a1e192023-09-14 10:13:45 +020042#include <openssl/evp.h>
43#include <openssl/x509.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020044
roman2eab4742023-06-06 10:00:26 +020045#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskoc14e3c82016-01-11 16:14:30 +010046
Michal Vasko086311b2016-01-08 09:53:11 +010047/* in seconds */
48#define NC_CLIENT_HELLO_TIMEOUT 60
fanchanghu75888b62017-08-01 19:51:20 +080049#define NC_SERVER_HELLO_TIMEOUT 60
Radek Krejci695d4fa2015-10-22 13:23:54 +020050
Michal Vasko05ba9df2016-01-13 14:40:27 +010051/* in milliseconds */
52#define NC_CLOSE_REPLY_TIMEOUT 200
53
Michal Vasko086311b2016-01-08 09:53:11 +010054extern struct nc_server_opts server_opts;
55
Michal Vaskod8a74192023-02-06 15:51:50 +010056void
57nc_timeouttime_get(struct timespec *ts, uint32_t add_ms)
Radek Krejci7ac16052016-07-15 11:48:18 +020058{
Michal Vaskod8a74192023-02-06 15:51:50 +010059 if (clock_gettime(COMPAT_CLOCK_ID, ts) == -1) {
60 ERR(NULL, "clock_gettime() failed (%s).", strerror(errno));
61 return;
62 }
63
64 if (!add_ms) {
65 return;
66 }
67
68 assert((ts->tv_nsec >= 0) && (ts->tv_nsec < 1000000000L));
69
70 ts->tv_sec += add_ms / 1000;
71 ts->tv_nsec += (add_ms % 1000) * 1000000L;
72
73 if (ts->tv_nsec >= 1000000000L) {
74 ++ts->tv_sec;
75 ts->tv_nsec -= 1000000000L;
76 } else if (ts->tv_nsec < 0) {
77 --ts->tv_sec;
78 ts->tv_nsec += 1000000000L;
79 }
80
81 assert((ts->tv_nsec >= 0) && (ts->tv_nsec < 1000000000L));
82}
83
84int32_t
85nc_timeouttime_cur_diff(const struct timespec *ts)
86{
87 struct timespec cur;
88 int64_t nsec_diff = 0;
89
90 nc_timeouttime_get(&cur, 0);
91
92 nsec_diff += (((int64_t)ts->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L;
93 nsec_diff += ((int64_t)ts->tv_nsec) - ((int64_t)cur.tv_nsec);
94
95 return nsec_diff / 1000000L;
96}
97
98void
99nc_realtime_get(struct timespec *ts)
100{
roman6ece9c52022-06-22 09:29:17 +0200101 if (clock_gettime(CLOCK_REALTIME, ts)) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100102 ERR(NULL, "clock_gettime() failed (%s).", strerror(errno));
103 return;
roman6ece9c52022-06-22 09:29:17 +0200104 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100105}
106
roman2eab4742023-06-06 10:00:26 +0200107#ifdef NC_ENABLED_SSH_TLS
108
Michal Vaskoddce1212019-05-24 09:58:49 +0200109const char *
roman3f9b65c2023-06-05 14:26:58 +0200110nc_privkey_format_to_str(NC_PRIVKEY_FORMAT format)
Michal Vaskoddce1212019-05-24 09:58:49 +0200111{
roman3f9b65c2023-06-05 14:26:58 +0200112 switch (format) {
113 case NC_PRIVKEY_FORMAT_RSA:
Michal Vaskoddce1212019-05-24 09:58:49 +0200114 return "RSA";
roman3f9b65c2023-06-05 14:26:58 +0200115 case NC_PRIVKEY_FORMAT_EC:
Michal Vaskoddce1212019-05-24 09:58:49 +0200116 return "EC";
roman3f9b65c2023-06-05 14:26:58 +0200117 case NC_PRIVKEY_FORMAT_X509:
roman44600f42023-04-28 15:54:27 +0200118 return NULL;
roman3f9b65c2023-06-05 14:26:58 +0200119 case NC_PRIVKEY_FORMAT_OPENSSH:
120 return "OPENSSH";
Michal Vaskoddce1212019-05-24 09:58:49 +0200121 default:
roman3f9b65c2023-06-05 14:26:58 +0200122 return NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +0200123 }
Michal Vaskoddce1212019-05-24 09:58:49 +0200124}
125
roman13145912023-08-17 15:36:54 +0200126int
127nc_base64_to_bin(const char *base64, char **bin)
128{
roman46172ac2023-11-08 15:12:40 +0100129 BIO *bio, *bio64 = NULL;
roman13145912023-08-17 15:36:54 +0200130 size_t used = 0, size = 0, r = 0;
131 void *tmp = NULL;
roman46172ac2023-11-08 15:12:40 +0100132 int nl_count, i, remainder, ret = 0;
roman13145912023-08-17 15:36:54 +0200133 char *b64;
134
135 /* insert new lines into the base64 string, so BIO_read works correctly */
136 nl_count = strlen(base64) / 64;
137 remainder = strlen(base64) - 64 * nl_count;
138 b64 = calloc(strlen(base64) + nl_count + 1, 1);
roman3a95bb22023-10-26 11:07:17 +0200139 NC_CHECK_ERRMEM_RET(!b64, -1);
roman13145912023-08-17 15:36:54 +0200140
141 for (i = 0; i < nl_count; i++) {
142 /* copy 64 bytes and add a NL */
143 strncpy(b64 + i * 65, base64 + i * 64, 64);
144 b64[i * 65 + 64] = '\n';
145 }
146
147 /* copy the rest */
148 strncpy(b64 + i * 65, base64 + i * 64, remainder);
149
150 bio64 = BIO_new(BIO_f_base64());
151 if (!bio64) {
152 ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error()));
roman46172ac2023-11-08 15:12:40 +0100153 ret = -1;
154 goto cleanup;
roman13145912023-08-17 15:36:54 +0200155 }
156
157 bio = BIO_new_mem_buf(b64, strlen(b64));
158 if (!bio) {
159 ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error()));
roman46172ac2023-11-08 15:12:40 +0100160 ret = -1;
161 goto cleanup;
roman13145912023-08-17 15:36:54 +0200162 }
163
164 BIO_push(bio64, bio);
165
166 /* store the decoded base64 in bin */
167 *bin = NULL;
168 do {
169 size += 64;
170
171 tmp = realloc(*bin, size);
172 if (!tmp) {
roman46172ac2023-11-08 15:12:40 +0100173 ERRMEM;
roman13145912023-08-17 15:36:54 +0200174 free(*bin);
roman46172ac2023-11-08 15:12:40 +0100175 *bin = NULL;
176 ret = -1;
177 goto cleanup;
roman13145912023-08-17 15:36:54 +0200178 }
179 *bin = tmp;
180
181 r = BIO_read(bio64, *bin + used, 64);
182 used += r;
183 } while (r == 64);
184
roman46172ac2023-11-08 15:12:40 +0100185 ret = size;
186
187cleanup:
roman13145912023-08-17 15:36:54 +0200188 free(b64);
189 BIO_free_all(bio64);
roman46172ac2023-11-08 15:12:40 +0100190 return ret;
roman13145912023-08-17 15:36:54 +0200191}
192
roman51a1e192023-09-14 10:13:45 +0200193int
194nc_is_pk_subject_public_key_info(const char *b64)
195{
196 int ret = 0;
197 long len;
198 char *bin = NULL, *tmp;
199 EVP_PKEY *pkey = NULL;
200
201 /* base64 2 binary */
202 len = nc_base64_to_bin(b64, &bin);
203 if (len == -1) {
204 ERR(NULL, "Decoding base64 public key to binary failed.");
205 ret = -1;
206 goto cleanup;
207 }
208
209 /* for deallocation later */
210 tmp = bin;
211
212 /* try to create EVP_PKEY from the supposed SubjectPublicKeyInfo binary data */
213 pkey = d2i_PUBKEY(NULL, (const unsigned char **)&tmp, len);
214 if (pkey) {
215 /* success, it's most likely SubjectPublicKeyInfo pubkey */
216 ret = 1;
217 } else {
218 /* fail, it's most likely not SubjectPublicKeyInfo pubkey */
219 ret = 0;
220 }
221
222cleanup:
223 EVP_PKEY_free(pkey);
224 free(bin);
225 return ret;
226}
227
roman2eab4742023-06-06 10:00:26 +0200228#endif /* NC_ENABLED_SSH_TLS */
229
Michal Vaskobe52dc22018-10-17 09:28:17 +0200230int
romanc1d2b092023-02-02 08:58:27 +0100231nc_sock_configure_keepalive(int sock, struct nc_keepalives *ka)
Michal Vaskobe52dc22018-10-17 09:28:17 +0200232{
233 int opt;
234
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200235 opt = ka->enabled;
Michal Vaskobe52dc22018-10-17 09:28:17 +0200236 if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200237 ERR(NULL, "Could not set SO_KEEPALIVE option (%s).", strerror(errno));
Michal Vaskobe52dc22018-10-17 09:28:17 +0200238 return -1;
239 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200240 if (!ka->enabled) {
241 return 0;
242 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200243
244#ifdef TCP_KEEPIDLE
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200245 opt = ka->idle_time;
Michal Vaskobe52dc22018-10-17 09:28:17 +0200246 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200247 ERR(NULL, "Setsockopt failed (%s).", strerror(errno));
Michal Vaskobe52dc22018-10-17 09:28:17 +0200248 return -1;
249 }
250#endif
251
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200252#ifdef TCP_KEEPCNT
253 opt = ka->max_probes;
254 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200255 ERR(NULL, "Setsockopt failed (%s).", strerror(errno));
Michal Vaskobe52dc22018-10-17 09:28:17 +0200256 return -1;
257 }
258#endif
259
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200260#ifdef TCP_KEEPINTVL
261 opt = ka->probe_interval;
262 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200263 ERR(NULL, "Setsockopt failed (%s).", strerror(errno));
Michal Vaskobe52dc22018-10-17 09:28:17 +0200264 return -1;
265 }
266#endif
267
268 return 0;
269}
270
Michal Vaskoade892d2017-02-22 13:40:35 +0100271struct nc_session *
Michal Vasko131120a2018-05-29 15:44:02 +0200272nc_new_session(NC_SIDE side, int shared_ti)
Michal Vaskoade892d2017-02-22 13:40:35 +0100273{
274 struct nc_session *sess;
275
276 sess = calloc(1, sizeof *sess);
277 if (!sess) {
278 return NULL;
279 }
280
Michal Vasko131120a2018-05-29 15:44:02 +0200281 sess->side = side;
282
283 if (side == NC_SERVER) {
Michal Vaskodf68e7e2022-04-21 11:04:00 +0200284 pthread_mutex_init(&sess->opts.server.ntf_status_lock, NULL);
Michal Vaskoacf98472021-02-04 15:33:57 +0100285 pthread_mutex_init(&sess->opts.server.rpc_lock, NULL);
286 pthread_cond_init(&sess->opts.server.rpc_cond, NULL);
Michal Vaskoacf98472021-02-04 15:33:57 +0100287
288 pthread_mutex_init(&sess->opts.server.ch_lock, NULL);
289 pthread_cond_init(&sess->opts.server.ch_cond, NULL);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +0200290 } else {
291 pthread_mutex_init(&sess->opts.client.msgs_lock, NULL);
Michal Vasko131120a2018-05-29 15:44:02 +0200292 }
293
294 if (!shared_ti) {
295 sess->io_lock = malloc(sizeof *sess->io_lock);
296 if (!sess->io_lock) {
297 goto error;
298 }
299 pthread_mutex_init(sess->io_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +0100300 }
301
302 return sess;
Michal Vasko131120a2018-05-29 15:44:02 +0200303
304error:
Michal Vasko131120a2018-05-29 15:44:02 +0200305 free(sess);
306 return NULL;
Michal Vaskoade892d2017-02-22 13:40:35 +0100307}
308
Michal Vasko96164bf2016-01-21 15:41:58 +0100309/*
310 * @return 1 - success
311 * 0 - timeout
312 * -1 - error
313 */
314int
Michal Vasko131120a2018-05-29 15:44:02 +0200315nc_session_rpc_lock(struct nc_session *session, int timeout, const char *func)
Michal Vasko96164bf2016-01-21 15:41:58 +0100316{
317 int ret;
Michal Vasko62be1ce2016-03-03 13:24:52 +0100318 struct timespec ts_timeout;
Michal Vasko96164bf2016-01-21 15:41:58 +0100319
Michal Vasko131120a2018-05-29 15:44:02 +0200320 if (session->side != NC_SERVER) {
321 ERRINT;
322 return -1;
323 }
324
Michal Vasko96164bf2016-01-21 15:41:58 +0100325 if (timeout > 0) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100326 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100327
Michal Vaskoade892d2017-02-22 13:40:35 +0100328 /* LOCK */
Michal Vaskod8a74192023-02-06 15:51:50 +0100329 ret = pthread_mutex_clocklock(&session->opts.server.rpc_lock, COMPAT_CLOCK_ID, &ts_timeout);
Michal Vaskoade892d2017-02-22 13:40:35 +0100330 if (!ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100331 while (session->opts.server.rpc_inuse) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100332 ret = pthread_cond_clockwait(&session->opts.server.rpc_cond, &session->opts.server.rpc_lock,
333 COMPAT_CLOCK_ID, &ts_timeout);
Michal Vaskoade892d2017-02-22 13:40:35 +0100334 if (ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100335 pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100336 break;
337 }
338 }
339 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100340 } else if (!timeout) {
Michal Vaskoade892d2017-02-22 13:40:35 +0100341 /* LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100342 ret = pthread_mutex_trylock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100343 if (!ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100344 if (session->opts.server.rpc_inuse) {
345 pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100346 return 0;
347 }
Michal vasko2f8e4b52016-10-05 13:04:11 +0200348 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100349 } else { /* timeout == -1 */
Michal Vaskoade892d2017-02-22 13:40:35 +0100350 /* LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100351 ret = pthread_mutex_lock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100352 if (!ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100353 while (session->opts.server.rpc_inuse) {
354 ret = pthread_cond_wait(&session->opts.server.rpc_cond, &session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100355 if (ret) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100356 pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100357 break;
358 }
359 }
360 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100361 }
362
Michal Vaskoade892d2017-02-22 13:40:35 +0100363 if (ret) {
364 if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
365 /* timeout */
366 return 0;
367 }
368
Michal Vasko96164bf2016-01-21 15:41:58 +0100369 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200370 ERR(session, "%s: failed to RPC lock a session (%s).", func, strerror(ret));
Michal Vasko96164bf2016-01-21 15:41:58 +0100371 return -1;
372 }
373
374 /* ok */
Michal Vaskoacf98472021-02-04 15:33:57 +0100375 assert(session->opts.server.rpc_inuse == 0);
376 session->opts.server.rpc_inuse = 1;
Michal Vaskoade892d2017-02-22 13:40:35 +0100377
378 /* UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100379 ret = pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100380 if (ret) {
381 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200382 ERR(session, "%s: failed to RPC unlock a session (%s).", func, strerror(ret));
Michal Vaskoade892d2017-02-22 13:40:35 +0100383 return -1;
384 }
385
386 return 1;
387}
388
389int
Michal Vasko131120a2018-05-29 15:44:02 +0200390nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func)
Michal Vaskoade892d2017-02-22 13:40:35 +0100391{
392 int ret;
393 struct timespec ts_timeout;
394
Michal Vasko131120a2018-05-29 15:44:02 +0200395 if (session->side != NC_SERVER) {
396 ERRINT;
397 return -1;
398 }
399
Michal Vaskoacf98472021-02-04 15:33:57 +0100400 assert(session->opts.server.rpc_inuse);
Michal Vaskoade892d2017-02-22 13:40:35 +0100401
402 if (timeout > 0) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100403 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vaskoade892d2017-02-22 13:40:35 +0100404
405 /* LOCK */
Michal Vaskod8a74192023-02-06 15:51:50 +0100406 ret = pthread_mutex_clocklock(&session->opts.server.rpc_lock, COMPAT_CLOCK_ID, &ts_timeout);
Michal Vaskoade892d2017-02-22 13:40:35 +0100407 } else if (!timeout) {
408 /* LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100409 ret = pthread_mutex_trylock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100410 } else { /* timeout == -1 */
411 /* LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100412 ret = pthread_mutex_lock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100413 }
414
415 if (ret && (ret != EBUSY) && (ret != ETIMEDOUT)) {
416 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200417 ERR(session, "%s: failed to RPC lock a session (%s).", func, strerror(ret));
Michal Vaskoade892d2017-02-22 13:40:35 +0100418 return -1;
419 } else if (ret) {
Michal Vasko69e98752022-12-14 14:20:17 +0100420 WRN(session, "%s: session RPC lock timeout, should not happen.", func);
Michal Vaskoade892d2017-02-22 13:40:35 +0100421 }
422
Michal Vaskoacf98472021-02-04 15:33:57 +0100423 session->opts.server.rpc_inuse = 0;
424 pthread_cond_signal(&session->opts.server.rpc_cond);
Michal Vaskoade892d2017-02-22 13:40:35 +0100425
426 if (!ret) {
427 /* UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100428 ret = pthread_mutex_unlock(&session->opts.server.rpc_lock);
Michal Vaskoade892d2017-02-22 13:40:35 +0100429 if (ret) {
430 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200431 ERR(session, "%s: failed to RPC unlock a session (%s).", func, strerror(ret));
Michal Vaskoade892d2017-02-22 13:40:35 +0100432 return -1;
433 }
434 }
435
Michal Vasko96164bf2016-01-21 15:41:58 +0100436 return 1;
437}
438
Michal Vasko131120a2018-05-29 15:44:02 +0200439int
440nc_session_io_lock(struct nc_session *session, int timeout, const char *func)
441{
442 int ret;
443 struct timespec ts_timeout;
444
445 if (timeout > 0) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100446 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko131120a2018-05-29 15:44:02 +0200447
Michal Vaskod8a74192023-02-06 15:51:50 +0100448 ret = pthread_mutex_clocklock(session->io_lock, COMPAT_CLOCK_ID, &ts_timeout);
Michal Vasko131120a2018-05-29 15:44:02 +0200449 } else if (!timeout) {
450 ret = pthread_mutex_trylock(session->io_lock);
451 } else { /* timeout == -1 */
Robin Jarry54ea2962018-10-10 10:33:40 +0200452 ret = pthread_mutex_lock(session->io_lock);
Michal Vasko131120a2018-05-29 15:44:02 +0200453 }
454
455 if (ret) {
456 if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
457 /* timeout */
458 return 0;
459 }
460
461 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200462 ERR(session, "%s: failed to IO lock a session (%s).", func, strerror(ret));
Michal Vasko131120a2018-05-29 15:44:02 +0200463 return -1;
464 }
465
466 return 1;
467}
468
469int
470nc_session_io_unlock(struct nc_session *session, const char *func)
471{
472 int ret;
473
474 ret = pthread_mutex_unlock(session->io_lock);
475 if (ret) {
476 /* error */
Michal Vasko05532772021-06-03 12:12:38 +0200477 ERR(session, "%s: failed to IO unlock a session (%s).", func, strerror(ret));
Michal Vasko131120a2018-05-29 15:44:02 +0200478 return -1;
479 }
480
481 return 1;
482}
483
Michal Vasko01130bd2021-08-26 11:47:38 +0200484int
485nc_session_client_msgs_lock(struct nc_session *session, int *timeout, const char *func)
486{
487 int ret;
488 int32_t diff_msec;
roman6ece9c52022-06-22 09:29:17 +0200489 struct timespec ts_timeout, ts_start;
Michal Vasko01130bd2021-08-26 11:47:38 +0200490
491 assert(session->side == NC_CLIENT);
492
493 if (*timeout > 0) {
494 /* get current time */
Michal Vaskod8a74192023-02-06 15:51:50 +0100495 nc_timeouttime_get(&ts_start, 0);
Michal Vasko01130bd2021-08-26 11:47:38 +0200496
Michal Vaskod8a74192023-02-06 15:51:50 +0100497 nc_timeouttime_get(&ts_timeout, *timeout);
Michal Vasko01130bd2021-08-26 11:47:38 +0200498
Michal Vaskod8a74192023-02-06 15:51:50 +0100499 ret = pthread_mutex_clocklock(&session->opts.client.msgs_lock, COMPAT_CLOCK_ID, &ts_timeout);
Michal Vasko01130bd2021-08-26 11:47:38 +0200500 if (!ret) {
501 /* update timeout based on what was elapsed */
Michal Vaskod8a74192023-02-06 15:51:50 +0100502 diff_msec = nc_timeouttime_cur_diff(&ts_start);
Michal Vasko01130bd2021-08-26 11:47:38 +0200503 *timeout -= diff_msec;
504 }
505 } else if (!*timeout) {
506 ret = pthread_mutex_trylock(&session->opts.client.msgs_lock);
507 } else { /* timeout == -1 */
508 ret = pthread_mutex_lock(&session->opts.client.msgs_lock);
509 }
510
511 if (ret) {
512 if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
513 /* timeout */
514 return 0;
515 }
516
517 /* error */
518 ERR(session, "%s: failed to MSGS lock a session (%s).", func, strerror(ret));
519 return -1;
520 }
521
522 return 1;
523}
524
525int
526nc_session_client_msgs_unlock(struct nc_session *session, const char *func)
527{
528 int ret;
529
530 assert(session->side == NC_CLIENT);
531
532 ret = pthread_mutex_unlock(&session->opts.client.msgs_lock);
533 if (ret) {
534 /* error */
535 ERR(session, "%s: failed to MSGS unlock a session (%s).", func, strerror(ret));
536 return -1;
537 }
538
539 return 1;
540}
541
Michal Vasko8dadf782016-01-15 10:29:36 +0100542API NC_STATUS
543nc_session_get_status(const struct nc_session *session)
544{
roman40672412023-05-04 11:10:22 +0200545 NC_CHECK_ARG_RET(session, session, NC_STATUS_ERR);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100546
Michal Vasko8dadf782016-01-15 10:29:36 +0100547 return session->status;
548}
549
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100550API NC_SESSION_TERM_REASON
Michal Vasko142cfea2017-08-07 10:12:11 +0200551nc_session_get_term_reason(const struct nc_session *session)
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100552{
roman40672412023-05-04 11:10:22 +0200553 NC_CHECK_ARG_RET(session, session, NC_SESSION_TERM_ERR);
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100554
555 return session->term_reason;
556}
557
Michal Vasko8dadf782016-01-15 10:29:36 +0100558API uint32_t
Michal Vasko142cfea2017-08-07 10:12:11 +0200559nc_session_get_killed_by(const struct nc_session *session)
560{
roman40672412023-05-04 11:10:22 +0200561 NC_CHECK_ARG_RET(session, session, 0);
Michal Vasko142cfea2017-08-07 10:12:11 +0200562
563 return session->killed_by;
564}
565
566API uint32_t
Michal Vasko8dadf782016-01-15 10:29:36 +0100567nc_session_get_id(const struct nc_session *session)
568{
roman40672412023-05-04 11:10:22 +0200569 NC_CHECK_ARG_RET(session, session, 0);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100570
Michal Vasko8dadf782016-01-15 10:29:36 +0100571 return session->id;
572}
573
Michal Vasko174fe8e2016-02-17 15:38:09 +0100574API int
575nc_session_get_version(const struct nc_session *session)
576{
roman40672412023-05-04 11:10:22 +0200577 NC_CHECK_ARG_RET(session, session, -1);
Michal Vasko174fe8e2016-02-17 15:38:09 +0100578
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200579 return session->version == NC_VERSION_10 ? 0 : 1;
Michal Vasko174fe8e2016-02-17 15:38:09 +0100580}
581
Michal Vasko8dadf782016-01-15 10:29:36 +0100582API NC_TRANSPORT_IMPL
583nc_session_get_ti(const struct nc_session *session)
584{
roman40672412023-05-04 11:10:22 +0200585 NC_CHECK_ARG_RET(session, session, 0);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100586
Michal Vasko8dadf782016-01-15 10:29:36 +0100587 return session->ti_type;
588}
589
590API const char *
591nc_session_get_username(const struct nc_session *session)
592{
roman40672412023-05-04 11:10:22 +0200593 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100594
Michal Vasko8dadf782016-01-15 10:29:36 +0100595 return session->username;
596}
597
598API const char *
599nc_session_get_host(const struct nc_session *session)
600{
roman40672412023-05-04 11:10:22 +0200601 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100602
Michal Vasko8dadf782016-01-15 10:29:36 +0100603 return session->host;
604}
605
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200606API const char *
607nc_session_get_path(const struct nc_session *session)
608{
roman40672412023-05-04 11:10:22 +0200609 NC_CHECK_ARG_RET(session, session, NULL);
610
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200611 if (session->ti_type != NC_TI_UNIX) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200612 return NULL;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200613 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200614
615 return session->path;
616}
617
Michal Vasko8dadf782016-01-15 10:29:36 +0100618API uint16_t
619nc_session_get_port(const struct nc_session *session)
620{
roman40672412023-05-04 11:10:22 +0200621 NC_CHECK_ARG_RET(session, session, 0);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100622
Michal Vasko8dadf782016-01-15 10:29:36 +0100623 return session->port;
624}
625
Michal Vasko93224072021-11-09 12:14:28 +0100626API const struct ly_ctx *
Michal Vasko9a25e932016-02-01 10:36:42 +0100627nc_session_get_ctx(const struct nc_session *session)
628{
roman40672412023-05-04 11:10:22 +0200629 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vasko9a25e932016-02-01 10:36:42 +0100630
631 return session->ctx;
632}
633
Michal Vasko2cc4c682016-03-01 09:16:48 +0100634API void
635nc_session_set_data(struct nc_session *session, void *data)
636{
637 if (!session) {
roman40672412023-05-04 11:10:22 +0200638 ERRARG(NULL, "session");
Michal Vasko2cc4c682016-03-01 09:16:48 +0100639 return;
640 }
641
642 session->data = data;
643}
644
645API void *
646nc_session_get_data(const struct nc_session *session)
647{
roman40672412023-05-04 11:10:22 +0200648 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vasko2cc4c682016-03-01 09:16:48 +0100649
650 return session->data;
651}
652
Michal Vaskodc96bb92023-03-28 08:52:48 +0200653API int
654nc_session_is_callhome(const struct nc_session *session)
655{
roman40672412023-05-04 11:10:22 +0200656 NC_CHECK_ARG_RET(session, session, 0);
Michal Vaskodc96bb92023-03-28 08:52:48 +0200657
658 if (session->flags & NC_SESSION_CALLHOME) {
659 return 1;
660 }
661
662 return 0;
663}
664
Michal Vasko086311b2016-01-08 09:53:11 +0100665NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200666nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200667{
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100668 if (session->ctx != LYD_CTX(op)) {
669 ERR(session, "RPC \"%s\" was created in different context than that of the session.", LYD_NAME(op));
Michal Vasko086311b2016-01-08 09:53:11 +0100670 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100671 }
672
Michal Vasko131120a2018-05-29 15:44:02 +0200673 return nc_write_msg_io(session, io_timeout, NC_MSG_RPC, op, NULL);
Michal Vasko7df39ec2015-12-09 15:26:24 +0100674}
675
Michal Vaskod4da3632022-05-25 11:49:10 +0200676/**
677 * @brief Send \<close-session\> and read the reply on a session.
678 *
679 * @param[in] session Closing NETCONF session.
680 */
681static void
682nc_session_free_close_session(struct nc_session *session)
683{
684 struct ly_in *msg;
685 struct lyd_node *close_rpc, *envp;
686 const struct lys_module *ietfnc;
687
688 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
689 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200690 WRN(session, "Missing ietf-netconf module in context, unable to send <close-session>.");
Michal Vaskod4da3632022-05-25 11:49:10 +0200691 return;
692 }
693 if (lyd_new_inner(NULL, ietfnc, "close-session", 0, &close_rpc)) {
694 WRN(session, "Failed to create <close-session> RPC.");
695 return;
696 }
697
698 /* send the RPC */
699 nc_send_msg_io(session, NC_SESSION_FREE_LOCK_TIMEOUT, close_rpc);
700
701read_msg:
702 switch (nc_read_msg_poll_io(session, NC_CLOSE_REPLY_TIMEOUT, &msg)) {
703 case 1:
704 if (!strncmp(ly_in_memory(msg, NULL), "<notification", 13)) {
705 /* ignore */
706 ly_in_free(msg, 1);
707 goto read_msg;
708 }
709 if (lyd_parse_op(session->ctx, close_rpc, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, &envp, NULL)) {
710 WRN(session, "Failed to parse <close-session> reply.");
711 } else if (!lyd_child(envp) || strcmp(LYD_NAME(lyd_child(envp)), "ok")) {
712 WRN(session, "Reply to <close-session> was not <ok> as expected.");
713 }
714 lyd_free_tree(envp);
715 ly_in_free(msg, 1);
716 break;
717 case 0:
718 WRN(session, "Timeout for receiving a reply to <close-session> elapsed.");
719 break;
720 case -1:
721 ERR(session, "Failed to receive a reply to <close-session>.");
722 break;
723 default:
724 /* cannot happen */
725 break;
726 }
727 lyd_free_tree(close_rpc);
728}
729
Michal Vasko33476c32022-09-09 11:21:40 +0200730/**
731 * @brief Free transport implementation members of a session.
732 *
733 * @param[in] session Session to free.
734 * @param[out] multisession Whether there are other NC sessions on the same SSH sessions.
735 */
736static void
737nc_session_free_transport(struct nc_session *session, int *multisession)
738{
739 int connected; /* flag to indicate whether the transport socket is still connected */
Michal Vaskoe44f2702022-12-12 07:58:06 +0100740 int sock = -1;
Michal Vasko33476c32022-09-09 11:21:40 +0200741 struct nc_session *siter;
742
743 *multisession = 0;
744 connected = nc_session_is_connected(session);
745
746 /* transport implementation cleanup */
747 switch (session->ti_type) {
748 case NC_TI_FD:
749 /* nothing needed - file descriptors were provided by caller,
750 * so it is up to the caller to close them correctly
751 * TODO use callbacks
752 */
753 /* just to avoid compiler warning */
754 (void)connected;
755 (void)siter;
756 break;
757
758 case NC_TI_UNIX:
759 sock = session->ti.unixsock.sock;
760 (void)connected;
761 (void)siter;
762 break;
763
roman2eab4742023-06-06 10:00:26 +0200764#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoe44f2702022-12-12 07:58:06 +0100765 case NC_TI_LIBSSH: {
766 int r;
767
Michal Vasko33476c32022-09-09 11:21:40 +0200768 if (connected) {
Michal Vasko64734402022-09-09 11:22:00 +0200769 ssh_channel_send_eof(session->ti.libssh.channel);
Michal Vasko33476c32022-09-09 11:21:40 +0200770 ssh_channel_free(session->ti.libssh.channel);
771 }
772 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
773 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
774 * it. Also, avoid concurrent free by multiple threads of sessions that share the SSH session.
775 */
776 /* SESSION IO LOCK */
777 r = nc_session_io_lock(session, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
778
779 if (session->ti.libssh.next) {
780 for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
781 if (siter->status != NC_STATUS_STARTING) {
782 *multisession = 1;
783 break;
784 }
785 }
786 }
787
788 if (!*multisession) {
789 /* it's not multisession yet, but we still need to free the starting sessions */
790 if (session->ti.libssh.next) {
791 do {
792 siter = session->ti.libssh.next;
793 session->ti.libssh.next = siter->ti.libssh.next;
794
795 /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */
796 free(siter->username);
797 free(siter->host);
798 if (!(siter->flags & NC_SESSION_SHAREDCTX)) {
799 ly_ctx_destroy((struct ly_ctx *)siter->ctx);
800 }
801
802 free(siter);
803 } while (session->ti.libssh.next != session);
804 }
805 /* remember sock so we can close it */
806 sock = ssh_get_fd(session->ti.libssh.session);
807 if (connected) {
808 ssh_disconnect(session->ti.libssh.session);
809 sock = -1;
810 }
811 ssh_free(session->ti.libssh.session);
812 } else {
813 /* remove the session from the list */
814 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next) {}
815 if (session->ti.libssh.next == siter) {
816 /* there will be only one session */
817 siter->ti.libssh.next = NULL;
818 } else {
819 /* there are still multiple sessions, keep the ring list */
820 siter->ti.libssh.next = session->ti.libssh.next;
821 }
Michal Vasko33476c32022-09-09 11:21:40 +0200822 }
823
824 /* SESSION IO UNLOCK */
825 if (r == 1) {
826 nc_session_io_unlock(session, __func__);
827 }
828 break;
Michal Vaskoe44f2702022-12-12 07:58:06 +0100829 }
Michal Vasko33476c32022-09-09 11:21:40 +0200830 case NC_TI_OPENSSL:
831 /* remember sock so we can close it */
832 sock = SSL_get_fd(session->ti.tls);
833
834 if (connected) {
835 SSL_shutdown(session->ti.tls);
836 }
837 SSL_free(session->ti.tls);
838
839 if (session->side == NC_SERVER) {
840 X509_free(session->opts.server.client_cert);
841 }
842 break;
roman2eab4742023-06-06 10:00:26 +0200843#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko33476c32022-09-09 11:21:40 +0200844 case NC_TI_NONE:
845 break;
846 }
847
848 /* close socket separately */
849 if (sock > -1) {
850 close(sock);
851 }
852}
853
Radek Krejci695d4fa2015-10-22 13:23:54 +0200854API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100855nc_session_free(struct nc_session *session, void (*data_free)(void *))
Radek Krejci695d4fa2015-10-22 13:23:54 +0200856{
Michal Vasko33476c32022-09-09 11:21:40 +0200857 int r, i, rpc_locked = 0, msgs_locked = 0, timeout;
Michal Vaskob48aa812016-01-18 14:13:09 +0100858 int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */
Michal Vaskoad611702015-12-03 13:41:51 +0100859 struct nc_msg_cont *contiter;
Michal Vasko77367452021-02-16 16:32:18 +0100860 struct ly_in *msg;
roman6ece9c52022-06-22 09:29:17 +0200861 struct timespec ts;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200862 void *p;
863
Michal Vasko428087d2016-01-14 16:04:28 +0100864 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200865 return;
866 }
867
Michal Vaskoa8ec54b2022-10-20 09:59:07 +0200868 /* stop notification threads if any */
869 if ((session->side == NC_CLIENT) && ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
870 /* let the threads know they should quit */
871 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200872
Michal Vaskoa8ec54b2022-10-20 09:59:07 +0200873 /* wait for them */
Michal Vaskod8a74192023-02-06 15:51:50 +0100874 nc_timeouttime_get(&ts, NC_SESSION_FREE_LOCK_TIMEOUT);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +0200875 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_count)) {
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200876 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +0100877 if (nc_timeouttime_cur_diff(&ts) < 1) {
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200878 ERR(session, "Waiting for notification thread exit failed (timed out).");
879 break;
880 }
881 }
Michal Vasko86d357c2016-03-11 13:46:38 +0100882 }
883
Michal Vaskoacf98472021-02-04 15:33:57 +0100884 if (session->side == NC_SERVER) {
Michal Vasko131120a2018-05-29 15:44:02 +0200885 r = nc_session_rpc_lock(session, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100886 if (r == -1) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100887 return;
Michal Vasko131120a2018-05-29 15:44:02 +0200888 } else if (r) {
889 rpc_locked = 1;
Michal Vasko96a28a32021-02-04 15:35:20 +0100890 } else {
891 /* else failed to lock it, too bad */
Michal Vasko05532772021-06-03 12:12:38 +0200892 ERR(session, "Freeing a session while an RPC is being processed.");
Michal Vasko96a28a32021-02-04 15:35:20 +0100893 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200894 }
895
Michal Vasko8c247822020-09-07 13:23:23 +0200896 if (session->side == NC_CLIENT) {
Michal Vasko01130bd2021-08-26 11:47:38 +0200897 timeout = NC_SESSION_FREE_LOCK_TIMEOUT;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +0200898
Michal Vasko01130bd2021-08-26 11:47:38 +0200899 /* MSGS LOCK */
900 r = nc_session_client_msgs_lock(session, &timeout, __func__);
901 if (r == -1) {
902 return;
903 } else if (r) {
904 msgs_locked = 1;
905 } else {
906 /* else failed to lock it, too bad */
907 ERR(session, "Freeing a session while messages are being received.");
908 }
909
910 /* cleanup message queue */
tadeas-vintrlik54f142a2021-08-23 10:36:18 +0200911 for (contiter = session->opts.client.msgs; contiter; ) {
Michal Vasko77367452021-02-16 16:32:18 +0100912 ly_in_free(contiter->msg, 1);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200913
Michal Vaskoad611702015-12-03 13:41:51 +0100914 p = contiter;
915 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200916 free(p);
917 }
918
Michal Vasko01130bd2021-08-26 11:47:38 +0200919 if (msgs_locked) {
920 /* MSGS UNLOCK */
921 nc_session_client_msgs_unlock(session, __func__);
922 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200923
Michal Vasko8c247822020-09-07 13:23:23 +0200924 if (session->status == NC_STATUS_RUNNING) {
Michal Vasko9c6d38c2021-09-03 13:02:53 +0200925 /* receive any leftover messages */
926 while (nc_read_msg_poll_io(session, 0, &msg) == 1) {
927 ly_in_free(msg, 1);
928 }
929
Michal Vasko8c247822020-09-07 13:23:23 +0200930 /* send closing info to the other side */
Michal Vaskod4da3632022-05-25 11:49:10 +0200931 nc_session_free_close_session(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200932 }
933
934 /* list of server's capabilities */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200935 if (session->opts.client.cpblts) {
936 for (i = 0; session->opts.client.cpblts[i]; i++) {
Michal Vasko96fc4bb2017-05-23 14:58:34 +0200937 free(session->opts.client.cpblts[i]);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200938 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200939 free(session->opts.client.cpblts);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200940 }
Michal Vasko78939072022-12-12 07:43:18 +0100941
942 /* LY ext data */
roman2eab4742023-06-06 10:00:26 +0200943#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoe44f2702022-12-12 07:58:06 +0100944 struct nc_session *siter;
945
Michal Vasko78939072022-12-12 07:43:18 +0100946 if ((session->flags & NC_SESSION_SHAREDCTX) && session->ti.libssh.next) {
947 for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
948 if (siter->status != NC_STATUS_STARTING) {
949 /* move LY ext data to this session */
950 assert(!siter->opts.client.ext_data);
951 siter->opts.client.ext_data = session->opts.client.ext_data;
952 session->opts.client.ext_data = NULL;
953 break;
954 }
955 }
Michal Vaskoe44f2702022-12-12 07:58:06 +0100956 } else
roman2eab4742023-06-06 10:00:26 +0200957#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskoe44f2702022-12-12 07:58:06 +0100958 {
Michal Vasko78939072022-12-12 07:43:18 +0100959 lyd_free_siblings(session->opts.client.ext_data);
960 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200961 }
962
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100963 if (session->data && data_free) {
964 data_free(session->data);
965 }
966
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200967 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
968 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +0100969 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200970 }
971
Michal Vasko86d357c2016-03-11 13:46:38 +0100972 /* mark session for closing */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200973 session->status = NC_STATUS_CLOSING;
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200974
Michal Vaskofeccb312022-03-24 15:24:59 +0100975 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CH_THREAD)) {
Michal Vaskoacf98472021-02-04 15:33:57 +0100976 pthread_cond_signal(&session->opts.server.ch_cond);
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200977
Michal Vaskod8a74192023-02-06 15:51:50 +0100978 nc_timeouttime_get(&ts, NC_SESSION_FREE_LOCK_TIMEOUT);
Michal Vasko0db3db52021-03-03 10:45:42 +0100979
980 /* wait for CH thread to actually wake up and terminate */
981 r = 0;
Michal Vaskofeccb312022-03-24 15:24:59 +0100982 while (!r && (session->flags & NC_SESSION_CH_THREAD)) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100983 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 +0100984 }
Michal Vasko0db3db52021-03-03 10:45:42 +0100985 if (r) {
Michal Vasko05532772021-06-03 12:12:38 +0200986 ERR(session, "Waiting for Call Home thread failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +0100987 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200988 }
989
Michal Vaskofeccb312022-03-24 15:24:59 +0100990 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
991 /* CH UNLOCK */
992 pthread_mutex_unlock(&session->opts.server.ch_lock);
993 }
994
Radek Krejci695d4fa2015-10-22 13:23:54 +0200995 /* transport implementation cleanup */
Michal Vasko33476c32022-09-09 11:21:40 +0200996 nc_session_free_transport(session, &multisession);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200997
Michal Vasko33476c32022-09-09 11:21:40 +0200998 /* final cleanup */
Michal Vasko93224072021-11-09 12:14:28 +0100999 free(session->username);
1000 free(session->host);
1001 free(session->path);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001002
Michal Vaskoacf98472021-02-04 15:33:57 +01001003 if (session->side == NC_SERVER) {
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001004 pthread_mutex_destroy(&session->opts.server.ntf_status_lock);
Michal Vasko131120a2018-05-29 15:44:02 +02001005 if (rpc_locked) {
1006 nc_session_rpc_unlock(session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko9e99f012016-03-03 13:25:20 +01001007 }
Michal Vaskoacf98472021-02-04 15:33:57 +01001008 pthread_mutex_destroy(&session->opts.server.rpc_lock);
1009 pthread_cond_destroy(&session->opts.server.rpc_cond);
Michal Vasko131120a2018-05-29 15:44:02 +02001010 }
1011
1012 if (session->io_lock && !multisession) {
1013 pthread_mutex_destroy(session->io_lock);
1014 free(session->io_lock);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001015 }
1016
1017 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
Michal Vasko93224072021-11-09 12:14:28 +01001018 ly_ctx_destroy((struct ly_ctx *)session->ctx);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001019 }
1020
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001021 if (session->side == NC_SERVER) {
Michal Vaskoacf98472021-02-04 15:33:57 +01001022 /* free CH synchronization structures */
1023 pthread_cond_destroy(&session->opts.server.ch_cond);
1024 pthread_mutex_destroy(&session->opts.server.ch_lock);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001025 } else {
1026 pthread_mutex_destroy(&session->opts.client.msgs_lock);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001027 }
1028
Radek Krejci695d4fa2015-10-22 13:23:54 +02001029 free(session);
1030}
1031
Michal Vasko086311b2016-01-08 09:53:11 +01001032static void
Michal Vasko93224072021-11-09 12:14:28 +01001033add_cpblt(const char *capab, char ***cpblts, int *size, int *count)
Michal Vasko086311b2016-01-08 09:53:11 +01001034{
Radek Krejci658782b2016-12-04 22:04:55 +01001035 size_t len;
1036 int i;
1037 char *p;
1038
1039 if (capab) {
1040 /* check if already present */
1041 p = strchr(capab, '?');
1042 if (p) {
1043 len = p - capab;
1044 } else {
1045 len = strlen(capab);
1046 }
1047 for (i = 0; i < *count; i++) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001048 if (!strncmp((*cpblts)[i], capab, len) && (((*cpblts)[i][len] == '\0') || ((*cpblts)[i][len] == '?'))) {
Radek Krejci658782b2016-12-04 22:04:55 +01001049 /* already present, do not duplicate it */
1050 return;
1051 }
1052 }
1053 }
1054
1055 /* add another capability */
Michal Vasko086311b2016-01-08 09:53:11 +01001056 if (*count == *size) {
1057 *size += 5;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001058 *cpblts = nc_realloc(*cpblts, *size * sizeof **cpblts);
1059 if (!(*cpblts)) {
1060 ERRMEM;
1061 return;
1062 }
Michal Vasko086311b2016-01-08 09:53:11 +01001063 }
1064
Michal Vasko93224072021-11-09 12:14:28 +01001065 (*cpblts)[*count] = capab ? strdup(capab) : NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001066 ++(*count);
1067}
1068
Michal Vasko93224072021-11-09 12:14:28 +01001069API char **
1070nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version)
Michal Vasko086311b2016-01-08 09:53:11 +01001071{
Michal Vasko93224072021-11-09 12:14:28 +01001072 char **cpblts;
Michal Vasko77367452021-02-16 16:32:18 +01001073 const struct lys_module *mod;
1074 struct lysp_feature *feat;
1075 int size = 10, count, features_count = 0, dev_count = 0, str_len, len;
Michal Vasko1440a742021-03-31 11:11:03 +02001076 uint32_t i, u;
Michal Vasko77367452021-02-16 16:32:18 +01001077 LY_ARRAY_COUNT_TYPE v;
Michal Vasko1440a742021-03-31 11:11:03 +02001078 char *yl_content_id;
romanc1d2b092023-02-02 08:58:27 +01001079 uint32_t wd_also_supported;
1080 uint32_t wd_basic_mode;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001081
Radek Krejci24a18412018-05-16 15:09:10 +02001082#define NC_CPBLT_BUF_LEN 4096
Michal Vasko2e47ef92016-06-20 10:03:24 +02001083 char str[NC_CPBLT_BUF_LEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001084
roman40672412023-05-04 11:10:22 +02001085 NC_CHECK_ARG_RET(NULL, ctx, NULL);
Michal Vasko4ffa3b22016-05-24 16:36:25 +02001086
Michal Vasko086311b2016-01-08 09:53:11 +01001087 cpblts = malloc(size * sizeof *cpblts);
roman124a4362023-10-26 15:36:22 +02001088 NC_CHECK_ERRMEM_GOTO(!cpblts, , error);
Michal Vasko93224072021-11-09 12:14:28 +01001089 cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0");
1090 cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1");
Michal Vasko086311b2016-01-08 09:53:11 +01001091 count = 2;
1092
1093 /* capabilities */
1094
Michal Vasko77367452021-02-16 16:32:18 +01001095 mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf");
Michal Vasko086311b2016-01-08 09:53:11 +01001096 if (mod) {
Michal Vasko77367452021-02-16 16:32:18 +01001097 if (lys_feature_value(mod, "writable-running") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001098 add_cpblt("urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001099 }
Michal Vasko77367452021-02-16 16:32:18 +01001100 if (lys_feature_value(mod, "candidate") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001101 add_cpblt("urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count);
Michal Vasko77367452021-02-16 16:32:18 +01001102 if (lys_feature_value(mod, "confirmed-commit") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001103 add_cpblt("urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001104 }
1105 }
Michal Vasko77367452021-02-16 16:32:18 +01001106 if (lys_feature_value(mod, "rollback-on-error") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001107 add_cpblt("urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001108 }
Michal Vasko77367452021-02-16 16:32:18 +01001109 if (lys_feature_value(mod, "validate") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001110 add_cpblt("urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001111 }
Michal Vasko77367452021-02-16 16:32:18 +01001112 if (lys_feature_value(mod, "startup") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001113 add_cpblt("urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001114 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001115
1116 /* The URL capability must be set manually using nc_server_set_capability()
1117 * because of the need for supported protocols to be included.
1118 * https://tools.ietf.org/html/rfc6241#section-8.8.3
1119 */
Michal Vasko77367452021-02-16 16:32:18 +01001120 // if (lys_feature_value(mod, "url") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001121 // add_cpblt("urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count);
mekleoa8de5e92020-02-13 09:05:56 +01001122 // }
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001123
Michal Vasko77367452021-02-16 16:32:18 +01001124 if (lys_feature_value(mod, "xpath") == LY_SUCCESS) {
Michal Vasko93224072021-11-09 12:14:28 +01001125 add_cpblt("urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001126 }
1127 }
1128
Michal Vasko77367452021-02-16 16:32:18 +01001129 mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01001130 if (mod) {
romanc1d2b092023-02-02 08:58:27 +01001131 wd_basic_mode = ATOMIC_LOAD_RELAXED(server_opts.wd_basic_mode);
1132 if (!wd_basic_mode) {
Michal Vasko05532772021-06-03 12:12:38 +02001133 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 +01001134 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001135 strcpy(str, "urn:ietf:params:netconf:capability:with-defaults:1.0");
romanc1d2b092023-02-02 08:58:27 +01001136 switch (wd_basic_mode) {
Michal Vasko086311b2016-01-08 09:53:11 +01001137 case NC_WD_ALL:
1138 strcat(str, "?basic-mode=report-all");
1139 break;
1140 case NC_WD_TRIM:
1141 strcat(str, "?basic-mode=trim");
1142 break;
1143 case NC_WD_EXPLICIT:
1144 strcat(str, "?basic-mode=explicit");
1145 break;
1146 default:
Michal Vasko9e036d52016-01-08 10:49:26 +01001147 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +01001148 break;
1149 }
1150
romanc1d2b092023-02-02 08:58:27 +01001151 wd_also_supported = ATOMIC_LOAD_RELAXED(server_opts.wd_also_supported);
1152 if (wd_also_supported) {
Michal Vasko2e47ef92016-06-20 10:03:24 +02001153 strcat(str, "&also-supported=");
romanc1d2b092023-02-02 08:58:27 +01001154 if (wd_also_supported & NC_WD_ALL) {
Michal Vasko086311b2016-01-08 09:53:11 +01001155 strcat(str, "report-all,");
1156 }
romanc1d2b092023-02-02 08:58:27 +01001157 if (wd_also_supported & NC_WD_ALL_TAG) {
Michal Vasko086311b2016-01-08 09:53:11 +01001158 strcat(str, "report-all-tagged,");
1159 }
romanc1d2b092023-02-02 08:58:27 +01001160 if (wd_also_supported & NC_WD_TRIM) {
Michal Vasko086311b2016-01-08 09:53:11 +01001161 strcat(str, "trim,");
1162 }
romanc1d2b092023-02-02 08:58:27 +01001163 if (wd_also_supported & NC_WD_EXPLICIT) {
Michal Vasko086311b2016-01-08 09:53:11 +01001164 strcat(str, "explicit,");
1165 }
1166 str[strlen(str) - 1] = '\0';
1167
Michal Vasko93224072021-11-09 12:14:28 +01001168 add_cpblt(str, &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001169 }
1170 }
1171 }
1172
Radek Krejci658782b2016-12-04 22:04:55 +01001173 /* other capabilities */
1174 for (u = 0; u < server_opts.capabilities_count; u++) {
Michal Vasko93224072021-11-09 12:14:28 +01001175 add_cpblt(server_opts.capabilities[u], &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001176 }
1177
1178 /* models */
Michal Vasko1440a742021-03-31 11:11:03 +02001179 u = 0;
Radek Krejci24a18412018-05-16 15:09:10 +02001180 while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
Radek Krejci24a18412018-05-16 15:09:10 +02001181 if (!strcmp(mod->name, "ietf-yang-library")) {
Michal Vasko77367452021-02-16 16:32:18 +01001182 if (!mod->revision || (strcmp(mod->revision, "2016-06-21") && strcmp(mod->revision, "2019-01-04"))) {
Michal Vasko05532772021-06-03 12:12:38 +02001183 ERR(NULL, "Unknown \"ietf-yang-library\" revision, only 2016-06-21 and 2019-01-04 are supported.");
Michal Vaskod5ada122020-03-19 18:28:06 +01001184 goto error;
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001185 }
Michal Vaskod5ada122020-03-19 18:28:06 +01001186
Michal Vasko1440a742021-03-31 11:11:03 +02001187 /* get content-id */
1188 if (server_opts.content_id_clb) {
1189 yl_content_id = server_opts.content_id_clb(server_opts.content_id_data);
roman124a4362023-10-26 15:36:22 +02001190 NC_CHECK_ERRMEM_GOTO(!yl_content_id, , error);
Michal Vasko1440a742021-03-31 11:11:03 +02001191 } else {
1192 yl_content_id = malloc(11);
roman124a4362023-10-26 15:36:22 +02001193 NC_CHECK_ERRMEM_GOTO(!yl_content_id, , error);
Michal Vasko1440a742021-03-31 11:11:03 +02001194 sprintf(yl_content_id, "%u", ly_ctx_get_change_count(ctx));
1195 }
1196
Michal Vasko77367452021-02-16 16:32:18 +01001197 if (!strcmp(mod->revision, "2019-01-04")) {
Michal Vasko7b5e3d92020-04-08 14:40:31 +02001198 /* new one (capab defined in RFC 8526 section 2) */
Michal Vasko1440a742021-03-31 11:11:03 +02001199 sprintf(str, "urn:ietf:params:netconf:capability:yang-library:1.1?revision=%s&content-id=%s",
1200 mod->revision, yl_content_id);
Michal Vasko93224072021-11-09 12:14:28 +01001201 add_cpblt(str, &cpblts, &size, &count);
Michal Vasko7b5e3d92020-04-08 14:40:31 +02001202 } else {
1203 /* old one (capab defined in RFC 7950 section 5.6.4) */
Michal Vasko1440a742021-03-31 11:11:03 +02001204 sprintf(str, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s",
1205 mod->revision, yl_content_id);
Michal Vasko93224072021-11-09 12:14:28 +01001206 add_cpblt(str, &cpblts, &size, &count);
Michal Vaskod5ada122020-03-19 18:28:06 +01001207 }
Michal Vasko1440a742021-03-31 11:11:03 +02001208 free(yl_content_id);
Radek Krejci24a18412018-05-16 15:09:10 +02001209 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001210 } else if ((version == LYS_VERSION_1_0) && (mod->parsed->version > version)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001211 /* skip YANG 1.1 modules */
Radek Krejci24a18412018-05-16 15:09:10 +02001212 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001213 } else if ((version == LYS_VERSION_1_1) && (mod->parsed->version != version)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001214 /* skip YANG 1.0 modules */
Radek Krejci24a18412018-05-16 15:09:10 +02001215 continue;
1216 }
Michal Vasko086311b2016-01-08 09:53:11 +01001217
Michal Vasko77367452021-02-16 16:32:18 +01001218 str_len = sprintf(str, "%s?module=%s%s%s", mod->ns, mod->name, mod->revision ? "&revision=" : "",
1219 mod->revision ? mod->revision : "");
Radek Krejci24a18412018-05-16 15:09:10 +02001220
Michal Vaskodafdc742020-03-11 16:15:59 +01001221 features_count = 0;
1222 i = 0;
1223 feat = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001224 while ((feat = lysp_feature_next(feat, mod->parsed, &i))) {
Michal Vaskodafdc742020-03-11 16:15:59 +01001225 if (!(feat->flags & LYS_FENABLED)) {
1226 continue;
Michal Vaskoe90e4d12016-06-20 10:05:01 +02001227 }
Michal Vaskodafdc742020-03-11 16:15:59 +01001228 if (!features_count) {
1229 strcat(str, "&features=");
1230 str_len += 10;
1231 }
1232 len = strlen(feat->name);
1233 if (str_len + 1 + len >= NC_CPBLT_BUF_LEN) {
1234 ERRINT;
1235 break;
1236 }
1237 if (features_count) {
1238 strcat(str, ",");
1239 ++str_len;
1240 }
1241 strcat(str, feat->name);
1242 str_len += len;
1243 features_count++;
Michal Vasko086311b2016-01-08 09:53:11 +01001244 }
Michal Vasko086311b2016-01-08 09:53:11 +01001245
Michal Vasko77367452021-02-16 16:32:18 +01001246 if (mod->deviated_by) {
Radek Krejci24a18412018-05-16 15:09:10 +02001247 strcat(str, "&deviations=");
1248 str_len += 12;
1249 dev_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +01001250 LY_ARRAY_FOR(mod->deviated_by, v) {
1251 len = strlen(mod->deviated_by[v]->name);
1252 if (str_len + 1 + len >= NC_CPBLT_BUF_LEN) {
1253 ERRINT;
1254 break;
Radek Krejci24a18412018-05-16 15:09:10 +02001255 }
Michal Vasko77367452021-02-16 16:32:18 +01001256 if (dev_count) {
1257 strcat(str, ",");
1258 ++str_len;
Radek Krejci24a18412018-05-16 15:09:10 +02001259 }
Michal Vasko77367452021-02-16 16:32:18 +01001260 strcat(str, mod->deviated_by[v]->name);
1261 str_len += len;
1262 dev_count++;
Radek Krejci24a18412018-05-16 15:09:10 +02001263 }
1264 }
1265
Michal Vasko93224072021-11-09 12:14:28 +01001266 add_cpblt(str, &cpblts, &size, &count);
Radek Krejci24a18412018-05-16 15:09:10 +02001267 }
Michal Vasko086311b2016-01-08 09:53:11 +01001268
1269 /* ending NULL capability */
Michal Vasko93224072021-11-09 12:14:28 +01001270 add_cpblt(NULL, &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +01001271
1272 return cpblts;
Radek Krejcif906e412017-09-22 14:44:45 +02001273
1274error:
Radek Krejcif906e412017-09-22 14:44:45 +02001275 free(cpblts);
Radek Krejcif906e412017-09-22 14:44:45 +02001276 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001277}
1278
Michal Vasko93224072021-11-09 12:14:28 +01001279API char **
1280nc_server_get_cpblts(const struct ly_ctx *ctx)
Radek Krejci24a18412018-05-16 15:09:10 +02001281{
1282 return nc_server_get_cpblts_version(ctx, LYS_VERSION_UNDEF);
1283}
1284
Radek Krejci695d4fa2015-10-22 13:23:54 +02001285static int
Michal Vasko77367452021-02-16 16:32:18 +01001286parse_cpblts(struct lyd_node *capabilities, char ***list)
Radek Krejci695d4fa2015-10-22 13:23:54 +02001287{
Michal Vasko77367452021-02-16 16:32:18 +01001288 struct lyd_node *iter;
1289 struct lyd_node_opaq *cpblt;
Michal Vasko156d3272017-04-11 11:46:49 +02001290 int ver = -1, i = 0;
1291 const char *cpb_start, *cpb_end;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001292
1293 if (list) {
1294 /* get the storage for server's capabilities */
Michal Vasko77367452021-02-16 16:32:18 +01001295 LY_LIST_FOR(lyd_child(capabilities), iter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +02001296 i++;
1297 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001298 /* last item remains NULL */
1299 *list = calloc(i + 1, sizeof **list);
roman3a95bb22023-10-26 11:07:17 +02001300 NC_CHECK_ERRMEM_RET(!*list, -1);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001301 i = 0;
1302 }
1303
Michal Vasko77367452021-02-16 16:32:18 +01001304 LY_LIST_FOR(lyd_child(capabilities), iter) {
1305 cpblt = (struct lyd_node_opaq *)iter;
1306
1307 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 +02001308 ERR(NULL, "Unexpected <%s> element in client's <hello>.", cpblt->name.name);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001309 return -1;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001310 }
1311
Michal Vasko156d3272017-04-11 11:46:49 +02001312 /* skip leading/trailing whitespaces */
Michal Vasko77367452021-02-16 16:32:18 +01001313 for (cpb_start = cpblt->value; isspace(cpb_start[0]); ++cpb_start) {}
1314 for (cpb_end = cpblt->value + strlen(cpblt->value); (cpb_end > cpblt->value) && isspace(cpb_end[-1]); --cpb_end) {}
1315 if (!cpb_start[0] || (cpb_end == cpblt->value)) {
Michal Vasko05532772021-06-03 12:12:38 +02001316 ERR(NULL, "Empty capability \"%s\" received.", cpblt->value);
Michal Vasko156d3272017-04-11 11:46:49 +02001317 return -1;
1318 }
1319
Radek Krejci695d4fa2015-10-22 13:23:54 +02001320 /* detect NETCONF version */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001321 if ((ver < 0) && !strncmp(cpb_start, "urn:ietf:params:netconf:base:1.0", cpb_end - cpb_start)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +02001322 ver = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001323 } 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 +02001324 ver = 1;
1325 }
1326
1327 /* store capabilities */
1328 if (list) {
Michal Vasko156d3272017-04-11 11:46:49 +02001329 (*list)[i] = strndup(cpb_start, cpb_end - cpb_start);
roman3a95bb22023-10-26 11:07:17 +02001330 NC_CHECK_ERRMEM_RET(!(*list)[i], -1);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001331 i++;
1332 }
1333 }
1334
1335 if (ver == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001336 ERR(NULL, "Peer does not support a compatible NETCONF version.");
Radek Krejci695d4fa2015-10-22 13:23:54 +02001337 }
1338
1339 return ver;
1340}
1341
1342static NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +02001343nc_send_hello_io(struct nc_session *session)
Michal Vasko086311b2016-01-08 09:53:11 +01001344{
Michal Vasko131120a2018-05-29 15:44:02 +02001345 NC_MSG_TYPE ret;
1346 int i, io_timeout;
Michal Vasko93224072021-11-09 12:14:28 +01001347 char **cpblts;
Michal Vasko131120a2018-05-29 15:44:02 +02001348 uint32_t *sid;
Michal Vasko086311b2016-01-08 09:53:11 +01001349
Michal Vasko131120a2018-05-29 15:44:02 +02001350 if (session->side == NC_CLIENT) {
1351 /* client side hello - send only NETCONF base capabilities */
1352 cpblts = malloc(3 * sizeof *cpblts);
roman3a95bb22023-10-26 11:07:17 +02001353 NC_CHECK_ERRMEM_RET(!cpblts, NC_MSG_ERROR);
Michal Vasko93224072021-11-09 12:14:28 +01001354 cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0");
1355 cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1");
Michal Vasko131120a2018-05-29 15:44:02 +02001356 cpblts[2] = NULL;
1357
1358 io_timeout = NC_CLIENT_HELLO_TIMEOUT * 1000;
1359 sid = NULL;
1360 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001361 cpblts = nc_server_get_cpblts_version(session->ctx, LYS_VERSION_1_0);
Michal Vasko5b24b6b2020-12-04 09:29:47 +01001362 if (!cpblts) {
1363 return NC_MSG_ERROR;
1364 }
Michal Vasko131120a2018-05-29 15:44:02 +02001365
1366 io_timeout = NC_SERVER_HELLO_TIMEOUT * 1000;
1367 sid = &session->id;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001368 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001369
Michal Vasko131120a2018-05-29 15:44:02 +02001370 ret = nc_write_msg_io(session, io_timeout, NC_MSG_HELLO, cpblts, sid);
Michal Vasko086311b2016-01-08 09:53:11 +01001371
Michal Vasko086311b2016-01-08 09:53:11 +01001372 for (i = 0; cpblts[i]; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01001373 free(cpblts[i]);
Michal Vasko086311b2016-01-08 09:53:11 +01001374 }
1375 free(cpblts);
1376
Michal Vasko131120a2018-05-29 15:44:02 +02001377 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001378}
1379
1380static NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +02001381nc_recv_client_hello_io(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +02001382{
Michal Vasko77367452021-02-16 16:32:18 +01001383 struct ly_in *msg;
1384 struct lyd_node *hello = NULL, *iter;
1385 struct lyd_node_opaq *node;
1386 int r, ver = -1, flag = 0;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001387 char *str;
Michal Vasko292c5542023-02-01 14:33:17 +01001388 long long id;
Michal Vasko77367452021-02-16 16:32:18 +01001389 NC_MSG_TYPE rc = NC_MSG_HELLO;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001390
Michal Vasko77367452021-02-16 16:32:18 +01001391 r = nc_read_msg_poll_io(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &msg);
1392 switch (r) {
1393 case 1:
Radek Krejci695d4fa2015-10-22 13:23:54 +02001394 /* parse <hello> data */
Michal Vasko77367452021-02-16 16:32:18 +01001395 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 +02001396 ERR(session, "Failed to parse server <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001397 rc = NC_MSG_ERROR;
1398 goto cleanup;
1399 }
1400
1401 LY_LIST_FOR(lyd_child(hello), iter) {
1402 node = (struct lyd_node_opaq *)iter;
1403
1404 if (!node->name.module_ns || strcmp(node->name.module_ns, NC_NS_BASE)) {
Michal Vasko11d142a2016-01-19 15:58:24 +01001405 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001406 } else if (!strcmp(node->name.name, "session-id")) {
1407 if (!node->value || !strlen(node->value)) {
Michal Vasko05532772021-06-03 12:12:38 +02001408 ERR(session, "No value of <session-id> element in server <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001409 rc = NC_MSG_ERROR;
1410 goto cleanup;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001411 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001412 str = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001413 id = strtoll(node->value, &str, 10);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001414 if (*str || (id < 1) || (id > UINT32_MAX)) {
Michal Vasko05532772021-06-03 12:12:38 +02001415 ERR(session, "Invalid value of <session-id> element in server <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001416 rc = NC_MSG_ERROR;
1417 goto cleanup;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001418 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001419 session->id = (uint32_t)id;
1420 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001421 } else if (strcmp(node->name.name, "capabilities")) {
Michal Vasko05532772021-06-03 12:12:38 +02001422 ERR(session, "Unexpected <%s> element in server <hello>.", node->name.name);
Michal Vasko77367452021-02-16 16:32:18 +01001423 rc = NC_MSG_ERROR;
1424 goto cleanup;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001425 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001426
1427 if (flag) {
1428 /* multiple capabilities elements */
Michal Vasko05532772021-06-03 12:12:38 +02001429 ERR(session, "Invalid <hello> message (multiple <capabilities> elements).");
Michal Vasko77367452021-02-16 16:32:18 +01001430 rc = NC_MSG_ERROR;
1431 goto cleanup;
Michal Vasko11d142a2016-01-19 15:58:24 +01001432 }
1433 flag = 1;
1434
Michal Vasko77367452021-02-16 16:32:18 +01001435 if ((ver = parse_cpblts(&node->node, &session->opts.client.cpblts)) < 0) {
1436 rc = NC_MSG_ERROR;
1437 goto cleanup;
Michal Vasko11d142a2016-01-19 15:58:24 +01001438 }
1439 session->version = ver;
1440 }
1441
1442 if (!session->id) {
Michal Vasko05532772021-06-03 12:12:38 +02001443 ERR(session, "Missing <session-id> in server <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001444 rc = NC_MSG_ERROR;
1445 goto cleanup;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001446 }
1447 break;
Michal Vasko77367452021-02-16 16:32:18 +01001448 case 0:
Michal Vasko05532772021-06-03 12:12:38 +02001449 ERR(session, "Server <hello> timeout elapsed.");
Michal Vasko77367452021-02-16 16:32:18 +01001450 rc = NC_MSG_WOULDBLOCK;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001451 break;
1452 default:
Michal Vasko77367452021-02-16 16:32:18 +01001453 rc = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +02001454 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001455 }
1456
Michal Vasko77367452021-02-16 16:32:18 +01001457cleanup:
1458 ly_in_free(msg, 1);
1459 lyd_free_tree(hello);
1460 return rc;
Radek Krejci5686ff72015-10-09 13:33:56 +02001461}
1462
Michal Vasko11d142a2016-01-19 15:58:24 +01001463static NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +02001464nc_recv_server_hello_io(struct nc_session *session)
Michal Vasko11d142a2016-01-19 15:58:24 +01001465{
Michal Vasko77367452021-02-16 16:32:18 +01001466 struct ly_in *msg;
1467 struct lyd_node *hello = NULL, *iter;
1468 struct lyd_node_opaq *node;
1469 NC_MSG_TYPE rc = NC_MSG_HELLO;
1470 int r, ver = -1, flag = 0, timeout_io;
Michal Vasko11d142a2016-01-19 15:58:24 +01001471
Michal Vasko77367452021-02-16 16:32:18 +01001472 timeout_io = server_opts.hello_timeout ? server_opts.hello_timeout * 1000 : NC_SERVER_HELLO_TIMEOUT * 1000;
1473 r = nc_read_msg_poll_io(session, timeout_io, &msg);
1474 switch (r) {
1475 case 1:
1476 /* parse <hello> data */
1477 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 +02001478 ERR(session, "Failed to parse client <hello>.");
Michal Vasko77367452021-02-16 16:32:18 +01001479 rc = NC_MSG_ERROR;
1480 goto cleanup;
1481 }
Michal Vasko11d142a2016-01-19 15:58:24 +01001482
Michal Vasko77367452021-02-16 16:32:18 +01001483 /* learn NETCONF version */
1484 LY_LIST_FOR(lyd_child(hello), iter) {
1485 node = (struct lyd_node_opaq *)iter;
1486
1487 if (!node->name.module_ns || strcmp(node->name.module_ns, NC_NS_BASE)) {
Michal Vasko11d142a2016-01-19 15:58:24 +01001488 continue;
Michal Vasko77367452021-02-16 16:32:18 +01001489 } else if (strcmp(node->name.name, "capabilities")) {
Michal Vasko05532772021-06-03 12:12:38 +02001490 ERR(session, "Unexpected <%s> element in client <hello>.", node->name.name);
Michal Vasko77367452021-02-16 16:32:18 +01001491 rc = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001492 goto cleanup;
1493 }
1494
1495 if (flag) {
1496 /* multiple capabilities elements */
Michal Vasko05532772021-06-03 12:12:38 +02001497 ERR(session, "Invalid <hello> message (multiple <capabilities> elements).");
Michal Vasko77367452021-02-16 16:32:18 +01001498 rc = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001499 goto cleanup;
1500 }
1501 flag = 1;
1502
Michal Vasko77367452021-02-16 16:32:18 +01001503 if ((ver = parse_cpblts(&node->node, NULL)) < 0) {
1504 rc = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001505 goto cleanup;
1506 }
1507 session->version = ver;
1508 }
1509 break;
Michal Vasko77367452021-02-16 16:32:18 +01001510 case 0:
Michal Vasko05532772021-06-03 12:12:38 +02001511 ERR(session, "Client <hello> timeout elapsed.");
Michal Vasko77367452021-02-16 16:32:18 +01001512 rc = NC_MSG_WOULDBLOCK;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001513 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001514 default:
Michal Vasko77367452021-02-16 16:32:18 +01001515 rc = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +02001516 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001517 }
1518
1519cleanup:
Michal Vasko77367452021-02-16 16:32:18 +01001520 ly_in_free(msg, 1);
1521 lyd_free_tree(hello);
1522 return rc;
Michal Vasko11d142a2016-01-19 15:58:24 +01001523}
1524
Michal Vasko71090fc2016-05-24 16:37:28 +02001525NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +02001526nc_handshake_io(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001527{
Michal Vasko086311b2016-01-08 09:53:11 +01001528 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001529
Michal Vasko131120a2018-05-29 15:44:02 +02001530 type = nc_send_hello_io(session);
Michal Vasko086311b2016-01-08 09:53:11 +01001531 if (type != NC_MSG_HELLO) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001532 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001533 }
1534
Michal Vasko11d142a2016-01-19 15:58:24 +01001535 if (session->side == NC_CLIENT) {
Michal Vasko131120a2018-05-29 15:44:02 +02001536 type = nc_recv_client_hello_io(session);
Michal Vasko11d142a2016-01-19 15:58:24 +01001537 } else {
Michal Vasko131120a2018-05-29 15:44:02 +02001538 type = nc_recv_server_hello_io(session);
Michal Vasko11d142a2016-01-19 15:58:24 +01001539 }
1540
Michal Vasko71090fc2016-05-24 16:37:28 +02001541 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001542}