blob: 047d6ed41d723829ce375e74e9d7a7935fa5650c [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 session client functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
15
Radek Krejcifd5b6682017-06-13 15:52:53 +020016#define _GNU_SOURCE
Michal Vaskobbf52c82020-04-07 13:08:50 +020017
18#ifdef __linux__
19# include <sys/syscall.h>
20#endif
21
Michal Vaskob83a3fa2021-05-26 09:53:42 +020022#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010023#include <assert.h>
tadeas-vintrlik54f142a2021-08-23 10:36:18 +020024#include <ctype.h>
Michal Vasko086311b2016-01-08 09:53:11 +010025#include <errno.h>
26#include <fcntl.h>
27#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010028#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010029#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020030#include <poll.h>
Michal Vasko086311b2016-01-08 09:53:11 +010031#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020032#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010033#include <stdlib.h>
34#include <string.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020035#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010036#include <sys/socket.h>
37#include <sys/stat.h>
38#include <sys/types.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020039#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010040#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010041
42#include <libyang/libyang.h>
43
Michal Vasko9e8ac262020-04-07 13:06:45 +020044#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020045#include "config.h"
46#include "log_p.h"
47#include "messages_p.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020048#include "session_client.h"
roman3f9b65c2023-06-05 14:26:58 +020049#include "session_client_ch.h"
50#include "session_p.h"
Michal Vasko086311b2016-01-08 09:53:11 +010051
Michal Vasko8a4e1462020-05-07 11:32:31 +020052#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020053#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020054
Michal Vasko80ef5d22016-01-18 09:21:02 +010055static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
56
roman2eab4742023-06-06 10:00:26 +020057#ifdef NC_ENABLED_SSH_TLS
Radek Krejci62aa0642017-05-25 16:33:49 +020058char *sshauth_password(const char *username, const char *hostname, void *priv);
59char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020060char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
roman2eab4742023-06-06 10:00:26 +020061#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +020062
63static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
64static pthread_key_t nc_client_context_key;
65#ifdef __linux__
66static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020067 .opts.ka = {
68 .enabled = 1,
69 .idle_time = 1,
70 .max_probes = 10,
71 .probe_interval = 5
72 },
roman2eab4742023-06-06 10:00:26 +020073#ifdef NC_ENABLED_SSH_TLS
Radek Krejci62aa0642017-05-25 16:33:49 +020074 .ssh_opts = {
roman41a11e42022-06-22 09:27:08 +020075 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020076 .auth_password = sshauth_password,
77 .auth_interactive = sshauth_interactive,
78 .auth_privkey_passphrase = sshauth_privkey_passphrase
79 },
80 .ssh_ch_opts = {
81 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020082 .auth_password = sshauth_password,
83 .auth_interactive = sshauth_interactive,
84 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020085 },
roman2eab4742023-06-06 10:00:26 +020086#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +020087 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020088 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020089};
90#endif
91
92static void
93nc_client_context_free(void *ptr)
94{
95 struct nc_client_context *c = (struct nc_client_context *)ptr;
96
Radek Krejci5cebc6b2017-05-26 13:24:38 +020097 if (--(c->refcount)) {
98 /* still used */
99 return;
100 }
101
Radek Krejci62aa0642017-05-25 16:33:49 +0200102#ifdef __linux__
103 /* in __linux__ we use static memory in the main thread,
104 * so this check is for programs terminating the main()
105 * function by pthread_exit() :)
106 */
107 if (c != &context_main)
108#endif
109 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200110 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400111 free(c->opts.schema_searchpath);
112
roman2eab4742023-06-06 10:00:26 +0200113#ifdef NC_ENABLED_SSH_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400114 int i;
Michal Vasko292c5542023-02-01 14:33:17 +0100115
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400116 for (i = 0; i < c->opts.ch_bind_count; ++i) {
117 close(c->opts.ch_binds[i].sock);
118 free((char *)c->opts.ch_binds[i].address);
119 }
120 free(c->opts.ch_binds);
121 c->opts.ch_binds = NULL;
122 c->opts.ch_bind_count = 0;
roman2eab4742023-06-06 10:00:26 +0200123
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400124 _nc_client_ssh_destroy_opts(&c->ssh_opts);
125 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
roman2eab4742023-06-06 10:00:26 +0200126
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400127 _nc_client_tls_destroy_opts(&c->tls_opts);
128 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
roman2eab4742023-06-06 10:00:26 +0200129#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +0200130 free(c);
131 }
132}
133
134static void
135nc_client_context_createkey(void)
136{
137 int r;
138
139 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200140 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200141 pthread_setspecific(nc_client_context_key, NULL);
142}
143
144struct nc_client_context *
145nc_client_context_location(void)
146{
147 struct nc_client_context *e;
148
149 pthread_once(&nc_client_context_once, nc_client_context_createkey);
150 e = pthread_getspecific(nc_client_context_key);
151 if (!e) {
152 /* prepare ly_err storage */
153#ifdef __linux__
154 if (getpid() == syscall(SYS_gettid)) {
155 /* main thread - use global variable instead of thread-specific variable. */
156 e = &context_main;
157 } else
158#endif /* __linux__ */
159 {
160 e = calloc(1, sizeof *e);
161 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200162 e->refcount = 1;
roman2eab4742023-06-06 10:00:26 +0200163#ifdef NC_ENABLED_SSH_TLS
Radek Krejci62aa0642017-05-25 16:33:49 +0200164 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
roman41a11e42022-06-22 09:27:08 +0200165 e->ssh_opts.auth_pref[0].value = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200166 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
167 e->ssh_opts.auth_pref[1].value = 2;
168 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
roman41a11e42022-06-22 09:27:08 +0200169 e->ssh_opts.auth_pref[2].value = 3;
Radek Krejci62aa0642017-05-25 16:33:49 +0200170 e->ssh_opts.auth_password = sshauth_password;
171 e->ssh_opts.auth_interactive = sshauth_interactive;
172 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
173
roman41a11e42022-06-22 09:27:08 +0200174 /* callhome settings are the same */
Radek Krejci62aa0642017-05-25 16:33:49 +0200175 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
176 e->ssh_ch_opts.auth_pref[0].value = 1;
177 e->ssh_ch_opts.auth_pref[1].value = 2;
178 e->ssh_ch_opts.auth_pref[2].value = 3;
roman2eab4742023-06-06 10:00:26 +0200179#endif /* NC_ENABLED_SSH_TLS */
Radek Krejci62aa0642017-05-25 16:33:49 +0200180 }
181 pthread_setspecific(nc_client_context_key, e);
182 }
183
184 return e;
185}
186
187#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100188
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200189API void *
190nc_client_get_thread_context(void)
191{
192 return nc_client_context_location();
193}
194
195API void
196nc_client_set_thread_context(void *context)
197{
198 struct nc_client_context *old, *new;
199
200 if (!context) {
roman40672412023-05-04 11:10:22 +0200201 ERRARG(NULL, "context");
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200202 return;
203 }
204
205 new = (struct nc_client_context *)context;
206 old = nc_client_context_location();
207 if (old == new) {
208 /* nothing to change */
209 return;
210 }
211
212 /* replace old by new, increase reference counter in the newly set context */
213 nc_client_context_free(old);
214 new->refcount++;
215 pthread_setspecific(nc_client_context_key, new);
216}
217
Michal Vasko78939072022-12-12 07:43:18 +0100218/**
219 * @brief Ext data callback for a context to provide schema mount data.
220 */
221static LY_ERR
222nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
223{
224 struct nc_session *session = user_data;
225
226 if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
227 return LY_EINVAL;
228 }
229
230 if (!session->opts.client.ext_data) {
231 ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server.");
232 return LY_ENOTFOUND;
233 }
234
235 /* return ext data */
236 *ext_data = session->opts.client.ext_data;
237 *ext_data_free = 0;
238
239 return LY_SUCCESS;
240}
241
Radek Krejcifd5b6682017-06-13 15:52:53 +0200242int
Michal Vasko78939072022-12-12 07:43:18 +0100243nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200244{
245 /* assign context (dicionary needed for handshake) */
246 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100247 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200248 return EXIT_FAILURE;
249 }
250
Michal Vasko5ca5d972022-09-14 13:51:31 +0200251 /* user path must be first, the first path is used to store modules retreived via get-schema */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200252 if (client_opts.schema_searchpath) {
253 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko5d0cdc32022-12-12 07:38:21 +0100254 }
Michal Vasko80227222022-12-12 09:05:24 +0100255 if (!access(NC_CLIENT_SEARCH_DIR, F_OK)) {
256 ly_ctx_set_searchdir(ctx, NC_CLIENT_SEARCH_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200257 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200258
Michal Vasko5ca5d972022-09-14 13:51:31 +0200259 /* set callback for getting modules, if provided */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200260 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
Michal Vasko78939072022-12-12 07:43:18 +0100261
262 /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */
263 ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200264 } else {
265 session->flags |= NC_SESSION_SHAREDCTX;
266 }
267
268 session->ctx = ctx;
269
270 return EXIT_SUCCESS;
271}
272
Michal Vasko086311b2016-01-08 09:53:11 +0100273API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100274nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100275{
Michal Vasko3031aae2016-01-27 16:07:18 +0100276 if (client_opts.schema_searchpath) {
277 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100278 }
Michal Vasko086311b2016-01-08 09:53:11 +0100279
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100280 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100281 client_opts.schema_searchpath = strdup(path);
282 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100283 ERRMEM;
284 return 1;
285 }
286 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100287 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100288 }
289
290 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100291}
292
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100293API const char *
294nc_client_get_schema_searchpath(void)
295{
296 return client_opts.schema_searchpath;
297}
298
Radek Krejcifd5b6682017-06-13 15:52:53 +0200299API int
300nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100301{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200302 client_opts.schema_clb = clb;
303 if (clb) {
304 client_opts.schema_clb_data = user_data;
305 } else {
306 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100307 }
308
309 return 0;
310}
311
Radek Krejcifd5b6682017-06-13 15:52:53 +0200312API ly_module_imp_clb
313nc_client_get_schema_callback(void **user_data)
314{
315 if (user_data) {
316 (*user_data) = client_opts.schema_clb_data;
317 }
318 return client_opts.schema_clb;
319}
320
Michal Vaskoa272e092023-07-10 14:34:03 +0200321API void
322nc_client_set_new_session_context_autofill(int enabled)
323{
324 client_opts.auto_context_fill_disabled = !enabled;
325}
326
Michal Vasko5ca5d972022-09-14 13:51:31 +0200327struct module_info {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200328 char *name;
329 char *revision;
Michal Vasko6ce8e822022-08-02 15:09:17 +0200330
Radek Krejci65ef6d52018-08-16 16:35:02 +0200331 struct {
332 char *name;
333 char *revision;
334 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100335 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200336 int implemented;
337};
338
339struct clb_data_s {
340 void *user_data;
341 ly_module_imp_clb user_clb;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200342 struct module_info *modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200343 struct nc_session *session;
344 int has_get_schema;
345};
346
Michal Vaskoc088f982021-10-05 12:23:07 +0200347/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200348 * @brief Retrieve YANG module content from a local file.
Michal Vaskoc088f982021-10-05 12:23:07 +0200349 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200350 * @param[in] name Module name.
351 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200352 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200353 * @param[out] format Module format.
354 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200355 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200356static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200357retrieve_module_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200358 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100359{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200360 char *localfile = NULL;
361 FILE *f;
362 long length, l;
363 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100364
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200365 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200366 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
367 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200368 return NULL;
369 }
370 if (localfile) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200371 VRB(clb_data->session, "Reading module \"%s@%s\" from local file \"%s\".", name, rev ? rev : "<latest>",
Michal Vasko7502b602022-08-26 11:51:17 +0200372 localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200373 f = fopen(localfile, "r");
374 if (!f) {
Michal Vasko7502b602022-08-26 11:51:17 +0200375 ERR(clb_data->session, "Unable to open file \"%s\" (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200376 free(localfile);
377 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100378 }
Michal Vasko086311b2016-01-08 09:53:11 +0100379
Radek Krejci65ef6d52018-08-16 16:35:02 +0200380 fseek(f, 0, SEEK_END);
381 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200382 if (length < 0) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200383 ERR(clb_data->session, "Unable to get the size of module file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200384 free(localfile);
385 fclose(f);
386 return NULL;
387 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200388 fseek(f, 0, SEEK_SET);
389
390 model_data = malloc(length + 1);
391 if (!model_data) {
392 ERRMEM;
393 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200394 ERR(clb_data->session, "Reading module from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
Michal Vasko05532772021-06-03 12:12:38 +0200395 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200396 free(model_data);
397 model_data = NULL;
398 } else {
399 /* terminating NULL byte */
400 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100401 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200402 fclose(f);
403 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100404 }
405
Radek Krejci65ef6d52018-08-16 16:35:02 +0200406 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100407}
408
Michal Vaskoc088f982021-10-05 12:23:07 +0200409/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200410 * @brief Retrieve YANG module content from a reply to get-schema RPC.
Michal Vaskoc088f982021-10-05 12:23:07 +0200411 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200412 * @param[in] name Module name.
413 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200414 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200415 * @param[out] format Module format.
416 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200417 */
Michal Vasko086311b2016-01-08 09:53:11 +0100418static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200419retrieve_module_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200420 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100421{
Michal Vasko086311b2016-01-08 09:53:11 +0100422 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100423 struct lyd_node *envp = NULL, *op = NULL;
424 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100425 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100426 uint64_t msgid;
Michal Vasko7502b602022-08-26 11:51:17 +0200427 char *localfile = NULL, *envp_str = NULL, *model_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200428 FILE *f;
Michal Vasko086311b2016-01-08 09:53:11 +0100429
Michal Vasko5ca5d972022-09-14 13:51:31 +0200430 VRB(clb_data->session, "Reading module \"%s@%s\" from server via get-schema.", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200431 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100432
Radek Krejci65ef6d52018-08-16 16:35:02 +0200433 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100434 usleep(1000);
435 }
436 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200437 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100438 nc_rpc_free(rpc);
439 return NULL;
440 }
441
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200442 do {
Michal Vasko77367452021-02-16 16:32:18 +0100443 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200444 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100445 nc_rpc_free(rpc);
446 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200447 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100448 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100449 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200450 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100451 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100452 } else if (!op) {
Michal Vasko7502b602022-08-26 11:51:17 +0200453 assert(envp);
454 lyd_print_mem(&envp_str, envp, LYD_XML, 0);
455 WRN(clb_data->session, "Received an unexpected reply to <get-schema>:\n%s", envp_str);
456 free(envp_str);
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100457 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100458 }
459
Michal Vasko77367452021-02-16 16:32:18 +0100460 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200461 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100462 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200463 }
Michal Vasko77367452021-02-16 16:32:18 +0100464 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200465 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200466 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100467 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200468 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200469 break;
470 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100471 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200472 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200473 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200474 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200475 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100476 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200477 }
Michal Vasko086311b2016-01-08 09:53:11 +0100478
Radek Krejci488b0702018-08-29 14:47:15 +0200479 if (model_data && !model_data[0]) {
480 /* empty data */
481 free(model_data);
482 model_data = NULL;
483 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100484 if (!model_data) {
485 goto cleanup;
486 }
487
488 /* set format */
489 *format = LYS_IN_YANG;
Radek Krejci488b0702018-08-29 14:47:15 +0200490
Michal Vasko5ca5d972022-09-14 13:51:31 +0200491 /* try to store the model_data into local module repository */
Michal Vaskoa1721172022-12-12 09:06:53 +0100492 lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx), 0, name, rev, &localfile, NULL);
493 if (client_opts.schema_searchpath && !localfile) {
494 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
495 rev ? rev : "") == -1) {
496 ERRMEM;
497 } else {
498 f = fopen(localfile, "w");
499 if (!f) {
500 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of module retrieved via <get-schema> (%s).",
501 localfile, strerror(errno));
Michal Vaskof945da52018-02-15 08:45:13 +0100502 } else {
Michal Vaskoa1721172022-12-12 09:06:53 +0100503 fputs(model_data, f);
504 fclose(f);
Michal Vaskof945da52018-02-15 08:45:13 +0100505 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200506 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200507 }
Michal Vaskoa1721172022-12-12 09:06:53 +0100508 free(localfile);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200509
Michal Vasko77367452021-02-16 16:32:18 +0100510cleanup:
511 lyd_free_tree(envp);
512 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100513 return model_data;
514}
515
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200516static void
517free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200518{
519 free(data);
520 (void)user_data;
521}
522
Michal Vaskoc088f982021-10-05 12:23:07 +0200523/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200524 * @brief Retrieve YANG module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200525 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200526 * @param[in] mod_name Module name.
527 * @param[in] mod_rev Module revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100528 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200529 * @param[out] format Module format.
530 * @param[out] module_data Module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100531 * @param[out] free_module_data Callback for freeing @p module_data.
532 * @return LY_ERR value.
533 */
534static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200535retrieve_module_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format,
Michal Vasko5e32c402022-01-12 14:05:09 +0100536 const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
537{
538 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
539 char *model_data = NULL;
540
Michal Vasko5e32c402022-01-12 14:05:09 +0100541 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200542 model_data = retrieve_module_data_localfile(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100543
544 /* 2. try to use <get-schema> */
545 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200546 model_data = retrieve_module_data_getschema(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100547 }
548
549 /* 3. try to use user callback */
550 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200551 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 +0100552 clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data,
553 free_module_data);
554 }
555
556 *free_module_data = free_with_user_data;
557 *module_data = model_data;
558 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
559}
560
561/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200562 * @brief Retrieve YANG import module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100563 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200564 * @param[in] mod_name Module name.
565 * @param[in] mod_rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200566 * @param[in] submod_name Optional submodule name.
567 * @param[in] sub_rev Submodule revision.
568 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200569 * @param[out] format Module format.
570 * @param[out] module_data Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200571 * @param[out] free_module_data Callback for freeing @p module_data.
572 * @return LY_ERR value.
573 */
Michal Vasko77367452021-02-16 16:32:18 +0100574static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200575retrieve_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 +0100576 void *user_data, LYS_INFORMAT *format, const char **module_data,
577 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200578{
579 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
Michal Vasko5e32c402022-01-12 14:05:09 +0100580 uint32_t u, v, match = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200581 const char *name = NULL, *rev = NULL;
582 char *model_data = NULL;
583
Michal Vasko5ca5d972022-09-14 13:51:31 +0200584 /* get and check the final name and revision of the module to be retrieved */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200585 if (!mod_rev || !mod_rev[0]) {
586 /* newest revision requested - get the newest revision from the list of available modules on server */
587 match = 0;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200588 for (u = 0; clb_data->modules[u].name; ++u) {
589 if (strcmp(mod_name, clb_data->modules[u].name)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200590 continue;
591 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200592 if (!match || (strcmp(mod_rev, clb_data->modules[u].revision) > 0)) {
593 mod_rev = clb_data->modules[u].revision;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200594 }
595 match = u + 1;
596 }
597 if (!match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200598 /* valid situation if we are retrieving YANG 1.1 module and have only capabilities for now
Michal Vasko1d2665c2021-02-12 09:28:44 +0100599 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200600 VRB(clb_data->session, "Unable to identify revision of the import module \"%s\" from "
Michal Vasko05532772021-06-03 12:12:38 +0200601 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200602 }
603 }
604 if (submod_name) {
605 name = submod_name;
606 if (sub_rev) {
607 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200608 } else if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200609 if (!clb_data->modules[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200610 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
Michal Vasko5ca5d972022-09-14 13:51:31 +0200611 "in import module \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200612 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200613 for (v = 0; clb_data->modules[match - 1].submodules[v].name; ++v) {
614 if (!strcmp(submod_name, clb_data->modules[match - 1].submodules[v].name)) {
615 rev = sub_rev = clb_data->modules[match - 1].submodules[v].revision;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200616 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200617 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200618 if (!rev) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200619 ERR(clb_data->session, "Requested submodule \"%s\" is not found in import module \"%s\" on server side.",
Michal Vasko05532772021-06-03 12:12:38 +0200620 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100621 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200622 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200623 }
624 }
625 } else {
626 name = mod_name;
627 rev = mod_rev;
628 }
629
Radek Krejci65ef6d52018-08-16 16:35:02 +0200630 if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200631 /* we have enough information to avoid communication with server and try to get the module locally */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200632
633 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200634 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200635
636 /* 2. try to use <get-schema> */
637 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200638 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200639 }
640 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200641 /* we are unsure which revision of the module we should load, so first try to get
Radek Krejci65ef6d52018-08-16 16:35:02 +0200642 * the newest revision from the server via get-schema and only if the server does not
643 * implement get-schema, try to load the newest revision locally. This is imperfect
644 * solution, but there are situation when a client does not know what revision is
645 * actually implemented by the server. */
646
647 /* 1. try to use <get-schema> */
648 if (clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200649 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200650 }
651
652 /* 2. try to get data locally */
653 if (!model_data) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200654 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200655 }
656 }
657
658 /* 3. try to use user callback */
659 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200660 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", name, rev ? rev : "<latest>");
Michal Vasko77367452021-02-16 16:32:18 +0100661 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
662 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200663 }
664
Jan Kundrát35972df2018-09-06 19:00:01 +0200665 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100666 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200667 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200668}
669
Michal Vaskoc088f982021-10-05 12:23:07 +0200670/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200671 * @brief Load a YANG module into context.
Michal Vaskoc088f982021-10-05 12:23:07 +0200672 *
673 * @param[in] session NC session.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200674 * @param[in] name Module name.
675 * @param[in] revision Module revision.
676 * @param[in] features Enabled module features.
677 * @param[in] modules Server module info built from capabilities.
678 * @param[in] user_clb User callback for retrieving module data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200679 * @param[in] user_data User data for @p user_clb.
680 * @param[in] has_get_schema Whether the server supports get-schema.
681 * @param[out] mod Loaded module.
682 * @return 0 on success.
683 * @return -1 on error.
684 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100685static int
Michal Vasko5e32c402022-01-12 14:05:09 +0100686nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features,
Michal Vasko5ca5d972022-09-14 13:51:31 +0200687 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 +0100688{
689 int ret = 0;
690 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200691 const char *module_data = NULL;
Michal Vasko5e32c402022-01-12 14:05:09 +0100692 struct ly_in *in;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200693 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200694
695 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200696 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100697
Michal Vasko5e32c402022-01-12 14:05:09 +0100698 /* try to use a module from the context */
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200699 *mod = ly_ctx_get_module_implemented(session->ctx, name);
700 if (!*mod) {
701 if (revision) {
702 *mod = ly_ctx_get_module(session->ctx, name, revision);
703 } else {
Michal Vasko5e32c402022-01-12 14:05:09 +0100704 *mod = ly_ctx_get_module_latest(session->ctx, name);
705 }
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200706 } else if (revision && (!(*mod)->revision || strcmp((*mod)->revision, revision))) {
707 WRN(session, "Server implements module \"%s\" in revision \"%s\" but revision \"%s\" is already implemented"
708 " and will be used instead.", name, revision, (*mod)->revision ? (*mod)->revision : "<none>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200709 }
Michal Vasko5e32c402022-01-12 14:05:09 +0100710
Michal Vaskoceae0152018-02-14 16:03:59 +0100711 if (*mod) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100712 /* make the present module implemented and/or enable all its features */
713 if (lys_set_implemented(*mod, features)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200714 ERR(session, "Failed to implement module \"%s\".", (*mod)->name);
Michal Vasko5e32c402022-01-12 14:05:09 +0100715 ret = -1;
Michal Vaskoceae0152018-02-14 16:03:59 +0100716 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200717 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100718 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200719 clb_data.has_get_schema = has_get_schema;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200720 clb_data.modules = modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200721 clb_data.session = session;
722 clb_data.user_clb = user_clb;
723 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100724
725 /* clear all the errors and just collect them for now */
726 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200727 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100728
Radek Krejci65ef6d52018-08-16 16:35:02 +0200729 /* get module data */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200730 retrieve_module_data(name, revision, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100731
Radek Krejci65ef6d52018-08-16 16:35:02 +0200732 if (module_data) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100733 /* set import callback */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200734 ly_ctx_set_module_imp_clb(session->ctx, retrieve_module_data_imp, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100735
Michal Vasko5ca5d972022-09-14 13:51:31 +0200736 /* parse the module */
Michal Vasko5e32c402022-01-12 14:05:09 +0100737 ly_in_new_memory(module_data, &in);
738 lys_parse(session->ctx, in, format, features, mod);
739 ly_in_free(in, 0);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200740 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100741 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100742 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100743
Radek Krejci65ef6d52018-08-16 16:35:02 +0200744 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
745 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100746
Michal Vaskodbf7c272018-02-19 09:07:59 +0100747 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200748 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100749 if (!(*mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100750 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100751 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100752 }
753 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200754 } else {
755 /* print only warnings */
Michal Vasko5e32c402022-01-12 14:05:09 +0100756 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200757 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100758 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200759 }
760 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100761 }
762
Michal Vaskodbf7c272018-02-19 09:07:59 +0100763 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100764 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100765 }
766
767 return ret;
768}
769
Radek Krejci65ef6d52018-08-16 16:35:02 +0200770static void
Michal Vasko5ca5d972022-09-14 13:51:31 +0200771free_module_info(struct module_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200772{
Michal Vasko77367452021-02-16 16:32:18 +0100773 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200774
Radek Krejci65ef6d52018-08-16 16:35:02 +0200775 if (!list) {
776 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200777 }
778
Radek Krejci65ef6d52018-08-16 16:35:02 +0200779 for (u = 0; list[u].name; ++u) {
780 free(list[u].name);
781 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100782 if (list[u].features) {
783 for (v = 0; list[u].features[v]; ++v) {
784 free(list[u].features[v]);
785 }
786 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200787 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200788 if (list[u].submodules) {
789 for (v = 0; list[u].submodules[v].name; ++v) {
790 free(list[u].submodules[v].name);
791 free(list[u].submodules[v].revision);
792 }
793 free(list[u].submodules);
794 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200795 }
796 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200797}
798
Michal Vaskoc088f982021-10-05 12:23:07 +0200799/**
Michal Vasko78939072022-12-12 07:43:18 +0100800 * @brief Retrieve yang-library and schema-mounts operational data from the server.
Michal Vaskoc088f982021-10-05 12:23:07 +0200801 *
802 * @param[in] session NC session.
803 * @param[in] has_get_data Whether get-data RPC is available or only get.
Michal Vasko78939072022-12-12 07:43:18 +0100804 * @param[in] filter Filter to use.
805 * @param[out] oper_data Received data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200806 * @return 0 on success.
807 * @return -1 on error.
808 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200809static int
Michal Vasko78939072022-12-12 07:43:18 +0100810get_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 +0200811{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200812 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100813 struct lyd_node *op = NULL, *envp = NULL;
814 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200815 NC_MSG_TYPE msg;
816 uint64_t msgid;
Michal Vasko303263d2021-10-05 12:18:21 +0200817 int ret = 0;
818 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200819
Michal Vasko78939072022-12-12 07:43:18 +0100820 /* get data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200821 if (has_get_data) {
Michal Vasko78939072022-12-12 07:43:18 +0100822 rpc_name = "<get-data>";
823 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 +0200824 } else {
Michal Vasko78939072022-12-12 07:43:18 +0100825 rpc_name = "<get>";
826 rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200827 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200828 if (!rpc) {
829 goto cleanup;
830 }
831
832 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
833 usleep(1000);
834 }
835 if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100836 WRN(session, "Failed to send %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200837 goto cleanup;
838 }
839
840 do {
Michal Vasko77367452021-02-16 16:32:18 +0100841 lyd_free_tree(envp);
842 lyd_free_tree(op);
843
844 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200845 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200846 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko78939072022-12-12 07:43:18 +0100847 WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200848 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100849 } else if (msg == NC_MSG_ERROR) {
Michal Vasko78939072022-12-12 07:43:18 +0100850 WRN(session, "Failed to receive a reply to %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200851 goto cleanup;
Jan Kundrát1d73a5a2022-03-30 16:54:49 +0200852 } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko78939072022-12-12 07:43:18 +0100853 WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200854 goto cleanup;
855 }
856
Michal Vasko77367452021-02-16 16:32:18 +0100857 data = (struct lyd_node_any *)lyd_child(op);
858 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko78939072022-12-12 07:43:18 +0100859 WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200860 goto cleanup;
861 } else if (!data->value.tree) {
Michal Vasko78939072022-12-12 07:43:18 +0100862 WRN(session, "No data in reply to a %s RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200863 goto cleanup;
864 }
865
Michal Vasko78939072022-12-12 07:43:18 +0100866 *oper_data = data->value.tree;
867 data->value.tree = NULL;
868
869cleanup:
870 nc_rpc_free(rpc);
871 lyd_free_tree(envp);
872 lyd_free_tree(op);
873
874 if (session->status != NC_STATUS_RUNNING) {
875 /* something bad happened, discard the session */
876 ERR(session, "Invalid session, discarding.");
877 ret = -1;
878 }
879
880 return ret;
881}
882
883/**
884 * @brief Build server module info from ietf-yang-library data.
885 *
886 * @param[in] session NC session.
887 * @param[in] get_data_sup Whether get-data RPC is available or only get.
888 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
889 * @param[out] result Server modules.
890 * @return 0 on success.
891 * @return -1 on error.
892 */
893static int
894build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result)
895{
896 struct ly_set *modules = NULL;
897 uint32_t u, v, submodules_count, feature_count;
898 struct lyd_node *iter, *child, *oper_data = NULL;
899 struct lys_module *mod;
900 int ret = 0;
901
902 /* get yang-library operational data */
903 if (xpath_sup) {
904 if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) {
905 goto cleanup;
906 }
907 } else {
908 if (get_oper_data(session, get_data_sup,
909 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", &oper_data)) {
910 goto cleanup;
911 }
912 }
913 if (!oper_data) {
914 goto cleanup;
915 }
916
917 if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) {
918 WRN(NULL, "No yang-library module information found.");
Radek Krejci2d088832018-08-21 13:40:14 +0200919 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200920 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200921
Michal Vasko77367452021-02-16 16:32:18 +0100922 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200923 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200924 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200925 ret = -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200926 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200927 }
928
Michal Vasko77367452021-02-16 16:32:18 +0100929 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200930 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100931 feature_count = 0;
932 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
933 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200934 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200935 /* ignore node from other schemas (augments) */
936 continue;
937 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200938 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200939 /* ignore empty nodes */
940 continue;
941 }
942 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200943 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200944 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200945 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200946 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200947 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200948 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100949 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
950 if (!(*result)[u].features) {
951 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200952 free_module_info(*result);
Michal Vasko77367452021-02-16 16:32:18 +0100953 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200954 ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100955 goto cleanup;
956 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200957 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100958 (*result)[u].features[feature_count + 1] = NULL;
959 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200960 } else if (!strcmp(iter->schema->name, "submodule")) {
961 submodules_count++;
962 }
963 }
964
965 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200966 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
967 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200968 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200969 free_module_info(*result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200970 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200971 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200972 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200973 } else {
974 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100975 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
976 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200977 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100978 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200979 if (mod != child->schema->module) {
980 continue;
981 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200982 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200983 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200984 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200985 }
986 }
987 }
988 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200989 }
990 }
991 }
992
Radek Krejcifd5b6682017-06-13 15:52:53 +0200993cleanup:
Michal Vasko78939072022-12-12 07:43:18 +0100994 lyd_free_siblings(oper_data);
Michal Vasko77367452021-02-16 16:32:18 +0100995 ly_set_free(modules, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200996 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200997}
998
Michal Vaskoc088f982021-10-05 12:23:07 +0200999/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001000 * @brief Build server module info from received capabilities.
Michal Vaskoc088f982021-10-05 12:23:07 +02001001 *
1002 * @param[in] cpblts Server capabilities.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001003 * @param[out] result Server modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001004 * @return 0 on success.
1005 * @return -1 on error.
1006 */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001007static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001008build_module_info_cpblts(char **cpblts, struct module_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001009{
Michal Vasko77367452021-02-16 16:32:18 +01001010 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001011 char *module_cpblt, *ptr, *ptr2;
1012
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001013 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +02001014 (*result) = calloc(u + 1, sizeof **result);
1015 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +02001016 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +02001017 return -1;
Radek Krejcic9390502018-08-17 10:23:13 +02001018 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001019
1020 for (u = v = 0; cpblts[u]; ++u) {
1021 module_cpblt = strstr(cpblts[u], "module=");
1022 /* this capability requires a module */
1023 if (!module_cpblt) {
1024 continue;
1025 }
1026
1027 /* get module's name */
1028 ptr = (char *)module_cpblt + 7;
1029 ptr2 = strchr(ptr, '&');
1030 if (!ptr2) {
1031 ptr2 = ptr + strlen(ptr);
1032 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001033 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001034
1035 /* get module's revision */
1036 ptr = strstr(module_cpblt, "revision=");
1037 if (ptr) {
1038 ptr += 9;
1039 ptr2 = strchr(ptr, '&');
1040 if (!ptr2) {
1041 ptr2 = ptr + strlen(ptr);
1042 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001043 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001044 }
1045
1046 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001047 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001048
1049 /* get module's features */
1050 ptr = strstr(module_cpblt, "features=");
1051 if (ptr) {
1052 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +01001053 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001054 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
1055 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +01001056 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1057 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1058 (*result)[v].features[feature_count + 1] = NULL;
1059 ++feature_count;
1060
Radek Krejci65ef6d52018-08-16 16:35:02 +02001061 ptr2 = ptr + 1;
1062 }
1063 }
1064 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +01001065 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1066 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1067 (*result)[v].features[feature_count + 1] = NULL;
1068 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001069 }
1070 ++v;
1071 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001072
Michal Vasko303263d2021-10-05 12:18:21 +02001073 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001074}
1075
Michal Vaskoc088f982021-10-05 12:23:07 +02001076/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001077 * @brief Fill client context based on server modules info.
Michal Vaskoc088f982021-10-05 12:23:07 +02001078 *
1079 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001080 * @param[in] modules Server modules info.
1081 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001082 * @param[in] user_data User data for @p user_clb.
1083 * @param[in] has_get_schema Whether server supports get-schema RPC.
1084 * @return 0 on success.
1085 * @return -1 on error.
1086 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001087static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001088nc_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 +01001089 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001090{
Michal Vasko303263d2021-10-05 12:18:21 +02001091 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +01001092 struct lys_module *mod;
1093 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001094
1095 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +02001096 /* skip import-only modules */
1097 if (!modules[u].implemented) {
1098 continue;
1099 }
1100
Radek Krejci65ef6d52018-08-16 16:35:02 +02001101 /* we can continue even if it fails */
Michal Vasko5e32c402022-01-12 14:05:09 +01001102 nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules,
1103 user_clb, user_data, has_get_schema, &mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001104
1105 if (!mod) {
1106 if (session->status != NC_STATUS_RUNNING) {
1107 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +02001108 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001109 goto cleanup;
1110 }
1111
Michal Vasko5ca5d972022-09-14 13:51:31 +02001112 /* all loading ways failed, the module will be ignored in the received data */
1113 WRN(session, "Failed to load module \"%s@%s\".", modules[u].name, modules[u].revision ?
Michal Vasko05532772021-06-03 12:12:38 +02001114 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001115 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001116 }
Michal Vasko60f66602017-10-17 13:52:18 +02001117 }
1118
Michal Vasko77367452021-02-16 16:32:18 +01001119 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001120 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001121
1122cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001123 return ret;
1124}
1125
Michal Vaskoc088f982021-10-05 12:23:07 +02001126/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001127 * @brief Fill client context with ietf-netconf module.
Michal Vaskoc088f982021-10-05 12:23:07 +02001128 *
1129 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001130 * @param[in] modules Server module info.
1131 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001132 * @param[in] user_data User data for @p user_clb.
1133 * @param[in] has_get_schema Whether server supports get-schema RPC.
1134 * @return 0 on success.
1135 * @return -1 on error.
1136 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001137static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001138nc_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 +01001139 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001140{
Michal Vasko77367452021-02-16 16:32:18 +01001141 uint32_t u;
Michal Vasko5e32c402022-01-12 14:05:09 +01001142 const char **features = NULL;
1143 struct ly_in *in;
Michal Vasko77367452021-02-16 16:32:18 +01001144 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001145
Michal Vasko5e32c402022-01-12 14:05:09 +01001146 /* find supported features (capabilities) in ietf-netconf */
1147 for (u = 0; modules[u].name; ++u) {
1148 if (!strcmp(modules[u].name, "ietf-netconf")) {
1149 assert(modules[u].implemented);
1150 features = (const char **)modules[u].features;
1151 break;
1152 }
1153 }
1154 if (!modules[u].name) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001155 ERR(session, "Base NETCONF module not supported by the server.");
Michal Vasko5e32c402022-01-12 14:05:09 +01001156 return -1;
1157 }
1158
Michal Vasko77367452021-02-16 16:32:18 +01001159 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vasko5e32c402022-01-12 14:05:09 +01001160 if (ietfnc) {
1161 /* make sure to enable all the features if already loaded */
1162 lys_set_implemented(ietfnc, features);
1163 } else {
1164 /* load the module */
1165 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 +02001166 if (!ietfnc) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001167 ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in);
1168 lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc);
1169 ly_in_free(in, 0);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001170 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001171 }
1172 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001173 ERR(session, "Loading base NETCONF module failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001174 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001175 }
1176
Radek Krejci65ef6d52018-08-16 16:35:02 +02001177 return 0;
1178}
1179
Michal Vasko78939072022-12-12 07:43:18 +01001180/**
1181 * @brief Set client session context to support schema-mount if possible.
1182 *
1183 * @param[in] session NC session with the context to modify.
1184 * @param[in] get_data_sup Whether get-data RPC is available or only get.
1185 * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter.
1186 * @return 0 on success.
1187 * @return -1 on error.
1188 */
1189static int
1190nc_ctx_schema_mount(struct nc_session *session, int get_data_sup, int xpath_sup)
1191{
1192 int rc = 0;
1193 struct lyd_node *oper_data = NULL;
1194
1195 if (session->flags & NC_SESSION_SHAREDCTX) {
1196 /* context is already fully set up */
1197 goto cleanup;
1198 }
1199
1200 /* get yang-library and schema-mounts operational data */
1201 if (xpath_sup) {
1202 if ((rc = get_oper_data(session, get_data_sup, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) {
1203 goto cleanup;
1204 }
1205 } else {
1206 if ((rc = get_oper_data(session, get_data_sup,
1207 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>"
1208 "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"/>", &oper_data))) {
1209 goto cleanup;
1210 }
1211 }
1212
1213 if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) {
1214 /* no schema-mounts operational data */
1215 goto cleanup;
1216 }
1217
1218 /* validate the data for the parent reference prefixes to be resolved */
1219 if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) {
1220 ERR(session, "Invalid operational data received from the server (%s).", ly_errmsg(LYD_CTX(oper_data)));
1221 rc = -1;
1222 goto cleanup;
1223 }
1224
1225 /* store the data in the session */
1226 session->opts.client.ext_data = oper_data;
1227 oper_data = NULL;
1228
1229cleanup:
1230 lyd_free_siblings(oper_data);
1231 return rc;
1232}
1233
Michal Vasko086311b2016-01-08 09:53:11 +01001234int
1235nc_ctx_check_and_fill(struct nc_session *session)
1236{
Michal Vaskod6c6f852022-12-14 15:37:21 +01001237 int i, get_schema_support = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001238 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001239 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001240 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001241 char *revision;
Michal Vasko5ca5d972022-09-14 13:51:31 +02001242 struct module_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001243
Michal Vasko2e6defd2016-10-07 15:48:15 +02001244 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001245
Michal Vaskoa272e092023-07-10 14:34:03 +02001246 if (client_opts.auto_context_fill_disabled) {
1247 VRB(session, "Context of the new session is left only with the default YANG modules.");
1248 return 0;
1249 }
1250
Radek Krejci65ef6d52018-08-16 16:35:02 +02001251 /* 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 +02001252 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001253
Radek Krejci65ef6d52018-08-16 16:35:02 +02001254 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
Michal Vasko5ca5d972022-09-14 13:51:31 +02001255 * modules only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001256 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001257
1258 /* our callback is set later with appropriate data */
1259 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1260
1261 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001262 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001263 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001264 get_schema_support = 1 + i;
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001265 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001266 yanglib_support = 1 + i;
Michal Vasko78939072022-12-12 07:43:18 +01001267 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) {
1268 xpath_support = 1 + i;
Michal Vaskod6c6f852022-12-14 15:37:21 +01001269 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda", 45)) {
1270 nmda_support = 1 + i;
Michal Vasko086311b2016-01-08 09:53:11 +01001271 }
1272 }
Michal Vaskod6c6f852022-12-14 15:37:21 +01001273 VRB(session, "Capability for <get-schema> support%s found.", get_schema_support ? "" : " not");
1274 VRB(session, "Capability for yang-library support%s found.", yanglib_support ? "" : " not");
1275 VRB(session, "Capability for XPath filter support%s found.", xpath_support ? "" : " not");
1276 VRB(session, "Capability for NMDA RPCs support%s found.", nmda_support ? "" : " not");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001277
Michal Vasko5ca5d972022-09-14 13:51:31 +02001278 /* get information about server's modules from capabilities list until we will have yang-library */
1279 if (build_module_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1280 ERR(session, "Unable to get server module information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001281 goto cleanup;
1282 }
1283
Michal Vasko086311b2016-01-08 09:53:11 +01001284 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001285 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 +02001286 WRN(session, "Loading NETCONF monitoring module failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001287 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001288 }
1289
1290 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001291 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001292 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001293 }
1294
Radek Krejci65ef6d52018-08-16 16:35:02 +02001295 /* get correct version of ietf-yang-library into context */
1296 if (yanglib_support) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001297 /* use get-schema to get server's ietf-yang-library */
Radek Krejcib1d250e2018-04-12 13:54:18 +02001298 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1299 if (!revision) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001300 WRN(session, "Loading NETCONF ietf-yang-library module failed, missing revision in NETCONF <hello> message.");
Michal Vasko05532772021-06-03 12:12:38 +02001301 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001302 yanglib_support = 0;
1303 } else {
1304 revision = strndup(&revision[9], 10);
Michal Vasko5e32c402022-01-12 14:05:09 +01001305 if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001306 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001307 WRN(session, "Loading NETCONF ietf-yang-library module failed, unable to use it to learn all "
Michal Vasko05532772021-06-03 12:12:38 +02001308 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001309 yanglib_support = 0;
1310 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001311 if (strcmp(revision, "2019-01-04") >= 0) {
1312 /* we also need ietf-datastores to be implemented */
Michal Vasko5e32c402022-01-12 14:05:09 +01001313 if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001314 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001315 WRN(session, "Loading NETCONF ietf-datastores module failed, unable to use yang-library "
Michal Vasko05532772021-06-03 12:12:38 +02001316 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001317 yanglib_support = 0;
1318 }
1319 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001320 free(revision);
1321 }
1322 }
1323
Michal Vaskod6c6f852022-12-14 15:37:21 +01001324 /* ietf-netconf-nmda is needed to issue get-data */
1325 if (nmda_support && nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb, old_data,
1326 get_schema_support, &mod)) {
1327 WRN(session, "Loading NMDA module failed, unable to use <get-data>.");
1328 nmda_support = 0;
1329 }
1330
Michal Vasko5ca5d972022-09-14 13:51:31 +02001331 /* prepare structured information about server's modules */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001332 if (yanglib_support) {
Michal Vaskod6c6f852022-12-14 15:37:21 +01001333 if (build_module_info_yl(session, nmda_support, xpath_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001334 goto cleanup;
1335 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001336 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001337 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001338 /* prefer yang-library information, currently we have it from capabilities used for getting correct
Michal Vasko5ca5d972022-09-14 13:51:31 +02001339 * yang-library module */
1340 free_module_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001341 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001342 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001343 }
Michal Vaskoef578332016-01-25 13:20:09 +01001344
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001345 /* compile all modules at once to avoid invalid errors or warnings */
1346 ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
1347
1348 /* fill the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001349 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1350 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001351 }
1352
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001353 /* compile it */
1354 if (ly_ctx_compile(session->ctx)) {
1355 goto cleanup;
1356 }
1357
Michal Vasko78939072022-12-12 07:43:18 +01001358 /* set support for schema-mount, if possible */
Michal Vaskod6c6f852022-12-14 15:37:21 +01001359 if (nc_ctx_schema_mount(session, nmda_support, xpath_support)) {
Michal Vasko78939072022-12-12 07:43:18 +01001360 goto cleanup;
1361 }
1362
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001363 /* success */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001364 ret = 0;
1365
Michal Vaskoeee99412016-11-21 10:19:43 +01001366 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001367 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 +02001368 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001369 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001370
1371cleanup:
Michal Vasko5ca5d972022-09-14 13:51:31 +02001372 free_module_info(server_modules);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001373
Radek Krejcifd5b6682017-06-13 15:52:53 +02001374 /* set user callback back */
1375 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001376 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001377 ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001378
Michal Vaskoef578332016-01-25 13:20:09 +01001379 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001380}
1381
1382API struct nc_session *
1383nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1384{
Michal Vaskod083db62016-01-19 10:31:29 +01001385 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001386
Michal Vasko45e53ae2016-04-07 11:46:03 +02001387 if (fdin < 0) {
roman40672412023-05-04 11:10:22 +02001388 ERRARG(NULL, "fdin");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001389 return NULL;
1390 } else if (fdout < 0) {
roman40672412023-05-04 11:10:22 +02001391 ERRARG(NULL, "fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001392 return NULL;
1393 }
1394
1395 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001396 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001397 if (!session) {
1398 ERRMEM;
1399 return NULL;
1400 }
1401 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001402
1403 /* transport specific data */
1404 session->ti_type = NC_TI_FD;
1405 session->ti.fd.in = fdin;
1406 session->ti.fd.out = fdout;
1407
Michal Vasko78939072022-12-12 07:43:18 +01001408 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001409 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001410 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001411 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001412
1413 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001414 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001415 goto fail;
1416 }
1417 session->status = NC_STATUS_RUNNING;
1418
Michal Vaskoef578332016-01-25 13:20:09 +01001419 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001420 goto fail;
1421 }
1422
1423 return session;
1424
1425fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001426 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001427 return NULL;
1428}
1429
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001430API struct nc_session *
1431nc_connect_unix(const char *address, struct ly_ctx *ctx)
1432{
1433 struct nc_session *session = NULL;
1434 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001435 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001436 char *username;
1437 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001438 char *buf = NULL;
1439 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001440
roman40672412023-05-04 11:10:22 +02001441 NC_CHECK_ARG_RET(NULL, address, NULL);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001442
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001443 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1444 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001445 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001446 goto fail;
1447 }
1448
1449 memset(&sun, 0, sizeof(sun));
1450 sun.sun_family = AF_UNIX;
1451 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1452
1453 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001454 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001455 goto fail;
1456 }
1457
1458 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001459 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001460 goto fail;
1461 }
1462
1463 /* prepare session structure */
1464 session = nc_new_session(NC_CLIENT, 0);
1465 if (!session) {
1466 ERRMEM;
1467 goto fail;
1468 }
1469 session->status = NC_STATUS_STARTING;
1470
1471 /* transport specific data */
1472 session->ti_type = NC_TI_UNIX;
1473 session->ti.unixsock.sock = sock;
1474 sock = -1; /* do not close sock in fail label anymore */
1475
Michal Vasko78939072022-12-12 07:43:18 +01001476 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Robin Jarry4e38e292019-10-15 17:14:14 +02001477 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001478 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001479 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001480
Michal Vasko93224072021-11-09 12:14:28 +01001481 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001482
romanf6e32012023-04-24 15:51:26 +02001483 pw = nc_getpw(geteuid(), NULL, &pw_buf, &buf, &buf_size);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001484 if (!pw) {
1485 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1486 goto fail;
1487 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001488 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001489 free(buf);
1490 if (!username) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001491 ERRMEM;
1492 goto fail;
1493 }
Michal Vasko93224072021-11-09 12:14:28 +01001494 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001495
1496 /* NETCONF handshake */
1497 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1498 goto fail;
1499 }
1500 session->status = NC_STATUS_RUNNING;
1501
1502 if (nc_ctx_check_and_fill(session) == -1) {
1503 goto fail;
1504 }
1505
1506 return session;
1507
1508fail:
1509 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001510 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001511 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001512 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001513 return NULL;
1514}
1515
Michal Vasko056f53c2022-10-21 13:38:15 +02001516/**
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001517 * @brief Convert socket IP address to string.
1518 *
1519 * @param[in] saddr Sockaddr to convert.
1520 * @param[out] str_ip String IP address.
Michal Vaskoc07d9762023-03-27 10:32:18 +02001521 * @param[out] port Optional port.
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001522 * @return 0 on success.
1523 * @return -1 on error.
1524 */
1525static int
Michal Vaskoc07d9762023-03-27 10:32:18 +02001526nc_saddr2str(const struct sockaddr *saddr, char **str_ip, uint16_t *port)
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001527{
1528 void *addr;
1529 socklen_t str_len;
1530
1531 assert((saddr->sa_family == AF_INET) || (saddr->sa_family == AF_INET6));
1532
1533 str_len = (saddr->sa_family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
1534 *str_ip = malloc(str_len);
1535 if (!*str_ip) {
1536 ERRMEM;
1537 return -1;
1538 }
1539
1540 if (saddr->sa_family == AF_INET) {
Michal Vaskoc0f85ca2023-03-27 10:19:00 +02001541 addr = &((struct sockaddr_in *)saddr)->sin_addr;
Michal Vaskoc07d9762023-03-27 10:32:18 +02001542 if (port) {
1543 *port = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1544 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001545 } else {
Michal Vaskoc0f85ca2023-03-27 10:19:00 +02001546 addr = &((struct sockaddr_in6 *)saddr)->sin6_addr;
Michal Vaskoc07d9762023-03-27 10:32:18 +02001547 if (port) {
1548 *port = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1549 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001550 }
1551 if (!inet_ntop(saddr->sa_family, addr, *str_ip, str_len)) {
1552 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
1553 free(*str_ip);
1554 return -1;
1555 }
1556
1557 return 0;
1558}
1559
1560/**
Michal Vasko056f53c2022-10-21 13:38:15 +02001561 * @brief Try to connect a socket, optionally a pending one from a previous attempt.
1562 *
1563 * @param[in] timeout_ms Timeout in ms to wait for the connection to be fully established, -1 to block.
1564 * @param[in,out] sock_pending Optional previously created socked that was not fully connected yet. If provided and
1565 * connected, is set to -1.
1566 * @param[in] res Addrinfo resource to use when creating a new socket.
1567 * @param[in] ka Keepalives to set.
1568 * @return Connected socket or -1 on error.
Frank Rimpler9f838b02018-07-25 06:44:03 +00001569 */
1570static int
Michal Vasko056f53c2022-10-21 13:38:15 +02001571sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001572{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001573 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001574 int sock = -1;
Michal Vaskob9336672023-10-11 09:27:30 +02001575 struct pollfd fds = {0};
Frank Rimpler9f838b02018-07-25 06:44:03 +00001576 socklen_t len = sizeof(int);
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001577 uint16_t port;
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001578 char *str;
Michal Vasko086311b2016-01-08 09:53:11 +01001579
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001580 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001581 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001582 sock = *sock_pending;
1583 } else {
1584 assert(res);
Michal Vaskoc07d9762023-03-27 10:32:18 +02001585 if (nc_saddr2str(res->ai_addr, &str, &port)) {
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001586 return -1;
1587 }
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001588 VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
1589 free(str);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001590
1591 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001592 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1593 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001594 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001595 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001596 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001597 /* make the socket non-blocking */
1598 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001599 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001600 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001601 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001602 /* non-blocking connect! */
1603 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1604 if (errno != EINPROGRESS) {
1605 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001606 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001607 goto cleanup;
1608 }
1609 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001610 }
Michal Vasko7d965dc2018-03-12 14:39:23 +01001611
Michal Vaskob9336672023-10-11 09:27:30 +02001612 fds.fd = sock;
1613 fds.events = POLLOUT;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001614
Michal Vaskob9336672023-10-11 09:27:30 +02001615 /* wait until we can write data to the socket */
1616 ret = poll(&fds, 1, timeout_ms);
1617 if (ret == -1) {
1618 /* error */
1619 ERR(NULL, "poll() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001620 goto cleanup;
Michal Vaskob9336672023-10-11 09:27:30 +02001621 } else if (ret == 0) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001622 /* there was a timeout */
Michal Vasko056f53c2022-10-21 13:38:15 +02001623 VRB(NULL, "Timed out after %d ms (%s).", timeout_ms, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001624 if (sock_pending) {
1625 /* no sock-close, we'll try it again */
1626 *sock_pending = sock;
1627 } else {
1628 close(sock);
1629 }
1630 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001631 }
Radek Krejci782041a2018-08-20 10:09:45 +02001632
1633 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001634 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001635 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001636 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001637 goto cleanup;
1638 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001639 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001640 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001641 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001642 errno = error;
1643 goto cleanup;
1644 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001645
1646 /* enable keep-alive */
romanc1d2b092023-02-02 08:58:27 +01001647 if (nc_sock_configure_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001648 goto cleanup;
1649 }
1650
Michal Vasko056f53c2022-10-21 13:38:15 +02001651 /* connected */
1652 if (sock_pending) {
1653 *sock_pending = -1;
1654 }
Michal Vasko086311b2016-01-08 09:53:11 +01001655 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001656
Frank Rimpler9f838b02018-07-25 06:44:03 +00001657cleanup:
1658 if (sock_pending) {
1659 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001660 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001661 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001662 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001663}
1664
Frank Rimpler9f838b02018-07-25 06:44:03 +00001665int
Michal Vasko056f53c2022-10-21 13:38:15 +02001666nc_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 +00001667{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001668 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001669 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001670 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001671 char port_s[6]; /* length of string representation of short int */
1672 struct sockaddr_storage saddr;
1673 socklen_t addr_len = sizeof saddr;
1674
1675 *ip_host = NULL;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001676
Michal Vasko056f53c2022-10-21 13:38:15 +02001677 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout_ms, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001678
1679 /* no pending socket */
1680 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001681 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001682 snprintf(port_s, 6, "%u", port);
1683 memset(&hints, 0, sizeof hints);
1684 hints.ai_family = AF_UNSPEC;
1685 hints.ai_socktype = SOCK_STREAM;
1686 hints.ai_protocol = IPPROTO_TCP;
1687 i = getaddrinfo(host, port_s, &hints, &res_list);
1688 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001689 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001690 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001691 }
1692
1693 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vasko056f53c2022-10-21 13:38:15 +02001694 sock = sock_connect(timeout_ms, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001695 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001696 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001697 /* try the next resource */
1698 continue;
1699 } else {
1700 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001701 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001702 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001703 }
Michal Vasko05532772021-06-03 12:12:38 +02001704 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 +01001705
1706 opt = 1;
1707 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001708 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001709 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001710 }
1711
Michal Vaskoc07d9762023-03-27 10:32:18 +02001712 if (nc_saddr2str(res->ai_addr, ip_host, NULL)) {
Michal Vasko7b1c4c22023-03-24 13:27:40 +01001713 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001714 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001715 break;
1716 }
1717 freeaddrinfo(res_list);
1718
1719 } else {
1720 /* try to get a connection with the pending socket */
1721 assert(sock_pending);
Michal Vasko056f53c2022-10-21 13:38:15 +02001722 sock = sock_connect(timeout_ms, sock_pending, NULL, ka);
Tie.Liao9bca73d2023-03-23 17:25:00 +01001723
1724 if (sock > 0) {
Barbaros Tokaoglu96da5912023-06-19 18:57:05 +03001725 if (getpeername(sock, (struct sockaddr *)&saddr, &addr_len)) {
1726 ERR(NULL, "getpeername failed (%s).", strerror(errno));
Tie.Liao9bca73d2023-03-23 17:25:00 +01001727 goto error;
1728 }
1729
Michal Vaskoc07d9762023-03-27 10:32:18 +02001730 if (nc_saddr2str((struct sockaddr *)&saddr, ip_host, NULL)) {
Tie.Liao9bca73d2023-03-23 17:25:00 +01001731 goto error;
1732 }
Tie.Liao9bca73d2023-03-23 17:25:00 +01001733 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001734 }
1735
1736 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001737
1738error:
Michal Vasko0be85692021-03-02 08:04:57 +01001739 if (res_list) {
1740 freeaddrinfo(res_list);
1741 }
Michal Vaskocb846632019-12-13 15:12:45 +01001742 if (sock != -1) {
1743 close(sock);
1744 }
1745 if (sock_pending) {
1746 *sock_pending = -1;
1747 }
1748 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001749}
1750
roman2eab4742023-06-06 10:00:26 +02001751#ifdef NC_ENABLED_SSH_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001752
Michal Vasko3031aae2016-01-27 16:07:18 +01001753int
Michal Vasko9d4cca52022-09-07 11:19:57 +02001754nc_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 +01001755{
1756 int sock;
1757
roman40672412023-05-04 11:10:22 +02001758 NC_CHECK_ARG_RET(NULL, address, port, -1);
Michal Vasko3031aae2016-01-27 16:07:18 +01001759
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001760 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001761 if (sock == -1) {
1762 return -1;
1763 }
1764
1765 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001766 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1767 if (!client_opts.ch_binds) {
1768 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001769 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001770 return -1;
1771 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001772
Michal Vasko9d4cca52022-09-07 11:19:57 +02001773 client_opts.ch_binds_aux = nc_realloc(client_opts.ch_binds_aux, client_opts.ch_bind_count * sizeof *client_opts.ch_binds_aux);
1774 if (!client_opts.ch_binds_aux) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001775 ERRMEM;
1776 close(sock);
1777 return -1;
1778 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001779 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].ti = ti;
1780 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].hostname = hostname ? strdup(hostname) : NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001781
Michal Vasko3031aae2016-01-27 16:07:18 +01001782 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
1783 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1784 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001785 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001786
1787 return 0;
1788}
1789
1790int
1791nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1792{
1793 uint32_t i;
1794 int ret = -1;
1795
1796 if (!address && !port && !ti) {
1797 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1798 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001799 free(client_opts.ch_binds[i].address);
1800
1801 free(client_opts.ch_binds_aux[i].hostname);
Michal Vasko3031aae2016-01-27 16:07:18 +01001802
1803 ret = 0;
1804 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001805 client_opts.ch_bind_count = 0;
1806
Michal Vasko3031aae2016-01-27 16:07:18 +01001807 free(client_opts.ch_binds);
1808 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001809
1810 free(client_opts.ch_binds_aux);
1811 client_opts.ch_binds_aux = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001812 } else {
1813 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001814 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1815 (!port || (client_opts.ch_binds[i].port == port)) &&
Michal Vasko9d4cca52022-09-07 11:19:57 +02001816 (!ti || (client_opts.ch_binds_aux[i].ti == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001817 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001818 free(client_opts.ch_binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001819
1820 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001821 if (!client_opts.ch_bind_count) {
1822 free(client_opts.ch_binds);
1823 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001824
1825 free(client_opts.ch_binds_aux);
1826 client_opts.ch_binds_aux = NULL;
Michal Vasko66c762a2016-10-13 10:34:14 +02001827 } else if (i < client_opts.ch_bind_count) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001828 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count],
1829 sizeof *client_opts.ch_binds);
1830
1831 memcpy(&client_opts.ch_binds_aux[i], &client_opts.ch_binds_aux[client_opts.ch_bind_count],
1832 sizeof *client_opts.ch_binds_aux);
Michal Vasko66c762a2016-10-13 10:34:14 +02001833 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001834
1835 ret = 0;
1836 }
1837 }
1838 }
1839
1840 return ret;
1841}
1842
1843API int
1844nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1845{
1846 int sock;
1847 char *host = NULL;
1848 uint16_t port, idx;
1849
roman40672412023-05-04 11:10:22 +02001850 NC_CHECK_ARG_RET(NULL, session, -1);
1851
Michal Vasko45e53ae2016-04-07 11:46:03 +02001852 if (!client_opts.ch_binds) {
1853 ERRINIT;
1854 return -1;
Michal Vasko3031aae2016-01-27 16:07:18 +01001855 }
1856
roman9075eab2023-09-14 10:07:26 +02001857 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, &client_opts.ch_bind_lock, timeout, &host, &port, &idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001858 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001859 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001860 return sock;
1861 }
1862
Michal Vasko9d4cca52022-09-07 11:19:57 +02001863 if (client_opts.ch_binds_aux[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001864 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
roman2eab4742023-06-06 10:00:26 +02001865 } else if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001866 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT,
1867 client_opts.ch_binds_aux[idx].hostname);
roman2eab4742023-06-06 10:00:26 +02001868 } else {
Michal Vaskofee717c2016-02-01 13:25:43 +01001869 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001870 *session = NULL;
1871 }
1872
1873 free(host);
1874
1875 if (!(*session)) {
1876 return -1;
1877 }
1878
1879 return 1;
1880}
1881
roman2eab4742023-06-06 10:00:26 +02001882#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001883
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001884API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001885nc_session_get_cpblts(const struct nc_session *session)
1886{
roman40672412023-05-04 11:10:22 +02001887 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vaskobdfb5242016-05-24 09:11:01 +02001888
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001889 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001890}
1891
1892API const char *
1893nc_session_cpblt(const struct nc_session *session, const char *capab)
1894{
1895 int i, len;
1896
roman40672412023-05-04 11:10:22 +02001897 NC_CHECK_ARG_RET(session, session, capab, NULL);
Michal Vaskobdfb5242016-05-24 09:11:01 +02001898
1899 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001900 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1901 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1902 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001903 }
1904 }
1905
1906 return NULL;
1907}
1908
Michal Vasko9cd26a82016-05-31 08:58:48 +02001909API int
1910nc_session_ntf_thread_running(const struct nc_session *session)
1911{
roman40672412023-05-04 11:10:22 +02001912 NC_CHECK_ARG_RET(session, session, 0);
1913
1914 if (session->side != NC_CLIENT) {
1915 ERRARG(NULL, "session");
Michal Vasko9cd26a82016-05-31 08:58:48 +02001916 return 0;
1917 }
1918
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02001919 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running);
Michal Vasko9cd26a82016-05-31 08:58:48 +02001920}
1921
roman9075eab2023-09-14 10:07:26 +02001922API int
1923nc_client_init(void)
1924{
1925 int r;
1926
1927 if ((r = pthread_mutex_init(&client_opts.ch_bind_lock, NULL))) {
1928 ERR(NULL, "%s: failed to init bind lock(%s).", __func__, strerror(r));
1929 return -1;
1930 }
1931
1932 return 0;
1933}
1934
Michal Vaskob7558c52016-02-26 15:04:19 +01001935API void
1936nc_client_destroy(void)
1937{
roman9075eab2023-09-14 10:07:26 +02001938 pthread_mutex_destroy(&client_opts.ch_bind_lock);
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001939 nc_client_set_schema_searchpath(NULL);
roman2eab4742023-06-06 10:00:26 +02001940#ifdef NC_ENABLED_SSH_TLS
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001941 nc_client_ch_del_bind(NULL, 0, 0);
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001942 nc_client_ssh_destroy_opts();
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001943 nc_client_tls_destroy_opts();
roman2eab4742023-06-06 10:00:26 +02001944#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskob7558c52016-02-26 15:04:19 +01001945}
1946
Michal Vasko77367452021-02-16 16:32:18 +01001947static NC_MSG_TYPE
1948recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001949{
Michal Vasko77367452021-02-16 16:32:18 +01001950 char *ptr;
1951 struct lyd_attr *attr;
1952 uint64_t cur_msgid;
1953
1954 assert(envp && !envp->schema);
1955
1956 /* find the message-id attribute */
1957 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1958 if (!strcmp(attr->name.name, "message-id")) {
1959 break;
1960 }
1961 }
1962
1963 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001964 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001965 return NC_MSG_REPLY_ERR_MSGID;
1966 }
1967
1968 cur_msgid = strtoul(attr->value, &ptr, 10);
1969 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001970 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1971 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001972 return NC_MSG_REPLY_ERR_MSGID;
1973 }
1974
1975 return NC_MSG_REPLY;
1976}
1977
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001978/**
1979 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1980 *
1981 * @param[in] session NETCONF session used to send error messages.
1982 * @param[in] msg Message to check for type.
1983 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1984 * @return NC_MSG_NOTIF If format roughly matches a notification;
1985 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1986 */
1987static NC_MSG_TYPE
1988get_msg_type(struct nc_session *session, struct ly_in *msg)
1989{
1990 const char *str, *end;
1991
1992 str = ly_in_memory(msg, NULL);
1993
1994 while (*str) {
1995 /* Skip whitespaces */
1996 while (isspace(*str)) {
1997 str++;
1998 }
1999
2000 if (*str == '<') {
2001 str++;
2002 if (!strncmp(str, "!--", 3)) {
2003 /* Skip comments */
2004 end = "-->";
2005 str = strstr(str, end);
2006 } else if (!strncmp(str, "?xml", 4)) {
2007 /* Skip xml declaration */
2008 end = "?>";
2009 str = strstr(str, end);
2010 } else if (!strncmp(str, "rpc-reply", 9)) {
2011 return NC_MSG_REPLY;
2012 } else if (!strncmp(str, "notification", 12)) {
2013 return NC_MSG_NOTIF;
2014 } else {
2015 ERR(session, "Unknown xml element '%.10s'.", str);
2016 return NC_MSG_ERROR;
2017 }
2018 if (!str) {
2019 /* No matching ending tag found */
2020 ERR(session, "No matching ending tag '%s' found in xml message.", end);
2021 return NC_MSG_ERROR;
2022 }
2023 str += strlen(end);
2024 } else {
2025 /* Not a valid xml */
2026 ERR(session, "Unexpected character '%c' in xml message.", *str);
2027 return NC_MSG_ERROR;
2028 }
2029 }
2030
2031 /* Unexpected end of message */
2032 ERR(session, "Unexpected end of xml message.");
2033 return NC_MSG_ERROR;
2034}
2035
2036/**
2037 * @brief Function to receive either replies or notifications.
2038 *
2039 * @param[in] session NETCONF session from which this function receives messages.
2040 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
2041 * @param[in] expected Type of the message the caller desired.
2042 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
2043 * @return NC_MSG_REPLY If a rpc-reply was received;
2044 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01002045 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002046 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
2047 */
2048static NC_MSG_TYPE
2049recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
2050{
2051 struct nc_msg_cont **cont_ptr;
2052 struct ly_in *msg = NULL;
2053 struct nc_msg_cont *cont, *prev;
2054 NC_MSG_TYPE ret = NC_MSG_ERROR;
2055 int r;
2056
2057 *message = NULL;
2058
2059 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002060 r = nc_session_client_msgs_lock(session, &timeout, __func__);
2061 if (!r) {
2062 ret = NC_MSG_WOULDBLOCK;
2063 goto cleanup;
2064 } else if (r == -1) {
2065 ret = NC_MSG_ERROR;
2066 goto cleanup;
2067 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002068
2069 /* Find the expected message in the buffer */
2070 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002071 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002072 prev = cont;
2073 }
2074
2075 if (cont) {
2076 /* Remove found message from buffer */
2077 if (prev) {
2078 prev->next = cont->next;
2079 } else {
2080 session->opts.client.msgs = cont->next;
2081 }
2082
2083 /* Use the buffer message */
2084 ret = cont->type;
2085 msg = cont->msg;
2086 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02002087 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002088 }
2089
2090 /* Read a message from the wire */
2091 r = nc_read_msg_poll_io(session, timeout, &msg);
2092 if (!r) {
2093 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02002094 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002095 } else if (r == -1) {
2096 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002097 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002098 }
2099
2100 /* Basic check to determine message type */
2101 ret = get_msg_type(session, msg);
2102 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02002103 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002104 }
2105
2106 /* If received a message of different type store it in the buffer */
2107 if (ret != expected) {
2108 cont_ptr = &session->opts.client.msgs;
2109 while (*cont_ptr) {
2110 cont_ptr = &((*cont_ptr)->next);
2111 }
2112 *cont_ptr = malloc(sizeof **cont_ptr);
2113 if (!*cont_ptr) {
2114 ERRMEM;
2115 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02002116 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002117 }
2118 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002119 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02002120 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002121 (*cont_ptr)->next = NULL;
2122 }
2123
Michal Vasko01130bd2021-08-26 11:47:38 +02002124cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002125 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002126 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002127
Michal Vasko01130bd2021-08-26 11:47:38 +02002128cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002129 if (ret == expected) {
2130 *message = msg;
2131 } else {
2132 ly_in_free(msg, 1);
2133 }
2134 return ret;
2135}
2136
Michal Vasko77367452021-02-16 16:32:18 +01002137static NC_MSG_TYPE
2138recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
2139{
Michal Vasko77367452021-02-16 16:32:18 +01002140 LY_ERR lyrc;
2141 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002142 NC_MSG_TYPE ret = NC_MSG_ERROR;
2143
2144 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
2145
2146 *envp = NULL;
2147
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002148 /* Receive messages until a rpc-reply is found or a timeout or error reached */
2149 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
2150 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01002151 goto cleanup;
2152 }
2153
2154 /* parse */
2155 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
2156 if (!lyrc) {
2157 ret = recv_reply_check_msgid(session, *envp, msgid);
2158 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002159 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002160 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
Michal Vasko9a108052022-04-01 12:06:54 +02002161 lyd_free_tree(*envp);
2162 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002163 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002164 goto cleanup;
2165 }
2166
Michal Vasko77367452021-02-16 16:32:18 +01002167cleanup:
2168 ly_in_free(msg, 1);
2169 return ret;
2170}
2171
2172static int
2173recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2174{
Michal Vasko143aa142021-10-01 15:31:48 +02002175 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002176 struct nc_rpc_act_generic *rpc_gen;
2177 struct ly_in *in;
2178 struct lyd_node *tree, *op2;
2179 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002180 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002181
2182 switch (rpc->type) {
2183 case NC_RPC_ACT_GENERIC:
2184 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2185 if (rpc_gen->has_data) {
2186 tree = rpc_gen->content.data;
2187
2188 /* find the operation node */
2189 lyrc = LY_EINVAL;
2190 LYD_TREE_DFS_BEGIN(tree, op2) {
2191 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2192 lyrc = lyd_dup_single(op2, NULL, 0, op);
2193 break;
2194 }
2195 LYD_TREE_DFS_END(tree, op2);
2196 }
2197 } else {
2198 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2199 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2200 ly_in_free(in, 0);
2201 if (lyrc) {
Michal Vasko9a108052022-04-01 12:06:54 +02002202 lyd_free_tree(tree);
Michal Vasko77367452021-02-16 16:32:18 +01002203 return -1;
2204 }
2205
2206 /* we want just the operation node */
2207 lyrc = lyd_dup_single(op2, NULL, 0, op);
2208
2209 lyd_free_tree(tree);
2210 }
2211 break;
2212 case NC_RPC_GETCONFIG:
2213 module_name = "ietf-netconf";
2214 rpc_name = "get-config";
2215 break;
2216 case NC_RPC_EDIT:
2217 module_name = "ietf-netconf";
2218 rpc_name = "edit-config";
2219 break;
2220 case NC_RPC_COPY:
2221 module_name = "ietf-netconf";
2222 rpc_name = "copy-config";
2223 break;
2224 case NC_RPC_DELETE:
2225 module_name = "ietf-netconf";
2226 rpc_name = "delete-config";
2227 break;
2228 case NC_RPC_LOCK:
2229 module_name = "ietf-netconf";
2230 rpc_name = "lock";
2231 break;
2232 case NC_RPC_UNLOCK:
2233 module_name = "ietf-netconf";
2234 rpc_name = "unlock";
2235 break;
2236 case NC_RPC_GET:
2237 module_name = "ietf-netconf";
2238 rpc_name = "get";
2239 break;
2240 case NC_RPC_KILL:
2241 module_name = "ietf-netconf";
2242 rpc_name = "kill-session";
2243 break;
2244 case NC_RPC_COMMIT:
2245 module_name = "ietf-netconf";
2246 rpc_name = "commit";
2247 break;
2248 case NC_RPC_DISCARD:
2249 module_name = "ietf-netconf";
2250 rpc_name = "discard-changes";
2251 break;
2252 case NC_RPC_CANCEL:
2253 module_name = "ietf-netconf";
2254 rpc_name = "cancel-commit";
2255 break;
2256 case NC_RPC_VALIDATE:
2257 module_name = "ietf-netconf";
2258 rpc_name = "validate";
2259 break;
2260 case NC_RPC_GETSCHEMA:
2261 module_name = "ietf-netconf-monitoring";
2262 rpc_name = "get-schema";
2263 break;
2264 case NC_RPC_SUBSCRIBE:
2265 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002266 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002267 break;
2268 case NC_RPC_GETDATA:
2269 module_name = "ietf-netconf-nmda";
2270 rpc_name = "get-data";
2271 break;
2272 case NC_RPC_EDITDATA:
2273 module_name = "ietf-netconf-nmda";
2274 rpc_name = "edit-data";
2275 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002276 case NC_RPC_ESTABLISHSUB:
2277 module_name = "ietf-subscribed-notifications";
2278 rpc_name = "establish-subscription";
2279 break;
2280 case NC_RPC_MODIFYSUB:
2281 module_name = "ietf-subscribed-notifications";
2282 rpc_name = "modify-subscription";
2283 break;
2284 case NC_RPC_DELETESUB:
2285 module_name = "ietf-subscribed-notifications";
2286 rpc_name = "delete-subscription";
2287 break;
2288 case NC_RPC_KILLSUB:
2289 module_name = "ietf-subscribed-notifications";
2290 rpc_name = "kill-subscription";
2291 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002292 case NC_RPC_ESTABLISHPUSH:
2293 module_name = "ietf-subscribed-notifications";
2294 rpc_name = "establish-subscription";
2295 module_check = "ietf-yang-push";
2296 break;
2297 case NC_RPC_MODIFYPUSH:
2298 module_name = "ietf-subscribed-notifications";
2299 rpc_name = "modify-subscription";
2300 module_check = "ietf-yang-push";
2301 break;
2302 case NC_RPC_RESYNCSUB:
2303 module_name = "ietf-yang-push";
2304 rpc_name = "resync-subscription";
2305 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002306 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002307 lyrc = LY_EINT;
2308 break;
2309 }
2310
2311 if (module_name && rpc_name) {
2312 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2313 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002314 ERR(session, "Missing \"%s\" module in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002315 return -1;
2316 }
2317
2318 /* create the operation node */
2319 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2320 }
Michal Vasko305faca2021-03-25 09:16:02 +01002321 if (module_check) {
2322 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002323 ERR(session, "Missing \"%s\" module in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002324 return -1;
2325 }
2326 }
Michal Vasko77367452021-02-16 16:32:18 +01002327
2328 if (lyrc) {
2329 return -1;
2330 }
2331 return 0;
2332}
2333
2334API NC_MSG_TYPE
2335nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2336 struct lyd_node **op)
2337{
2338 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002339
roman40672412023-05-04 11:10:22 +02002340 NC_CHECK_ARG_RET(session, session, rpc, envp, op, NC_MSG_ERROR);
2341
2342 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002343 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002344 return NC_MSG_ERROR;
2345 }
Michal Vasko77367452021-02-16 16:32:18 +01002346
2347 /* get a duplicate of the RPC node to append reply to */
2348 if (recv_reply_dup_rpc(session, rpc, op)) {
2349 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002350 }
Michal Vasko086311b2016-01-08 09:53:11 +01002351
Michal Vasko77367452021-02-16 16:32:18 +01002352 /* receive a reply */
2353 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002354
Michal Vasko77367452021-02-16 16:32:18 +01002355 /* do not return the RPC copy on error or if the reply includes no data */
2356 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2357 lyd_free_tree(*op);
2358 *op = NULL;
2359 }
2360 return ret;
2361}
2362
2363static NC_MSG_TYPE
2364recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2365{
Michal Vasko77367452021-02-16 16:32:18 +01002366 LY_ERR lyrc;
2367 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002368 NC_MSG_TYPE ret = NC_MSG_ERROR;
2369
2370 *op = NULL;
2371 *envp = NULL;
2372
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002373 /* Receive messages until a notification is found or a timeout or error reached */
2374 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2375 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002376 goto cleanup;
2377 }
2378
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002379 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002380 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2381 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002382 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002383 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002384 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
Michal Vasko9a108052022-04-01 12:06:54 +02002385 lyd_free_tree(*envp);
2386 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002387 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002388 goto cleanup;
2389 }
2390
Michal Vasko77367452021-02-16 16:32:18 +01002391cleanup:
2392 ly_in_free(msg, 1);
2393 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002394}
2395
2396API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002397nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002398{
roman40672412023-05-04 11:10:22 +02002399 NC_CHECK_ARG_RET(session, session, envp, op, NC_MSG_ERROR);
2400
2401 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002402 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002403 return NC_MSG_ERROR;
2404 }
2405
Michal Vasko77367452021-02-16 16:32:18 +01002406 /* receive a notification */
2407 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002408}
2409
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002410static void *
2411nc_recv_notif_thread(void *arg)
2412{
2413 struct nc_ntf_thread_arg *ntarg;
2414 struct nc_session *session;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002415 nc_notif_dispatch_clb notif_clb;
2416 void *user_data;
Michal Vasko743ff9f2022-10-20 10:03:13 +02002417
Michal Vaskoffb35e92022-10-20 09:07:25 +02002418 void (*free_data)(void *);
Michal Vasko77367452021-02-16 16:32:18 +01002419 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002420 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002421
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002422 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002423 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002424
2425 ntarg = (struct nc_ntf_thread_arg *)arg;
2426 session = ntarg->session;
2427 notif_clb = ntarg->notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002428 user_data = ntarg->user_data;
2429 free_data = ntarg->free_data;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002430 free(ntarg);
2431
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002432 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
Michal Vasko77367452021-02-16 16:32:18 +01002433 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002434 if (msgtype == NC_MSG_NOTIF) {
Michal Vaskoffb35e92022-10-20 09:07:25 +02002435 notif_clb(session, envp, op, user_data);
Michal Vasko77367452021-02-16 16:32:18 +01002436 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2437 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002438 lyd_free_all(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002439 break;
2440 }
Michal Vasko77367452021-02-16 16:32:18 +01002441 lyd_free_tree(envp);
Michal Vasko0c0239c2023-02-01 14:31:06 +01002442 lyd_free_all(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002443 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002444 /* quit this thread once the session is broken */
2445 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002446 }
2447
2448 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2449 }
2450
Michal Vasko05532772021-06-03 12:12:38 +02002451 VRB(session, "Notification thread exit.");
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002452 ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoffb35e92022-10-20 09:07:25 +02002453 if (free_data) {
2454 free_data(user_data);
2455 }
2456
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002457 return NULL;
2458}
2459
2460API int
Michal Vaskoffb35e92022-10-20 09:07:25 +02002461nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb)
2462{
2463 return nc_recv_notif_dispatch_data(session, notif_clb, NULL, NULL);
2464}
2465
2466API int
2467nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data,
2468 void (*free_data)(void *))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002469{
2470 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002471 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002472 int ret;
2473
roman40672412023-05-04 11:10:22 +02002474 NC_CHECK_ARG_RET(session, session, notif_clb, -1);
2475
2476 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002477 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002478 return -1;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002479 }
2480
2481 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002482 if (!ntarg) {
2483 ERRMEM;
2484 return -1;
2485 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002486 ntarg->session = session;
2487 ntarg->notif_clb = notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002488 ntarg->user_data = user_data;
2489 ntarg->free_data = free_data;
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002490 ATOMIC_INC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002491
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002492 /* just so that nc_recv_notif_thread() does not immediately exit */
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002493 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002494
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002495 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002496 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002497 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002498 free(ntarg);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002499 if (ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count) == 1) {
2500 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
2501 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002502 return -1;
2503 }
2504
2505 return 0;
2506}
2507
Michal Vasko77367452021-02-16 16:32:18 +01002508static const char *
2509nc_wd2str(NC_WD_MODE wd)
2510{
2511 switch (wd) {
2512 case NC_WD_ALL:
2513 return "report-all";
2514 case NC_WD_ALL_TAG:
2515 return "report-all-tagged";
2516 case NC_WD_TRIM:
2517 return "trim";
2518 case NC_WD_EXPLICIT:
2519 return "explicit";
2520 default:
2521 break;
2522 }
2523
2524 return NULL;
2525}
2526
Michal Vasko086311b2016-01-08 09:53:11 +01002527API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002528nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002529{
2530 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002531 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002532 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002533 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002534 struct nc_rpc_getconfig *rpc_gc;
2535 struct nc_rpc_edit *rpc_e;
2536 struct nc_rpc_copy *rpc_cp;
2537 struct nc_rpc_delete *rpc_del;
2538 struct nc_rpc_lock *rpc_lock;
2539 struct nc_rpc_get *rpc_g;
2540 struct nc_rpc_kill *rpc_k;
2541 struct nc_rpc_commit *rpc_com;
2542 struct nc_rpc_cancel *rpc_can;
2543 struct nc_rpc_validate *rpc_val;
2544 struct nc_rpc_getschema *rpc_gs;
2545 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002546 struct nc_rpc_getdata *rpc_getd;
2547 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002548 struct nc_rpc_establishsub *rpc_estsub;
2549 struct nc_rpc_modifysub *rpc_modsub;
2550 struct nc_rpc_deletesub *rpc_delsub;
2551 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002552 struct nc_rpc_establishpush *rpc_estpush;
2553 struct nc_rpc_modifypush *rpc_modpush;
2554 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002555 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002556 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002557 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002558 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002559 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002560 uint64_t cur_msgid;
2561
roman40672412023-05-04 11:10:22 +02002562 NC_CHECK_ARG_RET(session, session, rpc, msgid, NC_MSG_ERROR);
2563
2564 if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002565 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002566 return NC_MSG_ERROR;
2567 }
2568
Michal Vaskoc1171a42019-11-05 12:06:46 +01002569 switch (rpc->type) {
2570 case NC_RPC_ACT_GENERIC:
2571 /* checked when parsing */
2572 break;
2573 case NC_RPC_GETCONFIG:
2574 case NC_RPC_EDIT:
2575 case NC_RPC_COPY:
2576 case NC_RPC_DELETE:
2577 case NC_RPC_LOCK:
2578 case NC_RPC_UNLOCK:
2579 case NC_RPC_GET:
2580 case NC_RPC_KILL:
2581 case NC_RPC_COMMIT:
2582 case NC_RPC_DISCARD:
2583 case NC_RPC_CANCEL:
2584 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002585 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002586 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002587 ERR(session, "Missing \"ietf-netconf\" module in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002588 return NC_MSG_ERROR;
2589 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002590 break;
2591 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002592 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002593 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002594 ERR(session, "Missing \"ietf-netconf-monitoring\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002595 return NC_MSG_ERROR;
2596 }
2597 break;
2598 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002599 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002600 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002601 ERR(session, "Missing \"notifications\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002602 return NC_MSG_ERROR;
2603 }
2604 break;
2605 case NC_RPC_GETDATA:
2606 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002607 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002608 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002609 ERR(session, "Missing \"ietf-netconf-nmda\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002610 return NC_MSG_ERROR;
2611 }
2612 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002613 case NC_RPC_ESTABLISHSUB:
2614 case NC_RPC_MODIFYSUB:
2615 case NC_RPC_DELETESUB:
2616 case NC_RPC_KILLSUB:
2617 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2618 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002619 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002620 return NC_MSG_ERROR;
2621 }
2622 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002623 case NC_RPC_ESTABLISHPUSH:
2624 case NC_RPC_MODIFYPUSH:
2625 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2626 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002627 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002628 return NC_MSG_ERROR;
2629 }
2630 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2631 if (!mod2) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002632 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002633 return NC_MSG_ERROR;
2634 }
2635 break;
2636 case NC_RPC_RESYNCSUB:
2637 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2638 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002639 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002640 return NC_MSG_ERROR;
2641 }
2642 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002643 case NC_RPC_UNKNOWN:
2644 ERRINT;
2645 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002646 }
2647
Michal Vaskoab9deb62021-05-27 11:37:00 +02002648#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2649
Michal Vasko086311b2016-01-08 09:53:11 +01002650 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002651 case NC_RPC_ACT_GENERIC:
2652 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002653
2654 if (rpc_gen->has_data) {
2655 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002656 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002657 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002658 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2659 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2660 ly_in_free(in, 0);
2661 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002662 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002663 }
Michal Vasko086311b2016-01-08 09:53:11 +01002664 }
2665 break;
2666
2667 case NC_RPC_GETCONFIG:
2668 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2669
Michal Vaskoab9deb62021-05-27 11:37:00 +02002670 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2671 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2672 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002673 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002674 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002675 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2676 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002677 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002678 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2679 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2680 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002681 }
2682 }
2683
2684 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002685 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002686 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002687 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002688 lyrc = LY_ENOTFOUND;
2689 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002690 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002691 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 +01002692 }
2693 break;
2694
2695 case NC_RPC_EDIT:
2696 rpc_e = (struct nc_rpc_edit *)rpc;
2697
Michal Vaskoab9deb62021-05-27 11:37:00 +02002698 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2699 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2700 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002701
2702 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002703 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 +01002704 }
Michal Vasko086311b2016-01-08 09:53:11 +01002705 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002706 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 +01002707 }
Michal Vasko086311b2016-01-08 09:53:11 +01002708 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002709 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 +01002710 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002711 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002712 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 +01002713 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002714 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002715 }
2716 break;
2717
2718 case NC_RPC_COPY:
2719 rpc_cp = (struct nc_rpc_copy *)rpc;
2720
Michal Vaskoab9deb62021-05-27 11:37:00 +02002721 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2722 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002723 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002724 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002725 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002726 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002727 }
2728
Michal Vaskoab9deb62021-05-27 11:37:00 +02002729 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002730 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002731 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002732 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 +01002733 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002734 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002735 }
2736 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002737 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002738 }
2739
2740 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002741 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002742 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002743 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002744 lyrc = LY_ENOTFOUND;
2745 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002746 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002747 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 +01002748 }
2749 break;
2750
2751 case NC_RPC_DELETE:
2752 rpc_del = (struct nc_rpc_delete *)rpc;
2753
Michal Vaskoab9deb62021-05-27 11:37:00 +02002754 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2755 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002756 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002757 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002758 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002759 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002760 }
2761 break;
2762
2763 case NC_RPC_LOCK:
2764 rpc_lock = (struct nc_rpc_lock *)rpc;
2765
Michal Vaskoab9deb62021-05-27 11:37:00 +02002766 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2767 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2768 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002769 break;
2770
2771 case NC_RPC_UNLOCK:
2772 rpc_lock = (struct nc_rpc_lock *)rpc;
2773
Michal Vaskoab9deb62021-05-27 11:37:00 +02002774 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2775 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2776 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002777 break;
2778
2779 case NC_RPC_GET:
2780 rpc_g = (struct nc_rpc_get *)rpc;
2781
Michal Vaskoab9deb62021-05-27 11:37:00 +02002782 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002783 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002784 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002785 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2786 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002787 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002788 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2789 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2790 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002791 }
2792 }
2793
2794 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002795 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002796 if (!ietfncwd) {
Michal Vasko69e98752022-12-14 14:20:17 +01002797 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002798 lyrc = LY_ENOTFOUND;
2799 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002800 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002801 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 +01002802 }
2803 break;
2804
2805 case NC_RPC_KILL:
2806 rpc_k = (struct nc_rpc_kill *)rpc;
2807
Michal Vaskoab9deb62021-05-27 11:37:00 +02002808 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002809 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002810 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002811 break;
2812
2813 case NC_RPC_COMMIT:
2814 rpc_com = (struct nc_rpc_commit *)rpc;
2815
Michal Vaskoab9deb62021-05-27 11:37:00 +02002816 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002817 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002818 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002819 }
2820
2821 if (rpc_com->confirm_timeout) {
2822 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002823 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002824 }
Michal Vasko086311b2016-01-08 09:53:11 +01002825 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002826 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002827 }
Michal Vasko086311b2016-01-08 09:53:11 +01002828 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002829 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002830 }
2831 break;
2832
2833 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002834 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002835 break;
2836
2837 case NC_RPC_CANCEL:
2838 rpc_can = (struct nc_rpc_cancel *)rpc;
2839
Michal Vaskoab9deb62021-05-27 11:37:00 +02002840 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002841 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002842 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002843 }
2844 break;
2845
2846 case NC_RPC_VALIDATE:
2847 rpc_val = (struct nc_rpc_validate *)rpc;
2848
Michal Vaskoab9deb62021-05-27 11:37:00 +02002849 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2850 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002851 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002852 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002853 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 +01002854 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002855 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002856 }
2857 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002858 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002859 }
2860 break;
2861
2862 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002863 rpc_gs = (struct nc_rpc_getschema *)rpc;
2864
Michal Vaskoab9deb62021-05-27 11:37:00 +02002865 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2866 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002867 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002868 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002869 }
2870 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002871 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002872 }
2873 break;
2874
2875 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002876 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2877
Michal Vaskoab9deb62021-05-27 11:37:00 +02002878 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002879 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002880 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002881 }
2882
2883 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002884 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002885 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2886 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002887 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002888 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2889 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2890 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002891 }
2892 }
Michal Vasko086311b2016-01-08 09:53:11 +01002893 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002894 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002895 }
Michal Vasko086311b2016-01-08 09:53:11 +01002896 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002897 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002898 }
2899 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002900
2901 case NC_RPC_GETDATA:
2902 rpc_getd = (struct nc_rpc_getdata *)rpc;
2903
Michal Vaskoab9deb62021-05-27 11:37:00 +02002904 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2905 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2906
Michal Vaskoc1171a42019-11-05 12:06:46 +01002907 if (rpc_getd->filter) {
2908 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002909 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 +01002910 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002911 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002912 }
2913 }
2914 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002915 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002916 }
2917 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002918 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2919 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002920 }
2921 if (rpc_getd->max_depth) {
2922 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002923 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002924 }
2925 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002926 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002927 }
2928
2929 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002930 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002931 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 +01002932 }
2933 break;
2934
2935 case NC_RPC_EDITDATA:
2936 rpc_editd = (struct nc_rpc_editdata *)rpc;
2937
Michal Vaskoab9deb62021-05-27 11:37:00 +02002938 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2939 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002940
2941 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002942 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2943 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002944 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002945 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002946 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 +01002947 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002948 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002949 }
2950 break;
2951
Michal Vasko96f247a2021-03-15 13:32:10 +01002952 case NC_RPC_ESTABLISHSUB:
2953 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2954
Michal Vaskoab9deb62021-05-27 11:37:00 +02002955 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002956
2957 if (rpc_estsub->filter) {
2958 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002959 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2960 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002961 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002962 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002963 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002964 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002965 }
2966 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002967 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002968
2969 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002970 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002971 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002972 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002973 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002974 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002975 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002976 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002977 }
2978 break;
2979
2980 case NC_RPC_MODIFYSUB:
2981 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2982
Michal Vaskoab9deb62021-05-27 11:37:00 +02002983 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002984
2985 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002986 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002987
2988 if (rpc_modsub->filter) {
2989 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002990 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
2991 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002992 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002993 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002994 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002995 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002996 }
2997 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002998 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002999 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003000 }
3001 break;
3002
3003 case NC_RPC_DELETESUB:
3004 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
3005
Michal Vaskoab9deb62021-05-27 11:37:00 +02003006 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003007
3008 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003009 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003010 break;
3011
3012 case NC_RPC_KILLSUB:
3013 rpc_killsub = (struct nc_rpc_killsub *)rpc;
3014
Michal Vaskoab9deb62021-05-27 11:37:00 +02003015 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01003016
3017 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003018 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01003019 break;
3020
Michal Vasko305faca2021-03-25 09:16:02 +01003021 case NC_RPC_ESTABLISHPUSH:
3022 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
3023
Michal Vaskoab9deb62021-05-27 11:37:00 +02003024 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
3025 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003026
3027 if (rpc_estpush->filter) {
3028 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003029 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
3030 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003031 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003032 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003033 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003034 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003035 }
3036 }
3037
3038 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003039 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003040 }
Michal Vasko305faca2021-03-25 09:16:02 +01003041 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003042 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003043 }
3044
3045 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003046 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003047 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003048 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003049 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003050 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003051 }
3052 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003053 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003054 if (rpc_estpush->dampening_period) {
3055 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003056 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003057 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02003058 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
3059 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003060 if (rpc_estpush->excluded_change) {
3061 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003062 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
3063 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003064 }
3065 }
3066 }
3067 break;
3068
3069 case NC_RPC_MODIFYPUSH:
3070 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
3071
Michal Vaskoab9deb62021-05-27 11:37:00 +02003072 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003073
3074 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003075 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
3076 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003077
3078 if (rpc_modpush->filter) {
3079 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003080 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
3081 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003082 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003083 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003084 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003085 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003086 }
3087 }
Michal Vasko305faca2021-03-25 09:16:02 +01003088 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003089 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003090 }
3091
3092 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003093 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003094 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003095 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003096 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003097 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003098 }
3099 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003100 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003101 if (rpc_modpush->dampening_period) {
3102 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003103 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003104 }
3105 }
3106 break;
3107
3108 case NC_RPC_RESYNCSUB:
3109 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
3110
Michal Vaskoab9deb62021-05-27 11:37:00 +02003111 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003112 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003113 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003114 break;
3115
Michal Vasko96f247a2021-03-15 13:32:10 +01003116 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01003117 ERRINT;
3118 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01003119 }
3120
Michal Vaskoab9deb62021-05-27 11:37:00 +02003121#undef CHECK_LYRC_BREAK
3122
3123 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02003124 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02003125 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02003126 return NC_MSG_ERROR;
3127 }
3128
Michal Vasko131120a2018-05-29 15:44:02 +02003129 /* send RPC, store its message ID */
3130 r = nc_send_msg_io(session, timeout, data);
3131 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003132
Radek Krejcib4b19062018-02-07 16:33:06 +01003133 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003134 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003135 }
Michal Vasko086311b2016-01-08 09:53:11 +01003136
Michal Vasko131120a2018-05-29 15:44:02 +02003137 if (r == NC_MSG_RPC) {
3138 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003139 }
Michal Vasko131120a2018-05-29 15:44:02 +02003140 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003141}
Michal Vaskode2946c2017-01-12 12:19:26 +01003142
3143API void
3144nc_client_session_set_not_strict(struct nc_session *session)
3145{
3146 if (session->side != NC_CLIENT) {
roman40672412023-05-04 11:10:22 +02003147 ERRARG(NULL, "session");
Michal Vaskode2946c2017-01-12 12:19:26 +01003148 return;
3149 }
3150
3151 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3152}