blob: 383a599265089290cc9f122b0948c94a79332be2 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 session client functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
15
Radek Krejcifd5b6682017-06-13 15:52:53 +020016#define _GNU_SOURCE
Michal Vaskobbf52c82020-04-07 13:08:50 +020017
18#ifdef __linux__
19# include <sys/syscall.h>
20#endif
21
Michal Vaskob83a3fa2021-05-26 09:53:42 +020022#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010023#include <assert.h>
tadeas-vintrlik54f142a2021-08-23 10:36:18 +020024#include <ctype.h>
Michal Vasko086311b2016-01-08 09:53:11 +010025#include <errno.h>
26#include <fcntl.h>
27#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010028#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010029#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020030#include <poll.h>
Michal Vasko086311b2016-01-08 09:53:11 +010031#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020032#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010033#include <stdlib.h>
34#include <string.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020035#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010036#include <sys/socket.h>
37#include <sys/stat.h>
38#include <sys/types.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020039#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010040#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010041
42#include <libyang/libyang.h>
43
Michal Vasko9e8ac262020-04-07 13:06:45 +020044#include "compat.h"
Michal Vasko086311b2016-01-08 09:53:11 +010045#include "libnetconf.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010046#include "messages_client.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020047#include "session_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010048
Michal Vasko8a4e1462020-05-07 11:32:31 +020049#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020050#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020051
Michal Vasko80ef5d22016-01-18 09:21:02 +010052static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
53
Radek Krejci62aa0642017-05-25 16:33:49 +020054#ifdef NC_ENABLED_SSH
55int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
56char *sshauth_password(const char *username, const char *hostname, void *priv);
57char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020058char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
Radek Krejci62aa0642017-05-25 16:33:49 +020059#endif /* NC_ENABLED_SSH */
60
61static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
62static pthread_key_t nc_client_context_key;
63#ifdef __linux__
64static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020065 .opts.ka = {
66 .enabled = 1,
67 .idle_time = 1,
68 .max_probes = 10,
69 .probe_interval = 5
70 },
Radek Krejci62aa0642017-05-25 16:33:49 +020071#ifdef NC_ENABLED_SSH
72 .ssh_opts = {
roman41a11e42022-06-22 09:27:08 +020073 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Radek Krejci62aa0642017-05-25 16:33:49 +020074 .auth_hostkey_check = sshauth_hostkey_check,
75 .auth_password = sshauth_password,
76 .auth_interactive = sshauth_interactive,
77 .auth_privkey_passphrase = sshauth_privkey_passphrase
78 },
79 .ssh_ch_opts = {
80 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
81 .auth_hostkey_check = sshauth_hostkey_check,
82 .auth_password = sshauth_password,
83 .auth_interactive = sshauth_interactive,
84 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020085 },
Radek Krejci62aa0642017-05-25 16:33:49 +020086#endif /* NC_ENABLED_SSH */
87 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020088 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020089};
90#endif
91
92static void
93nc_client_context_free(void *ptr)
94{
95 struct nc_client_context *c = (struct nc_client_context *)ptr;
96
Radek Krejci5cebc6b2017-05-26 13:24:38 +020097 if (--(c->refcount)) {
98 /* still used */
99 return;
100 }
101
Radek Krejci62aa0642017-05-25 16:33:49 +0200102#ifdef __linux__
103 /* in __linux__ we use static memory in the main thread,
104 * so this check is for programs terminating the main()
105 * function by pthread_exit() :)
106 */
107 if (c != &context_main)
108#endif
109 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200110 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400111 free(c->opts.schema_searchpath);
112
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200113#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400114 int i;
115 for (i = 0; i < c->opts.ch_bind_count; ++i) {
116 close(c->opts.ch_binds[i].sock);
117 free((char *)c->opts.ch_binds[i].address);
118 }
119 free(c->opts.ch_binds);
120 c->opts.ch_binds = NULL;
121 c->opts.ch_bind_count = 0;
Radek Krejci62aa0642017-05-25 16:33:49 +0200122#endif
123#ifdef NC_ENABLED_SSH
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400124 _nc_client_ssh_destroy_opts(&c->ssh_opts);
125 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200126#endif
127#ifdef NC_ENABLED_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400128 _nc_client_tls_destroy_opts(&c->tls_opts);
129 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200130#endif
131 free(c);
132 }
133}
134
135static void
136nc_client_context_createkey(void)
137{
138 int r;
139
140 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200141 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200142 pthread_setspecific(nc_client_context_key, NULL);
143}
144
145struct nc_client_context *
146nc_client_context_location(void)
147{
148 struct nc_client_context *e;
149
150 pthread_once(&nc_client_context_once, nc_client_context_createkey);
151 e = pthread_getspecific(nc_client_context_key);
152 if (!e) {
153 /* prepare ly_err storage */
154#ifdef __linux__
155 if (getpid() == syscall(SYS_gettid)) {
156 /* main thread - use global variable instead of thread-specific variable. */
157 e = &context_main;
158 } else
159#endif /* __linux__ */
160 {
161 e = calloc(1, sizeof *e);
162 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200163 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200164#ifdef NC_ENABLED_SSH
165 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
roman41a11e42022-06-22 09:27:08 +0200166 e->ssh_opts.auth_pref[0].value = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200167 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
168 e->ssh_opts.auth_pref[1].value = 2;
169 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
roman41a11e42022-06-22 09:27:08 +0200170 e->ssh_opts.auth_pref[2].value = 3;
Radek Krejci62aa0642017-05-25 16:33:49 +0200171 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
172 e->ssh_opts.auth_password = sshauth_password;
173 e->ssh_opts.auth_interactive = sshauth_interactive;
174 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
175
roman41a11e42022-06-22 09:27:08 +0200176 /* callhome settings are the same */
Radek Krejci62aa0642017-05-25 16:33:49 +0200177 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
178 e->ssh_ch_opts.auth_pref[0].value = 1;
179 e->ssh_ch_opts.auth_pref[1].value = 2;
180 e->ssh_ch_opts.auth_pref[2].value = 3;
181#endif /* NC_ENABLED_SSH */
182 }
183 pthread_setspecific(nc_client_context_key, e);
184 }
185
186 return e;
187}
188
189#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100190
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200191API void *
192nc_client_get_thread_context(void)
193{
194 return nc_client_context_location();
195}
196
197API void
198nc_client_set_thread_context(void *context)
199{
200 struct nc_client_context *old, *new;
201
202 if (!context) {
203 ERRARG(context);
204 return;
205 }
206
207 new = (struct nc_client_context *)context;
208 old = nc_client_context_location();
209 if (old == new) {
210 /* nothing to change */
211 return;
212 }
213
214 /* replace old by new, increase reference counter in the newly set context */
215 nc_client_context_free(old);
216 new->refcount++;
217 pthread_setspecific(nc_client_context_key, new);
218}
219
Radek Krejcifd5b6682017-06-13 15:52:53 +0200220int
221nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
222{
223 /* assign context (dicionary needed for handshake) */
224 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100225 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200226 return EXIT_FAILURE;
227 }
228
Michal Vasko5ca5d972022-09-14 13:51:31 +0200229 /* user path must be first, the first path is used to store modules retreived via get-schema */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200230 if (client_opts.schema_searchpath) {
231 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200232 } else if (!access(NC_YANG_DIR, F_OK)) {
233 ly_ctx_set_searchdir(ctx, NC_YANG_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200234 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200235
Michal Vasko5ca5d972022-09-14 13:51:31 +0200236 /* set callback for getting modules, if provided */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200237 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
238 } else {
239 session->flags |= NC_SESSION_SHAREDCTX;
240 }
241
242 session->ctx = ctx;
243
244 return EXIT_SUCCESS;
245}
246
Michal Vasko086311b2016-01-08 09:53:11 +0100247API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100248nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100249{
Michal Vasko3031aae2016-01-27 16:07:18 +0100250 if (client_opts.schema_searchpath) {
251 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100252 }
Michal Vasko086311b2016-01-08 09:53:11 +0100253
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100254 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100255 client_opts.schema_searchpath = strdup(path);
256 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100257 ERRMEM;
258 return 1;
259 }
260 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100261 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100262 }
263
264 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100265}
266
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100267API const char *
268nc_client_get_schema_searchpath(void)
269{
270 return client_opts.schema_searchpath;
271}
272
Radek Krejcifd5b6682017-06-13 15:52:53 +0200273API int
274nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100275{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200276 client_opts.schema_clb = clb;
277 if (clb) {
278 client_opts.schema_clb_data = user_data;
279 } else {
280 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100281 }
282
283 return 0;
284}
285
Radek Krejcifd5b6682017-06-13 15:52:53 +0200286API ly_module_imp_clb
287nc_client_get_schema_callback(void **user_data)
288{
289 if (user_data) {
290 (*user_data) = client_opts.schema_clb_data;
291 }
292 return client_opts.schema_clb;
293}
294
Michal Vasko5ca5d972022-09-14 13:51:31 +0200295struct module_info {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200296 char *name;
297 char *revision;
Michal Vasko6ce8e822022-08-02 15:09:17 +0200298
Radek Krejci65ef6d52018-08-16 16:35:02 +0200299 struct {
300 char *name;
301 char *revision;
302 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100303 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200304 int implemented;
305};
306
307struct clb_data_s {
308 void *user_data;
309 ly_module_imp_clb user_clb;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200310 struct module_info *modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200311 struct nc_session *session;
312 int has_get_schema;
313};
314
Michal Vaskoc088f982021-10-05 12:23:07 +0200315/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200316 * @brief Retrieve YANG module content from a local file.
Michal Vaskoc088f982021-10-05 12:23:07 +0200317 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200318 * @param[in] name Module name.
319 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200320 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200321 * @param[out] format Module format.
322 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200323 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200324static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200325retrieve_module_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200326 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100327{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200328 char *localfile = NULL;
329 FILE *f;
330 long length, l;
331 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100332
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200333 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200334 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
335 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200336 return NULL;
337 }
338 if (localfile) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200339 VRB(clb_data->session, "Reading module \"%s@%s\" from local file \"%s\".", name, rev ? rev : "<latest>",
Michal Vasko7502b602022-08-26 11:51:17 +0200340 localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200341 f = fopen(localfile, "r");
342 if (!f) {
Michal Vasko7502b602022-08-26 11:51:17 +0200343 ERR(clb_data->session, "Unable to open file \"%s\" (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200344 free(localfile);
345 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100346 }
Michal Vasko086311b2016-01-08 09:53:11 +0100347
Radek Krejci65ef6d52018-08-16 16:35:02 +0200348 fseek(f, 0, SEEK_END);
349 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200350 if (length < 0) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200351 ERR(clb_data->session, "Unable to get the size of module file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200352 free(localfile);
353 fclose(f);
354 return NULL;
355 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200356 fseek(f, 0, SEEK_SET);
357
358 model_data = malloc(length + 1);
359 if (!model_data) {
360 ERRMEM;
361 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200362 ERR(clb_data->session, "Reading module from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
Michal Vasko05532772021-06-03 12:12:38 +0200363 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200364 free(model_data);
365 model_data = NULL;
366 } else {
367 /* terminating NULL byte */
368 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100369 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200370 fclose(f);
371 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100372 }
373
Radek Krejci65ef6d52018-08-16 16:35:02 +0200374 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100375}
376
Michal Vaskoc088f982021-10-05 12:23:07 +0200377/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200378 * @brief Retrieve YANG module content from a reply to get-schema RPC.
Michal Vaskoc088f982021-10-05 12:23:07 +0200379 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200380 * @param[in] name Module name.
381 * @param[in] rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200382 * @param[in] clb_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200383 * @param[out] format Module format.
384 * @return Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200385 */
Michal Vasko086311b2016-01-08 09:53:11 +0100386static char *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200387retrieve_module_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200388 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100389{
Michal Vasko086311b2016-01-08 09:53:11 +0100390 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100391 struct lyd_node *envp = NULL, *op = NULL;
392 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100393 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100394 uint64_t msgid;
Michal Vasko7502b602022-08-26 11:51:17 +0200395 char *localfile = NULL, *envp_str = NULL, *model_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200396 FILE *f;
Michal Vasko086311b2016-01-08 09:53:11 +0100397
Michal Vasko5ca5d972022-09-14 13:51:31 +0200398 VRB(clb_data->session, "Reading module \"%s@%s\" from server via get-schema.", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200399 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100400
Radek Krejci65ef6d52018-08-16 16:35:02 +0200401 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100402 usleep(1000);
403 }
404 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200405 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100406 nc_rpc_free(rpc);
407 return NULL;
408 }
409
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200410 do {
Michal Vasko77367452021-02-16 16:32:18 +0100411 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200412 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100413 nc_rpc_free(rpc);
414 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200415 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100416 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100417 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200418 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100419 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100420 } else if (!op) {
Michal Vasko7502b602022-08-26 11:51:17 +0200421 assert(envp);
422 lyd_print_mem(&envp_str, envp, LYD_XML, 0);
423 WRN(clb_data->session, "Received an unexpected reply to <get-schema>:\n%s", envp_str);
424 free(envp_str);
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100425 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100426 }
427
Michal Vasko77367452021-02-16 16:32:18 +0100428 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200429 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100430 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200431 }
Michal Vasko77367452021-02-16 16:32:18 +0100432 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200433 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200434 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100435 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200436 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200437 break;
438 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100439 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200440 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200441 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200442 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200443 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100444 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200445 }
Michal Vasko086311b2016-01-08 09:53:11 +0100446
Radek Krejci488b0702018-08-29 14:47:15 +0200447 if (model_data && !model_data[0]) {
448 /* empty data */
449 free(model_data);
450 model_data = NULL;
451 }
452
Michal Vasko5ca5d972022-09-14 13:51:31 +0200453 /* try to store the model_data into local module repository */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200454 if (model_data) {
455 *format = LYS_IN_YANG;
456 if (client_opts.schema_searchpath) {
Michal Vasko77367452021-02-16 16:32:18 +0100457 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
458 rev ? rev : "") == -1) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200459 ERRMEM;
Michal Vaskof945da52018-02-15 08:45:13 +0100460 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200461 f = fopen(localfile, "w");
462 if (!f) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200463 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of module retrieved via <get-schema> (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200464 localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200465 } else {
466 fputs(model_data, f);
467 fclose(f);
468 }
469 free(localfile);
Michal Vaskof945da52018-02-15 08:45:13 +0100470 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200471 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200472 }
473
Michal Vasko77367452021-02-16 16:32:18 +0100474cleanup:
475 lyd_free_tree(envp);
476 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100477 return model_data;
478}
479
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200480static void
481free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200482{
483 free(data);
484 (void)user_data;
485}
486
Michal Vaskoc088f982021-10-05 12:23:07 +0200487/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200488 * @brief Retrieve YANG module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200489 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200490 * @param[in] mod_name Module name.
491 * @param[in] mod_rev Module revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100492 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200493 * @param[out] format Module format.
494 * @param[out] module_data Module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100495 * @param[out] free_module_data Callback for freeing @p module_data.
496 * @return LY_ERR value.
497 */
498static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200499retrieve_module_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format,
Michal Vasko5e32c402022-01-12 14:05:09 +0100500 const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
501{
502 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
503 char *model_data = NULL;
504
Michal Vasko5e32c402022-01-12 14:05:09 +0100505 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200506 model_data = retrieve_module_data_localfile(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100507
508 /* 2. try to use <get-schema> */
509 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200510 model_data = retrieve_module_data_getschema(mod_name, mod_rev, clb_data, format);
Michal Vasko5e32c402022-01-12 14:05:09 +0100511 }
512
513 /* 3. try to use user callback */
514 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200515 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 +0100516 clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data,
517 free_module_data);
518 }
519
520 *free_module_data = free_with_user_data;
521 *module_data = model_data;
522 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
523}
524
525/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200526 * @brief Retrieve YANG import module content.
Michal Vasko5e32c402022-01-12 14:05:09 +0100527 *
Michal Vasko5ca5d972022-09-14 13:51:31 +0200528 * @param[in] mod_name Module name.
529 * @param[in] mod_rev Module revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200530 * @param[in] submod_name Optional submodule name.
531 * @param[in] sub_rev Submodule revision.
532 * @param[in] user_data get-schema callback data.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200533 * @param[out] format Module format.
534 * @param[out] module_data Module content.
Michal Vaskoc088f982021-10-05 12:23:07 +0200535 * @param[out] free_module_data Callback for freeing @p module_data.
536 * @return LY_ERR value.
537 */
Michal Vasko77367452021-02-16 16:32:18 +0100538static LY_ERR
Michal Vasko5ca5d972022-09-14 13:51:31 +0200539retrieve_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 +0100540 void *user_data, LYS_INFORMAT *format, const char **module_data,
541 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200542{
543 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
Michal Vasko5e32c402022-01-12 14:05:09 +0100544 uint32_t u, v, match = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200545 const char *name = NULL, *rev = NULL;
546 char *model_data = NULL;
547
Michal Vasko5ca5d972022-09-14 13:51:31 +0200548 /* get and check the final name and revision of the module to be retrieved */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200549 if (!mod_rev || !mod_rev[0]) {
550 /* newest revision requested - get the newest revision from the list of available modules on server */
551 match = 0;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200552 for (u = 0; clb_data->modules[u].name; ++u) {
553 if (strcmp(mod_name, clb_data->modules[u].name)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200554 continue;
555 }
Michal Vasko5ca5d972022-09-14 13:51:31 +0200556 if (!match || (strcmp(mod_rev, clb_data->modules[u].revision) > 0)) {
557 mod_rev = clb_data->modules[u].revision;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200558 }
559 match = u + 1;
560 }
561 if (!match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200562 /* valid situation if we are retrieving YANG 1.1 module and have only capabilities for now
Michal Vasko1d2665c2021-02-12 09:28:44 +0100563 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200564 VRB(clb_data->session, "Unable to identify revision of the import module \"%s\" from "
Michal Vasko05532772021-06-03 12:12:38 +0200565 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200566 }
567 }
568 if (submod_name) {
569 name = submod_name;
570 if (sub_rev) {
571 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200572 } else if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200573 if (!clb_data->modules[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200574 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
Michal Vasko5ca5d972022-09-14 13:51:31 +0200575 "in import module \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200576 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200577 for (v = 0; clb_data->modules[match - 1].submodules[v].name; ++v) {
578 if (!strcmp(submod_name, clb_data->modules[match - 1].submodules[v].name)) {
579 rev = sub_rev = clb_data->modules[match - 1].submodules[v].revision;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200580 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200581 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200582 if (!rev) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200583 ERR(clb_data->session, "Requested submodule \"%s\" is not found in import module \"%s\" on server side.",
Michal Vasko05532772021-06-03 12:12:38 +0200584 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100585 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200586 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200587 }
588 }
589 } else {
590 name = mod_name;
591 rev = mod_rev;
592 }
593
Radek Krejci65ef6d52018-08-16 16:35:02 +0200594 if (match) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200595 /* we have enough information to avoid communication with server and try to get the module locally */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200596
597 /* 1. try to get data locally */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200598 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200599
600 /* 2. try to use <get-schema> */
601 if (!model_data && clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200602 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200603 }
604 } else {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200605 /* we are unsure which revision of the module we should load, so first try to get
Radek Krejci65ef6d52018-08-16 16:35:02 +0200606 * the newest revision from the server via get-schema and only if the server does not
607 * implement get-schema, try to load the newest revision locally. This is imperfect
608 * solution, but there are situation when a client does not know what revision is
609 * actually implemented by the server. */
610
611 /* 1. try to use <get-schema> */
612 if (clb_data->has_get_schema) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200613 model_data = retrieve_module_data_getschema(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200614 }
615
616 /* 2. try to get data locally */
617 if (!model_data) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200618 model_data = retrieve_module_data_localfile(name, rev, clb_data, format);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200619 }
620 }
621
622 /* 3. try to use user callback */
623 if (!model_data && clb_data->user_clb) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200624 VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", name, rev ? rev : "<latest>");
Michal Vasko77367452021-02-16 16:32:18 +0100625 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
626 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200627 }
628
Jan Kundrát35972df2018-09-06 19:00:01 +0200629 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100630 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200631 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200632}
633
Michal Vaskoc088f982021-10-05 12:23:07 +0200634/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200635 * @brief Load a YANG module into context.
Michal Vaskoc088f982021-10-05 12:23:07 +0200636 *
637 * @param[in] session NC session.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200638 * @param[in] name Module name.
639 * @param[in] revision Module revision.
640 * @param[in] features Enabled module features.
641 * @param[in] modules Server module info built from capabilities.
642 * @param[in] user_clb User callback for retrieving module data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200643 * @param[in] user_data User data for @p user_clb.
644 * @param[in] has_get_schema Whether the server supports get-schema.
645 * @param[out] mod Loaded module.
646 * @return 0 on success.
647 * @return -1 on error.
648 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100649static int
Michal Vasko5e32c402022-01-12 14:05:09 +0100650nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features,
Michal Vasko5ca5d972022-09-14 13:51:31 +0200651 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 +0100652{
653 int ret = 0;
654 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200655 const char *module_data = NULL;
Michal Vasko5e32c402022-01-12 14:05:09 +0100656 struct ly_in *in;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200657 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200658
659 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200660 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100661
Michal Vasko5e32c402022-01-12 14:05:09 +0100662 /* try to use a module from the context */
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200663 *mod = ly_ctx_get_module_implemented(session->ctx, name);
664 if (!*mod) {
665 if (revision) {
666 *mod = ly_ctx_get_module(session->ctx, name, revision);
667 } else {
Michal Vasko5e32c402022-01-12 14:05:09 +0100668 *mod = ly_ctx_get_module_latest(session->ctx, name);
669 }
Michal Vaskoc3e402f2022-09-14 13:34:55 +0200670 } else if (revision && (!(*mod)->revision || strcmp((*mod)->revision, revision))) {
671 WRN(session, "Server implements module \"%s\" in revision \"%s\" but revision \"%s\" is already implemented"
672 " and will be used instead.", name, revision, (*mod)->revision ? (*mod)->revision : "<none>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200673 }
Michal Vasko5e32c402022-01-12 14:05:09 +0100674
Michal Vaskoceae0152018-02-14 16:03:59 +0100675 if (*mod) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100676 /* make the present module implemented and/or enable all its features */
677 if (lys_set_implemented(*mod, features)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +0200678 ERR(session, "Failed to implement module \"%s\".", (*mod)->name);
Michal Vasko5e32c402022-01-12 14:05:09 +0100679 ret = -1;
Michal Vaskoceae0152018-02-14 16:03:59 +0100680 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200681 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100682 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200683 clb_data.has_get_schema = has_get_schema;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200684 clb_data.modules = modules;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200685 clb_data.session = session;
686 clb_data.user_clb = user_clb;
687 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100688
689 /* clear all the errors and just collect them for now */
690 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200691 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100692
Radek Krejci65ef6d52018-08-16 16:35:02 +0200693 /* get module data */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200694 retrieve_module_data(name, revision, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100695
Radek Krejci65ef6d52018-08-16 16:35:02 +0200696 if (module_data) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100697 /* set import callback */
Michal Vasko5ca5d972022-09-14 13:51:31 +0200698 ly_ctx_set_module_imp_clb(session->ctx, retrieve_module_data_imp, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100699
Michal Vasko5ca5d972022-09-14 13:51:31 +0200700 /* parse the module */
Michal Vasko5e32c402022-01-12 14:05:09 +0100701 ly_in_new_memory(module_data, &in);
702 lys_parse(session->ctx, in, format, features, mod);
703 ly_in_free(in, 0);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200704 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100705 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100706 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100707
Radek Krejci65ef6d52018-08-16 16:35:02 +0200708 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
709 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100710
Michal Vaskodbf7c272018-02-19 09:07:59 +0100711 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200712 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100713 if (!(*mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100714 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100715 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100716 }
717 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200718 } else {
719 /* print only warnings */
Michal Vasko5e32c402022-01-12 14:05:09 +0100720 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200721 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100722 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200723 }
724 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100725 }
726
Michal Vaskodbf7c272018-02-19 09:07:59 +0100727 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100728 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100729 }
730
731 return ret;
732}
733
Radek Krejci65ef6d52018-08-16 16:35:02 +0200734static void
Michal Vasko5ca5d972022-09-14 13:51:31 +0200735free_module_info(struct module_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200736{
Michal Vasko77367452021-02-16 16:32:18 +0100737 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200738
Radek Krejci65ef6d52018-08-16 16:35:02 +0200739 if (!list) {
740 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200741 }
742
Radek Krejci65ef6d52018-08-16 16:35:02 +0200743 for (u = 0; list[u].name; ++u) {
744 free(list[u].name);
745 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100746 if (list[u].features) {
747 for (v = 0; list[u].features[v]; ++v) {
748 free(list[u].features[v]);
749 }
750 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200751 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200752 if (list[u].submodules) {
753 for (v = 0; list[u].submodules[v].name; ++v) {
754 free(list[u].submodules[v].name);
755 free(list[u].submodules[v].revision);
756 }
757 free(list[u].submodules);
758 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200759 }
760 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200761}
762
Michal Vaskoc088f982021-10-05 12:23:07 +0200763/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200764 * @brief Build server module info from ietf-yang-library data.
Michal Vaskoc088f982021-10-05 12:23:07 +0200765 *
766 * @param[in] session NC session.
767 * @param[in] has_get_data Whether get-data RPC is available or only get.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200768 * @param[out] result Server modules.
Michal Vaskoc088f982021-10-05 12:23:07 +0200769 * @return 0 on success.
770 * @return -1 on error.
771 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200772static int
Michal Vasko5ca5d972022-09-14 13:51:31 +0200773build_module_info_yl(struct nc_session *session, int has_get_data, struct module_info **result)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200774{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200775 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100776 struct lyd_node *op = NULL, *envp = NULL;
777 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200778 NC_MSG_TYPE msg;
779 uint64_t msgid;
Radek Krejcia8dfaea2018-08-17 10:55:09 +0200780 struct ly_set *modules = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100781 uint32_t u, v, submodules_count, feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200782 struct lyd_node *iter, *child;
783 struct lys_module *mod;
Michal Vasko303263d2021-10-05 12:18:21 +0200784 int ret = 0;
785 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200786
787 /* get yang-library data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200788 if (has_get_data) {
789 rpc_name = "get-data";
790 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
791 rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
792 NC_PARAMTYPE_CONST);
793 } else {
794 rpc = nc_rpc_getdata("ietf-datastores:operational",
795 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
796 0, NC_PARAMTYPE_CONST);
797 }
Radek Krejci261737f2018-08-21 09:22:34 +0200798 } else {
Michal Vasko303263d2021-10-05 12:18:21 +0200799 rpc_name = "get";
800 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
801 rpc = nc_rpc_get("/ietf-yang-library:*", 0, NC_PARAMTYPE_CONST);
802 } else {
803 rpc = nc_rpc_get("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", 0,
804 NC_PARAMTYPE_CONST);
805 }
Radek Krejci261737f2018-08-21 09:22:34 +0200806 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200807 if (!rpc) {
808 goto cleanup;
809 }
810
811 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
812 usleep(1000);
813 }
814 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200815 WRN(session, "Failed to send request for yang-library data.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200816 goto cleanup;
817 }
818
819 do {
Michal Vasko77367452021-02-16 16:32:18 +0100820 lyd_free_tree(envp);
821 lyd_free_tree(op);
822
823 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200824 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200825 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko303263d2021-10-05 12:18:21 +0200826 WRN(session, "Timeout for receiving reply to a <%s> yang-library data expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200827 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100828 } else if (msg == NC_MSG_ERROR) {
Michal Vasko303263d2021-10-05 12:18:21 +0200829 WRN(session, "Failed to receive a reply to <%s> of yang-library data.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200830 goto cleanup;
Jan Kundrát1d73a5a2022-03-30 16:54:49 +0200831 } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko303263d2021-10-05 12:18:21 +0200832 WRN(session, "Unexpected reply without data to a yang-library <%s> RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200833 goto cleanup;
834 }
835
Michal Vasko77367452021-02-16 16:32:18 +0100836 data = (struct lyd_node_any *)lyd_child(op);
837 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko303263d2021-10-05 12:18:21 +0200838 WRN(session, "Unexpected data in reply to a yang-library <%s> RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200839 goto cleanup;
840 } else if (!data->value.tree) {
Michal Vasko303263d2021-10-05 12:18:21 +0200841 WRN(session, "No data in reply to a yang-library <%s> RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200842 goto cleanup;
843 }
844
Michal Vasko77367452021-02-16 16:32:18 +0100845 if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
Michal Vasko303263d2021-10-05 12:18:21 +0200846 WRN(session, "No module information in reply to a yang-library <%s> RPC.", rpc_name);
Radek Krejci2d088832018-08-21 13:40:14 +0200847 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200848 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200849
Michal Vasko77367452021-02-16 16:32:18 +0100850 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200851 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200852 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200853 ret = -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200854 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200855 }
856
Michal Vasko77367452021-02-16 16:32:18 +0100857 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200858 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100859 feature_count = 0;
860 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
861 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200862 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200863 /* ignore node from other schemas (augments) */
864 continue;
865 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200866 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200867 /* ignore empty nodes */
868 continue;
869 }
870 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200871 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200872 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200873 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200874 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200875 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200876 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100877 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
878 if (!(*result)[u].features) {
879 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200880 free_module_info(*result);
Michal Vasko77367452021-02-16 16:32:18 +0100881 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200882 ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100883 goto cleanup;
884 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200885 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100886 (*result)[u].features[feature_count + 1] = NULL;
887 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200888 } else if (!strcmp(iter->schema->name, "submodule")) {
889 submodules_count++;
890 }
891 }
892
893 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200894 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
895 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200896 ERRMEM;
Michal Vasko5ca5d972022-09-14 13:51:31 +0200897 free_module_info(*result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200898 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200899 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200900 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200901 } else {
902 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100903 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
904 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200905 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100906 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200907 if (mod != child->schema->module) {
908 continue;
909 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200910 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200911 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200912 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200913 }
914 }
915 }
916 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200917 }
918 }
919 }
920
Radek Krejcifd5b6682017-06-13 15:52:53 +0200921cleanup:
922 nc_rpc_free(rpc);
Michal Vasko77367452021-02-16 16:32:18 +0100923 lyd_free_tree(envp);
924 lyd_free_tree(op);
925 ly_set_free(modules, NULL);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200926
Radek Krejci235d8cb2018-08-17 14:04:32 +0200927 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko05532772021-06-03 12:12:38 +0200928 /* something bad happened, discard the session */
929 ERR(session, "Invalid session, discarding.");
Michal Vaskoc088f982021-10-05 12:23:07 +0200930 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200931 }
932
933 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200934}
935
Michal Vaskoc088f982021-10-05 12:23:07 +0200936/**
Michal Vasko5ca5d972022-09-14 13:51:31 +0200937 * @brief Build server module info from received capabilities.
Michal Vaskoc088f982021-10-05 12:23:07 +0200938 *
939 * @param[in] cpblts Server capabilities.
Michal Vasko5ca5d972022-09-14 13:51:31 +0200940 * @param[out] result Server modules.
Michal Vaskoc088f982021-10-05 12:23:07 +0200941 * @return 0 on success.
942 * @return -1 on error.
943 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200944static int
Michal Vasko5ca5d972022-09-14 13:51:31 +0200945build_module_info_cpblts(char **cpblts, struct module_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200946{
Michal Vasko77367452021-02-16 16:32:18 +0100947 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200948 char *module_cpblt, *ptr, *ptr2;
949
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200950 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +0200951 (*result) = calloc(u + 1, sizeof **result);
952 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200953 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200954 return -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200955 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200956
957 for (u = v = 0; cpblts[u]; ++u) {
958 module_cpblt = strstr(cpblts[u], "module=");
959 /* this capability requires a module */
960 if (!module_cpblt) {
961 continue;
962 }
963
964 /* get module's name */
965 ptr = (char *)module_cpblt + 7;
966 ptr2 = strchr(ptr, '&');
967 if (!ptr2) {
968 ptr2 = ptr + strlen(ptr);
969 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200970 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200971
972 /* get module's revision */
973 ptr = strstr(module_cpblt, "revision=");
974 if (ptr) {
975 ptr += 9;
976 ptr2 = strchr(ptr, '&');
977 if (!ptr2) {
978 ptr2 = ptr + strlen(ptr);
979 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200980 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200981 }
982
983 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200984 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200985
986 /* get module's features */
987 ptr = strstr(module_cpblt, "features=");
988 if (ptr) {
989 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +0100990 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200991 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
992 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +0100993 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
994 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
995 (*result)[v].features[feature_count + 1] = NULL;
996 ++feature_count;
997
Radek Krejci65ef6d52018-08-16 16:35:02 +0200998 ptr2 = ptr + 1;
999 }
1000 }
1001 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +01001002 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1003 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1004 (*result)[v].features[feature_count + 1] = NULL;
1005 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001006 }
1007 ++v;
1008 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001009
Michal Vasko303263d2021-10-05 12:18:21 +02001010 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001011}
1012
Michal Vaskoc088f982021-10-05 12:23:07 +02001013/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001014 * @brief Fill client context based on server modules info.
Michal Vaskoc088f982021-10-05 12:23:07 +02001015 *
1016 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001017 * @param[in] modules Server modules info.
1018 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001019 * @param[in] user_data User data for @p user_clb.
1020 * @param[in] has_get_schema Whether server supports get-schema RPC.
1021 * @return 0 on success.
1022 * @return -1 on error.
1023 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001024static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001025nc_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 +01001026 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001027{
Michal Vasko303263d2021-10-05 12:18:21 +02001028 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +01001029 struct lys_module *mod;
1030 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001031
1032 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +02001033 /* skip import-only modules */
1034 if (!modules[u].implemented) {
1035 continue;
1036 }
1037
Radek Krejci65ef6d52018-08-16 16:35:02 +02001038 /* we can continue even if it fails */
Michal Vasko5e32c402022-01-12 14:05:09 +01001039 nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules,
1040 user_clb, user_data, has_get_schema, &mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001041
1042 if (!mod) {
1043 if (session->status != NC_STATUS_RUNNING) {
1044 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +02001045 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001046 goto cleanup;
1047 }
1048
Michal Vasko5ca5d972022-09-14 13:51:31 +02001049 /* all loading ways failed, the module will be ignored in the received data */
1050 WRN(session, "Failed to load module \"%s@%s\".", modules[u].name, modules[u].revision ?
Michal Vasko05532772021-06-03 12:12:38 +02001051 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001052 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001053 }
Michal Vasko60f66602017-10-17 13:52:18 +02001054 }
1055
Michal Vasko77367452021-02-16 16:32:18 +01001056 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001057 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001058
1059cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001060 return ret;
1061}
1062
Michal Vaskoc088f982021-10-05 12:23:07 +02001063/**
Michal Vasko5ca5d972022-09-14 13:51:31 +02001064 * @brief Fill client context with ietf-netconf module.
Michal Vaskoc088f982021-10-05 12:23:07 +02001065 *
1066 * @param[in] session NC session with the context to modify.
Michal Vasko5ca5d972022-09-14 13:51:31 +02001067 * @param[in] modules Server module info.
1068 * @param[in] user_clb User callback for retrieving specific modules.
Michal Vaskoc088f982021-10-05 12:23:07 +02001069 * @param[in] user_data User data for @p user_clb.
1070 * @param[in] has_get_schema Whether server supports get-schema RPC.
1071 * @return 0 on success.
1072 * @return -1 on error.
1073 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001074static int
Michal Vasko5ca5d972022-09-14 13:51:31 +02001075nc_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 +01001076 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001077{
Michal Vasko77367452021-02-16 16:32:18 +01001078 uint32_t u;
Michal Vasko5e32c402022-01-12 14:05:09 +01001079 const char **features = NULL;
1080 struct ly_in *in;
Michal Vasko77367452021-02-16 16:32:18 +01001081 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001082
Michal Vasko5e32c402022-01-12 14:05:09 +01001083 /* find supported features (capabilities) in ietf-netconf */
1084 for (u = 0; modules[u].name; ++u) {
1085 if (!strcmp(modules[u].name, "ietf-netconf")) {
1086 assert(modules[u].implemented);
1087 features = (const char **)modules[u].features;
1088 break;
1089 }
1090 }
1091 if (!modules[u].name) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001092 ERR(session, "Base NETCONF module not supported by the server.");
Michal Vasko5e32c402022-01-12 14:05:09 +01001093 return -1;
1094 }
1095
Michal Vasko77367452021-02-16 16:32:18 +01001096 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vasko5e32c402022-01-12 14:05:09 +01001097 if (ietfnc) {
1098 /* make sure to enable all the features if already loaded */
1099 lys_set_implemented(ietfnc, features);
1100 } else {
1101 /* load the module */
1102 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 +02001103 if (!ietfnc) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001104 ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in);
1105 lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc);
1106 ly_in_free(in, 0);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001107 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001108 }
1109 if (!ietfnc) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001110 ERR(session, "Loading base NETCONF module failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001111 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001112 }
1113
Radek Krejci65ef6d52018-08-16 16:35:02 +02001114 return 0;
1115}
1116
Michal Vasko086311b2016-01-08 09:53:11 +01001117int
1118nc_ctx_check_and_fill(struct nc_session *session)
1119{
Michal Vasko303263d2021-10-05 12:18:21 +02001120 int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001121 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001122 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001123 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001124 char *revision;
Michal Vasko5ca5d972022-09-14 13:51:31 +02001125 struct module_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001126
Michal Vasko2e6defd2016-10-07 15:48:15 +02001127 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001128
Radek Krejci65ef6d52018-08-16 16:35:02 +02001129 /* 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 +02001130 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001131
Radek Krejci65ef6d52018-08-16 16:35:02 +02001132 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
Michal Vasko5ca5d972022-09-14 13:51:31 +02001133 * modules only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001134 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001135
1136 /* our callback is set later with appropriate data */
1137 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1138
1139 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001140 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001141 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001142 get_schema_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +02001143 if (yanglib_support) {
1144 break;
1145 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001146 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001147 yanglib_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +02001148 if (get_schema_support) {
1149 break;
1150 }
Michal Vasko086311b2016-01-08 09:53:11 +01001151 }
1152 }
Michal Vasko31dd4c52020-04-17 10:29:44 +02001153 if (get_schema_support) {
Michal Vasko05532772021-06-03 12:12:38 +02001154 VRB(session, "Capability for <get-schema> support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001155 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001156 VRB(session, "Capability for <get-schema> support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001157 }
1158 if (yanglib_support) {
Michal Vasko05532772021-06-03 12:12:38 +02001159 VRB(session, "Capability for yang-library support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001160 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001161 VRB(session, "Capability for yang-library support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001162 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001163
Michal Vasko5ca5d972022-09-14 13:51:31 +02001164 /* get information about server's modules from capabilities list until we will have yang-library */
1165 if (build_module_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1166 ERR(session, "Unable to get server module information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001167 goto cleanup;
1168 }
1169
Michal Vasko086311b2016-01-08 09:53:11 +01001170 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001171 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 +02001172 WRN(session, "Loading NETCONF monitoring module failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001173 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001174 }
1175
1176 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001177 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001178 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001179 }
1180
Radek Krejci65ef6d52018-08-16 16:35:02 +02001181 /* get correct version of ietf-yang-library into context */
1182 if (yanglib_support) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001183 /* use get-schema to get server's ietf-yang-library */
Radek Krejcib1d250e2018-04-12 13:54:18 +02001184 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1185 if (!revision) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001186 WRN(session, "Loading NETCONF ietf-yang-library module failed, missing revision in NETCONF <hello> message.");
Michal Vasko05532772021-06-03 12:12:38 +02001187 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001188 yanglib_support = 0;
1189 } else {
1190 revision = strndup(&revision[9], 10);
Michal Vasko5e32c402022-01-12 14:05:09 +01001191 if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001192 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001193 WRN(session, "Loading NETCONF ietf-yang-library module failed, unable to use it to learn all "
Michal Vasko05532772021-06-03 12:12:38 +02001194 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001195 yanglib_support = 0;
1196 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001197 if (strcmp(revision, "2019-01-04") >= 0) {
1198 /* we also need ietf-datastores to be implemented */
Michal Vasko5e32c402022-01-12 14:05:09 +01001199 if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001200 get_schema_support, &mod)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001201 WRN(session, "Loading NETCONF ietf-datastores module failed, unable to use yang-library "
Michal Vasko05532772021-06-03 12:12:38 +02001202 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001203 yanglib_support = 0;
1204 }
1205 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001206 free(revision);
Michal Vasko77367452021-02-16 16:32:18 +01001207
1208 /* ietf-netconf-nmda is needed to issue get-data */
Michal Vasko5e32c402022-01-12 14:05:09 +01001209 if (!nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko77367452021-02-16 16:32:18 +01001210 get_schema_support, &mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001211 VRB(session, "Support for <get-data> from ietf-netconf-nmda found.");
Michal Vasko303263d2021-10-05 12:18:21 +02001212 get_data_support = 1;
Michal Vasko5e32c402022-01-12 14:05:09 +01001213 } else {
1214 VRB(session, "Support for <get-data> from ietf-netconf-nmda not found.");
Michal Vasko77367452021-02-16 16:32:18 +01001215 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001216 }
1217 }
1218
Michal Vasko5ca5d972022-09-14 13:51:31 +02001219 /* prepare structured information about server's modules */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001220 if (yanglib_support) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001221 if (build_module_info_yl(session, get_data_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001222 goto cleanup;
1223 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001224 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001225 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001226 /* prefer yang-library information, currently we have it from capabilities used for getting correct
Michal Vasko5ca5d972022-09-14 13:51:31 +02001227 * yang-library module */
1228 free_module_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001229 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001230 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001231 }
Michal Vaskoef578332016-01-25 13:20:09 +01001232
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001233 /* compile all modules at once to avoid invalid errors or warnings */
1234 ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
1235
1236 /* fill the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001237 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1238 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001239 }
1240
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001241 /* compile it */
1242 if (ly_ctx_compile(session->ctx)) {
1243 goto cleanup;
1244 }
1245
1246 /* success */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001247 ret = 0;
1248
Michal Vaskoeee99412016-11-21 10:19:43 +01001249 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02001250 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 +02001251 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001252 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001253
1254cleanup:
Michal Vasko5ca5d972022-09-14 13:51:31 +02001255 free_module_info(server_modules);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001256
Radek Krejcifd5b6682017-06-13 15:52:53 +02001257 /* set user callback back */
1258 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001259 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001260 ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001261
Michal Vaskoef578332016-01-25 13:20:09 +01001262 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001263}
1264
1265API struct nc_session *
1266nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1267{
Michal Vaskod083db62016-01-19 10:31:29 +01001268 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001269
Michal Vasko45e53ae2016-04-07 11:46:03 +02001270 if (fdin < 0) {
1271 ERRARG("fdin");
1272 return NULL;
1273 } else if (fdout < 0) {
1274 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001275 return NULL;
1276 }
1277
1278 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001279 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001280 if (!session) {
1281 ERRMEM;
1282 return NULL;
1283 }
1284 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001285
1286 /* transport specific data */
1287 session->ti_type = NC_TI_FD;
1288 session->ti.fd.in = fdin;
1289 session->ti.fd.out = fdout;
1290
Robin Jarry4e38e292019-10-15 17:14:14 +02001291 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1292 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001293 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001294 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001295
1296 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001297 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001298 goto fail;
1299 }
1300 session->status = NC_STATUS_RUNNING;
1301
Michal Vaskoef578332016-01-25 13:20:09 +01001302 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001303 goto fail;
1304 }
1305
1306 return session;
1307
1308fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001309 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001310 return NULL;
1311}
1312
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001313API struct nc_session *
1314nc_connect_unix(const char *address, struct ly_ctx *ctx)
1315{
1316 struct nc_session *session = NULL;
1317 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001318 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001319 char *username;
1320 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001321 char *buf = NULL;
1322 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001323
1324 if (address == NULL) {
1325 ERRARG("address");
1326 return NULL;
1327 }
1328
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001329 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1330 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001331 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001332 goto fail;
1333 }
1334
1335 memset(&sun, 0, sizeof(sun));
1336 sun.sun_family = AF_UNIX;
1337 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1338
1339 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001340 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001341 goto fail;
1342 }
1343
1344 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001345 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001346 goto fail;
1347 }
1348
1349 /* prepare session structure */
1350 session = nc_new_session(NC_CLIENT, 0);
1351 if (!session) {
1352 ERRMEM;
1353 goto fail;
1354 }
1355 session->status = NC_STATUS_STARTING;
1356
1357 /* transport specific data */
1358 session->ti_type = NC_TI_UNIX;
1359 session->ti.unixsock.sock = sock;
1360 sock = -1; /* do not close sock in fail label anymore */
1361
Robin Jarry4e38e292019-10-15 17:14:14 +02001362 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1363 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001364 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001365 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001366
Michal Vasko93224072021-11-09 12:14:28 +01001367 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001368
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001369 pw = nc_getpwuid(geteuid(), &pw_buf, &buf, &buf_size);
1370 if (!pw) {
1371 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1372 goto fail;
1373 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001374 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001375 free(buf);
1376 if (!username) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001377 ERRMEM;
1378 goto fail;
1379 }
Michal Vasko93224072021-11-09 12:14:28 +01001380 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001381
1382 /* NETCONF handshake */
1383 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1384 goto fail;
1385 }
1386 session->status = NC_STATUS_RUNNING;
1387
1388 if (nc_ctx_check_and_fill(session) == -1) {
1389 goto fail;
1390 }
1391
1392 return session;
1393
1394fail:
1395 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001396 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001397 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001398 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001399 return NULL;
1400}
1401
Michal Vasko056f53c2022-10-21 13:38:15 +02001402/**
1403 * @brief Try to connect a socket, optionally a pending one from a previous attempt.
1404 *
1405 * @param[in] timeout_ms Timeout in ms to wait for the connection to be fully established, -1 to block.
1406 * @param[in,out] sock_pending Optional previously created socked that was not fully connected yet. If provided and
1407 * connected, is set to -1.
1408 * @param[in] res Addrinfo resource to use when creating a new socket.
1409 * @param[in] ka Keepalives to set.
1410 * @return Connected socket or -1 on error.
Frank Rimpler9f838b02018-07-25 06:44:03 +00001411 */
1412static int
Michal Vasko056f53c2022-10-21 13:38:15 +02001413sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001414{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001415 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001416 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001417 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001418 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001419 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001420 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001421 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001422 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001423
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001424 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001425 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001426 sock = *sock_pending;
1427 } else {
1428 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001429 if (res->ai_family == AF_INET6) {
1430 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001431 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001432 } else {
1433 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001434 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001435 }
1436 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
Michal Vasko05532772021-06-03 12:12:38 +02001437 WRN(NULL, "inet_ntop() failed (%s).", strerror(errno));
Michal Vasko5e8d0192019-06-24 19:19:49 +02001438 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001439 VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001440 }
1441
1442 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001443 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1444 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001445 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001446 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001447 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001448 /* make the socket non-blocking */
1449 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001450 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001451 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001452 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001453 /* non-blocking connect! */
1454 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1455 if (errno != EINPROGRESS) {
1456 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001457 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001458 goto cleanup;
1459 }
1460 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001461 }
Michal Vasko7d965dc2018-03-12 14:39:23 +01001462
Frank Rimpler9f838b02018-07-25 06:44:03 +00001463 FD_ZERO(&wset);
1464 FD_SET(sock, &wset);
1465
Michal Vasko056f53c2022-10-21 13:38:15 +02001466 /* wait for some data on the socket */
1467 if (timeout_ms != -1) {
1468 ts.tv_sec = timeout_ms / 1000;
1469 ts.tv_usec = (timeout_ms % 1000) * 1000;
1470 }
1471 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout_ms != -1) ? &ts : NULL)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001472 ERR(NULL, "select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001473 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001474 }
1475
Michal Vasko5e8d0192019-06-24 19:19:49 +02001476 if (ret == 0) {
1477 /* there was a timeout */
Michal Vasko056f53c2022-10-21 13:38:15 +02001478 VRB(NULL, "Timed out after %d ms (%s).", timeout_ms, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001479 if (sock_pending) {
1480 /* no sock-close, we'll try it again */
1481 *sock_pending = sock;
1482 } else {
1483 close(sock);
1484 }
1485 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001486 }
Radek Krejci782041a2018-08-20 10:09:45 +02001487
1488 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001489 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001490 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001491 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001492 goto cleanup;
1493 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001494 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001495 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001496 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001497 errno = error;
1498 goto cleanup;
1499 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001500
1501 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001502 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001503 goto cleanup;
1504 }
1505
Michal Vasko056f53c2022-10-21 13:38:15 +02001506 /* connected */
1507 if (sock_pending) {
1508 *sock_pending = -1;
1509 }
Michal Vasko086311b2016-01-08 09:53:11 +01001510 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001511
Frank Rimpler9f838b02018-07-25 06:44:03 +00001512cleanup:
1513 if (sock_pending) {
1514 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001515 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001516 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001517 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001518}
1519
Frank Rimpler9f838b02018-07-25 06:44:03 +00001520int
Michal Vasko056f53c2022-10-21 13:38:15 +02001521nc_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 +00001522{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001523 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001524 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001525 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001526 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001527 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001528
Michal Vasko056f53c2022-10-21 13:38:15 +02001529 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout_ms, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001530
1531 /* no pending socket */
1532 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001533 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001534 snprintf(port_s, 6, "%u", port);
1535 memset(&hints, 0, sizeof hints);
1536 hints.ai_family = AF_UNSPEC;
1537 hints.ai_socktype = SOCK_STREAM;
1538 hints.ai_protocol = IPPROTO_TCP;
1539 i = getaddrinfo(host, port_s, &hints, &res_list);
1540 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001541 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001542 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001543 }
1544
1545 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vasko056f53c2022-10-21 13:38:15 +02001546 sock = sock_connect(timeout_ms, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001547 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001548 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001549 /* try the next resource */
1550 continue;
1551 } else {
1552 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001553 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001554 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001555 }
Michal Vasko05532772021-06-03 12:12:38 +02001556 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 +01001557
1558 opt = 1;
1559 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001560 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001561 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001562 }
1563
Michal Vasko66032bc2019-01-22 15:03:12 +01001564 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1565 buf = malloc(INET6_ADDRSTRLEN);
1566 if (!buf) {
1567 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001568 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001569 }
1570 if (res->ai_family == AF_INET) {
1571 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1572 } else {
1573 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1574 }
1575 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +02001576 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
Michal Vasko66032bc2019-01-22 15:03:12 +01001577 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001578 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001579 }
1580
1581 *ip_host = buf;
1582 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001583 break;
1584 }
1585 freeaddrinfo(res_list);
1586
1587 } else {
1588 /* try to get a connection with the pending socket */
1589 assert(sock_pending);
Michal Vasko056f53c2022-10-21 13:38:15 +02001590 sock = sock_connect(timeout_ms, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001591 }
1592
1593 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001594
1595error:
Michal Vasko0be85692021-03-02 08:04:57 +01001596 if (res_list) {
1597 freeaddrinfo(res_list);
1598 }
Michal Vaskocb846632019-12-13 15:12:45 +01001599 if (sock != -1) {
1600 close(sock);
1601 }
1602 if (sock_pending) {
1603 *sock_pending = -1;
1604 }
1605 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001606}
1607
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001608#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001609
Michal Vasko3031aae2016-01-27 16:07:18 +01001610int
Michal Vasko9d4cca52022-09-07 11:19:57 +02001611nc_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 +01001612{
1613 int sock;
1614
Michal Vasko45e53ae2016-04-07 11:46:03 +02001615 if (!address) {
1616 ERRARG("address");
1617 return -1;
1618 } else if (!port) {
1619 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001620 return -1;
1621 }
1622
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001623 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001624 if (sock == -1) {
1625 return -1;
1626 }
1627
1628 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001629 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1630 if (!client_opts.ch_binds) {
1631 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001632 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001633 return -1;
1634 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001635
Michal Vasko9d4cca52022-09-07 11:19:57 +02001636 client_opts.ch_binds_aux = nc_realloc(client_opts.ch_binds_aux, client_opts.ch_bind_count * sizeof *client_opts.ch_binds_aux);
1637 if (!client_opts.ch_binds_aux) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001638 ERRMEM;
1639 close(sock);
1640 return -1;
1641 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001642 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].ti = ti;
1643 client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].hostname = hostname ? strdup(hostname) : NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001644
Michal Vasko3031aae2016-01-27 16:07:18 +01001645 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
1646 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1647 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001648 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001649
1650 return 0;
1651}
1652
1653int
1654nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1655{
1656 uint32_t i;
1657 int ret = -1;
1658
1659 if (!address && !port && !ti) {
1660 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1661 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001662 free(client_opts.ch_binds[i].address);
1663
1664 free(client_opts.ch_binds_aux[i].hostname);
Michal Vasko3031aae2016-01-27 16:07:18 +01001665
1666 ret = 0;
1667 }
Michal Vasko9d4cca52022-09-07 11:19:57 +02001668 client_opts.ch_bind_count = 0;
1669
Michal Vasko3031aae2016-01-27 16:07:18 +01001670 free(client_opts.ch_binds);
1671 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001672
1673 free(client_opts.ch_binds_aux);
1674 client_opts.ch_binds_aux = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001675 } else {
1676 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001677 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1678 (!port || (client_opts.ch_binds[i].port == port)) &&
Michal Vasko9d4cca52022-09-07 11:19:57 +02001679 (!ti || (client_opts.ch_binds_aux[i].ti == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001680 close(client_opts.ch_binds[i].sock);
Michal Vasko9d4cca52022-09-07 11:19:57 +02001681 free(client_opts.ch_binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001682
1683 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001684 if (!client_opts.ch_bind_count) {
1685 free(client_opts.ch_binds);
1686 client_opts.ch_binds = NULL;
Michal Vasko9d4cca52022-09-07 11:19:57 +02001687
1688 free(client_opts.ch_binds_aux);
1689 client_opts.ch_binds_aux = NULL;
Michal Vasko66c762a2016-10-13 10:34:14 +02001690 } else if (i < client_opts.ch_bind_count) {
Michal Vasko9d4cca52022-09-07 11:19:57 +02001691 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count],
1692 sizeof *client_opts.ch_binds);
1693
1694 memcpy(&client_opts.ch_binds_aux[i], &client_opts.ch_binds_aux[client_opts.ch_bind_count],
1695 sizeof *client_opts.ch_binds_aux);
Michal Vasko66c762a2016-10-13 10:34:14 +02001696 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001697
1698 ret = 0;
1699 }
1700 }
1701 }
1702
1703 return ret;
1704}
1705
1706API int
1707nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1708{
1709 int sock;
1710 char *host = NULL;
1711 uint16_t port, idx;
1712
Michal Vasko45e53ae2016-04-07 11:46:03 +02001713 if (!client_opts.ch_binds) {
1714 ERRINIT;
1715 return -1;
1716 } else if (!session) {
1717 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001718 return -1;
1719 }
1720
1721 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001722 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001723 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001724 return sock;
1725 }
1726
Radek Krejci53691be2016-02-22 13:58:37 +01001727#ifdef NC_ENABLED_SSH
Michal Vasko9d4cca52022-09-07 11:19:57 +02001728 if (client_opts.ch_binds_aux[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001729 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001730 } else
1731#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001732#ifdef NC_ENABLED_TLS
Michal Vasko9d4cca52022-09-07 11:19:57 +02001733 if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) {
1734 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT,
1735 client_opts.ch_binds_aux[idx].hostname);
Michal Vasko3d865d22016-01-28 16:00:53 +01001736 } else
1737#endif
1738 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001739 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001740 *session = NULL;
1741 }
1742
1743 free(host);
1744
1745 if (!(*session)) {
1746 return -1;
1747 }
1748
1749 return 1;
1750}
1751
Radek Krejci53691be2016-02-22 13:58:37 +01001752#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001753
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001754API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001755nc_session_get_cpblts(const struct nc_session *session)
1756{
1757 if (!session) {
1758 ERRARG("session");
1759 return NULL;
1760 }
1761
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001762 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001763}
1764
1765API const char *
1766nc_session_cpblt(const struct nc_session *session, const char *capab)
1767{
1768 int i, len;
1769
1770 if (!session) {
1771 ERRARG("session");
1772 return NULL;
1773 } else if (!capab) {
1774 ERRARG("capab");
1775 return NULL;
1776 }
1777
1778 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001779 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1780 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1781 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001782 }
1783 }
1784
1785 return NULL;
1786}
1787
Michal Vasko9cd26a82016-05-31 08:58:48 +02001788API int
1789nc_session_ntf_thread_running(const struct nc_session *session)
1790{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001791 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001792 ERRARG("session");
1793 return 0;
1794 }
1795
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02001796 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running);
Michal Vasko9cd26a82016-05-31 08:58:48 +02001797}
1798
Michal Vaskob7558c52016-02-26 15:04:19 +01001799API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001800nc_client_init(void)
1801{
1802 nc_init();
1803}
1804
1805API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001806nc_client_destroy(void)
1807{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001808 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001809#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001810 nc_client_ch_del_bind(NULL, 0, 0);
1811#endif
1812#ifdef NC_ENABLED_SSH
1813 nc_client_ssh_destroy_opts();
1814#endif
1815#ifdef NC_ENABLED_TLS
1816 nc_client_tls_destroy_opts();
1817#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001818 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001819}
1820
Michal Vasko77367452021-02-16 16:32:18 +01001821static NC_MSG_TYPE
1822recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001823{
Michal Vasko77367452021-02-16 16:32:18 +01001824 char *ptr;
1825 struct lyd_attr *attr;
1826 uint64_t cur_msgid;
1827
1828 assert(envp && !envp->schema);
1829
1830 /* find the message-id attribute */
1831 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1832 if (!strcmp(attr->name.name, "message-id")) {
1833 break;
1834 }
1835 }
1836
1837 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001838 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001839 return NC_MSG_REPLY_ERR_MSGID;
1840 }
1841
1842 cur_msgid = strtoul(attr->value, &ptr, 10);
1843 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001844 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1845 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001846 return NC_MSG_REPLY_ERR_MSGID;
1847 }
1848
1849 return NC_MSG_REPLY;
1850}
1851
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001852/**
1853 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1854 *
1855 * @param[in] session NETCONF session used to send error messages.
1856 * @param[in] msg Message to check for type.
1857 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1858 * @return NC_MSG_NOTIF If format roughly matches a notification;
1859 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1860 */
1861static NC_MSG_TYPE
1862get_msg_type(struct nc_session *session, struct ly_in *msg)
1863{
1864 const char *str, *end;
1865
1866 str = ly_in_memory(msg, NULL);
1867
1868 while (*str) {
1869 /* Skip whitespaces */
1870 while (isspace(*str)) {
1871 str++;
1872 }
1873
1874 if (*str == '<') {
1875 str++;
1876 if (!strncmp(str, "!--", 3)) {
1877 /* Skip comments */
1878 end = "-->";
1879 str = strstr(str, end);
1880 } else if (!strncmp(str, "?xml", 4)) {
1881 /* Skip xml declaration */
1882 end = "?>";
1883 str = strstr(str, end);
1884 } else if (!strncmp(str, "rpc-reply", 9)) {
1885 return NC_MSG_REPLY;
1886 } else if (!strncmp(str, "notification", 12)) {
1887 return NC_MSG_NOTIF;
1888 } else {
1889 ERR(session, "Unknown xml element '%.10s'.", str);
1890 return NC_MSG_ERROR;
1891 }
1892 if (!str) {
1893 /* No matching ending tag found */
1894 ERR(session, "No matching ending tag '%s' found in xml message.", end);
1895 return NC_MSG_ERROR;
1896 }
1897 str += strlen(end);
1898 } else {
1899 /* Not a valid xml */
1900 ERR(session, "Unexpected character '%c' in xml message.", *str);
1901 return NC_MSG_ERROR;
1902 }
1903 }
1904
1905 /* Unexpected end of message */
1906 ERR(session, "Unexpected end of xml message.");
1907 return NC_MSG_ERROR;
1908}
1909
1910/**
1911 * @brief Function to receive either replies or notifications.
1912 *
1913 * @param[in] session NETCONF session from which this function receives messages.
1914 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
1915 * @param[in] expected Type of the message the caller desired.
1916 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
1917 * @return NC_MSG_REPLY If a rpc-reply was received;
1918 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01001919 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001920 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
1921 */
1922static NC_MSG_TYPE
1923recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
1924{
1925 struct nc_msg_cont **cont_ptr;
1926 struct ly_in *msg = NULL;
1927 struct nc_msg_cont *cont, *prev;
1928 NC_MSG_TYPE ret = NC_MSG_ERROR;
1929 int r;
1930
1931 *message = NULL;
1932
1933 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001934 r = nc_session_client_msgs_lock(session, &timeout, __func__);
1935 if (!r) {
1936 ret = NC_MSG_WOULDBLOCK;
1937 goto cleanup;
1938 } else if (r == -1) {
1939 ret = NC_MSG_ERROR;
1940 goto cleanup;
1941 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001942
1943 /* Find the expected message in the buffer */
1944 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001945 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001946 prev = cont;
1947 }
1948
1949 if (cont) {
1950 /* Remove found message from buffer */
1951 if (prev) {
1952 prev->next = cont->next;
1953 } else {
1954 session->opts.client.msgs = cont->next;
1955 }
1956
1957 /* Use the buffer message */
1958 ret = cont->type;
1959 msg = cont->msg;
1960 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02001961 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001962 }
1963
1964 /* Read a message from the wire */
1965 r = nc_read_msg_poll_io(session, timeout, &msg);
1966 if (!r) {
1967 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02001968 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001969 } else if (r == -1) {
1970 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001971 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001972 }
1973
1974 /* Basic check to determine message type */
1975 ret = get_msg_type(session, msg);
1976 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02001977 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001978 }
1979
1980 /* If received a message of different type store it in the buffer */
1981 if (ret != expected) {
1982 cont_ptr = &session->opts.client.msgs;
1983 while (*cont_ptr) {
1984 cont_ptr = &((*cont_ptr)->next);
1985 }
1986 *cont_ptr = malloc(sizeof **cont_ptr);
1987 if (!*cont_ptr) {
1988 ERRMEM;
1989 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001990 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001991 }
1992 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001993 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001994 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001995 (*cont_ptr)->next = NULL;
1996 }
1997
Michal Vasko01130bd2021-08-26 11:47:38 +02001998cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001999 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02002000 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002001
Michal Vasko01130bd2021-08-26 11:47:38 +02002002cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002003 if (ret == expected) {
2004 *message = msg;
2005 } else {
2006 ly_in_free(msg, 1);
2007 }
2008 return ret;
2009}
2010
Michal Vasko77367452021-02-16 16:32:18 +01002011static NC_MSG_TYPE
2012recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
2013{
Michal Vasko77367452021-02-16 16:32:18 +01002014 LY_ERR lyrc;
2015 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002016 NC_MSG_TYPE ret = NC_MSG_ERROR;
2017
2018 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
2019
2020 *envp = NULL;
2021
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002022 /* Receive messages until a rpc-reply is found or a timeout or error reached */
2023 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
2024 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01002025 goto cleanup;
2026 }
2027
2028 /* parse */
2029 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
2030 if (!lyrc) {
2031 ret = recv_reply_check_msgid(session, *envp, msgid);
2032 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002033 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002034 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
Michal Vasko9a108052022-04-01 12:06:54 +02002035 lyd_free_tree(*envp);
2036 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002037 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002038 goto cleanup;
2039 }
2040
Michal Vasko77367452021-02-16 16:32:18 +01002041cleanup:
2042 ly_in_free(msg, 1);
2043 return ret;
2044}
2045
2046static int
2047recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2048{
Michal Vasko143aa142021-10-01 15:31:48 +02002049 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002050 struct nc_rpc_act_generic *rpc_gen;
2051 struct ly_in *in;
2052 struct lyd_node *tree, *op2;
2053 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002054 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002055
2056 switch (rpc->type) {
2057 case NC_RPC_ACT_GENERIC:
2058 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2059 if (rpc_gen->has_data) {
2060 tree = rpc_gen->content.data;
2061
2062 /* find the operation node */
2063 lyrc = LY_EINVAL;
2064 LYD_TREE_DFS_BEGIN(tree, op2) {
2065 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2066 lyrc = lyd_dup_single(op2, NULL, 0, op);
2067 break;
2068 }
2069 LYD_TREE_DFS_END(tree, op2);
2070 }
2071 } else {
2072 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2073 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2074 ly_in_free(in, 0);
2075 if (lyrc) {
Michal Vasko9a108052022-04-01 12:06:54 +02002076 lyd_free_tree(tree);
Michal Vasko77367452021-02-16 16:32:18 +01002077 return -1;
2078 }
2079
2080 /* we want just the operation node */
2081 lyrc = lyd_dup_single(op2, NULL, 0, op);
2082
2083 lyd_free_tree(tree);
2084 }
2085 break;
2086 case NC_RPC_GETCONFIG:
2087 module_name = "ietf-netconf";
2088 rpc_name = "get-config";
2089 break;
2090 case NC_RPC_EDIT:
2091 module_name = "ietf-netconf";
2092 rpc_name = "edit-config";
2093 break;
2094 case NC_RPC_COPY:
2095 module_name = "ietf-netconf";
2096 rpc_name = "copy-config";
2097 break;
2098 case NC_RPC_DELETE:
2099 module_name = "ietf-netconf";
2100 rpc_name = "delete-config";
2101 break;
2102 case NC_RPC_LOCK:
2103 module_name = "ietf-netconf";
2104 rpc_name = "lock";
2105 break;
2106 case NC_RPC_UNLOCK:
2107 module_name = "ietf-netconf";
2108 rpc_name = "unlock";
2109 break;
2110 case NC_RPC_GET:
2111 module_name = "ietf-netconf";
2112 rpc_name = "get";
2113 break;
2114 case NC_RPC_KILL:
2115 module_name = "ietf-netconf";
2116 rpc_name = "kill-session";
2117 break;
2118 case NC_RPC_COMMIT:
2119 module_name = "ietf-netconf";
2120 rpc_name = "commit";
2121 break;
2122 case NC_RPC_DISCARD:
2123 module_name = "ietf-netconf";
2124 rpc_name = "discard-changes";
2125 break;
2126 case NC_RPC_CANCEL:
2127 module_name = "ietf-netconf";
2128 rpc_name = "cancel-commit";
2129 break;
2130 case NC_RPC_VALIDATE:
2131 module_name = "ietf-netconf";
2132 rpc_name = "validate";
2133 break;
2134 case NC_RPC_GETSCHEMA:
2135 module_name = "ietf-netconf-monitoring";
2136 rpc_name = "get-schema";
2137 break;
2138 case NC_RPC_SUBSCRIBE:
2139 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002140 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002141 break;
2142 case NC_RPC_GETDATA:
2143 module_name = "ietf-netconf-nmda";
2144 rpc_name = "get-data";
2145 break;
2146 case NC_RPC_EDITDATA:
2147 module_name = "ietf-netconf-nmda";
2148 rpc_name = "edit-data";
2149 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002150 case NC_RPC_ESTABLISHSUB:
2151 module_name = "ietf-subscribed-notifications";
2152 rpc_name = "establish-subscription";
2153 break;
2154 case NC_RPC_MODIFYSUB:
2155 module_name = "ietf-subscribed-notifications";
2156 rpc_name = "modify-subscription";
2157 break;
2158 case NC_RPC_DELETESUB:
2159 module_name = "ietf-subscribed-notifications";
2160 rpc_name = "delete-subscription";
2161 break;
2162 case NC_RPC_KILLSUB:
2163 module_name = "ietf-subscribed-notifications";
2164 rpc_name = "kill-subscription";
2165 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002166 case NC_RPC_ESTABLISHPUSH:
2167 module_name = "ietf-subscribed-notifications";
2168 rpc_name = "establish-subscription";
2169 module_check = "ietf-yang-push";
2170 break;
2171 case NC_RPC_MODIFYPUSH:
2172 module_name = "ietf-subscribed-notifications";
2173 rpc_name = "modify-subscription";
2174 module_check = "ietf-yang-push";
2175 break;
2176 case NC_RPC_RESYNCSUB:
2177 module_name = "ietf-yang-push";
2178 rpc_name = "resync-subscription";
2179 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002180 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002181 lyrc = LY_EINT;
2182 break;
2183 }
2184
2185 if (module_name && rpc_name) {
2186 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2187 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002188 ERR(session, "Missing \"%s\" module in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002189 return -1;
2190 }
2191
2192 /* create the operation node */
2193 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2194 }
Michal Vasko305faca2021-03-25 09:16:02 +01002195 if (module_check) {
2196 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002197 ERR(session, "Missing \"%s\" module in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002198 return -1;
2199 }
2200 }
Michal Vasko77367452021-02-16 16:32:18 +01002201
2202 if (lyrc) {
2203 return -1;
2204 }
2205 return 0;
2206}
2207
2208API NC_MSG_TYPE
2209nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2210 struct lyd_node **op)
2211{
2212 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002213
Michal Vasko45e53ae2016-04-07 11:46:03 +02002214 if (!session) {
2215 ERRARG("session");
2216 return NC_MSG_ERROR;
2217 } else if (!rpc) {
2218 ERRARG("rpc");
2219 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002220 } else if (!envp) {
2221 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02002222 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002223 } else if (!op) {
2224 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002225 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002226 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002227 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002228 return NC_MSG_ERROR;
2229 }
Michal Vasko77367452021-02-16 16:32:18 +01002230
2231 /* get a duplicate of the RPC node to append reply to */
2232 if (recv_reply_dup_rpc(session, rpc, op)) {
2233 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002234 }
Michal Vasko086311b2016-01-08 09:53:11 +01002235
Michal Vasko77367452021-02-16 16:32:18 +01002236 /* receive a reply */
2237 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002238
Michal Vasko77367452021-02-16 16:32:18 +01002239 /* do not return the RPC copy on error or if the reply includes no data */
2240 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2241 lyd_free_tree(*op);
2242 *op = NULL;
2243 }
2244 return ret;
2245}
2246
2247static NC_MSG_TYPE
2248recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2249{
Michal Vasko77367452021-02-16 16:32:18 +01002250 LY_ERR lyrc;
2251 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002252 NC_MSG_TYPE ret = NC_MSG_ERROR;
2253
2254 *op = NULL;
2255 *envp = NULL;
2256
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002257 /* Receive messages until a notification is found or a timeout or error reached */
2258 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2259 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002260 goto cleanup;
2261 }
2262
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002263 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002264 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2265 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002266 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002267 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002268 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
Michal Vasko9a108052022-04-01 12:06:54 +02002269 lyd_free_tree(*envp);
2270 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002271 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002272 goto cleanup;
2273 }
2274
Michal Vasko77367452021-02-16 16:32:18 +01002275cleanup:
2276 ly_in_free(msg, 1);
2277 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002278}
2279
2280API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002281nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002282{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002283 if (!session) {
2284 ERRARG("session");
2285 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002286 } else if (!envp) {
2287 ERRARG("envp");
2288 return NC_MSG_ERROR;
2289 } else if (!op) {
2290 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002291 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002292 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002293 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002294 return NC_MSG_ERROR;
2295 }
2296
Michal Vasko77367452021-02-16 16:32:18 +01002297 /* receive a notification */
2298 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002299}
2300
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002301static void *
2302nc_recv_notif_thread(void *arg)
2303{
2304 struct nc_ntf_thread_arg *ntarg;
2305 struct nc_session *session;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002306 nc_notif_dispatch_clb notif_clb;
2307 void *user_data;
Michal Vasko743ff9f2022-10-20 10:03:13 +02002308
Michal Vaskoffb35e92022-10-20 09:07:25 +02002309 void (*free_data)(void *);
Michal Vasko77367452021-02-16 16:32:18 +01002310 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002311 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002312
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002313 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002314 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002315
2316 ntarg = (struct nc_ntf_thread_arg *)arg;
2317 session = ntarg->session;
2318 notif_clb = ntarg->notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002319 user_data = ntarg->user_data;
2320 free_data = ntarg->free_data;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002321 free(ntarg);
2322
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002323 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) {
Michal Vasko77367452021-02-16 16:32:18 +01002324 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002325 if (msgtype == NC_MSG_NOTIF) {
Michal Vaskoffb35e92022-10-20 09:07:25 +02002326 notif_clb(session, envp, op, user_data);
Michal Vasko77367452021-02-16 16:32:18 +01002327 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2328 lyd_free_tree(envp);
2329 lyd_free_tree(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002330 break;
2331 }
Michal Vasko77367452021-02-16 16:32:18 +01002332 lyd_free_tree(envp);
2333 lyd_free_tree(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002334 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002335 /* quit this thread once the session is broken */
2336 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002337 }
2338
2339 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2340 }
2341
Michal Vasko05532772021-06-03 12:12:38 +02002342 VRB(session, "Notification thread exit.");
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002343 ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoffb35e92022-10-20 09:07:25 +02002344 if (free_data) {
2345 free_data(user_data);
2346 }
2347
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002348 return NULL;
2349}
2350
2351API int
Michal Vaskoffb35e92022-10-20 09:07:25 +02002352nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb)
2353{
2354 return nc_recv_notif_dispatch_data(session, notif_clb, NULL, NULL);
2355}
2356
2357API int
2358nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data,
2359 void (*free_data)(void *))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002360{
2361 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002362 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002363 int ret;
2364
Michal Vasko45e53ae2016-04-07 11:46:03 +02002365 if (!session) {
2366 ERRARG("session");
2367 return -1;
2368 } else if (!notif_clb) {
2369 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002370 return -1;
2371 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002372 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002373 return -1;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002374 }
2375
2376 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002377 if (!ntarg) {
2378 ERRMEM;
2379 return -1;
2380 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002381 ntarg->session = session;
2382 ntarg->notif_clb = notif_clb;
Michal Vaskoffb35e92022-10-20 09:07:25 +02002383 ntarg->user_data = user_data;
2384 ntarg->free_data = free_data;
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002385 ATOMIC_INC_RELAXED(session->opts.client.ntf_thread_count);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002386
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002387 /* just so that nc_recv_notif_thread() does not immediately exit */
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002388 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002389
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002390 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002391 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002392 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002393 free(ntarg);
Michal Vaskoa8ec54b2022-10-20 09:59:07 +02002394 if (ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count) == 1) {
2395 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0);
2396 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002397 return -1;
2398 }
2399
2400 return 0;
2401}
2402
Michal Vasko77367452021-02-16 16:32:18 +01002403static const char *
2404nc_wd2str(NC_WD_MODE wd)
2405{
2406 switch (wd) {
2407 case NC_WD_ALL:
2408 return "report-all";
2409 case NC_WD_ALL_TAG:
2410 return "report-all-tagged";
2411 case NC_WD_TRIM:
2412 return "trim";
2413 case NC_WD_EXPLICIT:
2414 return "explicit";
2415 default:
2416 break;
2417 }
2418
2419 return NULL;
2420}
2421
Michal Vasko086311b2016-01-08 09:53:11 +01002422API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002423nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002424{
2425 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002426 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002427 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002428 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002429 struct nc_rpc_getconfig *rpc_gc;
2430 struct nc_rpc_edit *rpc_e;
2431 struct nc_rpc_copy *rpc_cp;
2432 struct nc_rpc_delete *rpc_del;
2433 struct nc_rpc_lock *rpc_lock;
2434 struct nc_rpc_get *rpc_g;
2435 struct nc_rpc_kill *rpc_k;
2436 struct nc_rpc_commit *rpc_com;
2437 struct nc_rpc_cancel *rpc_can;
2438 struct nc_rpc_validate *rpc_val;
2439 struct nc_rpc_getschema *rpc_gs;
2440 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002441 struct nc_rpc_getdata *rpc_getd;
2442 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002443 struct nc_rpc_establishsub *rpc_estsub;
2444 struct nc_rpc_modifysub *rpc_modsub;
2445 struct nc_rpc_deletesub *rpc_delsub;
2446 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002447 struct nc_rpc_establishpush *rpc_estpush;
2448 struct nc_rpc_modifypush *rpc_modpush;
2449 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002450 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002451 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002452 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002453 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002454 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002455 uint64_t cur_msgid;
2456
Michal Vasko45e53ae2016-04-07 11:46:03 +02002457 if (!session) {
2458 ERRARG("session");
2459 return NC_MSG_ERROR;
2460 } else if (!rpc) {
2461 ERRARG("rpc");
2462 return NC_MSG_ERROR;
2463 } else if (!msgid) {
2464 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002465 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002466 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002467 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002468 return NC_MSG_ERROR;
2469 }
2470
Michal Vaskoc1171a42019-11-05 12:06:46 +01002471 switch (rpc->type) {
2472 case NC_RPC_ACT_GENERIC:
2473 /* checked when parsing */
2474 break;
2475 case NC_RPC_GETCONFIG:
2476 case NC_RPC_EDIT:
2477 case NC_RPC_COPY:
2478 case NC_RPC_DELETE:
2479 case NC_RPC_LOCK:
2480 case NC_RPC_UNLOCK:
2481 case NC_RPC_GET:
2482 case NC_RPC_KILL:
2483 case NC_RPC_COMMIT:
2484 case NC_RPC_DISCARD:
2485 case NC_RPC_CANCEL:
2486 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002487 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002488 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002489 ERR(session, "Missing \"ietf-netconf\" module in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002490 return NC_MSG_ERROR;
2491 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002492 break;
2493 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002494 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002495 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002496 ERR(session, "Missing \"ietf-netconf-monitoring\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002497 return NC_MSG_ERROR;
2498 }
2499 break;
2500 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002501 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002502 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002503 ERR(session, "Missing \"notifications\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002504 return NC_MSG_ERROR;
2505 }
2506 break;
2507 case NC_RPC_GETDATA:
2508 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002509 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002510 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002511 ERR(session, "Missing \"ietf-netconf-nmda\" module in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002512 return NC_MSG_ERROR;
2513 }
2514 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002515 case NC_RPC_ESTABLISHSUB:
2516 case NC_RPC_MODIFYSUB:
2517 case NC_RPC_DELETESUB:
2518 case NC_RPC_KILLSUB:
2519 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2520 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002521 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002522 return NC_MSG_ERROR;
2523 }
2524 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002525 case NC_RPC_ESTABLISHPUSH:
2526 case NC_RPC_MODIFYPUSH:
2527 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2528 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002529 ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002530 return NC_MSG_ERROR;
2531 }
2532 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2533 if (!mod2) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002534 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002535 return NC_MSG_ERROR;
2536 }
2537 break;
2538 case NC_RPC_RESYNCSUB:
2539 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2540 if (!mod) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002541 ERR(session, "Missing \"ietf-yang-push\" module in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002542 return NC_MSG_ERROR;
2543 }
2544 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002545 case NC_RPC_UNKNOWN:
2546 ERRINT;
2547 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002548 }
2549
Michal Vaskoab9deb62021-05-27 11:37:00 +02002550#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2551
Michal Vasko086311b2016-01-08 09:53:11 +01002552 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002553 case NC_RPC_ACT_GENERIC:
2554 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002555
2556 if (rpc_gen->has_data) {
2557 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002558 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002559 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002560 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2561 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2562 ly_in_free(in, 0);
2563 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002564 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002565 }
Michal Vasko086311b2016-01-08 09:53:11 +01002566 }
2567 break;
2568
2569 case NC_RPC_GETCONFIG:
2570 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2571
Michal Vaskoab9deb62021-05-27 11:37:00 +02002572 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2573 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2574 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002575 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002576 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002577 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2578 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002579 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002580 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2581 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2582 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002583 }
2584 }
2585
2586 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002587 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002588 if (!ietfncwd) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002589 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002590 lyrc = LY_ENOTFOUND;
2591 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002592 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002593 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 +01002594 }
2595 break;
2596
2597 case NC_RPC_EDIT:
2598 rpc_e = (struct nc_rpc_edit *)rpc;
2599
Michal Vaskoab9deb62021-05-27 11:37:00 +02002600 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2601 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2602 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002603
2604 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002605 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 +01002606 }
Michal Vasko086311b2016-01-08 09:53:11 +01002607 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002608 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 +01002609 }
Michal Vasko086311b2016-01-08 09:53:11 +01002610 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002611 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 +01002612 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002613 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002614 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 +01002615 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002616 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002617 }
2618 break;
2619
2620 case NC_RPC_COPY:
2621 rpc_cp = (struct nc_rpc_copy *)rpc;
2622
Michal Vaskoab9deb62021-05-27 11:37:00 +02002623 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2624 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002625 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002626 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002627 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002628 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002629 }
2630
Michal Vaskoab9deb62021-05-27 11:37:00 +02002631 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002632 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002633 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002634 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 +01002635 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002636 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002637 }
2638 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002639 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002640 }
2641
2642 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002643 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002644 if (!ietfncwd) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002645 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002646 lyrc = LY_ENOTFOUND;
2647 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002648 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002649 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 +01002650 }
2651 break;
2652
2653 case NC_RPC_DELETE:
2654 rpc_del = (struct nc_rpc_delete *)rpc;
2655
Michal Vaskoab9deb62021-05-27 11:37:00 +02002656 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2657 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002658 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002659 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002660 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002661 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002662 }
2663 break;
2664
2665 case NC_RPC_LOCK:
2666 rpc_lock = (struct nc_rpc_lock *)rpc;
2667
Michal Vaskoab9deb62021-05-27 11:37:00 +02002668 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2669 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2670 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002671 break;
2672
2673 case NC_RPC_UNLOCK:
2674 rpc_lock = (struct nc_rpc_lock *)rpc;
2675
Michal Vaskoab9deb62021-05-27 11:37:00 +02002676 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2677 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2678 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002679 break;
2680
2681 case NC_RPC_GET:
2682 rpc_g = (struct nc_rpc_get *)rpc;
2683
Michal Vaskoab9deb62021-05-27 11:37:00 +02002684 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002685 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002686 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002687 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2688 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002689 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002690 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2691 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2692 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002693 }
2694 }
2695
2696 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002697 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002698 if (!ietfncwd) {
Michal Vasko5ca5d972022-09-14 13:51:31 +02002699 ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002700 lyrc = LY_ENOTFOUND;
2701 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002702 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002703 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 +01002704 }
2705 break;
2706
2707 case NC_RPC_KILL:
2708 rpc_k = (struct nc_rpc_kill *)rpc;
2709
Michal Vaskoab9deb62021-05-27 11:37:00 +02002710 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002711 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002712 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002713 break;
2714
2715 case NC_RPC_COMMIT:
2716 rpc_com = (struct nc_rpc_commit *)rpc;
2717
Michal Vaskoab9deb62021-05-27 11:37:00 +02002718 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002719 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002720 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002721 }
2722
2723 if (rpc_com->confirm_timeout) {
2724 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002725 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002726 }
Michal Vasko086311b2016-01-08 09:53:11 +01002727 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002728 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002729 }
Michal Vasko086311b2016-01-08 09:53:11 +01002730 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002731 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002732 }
2733 break;
2734
2735 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002736 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002737 break;
2738
2739 case NC_RPC_CANCEL:
2740 rpc_can = (struct nc_rpc_cancel *)rpc;
2741
Michal Vaskoab9deb62021-05-27 11:37:00 +02002742 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002743 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002744 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002745 }
2746 break;
2747
2748 case NC_RPC_VALIDATE:
2749 rpc_val = (struct nc_rpc_validate *)rpc;
2750
Michal Vaskoab9deb62021-05-27 11:37:00 +02002751 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2752 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002753 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002754 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002755 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 +01002756 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002757 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002758 }
2759 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002760 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002761 }
2762 break;
2763
2764 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002765 rpc_gs = (struct nc_rpc_getschema *)rpc;
2766
Michal Vaskoab9deb62021-05-27 11:37:00 +02002767 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2768 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002769 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002770 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002771 }
2772 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002773 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002774 }
2775 break;
2776
2777 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002778 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2779
Michal Vaskoab9deb62021-05-27 11:37:00 +02002780 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002781 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002782 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002783 }
2784
2785 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002786 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002787 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2788 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002789 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002790 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2791 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2792 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002793 }
2794 }
Michal Vasko086311b2016-01-08 09:53:11 +01002795 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002796 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002797 }
Michal Vasko086311b2016-01-08 09:53:11 +01002798 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002799 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002800 }
2801 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002802
2803 case NC_RPC_GETDATA:
2804 rpc_getd = (struct nc_rpc_getdata *)rpc;
2805
Michal Vaskoab9deb62021-05-27 11:37:00 +02002806 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2807 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2808
Michal Vaskoc1171a42019-11-05 12:06:46 +01002809 if (rpc_getd->filter) {
2810 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002811 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 +01002812 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002813 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002814 }
2815 }
2816 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002817 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002818 }
2819 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002820 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2821 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002822 }
2823 if (rpc_getd->max_depth) {
2824 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002825 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002826 }
2827 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002828 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002829 }
2830
2831 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002832 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002833 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 +01002834 }
2835 break;
2836
2837 case NC_RPC_EDITDATA:
2838 rpc_editd = (struct nc_rpc_editdata *)rpc;
2839
Michal Vaskoab9deb62021-05-27 11:37:00 +02002840 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2841 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002842
2843 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002844 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2845 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002846 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002847 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002848 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 +01002849 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002850 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002851 }
2852 break;
2853
Michal Vasko96f247a2021-03-15 13:32:10 +01002854 case NC_RPC_ESTABLISHSUB:
2855 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2856
Michal Vaskoab9deb62021-05-27 11:37:00 +02002857 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002858
2859 if (rpc_estsub->filter) {
2860 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002861 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2862 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002863 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002864 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002865 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002866 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002867 }
2868 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002869 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002870
2871 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002872 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002873 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002874 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002875 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002876 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002877 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002878 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002879 }
2880 break;
2881
2882 case NC_RPC_MODIFYSUB:
2883 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2884
Michal Vaskoab9deb62021-05-27 11:37:00 +02002885 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002886
2887 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002888 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002889
2890 if (rpc_modsub->filter) {
2891 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002892 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
2893 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002894 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002895 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002896 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002897 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002898 }
2899 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002900 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002901 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002902 }
2903 break;
2904
2905 case NC_RPC_DELETESUB:
2906 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2907
Michal Vaskoab9deb62021-05-27 11:37:00 +02002908 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002909
2910 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002911 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002912 break;
2913
2914 case NC_RPC_KILLSUB:
2915 rpc_killsub = (struct nc_rpc_killsub *)rpc;
2916
Michal Vaskoab9deb62021-05-27 11:37:00 +02002917 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002918
2919 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002920 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002921 break;
2922
Michal Vasko305faca2021-03-25 09:16:02 +01002923 case NC_RPC_ESTABLISHPUSH:
2924 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
2925
Michal Vaskoab9deb62021-05-27 11:37:00 +02002926 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
2927 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002928
2929 if (rpc_estpush->filter) {
2930 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002931 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
2932 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002933 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002934 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002935 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002936 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002937 }
2938 }
2939
2940 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002941 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002942 }
Michal Vasko305faca2021-03-25 09:16:02 +01002943 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002944 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002945 }
2946
2947 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002948 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002949 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002950 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002951 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002952 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002953 }
2954 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002955 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002956 if (rpc_estpush->dampening_period) {
2957 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002958 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002959 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002960 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
2961 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002962 if (rpc_estpush->excluded_change) {
2963 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002964 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
2965 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002966 }
2967 }
2968 }
2969 break;
2970
2971 case NC_RPC_MODIFYPUSH:
2972 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
2973
Michal Vaskoab9deb62021-05-27 11:37:00 +02002974 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002975
2976 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002977 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
2978 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002979
2980 if (rpc_modpush->filter) {
2981 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002982 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
2983 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002984 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002985 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002986 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002987 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002988 }
2989 }
Michal Vasko305faca2021-03-25 09:16:02 +01002990 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002991 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002992 }
2993
2994 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002995 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002996 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002997 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002998 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002999 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003000 }
3001 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02003002 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01003003 if (rpc_modpush->dampening_period) {
3004 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003005 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003006 }
3007 }
3008 break;
3009
3010 case NC_RPC_RESYNCSUB:
3011 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
3012
Michal Vaskoab9deb62021-05-27 11:37:00 +02003013 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01003014 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02003015 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01003016 break;
3017
Michal Vasko96f247a2021-03-15 13:32:10 +01003018 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01003019 ERRINT;
3020 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01003021 }
3022
Michal Vaskoab9deb62021-05-27 11:37:00 +02003023#undef CHECK_LYRC_BREAK
3024
3025 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02003026 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02003027 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02003028 return NC_MSG_ERROR;
3029 }
3030
Michal Vasko131120a2018-05-29 15:44:02 +02003031 /* send RPC, store its message ID */
3032 r = nc_send_msg_io(session, timeout, data);
3033 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003034
Radek Krejcib4b19062018-02-07 16:33:06 +01003035 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003036 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003037 }
Michal Vasko086311b2016-01-08 09:53:11 +01003038
Michal Vasko131120a2018-05-29 15:44:02 +02003039 if (r == NC_MSG_RPC) {
3040 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003041 }
Michal Vasko131120a2018-05-29 15:44:02 +02003042 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003043}
Michal Vaskode2946c2017-01-12 12:19:26 +01003044
3045API void
3046nc_client_session_set_not_strict(struct nc_session *session)
3047{
3048 if (session->side != NC_CLIENT) {
3049 ERRARG("session");
3050 return;
3051 }
3052
3053 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3054}