blob: f9351c2de9168b32bd3b1eb162add1a7d593b2e2 [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
42#include <libyang/libyang.h>
43
Michal Vasko9e8ac262020-04-07 13:06:45 +020044#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020045#include "config.h"
46#include "log_p.h"
47#include "messages_p.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020048#include "session_client.h"
roman3f9b65c2023-06-05 14:26:58 +020049#include "session_client_ch.h"
50#include "session_p.h"
Michal Vasko086311b2016-01-08 09:53:11 +010051
Michal Vasko8a4e1462020-05-07 11:32:31 +020052#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020053#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020054
Michal Vasko80ef5d22016-01-18 09:21:02 +010055static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
56
roman2eab4742023-06-06 10:00:26 +020057#ifdef NC_ENABLED_SSH_TLS
Radek Krejci62aa0642017-05-25 16:33:49 +020058char *sshauth_password(const char *username, const char *hostname, void *priv);
59char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020060char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
roman2eab4742023-06-06 10:00:26 +020061#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +020062
63static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
64static pthread_key_t nc_client_context_key;
65#ifdef __linux__
66static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020067 .opts.ka = {
68 .enabled = 1,
69 .idle_time = 1,
70 .max_probes = 10,
71 .probe_interval = 5
72 },
roman2eab4742023-06-06 10:00:26 +020073#ifdef NC_ENABLED_SSH_TLS
Radek Krejci62aa0642017-05-25 16:33:49 +020074 .ssh_opts = {
roman41a11e42022-06-22 09:27:08 +020075 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020076 .auth_password = sshauth_password,
77 .auth_interactive = sshauth_interactive,
78 .auth_privkey_passphrase = sshauth_privkey_passphrase
79 },
80 .ssh_ch_opts = {
81 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020082 .auth_password = sshauth_password,
83 .auth_interactive = sshauth_interactive,
84 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020085 },
roman2eab4742023-06-06 10:00:26 +020086#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +020087 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020088 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020089};
90#endif
91
92static void
93nc_client_context_free(void *ptr)
94{
95 struct nc_client_context *c = (struct nc_client_context *)ptr;
96
Radek Krejci5cebc6b2017-05-26 13:24:38 +020097 if (--(c->refcount)) {
98 /* still used */
99 return;
100 }
101
Radek Krejci62aa0642017-05-25 16:33:49 +0200102#ifdef __linux__
103 /* in __linux__ we use static memory in the main thread,
104 * so this check is for programs terminating the main()
105 * function by pthread_exit() :)
106 */
107 if (c != &context_main)
108#endif
109 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200110 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400111 free(c->opts.schema_searchpath);
112
roman2eab4742023-06-06 10:00:26 +0200113#ifdef NC_ENABLED_SSH_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400114 int i;
Michal Vasko292c5542023-02-01 14:33:17 +0100115
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400116 for (i = 0; i < c->opts.ch_bind_count; ++i) {
117 close(c->opts.ch_binds[i].sock);
118 free((char *)c->opts.ch_binds[i].address);
119 }
120 free(c->opts.ch_binds);
121 c->opts.ch_binds = NULL;
122 c->opts.ch_bind_count = 0;
roman2eab4742023-06-06 10:00:26 +0200123
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400124 _nc_client_ssh_destroy_opts(&c->ssh_opts);
125 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
roman2eab4742023-06-06 10:00:26 +0200126
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400127 _nc_client_tls_destroy_opts(&c->tls_opts);
128 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
roman2eab4742023-06-06 10:00:26 +0200129#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +0200130 free(c);
131 }
132}
133
134static void
135nc_client_context_createkey(void)
136{
137 int r;
138
139 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200140 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200141 pthread_setspecific(nc_client_context_key, NULL);
142}
143
144struct nc_client_context *
145nc_client_context_location(void)
146{
147 struct nc_client_context *e;
148
149 pthread_once(&nc_client_context_once, nc_client_context_createkey);
150 e = pthread_getspecific(nc_client_context_key);
151 if (!e) {
152 /* prepare ly_err storage */
153#ifdef __linux__
154 if (getpid() == syscall(SYS_gettid)) {
155 /* main thread - use global variable instead of thread-specific variable. */
156 e = &context_main;
157 } else
158#endif /* __linux__ */
159 {
160 e = calloc(1, sizeof *e);
161 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200162 e->refcount = 1;
roman2eab4742023-06-06 10:00:26 +0200163#ifdef NC_ENABLED_SSH_TLS
Michal Vasko487ab802024-04-26 12:20:29 +0200164# ifdef HAVE_TERMIOS
165 e->ssh_opts.knownhosts_mode = NC_SSH_KNOWNHOSTS_ASK;
166# else
167 e->ssh_opts.knownhosts_mode = NC_SSH_KNOWNHOSTS_ACCEPT;
168# endif
Radek Krejci62aa0642017-05-25 16:33:49 +0200169 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
roman41a11e42022-06-22 09:27:08 +0200170 e->ssh_opts.auth_pref[0].value = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200171 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
172 e->ssh_opts.auth_pref[1].value = 2;
173 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
roman41a11e42022-06-22 09:27:08 +0200174 e->ssh_opts.auth_pref[2].value = 3;
Radek Krejci62aa0642017-05-25 16:33:49 +0200175 e->ssh_opts.auth_password = sshauth_password;
176 e->ssh_opts.auth_interactive = sshauth_interactive;
177 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
178
roman41a11e42022-06-22 09:27:08 +0200179 /* callhome settings are the same */
Radek Krejci62aa0642017-05-25 16:33:49 +0200180 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
181 e->ssh_ch_opts.auth_pref[0].value = 1;
182 e->ssh_ch_opts.auth_pref[1].value = 2;
183 e->ssh_ch_opts.auth_pref[2].value = 3;
roman2eab4742023-06-06 10:00:26 +0200184#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +0200185 }
186 pthread_setspecific(nc_client_context_key, e);
187 }
188
189 return e;
190}
191
192#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100193
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200194API void *
195nc_client_get_thread_context(void)
196{
197 return nc_client_context_location();
198}
199
200API void
201nc_client_set_thread_context(void *context)
202{
203 struct nc_client_context *old, *new;
204
205 if (!context) {
roman40672412023-05-04 11:10:22 +0200206 ERRARG(NULL, "context");
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200207 return;
208 }
209
210 new = (struct nc_client_context *)context;
211 old = nc_client_context_location();
212 if (old == new) {
213 /* nothing to change */
214 return;
215 }
216
217 /* replace old by new, increase reference counter in the newly set context */
218 nc_client_context_free(old);
219 new->refcount++;
220 pthread_setspecific(nc_client_context_key, new);
221}
222
Michal Vasko78939072022-12-12 07:43:18 +0100223/**
224 * @brief Ext data callback for a context to provide schema mount data.
225 */
226static LY_ERR
227nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
228{
229 struct nc_session *session = user_data;
230
231 if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
232 return LY_EINVAL;
233 }
234
235 if (!session->opts.client.ext_data) {
236 ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server.");
237 return LY_ENOTFOUND;
238 }
239
240 /* return ext data */
241 *ext_data = session->opts.client.ext_data;
242 *ext_data_free = 0;
243
244 return LY_SUCCESS;
245}
246
Radek Krejcifd5b6682017-06-13 15:52:53 +0200247int
Michal Vasko78939072022-12-12 07:43:18 +0100248nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200249{
250 /* assign context (dicionary needed for handshake) */
251 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100252 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200253 return EXIT_FAILURE;
254 }
255
Michal Vasko5ca5d972022-09-14 13:51:31 +0200256 /* user path must be first, the first path is used to store modules retreived via get-schema */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200257 if (client_opts.schema_searchpath) {
258 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko5d0cdc32022-12-12 07:38:21 +0100259 }
Michal Vasko80227222022-12-12 09:05:24 +0100260 if (!access(NC_CLIENT_SEARCH_DIR, F_OK)) {
261 ly_ctx_set_searchdir(ctx, NC_CLIENT_SEARCH_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200262 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200263
Michal Vasko5ca5d972022-09-14 13:51:31 +0200264 /* set callback for getting modules, if provided */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200265 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
Michal Vasko78939072022-12-12 07:43:18 +0100266
267 /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */
268 ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200269 } else {
270 session->flags |= NC_SESSION_SHAREDCTX;
271 }
272
273 session->ctx = ctx;
274
275 return EXIT_SUCCESS;
276}
277
Michal Vasko086311b2016-01-08 09:53:11 +0100278API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100279nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100280{
Michal Vasko3031aae2016-01-27 16:07:18 +0100281 if (client_opts.schema_searchpath) {
282 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100283 }
Michal Vasko086311b2016-01-08 09:53:11 +0100284
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100285 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100286 client_opts.schema_searchpath = strdup(path);
roman3a95bb22023-10-26 11:07:17 +0200287 NC_CHECK_ERRMEM_RET(!client_opts.schema_searchpath, 1);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100288 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100289 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100290 }
291
292 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100293}
294
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100295API const char *
296nc_client_get_schema_searchpath(void)
297{
298 return client_opts.schema_searchpath;
299}
300
Radek Krejcifd5b6682017-06-13 15:52:53 +0200301API int
302nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100303{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200304 client_opts.schema_clb = clb;
305 if (clb) {
306 client_opts.schema_clb_data = user_data;
307 } else {
308 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100309 }
310
311 return 0;
312}
313
Radek Krejcifd5b6682017-06-13 15:52:53 +0200314API ly_module_imp_clb
315nc_client_get_schema_callback(void **user_data)
316{
317 if (user_data) {
318 (*user_data) = client_opts.schema_clb_data;
319 }
320 return client_opts.schema_clb;
321}
322
Michal Vaskoa272e092023-07-10 14:34:03 +0200323API void
324nc_client_set_new_session_context_autofill(int enabled)
325{
326 client_opts.auto_context_fill_disabled = !enabled;
327}
328
Michal Vasko5ca5d972022-09-14 13:51:31 +0200329struct module_info {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200330 char *name;
331 char *revision;
Michal Vasko6ce8e822022-08-02 15:09:17 +0200332
Radek Krejci65ef6d52018-08-16 16:35:02 +0200333 struct {
334 char *name;
335 char *revision;
336 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100337 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200338 int implemented;
339};
340
341struct clb_data_s {
342 void *user_data;
343 ly_module_imp_clb user_clb;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200344 struct module_info *modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200345 struct nc_session *session;
346 int has_get_schema;
347};
348
Michal Vaskoc088f982021-10-05 12:23:07 +0200349/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200350 * @brief Retrieve YANG module content from a local file.
Michal Vaskoc088f982021-10-05 12:23:07 +0200351 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200352 * @param[in] name Module name.
353 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200354 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200355 * @param[out] format Module format.
356 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200357 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200358static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200359retrieve_module_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200360 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100361{
Michal Vaskob13a2242023-12-05 13:29:07 +0100362 char *localfile = NULL, *model_data = NULL;
363 const char *ptr;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200364 FILE *f;
365 long length, l;
Michal Vasko086311b2016-01-08 09:53:11 +0100366
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200367 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200368 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
369 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200370 return NULL;
371 }
Michal Vaskob13a2242023-12-05 13:29:07 +0100372 if (localfile && rev) {
373 ptr = strrchr(localfile, '/');
374 if (!strchr(ptr, '@')) {
375 /* we do not know the revision of the module and we require a specific one, so ignore this module */
376 localfile = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100377 }
Michal Vasko086311b2016-01-08 09:53:11 +0100378 }
379
Michal Vaskob13a2242023-12-05 13:29:07 +0100380 if (!localfile) {
381 return NULL;
382 }
383
384 VRB(clb_data->session, "Reading module \"%s@%s\" from local file \"%s\".", name, rev ? rev : "<latest>",
385 localfile);
386 f = fopen(localfile, "r");
387 if (!f) {
388 ERR(clb_data->session, "Unable to open file \"%s\" (%s).", localfile, strerror(errno));
389 free(localfile);
390 return NULL;
391 }
392
393 fseek(f, 0, SEEK_END);
394 length = ftell(f);
395 if (length < 0) {
396 ERR(clb_data->session, "Unable to get the size of module file \"%s\".", localfile);
397 free(localfile);
398 fclose(f);
399 return NULL;
400 }
401 fseek(f, 0, SEEK_SET);
402
403 model_data = malloc(length + 1);
404 if (!model_data) {
405 ERRMEM;
406 } else if ((l = fread(model_data, 1, length, f)) != length) {
407 ERR(clb_data->session, "Reading module from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
408 length);
409 free(model_data);
410 model_data = NULL;
411 } else {
412 /* terminating NULL byte */
413 model_data[length] = '\0';
414 }
415 fclose(f);
416 free(localfile);
417
Radek Krejci65ef6d52018-08-16 16:35:02 +0200418 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100419}
420
Michal Vaskoc088f982021-10-05 12:23:07 +0200421/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200422 * @brief Retrieve YANG module content from a reply to get-schema RPC.
Michal Vaskoc088f982021-10-05 12:23:07 +0200423 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200424 * @param[in] name Module name.
425 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200426 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200427 * @param[out] format Module format.
428 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200429 */
Michal Vasko086311b2016-01-08 09:53:11 +0100430static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200431retrieve_module_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200432 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100433{
Michal Vasko086311b2016-01-08 09:53:11 +0100434 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100435 struct lyd_node *envp = NULL, *op = NULL;
436 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100437 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100438 uint64_t msgid;
Michal Vasko7502b602022-08-26 11:51:17 +0200439 char *localfile = NULL, *envp_str = NULL, *model_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200440 FILE *f;
Michal Vasko086311b2016-01-08 09:53:11 +0100441
Michal Vasko5ca5d972022-09-14 13:51:31 +0200442 VRB(clb_data->session, "Reading module \"%s@%s\" from server via get-schema.", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200443 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100444
Radek Krejci65ef6d52018-08-16 16:35:02 +0200445 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100446 usleep(1000);
447 }
448 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200449 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100450 nc_rpc_free(rpc);
451 return NULL;
452 }
453
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200454 do {
Michal Vasko77367452021-02-16 16:32:18 +0100455 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200456 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100457 nc_rpc_free(rpc);
458 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200459 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100460 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100461 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200462 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100463 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100464 } else if (!op) {
Michal Vasko7502b602022-08-26 11:51:17 +0200465 assert(envp);
466 lyd_print_mem(&envp_str, envp, LYD_XML, 0);
467 WRN(clb_data->session, "Received an unexpected reply to <get-schema>:\n%s", envp_str);
468 free(envp_str);
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100469 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100470 }
471
Michal Vasko77367452021-02-16 16:32:18 +0100472 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200473 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100474 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200475 }
Michal Vasko77367452021-02-16 16:32:18 +0100476 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200477 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200478 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100479 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200480 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200481 break;
482 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100483 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200484 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200485 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200486 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200487 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100488 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200489 }
Michal Vasko086311b2016-01-08 09:53:11 +0100490
Radek Krejci488b0702018-08-29 14:47:15 +0200491 if (model_data && !model_data[0]) {
492 /* empty data */
493 free(model_data);
494 model_data = NULL;
495 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100496 if (!model_data) {
497 goto cleanup;
498 }
499
500 /* set format */
501 *format = LYS_IN_YANG;
Radek Krejci488b0702018-08-29 14:47:15 +0200502
Michal Vasko5ca5d972022-09-14 13:51:31 +0200503 /* try to store the model_data into local module repository */
Michal Vaskoa1721172022-12-12 09:06:53 +0100504 lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx), 0, name, rev, &localfile, NULL);
505 if (client_opts.schema_searchpath && !localfile) {
506 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
507 rev ? rev : "") == -1) {
508 ERRMEM;
509 } else {
510 f = fopen(localfile, "w");
511 if (!f) {
512 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of module retrieved via <get-schema> (%s).",
513 localfile, strerror(errno));
Michal Vaskof945da52018-02-15 08:45:13 +0100514 } else {
Michal Vaskoa1721172022-12-12 09:06:53 +0100515 fputs(model_data, f);
516 fclose(f);
Michal Vaskof945da52018-02-15 08:45:13 +0100517 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200518 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200519 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100520 free(localfile);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200521
Michal Vasko77367452021-02-16 16:32:18 +0100522cleanup:
523 lyd_free_tree(envp);
524 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100525 return model_data;
526}
527
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200528static void
529free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200530{
531 free(data);
532 (void)user_data;
533}
534
Michal Vaskoc088f982021-10-05 12:23:07 +0200535/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200536 * @brief Retrieve YANG module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200537 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200538 * @param[in] mod_name Module name.
539 * @param[in] mod_rev Module revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100540 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200541 * @param[out] format Module format.
542 * @param[out] module_data Module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100543 * @param[out] free_module_data Callback for freeing @p module_data.
544 * @return LY_ERR value.
545 */
546static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200547retrieve_module_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format,
Michal Vasko5e32c402022-01-12 14:05:09 +0100548 const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
549{
550 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
551 char *model_data = NULL;
552
Michal Vasko5e32c402022-01-12 14:05:09 +0100553 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200554 model_data = retrieve_module_data_localfile(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100555
556 /* 2. try to use <get-schema> */
557 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200558 model_data = retrieve_module_data_getschema(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100559 }
560
561 /* 3. try to use user callback */
562 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200563 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 +0100564 clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data,
565 free_module_data);
566 }
567
568 *free_module_data = free_with_user_data;
569 *module_data = model_data;
570 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
571}
572
573/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200574 * @brief Retrieve YANG import module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100575 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200576 * @param[in] mod_name Module name.
577 * @param[in] mod_rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200578 * @param[in] submod_name Optional submodule name.
579 * @param[in] sub_rev Submodule revision.
580 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200581 * @param[out] format Module format.
582 * @param[out] module_data Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200583 * @param[out] free_module_data Callback for freeing @p module_data.
584 * @return LY_ERR value.
585 */
Michal Vasko77367452021-02-16 16:32:18 +0100586static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200587retrieve_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 +0100588 void *user_data, LYS_INFORMAT *format, const char **module_data,
589 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200590{
591 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
Michal Vasko5e32c402022-01-12 14:05:09 +0100592 uint32_t u, v, match = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200593 const char *name = NULL, *rev = NULL;
594 char *model_data = NULL;
595
Michal Vasko5ca5d972022-09-14 13:51:31 +0200596 /* get and check the final name and revision of the module to be retrieved */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200597 if (!mod_rev || !mod_rev[0]) {
598 /* newest revision requested - get the newest revision from the list of available modules on server */
599 match = 0;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200600 for (u = 0; clb_data->modules[u].name; ++u) {
601 if (strcmp(mod_name, clb_data->modules[u].name)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200602 continue;
603 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200604 if (!match || (strcmp(mod_rev, clb_data->modules[u].revision) > 0)) {
605 mod_rev = clb_data->modules[u].revision;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200606 }
607 match = u + 1;
608 }
609 if (!match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200610 /* valid situation if we are retrieving YANG 1.1 module and have only capabilities for now
Michal Vasko1d2665c2021-02-12 09:28:44 +0100611 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200612 VRB(clb_data->session, "Unable to identify revision of the import module \"%s\" from "
Michal Vasko05532772021-06-03 12:12:38 +0200613 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200614 }
615 }
616 if (submod_name) {
617 name = submod_name;
618 if (sub_rev) {
619 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200620 } else if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200621 if (!clb_data->modules[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200622 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
Michal Vasko5ca5d972022-09-14 13:51:31 +0200623 "in import module \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200624 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200625 for (v = 0; clb_data->modules[match - 1].submodules[v].name; ++v) {
626 if (!strcmp(submod_name, clb_data->modules[match - 1].submodules[v].name)) {
627 rev = sub_rev = clb_data->modules[match - 1].submodules[v].revision;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200628 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200629 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200630 if (!rev) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200631 ERR(clb_data->session, "Requested submodule \"%s\" is not found in import module \"%s\" on server side.",
Michal Vasko05532772021-06-03 12:12:38 +0200632 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100633 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200634 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200635 }
636 }
637 } else {
638 name = mod_name;
639 rev = mod_rev;
640 }
641
Radek Krejci65ef6d52018-08-16 16:35:02 +0200642 if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200643 /* we have enough information to avoid communication with server and try to get the module locally */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200644
645 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200646 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200647
648 /* 2. try to use <get-schema> */
649 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200650 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200651 }
652 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200653 /* we are unsure which revision of the module we should load, so first try to get
Radek Krejci65ef6d52018-08-16 16:35:02 +0200654 * the newest revision from the server via get-schema and only if the server does not
655 * implement get-schema, try to load the newest revision locally. This is imperfect
656 * solution, but there are situation when a client does not know what revision is
657 * actually implemented by the server. */
658
659 /* 1. try to use <get-schema> */
660 if (clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200661 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200662 }
663
664 /* 2. try to get data locally */
665 if (!model_data) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200666 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200667 }
668 }
669
670 /* 3. try to use user callback */
671 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200672 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", name, rev ? rev : "<latest>");
Michal Vasko77367452021-02-16 16:32:18 +0100673 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
674 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200675 }
676
Jan Kundrát35972df2018-09-06 19:00:01 +0200677 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100678 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200679 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200680}
681
Michal Vaskoc088f982021-10-05 12:23:07 +0200682/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200683 * @brief Load a YANG module into context.
Michal Vaskoc088f982021-10-05 12:23:07 +0200684 *
685 * @param[in] session NC session.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200686 * @param[in] name Module name.
687 * @param[in] revision Module revision.
688 * @param[in] features Enabled module features.
689 * @param[in] modules Server module info built from capabilities.
690 * @param[in] user_clb User callback for retrieving module data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200691 * @param[in] user_data User data for @p user_clb.
692 * @param[in] has_get_schema Whether the server supports get-schema.
693 * @param[out] mod Loaded module.
694 * @return 0 on success.
695 * @return -1 on error.
696 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100697static int
Michal Vasko5e32c402022-01-12 14:05:09 +0100698nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features,
Michal Vasko5ca5d972022-09-14 13:51:31 +0200699 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 +0100700{
701 int ret = 0;
Michal Vasko58791da2024-02-26 13:52:59 +0100702 const struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200703 const char *module_data = NULL;
Michal Vasko5e32c402022-01-12 14:05:09 +0100704 struct ly_in *in;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200705 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200706
707 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200708 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100709
Michal Vasko5e32c402022-01-12 14:05:09 +0100710 /* try to use a module from the context */
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200711 *mod = ly_ctx_get_module_implemented(session->ctx, name);
712 if (!*mod) {
713 if (revision) {
714 *mod = ly_ctx_get_module(session->ctx, name, revision);
715 } else {
Michal Vasko5e32c402022-01-12 14:05:09 +0100716 *mod = ly_ctx_get_module_latest(session->ctx, name);
717 }
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200718 } else if (revision && (!(*mod)->revision || strcmp((*mod)->revision, revision))) {
719 WRN(session, "Server implements module \"%s\" in revision \"%s\" but revision \"%s\" is already implemented"
720 " and will be used instead.", name, revision, (*mod)->revision ? (*mod)->revision : "<none>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200721 }
Michal Vasko5e32c402022-01-12 14:05:09 +0100722
Michal Vaskoceae0152018-02-14 16:03:59 +0100723 if (*mod) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100724 /* make the present module implemented and/or enable all its features */
725 if (lys_set_implemented(*mod, features)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200726 ERR(session, "Failed to implement module \"%s\".", (*mod)->name);
Michal Vasko5e32c402022-01-12 14:05:09 +0100727 ret = -1;
Michal Vaskoceae0152018-02-14 16:03:59 +0100728 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200729 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100730 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200731 clb_data.has_get_schema = has_get_schema;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200732 clb_data.modules = modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200733 clb_data.session = session;
734 clb_data.user_clb = user_clb;
735 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100736
737 /* clear all the errors and just collect them for now */
738 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200739 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100740
Radek Krejci65ef6d52018-08-16 16:35:02 +0200741 /* get module data */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200742 retrieve_module_data(name, revision, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100743
Radek Krejci65ef6d52018-08-16 16:35:02 +0200744 if (module_data) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100745 /* set import callback */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200746 ly_ctx_set_module_imp_clb(session->ctx, retrieve_module_data_imp, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100747
Michal Vasko5ca5d972022-09-14 13:51:31 +0200748 /* parse the module */
Michal Vasko5e32c402022-01-12 14:05:09 +0100749 ly_in_new_memory(module_data, &in);
750 lys_parse(session->ctx, in, format, features, mod);
751 ly_in_free(in, 0);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200752 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100753 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100754 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100755
Radek Krejci65ef6d52018-08-16 16:35:02 +0200756 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
757 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100758
Michal Vaskodbf7c272018-02-19 09:07:59 +0100759 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200760 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100761 if (!(*mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100762 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100763 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100764 }
765 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200766 } else {
767 /* print only warnings */
Michal Vasko5e32c402022-01-12 14:05:09 +0100768 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200769 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100770 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200771 }
772 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100773 }
774
Michal Vaskodbf7c272018-02-19 09:07:59 +0100775 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100776 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100777 }
778
779 return ret;
780}
781
Radek Krejci65ef6d52018-08-16 16:35:02 +0200782static void
Michal Vasko5ca5d972022-09-14 13:51:31 +0200783free_module_info(struct module_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200784{
Michal Vasko77367452021-02-16 16:32:18 +0100785 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200786
Radek Krejci65ef6d52018-08-16 16:35:02 +0200787 if (!list) {
788 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200789 }
790
Radek Krejci65ef6d52018-08-16 16:35:02 +0200791 for (u = 0; list[u].name; ++u) {
792 free(list[u].name);
793 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100794 if (list[u].features) {
795 for (v = 0; list[u].features[v]; ++v) {
796 free(list[u].features[v]);
797 }
798 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200799 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200800 if (list[u].submodules) {
801 for (v = 0; list[u].submodules[v].name; ++v) {
802 free(list[u].submodules[v].name);
803 free(list[u].submodules[v].revision);
804 }
805 free(list[u].submodules);
806 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200807 }
808 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200809}
810
Michal Vaskoc088f982021-10-05 12:23:07 +0200811/**
Michal Vasko78939072022-12-12 07:43:18 +0100812 * @brief Retrieve yang-library and schema-mounts operational data from the server.
Michal Vaskoc088f982021-10-05 12:23:07 +0200813 *
814 * @param[in] session NC session.
815 * @param[in] has_get_data Whether get-data RPC is available or only get.
Michal Vasko78939072022-12-12 07:43:18 +0100816 * @param[in] filter Filter to use.
817 * @param[out] oper_data Received data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200818 * @return 0 on success.
819 * @return -1 on error.
820 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200821static int
Michal Vasko78939072022-12-12 07:43:18 +0100822get_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 +0200823{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200824 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100825 struct lyd_node *op = NULL, *envp = NULL;
826 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200827 NC_MSG_TYPE msg;
828 uint64_t msgid;
Michal Vasko303263d2021-10-05 12:18:21 +0200829 int ret = 0;
830 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200831
Michal Vasko78939072022-12-12 07:43:18 +0100832 /* get data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200833 if (has_get_data) {
Michal Vasko78939072022-12-12 07:43:18 +0100834 rpc_name = "<get-data>";
835 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 +0200836 } else {
Michal Vasko78939072022-12-12 07:43:18 +0100837 rpc_name = "<get>";
838 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200839 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200840 if (!rpc) {
841 goto cleanup;
842 }
843
844 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
845 usleep(1000);
846 }
847 if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100848 WRN(session, "Failed to send %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200849 goto cleanup;
850 }
851
852 do {
Michal Vasko77367452021-02-16 16:32:18 +0100853 lyd_free_tree(envp);
854 lyd_free_tree(op);
855
856 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200857 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200858 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko78939072022-12-12 07:43:18 +0100859 WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200860 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100861 } else if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100862 WRN(session, "Failed to receive a reply to %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200863 goto cleanup;
Jan Kundrát1d73a5a2022-03-30 16:54:49 +0200864 } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko78939072022-12-12 07:43:18 +0100865 WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200866 goto cleanup;
867 }
868
Michal Vasko77367452021-02-16 16:32:18 +0100869 data = (struct lyd_node_any *)lyd_child(op);
870 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko78939072022-12-12 07:43:18 +0100871 WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200872 goto cleanup;
873 } else if (!data->value.tree) {
Michal Vasko78939072022-12-12 07:43:18 +0100874 WRN(session, "No data in reply to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200875 goto cleanup;
876 }
877
Michal Vasko78939072022-12-12 07:43:18 +0100878 *oper_data = data->value.tree;
879 data->value.tree = NULL;
880
881cleanup:
882 nc_rpc_free(rpc);
883 lyd_free_tree(envp);
884 lyd_free_tree(op);
885
886 if (session->status != NC_STATUS_RUNNING) {
887 /* something bad happened, discard the session */
888 ERR(session, "Invalid session, discarding.");
889 ret = -1;
890 }
891
892 return ret;
893}
894
895/**
896 * @brief Build server module info from ietf-yang-library data.
897 *
898 * @param[in] session NC session.
899 * @param[in] get_data_sup Whether get-data RPC is available or only get.
900 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
901 * @param[out] result Server modules.
902 * @return 0 on success.
903 * @return -1 on error.
904 */
905static int
906build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result)
907{
908 struct ly_set *modules = NULL;
909 uint32_t u, v, submodules_count, feature_count;
910 struct lyd_node *iter, *child, *oper_data = NULL;
911 struct lys_module *mod;
912 int ret = 0;
913
914 /* get yang-library operational data */
915 if (xpath_sup) {
916 if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) {
917 goto cleanup;
918 }
919 } else {
920 if (get_oper_data(session, get_data_sup,
921 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", &oper_data)) {
922 goto cleanup;
923 }
924 }
925 if (!oper_data) {
926 goto cleanup;
927 }
928
929 if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) {
930 WRN(NULL, "No yang-library module information found.");
Radek Krejci2d088832018-08-21 13:40:14 +0200931 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200932 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200933
Michal Vasko77367452021-02-16 16:32:18 +0100934 (*result) = calloc(modules->count + 1, sizeof **result);
roman3a95bb22023-10-26 11:07:17 +0200935 NC_CHECK_ERRMEM_GOTO(!(*result), ret = -1, cleanup);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200936
Michal Vasko77367452021-02-16 16:32:18 +0100937 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200938 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100939 feature_count = 0;
940 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
941 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200942 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200943 /* ignore node from other schemas (augments) */
944 continue;
945 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200946 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200947 /* ignore empty nodes */
948 continue;
949 }
950 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200951 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200952 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200953 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200954 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200955 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200956 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100957 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
roman3a95bb22023-10-26 11:07:17 +0200958 NC_CHECK_ERRMEM_GOTO(!(*result)[u].features, free_module_info(*result); *result = NULL; ret = -1, cleanup);
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200959 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100960 (*result)[u].features[feature_count + 1] = NULL;
961 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200962 } else if (!strcmp(iter->schema->name, "submodule")) {
963 submodules_count++;
964 }
965 }
966
967 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200968 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
roman3a95bb22023-10-26 11:07:17 +0200969 NC_CHECK_ERRMEM_GOTO(!(*result)[u].submodules, free_module_info(*result); *result = NULL; ret = -1, cleanup);
970 v = 0;
971 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
972 mod = modules->dnodes[u]->schema->module;
973 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
974 LY_LIST_FOR(lyd_child(iter), child) {
975 if (mod != child->schema->module) {
976 continue;
977 } else if (!strcmp(child->schema->name, "name")) {
978 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
979 } else if (!strcmp(child->schema->name, "revision")) {
980 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200981 }
982 }
983 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200984 }
985 }
986 }
987
Radek Krejcifd5b6682017-06-13 15:52:53 +0200988cleanup:
Michal Vasko78939072022-12-12 07:43:18 +0100989 lyd_free_siblings(oper_data);
Michal Vasko77367452021-02-16 16:32:18 +0100990 ly_set_free(modules, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200991 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200992}
993
Michal Vaskoc088f982021-10-05 12:23:07 +0200994/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200995 * @brief Build server module info from received capabilities.
Michal Vaskoc088f982021-10-05 12:23:07 +0200996 *
997 * @param[in] cpblts Server capabilities.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200998 * @param[out] result Server modules.
Michal Vaskoc088f982021-10-05 12:23:07 +0200999 * @return 0 on success.
1000 * @return -1 on error.
1001 */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001002static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001003build_module_info_cpblts(char **cpblts, struct module_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001004{
Michal Vasko77367452021-02-16 16:32:18 +01001005 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001006 char *module_cpblt, *ptr, *ptr2;
1007
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001008 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +02001009 (*result) = calloc(u + 1, sizeof **result);
roman3a95bb22023-10-26 11:07:17 +02001010 NC_CHECK_ERRMEM_RET(!(*result), -1);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001011
1012 for (u = v = 0; cpblts[u]; ++u) {
1013 module_cpblt = strstr(cpblts[u], "module=");
1014 /* this capability requires a module */
1015 if (!module_cpblt) {
1016 continue;
1017 }
1018
1019 /* get module's name */
1020 ptr = (char *)module_cpblt + 7;
1021 ptr2 = strchr(ptr, '&');
1022 if (!ptr2) {
1023 ptr2 = ptr + strlen(ptr);
1024 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001025 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001026
1027 /* get module's revision */
1028 ptr = strstr(module_cpblt, "revision=");
1029 if (ptr) {
1030 ptr += 9;
1031 ptr2 = strchr(ptr, '&');
1032 if (!ptr2) {
1033 ptr2 = ptr + strlen(ptr);
1034 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001035 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001036 }
1037
1038 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001039 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001040
1041 /* get module's features */
1042 ptr = strstr(module_cpblt, "features=");
1043 if (ptr) {
1044 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +01001045 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001046 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
1047 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +01001048 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1049 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1050 (*result)[v].features[feature_count + 1] = NULL;
1051 ++feature_count;
1052
Radek Krejci65ef6d52018-08-16 16:35:02 +02001053 ptr2 = ptr + 1;
1054 }
1055 }
1056 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +01001057 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1058 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1059 (*result)[v].features[feature_count + 1] = NULL;
1060 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001061 }
1062 ++v;
1063 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001064
Michal Vasko303263d2021-10-05 12:18:21 +02001065 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001066}
1067
Michal Vaskoc088f982021-10-05 12:23:07 +02001068/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001069 * @brief Fill client context based on server modules info.
Michal Vaskoc088f982021-10-05 12:23:07 +02001070 *
1071 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001072 * @param[in] modules Server modules info.
1073 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001074 * @param[in] user_data User data for @p user_clb.
1075 * @param[in] has_get_schema Whether server supports get-schema RPC.
1076 * @return 0 on success.
1077 * @return -1 on error.
1078 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001079static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001080nc_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 +01001081 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001082{
Michal Vasko303263d2021-10-05 12:18:21 +02001083 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +01001084 struct lys_module *mod;
1085 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001086
1087 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +02001088 /* skip import-only modules */
1089 if (!modules[u].implemented) {
1090 continue;
1091 }
1092
Radek Krejci65ef6d52018-08-16 16:35:02 +02001093 /* we can continue even if it fails */
Michal Vasko5e32c402022-01-12 14:05:09 +01001094 nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules,
1095 user_clb, user_data, has_get_schema, &mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001096
1097 if (!mod) {
1098 if (session->status != NC_STATUS_RUNNING) {
1099 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +02001100 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001101 goto cleanup;
1102 }
1103
Michal Vasko5ca5d972022-09-14 13:51:31 +02001104 /* all loading ways failed, the module will be ignored in the received data */
1105 WRN(session, "Failed to load module \"%s@%s\".", modules[u].name, modules[u].revision ?
Michal Vasko05532772021-06-03 12:12:38 +02001106 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001107 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001108 }
Michal Vasko60f66602017-10-17 13:52:18 +02001109 }
1110
Michal Vasko77367452021-02-16 16:32:18 +01001111 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001112 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001113
1114cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001115 return ret;
1116}
1117
Michal Vaskoc088f982021-10-05 12:23:07 +02001118/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001119 * @brief Fill client context with ietf-netconf module.
Michal Vaskoc088f982021-10-05 12:23:07 +02001120 *
1121 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001122 * @param[in] modules Server module info.
1123 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001124 * @param[in] user_data User data for @p user_clb.
1125 * @param[in] has_get_schema Whether server supports get-schema RPC.
1126 * @return 0 on success.
1127 * @return -1 on error.
1128 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001129static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001130nc_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 +01001131 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001132{
Michal Vasko77367452021-02-16 16:32:18 +01001133 uint32_t u;
Michal Vasko5e32c402022-01-12 14:05:09 +01001134 const char **features = NULL;
1135 struct ly_in *in;
Michal Vasko77367452021-02-16 16:32:18 +01001136 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001137
Michal Vasko5e32c402022-01-12 14:05:09 +01001138 /* find supported features (capabilities) in ietf-netconf */
1139 for (u = 0; modules[u].name; ++u) {
1140 if (!strcmp(modules[u].name, "ietf-netconf")) {
1141 assert(modules[u].implemented);
1142 features = (const char **)modules[u].features;
1143 break;
1144 }
1145 }
1146 if (!modules[u].name) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001147 ERR(session, "Base NETCONF module not supported by the server.");
Michal Vasko5e32c402022-01-12 14:05:09 +01001148 return -1;
1149 }
1150
Michal Vasko77367452021-02-16 16:32:18 +01001151 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vasko5e32c402022-01-12 14:05:09 +01001152 if (ietfnc) {
1153 /* make sure to enable all the features if already loaded */
1154 lys_set_implemented(ietfnc, features);
1155 } else {
1156 /* load the module */
1157 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 +02001158 if (!ietfnc) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001159 ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in);
1160 lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc);
1161 ly_in_free(in, 0);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001162 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001163 }
1164 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001165 ERR(session, "Loading base NETCONF module failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001166 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001167 }
1168
Radek Krejci65ef6d52018-08-16 16:35:02 +02001169 return 0;
1170}
1171
Michal Vasko78939072022-12-12 07:43:18 +01001172/**
1173 * @brief Set client session context to support schema-mount if possible.
1174 *
1175 * @param[in] session NC session with the context to modify.
1176 * @param[in] get_data_sup Whether get-data RPC is available or only get.
1177 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
1178 * @return 0 on success.
1179 * @return -1 on error.
1180 */
1181static int
1182nc_ctx_schema_mount(struct nc_session *session, int get_data_sup, int xpath_sup)
1183{
1184 int rc = 0;
1185 struct lyd_node *oper_data = NULL;
1186
1187 if (session->flags & NC_SESSION_SHAREDCTX) {
1188 /* context is already fully set up */
1189 goto cleanup;
1190 }
1191
1192 /* get yang-library and schema-mounts operational data */
1193 if (xpath_sup) {
1194 if ((rc = get_oper_data(session, get_data_sup, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) {
1195 goto cleanup;
1196 }
1197 } else {
1198 if ((rc = get_oper_data(session, get_data_sup,
1199 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>"
1200 "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"/>", &oper_data))) {
1201 goto cleanup;
1202 }
1203 }
1204
1205 if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) {
1206 /* no schema-mounts operational data */
1207 goto cleanup;
1208 }
1209
1210 /* validate the data for the parent reference prefixes to be resolved */
1211 if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) {
Michal Vasko58791da2024-02-26 13:52:59 +01001212 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 +01001213 rc = -1;
1214 goto cleanup;
1215 }
1216
1217 /* store the data in the session */
1218 session->opts.client.ext_data = oper_data;
1219 oper_data = NULL;
1220
1221cleanup:
1222 lyd_free_siblings(oper_data);
1223 return rc;
1224}
1225
Michal Vasko086311b2016-01-08 09:53:11 +01001226int
1227nc_ctx_check_and_fill(struct nc_session *session)
1228{
Michal Vaskod6c6f852022-12-14 15:37:21 +01001229 int i, get_schema_support = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001230 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001231 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001232 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001233 char *revision;
Michal Vasko5ca5d972022-09-14 13:51:31 +02001234 struct module_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001235
Michal Vasko2e6defd2016-10-07 15:48:15 +02001236 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001237
Michal Vaskoa272e092023-07-10 14:34:03 +02001238 if (client_opts.auto_context_fill_disabled) {
1239 VRB(session, "Context of the new session is left only with the default YANG modules.");
1240 return 0;
1241 }
1242
Radek Krejci65ef6d52018-08-16 16:35:02 +02001243 /* 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 +02001244 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001245
Radek Krejci65ef6d52018-08-16 16:35:02 +02001246 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
Michal Vasko5ca5d972022-09-14 13:51:31 +02001247 * modules only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001248 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001249
1250 /* our callback is set later with appropriate data */
1251 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1252
1253 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001254 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001255 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001256 get_schema_support = 1 + i;
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001257 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001258 yanglib_support = 1 + i;
Michal Vasko78939072022-12-12 07:43:18 +01001259 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) {
1260 xpath_support = 1 + i;
Michal Vaskod6c6f852022-12-14 15:37:21 +01001261 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda", 45)) {
1262 nmda_support = 1 + i;
Michal Vasko086311b2016-01-08 09:53:11 +01001263 }
1264 }
Michal Vaskod6c6f852022-12-14 15:37:21 +01001265 VRB(session, "Capability for <get-schema> support%s found.", get_schema_support ? "" : " not");
1266 VRB(session, "Capability for yang-library support%s found.", yanglib_support ? "" : " not");
1267 VRB(session, "Capability for XPath filter support%s found.", xpath_support ? "" : " not");
1268 VRB(session, "Capability for NMDA RPCs support%s found.", nmda_support ? "" : " not");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001269
Michal Vasko5ca5d972022-09-14 13:51:31 +02001270 /* get information about server's modules from capabilities list until we will have yang-library */
1271 if (build_module_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1272 ERR(session, "Unable to get server module information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001273 goto cleanup;
1274 }
1275
Michal Vasko086311b2016-01-08 09:53:11 +01001276 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001277 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 +02001278 WRN(session, "Loading NETCONF monitoring module failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001279 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001280 }
1281
1282 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001283 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001284 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001285 }
1286
Radek Krejci65ef6d52018-08-16 16:35:02 +02001287 /* get correct version of ietf-yang-library into context */
1288 if (yanglib_support) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001289 /* use get-schema to get server's ietf-yang-library */
Radek Krejcib1d250e2018-04-12 13:54:18 +02001290 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1291 if (!revision) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001292 WRN(session, "Loading NETCONF ietf-yang-library module failed, missing revision in NETCONF <hello> message.");
Michal Vasko05532772021-06-03 12:12:38 +02001293 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001294 yanglib_support = 0;
1295 } else {
1296 revision = strndup(&revision[9], 10);
Michal Vasko5e32c402022-01-12 14:05:09 +01001297 if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001298 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001299 WRN(session, "Loading NETCONF ietf-yang-library module failed, unable to use it to learn all "
Michal Vasko05532772021-06-03 12:12:38 +02001300 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001301 yanglib_support = 0;
1302 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001303 if (strcmp(revision, "2019-01-04") >= 0) {
1304 /* we also need ietf-datastores to be implemented */
Michal Vasko5e32c402022-01-12 14:05:09 +01001305 if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001306 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001307 WRN(session, "Loading NETCONF ietf-datastores module failed, unable to use yang-library "
Michal Vasko05532772021-06-03 12:12:38 +02001308 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001309 yanglib_support = 0;
1310 }
1311 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001312 free(revision);
1313 }
1314 }
1315
Michal Vaskod6c6f852022-12-14 15:37:21 +01001316 /* ietf-netconf-nmda is needed to issue get-data */
1317 if (nmda_support && nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb, old_data,
1318 get_schema_support, &mod)) {
1319 WRN(session, "Loading NMDA module failed, unable to use <get-data>.");
1320 nmda_support = 0;
1321 }
1322
Michal Vasko5ca5d972022-09-14 13:51:31 +02001323 /* prepare structured information about server's modules */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001324 if (yanglib_support) {
Michal Vaskod6c6f852022-12-14 15:37:21 +01001325 if (build_module_info_yl(session, nmda_support, xpath_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001326 goto cleanup;
1327 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001328 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001329 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001330 /* prefer yang-library information, currently we have it from capabilities used for getting correct
Michal Vasko5ca5d972022-09-14 13:51:31 +02001331 * yang-library module */
1332 free_module_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001333 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001334 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001335 }
Michal Vaskoef578332016-01-25 13:20:09 +01001336
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001337 /* compile all modules at once to avoid invalid errors or warnings */
1338 ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
1339
1340 /* fill the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001341 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1342 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001343 }
1344
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001345 /* compile it */
1346 if (ly_ctx_compile(session->ctx)) {
1347 goto cleanup;
1348 }
1349
Michal Vasko59762c32024-04-12 12:13:12 +02001350 /* set support for schema-mount, if possible (requires ietf-yang-library support) */
1351 if (yanglib_support && nc_ctx_schema_mount(session, nmda_support, xpath_support)) {
Michal Vasko78939072022-12-12 07:43:18 +01001352 goto cleanup;
1353 }
1354
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001355 /* success */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001356 ret = 0;
1357
Michal Vaskoeee99412016-11-21 10:19:43 +01001358 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001359 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 +02001360 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001361 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001362
1363cleanup:
Michal Vasko5ca5d972022-09-14 13:51:31 +02001364 free_module_info(server_modules);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001365
Radek Krejcifd5b6682017-06-13 15:52:53 +02001366 /* set user callback back */
1367 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001368 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001369 ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001370
Michal Vaskoef578332016-01-25 13:20:09 +01001371 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001372}
1373
1374API struct nc_session *
1375nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1376{
Michal Vaskod083db62016-01-19 10:31:29 +01001377 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001378
Michal Vasko45e53ae2016-04-07 11:46:03 +02001379 if (fdin < 0) {
roman40672412023-05-04 11:10:22 +02001380 ERRARG(NULL, "fdin");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001381 return NULL;
1382 } else if (fdout < 0) {
roman40672412023-05-04 11:10:22 +02001383 ERRARG(NULL, "fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001384 return NULL;
1385 }
1386
1387 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001388 session = nc_new_session(NC_CLIENT, 0);
roman3a95bb22023-10-26 11:07:17 +02001389 NC_CHECK_ERRMEM_RET(!session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001390 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001391
1392 /* transport specific data */
1393 session->ti_type = NC_TI_FD;
1394 session->ti.fd.in = fdin;
1395 session->ti.fd.out = fdout;
1396
Michal Vasko78939072022-12-12 07:43:18 +01001397 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001398 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001399 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001400 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001401
1402 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001403 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001404 goto fail;
1405 }
1406 session->status = NC_STATUS_RUNNING;
1407
Michal Vaskoef578332016-01-25 13:20:09 +01001408 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001409 goto fail;
1410 }
1411
1412 return session;
1413
1414fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001415 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001416 return NULL;
1417}
1418
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001419API struct nc_session *
1420nc_connect_unix(const char *address, struct ly_ctx *ctx)
1421{
1422 struct nc_session *session = NULL;
1423 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001424 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001425 char *username;
1426 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001427 char *buf = NULL;
1428 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001429
roman40672412023-05-04 11:10:22 +02001430 NC_CHECK_ARG_RET(NULL, address, NULL);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001431
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001432 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1433 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001434 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001435 goto fail;
1436 }
1437
1438 memset(&sun, 0, sizeof(sun));
1439 sun.sun_family = AF_UNIX;
1440 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1441
1442 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001443 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001444 goto fail;
1445 }
1446
1447 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001448 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001449 goto fail;
1450 }
1451
1452 /* prepare session structure */
1453 session = nc_new_session(NC_CLIENT, 0);
roman124a4362023-10-26 15:36:22 +02001454 NC_CHECK_ERRMEM_GOTO(!session, , fail);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001455 session->status = NC_STATUS_STARTING;
1456
1457 /* transport specific data */
1458 session->ti_type = NC_TI_UNIX;
1459 session->ti.unixsock.sock = sock;
1460 sock = -1; /* do not close sock in fail label anymore */
1461
Michal Vasko78939072022-12-12 07:43:18 +01001462 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001463 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001464 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001465 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001466
Michal Vasko93224072021-11-09 12:14:28 +01001467 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001468
romanf6e32012023-04-24 15:51:26 +02001469 pw = nc_getpw(geteuid(), NULL, &pw_buf, &buf, &buf_size);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001470 if (!pw) {
1471 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1472 goto fail;
1473 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001474 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001475 free(buf);
roman124a4362023-10-26 15:36:22 +02001476 NC_CHECK_ERRMEM_GOTO(!username, , fail);
Michal Vasko93224072021-11-09 12:14:28 +01001477 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001478
1479 /* NETCONF handshake */
1480 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1481 goto fail;
1482 }
1483 session->status = NC_STATUS_RUNNING;
1484
1485 if (nc_ctx_check_and_fill(session) == -1) {
1486 goto fail;
1487 }
1488
1489 return session;
1490
1491fail:
1492 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001493 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001494 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001495 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001496 return NULL;
1497}
1498
Michal Vasko056f53c2022-10-21 13:38:15 +02001499/**
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001500 * @brief Convert socket IP address to string.
1501 *
1502 * @param[in] saddr Sockaddr to convert.
1503 * @param[out] str_ip String IP address.
Michal Vaskoc07d9762023-03-27 10:32:18 +02001504 * @param[out] port Optional port.
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001505 * @return 0 on success.
1506 * @return -1 on error.
1507 */
1508static int
Michal Vaskoc07d9762023-03-27 10:32:18 +02001509nc_saddr2str(const struct sockaddr *saddr, char **str_ip, uint16_t *port)
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001510{
1511 void *addr;
1512 socklen_t str_len;
1513
1514 assert((saddr->sa_family == AF_INET) || (saddr->sa_family == AF_INET6));
1515
1516 str_len = (saddr->sa_family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
1517 *str_ip = malloc(str_len);
roman3a95bb22023-10-26 11:07:17 +02001518 NC_CHECK_ERRMEM_RET(!(*str_ip), -1);
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001519
1520 if (saddr->sa_family == AF_INET) {
Michal Vaskoc0f85ca2023-03-27 10:19:00 +02001521 addr = &((struct sockaddr_in *)saddr)->sin_addr;
Michal Vaskoc07d9762023-03-27 10:32:18 +02001522 if (port) {
1523 *port = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1524 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001525 } else {
Michal Vaskoc0f85ca2023-03-27 10:19:00 +02001526 addr = &((struct sockaddr_in6 *)saddr)->sin6_addr;
Michal Vaskoc07d9762023-03-27 10:32:18 +02001527 if (port) {
1528 *port = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1529 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001530 }
1531 if (!inet_ntop(saddr->sa_family, addr, *str_ip, str_len)) {
1532 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
1533 free(*str_ip);
1534 return -1;
1535 }
1536
1537 return 0;
1538}
1539
1540/**
Michal Vasko056f53c2022-10-21 13:38:15 +02001541 * @brief Try to connect a socket, optionally a pending one from a previous attempt.
1542 *
1543 * @param[in] timeout_ms Timeout in ms to wait for the connection to be fully established, -1 to block.
1544 * @param[in,out] sock_pending Optional previously created socked that was not fully connected yet. If provided and
1545 * connected, is set to -1.
1546 * @param[in] res Addrinfo resource to use when creating a new socket.
1547 * @param[in] ka Keepalives to set.
1548 * @return Connected socket or -1 on error.
Frank Rimpler9f838b02018-07-25 06:44:03 +00001549 */
1550static int
Michal Vaskoc4550382024-05-03 12:02:38 +02001551sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, const struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001552{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001553 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001554 int sock = -1;
Michal Vaskob9336672023-10-11 09:27:30 +02001555 struct pollfd fds = {0};
Frank Rimpler9f838b02018-07-25 06:44:03 +00001556 socklen_t len = sizeof(int);
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001557 uint16_t port;
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001558 char *str;
Michal Vasko086311b2016-01-08 09:53:11 +01001559
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001560 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001561 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001562 sock = *sock_pending;
1563 } else {
1564 assert(res);
Michal Vaskoc07d9762023-03-27 10:32:18 +02001565 if (nc_saddr2str(res->ai_addr, &str, &port)) {
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001566 return -1;
1567 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001568 VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
1569 free(str);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001570
1571 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001572 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1573 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001574 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001575 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001576 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001577 /* make the socket non-blocking */
1578 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001579 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001580 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001581 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001582 /* non-blocking connect! */
1583 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1584 if (errno != EINPROGRESS) {
1585 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001586 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001587 goto cleanup;
1588 }
1589 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001590 }
Michal Vasko7d965dc2018-03-12 14:39:23 +01001591
Michal Vaskob9336672023-10-11 09:27:30 +02001592 fds.fd = sock;
1593 fds.events = POLLOUT;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001594
Michal Vaskob9336672023-10-11 09:27:30 +02001595 /* wait until we can write data to the socket */
1596 ret = poll(&fds, 1, timeout_ms);
1597 if (ret == -1) {
1598 /* error */
1599 ERR(NULL, "poll() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001600 goto cleanup;
Michal Vaskob9336672023-10-11 09:27:30 +02001601 } else if (ret == 0) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001602 /* there was a timeout */
Michal Vasko056f53c2022-10-21 13:38:15 +02001603 VRB(NULL, "Timed out after %d ms (%s).", timeout_ms, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001604 if (sock_pending) {
1605 /* no sock-close, we'll try it again */
1606 *sock_pending = sock;
1607 } else {
1608 close(sock);
1609 }
1610 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001611 }
Radek Krejci782041a2018-08-20 10:09:45 +02001612
1613 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001614 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001615 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001616 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001617 goto cleanup;
1618 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001619 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001620 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001621 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001622 errno = error;
1623 goto cleanup;
1624 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001625
roman56acd552024-04-18 16:00:37 +02001626 /* configure keepalives */
Michal Vaskoc4cdc9e2024-05-03 12:03:07 +02001627 if (nc_sock_configure_ka(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001628 goto cleanup;
1629 }
1630
Michal Vasko056f53c2022-10-21 13:38:15 +02001631 /* connected */
1632 if (sock_pending) {
1633 *sock_pending = -1;
1634 }
Michal Vasko086311b2016-01-08 09:53:11 +01001635 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001636
Frank Rimpler9f838b02018-07-25 06:44:03 +00001637cleanup:
1638 if (sock_pending) {
1639 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001640 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001641 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001642 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001643}
1644
Frank Rimpler9f838b02018-07-25 06:44:03 +00001645int
Michal Vasko056f53c2022-10-21 13:38:15 +02001646nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepalives *ka, int *sock_pending, char **ip_host)
Frank Rimpler9f838b02018-07-25 06:44:03 +00001647{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001648 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001649 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001650 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001651 char port_s[6]; /* length of string representation of short int */
1652 struct sockaddr_storage saddr;
1653 socklen_t addr_len = sizeof saddr;
1654
1655 *ip_host = NULL;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001656
Michal Vasko056f53c2022-10-21 13:38:15 +02001657 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout_ms, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001658
1659 /* no pending socket */
1660 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001661 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001662 snprintf(port_s, 6, "%u", port);
1663 memset(&hints, 0, sizeof hints);
1664 hints.ai_family = AF_UNSPEC;
1665 hints.ai_socktype = SOCK_STREAM;
1666 hints.ai_protocol = IPPROTO_TCP;
1667 i = getaddrinfo(host, port_s, &hints, &res_list);
1668 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001669 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001670 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001671 }
1672
1673 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vasko056f53c2022-10-21 13:38:15 +02001674 sock = sock_connect(timeout_ms, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001675 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001676 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001677 /* try the next resource */
1678 continue;
1679 } else {
1680 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001681 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001682 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001683 }
Michal Vasko05532772021-06-03 12:12:38 +02001684 VRB(NULL, "Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
Michal Vasko83ad17e2019-01-30 10:11:37 +01001685
1686 opt = 1;
1687 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001688 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001689 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001690 }
1691
Michal Vaskoc07d9762023-03-27 10:32:18 +02001692 if (nc_saddr2str(res->ai_addr, ip_host, NULL)) {
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001693 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001694 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001695 break;
1696 }
1697 freeaddrinfo(res_list);
1698
1699 } else {
1700 /* try to get a connection with the pending socket */
1701 assert(sock_pending);
Michal Vasko056f53c2022-10-21 13:38:15 +02001702 sock = sock_connect(timeout_ms, sock_pending, NULL, ka);
Tie.Liao9bca73d2023-03-23 17:25:00 +01001703
1704 if (sock > 0) {
Barbaros Tokaoglu96da5912023-06-19 18:57:05 +03001705 if (getpeername(sock, (struct sockaddr *)&saddr, &addr_len)) {
1706 ERR(NULL, "getpeername failed (%s).", strerror(errno));
Tie.Liao9bca73d2023-03-23 17:25:00 +01001707 goto error;
1708 }
1709
Michal Vaskoc07d9762023-03-27 10:32:18 +02001710 if (nc_saddr2str((struct sockaddr *)&saddr, ip_host, NULL)) {
Tie.Liao9bca73d2023-03-23 17:25:00 +01001711 goto error;
1712 }
Tie.Liao9bca73d2023-03-23 17:25:00 +01001713 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001714 }
1715
1716 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001717
1718error:
Michal Vasko0be85692021-03-02 08:04:57 +01001719 if (res_list) {
1720 freeaddrinfo(res_list);
1721 }
Michal Vaskocb846632019-12-13 15:12:45 +01001722 if (sock != -1) {
1723 close(sock);
1724 }
1725 if (sock_pending) {
1726 *sock_pending = -1;
1727 }
1728 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001729}
1730
roman2eab4742023-06-06 10:00:26 +02001731#ifdef NC_ENABLED_SSH_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001732
Michal Vasko3031aae2016-01-27 16:07:18 +01001733int
Michal Vasko9d4cca52022-09-07 11:19:57 +02001734nc_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 +01001735{
1736 int sock;
1737
roman40672412023-05-04 11:10:22 +02001738 NC_CHECK_ARG_RET(NULL, address, port, -1);
Michal Vasko3031aae2016-01-27 16:07:18 +01001739
Michal Vaskoc4cdc9e2024-05-03 12:03:07 +02001740 sock = nc_sock_listen_inet(address, port);
Michal Vasko3031aae2016-01-27 16:07:18 +01001741 if (sock == -1) {
1742 return -1;
1743 }
1744
1745 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001746 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1747 if (!client_opts.ch_binds) {
1748 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001749 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001750 return -1;
1751 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001752
Michal Vasko9d4cca52022-09-07 11:19:57 +02001753 client_opts.ch_binds_aux = nc_realloc(client_opts.ch_binds_aux, client_opts.ch_bind_count * sizeof *client_opts.ch_binds_aux);
1754 if (!client_opts.ch_binds_aux) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001755 ERRMEM;
1756 close(sock);
1757 return -1;
1758 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001759 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].ti = ti;
1760 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].hostname = hostname ? strdup(hostname) : NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001761
Michal Vasko3031aae2016-01-27 16:07:18 +01001762 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
1763 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1764 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001765 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001766
1767 return 0;
1768}
1769
1770int
1771nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1772{
1773 uint32_t i;
1774 int ret = -1;
1775
1776 if (!address && !port && !ti) {
1777 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1778 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001779 free(client_opts.ch_binds[i].address);
1780
1781 free(client_opts.ch_binds_aux[i].hostname);
Michal Vasko3031aae2016-01-27 16:07:18 +01001782
1783 ret = 0;
1784 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001785 client_opts.ch_bind_count = 0;
1786
Michal Vasko3031aae2016-01-27 16:07:18 +01001787 free(client_opts.ch_binds);
1788 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001789
1790 free(client_opts.ch_binds_aux);
1791 client_opts.ch_binds_aux = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001792 } else {
1793 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001794 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1795 (!port || (client_opts.ch_binds[i].port == port)) &&
Michal Vasko9d4cca52022-09-07 11:19:57 +02001796 (!ti || (client_opts.ch_binds_aux[i].ti == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001797 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001798 free(client_opts.ch_binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001799
1800 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001801 if (!client_opts.ch_bind_count) {
1802 free(client_opts.ch_binds);
1803 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001804
1805 free(client_opts.ch_binds_aux);
1806 client_opts.ch_binds_aux = NULL;
Michal Vasko66c762a2016-10-13 10:34:14 +02001807 } else if (i < client_opts.ch_bind_count) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001808 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count],
1809 sizeof *client_opts.ch_binds);
1810
1811 memcpy(&client_opts.ch_binds_aux[i], &client_opts.ch_binds_aux[client_opts.ch_bind_count],
1812 sizeof *client_opts.ch_binds_aux);
Michal Vasko66c762a2016-10-13 10:34:14 +02001813 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001814
1815 ret = 0;
1816 }
1817 }
1818 }
1819
1820 return ret;
1821}
1822
1823API int
1824nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1825{
1826 int sock;
1827 char *host = NULL;
1828 uint16_t port, idx;
1829
roman40672412023-05-04 11:10:22 +02001830 NC_CHECK_ARG_RET(NULL, session, -1);
1831
Michal Vasko45e53ae2016-04-07 11:46:03 +02001832 if (!client_opts.ch_binds) {
romand82caf12024-03-05 14:21:39 +01001833 ERR(NULL, "Call-Home binds not set.");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001834 return -1;
Michal Vasko3031aae2016-01-27 16:07:18 +01001835 }
1836
Michal Vasko5a2c5b92024-02-02 09:11:41 +01001837 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, &client_opts.ch_bind_lock, timeout,
1838 &host, &port, &idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001839 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001840 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001841 return sock;
1842 }
1843
Michal Vaskoc4cdc9e2024-05-03 12:03:07 +02001844 /* configure keepalives */
1845 if (nc_sock_configure_ka(sock, &client_opts.ka)) {
1846 free(host);
1847 close(sock);
1848 return -1;
1849 }
1850
Michal Vasko9d4cca52022-09-07 11:19:57 +02001851 if (client_opts.ch_binds_aux[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001852 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
roman2eab4742023-06-06 10:00:26 +02001853 } else if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001854 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT,
1855 client_opts.ch_binds_aux[idx].hostname);
roman2eab4742023-06-06 10:00:26 +02001856 } else {
Michal Vaskofee717c2016-02-01 13:25:43 +01001857 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001858 *session = NULL;
1859 }
1860
1861 free(host);
1862
1863 if (!(*session)) {
1864 return -1;
1865 }
1866
1867 return 1;
1868}
1869
roman2eab4742023-06-06 10:00:26 +02001870#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001871
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001872API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001873nc_session_get_cpblts(const struct nc_session *session)
1874{
roman40672412023-05-04 11:10:22 +02001875 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vaskobdfb5242016-05-24 09:11:01 +02001876
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001877 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001878}
1879
1880API const char *
1881nc_session_cpblt(const struct nc_session *session, const char *capab)
1882{
1883 int i, len;
1884
roman40672412023-05-04 11:10:22 +02001885 NC_CHECK_ARG_RET(session, session, capab, NULL);
Michal Vaskobdfb5242016-05-24 09:11:01 +02001886
1887 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001888 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1889 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1890 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001891 }
1892 }
1893
1894 return NULL;
1895}
1896
Michal Vasko9cd26a82016-05-31 08:58:48 +02001897API int
1898nc_session_ntf_thread_running(const struct nc_session *session)
1899{
roman40672412023-05-04 11:10:22 +02001900 NC_CHECK_ARG_RET(session, session, 0);
1901
1902 if (session->side != NC_CLIENT) {
1903 ERRARG(NULL, "session");
Michal Vasko9cd26a82016-05-31 08:58:48 +02001904 return 0;
1905 }
1906
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02001907 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running);
Michal Vasko9cd26a82016-05-31 08:58:48 +02001908}
1909
roman9075eab2023-09-14 10:07:26 +02001910API int
1911nc_client_init(void)
1912{
1913 int r;
1914
1915 if ((r = pthread_mutex_init(&client_opts.ch_bind_lock, NULL))) {
1916 ERR(NULL, "%s: failed to init bind lock(%s).", __func__, strerror(r));
1917 return -1;
1918 }
1919
1920 return 0;
1921}
1922
Michal Vaskob7558c52016-02-26 15:04:19 +01001923API void
1924nc_client_destroy(void)
1925{
roman9075eab2023-09-14 10:07:26 +02001926 pthread_mutex_destroy(&client_opts.ch_bind_lock);
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001927 nc_client_set_schema_searchpath(NULL);
roman2eab4742023-06-06 10:00:26 +02001928#ifdef NC_ENABLED_SSH_TLS
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001929 nc_client_ch_del_bind(NULL, 0, 0);
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001930 nc_client_ssh_destroy_opts();
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001931 nc_client_tls_destroy_opts();
roman2eab4742023-06-06 10:00:26 +02001932#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskob7558c52016-02-26 15:04:19 +01001933}
1934
Michal Vasko77367452021-02-16 16:32:18 +01001935static NC_MSG_TYPE
1936recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001937{
Michal Vasko77367452021-02-16 16:32:18 +01001938 char *ptr;
1939 struct lyd_attr *attr;
1940 uint64_t cur_msgid;
1941
1942 assert(envp && !envp->schema);
1943
1944 /* find the message-id attribute */
1945 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1946 if (!strcmp(attr->name.name, "message-id")) {
1947 break;
1948 }
1949 }
1950
1951 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001952 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001953 return NC_MSG_REPLY_ERR_MSGID;
1954 }
1955
1956 cur_msgid = strtoul(attr->value, &ptr, 10);
1957 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001958 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1959 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001960 return NC_MSG_REPLY_ERR_MSGID;
1961 }
1962
1963 return NC_MSG_REPLY;
1964}
1965
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001966/**
1967 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1968 *
1969 * @param[in] session NETCONF session used to send error messages.
1970 * @param[in] msg Message to check for type.
1971 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1972 * @return NC_MSG_NOTIF If format roughly matches a notification;
1973 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1974 */
1975static NC_MSG_TYPE
1976get_msg_type(struct nc_session *session, struct ly_in *msg)
1977{
1978 const char *str, *end;
1979
1980 str = ly_in_memory(msg, NULL);
1981
1982 while (*str) {
1983 /* Skip whitespaces */
1984 while (isspace(*str)) {
1985 str++;
1986 }
1987
1988 if (*str == '<') {
1989 str++;
1990 if (!strncmp(str, "!--", 3)) {
1991 /* Skip comments */
1992 end = "-->";
1993 str = strstr(str, end);
1994 } else if (!strncmp(str, "?xml", 4)) {
1995 /* Skip xml declaration */
1996 end = "?>";
1997 str = strstr(str, end);
1998 } else if (!strncmp(str, "rpc-reply", 9)) {
1999 return NC_MSG_REPLY;
2000 } else if (!strncmp(str, "notification", 12)) {
2001 return NC_MSG_NOTIF;
2002 } else {
2003 ERR(session, "Unknown xml element '%.10s'.", str);
2004 return NC_MSG_ERROR;
2005 }
2006 if (!str) {
2007 /* No matching ending tag found */
2008 ERR(session, "No matching ending tag '%s' found in xml message.", end);
2009 return NC_MSG_ERROR;
2010 }
2011 str += strlen(end);
2012 } else {
2013 /* Not a valid xml */
2014 ERR(session, "Unexpected character '%c' in xml message.", *str);
2015 return NC_MSG_ERROR;
2016 }
2017 }
2018
2019 /* Unexpected end of message */
2020 ERR(session, "Unexpected end of xml message.");
2021 return NC_MSG_ERROR;
2022}
2023
2024/**
2025 * @brief Function to receive either replies or notifications.
2026 *
2027 * @param[in] session NETCONF session from which this function receives messages.
2028 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
2029 * @param[in] expected Type of the message the caller desired.
2030 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
2031 * @return NC_MSG_REPLY If a rpc-reply was received;
2032 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01002033 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002034 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
2035 */
2036static NC_MSG_TYPE
2037recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
2038{
2039 struct nc_msg_cont **cont_ptr;
2040 struct ly_in *msg = NULL;
2041 struct nc_msg_cont *cont, *prev;
2042 NC_MSG_TYPE ret = NC_MSG_ERROR;
2043 int r;
2044
2045 *message = NULL;
2046
2047 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002048 r = nc_session_client_msgs_lock(session, &timeout, __func__);
2049 if (!r) {
2050 ret = NC_MSG_WOULDBLOCK;
2051 goto cleanup;
2052 } else if (r == -1) {
2053 ret = NC_MSG_ERROR;
2054 goto cleanup;
2055 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002056
2057 /* Find the expected message in the buffer */
2058 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002059 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002060 prev = cont;
2061 }
2062
2063 if (cont) {
2064 /* Remove found message from buffer */
2065 if (prev) {
2066 prev->next = cont->next;
2067 } else {
2068 session->opts.client.msgs = cont->next;
2069 }
2070
2071 /* Use the buffer message */
2072 ret = cont->type;
2073 msg = cont->msg;
2074 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02002075 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002076 }
2077
2078 /* Read a message from the wire */
2079 r = nc_read_msg_poll_io(session, timeout, &msg);
2080 if (!r) {
2081 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02002082 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002083 } else if (r == -1) {
2084 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002085 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002086 }
2087
2088 /* Basic check to determine message type */
2089 ret = get_msg_type(session, msg);
2090 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02002091 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002092 }
2093
2094 /* If received a message of different type store it in the buffer */
2095 if (ret != expected) {
2096 cont_ptr = &session->opts.client.msgs;
2097 while (*cont_ptr) {
2098 cont_ptr = &((*cont_ptr)->next);
2099 }
2100 *cont_ptr = malloc(sizeof **cont_ptr);
roman3a95bb22023-10-26 11:07:17 +02002101 NC_CHECK_ERRMEM_GOTO(!*cont_ptr, ret = NC_MSG_ERROR, cleanup_unlock);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002102 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002103 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002104 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002105 (*cont_ptr)->next = NULL;
2106 }
2107
Michal Vasko01130bd2021-08-26 11:47:38 +02002108cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002109 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002110 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002111
Michal Vasko01130bd2021-08-26 11:47:38 +02002112cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002113 if (ret == expected) {
2114 *message = msg;
2115 } else {
2116 ly_in_free(msg, 1);
2117 }
2118 return ret;
2119}
2120
Michal Vasko77367452021-02-16 16:32:18 +01002121static NC_MSG_TYPE
2122recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
2123{
Michal Vasko77367452021-02-16 16:32:18 +01002124 LY_ERR lyrc;
2125 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002126 NC_MSG_TYPE ret = NC_MSG_ERROR;
Michal Vaskob25c56f2024-03-27 15:45:56 +01002127 uint32_t temp_lo = LY_LOSTORE, *prev_lo;
Michal Vasko77367452021-02-16 16:32:18 +01002128
2129 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
2130
2131 *envp = NULL;
2132
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002133 /* Receive messages until a rpc-reply is found or a timeout or error reached */
2134 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
2135 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01002136 goto cleanup;
2137 }
2138
2139 /* parse */
Michal Vaskob25c56f2024-03-27 15:45:56 +01002140 prev_lo = ly_temp_log_options(&temp_lo);
Michal Vasko77367452021-02-16 16:32:18 +01002141 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
Michal Vaskob25c56f2024-03-27 15:45:56 +01002142 ly_temp_log_options(prev_lo);
2143
2144 if (*envp) {
2145 /* if the envelopes were parsed, check the message-id, even on error */
Michal Vasko77367452021-02-16 16:32:18 +01002146 ret = recv_reply_check_msgid(session, *envp, msgid);
2147 goto cleanup;
Michal Vaskob25c56f2024-03-27 15:45:56 +01002148 }
2149
2150 if (lyrc) {
2151 /* parsing error */
Michal Vasko58791da2024-02-26 13:52:59 +01002152 ERR(session, "Received an invalid message (%s).", ly_err_last(LYD_CTX(op))->msg);
Michal Vasko9a108052022-04-01 12:06:54 +02002153 lyd_free_tree(*envp);
2154 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002155 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002156 goto cleanup;
2157 }
2158
Michal Vasko77367452021-02-16 16:32:18 +01002159cleanup:
2160 ly_in_free(msg, 1);
2161 return ret;
2162}
2163
2164static int
2165recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2166{
Michal Vasko143aa142021-10-01 15:31:48 +02002167 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002168 struct nc_rpc_act_generic *rpc_gen;
2169 struct ly_in *in;
2170 struct lyd_node *tree, *op2;
2171 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002172 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002173
2174 switch (rpc->type) {
2175 case NC_RPC_ACT_GENERIC:
2176 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2177 if (rpc_gen->has_data) {
2178 tree = rpc_gen->content.data;
2179
2180 /* find the operation node */
2181 lyrc = LY_EINVAL;
2182 LYD_TREE_DFS_BEGIN(tree, op2) {
2183 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2184 lyrc = lyd_dup_single(op2, NULL, 0, op);
2185 break;
2186 }
2187 LYD_TREE_DFS_END(tree, op2);
2188 }
2189 } else {
2190 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2191 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2192 ly_in_free(in, 0);
2193 if (lyrc) {
Michal Vasko9a108052022-04-01 12:06:54 +02002194 lyd_free_tree(tree);
Michal Vasko77367452021-02-16 16:32:18 +01002195 return -1;
2196 }
2197
2198 /* we want just the operation node */
2199 lyrc = lyd_dup_single(op2, NULL, 0, op);
2200
2201 lyd_free_tree(tree);
2202 }
2203 break;
2204 case NC_RPC_GETCONFIG:
2205 module_name = "ietf-netconf";
2206 rpc_name = "get-config";
2207 break;
2208 case NC_RPC_EDIT:
2209 module_name = "ietf-netconf";
2210 rpc_name = "edit-config";
2211 break;
2212 case NC_RPC_COPY:
2213 module_name = "ietf-netconf";
2214 rpc_name = "copy-config";
2215 break;
2216 case NC_RPC_DELETE:
2217 module_name = "ietf-netconf";
2218 rpc_name = "delete-config";
2219 break;
2220 case NC_RPC_LOCK:
2221 module_name = "ietf-netconf";
2222 rpc_name = "lock";
2223 break;
2224 case NC_RPC_UNLOCK:
2225 module_name = "ietf-netconf";
2226 rpc_name = "unlock";
2227 break;
2228 case NC_RPC_GET:
2229 module_name = "ietf-netconf";
2230 rpc_name = "get";
2231 break;
2232 case NC_RPC_KILL:
2233 module_name = "ietf-netconf";
2234 rpc_name = "kill-session";
2235 break;
2236 case NC_RPC_COMMIT:
2237 module_name = "ietf-netconf";
2238 rpc_name = "commit";
2239 break;
2240 case NC_RPC_DISCARD:
2241 module_name = "ietf-netconf";
2242 rpc_name = "discard-changes";
2243 break;
2244 case NC_RPC_CANCEL:
2245 module_name = "ietf-netconf";
2246 rpc_name = "cancel-commit";
2247 break;
2248 case NC_RPC_VALIDATE:
2249 module_name = "ietf-netconf";
2250 rpc_name = "validate";
2251 break;
2252 case NC_RPC_GETSCHEMA:
2253 module_name = "ietf-netconf-monitoring";
2254 rpc_name = "get-schema";
2255 break;
2256 case NC_RPC_SUBSCRIBE:
2257 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002258 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002259 break;
2260 case NC_RPC_GETDATA:
2261 module_name = "ietf-netconf-nmda";
2262 rpc_name = "get-data";
2263 break;
2264 case NC_RPC_EDITDATA:
2265 module_name = "ietf-netconf-nmda";
2266 rpc_name = "edit-data";
2267 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002268 case NC_RPC_ESTABLISHSUB:
2269 module_name = "ietf-subscribed-notifications";
2270 rpc_name = "establish-subscription";
2271 break;
2272 case NC_RPC_MODIFYSUB:
2273 module_name = "ietf-subscribed-notifications";
2274 rpc_name = "modify-subscription";
2275 break;
2276 case NC_RPC_DELETESUB:
2277 module_name = "ietf-subscribed-notifications";
2278 rpc_name = "delete-subscription";
2279 break;
2280 case NC_RPC_KILLSUB:
2281 module_name = "ietf-subscribed-notifications";
2282 rpc_name = "kill-subscription";
2283 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002284 case NC_RPC_ESTABLISHPUSH:
2285 module_name = "ietf-subscribed-notifications";
2286 rpc_name = "establish-subscription";
2287 module_check = "ietf-yang-push";
2288 break;
2289 case NC_RPC_MODIFYPUSH:
2290 module_name = "ietf-subscribed-notifications";
2291 rpc_name = "modify-subscription";
2292 module_check = "ietf-yang-push";
2293 break;
2294 case NC_RPC_RESYNCSUB:
2295 module_name = "ietf-yang-push";
2296 rpc_name = "resync-subscription";
2297 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002298 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002299 lyrc = LY_EINT;
2300 break;
2301 }
2302
2303 if (module_name && rpc_name) {
2304 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2305 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002306 ERR(session, "Missing \"%s\" module in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002307 return -1;
2308 }
2309
2310 /* create the operation node */
2311 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2312 }
Michal Vasko305faca2021-03-25 09:16:02 +01002313 if (module_check) {
2314 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002315 ERR(session, "Missing \"%s\" module in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002316 return -1;
2317 }
2318 }
Michal Vasko77367452021-02-16 16:32:18 +01002319
2320 if (lyrc) {
2321 return -1;
2322 }
2323 return 0;
2324}
2325
2326API NC_MSG_TYPE
2327nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2328 struct lyd_node **op)
2329{
2330 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002331
roman40672412023-05-04 11:10:22 +02002332 NC_CHECK_ARG_RET(session, session, rpc, envp, op, NC_MSG_ERROR);
2333
2334 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002335 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002336 return NC_MSG_ERROR;
2337 }
Michal Vasko77367452021-02-16 16:32:18 +01002338
2339 /* get a duplicate of the RPC node to append reply to */
2340 if (recv_reply_dup_rpc(session, rpc, op)) {
2341 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002342 }
Michal Vasko086311b2016-01-08 09:53:11 +01002343
Michal Vasko77367452021-02-16 16:32:18 +01002344 /* receive a reply */
2345 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002346
Michal Vasko77367452021-02-16 16:32:18 +01002347 /* do not return the RPC copy on error or if the reply includes no data */
2348 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2349 lyd_free_tree(*op);
2350 *op = NULL;
2351 }
2352 return ret;
2353}
2354
2355static NC_MSG_TYPE
2356recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2357{
Michal Vasko77367452021-02-16 16:32:18 +01002358 LY_ERR lyrc;
2359 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002360 NC_MSG_TYPE ret = NC_MSG_ERROR;
2361
2362 *op = NULL;
2363 *envp = NULL;
2364
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002365 /* Receive messages until a notification is found or a timeout or error reached */
2366 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2367 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002368 goto cleanup;
2369 }
2370
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002371 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002372 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2373 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002374 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002375 } else {
Michal Vasko58791da2024-02-26 13:52:59 +01002376 ERR(session, "Received an invalid message (%s).", ly_err_last(session->ctx)->msg);
Michal Vasko9a108052022-04-01 12:06:54 +02002377 lyd_free_tree(*envp);
2378 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002379 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002380 goto cleanup;
2381 }
2382
Michal Vasko77367452021-02-16 16:32:18 +01002383cleanup:
2384 ly_in_free(msg, 1);
2385 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002386}
2387
2388API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002389nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002390{
roman40672412023-05-04 11:10:22 +02002391 NC_CHECK_ARG_RET(session, session, envp, op, NC_MSG_ERROR);
2392
2393 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002394 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002395 return NC_MSG_ERROR;
2396 }
2397
Michal Vasko77367452021-02-16 16:32:18 +01002398 /* receive a notification */
2399 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002400}
2401
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002402static void *
2403nc_recv_notif_thread(void *arg)
2404{
2405 struct nc_ntf_thread_arg *ntarg;
2406 struct nc_session *session;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002407 nc_notif_dispatch_clb notif_clb;
2408 void *user_data;
Michal Vasko743ff9f2022-10-20 10:03:13 +02002409
Michal Vaskoffb35e92022-10-20 09:07:25 +02002410 void (*free_data)(void *);
Michal Vasko77367452021-02-16 16:32:18 +01002411 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002412 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002413
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002414 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002415 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002416
2417 ntarg = (struct nc_ntf_thread_arg *)arg;
2418 session = ntarg->session;
2419 notif_clb = ntarg->notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002420 user_data = ntarg->user_data;
2421 free_data = ntarg->free_data;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002422 free(ntarg);
2423
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002424 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
Michal Vasko77367452021-02-16 16:32:18 +01002425 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002426 if (msgtype == NC_MSG_NOTIF) {
Michal Vaskoffb35e92022-10-20 09:07:25 +02002427 notif_clb(session, envp, op, user_data);
Michal Vasko77367452021-02-16 16:32:18 +01002428 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2429 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002430 lyd_free_all(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002431 break;
2432 }
Michal Vasko77367452021-02-16 16:32:18 +01002433 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002434 lyd_free_all(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002435 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002436 /* quit this thread once the session is broken */
2437 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002438 }
2439
2440 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2441 }
2442
Michal Vasko05532772021-06-03 12:12:38 +02002443 VRB(session, "Notification thread exit.");
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002444 ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoffb35e92022-10-20 09:07:25 +02002445 if (free_data) {
2446 free_data(user_data);
2447 }
2448
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002449 return NULL;
2450}
2451
2452API int
Michal Vaskoffb35e92022-10-20 09:07:25 +02002453nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb)
2454{
2455 return nc_recv_notif_dispatch_data(session, notif_clb, NULL, NULL);
2456}
2457
2458API int
2459nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data,
2460 void (*free_data)(void *))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002461{
2462 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002463 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002464 int ret;
2465
roman40672412023-05-04 11:10:22 +02002466 NC_CHECK_ARG_RET(session, session, notif_clb, -1);
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 Vaskoa8ad4482016-01-28 14:25:54 +01002470 return -1;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002471 }
2472
2473 ntarg = malloc(sizeof *ntarg);
roman3a95bb22023-10-26 11:07:17 +02002474 NC_CHECK_ERRMEM_RET(!ntarg, -1);
2475
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002476 ntarg->session = session;
2477 ntarg->notif_clb = notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002478 ntarg->user_data = user_data;
2479 ntarg->free_data = free_data;
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002480 ATOMIC_INC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002481
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002482 /* just so that nc_recv_notif_thread() does not immediately exit */
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002483 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002484
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002485 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002486 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002487 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002488 free(ntarg);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002489 if (ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count) == 1) {
2490 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
2491 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002492 return -1;
2493 }
2494
2495 return 0;
2496}
2497
Michal Vasko77367452021-02-16 16:32:18 +01002498static const char *
2499nc_wd2str(NC_WD_MODE wd)
2500{
2501 switch (wd) {
2502 case NC_WD_ALL:
2503 return "report-all";
2504 case NC_WD_ALL_TAG:
2505 return "report-all-tagged";
2506 case NC_WD_TRIM:
2507 return "trim";
2508 case NC_WD_EXPLICIT:
2509 return "explicit";
2510 default:
2511 break;
2512 }
2513
2514 return NULL;
2515}
2516
Michal Vasko086311b2016-01-08 09:53:11 +01002517API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002518nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002519{
2520 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002521 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002522 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002523 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002524 struct nc_rpc_getconfig *rpc_gc;
2525 struct nc_rpc_edit *rpc_e;
2526 struct nc_rpc_copy *rpc_cp;
2527 struct nc_rpc_delete *rpc_del;
2528 struct nc_rpc_lock *rpc_lock;
2529 struct nc_rpc_get *rpc_g;
2530 struct nc_rpc_kill *rpc_k;
2531 struct nc_rpc_commit *rpc_com;
2532 struct nc_rpc_cancel *rpc_can;
2533 struct nc_rpc_validate *rpc_val;
2534 struct nc_rpc_getschema *rpc_gs;
2535 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002536 struct nc_rpc_getdata *rpc_getd;
2537 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002538 struct nc_rpc_establishsub *rpc_estsub;
2539 struct nc_rpc_modifysub *rpc_modsub;
2540 struct nc_rpc_deletesub *rpc_delsub;
2541 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002542 struct nc_rpc_establishpush *rpc_estpush;
2543 struct nc_rpc_modifypush *rpc_modpush;
2544 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002545 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002546 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002547 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002548 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002549 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002550 uint64_t cur_msgid;
2551
roman40672412023-05-04 11:10:22 +02002552 NC_CHECK_ARG_RET(session, session, rpc, msgid, NC_MSG_ERROR);
2553
2554 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002555 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002556 return NC_MSG_ERROR;
2557 }
2558
Michal Vaskoc1171a42019-11-05 12:06:46 +01002559 switch (rpc->type) {
2560 case NC_RPC_ACT_GENERIC:
2561 /* checked when parsing */
2562 break;
2563 case NC_RPC_GETCONFIG:
2564 case NC_RPC_EDIT:
2565 case NC_RPC_COPY:
2566 case NC_RPC_DELETE:
2567 case NC_RPC_LOCK:
2568 case NC_RPC_UNLOCK:
2569 case NC_RPC_GET:
2570 case NC_RPC_KILL:
2571 case NC_RPC_COMMIT:
2572 case NC_RPC_DISCARD:
2573 case NC_RPC_CANCEL:
2574 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002575 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002576 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002577 ERR(session, "Missing \"ietf-netconf\" module in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002578 return NC_MSG_ERROR;
2579 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002580 break;
2581 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002582 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002583 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002584 ERR(session, "Missing \"ietf-netconf-monitoring\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002585 return NC_MSG_ERROR;
2586 }
2587 break;
2588 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002589 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002590 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002591 ERR(session, "Missing \"notifications\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002592 return NC_MSG_ERROR;
2593 }
2594 break;
2595 case NC_RPC_GETDATA:
2596 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002597 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002598 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002599 ERR(session, "Missing \"ietf-netconf-nmda\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002600 return NC_MSG_ERROR;
2601 }
2602 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002603 case NC_RPC_ESTABLISHSUB:
2604 case NC_RPC_MODIFYSUB:
2605 case NC_RPC_DELETESUB:
2606 case NC_RPC_KILLSUB:
2607 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2608 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002609 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002610 return NC_MSG_ERROR;
2611 }
2612 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002613 case NC_RPC_ESTABLISHPUSH:
2614 case NC_RPC_MODIFYPUSH:
2615 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2616 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002617 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002618 return NC_MSG_ERROR;
2619 }
2620 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2621 if (!mod2) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002622 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002623 return NC_MSG_ERROR;
2624 }
2625 break;
2626 case NC_RPC_RESYNCSUB:
2627 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2628 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002629 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002630 return NC_MSG_ERROR;
2631 }
2632 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002633 case NC_RPC_UNKNOWN:
2634 ERRINT;
2635 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002636 }
2637
Michal Vaskoab9deb62021-05-27 11:37:00 +02002638#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2639
Michal Vasko086311b2016-01-08 09:53:11 +01002640 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002641 case NC_RPC_ACT_GENERIC:
2642 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002643
2644 if (rpc_gen->has_data) {
2645 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002646 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002647 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002648 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2649 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2650 ly_in_free(in, 0);
2651 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002652 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002653 }
Michal Vasko086311b2016-01-08 09:53:11 +01002654 }
2655 break;
2656
2657 case NC_RPC_GETCONFIG:
2658 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2659
Michal Vaskoab9deb62021-05-27 11:37:00 +02002660 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2661 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2662 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002663 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002664 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002665 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, LYD_ANYDATA_XML, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002666 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002667 } else {
Michal Vasko58791da2024-02-26 13:52:59 +01002668 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002669 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2670 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002671 }
2672 }
2673
2674 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002675 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002676 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002677 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002678 lyrc = LY_ENOTFOUND;
2679 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002680 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002681 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 +01002682 }
2683 break;
2684
2685 case NC_RPC_EDIT:
2686 rpc_e = (struct nc_rpc_edit *)rpc;
2687
Michal Vaskoab9deb62021-05-27 11:37:00 +02002688 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2689 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2690 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002691
2692 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002693 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 +01002694 }
Michal Vasko086311b2016-01-08 09:53:11 +01002695 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002696 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 +01002697 }
Michal Vasko086311b2016-01-08 09:53:11 +01002698 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002699 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 +01002700 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002701 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002702 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 +01002703 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002704 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002705 }
2706 break;
2707
2708 case NC_RPC_COPY:
2709 rpc_cp = (struct nc_rpc_copy *)rpc;
2710
Michal Vaskoab9deb62021-05-27 11:37:00 +02002711 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2712 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002713 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002714 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002715 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002716 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002717 }
2718
Michal Vaskoab9deb62021-05-27 11:37:00 +02002719 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002720 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002721 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002722 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 +01002723 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002724 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002725 }
2726 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002727 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002728 }
2729
2730 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002731 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002732 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002733 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002734 lyrc = LY_ENOTFOUND;
2735 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002736 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002737 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 +01002738 }
2739 break;
2740
2741 case NC_RPC_DELETE:
2742 rpc_del = (struct nc_rpc_delete *)rpc;
2743
Michal Vaskoab9deb62021-05-27 11:37:00 +02002744 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2745 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002746 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002747 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002748 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002749 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002750 }
2751 break;
2752
2753 case NC_RPC_LOCK:
2754 rpc_lock = (struct nc_rpc_lock *)rpc;
2755
Michal Vaskoab9deb62021-05-27 11:37:00 +02002756 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2757 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2758 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002759 break;
2760
2761 case NC_RPC_UNLOCK:
2762 rpc_lock = (struct nc_rpc_lock *)rpc;
2763
Michal Vaskoab9deb62021-05-27 11:37:00 +02002764 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2765 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2766 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002767 break;
2768
2769 case NC_RPC_GET:
2770 rpc_g = (struct nc_rpc_get *)rpc;
2771
Michal Vaskoab9deb62021-05-27 11:37:00 +02002772 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002773 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002774 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002775 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, LYD_ANYDATA_XML, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002776 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002777 } else {
Michal Vasko58791da2024-02-26 13:52:59 +01002778 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002779 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2780 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002781 }
2782 }
2783
2784 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002785 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002786 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002787 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002788 lyrc = LY_ENOTFOUND;
2789 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002790 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002791 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 +01002792 }
2793 break;
2794
2795 case NC_RPC_KILL:
2796 rpc_k = (struct nc_rpc_kill *)rpc;
2797
Michal Vaskoab9deb62021-05-27 11:37:00 +02002798 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko16374712024-04-26 14:13:00 +02002799 sprintf(str, "%" PRIu32, rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002800 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002801 break;
2802
2803 case NC_RPC_COMMIT:
2804 rpc_com = (struct nc_rpc_commit *)rpc;
2805
Michal Vaskoab9deb62021-05-27 11:37:00 +02002806 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002807 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002808 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002809 }
2810
2811 if (rpc_com->confirm_timeout) {
Michal Vasko16374712024-04-26 14:13:00 +02002812 sprintf(str, "%" PRIu32, rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002813 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002814 }
Michal Vasko086311b2016-01-08 09:53:11 +01002815 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002816 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002817 }
Michal Vasko086311b2016-01-08 09:53:11 +01002818 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002819 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002820 }
2821 break;
2822
2823 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002824 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002825 break;
2826
2827 case NC_RPC_CANCEL:
2828 rpc_can = (struct nc_rpc_cancel *)rpc;
2829
Michal Vaskoab9deb62021-05-27 11:37:00 +02002830 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002831 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002832 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002833 }
2834 break;
2835
2836 case NC_RPC_VALIDATE:
2837 rpc_val = (struct nc_rpc_validate *)rpc;
2838
Michal Vaskoab9deb62021-05-27 11:37:00 +02002839 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2840 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002841 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002842 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002843 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 +01002844 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002845 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002846 }
2847 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002848 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002849 }
2850 break;
2851
2852 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002853 rpc_gs = (struct nc_rpc_getschema *)rpc;
2854
Michal Vaskoab9deb62021-05-27 11:37:00 +02002855 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2856 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002857 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002858 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002859 }
2860 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002861 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002862 }
2863 break;
2864
2865 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002866 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2867
Michal Vaskoab9deb62021-05-27 11:37:00 +02002868 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002869 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002870 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002871 }
2872
2873 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002874 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002875 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, LYD_ANYDATA_XML, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002876 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002877 } else {
Michal Vasko58791da2024-02-26 13:52:59 +01002878 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node));
Michal Vaskoab9deb62021-05-27 11:37:00 +02002879 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2880 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002881 }
2882 }
Michal Vasko086311b2016-01-08 09:53:11 +01002883 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002884 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002885 }
Michal Vasko086311b2016-01-08 09:53:11 +01002886 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002887 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002888 }
2889 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002890
2891 case NC_RPC_GETDATA:
2892 rpc_getd = (struct nc_rpc_getdata *)rpc;
2893
Michal Vaskoab9deb62021-05-27 11:37:00 +02002894 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2895 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2896
Michal Vaskoc1171a42019-11-05 12:06:46 +01002897 if (rpc_getd->filter) {
2898 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002899 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 +01002900 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002901 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002902 }
2903 }
2904 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002905 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002906 }
2907 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002908 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2909 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002910 }
2911 if (rpc_getd->max_depth) {
2912 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002913 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002914 }
2915 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002916 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002917 }
2918
2919 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002920 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002921 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 +01002922 }
2923 break;
2924
2925 case NC_RPC_EDITDATA:
2926 rpc_editd = (struct nc_rpc_editdata *)rpc;
2927
Michal Vaskoab9deb62021-05-27 11:37:00 +02002928 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2929 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002930
2931 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002932 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2933 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002934 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002935 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002936 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 +01002937 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002938 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002939 }
2940 break;
2941
Michal Vasko96f247a2021-03-15 13:32:10 +01002942 case NC_RPC_ESTABLISHSUB:
2943 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2944
Michal Vaskoab9deb62021-05-27 11:37:00 +02002945 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002946
2947 if (rpc_estsub->filter) {
2948 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002949 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, LYD_ANYDATA_XML,
Michal Vaskoab9deb62021-05-27 11:37:00 +02002950 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002951 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002952 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002953 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002954 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002955 }
2956 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002957 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002958
2959 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002960 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002961 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002962 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002963 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002964 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002965 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002966 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002967 }
2968 break;
2969
2970 case NC_RPC_MODIFYSUB:
2971 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2972
Michal Vaskoab9deb62021-05-27 11:37:00 +02002973 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002974
Michal Vasko16374712024-04-26 14:13:00 +02002975 sprintf(str, "%" PRIu32, rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002976 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002977
2978 if (rpc_modsub->filter) {
2979 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01002980 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, LYD_ANYDATA_XML,
Michal Vaskoab9deb62021-05-27 11:37:00 +02002981 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002982 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002983 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002984 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002985 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002986 }
2987 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002988 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002989 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002990 }
2991 break;
2992
2993 case NC_RPC_DELETESUB:
2994 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2995
Michal Vaskoab9deb62021-05-27 11:37:00 +02002996 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002997
Michal Vasko16374712024-04-26 14:13:00 +02002998 sprintf(str, "%" PRIu32, rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002999 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003000 break;
3001
3002 case NC_RPC_KILLSUB:
3003 rpc_killsub = (struct nc_rpc_killsub *)rpc;
3004
Michal Vaskoab9deb62021-05-27 11:37:00 +02003005 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003006
Michal Vasko16374712024-04-26 14:13:00 +02003007 sprintf(str, "%" PRIu32, rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003008 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003009 break;
3010
Michal Vasko305faca2021-03-25 09:16:02 +01003011 case NC_RPC_ESTABLISHPUSH:
3012 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
3013
Michal Vaskoab9deb62021-05-27 11:37:00 +02003014 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
3015 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003016
3017 if (rpc_estpush->filter) {
3018 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01003019 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter,
Michal Vaskoab9deb62021-05-27 11:37:00 +02003020 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003021 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003022 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003023 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003024 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003025 }
3026 }
3027
3028 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003029 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003030 }
Michal Vasko305faca2021-03-25 09:16:02 +01003031 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003032 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003033 }
3034
3035 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003036 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003037 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003038 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003039 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003040 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003041 }
3042 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003043 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003044 if (rpc_estpush->dampening_period) {
3045 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003046 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003047 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02003048 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
3049 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003050 if (rpc_estpush->excluded_change) {
3051 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003052 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
3053 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003054 }
3055 }
3056 }
3057 break;
3058
3059 case NC_RPC_MODIFYPUSH:
3060 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
3061
Michal Vaskoab9deb62021-05-27 11:37:00 +02003062 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003063
Michal Vasko16374712024-04-26 14:13:00 +02003064 sprintf(str, "%" PRIu32, rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003065 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
3066 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003067
3068 if (rpc_modpush->filter) {
3069 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vasko58791da2024-02-26 13:52:59 +01003070 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter,
Michal Vaskoab9deb62021-05-27 11:37:00 +02003071 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003072 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003073 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003074 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003075 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003076 }
3077 }
Michal Vasko305faca2021-03-25 09:16:02 +01003078 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003079 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003080 }
3081
3082 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003083 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003084 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003085 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003086 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003087 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003088 }
3089 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003090 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003091 if (rpc_modpush->dampening_period) {
3092 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003093 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003094 }
3095 }
3096 break;
3097
3098 case NC_RPC_RESYNCSUB:
3099 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
3100
Michal Vaskoab9deb62021-05-27 11:37:00 +02003101 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko16374712024-04-26 14:13:00 +02003102 sprintf(str, "%" PRIu32, rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003103 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003104 break;
3105
Michal Vasko96f247a2021-03-15 13:32:10 +01003106 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01003107 ERRINT;
3108 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01003109 }
3110
Michal Vaskoab9deb62021-05-27 11:37:00 +02003111#undef CHECK_LYRC_BREAK
3112
3113 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02003114 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02003115 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02003116 return NC_MSG_ERROR;
3117 }
3118
Michal Vasko131120a2018-05-29 15:44:02 +02003119 /* send RPC, store its message ID */
3120 r = nc_send_msg_io(session, timeout, data);
3121 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003122
Radek Krejcib4b19062018-02-07 16:33:06 +01003123 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003124 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003125 }
Michal Vasko086311b2016-01-08 09:53:11 +01003126
Michal Vasko131120a2018-05-29 15:44:02 +02003127 if (r == NC_MSG_RPC) {
3128 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003129 }
Michal Vasko131120a2018-05-29 15:44:02 +02003130 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003131}
Michal Vaskode2946c2017-01-12 12:19:26 +01003132
3133API void
3134nc_client_session_set_not_strict(struct nc_session *session)
3135{
3136 if (session->side != NC_CLIENT) {
roman40672412023-05-04 11:10:22 +02003137 ERRARG(NULL, "session");
Michal Vaskode2946c2017-01-12 12:19:26 +01003138 return;
3139 }
3140
3141 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3142}