blob: a7ed96899f8f9d8704f2677fe0de025459d37334 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 session client functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
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
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
15
Radek Krejcifd5b6682017-06-13 15:52:53 +020016#define _GNU_SOURCE
Michal Vaskobbf52c82020-04-07 13:08:50 +020017
18#ifdef __linux__
19# include <sys/syscall.h>
20#endif
21
Michal Vaskob83a3fa2021-05-26 09:53:42 +020022#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010023#include <assert.h>
tadeas-vintrlik54f142a2021-08-23 10:36:18 +020024#include <ctype.h>
Michal Vasko086311b2016-01-08 09:53:11 +010025#include <errno.h>
26#include <fcntl.h>
27#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010028#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010029#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020030#include <poll.h>
Michal Vasko086311b2016-01-08 09:53:11 +010031#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020032#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010033#include <stdlib.h>
34#include <string.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020035#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010036#include <sys/socket.h>
37#include <sys/stat.h>
38#include <sys/types.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020039#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010040#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010041
Michal Vasko5788c802024-05-03 16:14:40 +020042#ifdef NC_ENABLED_SSH_TLS
43#include <libssh/libssh.h>
44#endif
Michal Vasko086311b2016-01-08 09:53:11 +010045#include <libyang/libyang.h>
46
Michal Vasko9e8ac262020-04-07 13:06:45 +020047#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020048#include "config.h"
49#include "log_p.h"
50#include "messages_p.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020051#include "session_client.h"
roman3f9b65c2023-06-05 14:26:58 +020052#include "session_client_ch.h"
53#include "session_p.h"
Michal Vasko086311b2016-01-08 09:53:11 +010054
Michal Vasko8a4e1462020-05-07 11:32:31 +020055#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020056#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020057
Michal Vasko80ef5d22016-01-18 09:21:02 +010058static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
59
roman2eab4742023-06-06 10:00:26 +020060#ifdef NC_ENABLED_SSH_TLS
Radek Krejci62aa0642017-05-25 16:33:49 +020061char *sshauth_password(const char *username, const char *hostname, void *priv);
62char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020063char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
roman2eab4742023-06-06 10:00:26 +020064#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +020065
66static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
67static pthread_key_t nc_client_context_key;
68#ifdef __linux__
69static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020070 .opts.ka = {
71 .enabled = 1,
72 .idle_time = 1,
73 .max_probes = 10,
74 .probe_interval = 5
75 },
roman2eab4742023-06-06 10:00:26 +020076#ifdef NC_ENABLED_SSH_TLS
Radek Krejci62aa0642017-05-25 16:33:49 +020077 .ssh_opts = {
roman41a11e42022-06-22 09:27:08 +020078 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020079 .auth_password = sshauth_password,
80 .auth_interactive = sshauth_interactive,
81 .auth_privkey_passphrase = sshauth_privkey_passphrase
82 },
83 .ssh_ch_opts = {
84 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020085 .auth_password = sshauth_password,
86 .auth_interactive = sshauth_interactive,
87 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020088 },
roman2eab4742023-06-06 10:00:26 +020089#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +020090 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020091 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020092};
93#endif
94
95static void
96nc_client_context_free(void *ptr)
97{
98 struct nc_client_context *c = (struct nc_client_context *)ptr;
99
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200100 if (--(c->refcount)) {
101 /* still used */
102 return;
103 }
104
Radek Krejci62aa0642017-05-25 16:33:49 +0200105#ifdef __linux__
106 /* in __linux__ we use static memory in the main thread,
107 * so this check is for programs terminating the main()
108 * function by pthread_exit() :)
109 */
110 if (c != &context_main)
111#endif
112 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200113 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400114 free(c->opts.schema_searchpath);
115
roman2eab4742023-06-06 10:00:26 +0200116#ifdef NC_ENABLED_SSH_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400117 int i;
Michal Vasko292c5542023-02-01 14:33:17 +0100118
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400119 for (i = 0; i < c->opts.ch_bind_count; ++i) {
120 close(c->opts.ch_binds[i].sock);
121 free((char *)c->opts.ch_binds[i].address);
122 }
123 free(c->opts.ch_binds);
124 c->opts.ch_binds = NULL;
125 c->opts.ch_bind_count = 0;
roman2eab4742023-06-06 10:00:26 +0200126
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400127 _nc_client_ssh_destroy_opts(&c->ssh_opts);
128 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
roman2eab4742023-06-06 10:00:26 +0200129
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400130 _nc_client_tls_destroy_opts(&c->tls_opts);
131 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
roman2eab4742023-06-06 10:00:26 +0200132#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +0200133 free(c);
134 }
135}
136
137static void
138nc_client_context_createkey(void)
139{
140 int r;
141
142 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200143 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200144 pthread_setspecific(nc_client_context_key, NULL);
145}
146
147struct nc_client_context *
148nc_client_context_location(void)
149{
150 struct nc_client_context *e;
151
152 pthread_once(&nc_client_context_once, nc_client_context_createkey);
153 e = pthread_getspecific(nc_client_context_key);
154 if (!e) {
155 /* prepare ly_err storage */
156#ifdef __linux__
157 if (getpid() == syscall(SYS_gettid)) {
158 /* main thread - use global variable instead of thread-specific variable. */
159 e = &context_main;
160 } else
161#endif /* __linux__ */
162 {
163 e = calloc(1, sizeof *e);
164 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200165 e->refcount = 1;
roman2eab4742023-06-06 10:00:26 +0200166#ifdef NC_ENABLED_SSH_TLS
Michal Vasko487ab802024-04-26 12:20:29 +0200167# ifdef HAVE_TERMIOS
168 e->ssh_opts.knownhosts_mode = NC_SSH_KNOWNHOSTS_ASK;
169# else
170 e->ssh_opts.knownhosts_mode = NC_SSH_KNOWNHOSTS_ACCEPT;
171# endif
Radek Krejci62aa0642017-05-25 16:33:49 +0200172 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
roman41a11e42022-06-22 09:27:08 +0200173 e->ssh_opts.auth_pref[0].value = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200174 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
175 e->ssh_opts.auth_pref[1].value = 2;
176 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
roman41a11e42022-06-22 09:27:08 +0200177 e->ssh_opts.auth_pref[2].value = 3;
Radek Krejci62aa0642017-05-25 16:33:49 +0200178 e->ssh_opts.auth_password = sshauth_password;
179 e->ssh_opts.auth_interactive = sshauth_interactive;
180 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
181
roman41a11e42022-06-22 09:27:08 +0200182 /* callhome settings are the same */
Radek Krejci62aa0642017-05-25 16:33:49 +0200183 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
184 e->ssh_ch_opts.auth_pref[0].value = 1;
185 e->ssh_ch_opts.auth_pref[1].value = 2;
186 e->ssh_ch_opts.auth_pref[2].value = 3;
roman2eab4742023-06-06 10:00:26 +0200187#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +0200188 }
189 pthread_setspecific(nc_client_context_key, e);
190 }
191
192 return e;
193}
194
195#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100196
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200197API void *
198nc_client_get_thread_context(void)
199{
200 return nc_client_context_location();
201}
202
203API void
204nc_client_set_thread_context(void *context)
205{
206 struct nc_client_context *old, *new;
207
208 if (!context) {
roman40672412023-05-04 11:10:22 +0200209 ERRARG(NULL, "context");
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200210 return;
211 }
212
213 new = (struct nc_client_context *)context;
214 old = nc_client_context_location();
215 if (old == new) {
216 /* nothing to change */
217 return;
218 }
219
220 /* replace old by new, increase reference counter in the newly set context */
221 nc_client_context_free(old);
222 new->refcount++;
223 pthread_setspecific(nc_client_context_key, new);
224}
225
Michal Vasko78939072022-12-12 07:43:18 +0100226/**
227 * @brief Ext data callback for a context to provide schema mount data.
228 */
229static LY_ERR
230nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
231{
232 struct nc_session *session = user_data;
233
234 if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
235 return LY_EINVAL;
236 }
237
238 if (!session->opts.client.ext_data) {
239 ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server.");
240 return LY_ENOTFOUND;
241 }
242
243 /* return ext data */
244 *ext_data = session->opts.client.ext_data;
245 *ext_data_free = 0;
246
247 return LY_SUCCESS;
248}
249
Radek Krejcifd5b6682017-06-13 15:52:53 +0200250int
Michal Vasko78939072022-12-12 07:43:18 +0100251nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200252{
253 /* assign context (dicionary needed for handshake) */
254 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100255 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200256 return EXIT_FAILURE;
257 }
258
Michal Vasko5ca5d972022-09-14 13:51:31 +0200259 /* user path must be first, the first path is used to store modules retreived via get-schema */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200260 if (client_opts.schema_searchpath) {
261 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko5d0cdc32022-12-12 07:38:21 +0100262 }
Michal Vasko80227222022-12-12 09:05:24 +0100263 if (!access(NC_CLIENT_SEARCH_DIR, F_OK)) {
264 ly_ctx_set_searchdir(ctx, NC_CLIENT_SEARCH_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200265 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200266
Michal Vasko5ca5d972022-09-14 13:51:31 +0200267 /* set callback for getting modules, if provided */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200268 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
Michal Vasko78939072022-12-12 07:43:18 +0100269
270 /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */
271 ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200272 } else {
273 session->flags |= NC_SESSION_SHAREDCTX;
274 }
275
276 session->ctx = ctx;
277
278 return EXIT_SUCCESS;
279}
280
Michal Vasko086311b2016-01-08 09:53:11 +0100281API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100282nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100283{
Michal Vasko3031aae2016-01-27 16:07:18 +0100284 if (client_opts.schema_searchpath) {
285 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100286 }
Michal Vasko086311b2016-01-08 09:53:11 +0100287
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100288 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100289 client_opts.schema_searchpath = strdup(path);
roman3a95bb22023-10-26 11:07:17 +0200290 NC_CHECK_ERRMEM_RET(!client_opts.schema_searchpath, 1);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100291 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100292 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100293 }
294
295 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100296}
297
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100298API const char *
299nc_client_get_schema_searchpath(void)
300{
301 return client_opts.schema_searchpath;
302}
303
Radek Krejcifd5b6682017-06-13 15:52:53 +0200304API int
305nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100306{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200307 client_opts.schema_clb = clb;
308 if (clb) {
309 client_opts.schema_clb_data = user_data;
310 } else {
311 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100312 }
313
314 return 0;
315}
316
Radek Krejcifd5b6682017-06-13 15:52:53 +0200317API ly_module_imp_clb
318nc_client_get_schema_callback(void **user_data)
319{
320 if (user_data) {
321 (*user_data) = client_opts.schema_clb_data;
322 }
323 return client_opts.schema_clb;
324}
325
Michal Vaskoa272e092023-07-10 14:34:03 +0200326API void
327nc_client_set_new_session_context_autofill(int enabled)
328{
329 client_opts.auto_context_fill_disabled = !enabled;
330}
331
Michal Vasko5ca5d972022-09-14 13:51:31 +0200332struct module_info {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200333 char *name;
334 char *revision;
Michal Vasko6ce8e822022-08-02 15:09:17 +0200335
Radek Krejci65ef6d52018-08-16 16:35:02 +0200336 struct {
337 char *name;
338 char *revision;
339 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100340 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200341 int implemented;
342};
343
344struct clb_data_s {
345 void *user_data;
346 ly_module_imp_clb user_clb;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200347 struct module_info *modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200348 struct nc_session *session;
349 int has_get_schema;
350};
351
Michal Vaskoc088f982021-10-05 12:23:07 +0200352/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200353 * @brief Retrieve YANG module content from a local file.
Michal Vaskoc088f982021-10-05 12:23:07 +0200354 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200355 * @param[in] name Module name.
356 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200357 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200358 * @param[out] format Module format.
359 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200360 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200361static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200362retrieve_module_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200363 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100364{
Michal Vaskob13a2242023-12-05 13:29:07 +0100365 char *localfile = NULL, *model_data = NULL;
366 const char *ptr;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200367 FILE *f;
368 long length, l;
Michal Vasko086311b2016-01-08 09:53:11 +0100369
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200370 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200371 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
372 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200373 return NULL;
374 }
Michal Vaskob13a2242023-12-05 13:29:07 +0100375 if (localfile && rev) {
376 ptr = strrchr(localfile, '/');
377 if (!strchr(ptr, '@')) {
378 /* we do not know the revision of the module and we require a specific one, so ignore this module */
379 localfile = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100380 }
Michal Vasko086311b2016-01-08 09:53:11 +0100381 }
382
Michal Vaskob13a2242023-12-05 13:29:07 +0100383 if (!localfile) {
384 return NULL;
385 }
386
387 VRB(clb_data->session, "Reading module \"%s@%s\" from local file \"%s\".", name, rev ? rev : "<latest>",
388 localfile);
389 f = fopen(localfile, "r");
390 if (!f) {
391 ERR(clb_data->session, "Unable to open file \"%s\" (%s).", localfile, strerror(errno));
392 free(localfile);
393 return NULL;
394 }
395
396 fseek(f, 0, SEEK_END);
397 length = ftell(f);
398 if (length < 0) {
399 ERR(clb_data->session, "Unable to get the size of module file \"%s\".", localfile);
400 free(localfile);
401 fclose(f);
402 return NULL;
403 }
404 fseek(f, 0, SEEK_SET);
405
406 model_data = malloc(length + 1);
407 if (!model_data) {
408 ERRMEM;
409 } else if ((l = fread(model_data, 1, length, f)) != length) {
410 ERR(clb_data->session, "Reading module from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
411 length);
412 free(model_data);
413 model_data = NULL;
414 } else {
415 /* terminating NULL byte */
416 model_data[length] = '\0';
417 }
418 fclose(f);
419 free(localfile);
420
Radek Krejci65ef6d52018-08-16 16:35:02 +0200421 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100422}
423
Michal Vaskoc088f982021-10-05 12:23:07 +0200424/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200425 * @brief Retrieve YANG module content from a reply to get-schema RPC.
Michal Vaskoc088f982021-10-05 12:23:07 +0200426 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200427 * @param[in] name Module name.
428 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200429 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200430 * @param[out] format Module format.
431 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200432 */
Michal Vasko086311b2016-01-08 09:53:11 +0100433static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200434retrieve_module_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200435 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100436{
Michal Vasko086311b2016-01-08 09:53:11 +0100437 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100438 struct lyd_node *envp = NULL, *op = NULL;
439 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100440 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100441 uint64_t msgid;
Michal Vasko7502b602022-08-26 11:51:17 +0200442 char *localfile = NULL, *envp_str = NULL, *model_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200443 FILE *f;
Michal Vasko086311b2016-01-08 09:53:11 +0100444
Michal Vasko5ca5d972022-09-14 13:51:31 +0200445 VRB(clb_data->session, "Reading module \"%s@%s\" from server via get-schema.", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200446 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100447
Radek Krejci65ef6d52018-08-16 16:35:02 +0200448 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100449 usleep(1000);
450 }
451 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200452 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100453 nc_rpc_free(rpc);
454 return NULL;
455 }
456
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200457 do {
Michal Vasko77367452021-02-16 16:32:18 +0100458 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200459 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100460 nc_rpc_free(rpc);
461 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200462 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100463 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100464 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200465 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100466 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100467 } else if (!op) {
Michal Vasko7502b602022-08-26 11:51:17 +0200468 assert(envp);
469 lyd_print_mem(&envp_str, envp, LYD_XML, 0);
470 WRN(clb_data->session, "Received an unexpected reply to <get-schema>:\n%s", envp_str);
471 free(envp_str);
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100472 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100473 }
474
Michal Vasko77367452021-02-16 16:32:18 +0100475 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200476 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100477 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200478 }
Michal Vasko77367452021-02-16 16:32:18 +0100479 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200480 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200481 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100482 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200483 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200484 break;
485 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100486 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200487 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200488 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200489 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200490 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100491 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200492 }
Michal Vasko086311b2016-01-08 09:53:11 +0100493
Radek Krejci488b0702018-08-29 14:47:15 +0200494 if (model_data && !model_data[0]) {
495 /* empty data */
496 free(model_data);
497 model_data = NULL;
498 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100499 if (!model_data) {
500 goto cleanup;
501 }
502
503 /* set format */
504 *format = LYS_IN_YANG;
Radek Krejci488b0702018-08-29 14:47:15 +0200505
Michal Vasko5ca5d972022-09-14 13:51:31 +0200506 /* try to store the model_data into local module repository */
Michal Vaskoa1721172022-12-12 09:06:53 +0100507 lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx), 0, name, rev, &localfile, NULL);
508 if (client_opts.schema_searchpath && !localfile) {
509 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
510 rev ? rev : "") == -1) {
511 ERRMEM;
512 } else {
513 f = fopen(localfile, "w");
514 if (!f) {
515 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of module retrieved via <get-schema> (%s).",
516 localfile, strerror(errno));
Michal Vaskof945da52018-02-15 08:45:13 +0100517 } else {
Michal Vaskoa1721172022-12-12 09:06:53 +0100518 fputs(model_data, f);
519 fclose(f);
Michal Vaskof945da52018-02-15 08:45:13 +0100520 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200521 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200522 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100523 free(localfile);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200524
Michal Vasko77367452021-02-16 16:32:18 +0100525cleanup:
526 lyd_free_tree(envp);
527 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100528 return model_data;
529}
530
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200531static void
532free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200533{
534 free(data);
535 (void)user_data;
536}
537
Michal Vaskoc088f982021-10-05 12:23:07 +0200538/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200539 * @brief Retrieve YANG module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200540 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200541 * @param[in] mod_name Module name.
542 * @param[in] mod_rev Module revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100543 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200544 * @param[out] format Module format.
545 * @param[out] module_data Module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100546 * @param[out] free_module_data Callback for freeing @p module_data.
547 * @return LY_ERR value.
548 */
549static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200550retrieve_module_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format,
Michal Vasko5e32c402022-01-12 14:05:09 +0100551 const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
552{
553 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
554 char *model_data = NULL;
555
Michal Vasko5e32c402022-01-12 14:05:09 +0100556 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200557 model_data = retrieve_module_data_localfile(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100558
559 /* 2. try to use <get-schema> */
560 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200561 model_data = retrieve_module_data_getschema(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100562 }
563
564 /* 3. try to use user callback */
565 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200566 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", mod_name, mod_rev ? mod_rev : "<latest>");
Michal Vasko5e32c402022-01-12 14:05:09 +0100567 clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data,
568 free_module_data);
569 }
570
571 *free_module_data = free_with_user_data;
572 *module_data = model_data;
573 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
574}
575
576/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200577 * @brief Retrieve YANG import module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100578 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200579 * @param[in] mod_name Module name.
580 * @param[in] mod_rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200581 * @param[in] submod_name Optional submodule name.
582 * @param[in] sub_rev Submodule revision.
583 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200584 * @param[out] format Module format.
585 * @param[out] module_data Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200586 * @param[out] free_module_data Callback for freeing @p module_data.
587 * @return LY_ERR value.
588 */
Michal Vasko77367452021-02-16 16:32:18 +0100589static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200590retrieve_module_data_imp(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
Michal Vasko77367452021-02-16 16:32:18 +0100591 void *user_data, LYS_INFORMAT *format, const char **module_data,
592 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200593{
594 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
Michal Vasko5e32c402022-01-12 14:05:09 +0100595 uint32_t u, v, match = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200596 const char *name = NULL, *rev = NULL;
597 char *model_data = NULL;
598
Michal Vasko5ca5d972022-09-14 13:51:31 +0200599 /* get and check the final name and revision of the module to be retrieved */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200600 if (!mod_rev || !mod_rev[0]) {
601 /* newest revision requested - get the newest revision from the list of available modules on server */
602 match = 0;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200603 for (u = 0; clb_data->modules[u].name; ++u) {
604 if (strcmp(mod_name, clb_data->modules[u].name)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200605 continue;
606 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200607 if (!match || (strcmp(mod_rev, clb_data->modules[u].revision) > 0)) {
608 mod_rev = clb_data->modules[u].revision;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200609 }
610 match = u + 1;
611 }
612 if (!match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200613 /* valid situation if we are retrieving YANG 1.1 module and have only capabilities for now
Michal Vasko1d2665c2021-02-12 09:28:44 +0100614 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200615 VRB(clb_data->session, "Unable to identify revision of the import module \"%s\" from "
Michal Vasko05532772021-06-03 12:12:38 +0200616 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200617 }
618 }
619 if (submod_name) {
620 name = submod_name;
621 if (sub_rev) {
622 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200623 } else if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200624 if (!clb_data->modules[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200625 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
Michal Vasko5ca5d972022-09-14 13:51:31 +0200626 "in import module \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200627 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200628 for (v = 0; clb_data->modules[match - 1].submodules[v].name; ++v) {
629 if (!strcmp(submod_name, clb_data->modules[match - 1].submodules[v].name)) {
630 rev = sub_rev = clb_data->modules[match - 1].submodules[v].revision;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200631 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200632 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200633 if (!rev) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200634 ERR(clb_data->session, "Requested submodule \"%s\" is not found in import module \"%s\" on server side.",
Michal Vasko05532772021-06-03 12:12:38 +0200635 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100636 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200637 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200638 }
639 }
640 } else {
641 name = mod_name;
642 rev = mod_rev;
643 }
644
Radek Krejci65ef6d52018-08-16 16:35:02 +0200645 if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200646 /* we have enough information to avoid communication with server and try to get the module locally */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200647
648 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200649 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200650
651 /* 2. try to use <get-schema> */
652 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200653 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200654 }
655 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200656 /* we are unsure which revision of the module we should load, so first try to get
Radek Krejci65ef6d52018-08-16 16:35:02 +0200657 * the newest revision from the server via get-schema and only if the server does not
658 * implement get-schema, try to load the newest revision locally. This is imperfect
659 * solution, but there are situation when a client does not know what revision is
660 * actually implemented by the server. */
661
662 /* 1. try to use <get-schema> */
663 if (clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200664 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200665 }
666
667 /* 2. try to get data locally */
668 if (!model_data) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200669 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200670 }
671 }
672
673 /* 3. try to use user callback */
674 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200675 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", name, rev ? rev : "<latest>");
Michal Vasko77367452021-02-16 16:32:18 +0100676 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
677 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200678 }
679
Jan Kundrát35972df2018-09-06 19:00:01 +0200680 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100681 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200682 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200683}
684
Michal Vaskoc088f982021-10-05 12:23:07 +0200685/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200686 * @brief Load a YANG module into context.
Michal Vaskoc088f982021-10-05 12:23:07 +0200687 *
688 * @param[in] session NC session.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200689 * @param[in] name Module name.
690 * @param[in] revision Module revision.
691 * @param[in] features Enabled module features.
692 * @param[in] modules Server module info built from capabilities.
693 * @param[in] user_clb User callback for retrieving module data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200694 * @param[in] user_data User data for @p user_clb.
695 * @param[in] has_get_schema Whether the server supports get-schema.
696 * @param[out] mod Loaded module.
697 * @return 0 on success.
698 * @return -1 on error.
699 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100700static int
Michal Vasko5e32c402022-01-12 14:05:09 +0100701nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features,
Michal Vasko5ca5d972022-09-14 13:51:31 +0200702 struct module_info *modules, ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod)
Michal Vaskoceae0152018-02-14 16:03:59 +0100703{
704 int ret = 0;
Michal Vasko58791da2024-02-26 13:52:59 +0100705 const struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200706 const char *module_data = NULL;
Michal Vasko5e32c402022-01-12 14:05:09 +0100707 struct ly_in *in;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200708 LYS_INFORMAT format;
Michal Vasko6e11d082024-06-05 15:54:01 +0200709 uint32_t temp_lo = LY_LOSTORE, *prev_lo;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200710
711 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200712 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100713
Michal Vasko5e32c402022-01-12 14:05:09 +0100714 /* try to use a module from the context */
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200715 *mod = ly_ctx_get_module_implemented(session->ctx, name);
716 if (!*mod) {
717 if (revision) {
718 *mod = ly_ctx_get_module(session->ctx, name, revision);
719 } else {
Michal Vasko5e32c402022-01-12 14:05:09 +0100720 *mod = ly_ctx_get_module_latest(session->ctx, name);
721 }
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200722 } else if (revision && (!(*mod)->revision || strcmp((*mod)->revision, revision))) {
723 WRN(session, "Server implements module \"%s\" in revision \"%s\" but revision \"%s\" is already implemented"
724 " and will be used instead.", name, revision, (*mod)->revision ? (*mod)->revision : "<none>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200725 }
Michal Vasko5e32c402022-01-12 14:05:09 +0100726
Michal Vaskoceae0152018-02-14 16:03:59 +0100727 if (*mod) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100728 /* make the present module implemented and/or enable all its features */
729 if (lys_set_implemented(*mod, features)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200730 ERR(session, "Failed to implement module \"%s\".", (*mod)->name);
Michal Vasko6e11d082024-06-05 15:54:01 +0200731 return -1;
Michal Vaskoceae0152018-02-14 16:03:59 +0100732 }
Michal Vasko6e11d082024-06-05 15:54:01 +0200733 return 0;
734 }
735
736 /* missing implemented module, load it ... */
737 clb_data.has_get_schema = has_get_schema;
738 clb_data.modules = modules;
739 clb_data.session = session;
740 clb_data.user_clb = user_clb;
741 clb_data.user_data = user_data;
742
743 /* clear all the errors and just collect them for now */
744 ly_err_clean(session->ctx, NULL);
745 prev_lo = ly_temp_log_options(&temp_lo);
746
747 /* get module data */
748 if (!retrieve_module_data(name, revision, &clb_data, &format, &module_data, &free_module_data)) {
749 /* set import callback */
750 ly_ctx_set_module_imp_clb(session->ctx, retrieve_module_data_imp, &clb_data);
751
752 /* parse the module */
753 ly_in_new_memory(module_data, &in);
754 lys_parse(session->ctx, in, format, features, mod);
755 ly_in_free(in, 0);
756 if (free_module_data) {
757 free_module_data((char *)module_data, user_data);
758 }
759
760 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
761 }
762
763 /* restore logging options, then print errors on definite failure */
764 ly_temp_log_options(prev_lo);
765 if (!(*mod)) {
766 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
767 ly_err_print(session->ctx, eitem);
768 }
769 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200770 } else {
Michal Vasko6e11d082024-06-05 15:54:01 +0200771 /* print only warnings */
772 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
773 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100774 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100775 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100776 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100777 }
778
Michal Vasko6e11d082024-06-05 15:54:01 +0200779 /* clean the errors */
780 ly_err_clean(session->ctx, NULL);
781
Michal Vaskoceae0152018-02-14 16:03:59 +0100782 return ret;
783}
784
Radek Krejci65ef6d52018-08-16 16:35:02 +0200785static void
Michal Vasko5ca5d972022-09-14 13:51:31 +0200786free_module_info(struct module_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200787{
Michal Vasko77367452021-02-16 16:32:18 +0100788 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200789
Radek Krejci65ef6d52018-08-16 16:35:02 +0200790 if (!list) {
791 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200792 }
793
Radek Krejci65ef6d52018-08-16 16:35:02 +0200794 for (u = 0; list[u].name; ++u) {
795 free(list[u].name);
796 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100797 if (list[u].features) {
798 for (v = 0; list[u].features[v]; ++v) {
799 free(list[u].features[v]);
800 }
801 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200802 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200803 if (list[u].submodules) {
804 for (v = 0; list[u].submodules[v].name; ++v) {
805 free(list[u].submodules[v].name);
806 free(list[u].submodules[v].revision);
807 }
808 free(list[u].submodules);
809 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200810 }
811 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200812}
813
Michal Vaskoc088f982021-10-05 12:23:07 +0200814/**
Michal Vasko78939072022-12-12 07:43:18 +0100815 * @brief Retrieve yang-library and schema-mounts operational data from the server.
Michal Vaskoc088f982021-10-05 12:23:07 +0200816 *
817 * @param[in] session NC session.
818 * @param[in] has_get_data Whether get-data RPC is available or only get.
Michal Vasko78939072022-12-12 07:43:18 +0100819 * @param[in] filter Filter to use.
820 * @param[out] oper_data Received data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200821 * @return 0 on success.
822 * @return -1 on error.
823 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200824static int
Michal Vasko78939072022-12-12 07:43:18 +0100825get_oper_data(struct nc_session *session, int has_get_data, const char *filter, struct lyd_node **oper_data)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200826{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200827 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100828 struct lyd_node *op = NULL, *envp = NULL;
829 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200830 NC_MSG_TYPE msg;
831 uint64_t msgid;
Michal Vasko303263d2021-10-05 12:18:21 +0200832 int ret = 0;
833 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200834
Michal Vasko78939072022-12-12 07:43:18 +0100835 /* get data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200836 if (has_get_data) {
Michal Vasko78939072022-12-12 07:43:18 +0100837 rpc_name = "<get-data>";
838 rpc = nc_rpc_getdata("ietf-datastores:operational", filter, "false", NULL, 0, 0, 0, 0, 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200839 } else {
Michal Vasko78939072022-12-12 07:43:18 +0100840 rpc_name = "<get>";
841 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200842 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200843 if (!rpc) {
844 goto cleanup;
845 }
846
847 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
848 usleep(1000);
849 }
850 if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100851 WRN(session, "Failed to send %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200852 goto cleanup;
853 }
854
855 do {
Michal Vasko77367452021-02-16 16:32:18 +0100856 lyd_free_tree(envp);
857 lyd_free_tree(op);
858
859 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200860 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200861 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko78939072022-12-12 07:43:18 +0100862 WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200863 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100864 } else if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100865 WRN(session, "Failed to receive a reply to %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200866 goto cleanup;
Jan Kundrát1d73a5a2022-03-30 16:54:49 +0200867 } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko78939072022-12-12 07:43:18 +0100868 WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200869 goto cleanup;
870 }
871
Michal Vasko77367452021-02-16 16:32:18 +0100872 data = (struct lyd_node_any *)lyd_child(op);
873 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko78939072022-12-12 07:43:18 +0100874 WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200875 goto cleanup;
876 } else if (!data->value.tree) {
Michal Vasko78939072022-12-12 07:43:18 +0100877 WRN(session, "No data in reply to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200878 goto cleanup;
879 }
880
Michal Vasko78939072022-12-12 07:43:18 +0100881 *oper_data = data->value.tree;
882 data->value.tree = NULL;
883
884cleanup:
885 nc_rpc_free(rpc);
886 lyd_free_tree(envp);
887 lyd_free_tree(op);
888
889 if (session->status != NC_STATUS_RUNNING) {
890 /* something bad happened, discard the session */
891 ERR(session, "Invalid session, discarding.");
892 ret = -1;
893 }
894
895 return ret;
896}
897
898/**
899 * @brief Build server module info from ietf-yang-library data.
900 *
901 * @param[in] session NC session.
902 * @param[in] get_data_sup Whether get-data RPC is available or only get.
903 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
904 * @param[out] result Server modules.
905 * @return 0 on success.
906 * @return -1 on error.
907 */
908static int
909build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result)
910{
911 struct ly_set *modules = NULL;
912 uint32_t u, v, submodules_count, feature_count;
913 struct lyd_node *iter, *child, *oper_data = NULL;
914 struct lys_module *mod;
915 int ret = 0;
Siddharth Sharma01614f32024-06-26 18:42:27 +0200916 uint8_t notifications_found = 0;
917 uint8_t nc_notifications_found = 0;
Michal Vasko78939072022-12-12 07:43:18 +0100918
919 /* get yang-library operational data */
920 if (xpath_sup) {
921 if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) {
922 goto cleanup;
923 }
924 } else {
925 if (get_oper_data(session, get_data_sup,
926 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", &oper_data)) {
927 goto cleanup;
928 }
929 }
930 if (!oper_data) {
931 goto cleanup;
932 }
933
934 if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) {
935 WRN(NULL, "No yang-library module information found.");
Radek Krejci2d088832018-08-21 13:40:14 +0200936 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200937 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200938
Michal Vasko77367452021-02-16 16:32:18 +0100939 (*result) = calloc(modules->count + 1, sizeof **result);
roman3a95bb22023-10-26 11:07:17 +0200940 NC_CHECK_ERRMEM_GOTO(!(*result), ret = -1, cleanup);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200941
Michal Vasko77367452021-02-16 16:32:18 +0100942 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200943 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100944 feature_count = 0;
945 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
946 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200947 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200948 /* ignore node from other schemas (augments) */
949 continue;
950 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200951 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200952 /* ignore empty nodes */
953 continue;
954 }
955 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200956 (*result)[u].name = strdup(lyd_get_value(iter));
Siddharth Sharma01614f32024-06-26 18:42:27 +0200957 if (!strcmp((*result)[u].name, "notifications")) {
958 notifications_found = 1;
959 } else if (!strcmp((*result)[u].name, "nc-notifications")) {
960 nc_notifications_found = 1;
961 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200962 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200963 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200964 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200965 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200966 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100967 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
roman3a95bb22023-10-26 11:07:17 +0200968 NC_CHECK_ERRMEM_GOTO(!(*result)[u].features, free_module_info(*result); *result = NULL; ret = -1, cleanup);
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200969 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100970 (*result)[u].features[feature_count + 1] = NULL;
971 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200972 } else if (!strcmp(iter->schema->name, "submodule")) {
973 submodules_count++;
974 }
975 }
976
977 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200978 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
roman3a95bb22023-10-26 11:07:17 +0200979 NC_CHECK_ERRMEM_GOTO(!(*result)[u].submodules, free_module_info(*result); *result = NULL; ret = -1, cleanup);
980 v = 0;
981 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
982 mod = modules->dnodes[u]->schema->module;
983 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
984 LY_LIST_FOR(lyd_child(iter), child) {
985 if (mod != child->schema->module) {
986 continue;
987 } else if (!strcmp(child->schema->name, "name")) {
988 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
989 } else if (!strcmp(child->schema->name, "revision")) {
990 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200991 }
992 }
993 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200994 }
995 }
996 }
997
Siddharth Sharma01614f32024-06-26 18:42:27 +0200998 /* If NETCONF server supports RFC5277 notification capability and libnetconf2
999 * required notifications and nc-notifications are not present on the NETCONF
1000 * server (which it is not obligated to support), then the libyang context
1001 * needs to be initialized using client side local YANG schema files */
1002 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:notification:1.0") &&
1003 !notifications_found && !nc_notifications_found) {
1004
1005 (*result) = nc_realloc(*result, (modules->count + 3) * sizeof **result);
1006 NC_CHECK_ERRMEM_GOTO(!(*result), ret = -1, cleanup);
1007
1008 (*result)[u].name = strdup("notifications");
1009 (*result)[u].revision = strdup("2008-07-14");
1010 (*result)[u].implemented = 1;
1011 u++;
1012
1013 (*result)[u].name = strdup("nc-notifications");
1014 (*result)[u].revision = strdup("2008-07-14");
1015 (*result)[u].implemented = 1;
1016 }
1017
Radek Krejcifd5b6682017-06-13 15:52:53 +02001018cleanup:
Michal Vasko78939072022-12-12 07:43:18 +01001019 lyd_free_siblings(oper_data);
Michal Vasko77367452021-02-16 16:32:18 +01001020 ly_set_free(modules, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +02001021 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001022}
1023
Michal Vaskoc088f982021-10-05 12:23:07 +02001024/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001025 * @brief Build server module info from received capabilities.
Michal Vaskoc088f982021-10-05 12:23:07 +02001026 *
1027 * @param[in] cpblts Server capabilities.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001028 * @param[out] result Server modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001029 * @return 0 on success.
1030 * @return -1 on error.
1031 */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001032static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001033build_module_info_cpblts(char **cpblts, struct module_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001034{
Michal Vasko77367452021-02-16 16:32:18 +01001035 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001036 char *module_cpblt, *ptr, *ptr2;
1037
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001038 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +02001039 (*result) = calloc(u + 1, sizeof **result);
roman3a95bb22023-10-26 11:07:17 +02001040 NC_CHECK_ERRMEM_RET(!(*result), -1);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001041
1042 for (u = v = 0; cpblts[u]; ++u) {
1043 module_cpblt = strstr(cpblts[u], "module=");
1044 /* this capability requires a module */
1045 if (!module_cpblt) {
1046 continue;
1047 }
1048
1049 /* get module's name */
1050 ptr = (char *)module_cpblt + 7;
1051 ptr2 = strchr(ptr, '&');
1052 if (!ptr2) {
1053 ptr2 = ptr + strlen(ptr);
1054 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001055 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001056
1057 /* get module's revision */
1058 ptr = strstr(module_cpblt, "revision=");
1059 if (ptr) {
1060 ptr += 9;
1061 ptr2 = strchr(ptr, '&');
1062 if (!ptr2) {
1063 ptr2 = ptr + strlen(ptr);
1064 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001065 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001066 }
1067
1068 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001069 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001070
1071 /* get module's features */
1072 ptr = strstr(module_cpblt, "features=");
1073 if (ptr) {
1074 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +01001075 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001076 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
1077 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +01001078 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1079 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1080 (*result)[v].features[feature_count + 1] = NULL;
1081 ++feature_count;
1082
Radek Krejci65ef6d52018-08-16 16:35:02 +02001083 ptr2 = ptr + 1;
1084 }
1085 }
1086 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +01001087 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1088 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1089 (*result)[v].features[feature_count + 1] = NULL;
1090 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001091 }
1092 ++v;
1093 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001094
Michal Vasko303263d2021-10-05 12:18:21 +02001095 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001096}
1097
Michal Vaskoc088f982021-10-05 12:23:07 +02001098/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001099 * @brief Fill client context based on server modules info.
Michal Vaskoc088f982021-10-05 12:23:07 +02001100 *
1101 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001102 * @param[in] modules Server modules info.
1103 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001104 * @param[in] user_data User data for @p user_clb.
1105 * @param[in] has_get_schema Whether server supports get-schema RPC.
1106 * @return 0 on success.
1107 * @return -1 on error.
1108 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001109static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001110nc_ctx_fill(struct nc_session *session, struct module_info *modules, ly_module_imp_clb user_clb, void *user_data,
Michal Vasko77367452021-02-16 16:32:18 +01001111 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001112{
Michal Vasko303263d2021-10-05 12:18:21 +02001113 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +01001114 struct lys_module *mod;
1115 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001116
1117 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +02001118 /* skip import-only modules */
1119 if (!modules[u].implemented) {
1120 continue;
1121 }
1122
Radek Krejci65ef6d52018-08-16 16:35:02 +02001123 /* we can continue even if it fails */
Michal Vasko5e32c402022-01-12 14:05:09 +01001124 nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules,
1125 user_clb, user_data, has_get_schema, &mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001126
1127 if (!mod) {
1128 if (session->status != NC_STATUS_RUNNING) {
1129 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +02001130 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001131 goto cleanup;
1132 }
1133
Michal Vasko5ca5d972022-09-14 13:51:31 +02001134 /* all loading ways failed, the module will be ignored in the received data */
1135 WRN(session, "Failed to load module \"%s@%s\".", modules[u].name, modules[u].revision ?
Michal Vasko05532772021-06-03 12:12:38 +02001136 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001137 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001138 }
Michal Vasko60f66602017-10-17 13:52:18 +02001139 }
1140
Michal Vasko77367452021-02-16 16:32:18 +01001141 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001142 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001143
1144cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001145 return ret;
1146}
1147
Michal Vaskoc088f982021-10-05 12:23:07 +02001148/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001149 * @brief Fill client context with ietf-netconf module.
Michal Vaskoc088f982021-10-05 12:23:07 +02001150 *
1151 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001152 * @param[in] modules Server module info.
1153 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001154 * @param[in] user_data User data for @p user_clb.
1155 * @param[in] has_get_schema Whether server supports get-schema RPC.
1156 * @return 0 on success.
1157 * @return -1 on error.
1158 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001159static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001160nc_ctx_fill_ietf_netconf(struct nc_session *session, struct module_info *modules, ly_module_imp_clb user_clb,
Michal Vasko77367452021-02-16 16:32:18 +01001161 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001162{
Michal Vasko77367452021-02-16 16:32:18 +01001163 uint32_t u;
Michal Vasko5e32c402022-01-12 14:05:09 +01001164 const char **features = NULL;
1165 struct ly_in *in;
Michal Vasko77367452021-02-16 16:32:18 +01001166 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001167
Michal Vasko5e32c402022-01-12 14:05:09 +01001168 /* find supported features (capabilities) in ietf-netconf */
1169 for (u = 0; modules[u].name; ++u) {
1170 if (!strcmp(modules[u].name, "ietf-netconf")) {
1171 assert(modules[u].implemented);
1172 features = (const char **)modules[u].features;
1173 break;
1174 }
1175 }
1176 if (!modules[u].name) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001177 ERR(session, "Base NETCONF module not supported by the server.");
Michal Vasko5e32c402022-01-12 14:05:09 +01001178 return -1;
1179 }
1180
Michal Vasko77367452021-02-16 16:32:18 +01001181 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vasko5e32c402022-01-12 14:05:09 +01001182 if (ietfnc) {
1183 /* make sure to enable all the features if already loaded */
1184 lys_set_implemented(ietfnc, features);
1185 } else {
1186 /* load the module */
1187 nc_ctx_load_module(session, "ietf-netconf", NULL, features, modules, user_clb, user_data, has_get_schema, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001188 if (!ietfnc) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001189 ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in);
1190 lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc);
1191 ly_in_free(in, 0);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001192 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001193 }
1194 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001195 ERR(session, "Loading base NETCONF module failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001196 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001197 }
1198
Radek Krejci65ef6d52018-08-16 16:35:02 +02001199 return 0;
1200}
1201
Michal Vasko56226c72024-06-05 15:54:25 +02001202API int
1203nc_client_set_new_session_context_schema_mount(struct nc_session *session)
Michal Vasko78939072022-12-12 07:43:18 +01001204{
Michal Vasko56226c72024-06-05 15:54:25 +02001205 int rc = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0;
Michal Vasko78939072022-12-12 07:43:18 +01001206 struct lyd_node *oper_data = NULL;
Michal Vasko56226c72024-06-05 15:54:25 +02001207 const struct lys_module *mod;
Michal Vasko78939072022-12-12 07:43:18 +01001208
1209 if (session->flags & NC_SESSION_SHAREDCTX) {
1210 /* context is already fully set up */
1211 goto cleanup;
1212 }
1213
Michal Vasko56226c72024-06-05 15:54:25 +02001214 /* check all useful capabilities */
1215 if (ly_ctx_get_module_implemented(session->ctx, "ietf-yang-library")) {
1216 yanglib_support = 1;
1217 }
1218 if ((mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf")) && !lys_feature_value(mod, "xpath")) {
1219 xpath_support = 1;
1220 }
1221 if (ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda")) {
1222 nmda_support = 1;
1223 }
1224
1225 if (!yanglib_support) {
1226 ERR(session, "Module \"ietf-yang-library\" missing to retrieve schema-mount data.");
1227 rc = -1;
1228 goto cleanup;
1229 }
1230
Michal Vasko78939072022-12-12 07:43:18 +01001231 /* get yang-library and schema-mounts operational data */
Michal Vasko56226c72024-06-05 15:54:25 +02001232 if (xpath_support) {
1233 if ((rc = get_oper_data(session, nmda_support, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) {
Michal Vasko78939072022-12-12 07:43:18 +01001234 goto cleanup;
1235 }
1236 } else {
Michal Vasko56226c72024-06-05 15:54:25 +02001237 if ((rc = get_oper_data(session, nmda_support,
Michal Vasko78939072022-12-12 07:43:18 +01001238 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>"
1239 "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"/>", &oper_data))) {
1240 goto cleanup;
1241 }
1242 }
1243
1244 if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) {
1245 /* no schema-mounts operational data */
1246 goto cleanup;
1247 }
1248
1249 /* validate the data for the parent reference prefixes to be resolved */
1250 if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) {
Michal Vasko58791da2024-02-26 13:52:59 +01001251 ERR(session, "Invalid operational data received from the server (%s).", ly_err_last(LYD_CTX(oper_data))->msg);
Michal Vasko78939072022-12-12 07:43:18 +01001252 rc = -1;
1253 goto cleanup;
1254 }
1255
1256 /* store the data in the session */
Michal Vasko56226c72024-06-05 15:54:25 +02001257 lyd_free_siblings(session->opts.client.ext_data);
Michal Vasko78939072022-12-12 07:43:18 +01001258 session->opts.client.ext_data = oper_data;
1259 oper_data = NULL;
1260
1261cleanup:
1262 lyd_free_siblings(oper_data);
1263 return rc;
1264}
1265
Michal Vasko086311b2016-01-08 09:53:11 +01001266int
1267nc_ctx_check_and_fill(struct nc_session *session)
1268{
Michal Vaskod6c6f852022-12-14 15:37:21 +01001269 int i, get_schema_support = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001270 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001271 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001272 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001273 char *revision;
Michal Vasko5ca5d972022-09-14 13:51:31 +02001274 struct module_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001275
Michal Vasko2e6defd2016-10-07 15:48:15 +02001276 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001277
Michal Vaskoa272e092023-07-10 14:34:03 +02001278 if (client_opts.auto_context_fill_disabled) {
1279 VRB(session, "Context of the new session is left only with the default YANG modules.");
1280 return 0;
1281 }
1282
Radek Krejci65ef6d52018-08-16 16:35:02 +02001283 /* store the original user's callback, we will be switching between local search, get-schema and user callback */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001284 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001285
Radek Krejci65ef6d52018-08-16 16:35:02 +02001286 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
Michal Vasko5ca5d972022-09-14 13:51:31 +02001287 * modules only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001288 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001289
1290 /* our callback is set later with appropriate data */
1291 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1292
1293 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001294 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001295 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001296 get_schema_support = 1 + i;
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001297 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001298 yanglib_support = 1 + i;
Michal Vasko78939072022-12-12 07:43:18 +01001299 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) {
1300 xpath_support = 1 + i;
Michal Vasko086311b2016-01-08 09:53:11 +01001301 }
Michal Vaskoa0b8b702024-05-29 15:21:58 +02001302 /* NMDA is YANG 1.1 module, which is not present in the capabilities */
Michal Vasko086311b2016-01-08 09:53:11 +01001303 }
Michal Vaskod6c6f852022-12-14 15:37:21 +01001304 VRB(session, "Capability for <get-schema> support%s found.", get_schema_support ? "" : " not");
1305 VRB(session, "Capability for yang-library support%s found.", yanglib_support ? "" : " not");
1306 VRB(session, "Capability for XPath filter support%s found.", xpath_support ? "" : " not");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001307
Michal Vasko5ca5d972022-09-14 13:51:31 +02001308 /* get information about server's modules from capabilities list until we will have yang-library */
1309 if (build_module_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1310 ERR(session, "Unable to get server module information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001311 goto cleanup;
1312 }
1313
Michal Vasko086311b2016-01-08 09:53:11 +01001314 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001315 if (get_schema_support && lys_parse_mem(session->ctx, ietf_netconf_monitoring_2010_10_04_yang, LYS_IN_YANG, NULL)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001316 WRN(session, "Loading NETCONF monitoring module failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001317 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001318 }
1319
1320 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001321 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001322 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001323 }
1324
Radek Krejci65ef6d52018-08-16 16:35:02 +02001325 /* get correct version of ietf-yang-library into context */
1326 if (yanglib_support) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001327 /* use get-schema to get server's ietf-yang-library */
Radek Krejcib1d250e2018-04-12 13:54:18 +02001328 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1329 if (!revision) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001330 WRN(session, "Loading NETCONF ietf-yang-library module failed, missing revision in NETCONF <hello> message.");
Michal Vasko05532772021-06-03 12:12:38 +02001331 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001332 yanglib_support = 0;
1333 } else {
1334 revision = strndup(&revision[9], 10);
Michal Vasko5e32c402022-01-12 14:05:09 +01001335 if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001336 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001337 WRN(session, "Loading NETCONF ietf-yang-library module failed, unable to use it to learn all "
Michal Vasko05532772021-06-03 12:12:38 +02001338 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001339 yanglib_support = 0;
1340 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001341 if (strcmp(revision, "2019-01-04") >= 0) {
1342 /* we also need ietf-datastores to be implemented */
Michal Vasko5e32c402022-01-12 14:05:09 +01001343 if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001344 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001345 WRN(session, "Loading NETCONF ietf-datastores module failed, unable to use yang-library "
Michal Vasko05532772021-06-03 12:12:38 +02001346 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001347 yanglib_support = 0;
1348 }
1349 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001350 free(revision);
1351 }
1352 }
1353
Michal Vasko5ca5d972022-09-14 13:51:31 +02001354 /* prepare structured information about server's modules */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001355 if (yanglib_support) {
Michal Vasko56226c72024-06-05 15:54:25 +02001356 if (build_module_info_yl(session, 0, xpath_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001357 goto cleanup;
1358 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001359 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001360 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001361 /* prefer yang-library information, currently we have it from capabilities used for getting correct
Michal Vasko5ca5d972022-09-14 13:51:31 +02001362 * yang-library module */
1363 free_module_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001364 server_modules = sm;
Michal Vaskoa0b8b702024-05-29 15:21:58 +02001365
1366 /* check for NMDA support */
1367 for (i = 0; server_modules[i].name; ++i) {
1368 if (!strcmp(server_modules[i].name, "ietf-netconf-nmda") && server_modules[i].implemented) {
1369 nmda_support = 1;
1370 break;
1371 }
1372 }
1373
1374 /* ietf-netconf-nmda is needed to issue get-data */
1375 if (nmda_support && nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb,
1376 old_data, get_schema_support, &mod)) {
1377 WRN(session, "Loading NMDA module failed, unable to use <get-data>.");
Michal Vaskoa0b8b702024-05-29 15:21:58 +02001378 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001379 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001380 }
Michal Vaskoef578332016-01-25 13:20:09 +01001381
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001382 /* compile all modules at once to avoid invalid errors or warnings */
1383 ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
1384
1385 /* fill the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001386 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1387 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001388 }
1389
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001390 /* compile it */
1391 if (ly_ctx_compile(session->ctx)) {
1392 goto cleanup;
1393 }
1394
Michal Vasko59762c32024-04-12 12:13:12 +02001395 /* set support for schema-mount, if possible (requires ietf-yang-library support) */
Michal Vasko56226c72024-06-05 15:54:25 +02001396 if (yanglib_support && nc_client_set_new_session_context_schema_mount(session)) {
Michal Vasko78939072022-12-12 07:43:18 +01001397 goto cleanup;
1398 }
1399
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001400 /* success */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001401 ret = 0;
1402
Michal Vaskoeee99412016-11-21 10:19:43 +01001403 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001404 WRN(session, "Some modules failed to be loaded, any data from these modules (and any other unknown) will "
Michal Vasko05532772021-06-03 12:12:38 +02001405 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001406 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001407
1408cleanup:
Michal Vasko5ca5d972022-09-14 13:51:31 +02001409 free_module_info(server_modules);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001410
Radek Krejcifd5b6682017-06-13 15:52:53 +02001411 /* set user callback back */
1412 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001413 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001414 ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001415
Michal Vaskoef578332016-01-25 13:20:09 +01001416 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001417}
1418
1419API struct nc_session *
1420nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1421{
Michal Vaskod083db62016-01-19 10:31:29 +01001422 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001423
Michal Vasko45e53ae2016-04-07 11:46:03 +02001424 if (fdin < 0) {
roman40672412023-05-04 11:10:22 +02001425 ERRARG(NULL, "fdin");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001426 return NULL;
1427 } else if (fdout < 0) {
roman40672412023-05-04 11:10:22 +02001428 ERRARG(NULL, "fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001429 return NULL;
1430 }
1431
1432 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001433 session = nc_new_session(NC_CLIENT, 0);
roman3a95bb22023-10-26 11:07:17 +02001434 NC_CHECK_ERRMEM_RET(!session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001435 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001436
1437 /* transport specific data */
1438 session->ti_type = NC_TI_FD;
1439 session->ti.fd.in = fdin;
1440 session->ti.fd.out = fdout;
1441
Michal Vasko78939072022-12-12 07:43:18 +01001442 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001443 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001444 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001445 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001446
1447 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001448 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001449 goto fail;
1450 }
1451 session->status = NC_STATUS_RUNNING;
1452
Michal Vaskoef578332016-01-25 13:20:09 +01001453 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001454 goto fail;
1455 }
1456
1457 return session;
1458
1459fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001460 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001461 return NULL;
1462}
1463
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001464API struct nc_session *
1465nc_connect_unix(const char *address, struct ly_ctx *ctx)
1466{
1467 struct nc_session *session = NULL;
1468 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001469 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001470 char *username;
1471 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001472 char *buf = NULL;
1473 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001474
roman40672412023-05-04 11:10:22 +02001475 NC_CHECK_ARG_RET(NULL, address, NULL);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001476
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001477 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1478 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001479 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001480 goto fail;
1481 }
1482
1483 memset(&sun, 0, sizeof(sun));
1484 sun.sun_family = AF_UNIX;
1485 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1486
1487 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001488 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001489 goto fail;
1490 }
1491
1492 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001493 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001494 goto fail;
1495 }
1496
1497 /* prepare session structure */
1498 session = nc_new_session(NC_CLIENT, 0);
roman124a4362023-10-26 15:36:22 +02001499 NC_CHECK_ERRMEM_GOTO(!session, , fail);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001500 session->status = NC_STATUS_STARTING;
1501
1502 /* transport specific data */
1503 session->ti_type = NC_TI_UNIX;
1504 session->ti.unixsock.sock = sock;
1505 sock = -1; /* do not close sock in fail label anymore */
1506
Michal Vasko78939072022-12-12 07:43:18 +01001507 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001508 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001509 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001510 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001511
Michal Vasko93224072021-11-09 12:14:28 +01001512 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001513
romanf6e32012023-04-24 15:51:26 +02001514 pw = nc_getpw(geteuid(), NULL, &pw_buf, &buf, &buf_size);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001515 if (!pw) {
1516 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1517 goto fail;
1518 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001519 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001520 free(buf);
roman124a4362023-10-26 15:36:22 +02001521 NC_CHECK_ERRMEM_GOTO(!username, , fail);
Michal Vasko93224072021-11-09 12:14:28 +01001522 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001523
1524 /* NETCONF handshake */
1525 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1526 goto fail;
1527 }
1528 session->status = NC_STATUS_RUNNING;
1529
1530 if (nc_ctx_check_and_fill(session) == -1) {
1531 goto fail;
1532 }
1533
1534 return session;
1535
1536fail:
1537 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001538 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001539 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001540 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001541 return NULL;
1542}
1543
Michal Vasko056f53c2022-10-21 13:38:15 +02001544/**
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001545 * @brief Convert socket IP address to string.
1546 *
1547 * @param[in] saddr Sockaddr to convert.
1548 * @param[out] str_ip String IP address.
Michal Vaskoc07d9762023-03-27 10:32:18 +02001549 * @param[out] port Optional port.
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001550 * @return 0 on success.
1551 * @return -1 on error.
1552 */
1553static int
Michal Vaskoc07d9762023-03-27 10:32:18 +02001554nc_saddr2str(const struct sockaddr *saddr, char **str_ip, uint16_t *port)
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001555{
1556 void *addr;
1557 socklen_t str_len;
1558
1559 assert((saddr->sa_family == AF_INET) || (saddr->sa_family == AF_INET6));
1560
1561 str_len = (saddr->sa_family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
1562 *str_ip = malloc(str_len);
roman3a95bb22023-10-26 11:07:17 +02001563 NC_CHECK_ERRMEM_RET(!(*str_ip), -1);
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001564
1565 if (saddr->sa_family == AF_INET) {
Michal Vaskoc0f85ca2023-03-27 10:19:00 +02001566 addr = &((struct sockaddr_in *)saddr)->sin_addr;
Michal Vaskoc07d9762023-03-27 10:32:18 +02001567 if (port) {
1568 *port = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1569 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001570 } else {
Michal Vaskoc0f85ca2023-03-27 10:19:00 +02001571 addr = &((struct sockaddr_in6 *)saddr)->sin6_addr;
Michal Vaskoc07d9762023-03-27 10:32:18 +02001572 if (port) {
1573 *port = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1574 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001575 }
1576 if (!inet_ntop(saddr->sa_family, addr, *str_ip, str_len)) {
1577 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
1578 free(*str_ip);
1579 return -1;
1580 }
1581
1582 return 0;
1583}
1584
1585/**
Michal Vasko056f53c2022-10-21 13:38:15 +02001586 * @brief Try to connect a socket, optionally a pending one from a previous attempt.
1587 *
1588 * @param[in] timeout_ms Timeout in ms to wait for the connection to be fully established, -1 to block.
1589 * @param[in,out] sock_pending Optional previously created socked that was not fully connected yet. If provided and
1590 * connected, is set to -1.
1591 * @param[in] res Addrinfo resource to use when creating a new socket.
1592 * @param[in] ka Keepalives to set.
1593 * @return Connected socket or -1 on error.
Frank Rimpler9f838b02018-07-25 06:44:03 +00001594 */
1595static int
roman9ad4a8a2024-08-08 12:44:01 +02001596sock_connect(const char *src_addr, uint16_t src_port, int timeout_ms, int *sock_pending, struct addrinfo *res,
1597 const struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001598{
roman9ad4a8a2024-08-08 12:44:01 +02001599 int flags, ret, error, opt;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001600 int sock = -1;
Michal Vaskob9336672023-10-11 09:27:30 +02001601 struct pollfd fds = {0};
Frank Rimpler9f838b02018-07-25 06:44:03 +00001602 socklen_t len = sizeof(int);
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001603 uint16_t port;
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001604 char *str;
Michal Vasko086311b2016-01-08 09:53:11 +01001605
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001606 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001607 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001608 sock = *sock_pending;
1609 } else {
1610 assert(res);
Michal Vaskoc07d9762023-03-27 10:32:18 +02001611 if (nc_saddr2str(res->ai_addr, &str, &port)) {
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001612 return -1;
1613 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001614 VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
1615 free(str);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001616
1617 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001618 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1619 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001620 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001621 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001622 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001623 /* make the socket non-blocking */
1624 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001625 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001626 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001627 }
roman9ad4a8a2024-08-08 12:44:01 +02001628
1629 /* bind the socket to a specific address/port to make the connection from (CH only) */
1630 if (src_addr || src_port) {
1631 /* enable address reuse, so that we're able to bind this address again when the CH conn is dropped and retried */
1632 opt = 1;
1633 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
1634 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
1635 goto cleanup;
1636 }
1637
1638 if (nc_sock_bind_inet(sock, src_addr, src_port, (res->ai_family == AF_INET) ? 1 : 0)) {
1639 goto cleanup;
1640 }
1641 }
1642
Frank Rimpler9f838b02018-07-25 06:44:03 +00001643 /* non-blocking connect! */
1644 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1645 if (errno != EINPROGRESS) {
1646 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001647 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001648 goto cleanup;
1649 }
1650 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001651 }
Michal Vasko7d965dc2018-03-12 14:39:23 +01001652
Michal Vaskob9336672023-10-11 09:27:30 +02001653 fds.fd = sock;
1654 fds.events = POLLOUT;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001655
Michal Vaskob9336672023-10-11 09:27:30 +02001656 /* wait until we can write data to the socket */
1657 ret = poll(&fds, 1, timeout_ms);
1658 if (ret == -1) {
1659 /* error */
1660 ERR(NULL, "poll() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001661 goto cleanup;
Michal Vaskob9336672023-10-11 09:27:30 +02001662 } else if (ret == 0) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001663 /* there was a timeout */
Michal Vasko056f53c2022-10-21 13:38:15 +02001664 VRB(NULL, "Timed out after %d ms (%s).", timeout_ms, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001665 if (sock_pending) {
1666 /* no sock-close, we'll try it again */
1667 *sock_pending = sock;
1668 } else {
1669 close(sock);
1670 }
1671 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001672 }
Radek Krejci782041a2018-08-20 10:09:45 +02001673
1674 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001675 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001676 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001677 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001678 goto cleanup;
1679 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001680 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001681 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001682 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001683 errno = error;
1684 goto cleanup;
1685 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001686
roman56acd552024-04-18 16:00:37 +02001687 /* configure keepalives */
Michal Vaskoc4cdc9e2024-05-03 12:03:07 +02001688 if (nc_sock_configure_ka(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001689 goto cleanup;
1690 }
1691
Michal Vasko056f53c2022-10-21 13:38:15 +02001692 /* connected */
1693 if (sock_pending) {
1694 *sock_pending = -1;
1695 }
Michal Vasko086311b2016-01-08 09:53:11 +01001696 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001697
Frank Rimpler9f838b02018-07-25 06:44:03 +00001698cleanup:
1699 if (sock_pending) {
1700 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001701 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001702 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001703 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001704}
1705
Frank Rimpler9f838b02018-07-25 06:44:03 +00001706int
roman9ad4a8a2024-08-08 12:44:01 +02001707nc_sock_connect(const char *src_addr, uint16_t src_port, const char *dst_addr, uint16_t dst_port, int timeout_ms,
1708 struct nc_keepalives *ka, int *sock_pending, char **ip_host)
Frank Rimpler9f838b02018-07-25 06:44:03 +00001709{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001710 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001711 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001712 struct addrinfo hints, *res_list = NULL, *res;
roman9ad4a8a2024-08-08 12:44:01 +02001713 char dst_port_str[6]; /* length of string representation of short int */
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001714 struct sockaddr_storage saddr;
1715 socklen_t addr_len = sizeof saddr;
1716
1717 *ip_host = NULL;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001718
roman9ad4a8a2024-08-08 12:44:01 +02001719 DBG(NULL, "nc_sock_connect(%s, %u, %s, %u, %d, %d)", src_addr, src_port, dst_addr, dst_port, timeout_ms, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001720
1721 /* no pending socket */
1722 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001723 /* connect to a server */
roman9ad4a8a2024-08-08 12:44:01 +02001724 snprintf(dst_port_str, 6, "%u", dst_port);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001725 memset(&hints, 0, sizeof hints);
1726 hints.ai_family = AF_UNSPEC;
1727 hints.ai_socktype = SOCK_STREAM;
1728 hints.ai_protocol = IPPROTO_TCP;
roman9ad4a8a2024-08-08 12:44:01 +02001729 i = getaddrinfo(dst_addr, dst_port_str, &hints, &res_list);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001730 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001731 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001732 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001733 }
1734
1735 for (res = res_list; res != NULL; res = res->ai_next) {
roman9ad4a8a2024-08-08 12:44:01 +02001736 sock = sock_connect(src_addr, src_port, timeout_ms, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001737 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001738 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001739 /* try the next resource */
1740 continue;
1741 } else {
1742 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001743 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001744 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001745 }
roman9ad4a8a2024-08-08 12:44:01 +02001746
1747 if (res->ai_family == AF_INET) {
1748 VRB(NULL, "Successfully connected to %s:%s over IPv4.", dst_addr, dst_port_str);
1749 } else {
1750 VRB(NULL, "Successfully connected to [%s]:%s over IPv6.", dst_addr, dst_port_str);
1751 }
Michal Vasko83ad17e2019-01-30 10:11:37 +01001752
1753 opt = 1;
1754 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001755 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001756 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001757 }
1758
Michal Vaskoc07d9762023-03-27 10:32:18 +02001759 if (nc_saddr2str(res->ai_addr, ip_host, NULL)) {
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001760 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001761 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001762 break;
1763 }
1764 freeaddrinfo(res_list);
1765
1766 } else {
1767 /* try to get a connection with the pending socket */
1768 assert(sock_pending);
roman9ad4a8a2024-08-08 12:44:01 +02001769 sock = sock_connect(src_addr, src_port, timeout_ms, sock_pending, NULL, ka);
Tie.Liao9bca73d2023-03-23 17:25:00 +01001770
1771 if (sock > 0) {
Barbaros Tokaoglu96da5912023-06-19 18:57:05 +03001772 if (getpeername(sock, (struct sockaddr *)&saddr, &addr_len)) {
1773 ERR(NULL, "getpeername failed (%s).", strerror(errno));
Tie.Liao9bca73d2023-03-23 17:25:00 +01001774 goto error;
1775 }
1776
Michal Vaskoc07d9762023-03-27 10:32:18 +02001777 if (nc_saddr2str((struct sockaddr *)&saddr, ip_host, NULL)) {
Tie.Liao9bca73d2023-03-23 17:25:00 +01001778 goto error;
1779 }
Tie.Liao9bca73d2023-03-23 17:25:00 +01001780 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001781 }
1782
1783 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001784
1785error:
Michal Vasko0be85692021-03-02 08:04:57 +01001786 if (res_list) {
1787 freeaddrinfo(res_list);
1788 }
Michal Vaskocb846632019-12-13 15:12:45 +01001789 if (sock != -1) {
1790 close(sock);
1791 }
1792 if (sock_pending) {
1793 *sock_pending = -1;
1794 }
1795 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001796}
1797
roman2eab4742023-06-06 10:00:26 +02001798#ifdef NC_ENABLED_SSH_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001799
Michal Vasko3031aae2016-01-27 16:07:18 +01001800int
Michal Vasko9d4cca52022-09-07 11:19:57 +02001801nc_client_ch_add_bind_listen(const char *address, uint16_t port, const char *hostname, NC_TRANSPORT_IMPL ti)
Michal Vasko3031aae2016-01-27 16:07:18 +01001802{
1803 int sock;
1804
roman40672412023-05-04 11:10:22 +02001805 NC_CHECK_ARG_RET(NULL, address, port, -1);
Michal Vasko3031aae2016-01-27 16:07:18 +01001806
Michal Vaskoc4cdc9e2024-05-03 12:03:07 +02001807 sock = nc_sock_listen_inet(address, port);
Michal Vasko3031aae2016-01-27 16:07:18 +01001808 if (sock == -1) {
1809 return -1;
1810 }
1811
1812 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001813 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1814 if (!client_opts.ch_binds) {
1815 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001816 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001817 return -1;
1818 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001819
Michal Vasko9d4cca52022-09-07 11:19:57 +02001820 client_opts.ch_binds_aux = nc_realloc(client_opts.ch_binds_aux, client_opts.ch_bind_count * sizeof *client_opts.ch_binds_aux);
1821 if (!client_opts.ch_binds_aux) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001822 ERRMEM;
1823 close(sock);
1824 return -1;
1825 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001826 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].ti = ti;
1827 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].hostname = hostname ? strdup(hostname) : NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001828
Michal Vasko3031aae2016-01-27 16:07:18 +01001829 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
1830 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1831 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001832 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001833
1834 return 0;
1835}
1836
1837int
1838nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1839{
1840 uint32_t i;
1841 int ret = -1;
1842
1843 if (!address && !port && !ti) {
1844 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1845 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001846 free(client_opts.ch_binds[i].address);
1847
1848 free(client_opts.ch_binds_aux[i].hostname);
Michal Vasko3031aae2016-01-27 16:07:18 +01001849
1850 ret = 0;
1851 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001852 client_opts.ch_bind_count = 0;
1853
Michal Vasko3031aae2016-01-27 16:07:18 +01001854 free(client_opts.ch_binds);
1855 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001856
1857 free(client_opts.ch_binds_aux);
1858 client_opts.ch_binds_aux = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001859 } else {
1860 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001861 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1862 (!port || (client_opts.ch_binds[i].port == port)) &&
Michal Vasko9d4cca52022-09-07 11:19:57 +02001863 (!ti || (client_opts.ch_binds_aux[i].ti == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001864 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001865 free(client_opts.ch_binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001866
1867 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001868 if (!client_opts.ch_bind_count) {
1869 free(client_opts.ch_binds);
1870 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001871
1872 free(client_opts.ch_binds_aux);
1873 client_opts.ch_binds_aux = NULL;
Michal Vasko66c762a2016-10-13 10:34:14 +02001874 } else if (i < client_opts.ch_bind_count) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001875 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count],
1876 sizeof *client_opts.ch_binds);
1877
1878 memcpy(&client_opts.ch_binds_aux[i], &client_opts.ch_binds_aux[client_opts.ch_bind_count],
1879 sizeof *client_opts.ch_binds_aux);
Michal Vasko66c762a2016-10-13 10:34:14 +02001880 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001881
1882 ret = 0;
1883 }
1884 }
1885 }
1886
1887 return ret;
1888}
1889
1890API int
1891nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1892{
Michal Vasko8b1740f2024-06-28 13:47:19 +02001893 int ret, sock;
Michal Vasko3031aae2016-01-27 16:07:18 +01001894 char *host = NULL;
1895 uint16_t port, idx;
1896
roman40672412023-05-04 11:10:22 +02001897 NC_CHECK_ARG_RET(NULL, session, -1);
1898
Michal Vasko45e53ae2016-04-07 11:46:03 +02001899 if (!client_opts.ch_binds) {
romand82caf12024-03-05 14:21:39 +01001900 ERR(NULL, "Call-Home binds not set.");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001901 return -1;
Michal Vasko3031aae2016-01-27 16:07:18 +01001902 }
1903
Michal Vasko8b1740f2024-06-28 13:47:19 +02001904 ret = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, &client_opts.ch_bind_lock, timeout,
1905 &host, &port, &idx, &sock);
1906 if (ret < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001907 free(host);
Michal Vasko8b1740f2024-06-28 13:47:19 +02001908 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +01001909 }
1910
Michal Vaskoc4cdc9e2024-05-03 12:03:07 +02001911 /* configure keepalives */
1912 if (nc_sock_configure_ka(sock, &client_opts.ka)) {
1913 free(host);
1914 close(sock);
1915 return -1;
1916 }
1917
roman506354a2024-04-11 09:37:22 +02001918 if (client_opts.ch_binds_aux[idx].ti == NC_TI_SSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001919 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
roman506354a2024-04-11 09:37:22 +02001920 } else if (client_opts.ch_binds_aux[idx].ti == NC_TI_TLS) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001921 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT,
1922 client_opts.ch_binds_aux[idx].hostname);
roman2eab4742023-06-06 10:00:26 +02001923 } else {
Michal Vaskofee717c2016-02-01 13:25:43 +01001924 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001925 *session = NULL;
1926 }
1927
1928 free(host);
1929
1930 if (!(*session)) {
1931 return -1;
1932 }
1933
1934 return 1;
1935}
1936
roman2eab4742023-06-06 10:00:26 +02001937#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001938
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001939API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001940nc_session_get_cpblts(const struct nc_session *session)
1941{
roman40672412023-05-04 11:10:22 +02001942 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vaskobdfb5242016-05-24 09:11:01 +02001943
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001944 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001945}
1946
1947API const char *
1948nc_session_cpblt(const struct nc_session *session, const char *capab)
1949{
1950 int i, len;
1951
roman40672412023-05-04 11:10:22 +02001952 NC_CHECK_ARG_RET(session, session, capab, NULL);
Michal Vaskobdfb5242016-05-24 09:11:01 +02001953
1954 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001955 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1956 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1957 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001958 }
1959 }
1960
1961 return NULL;
1962}
1963
Michal Vasko9cd26a82016-05-31 08:58:48 +02001964API int
1965nc_session_ntf_thread_running(const struct nc_session *session)
1966{
roman40672412023-05-04 11:10:22 +02001967 NC_CHECK_ARG_RET(session, session, 0);
1968
1969 if (session->side != NC_CLIENT) {
1970 ERRARG(NULL, "session");
Michal Vasko9cd26a82016-05-31 08:58:48 +02001971 return 0;
1972 }
1973
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02001974 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running);
Michal Vasko9cd26a82016-05-31 08:58:48 +02001975}
1976
roman9075eab2023-09-14 10:07:26 +02001977API int
1978nc_client_init(void)
1979{
1980 int r;
1981
1982 if ((r = pthread_mutex_init(&client_opts.ch_bind_lock, NULL))) {
1983 ERR(NULL, "%s: failed to init bind lock(%s).", __func__, strerror(r));
1984 return -1;
1985 }
1986
Michal Vasko5788c802024-05-03 16:14:40 +02001987#ifdef NC_ENABLED_SSH_TLS
1988 if (ssh_init()) {
1989 ERR(NULL, "%s: failed to init libssh.", __func__);
1990 return -1;
1991 }
1992#endif
1993
roman9075eab2023-09-14 10:07:26 +02001994 return 0;
1995}
1996
Michal Vaskob7558c52016-02-26 15:04:19 +01001997API void
1998nc_client_destroy(void)
1999{
roman9075eab2023-09-14 10:07:26 +02002000 pthread_mutex_destroy(&client_opts.ch_bind_lock);
Radek Krejci5cebc6b2017-05-26 13:24:38 +02002001 nc_client_set_schema_searchpath(NULL);
roman2eab4742023-06-06 10:00:26 +02002002#ifdef NC_ENABLED_SSH_TLS
Radek Krejci5cebc6b2017-05-26 13:24:38 +02002003 nc_client_ch_del_bind(NULL, 0, 0);
Radek Krejci5cebc6b2017-05-26 13:24:38 +02002004 nc_client_ssh_destroy_opts();
Radek Krejci5cebc6b2017-05-26 13:24:38 +02002005 nc_client_tls_destroy_opts();
Michal Vasko5788c802024-05-03 16:14:40 +02002006 ssh_finalize();
roman2eab4742023-06-06 10:00:26 +02002007#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskob7558c52016-02-26 15:04:19 +01002008}
2009
Michal Vasko77367452021-02-16 16:32:18 +01002010static NC_MSG_TYPE
2011recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002012{
Michal Vasko77367452021-02-16 16:32:18 +01002013 char *ptr;
2014 struct lyd_attr *attr;
2015 uint64_t cur_msgid;
2016
2017 assert(envp && !envp->schema);
2018
2019 /* find the message-id attribute */
2020 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
2021 if (!strcmp(attr->name.name, "message-id")) {
2022 break;
2023 }
2024 }
2025
2026 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02002027 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01002028 return NC_MSG_REPLY_ERR_MSGID;
2029 }
2030
2031 cur_msgid = strtoul(attr->value, &ptr, 10);
2032 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02002033 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
2034 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01002035 return NC_MSG_REPLY_ERR_MSGID;
2036 }
2037
2038 return NC_MSG_REPLY;
2039}
2040
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002041/**
2042 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
2043 *
2044 * @param[in] session NETCONF session used to send error messages.
2045 * @param[in] msg Message to check for type.
2046 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
2047 * @return NC_MSG_NOTIF If format roughly matches a notification;
2048 * @return NC_MSG_ERROR If format is malformed or unrecognized.
2049 */
2050static NC_MSG_TYPE
2051get_msg_type(struct nc_session *session, struct ly_in *msg)
2052{
2053 const char *str, *end;
2054
2055 str = ly_in_memory(msg, NULL);
2056
2057 while (*str) {
2058 /* Skip whitespaces */
2059 while (isspace(*str)) {
2060 str++;
2061 }
2062
2063 if (*str == '<') {
2064 str++;
2065 if (!strncmp(str, "!--", 3)) {
2066 /* Skip comments */
2067 end = "-->";
2068 str = strstr(str, end);
2069 } else if (!strncmp(str, "?xml", 4)) {
2070 /* Skip xml declaration */
2071 end = "?>";
2072 str = strstr(str, end);
2073 } else if (!strncmp(str, "rpc-reply", 9)) {
2074 return NC_MSG_REPLY;
2075 } else if (!strncmp(str, "notification", 12)) {
2076 return NC_MSG_NOTIF;
2077 } else {
2078 ERR(session, "Unknown xml element '%.10s'.", str);
2079 return NC_MSG_ERROR;
2080 }
2081 if (!str) {
2082 /* No matching ending tag found */
2083 ERR(session, "No matching ending tag '%s' found in xml message.", end);
2084 return NC_MSG_ERROR;
2085 }
2086 str += strlen(end);
2087 } else {
2088 /* Not a valid xml */
2089 ERR(session, "Unexpected character '%c' in xml message.", *str);
2090 return NC_MSG_ERROR;
2091 }
2092 }
2093
2094 /* Unexpected end of message */
2095 ERR(session, "Unexpected end of xml message.");
2096 return NC_MSG_ERROR;
2097}
2098
2099/**
2100 * @brief Function to receive either replies or notifications.
2101 *
2102 * @param[in] session NETCONF session from which this function receives messages.
2103 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
2104 * @param[in] expected Type of the message the caller desired.
2105 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
2106 * @return NC_MSG_REPLY If a rpc-reply was received;
2107 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01002108 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002109 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
2110 */
2111static NC_MSG_TYPE
2112recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
2113{
2114 struct nc_msg_cont **cont_ptr;
2115 struct ly_in *msg = NULL;
2116 struct nc_msg_cont *cont, *prev;
2117 NC_MSG_TYPE ret = NC_MSG_ERROR;
2118 int r;
2119
2120 *message = NULL;
2121
2122 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002123 r = nc_session_client_msgs_lock(session, &timeout, __func__);
2124 if (!r) {
2125 ret = NC_MSG_WOULDBLOCK;
2126 goto cleanup;
2127 } else if (r == -1) {
2128 ret = NC_MSG_ERROR;
2129 goto cleanup;
2130 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002131
2132 /* Find the expected message in the buffer */
2133 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002134 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002135 prev = cont;
2136 }
2137
2138 if (cont) {
2139 /* Remove found message from buffer */
2140 if (prev) {
2141 prev->next = cont->next;
2142 } else {
2143 session->opts.client.msgs = cont->next;
2144 }
2145
2146 /* Use the buffer message */
2147 ret = cont->type;
2148 msg = cont->msg;
2149 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02002150 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002151 }
2152
2153 /* Read a message from the wire */
2154 r = nc_read_msg_poll_io(session, timeout, &msg);
2155 if (!r) {
2156 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02002157 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002158 } else if (r == -1) {
2159 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002160 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002161 }
2162
2163 /* Basic check to determine message type */
2164 ret = get_msg_type(session, msg);
2165 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02002166 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002167 }
2168
2169 /* If received a message of different type store it in the buffer */
2170 if (ret != expected) {
2171 cont_ptr = &session->opts.client.msgs;
2172 while (*cont_ptr) {
2173 cont_ptr = &((*cont_ptr)->next);
2174 }
2175 *cont_ptr = malloc(sizeof **cont_ptr);
roman3a95bb22023-10-26 11:07:17 +02002176 NC_CHECK_ERRMEM_GOTO(!*cont_ptr, ret = NC_MSG_ERROR, cleanup_unlock);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002177 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002178 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002179 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002180 (*cont_ptr)->next = NULL;
2181 }
2182
Michal Vasko01130bd2021-08-26 11:47:38 +02002183cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002184 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002185 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002186
Michal Vasko01130bd2021-08-26 11:47:38 +02002187cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002188 if (ret == expected) {
2189 *message = msg;
2190 } else {
2191 ly_in_free(msg, 1);
2192 }
2193 return ret;
2194}
2195
Michal Vasko77367452021-02-16 16:32:18 +01002196static NC_MSG_TYPE
2197recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
2198{
Michal Vasko77367452021-02-16 16:32:18 +01002199 LY_ERR lyrc;
2200 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002201 NC_MSG_TYPE ret = NC_MSG_ERROR;
Michal Vaskob25c56f2024-03-27 15:45:56 +01002202 uint32_t temp_lo = LY_LOSTORE, *prev_lo;
Michal Vasko77367452021-02-16 16:32:18 +01002203
2204 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
2205
2206 *envp = NULL;
2207
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002208 /* Receive messages until a rpc-reply is found or a timeout or error reached */
2209 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
2210 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01002211 goto cleanup;
2212 }
2213
2214 /* parse */
Michal Vaskob25c56f2024-03-27 15:45:56 +01002215 prev_lo = ly_temp_log_options(&temp_lo);
Michal Vasko77367452021-02-16 16:32:18 +01002216 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
Michal Vaskob25c56f2024-03-27 15:45:56 +01002217 ly_temp_log_options(prev_lo);
2218
2219 if (*envp) {
2220 /* if the envelopes were parsed, check the message-id, even on error */
Michal Vasko77367452021-02-16 16:32:18 +01002221 ret = recv_reply_check_msgid(session, *envp, msgid);
2222 goto cleanup;
Michal Vaskob25c56f2024-03-27 15:45:56 +01002223 }
2224
2225 if (lyrc) {
2226 /* parsing error */
Michal Vasko58791da2024-02-26 13:52:59 +01002227 ERR(session, "Received an invalid message (%s).", ly_err_last(LYD_CTX(op))->msg);
Michal Vasko9a108052022-04-01 12:06:54 +02002228 lyd_free_tree(*envp);
2229 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002230 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002231 goto cleanup;
2232 }
2233
Michal Vasko77367452021-02-16 16:32:18 +01002234cleanup:
2235 ly_in_free(msg, 1);
2236 return ret;
2237}
2238
2239static int
2240recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2241{
Michal Vasko143aa142021-10-01 15:31:48 +02002242 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002243 struct nc_rpc_act_generic *rpc_gen;
2244 struct ly_in *in;
2245 struct lyd_node *tree, *op2;
2246 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002247 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002248
2249 switch (rpc->type) {
2250 case NC_RPC_ACT_GENERIC:
2251 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2252 if (rpc_gen->has_data) {
2253 tree = rpc_gen->content.data;
2254
2255 /* find the operation node */
2256 lyrc = LY_EINVAL;
2257 LYD_TREE_DFS_BEGIN(tree, op2) {
2258 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2259 lyrc = lyd_dup_single(op2, NULL, 0, op);
2260 break;
2261 }
2262 LYD_TREE_DFS_END(tree, op2);
2263 }
2264 } else {
2265 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2266 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2267 ly_in_free(in, 0);
2268 if (lyrc) {
Michal Vasko9a108052022-04-01 12:06:54 +02002269 lyd_free_tree(tree);
Michal Vasko77367452021-02-16 16:32:18 +01002270 return -1;
2271 }
2272
2273 /* we want just the operation node */
2274 lyrc = lyd_dup_single(op2, NULL, 0, op);
2275
2276 lyd_free_tree(tree);
2277 }
2278 break;
2279 case NC_RPC_GETCONFIG:
2280 module_name = "ietf-netconf";
2281 rpc_name = "get-config";
2282 break;
2283 case NC_RPC_EDIT:
2284 module_name = "ietf-netconf";
2285 rpc_name = "edit-config";
2286 break;
2287 case NC_RPC_COPY:
2288 module_name = "ietf-netconf";
2289 rpc_name = "copy-config";
2290 break;
2291 case NC_RPC_DELETE:
2292 module_name = "ietf-netconf";
2293 rpc_name = "delete-config";
2294 break;
2295 case NC_RPC_LOCK:
2296 module_name = "ietf-netconf";
2297 rpc_name = "lock";
2298 break;
2299 case NC_RPC_UNLOCK:
2300 module_name = "ietf-netconf";
2301 rpc_name = "unlock";
2302 break;
2303 case NC_RPC_GET:
2304 module_name = "ietf-netconf";
2305 rpc_name = "get";
2306 break;
2307 case NC_RPC_KILL:
2308 module_name = "ietf-netconf";
2309 rpc_name = "kill-session";
2310 break;
2311 case NC_RPC_COMMIT:
2312 module_name = "ietf-netconf";
2313 rpc_name = "commit";
2314 break;
2315 case NC_RPC_DISCARD:
2316 module_name = "ietf-netconf";
2317 rpc_name = "discard-changes";
2318 break;
2319 case NC_RPC_CANCEL:
2320 module_name = "ietf-netconf";
2321 rpc_name = "cancel-commit";
2322 break;
2323 case NC_RPC_VALIDATE:
2324 module_name = "ietf-netconf";
2325 rpc_name = "validate";
2326 break;
2327 case NC_RPC_GETSCHEMA:
2328 module_name = "ietf-netconf-monitoring";
2329 rpc_name = "get-schema";
2330 break;
2331 case NC_RPC_SUBSCRIBE:
2332 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002333 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002334 break;
2335 case NC_RPC_GETDATA:
2336 module_name = "ietf-netconf-nmda";
2337 rpc_name = "get-data";
2338 break;
2339 case NC_RPC_EDITDATA:
2340 module_name = "ietf-netconf-nmda";
2341 rpc_name = "edit-data";
2342 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002343 case NC_RPC_ESTABLISHSUB:
2344 module_name = "ietf-subscribed-notifications";
2345 rpc_name = "establish-subscription";
2346 break;
2347 case NC_RPC_MODIFYSUB:
2348 module_name = "ietf-subscribed-notifications";
2349 rpc_name = "modify-subscription";
2350 break;
2351 case NC_RPC_DELETESUB:
2352 module_name = "ietf-subscribed-notifications";
2353 rpc_name = "delete-subscription";
2354 break;
2355 case NC_RPC_KILLSUB:
2356 module_name = "ietf-subscribed-notifications";
2357 rpc_name = "kill-subscription";
2358 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002359 case NC_RPC_ESTABLISHPUSH:
2360 module_name = "ietf-subscribed-notifications";
2361 rpc_name = "establish-subscription";
2362 module_check = "ietf-yang-push";
2363 break;
2364 case NC_RPC_MODIFYPUSH:
2365 module_name = "ietf-subscribed-notifications";
2366 rpc_name = "modify-subscription";
2367 module_check = "ietf-yang-push";
2368 break;
2369 case NC_RPC_RESYNCSUB:
2370 module_name = "ietf-yang-push";
2371 rpc_name = "resync-subscription";
2372 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002373 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002374 lyrc = LY_EINT;
2375 break;
2376 }
2377
2378 if (module_name && rpc_name) {
2379 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2380 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002381 ERR(session, "Missing \"%s\" module in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002382 return -1;
2383 }
2384
2385 /* create the operation node */
2386 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2387 }
Michal Vasko305faca2021-03-25 09:16:02 +01002388 if (module_check) {
2389 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002390 ERR(session, "Missing \"%s\" module in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002391 return -1;
2392 }
2393 }
Michal Vasko77367452021-02-16 16:32:18 +01002394
2395 if (lyrc) {
2396 return -1;
2397 }
2398 return 0;
2399}
2400
2401API NC_MSG_TYPE
2402nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2403 struct lyd_node **op)
2404{
2405 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002406
roman40672412023-05-04 11:10:22 +02002407 NC_CHECK_ARG_RET(session, session, rpc, envp, op, NC_MSG_ERROR);
2408
2409 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002410 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002411 return NC_MSG_ERROR;
2412 }
Michal Vasko77367452021-02-16 16:32:18 +01002413
2414 /* get a duplicate of the RPC node to append reply to */
2415 if (recv_reply_dup_rpc(session, rpc, op)) {
2416 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002417 }
Michal Vasko086311b2016-01-08 09:53:11 +01002418
Michal Vasko77367452021-02-16 16:32:18 +01002419 /* receive a reply */
2420 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002421
Michal Vasko77367452021-02-16 16:32:18 +01002422 /* do not return the RPC copy on error or if the reply includes no data */
2423 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2424 lyd_free_tree(*op);
2425 *op = NULL;
2426 }
2427 return ret;
2428}
2429
2430static NC_MSG_TYPE
2431recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2432{
Michal Vasko77367452021-02-16 16:32:18 +01002433 LY_ERR lyrc;
2434 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002435 NC_MSG_TYPE ret = NC_MSG_ERROR;
2436
2437 *op = NULL;
2438 *envp = NULL;
2439
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002440 /* Receive messages until a notification is found or a timeout or error reached */
2441 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2442 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002443 goto cleanup;
2444 }
2445
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002446 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002447 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2448 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002449 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002450 } else {
Michal Vasko58791da2024-02-26 13:52:59 +01002451 ERR(session, "Received an invalid message (%s).", ly_err_last(session->ctx)->msg);
Michal Vasko9a108052022-04-01 12:06:54 +02002452 lyd_free_tree(*envp);
2453 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002454 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002455 goto cleanup;
2456 }
2457
Michal Vasko77367452021-02-16 16:32:18 +01002458cleanup:
2459 ly_in_free(msg, 1);
2460 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002461}
2462
2463API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002464nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002465{
roman40672412023-05-04 11:10:22 +02002466 NC_CHECK_ARG_RET(session, session, envp, op, NC_MSG_ERROR);
2467
2468 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002469 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002470 return NC_MSG_ERROR;
2471 }
2472
Michal Vasko77367452021-02-16 16:32:18 +01002473 /* receive a notification */
2474 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002475}
2476
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002477static void *
2478nc_recv_notif_thread(void *arg)
2479{
2480 struct nc_ntf_thread_arg *ntarg;
2481 struct nc_session *session;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002482 nc_notif_dispatch_clb notif_clb;
2483 void *user_data;
Michal Vasko743ff9f2022-10-20 10:03:13 +02002484
Michal Vaskoffb35e92022-10-20 09:07:25 +02002485 void (*free_data)(void *);
Michal Vasko77367452021-02-16 16:32:18 +01002486 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002487 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002488
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002489 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002490 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002491
2492 ntarg = (struct nc_ntf_thread_arg *)arg;
2493 session = ntarg->session;
2494 notif_clb = ntarg->notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002495 user_data = ntarg->user_data;
2496 free_data = ntarg->free_data;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002497 free(ntarg);
2498
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002499 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
Michal Vasko77367452021-02-16 16:32:18 +01002500 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002501 if (msgtype == NC_MSG_NOTIF) {
Michal Vaskoffb35e92022-10-20 09:07:25 +02002502 notif_clb(session, envp, op, user_data);
Michal Vasko77367452021-02-16 16:32:18 +01002503 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
Michal Vaskoc865a9b2024-07-19 14:25:02 +02002504 lyd_free_all(envp);
2505 lyd_free_all(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002506 break;
2507 }
Michal Vaskoc865a9b2024-07-19 14:25:02 +02002508 lyd_free_all(envp);
2509 lyd_free_all(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002510 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002511 /* quit this thread once the session is broken */
2512 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002513 }
2514
2515 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2516 }
2517
Michal Vasko05532772021-06-03 12:12:38 +02002518 VRB(session, "Notification thread exit.");
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002519 ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoffb35e92022-10-20 09:07:25 +02002520 if (free_data) {
2521 free_data(user_data);
2522 }
2523
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002524 return NULL;
2525}
2526
2527API int
Michal Vaskoffb35e92022-10-20 09:07:25 +02002528nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb)
2529{
2530 return nc_recv_notif_dispatch_data(session, notif_clb, NULL, NULL);
2531}
2532
2533API int
2534nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data,
2535 void (*free_data)(void *))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002536{
2537 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002538 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002539 int ret;
2540
roman40672412023-05-04 11:10:22 +02002541 NC_CHECK_ARG_RET(session, session, notif_clb, -1);
2542
2543 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002544 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002545 return -1;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002546 }
2547
2548 ntarg = malloc(sizeof *ntarg);
roman3a95bb22023-10-26 11:07:17 +02002549 NC_CHECK_ERRMEM_RET(!ntarg, -1);
2550
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002551 ntarg->session = session;
2552 ntarg->notif_clb = notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002553 ntarg->user_data = user_data;
2554 ntarg->free_data = free_data;
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002555 ATOMIC_INC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002556
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002557 /* just so that nc_recv_notif_thread() does not immediately exit */
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002558 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002559
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002560 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002561 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002562 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002563 free(ntarg);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002564 if (ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count) == 1) {
2565 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
2566 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002567 return -1;
2568 }
2569
2570 return 0;
2571}
2572
Michal Vasko77367452021-02-16 16:32:18 +01002573static const char *
2574nc_wd2str(NC_WD_MODE wd)
2575{
2576 switch (wd) {
2577 case NC_WD_ALL:
2578 return "report-all";
2579 case NC_WD_ALL_TAG:
2580 return "report-all-tagged";
2581 case NC_WD_TRIM:
2582 return "trim";
2583 case NC_WD_EXPLICIT:
2584 return "explicit";
2585 default:
2586 break;
2587 }
2588
2589 return NULL;
2590}
2591
Michal Vasko086311b2016-01-08 09:53:11 +01002592API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002593nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002594{
2595 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002596 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002597 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002598 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002599 struct nc_rpc_getconfig *rpc_gc;
2600 struct nc_rpc_edit *rpc_e;
2601 struct nc_rpc_copy *rpc_cp;
2602 struct nc_rpc_delete *rpc_del;
2603 struct nc_rpc_lock *rpc_lock;
2604 struct nc_rpc_get *rpc_g;
2605 struct nc_rpc_kill *rpc_k;
2606 struct nc_rpc_commit *rpc_com;
2607 struct nc_rpc_cancel *rpc_can;
2608 struct nc_rpc_validate *rpc_val;
2609 struct nc_rpc_getschema *rpc_gs;
2610 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002611 struct nc_rpc_getdata *rpc_getd;
2612 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002613 struct nc_rpc_establishsub *rpc_estsub;
2614 struct nc_rpc_modifysub *rpc_modsub;
2615 struct nc_rpc_deletesub *rpc_delsub;
2616 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002617 struct nc_rpc_establishpush *rpc_estpush;
2618 struct nc_rpc_modifypush *rpc_modpush;
2619 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002620 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002621 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002622 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002623 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002624 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002625 uint64_t cur_msgid;
2626
roman40672412023-05-04 11:10:22 +02002627 NC_CHECK_ARG_RET(session, session, rpc, msgid, NC_MSG_ERROR);
2628
2629 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002630 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002631 return NC_MSG_ERROR;
2632 }
2633
Michal Vaskoc1171a42019-11-05 12:06:46 +01002634 switch (rpc->type) {
2635 case NC_RPC_ACT_GENERIC:
2636 /* checked when parsing */
2637 break;
2638 case NC_RPC_GETCONFIG:
2639 case NC_RPC_EDIT:
2640 case NC_RPC_COPY:
2641 case NC_RPC_DELETE:
2642 case NC_RPC_LOCK:
2643 case NC_RPC_UNLOCK:
2644 case NC_RPC_GET:
2645 case NC_RPC_KILL:
2646 case NC_RPC_COMMIT:
2647 case NC_RPC_DISCARD:
2648 case NC_RPC_CANCEL:
2649 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002650 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002651 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002652 ERR(session, "Missing \"ietf-netconf\" module in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002653 return NC_MSG_ERROR;
2654 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002655 break;
2656 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002657 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002658 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002659 ERR(session, "Missing \"ietf-netconf-monitoring\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002660 return NC_MSG_ERROR;
2661 }
2662 break;
2663 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002664 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002665 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002666 ERR(session, "Missing \"notifications\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002667 return NC_MSG_ERROR;
2668 }
2669 break;
2670 case NC_RPC_GETDATA:
2671 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002672 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002673 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002674 ERR(session, "Missing \"ietf-netconf-nmda\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002675 return NC_MSG_ERROR;
2676 }
2677 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002678 case NC_RPC_ESTABLISHSUB:
2679 case NC_RPC_MODIFYSUB:
2680 case NC_RPC_DELETESUB:
2681 case NC_RPC_KILLSUB:
2682 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2683 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002684 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002685 return NC_MSG_ERROR;
2686 }
2687 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002688 case NC_RPC_ESTABLISHPUSH:
2689 case NC_RPC_MODIFYPUSH:
2690 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2691 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002692 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002693 return NC_MSG_ERROR;
2694 }
2695 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2696 if (!mod2) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002697 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002698 return NC_MSG_ERROR;
2699 }
2700 break;
2701 case NC_RPC_RESYNCSUB:
2702 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2703 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002704 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002705 return NC_MSG_ERROR;
2706 }
2707 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002708 case NC_RPC_UNKNOWN:
2709 ERRINT;
2710 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002711 }
2712
Michal Vaskoab9deb62021-05-27 11:37:00 +02002713#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2714
Michal Vasko086311b2016-01-08 09:53:11 +01002715 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002716 case NC_RPC_ACT_GENERIC:
2717 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002718
2719 if (rpc_gen->has_data) {
2720 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002721 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002722 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002723 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2724 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2725 ly_in_free(in, 0);
2726 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002727 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002728 }
Michal Vasko086311b2016-01-08 09:53:11 +01002729 }
2730 break;
2731
2732 case NC_RPC_GETCONFIG:
2733 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2734
Michal Vaskoab9deb62021-05-27 11:37:00 +02002735 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2736 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2737 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002738 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002739 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002740 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, LYD_ANYDATA_XML, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002741 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002742 } else {
Michal Vasko58791da2024-02-26 13:52:59 +01002743 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002744 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2745 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002746 }
2747 }
2748
2749 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002750 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002751 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002752 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002753 lyrc = LY_ENOTFOUND;
2754 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002755 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002756 CHECK_LYRC_BREAK(lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_gc->wd_mode), 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002757 }
2758 break;
2759
2760 case NC_RPC_EDIT:
2761 rpc_e = (struct nc_rpc_edit *)rpc;
2762
Michal Vaskoab9deb62021-05-27 11:37:00 +02002763 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2764 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2765 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002766
2767 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002768 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_e->default_op], 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002769 }
Michal Vasko086311b2016-01-08 09:53:11 +01002770 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002771 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "test-option", rpcedit_testopt2str[rpc_e->test_opt], 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002772 }
Michal Vasko086311b2016-01-08 09:53:11 +01002773 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002774 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "error-option", rpcedit_erropt2str[rpc_e->error_opt], 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002775 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002776 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002777 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_e->edit_cont, LYD_ANYDATA_XML, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002778 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002779 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002780 }
2781 break;
2782
2783 case NC_RPC_COPY:
2784 rpc_cp = (struct nc_rpc_copy *)rpc;
2785
Michal Vaskoab9deb62021-05-27 11:37:00 +02002786 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2787 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002788 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002789 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002790 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002791 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002792 }
2793
Michal Vaskoab9deb62021-05-27 11:37:00 +02002794 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002795 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002796 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002797 CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_cp->url_config_src, LYD_ANYDATA_XML, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002798 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002799 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002800 }
2801 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002802 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002803 }
2804
2805 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002806 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002807 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002808 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002809 lyrc = LY_ENOTFOUND;
2810 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002811 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002812 CHECK_LYRC_BREAK(lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_cp->wd_mode), 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002813 }
2814 break;
2815
2816 case NC_RPC_DELETE:
2817 rpc_del = (struct nc_rpc_delete *)rpc;
2818
Michal Vaskoab9deb62021-05-27 11:37:00 +02002819 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2820 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002821 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002822 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002823 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002824 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002825 }
2826 break;
2827
2828 case NC_RPC_LOCK:
2829 rpc_lock = (struct nc_rpc_lock *)rpc;
2830
Michal Vaskoab9deb62021-05-27 11:37:00 +02002831 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2832 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2833 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002834 break;
2835
2836 case NC_RPC_UNLOCK:
2837 rpc_lock = (struct nc_rpc_lock *)rpc;
2838
Michal Vaskoab9deb62021-05-27 11:37:00 +02002839 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2840 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2841 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002842 break;
2843
2844 case NC_RPC_GET:
2845 rpc_g = (struct nc_rpc_get *)rpc;
2846
Michal Vaskoab9deb62021-05-27 11:37:00 +02002847 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002848 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002849 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002850 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, LYD_ANYDATA_XML, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002851 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002852 } else {
Michal Vasko58791da2024-02-26 13:52:59 +01002853 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002854 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2855 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002856 }
2857 }
2858
2859 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002860 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002861 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002862 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002863 lyrc = LY_ENOTFOUND;
2864 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002865 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002866 CHECK_LYRC_BREAK(lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_g->wd_mode), 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002867 }
2868 break;
2869
2870 case NC_RPC_KILL:
2871 rpc_k = (struct nc_rpc_kill *)rpc;
2872
Michal Vaskoab9deb62021-05-27 11:37:00 +02002873 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko16374712024-04-26 14:13:00 +02002874 sprintf(str, "%" PRIu32, rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002875 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002876 break;
2877
2878 case NC_RPC_COMMIT:
2879 rpc_com = (struct nc_rpc_commit *)rpc;
2880
Michal Vaskoab9deb62021-05-27 11:37:00 +02002881 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002882 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002883 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002884 }
2885
2886 if (rpc_com->confirm_timeout) {
Michal Vasko16374712024-04-26 14:13:00 +02002887 sprintf(str, "%" PRIu32, rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002888 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002889 }
Michal Vasko086311b2016-01-08 09:53:11 +01002890 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002891 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002892 }
Michal Vasko086311b2016-01-08 09:53:11 +01002893 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002894 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002895 }
2896 break;
2897
2898 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002899 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002900 break;
2901
2902 case NC_RPC_CANCEL:
2903 rpc_can = (struct nc_rpc_cancel *)rpc;
2904
Michal Vaskoab9deb62021-05-27 11:37:00 +02002905 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002906 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002907 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002908 }
2909 break;
2910
2911 case NC_RPC_VALIDATE:
2912 rpc_val = (struct nc_rpc_validate *)rpc;
2913
Michal Vaskoab9deb62021-05-27 11:37:00 +02002914 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2915 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002916 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002917 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002918 CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_val->url_config_src, LYD_ANYDATA_XML, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002919 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002920 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002921 }
2922 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002923 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002924 }
2925 break;
2926
2927 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002928 rpc_gs = (struct nc_rpc_getschema *)rpc;
2929
Michal Vaskoab9deb62021-05-27 11:37:00 +02002930 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2931 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002932 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002933 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002934 }
2935 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002936 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002937 }
2938 break;
2939
2940 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002941 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2942
Michal Vaskoab9deb62021-05-27 11:37:00 +02002943 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002944 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002945 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002946 }
2947
2948 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002949 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002950 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, LYD_ANYDATA_XML, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002951 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002952 } else {
Michal Vasko58791da2024-02-26 13:52:59 +01002953 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002954 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2955 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002956 }
2957 }
Michal Vasko086311b2016-01-08 09:53:11 +01002958 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002959 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002960 }
Michal Vasko086311b2016-01-08 09:53:11 +01002961 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002962 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002963 }
2964 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002965
2966 case NC_RPC_GETDATA:
2967 rpc_getd = (struct nc_rpc_getdata *)rpc;
2968
Michal Vaskoab9deb62021-05-27 11:37:00 +02002969 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2970 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2971
Michal Vaskoc1171a42019-11-05 12:06:46 +01002972 if (rpc_getd->filter) {
2973 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002974 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "subtree-filter", rpc_getd->filter, LYD_ANYDATA_XML, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002975 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002976 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002977 }
2978 }
2979 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002980 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002981 }
2982 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002983 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2984 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002985 }
2986 if (rpc_getd->max_depth) {
2987 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002988 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002989 }
2990 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002991 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002992 }
2993
2994 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002995 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002996 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-defaults", nc_wd2str(rpc_getd->wd_mode), 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002997 }
2998 break;
2999
3000 case NC_RPC_EDITDATA:
3001 rpc_editd = (struct nc_rpc_editdata *)rpc;
3002
Michal Vaskoab9deb62021-05-27 11:37:00 +02003003 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
3004 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01003005
3006 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003007 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
3008 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01003009 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01003010 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01003011 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_editd->edit_cont, LYD_ANYDATA_XML, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01003012 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003013 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01003014 }
3015 break;
3016
Michal Vasko96f247a2021-03-15 13:32:10 +01003017 case NC_RPC_ESTABLISHSUB:
3018 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
3019
Michal Vaskoab9deb62021-05-27 11:37:00 +02003020 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003021
3022 if (rpc_estsub->filter) {
3023 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01003024 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, LYD_ANYDATA_XML,
Michal Vaskoab9deb62021-05-27 11:37:00 +02003025 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003026 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003027 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003028 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003029 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003030 }
3031 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02003032 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003033
3034 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003035 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003036 }
Michal Vasko96f247a2021-03-15 13:32:10 +01003037 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003038 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003039 }
Michal Vasko96f247a2021-03-15 13:32:10 +01003040 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003041 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003042 }
3043 break;
3044
3045 case NC_RPC_MODIFYSUB:
3046 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
3047
Michal Vaskoab9deb62021-05-27 11:37:00 +02003048 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003049
Michal Vasko16374712024-04-26 14:13:00 +02003050 sprintf(str, "%" PRIu32, rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003051 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003052
3053 if (rpc_modsub->filter) {
3054 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01003055 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, LYD_ANYDATA_XML,
Michal Vaskoab9deb62021-05-27 11:37:00 +02003056 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003057 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003058 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003059 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003060 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003061 }
3062 }
Michal Vasko96f247a2021-03-15 13:32:10 +01003063 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003064 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003065 }
3066 break;
3067
3068 case NC_RPC_DELETESUB:
3069 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
3070
Michal Vaskoab9deb62021-05-27 11:37:00 +02003071 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003072
Michal Vasko16374712024-04-26 14:13:00 +02003073 sprintf(str, "%" PRIu32, rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003074 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003075 break;
3076
3077 case NC_RPC_KILLSUB:
3078 rpc_killsub = (struct nc_rpc_killsub *)rpc;
3079
Michal Vaskoab9deb62021-05-27 11:37:00 +02003080 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003081
Michal Vasko16374712024-04-26 14:13:00 +02003082 sprintf(str, "%" PRIu32, rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003083 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003084 break;
3085
Michal Vasko305faca2021-03-25 09:16:02 +01003086 case NC_RPC_ESTABLISHPUSH:
3087 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
3088
Michal Vaskoab9deb62021-05-27 11:37:00 +02003089 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
3090 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003091
3092 if (rpc_estpush->filter) {
3093 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01003094 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter,
Michal Vaskoab9deb62021-05-27 11:37:00 +02003095 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003096 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003097 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003098 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003099 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003100 }
3101 }
3102
3103 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003104 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003105 }
Michal Vasko305faca2021-03-25 09:16:02 +01003106 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003107 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003108 }
3109
3110 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003111 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003112 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003113 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003114 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003115 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003116 }
3117 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003118 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003119 if (rpc_estpush->dampening_period) {
3120 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003121 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003122 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02003123 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
3124 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003125 if (rpc_estpush->excluded_change) {
3126 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003127 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
3128 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003129 }
3130 }
3131 }
3132 break;
3133
3134 case NC_RPC_MODIFYPUSH:
3135 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
3136
Michal Vaskoab9deb62021-05-27 11:37:00 +02003137 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003138
Michal Vasko16374712024-04-26 14:13:00 +02003139 sprintf(str, "%" PRIu32, rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003140 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
3141 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003142
3143 if (rpc_modpush->filter) {
3144 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01003145 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter,
Michal Vaskoab9deb62021-05-27 11:37:00 +02003146 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003147 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003148 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003149 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003150 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003151 }
3152 }
Michal Vasko305faca2021-03-25 09:16:02 +01003153 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003154 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003155 }
3156
3157 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003158 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003159 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003160 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003161 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003162 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003163 }
3164 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003165 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003166 if (rpc_modpush->dampening_period) {
3167 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003168 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003169 }
3170 }
3171 break;
3172
3173 case NC_RPC_RESYNCSUB:
3174 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
3175
Michal Vaskoab9deb62021-05-27 11:37:00 +02003176 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko16374712024-04-26 14:13:00 +02003177 sprintf(str, "%" PRIu32, rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003178 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003179 break;
3180
Michal Vasko96f247a2021-03-15 13:32:10 +01003181 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01003182 ERRINT;
3183 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01003184 }
3185
Michal Vaskoab9deb62021-05-27 11:37:00 +02003186#undef CHECK_LYRC_BREAK
3187
3188 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02003189 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02003190 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02003191 return NC_MSG_ERROR;
3192 }
3193
Michal Vasko131120a2018-05-29 15:44:02 +02003194 /* send RPC, store its message ID */
3195 r = nc_send_msg_io(session, timeout, data);
3196 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003197
Radek Krejcib4b19062018-02-07 16:33:06 +01003198 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003199 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003200 }
Michal Vasko086311b2016-01-08 09:53:11 +01003201
Michal Vasko131120a2018-05-29 15:44:02 +02003202 if (r == NC_MSG_RPC) {
3203 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003204 }
Michal Vasko131120a2018-05-29 15:44:02 +02003205 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003206}
Michal Vaskode2946c2017-01-12 12:19:26 +01003207
3208API void
3209nc_client_session_set_not_strict(struct nc_session *session)
3210{
3211 if (session->side != NC_CLIENT) {
roman40672412023-05-04 11:10:22 +02003212 ERRARG(NULL, "session");
Michal Vaskode2946c2017-01-12 12:19:26 +01003213 return;
3214 }
3215
3216 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3217}