blob: 7e90fc1674d7c95224dd671139c536a40b0f2cf3 [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"
Michal Vasko086311b2016-01-08 09:53:11 +010045#include "libnetconf.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010046#include "messages_client.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020047#include "session_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010048
Michal Vasko8a4e1462020-05-07 11:32:31 +020049#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020050#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020051
Michal Vasko80ef5d22016-01-18 09:21:02 +010052static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
53
Radek Krejci62aa0642017-05-25 16:33:49 +020054#ifdef NC_ENABLED_SSH
55int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
56char *sshauth_password(const char *username, const char *hostname, void *priv);
57char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020058char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
Radek Krejci62aa0642017-05-25 16:33:49 +020059#endif /* NC_ENABLED_SSH */
60
61static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
62static pthread_key_t nc_client_context_key;
63#ifdef __linux__
64static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020065 .opts.ka = {
66 .enabled = 1,
67 .idle_time = 1,
68 .max_probes = 10,
69 .probe_interval = 5
70 },
Radek Krejci62aa0642017-05-25 16:33:49 +020071#ifdef NC_ENABLED_SSH
72 .ssh_opts = {
roman41a11e42022-06-22 09:27:08 +020073 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020074 .auth_hostkey_check = sshauth_hostkey_check,
75 .auth_password = sshauth_password,
76 .auth_interactive = sshauth_interactive,
77 .auth_privkey_passphrase = sshauth_privkey_passphrase
78 },
79 .ssh_ch_opts = {
80 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
81 .auth_hostkey_check = sshauth_hostkey_check,
82 .auth_password = sshauth_password,
83 .auth_interactive = sshauth_interactive,
84 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020085 },
Radek Krejci62aa0642017-05-25 16:33:49 +020086#endif /* NC_ENABLED_SSH */
87 /* .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
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200113#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400114 int i;
115 for (i = 0; i < c->opts.ch_bind_count; ++i) {
116 close(c->opts.ch_binds[i].sock);
117 free((char *)c->opts.ch_binds[i].address);
118 }
119 free(c->opts.ch_binds);
120 c->opts.ch_binds = NULL;
121 c->opts.ch_bind_count = 0;
Radek Krejci62aa0642017-05-25 16:33:49 +0200122#endif
123#ifdef NC_ENABLED_SSH
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);
Radek Krejci62aa0642017-05-25 16:33:49 +0200126#endif
127#ifdef NC_ENABLED_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400128 _nc_client_tls_destroy_opts(&c->tls_opts);
129 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200130#endif
131 free(c);
132 }
133}
134
135static void
136nc_client_context_createkey(void)
137{
138 int r;
139
140 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200141 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200142 pthread_setspecific(nc_client_context_key, NULL);
143}
144
145struct nc_client_context *
146nc_client_context_location(void)
147{
148 struct nc_client_context *e;
149
150 pthread_once(&nc_client_context_once, nc_client_context_createkey);
151 e = pthread_getspecific(nc_client_context_key);
152 if (!e) {
153 /* prepare ly_err storage */
154#ifdef __linux__
155 if (getpid() == syscall(SYS_gettid)) {
156 /* main thread - use global variable instead of thread-specific variable. */
157 e = &context_main;
158 } else
159#endif /* __linux__ */
160 {
161 e = calloc(1, sizeof *e);
162 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200163 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200164#ifdef NC_ENABLED_SSH
165 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
roman41a11e42022-06-22 09:27:08 +0200166 e->ssh_opts.auth_pref[0].value = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200167 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
168 e->ssh_opts.auth_pref[1].value = 2;
169 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
roman41a11e42022-06-22 09:27:08 +0200170 e->ssh_opts.auth_pref[2].value = 3;
Radek Krejci62aa0642017-05-25 16:33:49 +0200171 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
172 e->ssh_opts.auth_password = sshauth_password;
173 e->ssh_opts.auth_interactive = sshauth_interactive;
174 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
175
roman41a11e42022-06-22 09:27:08 +0200176 /* callhome settings are the same */
Radek Krejci62aa0642017-05-25 16:33:49 +0200177 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
178 e->ssh_ch_opts.auth_pref[0].value = 1;
179 e->ssh_ch_opts.auth_pref[1].value = 2;
180 e->ssh_ch_opts.auth_pref[2].value = 3;
181#endif /* NC_ENABLED_SSH */
182 }
183 pthread_setspecific(nc_client_context_key, e);
184 }
185
186 return e;
187}
188
189#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100190
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200191API void *
192nc_client_get_thread_context(void)
193{
194 return nc_client_context_location();
195}
196
197API void
198nc_client_set_thread_context(void *context)
199{
200 struct nc_client_context *old, *new;
201
202 if (!context) {
203 ERRARG(context);
204 return;
205 }
206
207 new = (struct nc_client_context *)context;
208 old = nc_client_context_location();
209 if (old == new) {
210 /* nothing to change */
211 return;
212 }
213
214 /* replace old by new, increase reference counter in the newly set context */
215 nc_client_context_free(old);
216 new->refcount++;
217 pthread_setspecific(nc_client_context_key, new);
218}
219
Michal Vasko78939072022-12-12 07:43:18 +0100220/**
221 * @brief Ext data callback for a context to provide schema mount data.
222 */
223static LY_ERR
224nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
225{
226 struct nc_session *session = user_data;
227
228 if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
229 return LY_EINVAL;
230 }
231
232 if (!session->opts.client.ext_data) {
233 ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server.");
234 return LY_ENOTFOUND;
235 }
236
237 /* return ext data */
238 *ext_data = session->opts.client.ext_data;
239 *ext_data_free = 0;
240
241 return LY_SUCCESS;
242}
243
Radek Krejcifd5b6682017-06-13 15:52:53 +0200244int
Michal Vasko78939072022-12-12 07:43:18 +0100245nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200246{
247 /* assign context (dicionary needed for handshake) */
248 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100249 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200250 return EXIT_FAILURE;
251 }
252
Michal Vasko5ca5d972022-09-14 13:51:31 +0200253 /* user path must be first, the first path is used to store modules retreived via get-schema */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200254 if (client_opts.schema_searchpath) {
255 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko5d0cdc32022-12-12 07:38:21 +0100256 }
Michal Vasko80227222022-12-12 09:05:24 +0100257 if (!access(NC_CLIENT_SEARCH_DIR, F_OK)) {
258 ly_ctx_set_searchdir(ctx, NC_CLIENT_SEARCH_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200259 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200260
Michal Vasko5ca5d972022-09-14 13:51:31 +0200261 /* set callback for getting modules, if provided */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200262 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
Michal Vasko78939072022-12-12 07:43:18 +0100263
264 /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */
265 ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200266 } else {
267 session->flags |= NC_SESSION_SHAREDCTX;
268 }
269
270 session->ctx = ctx;
271
272 return EXIT_SUCCESS;
273}
274
Michal Vasko086311b2016-01-08 09:53:11 +0100275API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100276nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100277{
Michal Vasko3031aae2016-01-27 16:07:18 +0100278 if (client_opts.schema_searchpath) {
279 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100280 }
Michal Vasko086311b2016-01-08 09:53:11 +0100281
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100282 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100283 client_opts.schema_searchpath = strdup(path);
284 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100285 ERRMEM;
286 return 1;
287 }
288 } 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 Vasko5ca5d972022-09-14 13:51:31 +0200323struct module_info {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200324 char *name;
325 char *revision;
Michal Vasko6ce8e822022-08-02 15:09:17 +0200326
Radek Krejci65ef6d52018-08-16 16:35:02 +0200327 struct {
328 char *name;
329 char *revision;
330 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100331 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200332 int implemented;
333};
334
335struct clb_data_s {
336 void *user_data;
337 ly_module_imp_clb user_clb;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200338 struct module_info *modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200339 struct nc_session *session;
340 int has_get_schema;
341};
342
Michal Vaskoc088f982021-10-05 12:23:07 +0200343/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200344 * @brief Retrieve YANG module content from a local file.
Michal Vaskoc088f982021-10-05 12:23:07 +0200345 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200346 * @param[in] name Module name.
347 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200348 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200349 * @param[out] format Module format.
350 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200351 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200352static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200353retrieve_module_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200354 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100355{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200356 char *localfile = NULL;
357 FILE *f;
358 long length, l;
359 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100360
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200361 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200362 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
363 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200364 return NULL;
365 }
366 if (localfile) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200367 VRB(clb_data->session, "Reading module \"%s@%s\" from local file \"%s\".", name, rev ? rev : "<latest>",
Michal Vasko7502b602022-08-26 11:51:17 +0200368 localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200369 f = fopen(localfile, "r");
370 if (!f) {
Michal Vasko7502b602022-08-26 11:51:17 +0200371 ERR(clb_data->session, "Unable to open file \"%s\" (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200372 free(localfile);
373 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100374 }
Michal Vasko086311b2016-01-08 09:53:11 +0100375
Radek Krejci65ef6d52018-08-16 16:35:02 +0200376 fseek(f, 0, SEEK_END);
377 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200378 if (length < 0) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200379 ERR(clb_data->session, "Unable to get the size of module file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200380 free(localfile);
381 fclose(f);
382 return NULL;
383 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200384 fseek(f, 0, SEEK_SET);
385
386 model_data = malloc(length + 1);
387 if (!model_data) {
388 ERRMEM;
389 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200390 ERR(clb_data->session, "Reading module from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
Michal Vasko05532772021-06-03 12:12:38 +0200391 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200392 free(model_data);
393 model_data = NULL;
394 } else {
395 /* terminating NULL byte */
396 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100397 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200398 fclose(f);
399 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100400 }
401
Radek Krejci65ef6d52018-08-16 16:35:02 +0200402 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100403}
404
Michal Vaskoc088f982021-10-05 12:23:07 +0200405/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200406 * @brief Retrieve YANG module content from a reply to get-schema RPC.
Michal Vaskoc088f982021-10-05 12:23:07 +0200407 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200408 * @param[in] name Module name.
409 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200410 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200411 * @param[out] format Module format.
412 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200413 */
Michal Vasko086311b2016-01-08 09:53:11 +0100414static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200415retrieve_module_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200416 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100417{
Michal Vasko086311b2016-01-08 09:53:11 +0100418 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100419 struct lyd_node *envp = NULL, *op = NULL;
420 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100421 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100422 uint64_t msgid;
Michal Vasko7502b602022-08-26 11:51:17 +0200423 char *localfile = NULL, *envp_str = NULL, *model_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200424 FILE *f;
Michal Vasko086311b2016-01-08 09:53:11 +0100425
Michal Vasko5ca5d972022-09-14 13:51:31 +0200426 VRB(clb_data->session, "Reading module \"%s@%s\" from server via get-schema.", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200427 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100428
Radek Krejci65ef6d52018-08-16 16:35:02 +0200429 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100430 usleep(1000);
431 }
432 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200433 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100434 nc_rpc_free(rpc);
435 return NULL;
436 }
437
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200438 do {
Michal Vasko77367452021-02-16 16:32:18 +0100439 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200440 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100441 nc_rpc_free(rpc);
442 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200443 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100444 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100445 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200446 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100447 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100448 } else if (!op) {
Michal Vasko7502b602022-08-26 11:51:17 +0200449 assert(envp);
450 lyd_print_mem(&envp_str, envp, LYD_XML, 0);
451 WRN(clb_data->session, "Received an unexpected reply to <get-schema>:\n%s", envp_str);
452 free(envp_str);
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100453 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100454 }
455
Michal Vasko77367452021-02-16 16:32:18 +0100456 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200457 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100458 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200459 }
Michal Vasko77367452021-02-16 16:32:18 +0100460 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200461 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200462 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100463 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200464 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200465 break;
466 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100467 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200468 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200469 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200470 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200471 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100472 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200473 }
Michal Vasko086311b2016-01-08 09:53:11 +0100474
Radek Krejci488b0702018-08-29 14:47:15 +0200475 if (model_data && !model_data[0]) {
476 /* empty data */
477 free(model_data);
478 model_data = NULL;
479 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100480 if (!model_data) {
481 goto cleanup;
482 }
483
484 /* set format */
485 *format = LYS_IN_YANG;
Radek Krejci488b0702018-08-29 14:47:15 +0200486
Michal Vasko5ca5d972022-09-14 13:51:31 +0200487 /* try to store the model_data into local module repository */
Michal Vaskoa1721172022-12-12 09:06:53 +0100488 lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx), 0, name, rev, &localfile, NULL);
489 if (client_opts.schema_searchpath && !localfile) {
490 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
491 rev ? rev : "") == -1) {
492 ERRMEM;
493 } else {
494 f = fopen(localfile, "w");
495 if (!f) {
496 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of module retrieved via <get-schema> (%s).",
497 localfile, strerror(errno));
Michal Vaskof945da52018-02-15 08:45:13 +0100498 } else {
Michal Vaskoa1721172022-12-12 09:06:53 +0100499 fputs(model_data, f);
500 fclose(f);
Michal Vaskof945da52018-02-15 08:45:13 +0100501 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200502 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200503 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100504 free(localfile);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200505
Michal Vasko77367452021-02-16 16:32:18 +0100506cleanup:
507 lyd_free_tree(envp);
508 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100509 return model_data;
510}
511
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200512static void
513free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200514{
515 free(data);
516 (void)user_data;
517}
518
Michal Vaskoc088f982021-10-05 12:23:07 +0200519/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200520 * @brief Retrieve YANG module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200521 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200522 * @param[in] mod_name Module name.
523 * @param[in] mod_rev Module revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100524 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200525 * @param[out] format Module format.
526 * @param[out] module_data Module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100527 * @param[out] free_module_data Callback for freeing @p module_data.
528 * @return LY_ERR value.
529 */
530static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200531retrieve_module_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format,
Michal Vasko5e32c402022-01-12 14:05:09 +0100532 const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
533{
534 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
535 char *model_data = NULL;
536
Michal Vasko5e32c402022-01-12 14:05:09 +0100537 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200538 model_data = retrieve_module_data_localfile(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100539
540 /* 2. try to use <get-schema> */
541 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200542 model_data = retrieve_module_data_getschema(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100543 }
544
545 /* 3. try to use user callback */
546 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200547 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 +0100548 clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data,
549 free_module_data);
550 }
551
552 *free_module_data = free_with_user_data;
553 *module_data = model_data;
554 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
555}
556
557/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200558 * @brief Retrieve YANG import module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100559 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200560 * @param[in] mod_name Module name.
561 * @param[in] mod_rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200562 * @param[in] submod_name Optional submodule name.
563 * @param[in] sub_rev Submodule revision.
564 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200565 * @param[out] format Module format.
566 * @param[out] module_data Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200567 * @param[out] free_module_data Callback for freeing @p module_data.
568 * @return LY_ERR value.
569 */
Michal Vasko77367452021-02-16 16:32:18 +0100570static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200571retrieve_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 +0100572 void *user_data, LYS_INFORMAT *format, const char **module_data,
573 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200574{
575 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
Michal Vasko5e32c402022-01-12 14:05:09 +0100576 uint32_t u, v, match = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200577 const char *name = NULL, *rev = NULL;
578 char *model_data = NULL;
579
Michal Vasko5ca5d972022-09-14 13:51:31 +0200580 /* get and check the final name and revision of the module to be retrieved */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200581 if (!mod_rev || !mod_rev[0]) {
582 /* newest revision requested - get the newest revision from the list of available modules on server */
583 match = 0;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200584 for (u = 0; clb_data->modules[u].name; ++u) {
585 if (strcmp(mod_name, clb_data->modules[u].name)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200586 continue;
587 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200588 if (!match || (strcmp(mod_rev, clb_data->modules[u].revision) > 0)) {
589 mod_rev = clb_data->modules[u].revision;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200590 }
591 match = u + 1;
592 }
593 if (!match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200594 /* valid situation if we are retrieving YANG 1.1 module and have only capabilities for now
Michal Vasko1d2665c2021-02-12 09:28:44 +0100595 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200596 VRB(clb_data->session, "Unable to identify revision of the import module \"%s\" from "
Michal Vasko05532772021-06-03 12:12:38 +0200597 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200598 }
599 }
600 if (submod_name) {
601 name = submod_name;
602 if (sub_rev) {
603 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200604 } else if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200605 if (!clb_data->modules[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200606 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
Michal Vasko5ca5d972022-09-14 13:51:31 +0200607 "in import module \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200608 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200609 for (v = 0; clb_data->modules[match - 1].submodules[v].name; ++v) {
610 if (!strcmp(submod_name, clb_data->modules[match - 1].submodules[v].name)) {
611 rev = sub_rev = clb_data->modules[match - 1].submodules[v].revision;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200612 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200613 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200614 if (!rev) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200615 ERR(clb_data->session, "Requested submodule \"%s\" is not found in import module \"%s\" on server side.",
Michal Vasko05532772021-06-03 12:12:38 +0200616 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100617 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200618 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200619 }
620 }
621 } else {
622 name = mod_name;
623 rev = mod_rev;
624 }
625
Radek Krejci65ef6d52018-08-16 16:35:02 +0200626 if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200627 /* we have enough information to avoid communication with server and try to get the module locally */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200628
629 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200630 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200631
632 /* 2. try to use <get-schema> */
633 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200634 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200635 }
636 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200637 /* we are unsure which revision of the module we should load, so first try to get
Radek Krejci65ef6d52018-08-16 16:35:02 +0200638 * the newest revision from the server via get-schema and only if the server does not
639 * implement get-schema, try to load the newest revision locally. This is imperfect
640 * solution, but there are situation when a client does not know what revision is
641 * actually implemented by the server. */
642
643 /* 1. try to use <get-schema> */
644 if (clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200645 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200646 }
647
648 /* 2. try to get data locally */
649 if (!model_data) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200650 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200651 }
652 }
653
654 /* 3. try to use user callback */
655 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200656 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", name, rev ? rev : "<latest>");
Michal Vasko77367452021-02-16 16:32:18 +0100657 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
658 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200659 }
660
Jan Kundrát35972df2018-09-06 19:00:01 +0200661 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100662 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200663 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200664}
665
Michal Vaskoc088f982021-10-05 12:23:07 +0200666/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200667 * @brief Load a YANG module into context.
Michal Vaskoc088f982021-10-05 12:23:07 +0200668 *
669 * @param[in] session NC session.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200670 * @param[in] name Module name.
671 * @param[in] revision Module revision.
672 * @param[in] features Enabled module features.
673 * @param[in] modules Server module info built from capabilities.
674 * @param[in] user_clb User callback for retrieving module data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200675 * @param[in] user_data User data for @p user_clb.
676 * @param[in] has_get_schema Whether the server supports get-schema.
677 * @param[out] mod Loaded module.
678 * @return 0 on success.
679 * @return -1 on error.
680 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100681static int
Michal Vasko5e32c402022-01-12 14:05:09 +0100682nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features,
Michal Vasko5ca5d972022-09-14 13:51:31 +0200683 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 +0100684{
685 int ret = 0;
686 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200687 const char *module_data = NULL;
Michal Vasko5e32c402022-01-12 14:05:09 +0100688 struct ly_in *in;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200689 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200690
691 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200692 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100693
Michal Vasko5e32c402022-01-12 14:05:09 +0100694 /* try to use a module from the context */
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200695 *mod = ly_ctx_get_module_implemented(session->ctx, name);
696 if (!*mod) {
697 if (revision) {
698 *mod = ly_ctx_get_module(session->ctx, name, revision);
699 } else {
Michal Vasko5e32c402022-01-12 14:05:09 +0100700 *mod = ly_ctx_get_module_latest(session->ctx, name);
701 }
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200702 } else if (revision && (!(*mod)->revision || strcmp((*mod)->revision, revision))) {
703 WRN(session, "Server implements module \"%s\" in revision \"%s\" but revision \"%s\" is already implemented"
704 " and will be used instead.", name, revision, (*mod)->revision ? (*mod)->revision : "<none>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200705 }
Michal Vasko5e32c402022-01-12 14:05:09 +0100706
Michal Vaskoceae0152018-02-14 16:03:59 +0100707 if (*mod) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100708 /* make the present module implemented and/or enable all its features */
709 if (lys_set_implemented(*mod, features)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200710 ERR(session, "Failed to implement module \"%s\".", (*mod)->name);
Michal Vasko5e32c402022-01-12 14:05:09 +0100711 ret = -1;
Michal Vaskoceae0152018-02-14 16:03:59 +0100712 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200713 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100714 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200715 clb_data.has_get_schema = has_get_schema;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200716 clb_data.modules = modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200717 clb_data.session = session;
718 clb_data.user_clb = user_clb;
719 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100720
721 /* clear all the errors and just collect them for now */
722 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200723 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100724
Radek Krejci65ef6d52018-08-16 16:35:02 +0200725 /* get module data */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200726 retrieve_module_data(name, revision, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100727
Radek Krejci65ef6d52018-08-16 16:35:02 +0200728 if (module_data) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100729 /* set import callback */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200730 ly_ctx_set_module_imp_clb(session->ctx, retrieve_module_data_imp, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100731
Michal Vasko5ca5d972022-09-14 13:51:31 +0200732 /* parse the module */
Michal Vasko5e32c402022-01-12 14:05:09 +0100733 ly_in_new_memory(module_data, &in);
734 lys_parse(session->ctx, in, format, features, mod);
735 ly_in_free(in, 0);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200736 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100737 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100738 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100739
Radek Krejci65ef6d52018-08-16 16:35:02 +0200740 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
741 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100742
Michal Vaskodbf7c272018-02-19 09:07:59 +0100743 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200744 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100745 if (!(*mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100746 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100747 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100748 }
749 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200750 } else {
751 /* print only warnings */
Michal Vasko5e32c402022-01-12 14:05:09 +0100752 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200753 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100754 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200755 }
756 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100757 }
758
Michal Vaskodbf7c272018-02-19 09:07:59 +0100759 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100760 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100761 }
762
763 return ret;
764}
765
Radek Krejci65ef6d52018-08-16 16:35:02 +0200766static void
Michal Vasko5ca5d972022-09-14 13:51:31 +0200767free_module_info(struct module_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200768{
Michal Vasko77367452021-02-16 16:32:18 +0100769 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200770
Radek Krejci65ef6d52018-08-16 16:35:02 +0200771 if (!list) {
772 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200773 }
774
Radek Krejci65ef6d52018-08-16 16:35:02 +0200775 for (u = 0; list[u].name; ++u) {
776 free(list[u].name);
777 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100778 if (list[u].features) {
779 for (v = 0; list[u].features[v]; ++v) {
780 free(list[u].features[v]);
781 }
782 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200783 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200784 if (list[u].submodules) {
785 for (v = 0; list[u].submodules[v].name; ++v) {
786 free(list[u].submodules[v].name);
787 free(list[u].submodules[v].revision);
788 }
789 free(list[u].submodules);
790 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200791 }
792 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200793}
794
Michal Vaskoc088f982021-10-05 12:23:07 +0200795/**
Michal Vasko78939072022-12-12 07:43:18 +0100796 * @brief Retrieve yang-library and schema-mounts operational data from the server.
Michal Vaskoc088f982021-10-05 12:23:07 +0200797 *
798 * @param[in] session NC session.
799 * @param[in] has_get_data Whether get-data RPC is available or only get.
Michal Vasko78939072022-12-12 07:43:18 +0100800 * @param[in] filter Filter to use.
801 * @param[out] oper_data Received data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200802 * @return 0 on success.
803 * @return -1 on error.
804 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200805static int
Michal Vasko78939072022-12-12 07:43:18 +0100806get_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 +0200807{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200808 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100809 struct lyd_node *op = NULL, *envp = NULL;
810 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200811 NC_MSG_TYPE msg;
812 uint64_t msgid;
Michal Vasko303263d2021-10-05 12:18:21 +0200813 int ret = 0;
814 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200815
Michal Vasko78939072022-12-12 07:43:18 +0100816 /* get data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200817 if (has_get_data) {
Michal Vasko78939072022-12-12 07:43:18 +0100818 rpc_name = "<get-data>";
819 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 +0200820 } else {
Michal Vasko78939072022-12-12 07:43:18 +0100821 rpc_name = "<get>";
822 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200823 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200824 if (!rpc) {
825 goto cleanup;
826 }
827
828 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
829 usleep(1000);
830 }
831 if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100832 WRN(session, "Failed to send %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200833 goto cleanup;
834 }
835
836 do {
Michal Vasko77367452021-02-16 16:32:18 +0100837 lyd_free_tree(envp);
838 lyd_free_tree(op);
839
840 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200841 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200842 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko78939072022-12-12 07:43:18 +0100843 WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200844 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100845 } else if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100846 WRN(session, "Failed to receive a reply to %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200847 goto cleanup;
Jan Kundrát1d73a5a2022-03-30 16:54:49 +0200848 } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko78939072022-12-12 07:43:18 +0100849 WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200850 goto cleanup;
851 }
852
Michal Vasko77367452021-02-16 16:32:18 +0100853 data = (struct lyd_node_any *)lyd_child(op);
854 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko78939072022-12-12 07:43:18 +0100855 WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200856 goto cleanup;
857 } else if (!data->value.tree) {
Michal Vasko78939072022-12-12 07:43:18 +0100858 WRN(session, "No data in reply to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200859 goto cleanup;
860 }
861
Michal Vasko78939072022-12-12 07:43:18 +0100862 *oper_data = data->value.tree;
863 data->value.tree = NULL;
864
865cleanup:
866 nc_rpc_free(rpc);
867 lyd_free_tree(envp);
868 lyd_free_tree(op);
869
870 if (session->status != NC_STATUS_RUNNING) {
871 /* something bad happened, discard the session */
872 ERR(session, "Invalid session, discarding.");
873 ret = -1;
874 }
875
876 return ret;
877}
878
879/**
880 * @brief Build server module info from ietf-yang-library data.
881 *
882 * @param[in] session NC session.
883 * @param[in] get_data_sup Whether get-data RPC is available or only get.
884 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
885 * @param[out] result Server modules.
886 * @return 0 on success.
887 * @return -1 on error.
888 */
889static int
890build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result)
891{
892 struct ly_set *modules = NULL;
893 uint32_t u, v, submodules_count, feature_count;
894 struct lyd_node *iter, *child, *oper_data = NULL;
895 struct lys_module *mod;
896 int ret = 0;
897
898 /* get yang-library operational data */
899 if (xpath_sup) {
900 if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) {
901 goto cleanup;
902 }
903 } else {
904 if (get_oper_data(session, get_data_sup,
905 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", &oper_data)) {
906 goto cleanup;
907 }
908 }
909 if (!oper_data) {
910 goto cleanup;
911 }
912
913 if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) {
914 WRN(NULL, "No yang-library module information found.");
Radek Krejci2d088832018-08-21 13:40:14 +0200915 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200916 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200917
Michal Vasko77367452021-02-16 16:32:18 +0100918 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200919 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200920 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200921 ret = -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200922 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200923 }
924
Michal Vasko77367452021-02-16 16:32:18 +0100925 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200926 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100927 feature_count = 0;
928 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
929 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200930 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200931 /* ignore node from other schemas (augments) */
932 continue;
933 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200934 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200935 /* ignore empty nodes */
936 continue;
937 }
938 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200939 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200940 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200941 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200942 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200943 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200944 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100945 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
946 if (!(*result)[u].features) {
947 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200948 free_module_info(*result);
Michal Vasko77367452021-02-16 16:32:18 +0100949 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200950 ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100951 goto cleanup;
952 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200953 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100954 (*result)[u].features[feature_count + 1] = NULL;
955 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200956 } else if (!strcmp(iter->schema->name, "submodule")) {
957 submodules_count++;
958 }
959 }
960
961 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200962 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
963 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200964 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200965 free_module_info(*result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200966 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200967 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200968 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200969 } else {
970 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100971 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
972 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200973 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100974 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200975 if (mod != child->schema->module) {
976 continue;
977 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200978 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200979 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200980 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200981 }
982 }
983 }
984 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200985 }
986 }
987 }
988
Radek Krejcifd5b6682017-06-13 15:52:53 +0200989cleanup:
Michal Vasko78939072022-12-12 07:43:18 +0100990 lyd_free_siblings(oper_data);
Michal Vasko77367452021-02-16 16:32:18 +0100991 ly_set_free(modules, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200992 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200993}
994
Michal Vaskoc088f982021-10-05 12:23:07 +0200995/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200996 * @brief Build server module info from received capabilities.
Michal Vaskoc088f982021-10-05 12:23:07 +0200997 *
998 * @param[in] cpblts Server capabilities.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200999 * @param[out] result Server modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001000 * @return 0 on success.
1001 * @return -1 on error.
1002 */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001003static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001004build_module_info_cpblts(char **cpblts, struct module_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001005{
Michal Vasko77367452021-02-16 16:32:18 +01001006 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001007 char *module_cpblt, *ptr, *ptr2;
1008
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001009 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +02001010 (*result) = calloc(u + 1, sizeof **result);
1011 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +02001012 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +02001013 return -1;
Radek Krejcic9390502018-08-17 10:23:13 +02001014 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001015
1016 for (u = v = 0; cpblts[u]; ++u) {
1017 module_cpblt = strstr(cpblts[u], "module=");
1018 /* this capability requires a module */
1019 if (!module_cpblt) {
1020 continue;
1021 }
1022
1023 /* get module's name */
1024 ptr = (char *)module_cpblt + 7;
1025 ptr2 = strchr(ptr, '&');
1026 if (!ptr2) {
1027 ptr2 = ptr + strlen(ptr);
1028 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001029 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001030
1031 /* get module's revision */
1032 ptr = strstr(module_cpblt, "revision=");
1033 if (ptr) {
1034 ptr += 9;
1035 ptr2 = strchr(ptr, '&');
1036 if (!ptr2) {
1037 ptr2 = ptr + strlen(ptr);
1038 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001039 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001040 }
1041
1042 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001043 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001044
1045 /* get module's features */
1046 ptr = strstr(module_cpblt, "features=");
1047 if (ptr) {
1048 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +01001049 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001050 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
1051 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +01001052 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1053 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1054 (*result)[v].features[feature_count + 1] = NULL;
1055 ++feature_count;
1056
Radek Krejci65ef6d52018-08-16 16:35:02 +02001057 ptr2 = ptr + 1;
1058 }
1059 }
1060 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +01001061 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1062 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1063 (*result)[v].features[feature_count + 1] = NULL;
1064 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001065 }
1066 ++v;
1067 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001068
Michal Vasko303263d2021-10-05 12:18:21 +02001069 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001070}
1071
Michal Vaskoc088f982021-10-05 12:23:07 +02001072/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001073 * @brief Fill client context based on server modules info.
Michal Vaskoc088f982021-10-05 12:23:07 +02001074 *
1075 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001076 * @param[in] modules Server modules info.
1077 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001078 * @param[in] user_data User data for @p user_clb.
1079 * @param[in] has_get_schema Whether server supports get-schema RPC.
1080 * @return 0 on success.
1081 * @return -1 on error.
1082 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001083static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001084nc_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 +01001085 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001086{
Michal Vasko303263d2021-10-05 12:18:21 +02001087 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +01001088 struct lys_module *mod;
1089 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001090
1091 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +02001092 /* skip import-only modules */
1093 if (!modules[u].implemented) {
1094 continue;
1095 }
1096
Radek Krejci65ef6d52018-08-16 16:35:02 +02001097 /* we can continue even if it fails */
Michal Vasko5e32c402022-01-12 14:05:09 +01001098 nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules,
1099 user_clb, user_data, has_get_schema, &mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001100
1101 if (!mod) {
1102 if (session->status != NC_STATUS_RUNNING) {
1103 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +02001104 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001105 goto cleanup;
1106 }
1107
Michal Vasko5ca5d972022-09-14 13:51:31 +02001108 /* all loading ways failed, the module will be ignored in the received data */
1109 WRN(session, "Failed to load module \"%s@%s\".", modules[u].name, modules[u].revision ?
Michal Vasko05532772021-06-03 12:12:38 +02001110 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001111 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001112 }
Michal Vasko60f66602017-10-17 13:52:18 +02001113 }
1114
Michal Vasko77367452021-02-16 16:32:18 +01001115 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001116 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001117
1118cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001119 return ret;
1120}
1121
Michal Vaskoc088f982021-10-05 12:23:07 +02001122/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001123 * @brief Fill client context with ietf-netconf module.
Michal Vaskoc088f982021-10-05 12:23:07 +02001124 *
1125 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001126 * @param[in] modules Server module info.
1127 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001128 * @param[in] user_data User data for @p user_clb.
1129 * @param[in] has_get_schema Whether server supports get-schema RPC.
1130 * @return 0 on success.
1131 * @return -1 on error.
1132 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001133static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001134nc_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 +01001135 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001136{
Michal Vasko77367452021-02-16 16:32:18 +01001137 uint32_t u;
Michal Vasko5e32c402022-01-12 14:05:09 +01001138 const char **features = NULL;
1139 struct ly_in *in;
Michal Vasko77367452021-02-16 16:32:18 +01001140 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001141
Michal Vasko5e32c402022-01-12 14:05:09 +01001142 /* find supported features (capabilities) in ietf-netconf */
1143 for (u = 0; modules[u].name; ++u) {
1144 if (!strcmp(modules[u].name, "ietf-netconf")) {
1145 assert(modules[u].implemented);
1146 features = (const char **)modules[u].features;
1147 break;
1148 }
1149 }
1150 if (!modules[u].name) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001151 ERR(session, "Base NETCONF module not supported by the server.");
Michal Vasko5e32c402022-01-12 14:05:09 +01001152 return -1;
1153 }
1154
Michal Vasko77367452021-02-16 16:32:18 +01001155 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vasko5e32c402022-01-12 14:05:09 +01001156 if (ietfnc) {
1157 /* make sure to enable all the features if already loaded */
1158 lys_set_implemented(ietfnc, features);
1159 } else {
1160 /* load the module */
1161 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 +02001162 if (!ietfnc) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001163 ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in);
1164 lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc);
1165 ly_in_free(in, 0);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001166 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001167 }
1168 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001169 ERR(session, "Loading base NETCONF module failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001170 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001171 }
1172
Radek Krejci65ef6d52018-08-16 16:35:02 +02001173 return 0;
1174}
1175
Michal Vasko78939072022-12-12 07:43:18 +01001176/**
1177 * @brief Set client session context to support schema-mount if possible.
1178 *
1179 * @param[in] session NC session with the context to modify.
1180 * @param[in] get_data_sup Whether get-data RPC is available or only get.
1181 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
1182 * @return 0 on success.
1183 * @return -1 on error.
1184 */
1185static int
1186nc_ctx_schema_mount(struct nc_session *session, int get_data_sup, int xpath_sup)
1187{
1188 int rc = 0;
1189 struct lyd_node *oper_data = NULL;
1190
1191 if (session->flags & NC_SESSION_SHAREDCTX) {
1192 /* context is already fully set up */
1193 goto cleanup;
1194 }
1195
1196 /* get yang-library and schema-mounts operational data */
1197 if (xpath_sup) {
1198 if ((rc = get_oper_data(session, get_data_sup, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) {
1199 goto cleanup;
1200 }
1201 } else {
1202 if ((rc = get_oper_data(session, get_data_sup,
1203 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>"
1204 "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"/>", &oper_data))) {
1205 goto cleanup;
1206 }
1207 }
1208
1209 if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) {
1210 /* no schema-mounts operational data */
1211 goto cleanup;
1212 }
1213
1214 /* validate the data for the parent reference prefixes to be resolved */
1215 if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) {
1216 ERR(session, "Invalid operational data received from the server (%s).", ly_errmsg(LYD_CTX(oper_data)));
1217 rc = -1;
1218 goto cleanup;
1219 }
1220
1221 /* store the data in the session */
1222 session->opts.client.ext_data = oper_data;
1223 oper_data = NULL;
1224
1225cleanup:
1226 lyd_free_siblings(oper_data);
1227 return rc;
1228}
1229
Michal Vasko086311b2016-01-08 09:53:11 +01001230int
1231nc_ctx_check_and_fill(struct nc_session *session)
1232{
Michal Vaskod6c6f852022-12-14 15:37:21 +01001233 int i, get_schema_support = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001234 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001235 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001236 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001237 char *revision;
Michal Vasko5ca5d972022-09-14 13:51:31 +02001238 struct module_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001239
Michal Vasko2e6defd2016-10-07 15:48:15 +02001240 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001241
Radek Krejci65ef6d52018-08-16 16:35:02 +02001242 /* 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 +02001243 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001244
Radek Krejci65ef6d52018-08-16 16:35:02 +02001245 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
Michal Vasko5ca5d972022-09-14 13:51:31 +02001246 * modules only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001247 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001248
1249 /* our callback is set later with appropriate data */
1250 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1251
1252 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001253 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001254 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001255 get_schema_support = 1 + i;
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001256 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001257 yanglib_support = 1 + i;
Michal Vasko78939072022-12-12 07:43:18 +01001258 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) {
1259 xpath_support = 1 + i;
Michal Vaskod6c6f852022-12-14 15:37:21 +01001260 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda", 45)) {
1261 nmda_support = 1 + i;
Michal Vasko086311b2016-01-08 09:53:11 +01001262 }
1263 }
Michal Vaskod6c6f852022-12-14 15:37:21 +01001264 VRB(session, "Capability for <get-schema> support%s found.", get_schema_support ? "" : " not");
1265 VRB(session, "Capability for yang-library support%s found.", yanglib_support ? "" : " not");
1266 VRB(session, "Capability for XPath filter support%s found.", xpath_support ? "" : " not");
1267 VRB(session, "Capability for NMDA RPCs support%s found.", nmda_support ? "" : " not");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001268
Michal Vasko5ca5d972022-09-14 13:51:31 +02001269 /* get information about server's modules from capabilities list until we will have yang-library */
1270 if (build_module_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1271 ERR(session, "Unable to get server module information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001272 goto cleanup;
1273 }
1274
Michal Vasko086311b2016-01-08 09:53:11 +01001275 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001276 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 +02001277 WRN(session, "Loading NETCONF monitoring module failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001278 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001279 }
1280
1281 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001282 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001283 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001284 }
1285
Radek Krejci65ef6d52018-08-16 16:35:02 +02001286 /* get correct version of ietf-yang-library into context */
1287 if (yanglib_support) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001288 /* use get-schema to get server's ietf-yang-library */
Radek Krejcib1d250e2018-04-12 13:54:18 +02001289 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1290 if (!revision) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001291 WRN(session, "Loading NETCONF ietf-yang-library module failed, missing revision in NETCONF <hello> message.");
Michal Vasko05532772021-06-03 12:12:38 +02001292 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001293 yanglib_support = 0;
1294 } else {
1295 revision = strndup(&revision[9], 10);
Michal Vasko5e32c402022-01-12 14:05:09 +01001296 if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001297 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001298 WRN(session, "Loading NETCONF ietf-yang-library module failed, unable to use it to learn all "
Michal Vasko05532772021-06-03 12:12:38 +02001299 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001300 yanglib_support = 0;
1301 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001302 if (strcmp(revision, "2019-01-04") >= 0) {
1303 /* we also need ietf-datastores to be implemented */
Michal Vasko5e32c402022-01-12 14:05:09 +01001304 if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001305 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001306 WRN(session, "Loading NETCONF ietf-datastores module failed, unable to use yang-library "
Michal Vasko05532772021-06-03 12:12:38 +02001307 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001308 yanglib_support = 0;
1309 }
1310 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001311 free(revision);
1312 }
1313 }
1314
Michal Vaskod6c6f852022-12-14 15:37:21 +01001315 /* ietf-netconf-nmda is needed to issue get-data */
1316 if (nmda_support && nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb, old_data,
1317 get_schema_support, &mod)) {
1318 WRN(session, "Loading NMDA module failed, unable to use <get-data>.");
1319 nmda_support = 0;
1320 }
1321
Michal Vasko5ca5d972022-09-14 13:51:31 +02001322 /* prepare structured information about server's modules */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001323 if (yanglib_support) {
Michal Vaskod6c6f852022-12-14 15:37:21 +01001324 if (build_module_info_yl(session, nmda_support, xpath_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001325 goto cleanup;
1326 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001327 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001328 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001329 /* prefer yang-library information, currently we have it from capabilities used for getting correct
Michal Vasko5ca5d972022-09-14 13:51:31 +02001330 * yang-library module */
1331 free_module_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001332 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001333 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001334 }
Michal Vaskoef578332016-01-25 13:20:09 +01001335
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001336 /* compile all modules at once to avoid invalid errors or warnings */
1337 ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
1338
1339 /* fill the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001340 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1341 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001342 }
1343
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001344 /* compile it */
1345 if (ly_ctx_compile(session->ctx)) {
1346 goto cleanup;
1347 }
1348
Michal Vasko78939072022-12-12 07:43:18 +01001349 /* set support for schema-mount, if possible */
Michal Vaskod6c6f852022-12-14 15:37:21 +01001350 if (nc_ctx_schema_mount(session, nmda_support, xpath_support)) {
Michal Vasko78939072022-12-12 07:43:18 +01001351 goto cleanup;
1352 }
1353
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001354 /* success */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001355 ret = 0;
1356
Michal Vaskoeee99412016-11-21 10:19:43 +01001357 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001358 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 +02001359 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001360 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001361
1362cleanup:
Michal Vasko5ca5d972022-09-14 13:51:31 +02001363 free_module_info(server_modules);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001364
Radek Krejcifd5b6682017-06-13 15:52:53 +02001365 /* set user callback back */
1366 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001367 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001368 ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001369
Michal Vaskoef578332016-01-25 13:20:09 +01001370 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001371}
1372
1373API struct nc_session *
1374nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1375{
Michal Vaskod083db62016-01-19 10:31:29 +01001376 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001377
Michal Vasko45e53ae2016-04-07 11:46:03 +02001378 if (fdin < 0) {
1379 ERRARG("fdin");
1380 return NULL;
1381 } else if (fdout < 0) {
1382 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001383 return NULL;
1384 }
1385
1386 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001387 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001388 if (!session) {
1389 ERRMEM;
1390 return NULL;
1391 }
1392 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001393
1394 /* transport specific data */
1395 session->ti_type = NC_TI_FD;
1396 session->ti.fd.in = fdin;
1397 session->ti.fd.out = fdout;
1398
Michal Vasko78939072022-12-12 07:43:18 +01001399 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001400 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001401 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001402 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001403
1404 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001405 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001406 goto fail;
1407 }
1408 session->status = NC_STATUS_RUNNING;
1409
Michal Vaskoef578332016-01-25 13:20:09 +01001410 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001411 goto fail;
1412 }
1413
1414 return session;
1415
1416fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001417 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001418 return NULL;
1419}
1420
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001421API struct nc_session *
1422nc_connect_unix(const char *address, struct ly_ctx *ctx)
1423{
1424 struct nc_session *session = NULL;
1425 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001426 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001427 char *username;
1428 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001429 char *buf = NULL;
1430 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001431
1432 if (address == NULL) {
1433 ERRARG("address");
1434 return NULL;
1435 }
1436
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001437 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1438 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001439 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001440 goto fail;
1441 }
1442
1443 memset(&sun, 0, sizeof(sun));
1444 sun.sun_family = AF_UNIX;
1445 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1446
1447 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001448 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001449 goto fail;
1450 }
1451
1452 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001453 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001454 goto fail;
1455 }
1456
1457 /* prepare session structure */
1458 session = nc_new_session(NC_CLIENT, 0);
1459 if (!session) {
1460 ERRMEM;
1461 goto fail;
1462 }
1463 session->status = NC_STATUS_STARTING;
1464
1465 /* transport specific data */
1466 session->ti_type = NC_TI_UNIX;
1467 session->ti.unixsock.sock = sock;
1468 sock = -1; /* do not close sock in fail label anymore */
1469
Michal Vasko78939072022-12-12 07:43:18 +01001470 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001471 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001472 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001473 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001474
Michal Vasko93224072021-11-09 12:14:28 +01001475 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001476
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001477 pw = nc_getpwuid(geteuid(), &pw_buf, &buf, &buf_size);
1478 if (!pw) {
1479 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1480 goto fail;
1481 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001482 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001483 free(buf);
1484 if (!username) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001485 ERRMEM;
1486 goto fail;
1487 }
Michal Vasko93224072021-11-09 12:14:28 +01001488 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001489
1490 /* NETCONF handshake */
1491 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1492 goto fail;
1493 }
1494 session->status = NC_STATUS_RUNNING;
1495
1496 if (nc_ctx_check_and_fill(session) == -1) {
1497 goto fail;
1498 }
1499
1500 return session;
1501
1502fail:
1503 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001504 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001505 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001506 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001507 return NULL;
1508}
1509
Michal Vasko056f53c2022-10-21 13:38:15 +02001510/**
1511 * @brief Try to connect a socket, optionally a pending one from a previous attempt.
1512 *
1513 * @param[in] timeout_ms Timeout in ms to wait for the connection to be fully established, -1 to block.
1514 * @param[in,out] sock_pending Optional previously created socked that was not fully connected yet. If provided and
1515 * connected, is set to -1.
1516 * @param[in] res Addrinfo resource to use when creating a new socket.
1517 * @param[in] ka Keepalives to set.
1518 * @return Connected socket or -1 on error.
Frank Rimpler9f838b02018-07-25 06:44:03 +00001519 */
1520static int
Michal Vasko056f53c2022-10-21 13:38:15 +02001521sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001522{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001523 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001524 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001525 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001526 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001527 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001528 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001529 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001530 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001531
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001532 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001533 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001534 sock = *sock_pending;
1535 } else {
1536 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001537 if (res->ai_family == AF_INET6) {
1538 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001539 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001540 } else {
1541 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001542 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001543 }
1544 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
Michal Vasko05532772021-06-03 12:12:38 +02001545 WRN(NULL, "inet_ntop() failed (%s).", strerror(errno));
Michal Vasko5e8d0192019-06-24 19:19:49 +02001546 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001547 VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001548 }
1549
1550 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001551 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1552 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001553 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001554 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001555 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001556 /* make the socket non-blocking */
1557 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001558 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001559 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001560 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001561 /* non-blocking connect! */
1562 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1563 if (errno != EINPROGRESS) {
1564 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001565 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001566 goto cleanup;
1567 }
1568 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001569 }
Michal Vasko7d965dc2018-03-12 14:39:23 +01001570
Frank Rimpler9f838b02018-07-25 06:44:03 +00001571 FD_ZERO(&wset);
1572 FD_SET(sock, &wset);
1573
Michal Vasko056f53c2022-10-21 13:38:15 +02001574 /* wait for some data on the socket */
1575 if (timeout_ms != -1) {
1576 ts.tv_sec = timeout_ms / 1000;
1577 ts.tv_usec = (timeout_ms % 1000) * 1000;
1578 }
1579 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout_ms != -1) ? &ts : NULL)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001580 ERR(NULL, "select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001581 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001582 }
1583
Michal Vasko5e8d0192019-06-24 19:19:49 +02001584 if (ret == 0) {
1585 /* there was a timeout */
Michal Vasko056f53c2022-10-21 13:38:15 +02001586 VRB(NULL, "Timed out after %d ms (%s).", timeout_ms, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001587 if (sock_pending) {
1588 /* no sock-close, we'll try it again */
1589 *sock_pending = sock;
1590 } else {
1591 close(sock);
1592 }
1593 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001594 }
Radek Krejci782041a2018-08-20 10:09:45 +02001595
1596 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001597 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001598 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001599 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001600 goto cleanup;
1601 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001602 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001603 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001604 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001605 errno = error;
1606 goto cleanup;
1607 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001608
1609 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001610 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001611 goto cleanup;
1612 }
1613
Michal Vasko056f53c2022-10-21 13:38:15 +02001614 /* connected */
1615 if (sock_pending) {
1616 *sock_pending = -1;
1617 }
Michal Vasko086311b2016-01-08 09:53:11 +01001618 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001619
Frank Rimpler9f838b02018-07-25 06:44:03 +00001620cleanup:
1621 if (sock_pending) {
1622 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001623 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001624 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001625 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001626}
1627
Frank Rimpler9f838b02018-07-25 06:44:03 +00001628int
Michal Vasko056f53c2022-10-21 13:38:15 +02001629nc_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 +00001630{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001631 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001632 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001633 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001634 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001635 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001636
Michal Vasko056f53c2022-10-21 13:38:15 +02001637 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout_ms, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001638
1639 /* no pending socket */
1640 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001641 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001642 snprintf(port_s, 6, "%u", port);
1643 memset(&hints, 0, sizeof hints);
1644 hints.ai_family = AF_UNSPEC;
1645 hints.ai_socktype = SOCK_STREAM;
1646 hints.ai_protocol = IPPROTO_TCP;
1647 i = getaddrinfo(host, port_s, &hints, &res_list);
1648 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001649 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001650 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001651 }
1652
1653 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vasko056f53c2022-10-21 13:38:15 +02001654 sock = sock_connect(timeout_ms, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001655 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001656 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001657 /* try the next resource */
1658 continue;
1659 } else {
1660 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001661 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001662 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001663 }
Michal Vasko05532772021-06-03 12:12:38 +02001664 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 +01001665
1666 opt = 1;
1667 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001668 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001669 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001670 }
1671
Michal Vasko66032bc2019-01-22 15:03:12 +01001672 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1673 buf = malloc(INET6_ADDRSTRLEN);
1674 if (!buf) {
1675 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001676 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001677 }
1678 if (res->ai_family == AF_INET) {
1679 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1680 } else {
1681 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1682 }
1683 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +02001684 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
Michal Vasko66032bc2019-01-22 15:03:12 +01001685 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001686 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001687 }
1688
1689 *ip_host = buf;
1690 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001691 break;
1692 }
1693 freeaddrinfo(res_list);
1694
1695 } else {
1696 /* try to get a connection with the pending socket */
1697 assert(sock_pending);
Michal Vasko056f53c2022-10-21 13:38:15 +02001698 sock = sock_connect(timeout_ms, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001699 }
1700
1701 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001702
1703error:
Michal Vasko0be85692021-03-02 08:04:57 +01001704 if (res_list) {
1705 freeaddrinfo(res_list);
1706 }
Michal Vaskocb846632019-12-13 15:12:45 +01001707 if (sock != -1) {
1708 close(sock);
1709 }
1710 if (sock_pending) {
1711 *sock_pending = -1;
1712 }
1713 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001714}
1715
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001716#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001717
Michal Vasko3031aae2016-01-27 16:07:18 +01001718int
Michal Vasko9d4cca52022-09-07 11:19:57 +02001719nc_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 +01001720{
1721 int sock;
1722
Michal Vasko45e53ae2016-04-07 11:46:03 +02001723 if (!address) {
1724 ERRARG("address");
1725 return -1;
1726 } else if (!port) {
1727 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001728 return -1;
1729 }
1730
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001731 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001732 if (sock == -1) {
1733 return -1;
1734 }
1735
1736 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001737 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1738 if (!client_opts.ch_binds) {
1739 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001740 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001741 return -1;
1742 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001743
Michal Vasko9d4cca52022-09-07 11:19:57 +02001744 client_opts.ch_binds_aux = nc_realloc(client_opts.ch_binds_aux, client_opts.ch_bind_count * sizeof *client_opts.ch_binds_aux);
1745 if (!client_opts.ch_binds_aux) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001746 ERRMEM;
1747 close(sock);
1748 return -1;
1749 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001750 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].ti = ti;
1751 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].hostname = hostname ? strdup(hostname) : NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001752
Michal Vasko3031aae2016-01-27 16:07:18 +01001753 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
1754 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1755 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001756 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001757
1758 return 0;
1759}
1760
1761int
1762nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1763{
1764 uint32_t i;
1765 int ret = -1;
1766
1767 if (!address && !port && !ti) {
1768 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1769 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001770 free(client_opts.ch_binds[i].address);
1771
1772 free(client_opts.ch_binds_aux[i].hostname);
Michal Vasko3031aae2016-01-27 16:07:18 +01001773
1774 ret = 0;
1775 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001776 client_opts.ch_bind_count = 0;
1777
Michal Vasko3031aae2016-01-27 16:07:18 +01001778 free(client_opts.ch_binds);
1779 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001780
1781 free(client_opts.ch_binds_aux);
1782 client_opts.ch_binds_aux = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001783 } else {
1784 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001785 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1786 (!port || (client_opts.ch_binds[i].port == port)) &&
Michal Vasko9d4cca52022-09-07 11:19:57 +02001787 (!ti || (client_opts.ch_binds_aux[i].ti == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001788 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001789 free(client_opts.ch_binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001790
1791 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001792 if (!client_opts.ch_bind_count) {
1793 free(client_opts.ch_binds);
1794 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001795
1796 free(client_opts.ch_binds_aux);
1797 client_opts.ch_binds_aux = NULL;
Michal Vasko66c762a2016-10-13 10:34:14 +02001798 } else if (i < client_opts.ch_bind_count) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001799 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count],
1800 sizeof *client_opts.ch_binds);
1801
1802 memcpy(&client_opts.ch_binds_aux[i], &client_opts.ch_binds_aux[client_opts.ch_bind_count],
1803 sizeof *client_opts.ch_binds_aux);
Michal Vasko66c762a2016-10-13 10:34:14 +02001804 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001805
1806 ret = 0;
1807 }
1808 }
1809 }
1810
1811 return ret;
1812}
1813
1814API int
1815nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1816{
1817 int sock;
1818 char *host = NULL;
1819 uint16_t port, idx;
1820
Michal Vasko45e53ae2016-04-07 11:46:03 +02001821 if (!client_opts.ch_binds) {
1822 ERRINIT;
1823 return -1;
1824 } else if (!session) {
1825 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001826 return -1;
1827 }
1828
1829 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001830 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001831 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001832 return sock;
1833 }
1834
Radek Krejci53691be2016-02-22 13:58:37 +01001835#ifdef NC_ENABLED_SSH
Michal Vasko9d4cca52022-09-07 11:19:57 +02001836 if (client_opts.ch_binds_aux[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001837 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001838 } else
1839#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001840#ifdef NC_ENABLED_TLS
Michal Vasko9d4cca52022-09-07 11:19:57 +02001841 if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) {
1842 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT,
1843 client_opts.ch_binds_aux[idx].hostname);
Michal Vasko3d865d22016-01-28 16:00:53 +01001844 } else
1845#endif
1846 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001847 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001848 *session = NULL;
1849 }
1850
1851 free(host);
1852
1853 if (!(*session)) {
1854 return -1;
1855 }
1856
1857 return 1;
1858}
1859
Radek Krejci53691be2016-02-22 13:58:37 +01001860#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001861
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001862API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001863nc_session_get_cpblts(const struct nc_session *session)
1864{
1865 if (!session) {
1866 ERRARG("session");
1867 return NULL;
1868 }
1869
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001870 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001871}
1872
1873API const char *
1874nc_session_cpblt(const struct nc_session *session, const char *capab)
1875{
1876 int i, len;
1877
1878 if (!session) {
1879 ERRARG("session");
1880 return NULL;
1881 } else if (!capab) {
1882 ERRARG("capab");
1883 return NULL;
1884 }
1885
1886 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001887 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1888 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1889 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001890 }
1891 }
1892
1893 return NULL;
1894}
1895
Michal Vasko9cd26a82016-05-31 08:58:48 +02001896API int
1897nc_session_ntf_thread_running(const struct nc_session *session)
1898{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001899 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001900 ERRARG("session");
1901 return 0;
1902 }
1903
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02001904 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running);
Michal Vasko9cd26a82016-05-31 08:58:48 +02001905}
1906
Michal Vaskob7558c52016-02-26 15:04:19 +01001907API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001908nc_client_init(void)
1909{
1910 nc_init();
1911}
1912
1913API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001914nc_client_destroy(void)
1915{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001916 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001917#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001918 nc_client_ch_del_bind(NULL, 0, 0);
1919#endif
1920#ifdef NC_ENABLED_SSH
1921 nc_client_ssh_destroy_opts();
1922#endif
1923#ifdef NC_ENABLED_TLS
1924 nc_client_tls_destroy_opts();
1925#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001926 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001927}
1928
Michal Vasko77367452021-02-16 16:32:18 +01001929static NC_MSG_TYPE
1930recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001931{
Michal Vasko77367452021-02-16 16:32:18 +01001932 char *ptr;
1933 struct lyd_attr *attr;
1934 uint64_t cur_msgid;
1935
1936 assert(envp && !envp->schema);
1937
1938 /* find the message-id attribute */
1939 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1940 if (!strcmp(attr->name.name, "message-id")) {
1941 break;
1942 }
1943 }
1944
1945 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001946 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001947 return NC_MSG_REPLY_ERR_MSGID;
1948 }
1949
1950 cur_msgid = strtoul(attr->value, &ptr, 10);
1951 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001952 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1953 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001954 return NC_MSG_REPLY_ERR_MSGID;
1955 }
1956
1957 return NC_MSG_REPLY;
1958}
1959
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001960/**
1961 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1962 *
1963 * @param[in] session NETCONF session used to send error messages.
1964 * @param[in] msg Message to check for type.
1965 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1966 * @return NC_MSG_NOTIF If format roughly matches a notification;
1967 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1968 */
1969static NC_MSG_TYPE
1970get_msg_type(struct nc_session *session, struct ly_in *msg)
1971{
1972 const char *str, *end;
1973
1974 str = ly_in_memory(msg, NULL);
1975
1976 while (*str) {
1977 /* Skip whitespaces */
1978 while (isspace(*str)) {
1979 str++;
1980 }
1981
1982 if (*str == '<') {
1983 str++;
1984 if (!strncmp(str, "!--", 3)) {
1985 /* Skip comments */
1986 end = "-->";
1987 str = strstr(str, end);
1988 } else if (!strncmp(str, "?xml", 4)) {
1989 /* Skip xml declaration */
1990 end = "?>";
1991 str = strstr(str, end);
1992 } else if (!strncmp(str, "rpc-reply", 9)) {
1993 return NC_MSG_REPLY;
1994 } else if (!strncmp(str, "notification", 12)) {
1995 return NC_MSG_NOTIF;
1996 } else {
1997 ERR(session, "Unknown xml element '%.10s'.", str);
1998 return NC_MSG_ERROR;
1999 }
2000 if (!str) {
2001 /* No matching ending tag found */
2002 ERR(session, "No matching ending tag '%s' found in xml message.", end);
2003 return NC_MSG_ERROR;
2004 }
2005 str += strlen(end);
2006 } else {
2007 /* Not a valid xml */
2008 ERR(session, "Unexpected character '%c' in xml message.", *str);
2009 return NC_MSG_ERROR;
2010 }
2011 }
2012
2013 /* Unexpected end of message */
2014 ERR(session, "Unexpected end of xml message.");
2015 return NC_MSG_ERROR;
2016}
2017
2018/**
2019 * @brief Function to receive either replies or notifications.
2020 *
2021 * @param[in] session NETCONF session from which this function receives messages.
2022 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
2023 * @param[in] expected Type of the message the caller desired.
2024 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
2025 * @return NC_MSG_REPLY If a rpc-reply was received;
2026 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01002027 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002028 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
2029 */
2030static NC_MSG_TYPE
2031recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
2032{
2033 struct nc_msg_cont **cont_ptr;
2034 struct ly_in *msg = NULL;
2035 struct nc_msg_cont *cont, *prev;
2036 NC_MSG_TYPE ret = NC_MSG_ERROR;
2037 int r;
2038
2039 *message = NULL;
2040
2041 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002042 r = nc_session_client_msgs_lock(session, &timeout, __func__);
2043 if (!r) {
2044 ret = NC_MSG_WOULDBLOCK;
2045 goto cleanup;
2046 } else if (r == -1) {
2047 ret = NC_MSG_ERROR;
2048 goto cleanup;
2049 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002050
2051 /* Find the expected message in the buffer */
2052 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002053 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002054 prev = cont;
2055 }
2056
2057 if (cont) {
2058 /* Remove found message from buffer */
2059 if (prev) {
2060 prev->next = cont->next;
2061 } else {
2062 session->opts.client.msgs = cont->next;
2063 }
2064
2065 /* Use the buffer message */
2066 ret = cont->type;
2067 msg = cont->msg;
2068 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02002069 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002070 }
2071
2072 /* Read a message from the wire */
2073 r = nc_read_msg_poll_io(session, timeout, &msg);
2074 if (!r) {
2075 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02002076 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002077 } else if (r == -1) {
2078 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002079 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002080 }
2081
2082 /* Basic check to determine message type */
2083 ret = get_msg_type(session, msg);
2084 if (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 /* If received a message of different type store it in the buffer */
2089 if (ret != expected) {
2090 cont_ptr = &session->opts.client.msgs;
2091 while (*cont_ptr) {
2092 cont_ptr = &((*cont_ptr)->next);
2093 }
2094 *cont_ptr = malloc(sizeof **cont_ptr);
2095 if (!*cont_ptr) {
2096 ERRMEM;
2097 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002098 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002099 }
2100 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002101 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002102 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002103 (*cont_ptr)->next = NULL;
2104 }
2105
Michal Vasko01130bd2021-08-26 11:47:38 +02002106cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002107 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002108 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002109
Michal Vasko01130bd2021-08-26 11:47:38 +02002110cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002111 if (ret == expected) {
2112 *message = msg;
2113 } else {
2114 ly_in_free(msg, 1);
2115 }
2116 return ret;
2117}
2118
Michal Vasko77367452021-02-16 16:32:18 +01002119static NC_MSG_TYPE
2120recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
2121{
Michal Vasko77367452021-02-16 16:32:18 +01002122 LY_ERR lyrc;
2123 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002124 NC_MSG_TYPE ret = NC_MSG_ERROR;
2125
2126 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
2127
2128 *envp = NULL;
2129
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002130 /* Receive messages until a rpc-reply is found or a timeout or error reached */
2131 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
2132 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01002133 goto cleanup;
2134 }
2135
2136 /* parse */
2137 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
2138 if (!lyrc) {
2139 ret = recv_reply_check_msgid(session, *envp, msgid);
2140 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002141 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002142 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
Michal Vasko9a108052022-04-01 12:06:54 +02002143 lyd_free_tree(*envp);
2144 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002145 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002146 goto cleanup;
2147 }
2148
Michal Vasko77367452021-02-16 16:32:18 +01002149cleanup:
2150 ly_in_free(msg, 1);
2151 return ret;
2152}
2153
2154static int
2155recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2156{
Michal Vasko143aa142021-10-01 15:31:48 +02002157 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002158 struct nc_rpc_act_generic *rpc_gen;
2159 struct ly_in *in;
2160 struct lyd_node *tree, *op2;
2161 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002162 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002163
2164 switch (rpc->type) {
2165 case NC_RPC_ACT_GENERIC:
2166 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2167 if (rpc_gen->has_data) {
2168 tree = rpc_gen->content.data;
2169
2170 /* find the operation node */
2171 lyrc = LY_EINVAL;
2172 LYD_TREE_DFS_BEGIN(tree, op2) {
2173 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2174 lyrc = lyd_dup_single(op2, NULL, 0, op);
2175 break;
2176 }
2177 LYD_TREE_DFS_END(tree, op2);
2178 }
2179 } else {
2180 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2181 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2182 ly_in_free(in, 0);
2183 if (lyrc) {
Michal Vasko9a108052022-04-01 12:06:54 +02002184 lyd_free_tree(tree);
Michal Vasko77367452021-02-16 16:32:18 +01002185 return -1;
2186 }
2187
2188 /* we want just the operation node */
2189 lyrc = lyd_dup_single(op2, NULL, 0, op);
2190
2191 lyd_free_tree(tree);
2192 }
2193 break;
2194 case NC_RPC_GETCONFIG:
2195 module_name = "ietf-netconf";
2196 rpc_name = "get-config";
2197 break;
2198 case NC_RPC_EDIT:
2199 module_name = "ietf-netconf";
2200 rpc_name = "edit-config";
2201 break;
2202 case NC_RPC_COPY:
2203 module_name = "ietf-netconf";
2204 rpc_name = "copy-config";
2205 break;
2206 case NC_RPC_DELETE:
2207 module_name = "ietf-netconf";
2208 rpc_name = "delete-config";
2209 break;
2210 case NC_RPC_LOCK:
2211 module_name = "ietf-netconf";
2212 rpc_name = "lock";
2213 break;
2214 case NC_RPC_UNLOCK:
2215 module_name = "ietf-netconf";
2216 rpc_name = "unlock";
2217 break;
2218 case NC_RPC_GET:
2219 module_name = "ietf-netconf";
2220 rpc_name = "get";
2221 break;
2222 case NC_RPC_KILL:
2223 module_name = "ietf-netconf";
2224 rpc_name = "kill-session";
2225 break;
2226 case NC_RPC_COMMIT:
2227 module_name = "ietf-netconf";
2228 rpc_name = "commit";
2229 break;
2230 case NC_RPC_DISCARD:
2231 module_name = "ietf-netconf";
2232 rpc_name = "discard-changes";
2233 break;
2234 case NC_RPC_CANCEL:
2235 module_name = "ietf-netconf";
2236 rpc_name = "cancel-commit";
2237 break;
2238 case NC_RPC_VALIDATE:
2239 module_name = "ietf-netconf";
2240 rpc_name = "validate";
2241 break;
2242 case NC_RPC_GETSCHEMA:
2243 module_name = "ietf-netconf-monitoring";
2244 rpc_name = "get-schema";
2245 break;
2246 case NC_RPC_SUBSCRIBE:
2247 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002248 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002249 break;
2250 case NC_RPC_GETDATA:
2251 module_name = "ietf-netconf-nmda";
2252 rpc_name = "get-data";
2253 break;
2254 case NC_RPC_EDITDATA:
2255 module_name = "ietf-netconf-nmda";
2256 rpc_name = "edit-data";
2257 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002258 case NC_RPC_ESTABLISHSUB:
2259 module_name = "ietf-subscribed-notifications";
2260 rpc_name = "establish-subscription";
2261 break;
2262 case NC_RPC_MODIFYSUB:
2263 module_name = "ietf-subscribed-notifications";
2264 rpc_name = "modify-subscription";
2265 break;
2266 case NC_RPC_DELETESUB:
2267 module_name = "ietf-subscribed-notifications";
2268 rpc_name = "delete-subscription";
2269 break;
2270 case NC_RPC_KILLSUB:
2271 module_name = "ietf-subscribed-notifications";
2272 rpc_name = "kill-subscription";
2273 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002274 case NC_RPC_ESTABLISHPUSH:
2275 module_name = "ietf-subscribed-notifications";
2276 rpc_name = "establish-subscription";
2277 module_check = "ietf-yang-push";
2278 break;
2279 case NC_RPC_MODIFYPUSH:
2280 module_name = "ietf-subscribed-notifications";
2281 rpc_name = "modify-subscription";
2282 module_check = "ietf-yang-push";
2283 break;
2284 case NC_RPC_RESYNCSUB:
2285 module_name = "ietf-yang-push";
2286 rpc_name = "resync-subscription";
2287 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002288 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002289 lyrc = LY_EINT;
2290 break;
2291 }
2292
2293 if (module_name && rpc_name) {
2294 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2295 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002296 ERR(session, "Missing \"%s\" module in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002297 return -1;
2298 }
2299
2300 /* create the operation node */
2301 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2302 }
Michal Vasko305faca2021-03-25 09:16:02 +01002303 if (module_check) {
2304 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002305 ERR(session, "Missing \"%s\" module in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002306 return -1;
2307 }
2308 }
Michal Vasko77367452021-02-16 16:32:18 +01002309
2310 if (lyrc) {
2311 return -1;
2312 }
2313 return 0;
2314}
2315
2316API NC_MSG_TYPE
2317nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2318 struct lyd_node **op)
2319{
2320 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002321
Michal Vasko45e53ae2016-04-07 11:46:03 +02002322 if (!session) {
2323 ERRARG("session");
2324 return NC_MSG_ERROR;
2325 } else if (!rpc) {
2326 ERRARG("rpc");
2327 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002328 } else if (!envp) {
2329 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02002330 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002331 } else if (!op) {
2332 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002333 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002334 } else 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 Vasko05532772021-06-03 12:12:38 +02002376 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
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{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002391 if (!session) {
2392 ERRARG("session");
2393 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002394 } else if (!envp) {
2395 ERRARG("envp");
2396 return NC_MSG_ERROR;
2397 } else if (!op) {
2398 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002399 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002400 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002401 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002402 return NC_MSG_ERROR;
2403 }
2404
Michal Vasko77367452021-02-16 16:32:18 +01002405 /* receive a notification */
2406 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002407}
2408
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002409static void *
2410nc_recv_notif_thread(void *arg)
2411{
2412 struct nc_ntf_thread_arg *ntarg;
2413 struct nc_session *session;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002414 nc_notif_dispatch_clb notif_clb;
2415 void *user_data;
Michal Vasko743ff9f2022-10-20 10:03:13 +02002416
Michal Vaskoffb35e92022-10-20 09:07:25 +02002417 void (*free_data)(void *);
Michal Vasko77367452021-02-16 16:32:18 +01002418 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002419 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002420
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002421 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002422 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002423
2424 ntarg = (struct nc_ntf_thread_arg *)arg;
2425 session = ntarg->session;
2426 notif_clb = ntarg->notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002427 user_data = ntarg->user_data;
2428 free_data = ntarg->free_data;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002429 free(ntarg);
2430
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002431 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
Michal Vasko77367452021-02-16 16:32:18 +01002432 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002433 if (msgtype == NC_MSG_NOTIF) {
Michal Vaskoffb35e92022-10-20 09:07:25 +02002434 notif_clb(session, envp, op, user_data);
Michal Vasko77367452021-02-16 16:32:18 +01002435 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2436 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002437 lyd_free_all(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002438 break;
2439 }
Michal Vasko77367452021-02-16 16:32:18 +01002440 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002441 lyd_free_all(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002442 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002443 /* quit this thread once the session is broken */
2444 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002445 }
2446
2447 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2448 }
2449
Michal Vasko05532772021-06-03 12:12:38 +02002450 VRB(session, "Notification thread exit.");
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002451 ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoffb35e92022-10-20 09:07:25 +02002452 if (free_data) {
2453 free_data(user_data);
2454 }
2455
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002456 return NULL;
2457}
2458
2459API int
Michal Vaskoffb35e92022-10-20 09:07:25 +02002460nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb)
2461{
2462 return nc_recv_notif_dispatch_data(session, notif_clb, NULL, NULL);
2463}
2464
2465API int
2466nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data,
2467 void (*free_data)(void *))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002468{
2469 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002470 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002471 int ret;
2472
Michal Vasko45e53ae2016-04-07 11:46:03 +02002473 if (!session) {
2474 ERRARG("session");
2475 return -1;
2476 } else if (!notif_clb) {
2477 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002478 return -1;
2479 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002480 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002481 return -1;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002482 }
2483
2484 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002485 if (!ntarg) {
2486 ERRMEM;
2487 return -1;
2488 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002489 ntarg->session = session;
2490 ntarg->notif_clb = notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002491 ntarg->user_data = user_data;
2492 ntarg->free_data = free_data;
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002493 ATOMIC_INC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002494
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002495 /* just so that nc_recv_notif_thread() does not immediately exit */
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002496 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002497
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002498 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002499 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002500 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002501 free(ntarg);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002502 if (ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count) == 1) {
2503 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
2504 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002505 return -1;
2506 }
2507
2508 return 0;
2509}
2510
Michal Vasko77367452021-02-16 16:32:18 +01002511static const char *
2512nc_wd2str(NC_WD_MODE wd)
2513{
2514 switch (wd) {
2515 case NC_WD_ALL:
2516 return "report-all";
2517 case NC_WD_ALL_TAG:
2518 return "report-all-tagged";
2519 case NC_WD_TRIM:
2520 return "trim";
2521 case NC_WD_EXPLICIT:
2522 return "explicit";
2523 default:
2524 break;
2525 }
2526
2527 return NULL;
2528}
2529
Michal Vasko086311b2016-01-08 09:53:11 +01002530API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002531nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002532{
2533 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002534 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002535 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002536 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002537 struct nc_rpc_getconfig *rpc_gc;
2538 struct nc_rpc_edit *rpc_e;
2539 struct nc_rpc_copy *rpc_cp;
2540 struct nc_rpc_delete *rpc_del;
2541 struct nc_rpc_lock *rpc_lock;
2542 struct nc_rpc_get *rpc_g;
2543 struct nc_rpc_kill *rpc_k;
2544 struct nc_rpc_commit *rpc_com;
2545 struct nc_rpc_cancel *rpc_can;
2546 struct nc_rpc_validate *rpc_val;
2547 struct nc_rpc_getschema *rpc_gs;
2548 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002549 struct nc_rpc_getdata *rpc_getd;
2550 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002551 struct nc_rpc_establishsub *rpc_estsub;
2552 struct nc_rpc_modifysub *rpc_modsub;
2553 struct nc_rpc_deletesub *rpc_delsub;
2554 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002555 struct nc_rpc_establishpush *rpc_estpush;
2556 struct nc_rpc_modifypush *rpc_modpush;
2557 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002558 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002559 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002560 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002561 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002562 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002563 uint64_t cur_msgid;
2564
Michal Vasko45e53ae2016-04-07 11:46:03 +02002565 if (!session) {
2566 ERRARG("session");
2567 return NC_MSG_ERROR;
2568 } else if (!rpc) {
2569 ERRARG("rpc");
2570 return NC_MSG_ERROR;
2571 } else if (!msgid) {
2572 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002573 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002574 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002575 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002576 return NC_MSG_ERROR;
2577 }
2578
Michal Vaskoc1171a42019-11-05 12:06:46 +01002579 switch (rpc->type) {
2580 case NC_RPC_ACT_GENERIC:
2581 /* checked when parsing */
2582 break;
2583 case NC_RPC_GETCONFIG:
2584 case NC_RPC_EDIT:
2585 case NC_RPC_COPY:
2586 case NC_RPC_DELETE:
2587 case NC_RPC_LOCK:
2588 case NC_RPC_UNLOCK:
2589 case NC_RPC_GET:
2590 case NC_RPC_KILL:
2591 case NC_RPC_COMMIT:
2592 case NC_RPC_DISCARD:
2593 case NC_RPC_CANCEL:
2594 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002595 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002596 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002597 ERR(session, "Missing \"ietf-netconf\" module in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002598 return NC_MSG_ERROR;
2599 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002600 break;
2601 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002602 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002603 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002604 ERR(session, "Missing \"ietf-netconf-monitoring\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002605 return NC_MSG_ERROR;
2606 }
2607 break;
2608 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002609 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002610 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002611 ERR(session, "Missing \"notifications\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002612 return NC_MSG_ERROR;
2613 }
2614 break;
2615 case NC_RPC_GETDATA:
2616 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002617 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002618 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002619 ERR(session, "Missing \"ietf-netconf-nmda\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002620 return NC_MSG_ERROR;
2621 }
2622 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002623 case NC_RPC_ESTABLISHSUB:
2624 case NC_RPC_MODIFYSUB:
2625 case NC_RPC_DELETESUB:
2626 case NC_RPC_KILLSUB:
2627 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2628 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002629 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002630 return NC_MSG_ERROR;
2631 }
2632 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002633 case NC_RPC_ESTABLISHPUSH:
2634 case NC_RPC_MODIFYPUSH:
2635 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2636 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002637 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002638 return NC_MSG_ERROR;
2639 }
2640 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2641 if (!mod2) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002642 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002643 return NC_MSG_ERROR;
2644 }
2645 break;
2646 case NC_RPC_RESYNCSUB:
2647 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2648 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002649 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002650 return NC_MSG_ERROR;
2651 }
2652 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002653 case NC_RPC_UNKNOWN:
2654 ERRINT;
2655 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002656 }
2657
Michal Vaskoab9deb62021-05-27 11:37:00 +02002658#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2659
Michal Vasko086311b2016-01-08 09:53:11 +01002660 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002661 case NC_RPC_ACT_GENERIC:
2662 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002663
2664 if (rpc_gen->has_data) {
2665 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002666 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002667 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002668 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2669 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2670 ly_in_free(in, 0);
2671 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002672 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002673 }
Michal Vasko086311b2016-01-08 09:53:11 +01002674 }
2675 break;
2676
2677 case NC_RPC_GETCONFIG:
2678 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2679
Michal Vaskoab9deb62021-05-27 11:37:00 +02002680 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2681 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2682 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002683 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002684 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002685 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2686 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002687 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002688 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2689 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2690 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002691 }
2692 }
2693
2694 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002695 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002696 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002697 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002698 lyrc = LY_ENOTFOUND;
2699 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002700 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002701 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 +01002702 }
2703 break;
2704
2705 case NC_RPC_EDIT:
2706 rpc_e = (struct nc_rpc_edit *)rpc;
2707
Michal Vaskoab9deb62021-05-27 11:37:00 +02002708 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2709 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2710 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002711
2712 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002713 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 +01002714 }
Michal Vasko086311b2016-01-08 09:53:11 +01002715 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002716 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 +01002717 }
Michal Vasko086311b2016-01-08 09:53:11 +01002718 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002719 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 +01002720 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002721 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002722 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_e->edit_cont, 0, 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(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002725 }
2726 break;
2727
2728 case NC_RPC_COPY:
2729 rpc_cp = (struct nc_rpc_copy *)rpc;
2730
Michal Vaskoab9deb62021-05-27 11:37:00 +02002731 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2732 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002733 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002734 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002735 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002736 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002737 }
2738
Michal Vaskoab9deb62021-05-27 11:37:00 +02002739 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002740 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002741 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002742 CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_cp->url_config_src, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002743 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002744 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002745 }
2746 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002747 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002748 }
2749
2750 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002751 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002752 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002753 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002754 lyrc = LY_ENOTFOUND;
2755 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002756 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002757 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 +01002758 }
2759 break;
2760
2761 case NC_RPC_DELETE:
2762 rpc_del = (struct nc_rpc_delete *)rpc;
2763
Michal Vaskoab9deb62021-05-27 11:37:00 +02002764 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2765 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002766 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002767 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002768 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002769 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002770 }
2771 break;
2772
2773 case NC_RPC_LOCK:
2774 rpc_lock = (struct nc_rpc_lock *)rpc;
2775
Michal Vaskoab9deb62021-05-27 11:37:00 +02002776 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2777 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2778 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002779 break;
2780
2781 case NC_RPC_UNLOCK:
2782 rpc_lock = (struct nc_rpc_lock *)rpc;
2783
Michal Vaskoab9deb62021-05-27 11:37:00 +02002784 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2785 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2786 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002787 break;
2788
2789 case NC_RPC_GET:
2790 rpc_g = (struct nc_rpc_get *)rpc;
2791
Michal Vaskoab9deb62021-05-27 11:37:00 +02002792 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002793 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002794 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002795 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2796 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002797 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002798 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2799 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2800 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002801 }
2802 }
2803
2804 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002805 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002806 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002807 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002808 lyrc = LY_ENOTFOUND;
2809 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002810 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002811 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 +01002812 }
2813 break;
2814
2815 case NC_RPC_KILL:
2816 rpc_k = (struct nc_rpc_kill *)rpc;
2817
Michal Vaskoab9deb62021-05-27 11:37:00 +02002818 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002819 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002820 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002821 break;
2822
2823 case NC_RPC_COMMIT:
2824 rpc_com = (struct nc_rpc_commit *)rpc;
2825
Michal Vaskoab9deb62021-05-27 11:37:00 +02002826 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002827 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002828 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002829 }
2830
2831 if (rpc_com->confirm_timeout) {
2832 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002833 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002834 }
Michal Vasko086311b2016-01-08 09:53:11 +01002835 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002836 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002837 }
Michal Vasko086311b2016-01-08 09:53:11 +01002838 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002839 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002840 }
2841 break;
2842
2843 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002844 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002845 break;
2846
2847 case NC_RPC_CANCEL:
2848 rpc_can = (struct nc_rpc_cancel *)rpc;
2849
Michal Vaskoab9deb62021-05-27 11:37:00 +02002850 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002851 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002852 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002853 }
2854 break;
2855
2856 case NC_RPC_VALIDATE:
2857 rpc_val = (struct nc_rpc_validate *)rpc;
2858
Michal Vaskoab9deb62021-05-27 11:37:00 +02002859 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2860 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002861 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002862 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002863 CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_val->url_config_src, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002864 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002865 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002866 }
2867 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002868 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002869 }
2870 break;
2871
2872 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002873 rpc_gs = (struct nc_rpc_getschema *)rpc;
2874
Michal Vaskoab9deb62021-05-27 11:37:00 +02002875 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2876 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002877 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002878 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002879 }
2880 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002881 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002882 }
2883 break;
2884
2885 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002886 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2887
Michal Vaskoab9deb62021-05-27 11:37:00 +02002888 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002889 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002890 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002891 }
2892
2893 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002894 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002895 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2896 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002897 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002898 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2899 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2900 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002901 }
2902 }
Michal Vasko086311b2016-01-08 09:53:11 +01002903 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002904 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002905 }
Michal Vasko086311b2016-01-08 09:53:11 +01002906 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002907 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002908 }
2909 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002910
2911 case NC_RPC_GETDATA:
2912 rpc_getd = (struct nc_rpc_getdata *)rpc;
2913
Michal Vaskoab9deb62021-05-27 11:37:00 +02002914 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2915 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2916
Michal Vaskoc1171a42019-11-05 12:06:46 +01002917 if (rpc_getd->filter) {
2918 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002919 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "subtree-filter", rpc_getd->filter, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002920 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002921 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002922 }
2923 }
2924 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002925 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002926 }
2927 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002928 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2929 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002930 }
2931 if (rpc_getd->max_depth) {
2932 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002933 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002934 }
2935 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002936 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002937 }
2938
2939 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002940 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002941 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 +01002942 }
2943 break;
2944
2945 case NC_RPC_EDITDATA:
2946 rpc_editd = (struct nc_rpc_editdata *)rpc;
2947
Michal Vaskoab9deb62021-05-27 11:37:00 +02002948 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2949 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002950
2951 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002952 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2953 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002954 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002955 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002956 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_editd->edit_cont, 0, LYD_ANYDATA_XML, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002957 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002958 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002959 }
2960 break;
2961
Michal Vasko96f247a2021-03-15 13:32:10 +01002962 case NC_RPC_ESTABLISHSUB:
2963 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2964
Michal Vaskoab9deb62021-05-27 11:37:00 +02002965 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002966
2967 if (rpc_estsub->filter) {
2968 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002969 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2970 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002971 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002972 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002973 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002974 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002975 }
2976 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002977 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002978
2979 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002980 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002981 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002982 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002983 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002984 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002985 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002986 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002987 }
2988 break;
2989
2990 case NC_RPC_MODIFYSUB:
2991 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2992
Michal Vaskoab9deb62021-05-27 11:37:00 +02002993 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002994
2995 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002996 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002997
2998 if (rpc_modsub->filter) {
2999 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003000 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
3001 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003002 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003003 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003004 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003005 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003006 }
3007 }
Michal Vasko96f247a2021-03-15 13:32:10 +01003008 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003009 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003010 }
3011 break;
3012
3013 case NC_RPC_DELETESUB:
3014 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
3015
Michal Vaskoab9deb62021-05-27 11:37:00 +02003016 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003017
3018 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003019 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003020 break;
3021
3022 case NC_RPC_KILLSUB:
3023 rpc_killsub = (struct nc_rpc_killsub *)rpc;
3024
Michal Vaskoab9deb62021-05-27 11:37:00 +02003025 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003026
3027 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003028 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003029 break;
3030
Michal Vasko305faca2021-03-25 09:16:02 +01003031 case NC_RPC_ESTABLISHPUSH:
3032 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
3033
Michal Vaskoab9deb62021-05-27 11:37:00 +02003034 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
3035 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003036
3037 if (rpc_estpush->filter) {
3038 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003039 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
3040 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003041 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003042 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003043 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003044 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003045 }
3046 }
3047
3048 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003049 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003050 }
Michal Vasko305faca2021-03-25 09:16:02 +01003051 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003052 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003053 }
3054
3055 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003056 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003057 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003058 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003059 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003060 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003061 }
3062 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003063 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003064 if (rpc_estpush->dampening_period) {
3065 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003066 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003067 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02003068 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
3069 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003070 if (rpc_estpush->excluded_change) {
3071 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003072 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
3073 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003074 }
3075 }
3076 }
3077 break;
3078
3079 case NC_RPC_MODIFYPUSH:
3080 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
3081
Michal Vaskoab9deb62021-05-27 11:37:00 +02003082 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003083
3084 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003085 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
3086 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003087
3088 if (rpc_modpush->filter) {
3089 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003090 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
3091 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003092 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003093 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003094 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003095 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003096 }
3097 }
Michal Vasko305faca2021-03-25 09:16:02 +01003098 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003099 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003100 }
3101
3102 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003103 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003104 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003105 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003106 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003107 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003108 }
3109 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003110 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003111 if (rpc_modpush->dampening_period) {
3112 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003113 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003114 }
3115 }
3116 break;
3117
3118 case NC_RPC_RESYNCSUB:
3119 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
3120
Michal Vaskoab9deb62021-05-27 11:37:00 +02003121 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003122 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003123 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003124 break;
3125
Michal Vasko96f247a2021-03-15 13:32:10 +01003126 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01003127 ERRINT;
3128 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01003129 }
3130
Michal Vaskoab9deb62021-05-27 11:37:00 +02003131#undef CHECK_LYRC_BREAK
3132
3133 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02003134 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02003135 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02003136 return NC_MSG_ERROR;
3137 }
3138
Michal Vasko131120a2018-05-29 15:44:02 +02003139 /* send RPC, store its message ID */
3140 r = nc_send_msg_io(session, timeout, data);
3141 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003142
Radek Krejcib4b19062018-02-07 16:33:06 +01003143 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003144 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003145 }
Michal Vasko086311b2016-01-08 09:53:11 +01003146
Michal Vasko131120a2018-05-29 15:44:02 +02003147 if (r == NC_MSG_RPC) {
3148 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003149 }
Michal Vasko131120a2018-05-29 15:44:02 +02003150 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003151}
Michal Vaskode2946c2017-01-12 12:19:26 +01003152
3153API void
3154nc_client_session_set_not_strict(struct nc_session *session)
3155{
3156 if (session->side != NC_CLIENT) {
3157 ERRARG("session");
3158 return;
3159 }
3160
3161 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3162}