blob: 21d444cfb40f68fc37afea244404cf4f74371eb0 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_client.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 session client functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
Radek Krejcifd5b6682017-06-13 15:52:53 +020015#define _GNU_SOURCE
Michal Vaskobbf52c82020-04-07 13:08:50 +020016
17#ifdef __linux__
18# include <sys/syscall.h>
19#endif
20
Michal Vaskob83a3fa2021-05-26 09:53:42 +020021#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010022#include <assert.h>
tadeas-vintrlik54f142a2021-08-23 10:36:18 +020023#include <ctype.h>
Michal Vasko086311b2016-01-08 09:53:11 +010024#include <errno.h>
25#include <fcntl.h>
26#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010027#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010028#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020029#include <poll.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020031#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010032#include <stdlib.h>
33#include <string.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020034#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035#include <sys/socket.h>
36#include <sys/stat.h>
37#include <sys/types.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020038#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010039#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010040
41#include <libyang/libyang.h>
42
Michal Vasko9e8ac262020-04-07 13:06:45 +020043#include "compat.h"
Michal Vasko086311b2016-01-08 09:53:11 +010044#include "libnetconf.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010045#include "messages_client.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020046#include "session_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010047
Michal Vasko8a4e1462020-05-07 11:32:31 +020048#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020049#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020050
Michal Vasko80ef5d22016-01-18 09:21:02 +010051static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
52
Radek Krejci62aa0642017-05-25 16:33:49 +020053#ifdef NC_ENABLED_SSH
54int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
55char *sshauth_password(const char *username, const char *hostname, void *priv);
56char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020057char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
Radek Krejci62aa0642017-05-25 16:33:49 +020058#endif /* NC_ENABLED_SSH */
59
60static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
61static pthread_key_t nc_client_context_key;
62#ifdef __linux__
63static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020064 .opts.ka = {
65 .enabled = 1,
66 .idle_time = 1,
67 .max_probes = 10,
68 .probe_interval = 5
69 },
Radek Krejci62aa0642017-05-25 16:33:49 +020070#ifdef NC_ENABLED_SSH
71 .ssh_opts = {
72 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
73 .auth_hostkey_check = sshauth_hostkey_check,
74 .auth_password = sshauth_password,
75 .auth_interactive = sshauth_interactive,
76 .auth_privkey_passphrase = sshauth_privkey_passphrase
77 },
78 .ssh_ch_opts = {
79 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
80 .auth_hostkey_check = sshauth_hostkey_check,
81 .auth_password = sshauth_password,
82 .auth_interactive = sshauth_interactive,
83 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020084 },
Radek Krejci62aa0642017-05-25 16:33:49 +020085#endif /* NC_ENABLED_SSH */
86 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020087 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020088};
89#endif
90
91static void
92nc_client_context_free(void *ptr)
93{
94 struct nc_client_context *c = (struct nc_client_context *)ptr;
95
Radek Krejci5cebc6b2017-05-26 13:24:38 +020096 if (--(c->refcount)) {
97 /* still used */
98 return;
99 }
100
Radek Krejci62aa0642017-05-25 16:33:49 +0200101#ifdef __linux__
102 /* in __linux__ we use static memory in the main thread,
103 * so this check is for programs terminating the main()
104 * function by pthread_exit() :)
105 */
106 if (c != &context_main)
107#endif
108 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200109 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400110 free(c->opts.schema_searchpath);
111
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200112#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400113 int i;
114 for (i = 0; i < c->opts.ch_bind_count; ++i) {
115 close(c->opts.ch_binds[i].sock);
116 free((char *)c->opts.ch_binds[i].address);
117 }
118 free(c->opts.ch_binds);
119 c->opts.ch_binds = NULL;
120 c->opts.ch_bind_count = 0;
Radek Krejci62aa0642017-05-25 16:33:49 +0200121#endif
122#ifdef NC_ENABLED_SSH
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400123 _nc_client_ssh_destroy_opts(&c->ssh_opts);
124 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200125#endif
126#ifdef NC_ENABLED_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400127 _nc_client_tls_destroy_opts(&c->tls_opts);
128 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200129#endif
130 free(c);
131 }
132}
133
134static void
135nc_client_context_createkey(void)
136{
137 int r;
138
139 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200140 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200141 pthread_setspecific(nc_client_context_key, NULL);
142}
143
144struct nc_client_context *
145nc_client_context_location(void)
146{
147 struct nc_client_context *e;
148
149 pthread_once(&nc_client_context_once, nc_client_context_createkey);
150 e = pthread_getspecific(nc_client_context_key);
151 if (!e) {
152 /* prepare ly_err storage */
153#ifdef __linux__
154 if (getpid() == syscall(SYS_gettid)) {
155 /* main thread - use global variable instead of thread-specific variable. */
156 e = &context_main;
157 } else
158#endif /* __linux__ */
159 {
160 e = calloc(1, sizeof *e);
161 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200162 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200163#ifdef NC_ENABLED_SSH
164 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
165 e->ssh_opts.auth_pref[0].value = 3;
166 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
167 e->ssh_opts.auth_pref[1].value = 2;
168 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
169 e->ssh_opts.auth_pref[2].value = 1;
170 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
171 e->ssh_opts.auth_password = sshauth_password;
172 e->ssh_opts.auth_interactive = sshauth_interactive;
173 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
174
175 /* callhome settings are the same except the inverted auth methods preferences */
176 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
177 e->ssh_ch_opts.auth_pref[0].value = 1;
178 e->ssh_ch_opts.auth_pref[1].value = 2;
179 e->ssh_ch_opts.auth_pref[2].value = 3;
180#endif /* NC_ENABLED_SSH */
181 }
182 pthread_setspecific(nc_client_context_key, e);
183 }
184
185 return e;
186}
187
188#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100189
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200190API void *
191nc_client_get_thread_context(void)
192{
193 return nc_client_context_location();
194}
195
196API void
197nc_client_set_thread_context(void *context)
198{
199 struct nc_client_context *old, *new;
200
201 if (!context) {
202 ERRARG(context);
203 return;
204 }
205
206 new = (struct nc_client_context *)context;
207 old = nc_client_context_location();
208 if (old == new) {
209 /* nothing to change */
210 return;
211 }
212
213 /* replace old by new, increase reference counter in the newly set context */
214 nc_client_context_free(old);
215 new->refcount++;
216 pthread_setspecific(nc_client_context_key, new);
217}
218
Radek Krejcifd5b6682017-06-13 15:52:53 +0200219int
220nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
221{
222 /* assign context (dicionary needed for handshake) */
223 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100224 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200225 return EXIT_FAILURE;
226 }
227
228 /* user path must be first, the first path is used to store schemas retreived via get-schema */
229 if (client_opts.schema_searchpath) {
230 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200231 } else if (!access(NC_YANG_DIR, F_OK)) {
232 ly_ctx_set_searchdir(ctx, NC_YANG_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200233 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200234
235 /* set callback for getting schemas, if provided */
236 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
237 } else {
238 session->flags |= NC_SESSION_SHAREDCTX;
239 }
240
241 session->ctx = ctx;
242
243 return EXIT_SUCCESS;
244}
245
Michal Vasko086311b2016-01-08 09:53:11 +0100246API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100247nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100248{
Michal Vasko3031aae2016-01-27 16:07:18 +0100249 if (client_opts.schema_searchpath) {
250 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100251 }
Michal Vasko086311b2016-01-08 09:53:11 +0100252
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100253 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100254 client_opts.schema_searchpath = strdup(path);
255 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100256 ERRMEM;
257 return 1;
258 }
259 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100260 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100261 }
262
263 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100264}
265
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100266API const char *
267nc_client_get_schema_searchpath(void)
268{
269 return client_opts.schema_searchpath;
270}
271
Radek Krejcifd5b6682017-06-13 15:52:53 +0200272API int
273nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100274{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200275 client_opts.schema_clb = clb;
276 if (clb) {
277 client_opts.schema_clb_data = user_data;
278 } else {
279 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100280 }
281
282 return 0;
283}
284
Radek Krejcifd5b6682017-06-13 15:52:53 +0200285API ly_module_imp_clb
286nc_client_get_schema_callback(void **user_data)
287{
288 if (user_data) {
289 (*user_data) = client_opts.schema_clb_data;
290 }
291 return client_opts.schema_clb;
292}
293
Radek Krejci65ef6d52018-08-16 16:35:02 +0200294struct schema_info {
295 char *name;
296 char *revision;
297 struct {
298 char *name;
299 char *revision;
300 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100301 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200302 int implemented;
303};
304
305struct clb_data_s {
306 void *user_data;
307 ly_module_imp_clb user_clb;
308 struct schema_info *schemas;
309 struct nc_session *session;
310 int has_get_schema;
311};
312
Michal Vaskoc088f982021-10-05 12:23:07 +0200313/**
314 * @brief Retrieve YANG schema content from a local file.
315 *
316 * @param[in] name Schema name.
317 * @param[in] rev Schema revision.
318 * @param[in] clb_data get-schema callback data.
319 * @param[out] format Schema format.
320 * @return Schema content.
321 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200322static char *
323retrieve_schema_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200324 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100325{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200326 char *localfile = NULL;
327 FILE *f;
328 long length, l;
329 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100330
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200331 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200332 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
333 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200334 return NULL;
335 }
336 if (localfile) {
Michal Vasko05532772021-06-03 12:12:38 +0200337 VRB(clb_data->session, "Reading schema from localfile \"%s\".", localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200338 f = fopen(localfile, "r");
339 if (!f) {
Michal Vasko05532772021-06-03 12:12:38 +0200340 ERR(clb_data->session, "Unable to open \"%s\" file to get schema (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200341 free(localfile);
342 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100343 }
Michal Vasko086311b2016-01-08 09:53:11 +0100344
Radek Krejci65ef6d52018-08-16 16:35:02 +0200345 fseek(f, 0, SEEK_END);
346 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200347 if (length < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200348 ERR(clb_data->session, "Unable to get size of schema file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200349 free(localfile);
350 fclose(f);
351 return NULL;
352 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200353 fseek(f, 0, SEEK_SET);
354
355 model_data = malloc(length + 1);
356 if (!model_data) {
357 ERRMEM;
358 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko05532772021-06-03 12:12:38 +0200359 ERR(clb_data->session, "Reading schema from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
360 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200361 free(model_data);
362 model_data = NULL;
363 } else {
364 /* terminating NULL byte */
365 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100366 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200367 fclose(f);
368 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100369 }
370
Radek Krejci65ef6d52018-08-16 16:35:02 +0200371 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100372}
373
Michal Vaskoc088f982021-10-05 12:23:07 +0200374/**
375 * @brief Retrieve YANG schema content from a reply to get-schema RPC.
376 *
377 * @param[in] name Schema name.
378 * @param[in] rev Schema revision.
379 * @param[in] clb_data get-schema callback data.
380 * @param[out] format Schema format.
381 * @return Schema content.
382 */
Michal Vasko086311b2016-01-08 09:53:11 +0100383static char *
Radek Krejci65ef6d52018-08-16 16:35:02 +0200384retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200385 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100386{
Michal Vasko086311b2016-01-08 09:53:11 +0100387 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100388 struct lyd_node *envp = NULL, *op = NULL;
389 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100390 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100391 uint64_t msgid;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200392 char *localfile = NULL;
393 FILE *f;
394 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100395
Michal Vasko05532772021-06-03 12:12:38 +0200396 VRB(clb_data->session, "Reading schema from server via get-schema.");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200397 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100398
Radek Krejci65ef6d52018-08-16 16:35:02 +0200399 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100400 usleep(1000);
401 }
402 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200403 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100404 nc_rpc_free(rpc);
405 return NULL;
406 }
407
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200408 do {
Michal Vasko77367452021-02-16 16:32:18 +0100409 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200410 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100411 nc_rpc_free(rpc);
412 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200413 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100414 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200415 } else if ((msg == NC_MSG_ERROR) || !op) {
Michal Vasko05532772021-06-03 12:12:38 +0200416 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100417 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100418 }
419
Michal Vasko77367452021-02-16 16:32:18 +0100420 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200421 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100422 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200423 }
Michal Vasko77367452021-02-16 16:32:18 +0100424 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200425 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200426 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100427 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200428 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200429 break;
430 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100431 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200432 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200433 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200434 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200435 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100436 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200437 }
Michal Vasko086311b2016-01-08 09:53:11 +0100438
Radek Krejci488b0702018-08-29 14:47:15 +0200439 if (model_data && !model_data[0]) {
440 /* empty data */
441 free(model_data);
442 model_data = NULL;
443 }
444
Radek Krejcifd5b6682017-06-13 15:52:53 +0200445 /* try to store the model_data into local schema repository */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200446 if (model_data) {
447 *format = LYS_IN_YANG;
448 if (client_opts.schema_searchpath) {
Michal Vasko77367452021-02-16 16:32:18 +0100449 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
450 rev ? rev : "") == -1) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200451 ERRMEM;
Michal Vaskof945da52018-02-15 08:45:13 +0100452 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200453 f = fopen(localfile, "w");
454 if (!f) {
Michal Vasko05532772021-06-03 12:12:38 +0200455 WRN(clb_data->session, "Unable to store \"%s\" as a local copy of schema retrieved via <get-schema> (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200456 localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200457 } else {
458 fputs(model_data, f);
459 fclose(f);
460 }
461 free(localfile);
Michal Vaskof945da52018-02-15 08:45:13 +0100462 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200463 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200464 }
465
Michal Vasko77367452021-02-16 16:32:18 +0100466cleanup:
467 lyd_free_tree(envp);
468 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100469 return model_data;
470}
471
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200472static void
473free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200474{
475 free(data);
476 (void)user_data;
477}
478
Michal Vaskoc088f982021-10-05 12:23:07 +0200479/**
480 * @brief Retrieve YANG schema content.
481 *
482 * @param[in] mod_name Schema name.
483 * @param[in] mod_rev Schema revision.
484 * @param[in] submod_name Optional submodule name.
485 * @param[in] sub_rev Submodule revision.
486 * @param[in] user_data get-schema callback data.
487 * @param[out] format Schema format.
488 * @param[out] module_data Schema content.
489 * @param[out] free_module_data Callback for freeing @p module_data.
490 * @return LY_ERR value.
491 */
Michal Vasko77367452021-02-16 16:32:18 +0100492static LY_ERR
Radek Krejci65ef6d52018-08-16 16:35:02 +0200493retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
Michal Vasko77367452021-02-16 16:32:18 +0100494 void *user_data, LYS_INFORMAT *format, const char **module_data,
495 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200496{
497 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
498 unsigned int u, v, match = 1;
499 const char *name = NULL, *rev = NULL;
500 char *model_data = NULL;
501
502 /* get and check the final name and revision of the schema to be retrieved */
503 if (!mod_rev || !mod_rev[0]) {
504 /* newest revision requested - get the newest revision from the list of available modules on server */
505 match = 0;
506 for (u = 0; clb_data->schemas[u].name; ++u) {
507 if (strcmp(mod_name, clb_data->schemas[u].name)) {
508 continue;
509 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200510 if (!match || (strcmp(mod_rev, clb_data->schemas[u].revision) > 0)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200511 mod_rev = clb_data->schemas[u].revision;
512 }
513 match = u + 1;
514 }
515 if (!match) {
Michal Vasko1d2665c2021-02-12 09:28:44 +0100516 /* valid situation if we are retrieving YANG 1.1 schema and have only capabilities for now
517 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko05532772021-06-03 12:12:38 +0200518 VRB(clb_data->session, "Unable to identify revision of the schema \"%s\" from "
519 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200520 }
521 }
522 if (submod_name) {
523 name = submod_name;
524 if (sub_rev) {
525 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200526 } else if (match) {
527 if (!clb_data->schemas[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200528 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
529 "in schema \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200530 } else {
531 for (v = 0; clb_data->schemas[match - 1].submodules[v].name; ++v) {
532 if (!strcmp(submod_name, clb_data->schemas[match - 1].submodules[v].name)) {
533 rev = sub_rev = clb_data->schemas[match - 1].submodules[v].revision;
534 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200535 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200536 if (!rev) {
Michal Vasko05532772021-06-03 12:12:38 +0200537 ERR(clb_data->session, "Requested submodule \"%s\" is not known for schema \"%s\" on server side.",
538 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100539 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200540 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200541 }
542 }
543 } else {
544 name = mod_name;
545 rev = mod_rev;
546 }
547
Michal Vasko05532772021-06-03 12:12:38 +0200548 VRB(clb_data->session, "Retrieving data for schema \"%s\", revision \"%s\".", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200549
550 if (match) {
551 /* we have enough information to avoid communication with server and try to get
552 * the schema locally */
553
554 /* 1. try to get data locally */
555 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
556
557 /* 2. try to use <get-schema> */
558 if (!model_data && clb_data->has_get_schema) {
559 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
560 }
561 } else {
562 /* we are unsure which revision of the schema we should load, so first try to get
563 * the newest revision from the server via get-schema and only if the server does not
564 * implement get-schema, try to load the newest revision locally. This is imperfect
565 * solution, but there are situation when a client does not know what revision is
566 * actually implemented by the server. */
567
568 /* 1. try to use <get-schema> */
569 if (clb_data->has_get_schema) {
570 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
571 }
572
573 /* 2. try to get data locally */
574 if (!model_data) {
575 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
576 }
577 }
578
579 /* 3. try to use user callback */
580 if (!model_data && clb_data->user_clb) {
Michal Vasko05532772021-06-03 12:12:38 +0200581 VRB(clb_data->session, "Reading schema via user callback.");
Michal Vasko77367452021-02-16 16:32:18 +0100582 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
583 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200584 }
585
Jan Kundrát35972df2018-09-06 19:00:01 +0200586 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100587 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200588 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200589}
590
Michal Vaskoc088f982021-10-05 12:23:07 +0200591/**
592 * @brief Load a YANG schema into context.
593 *
594 * @param[in] session NC session.
595 * @param[in] name Schema name.
596 * @param[in] revision Schema revision.
597 * @param[in] schemas Server schema info built from capabilities.
598 * @param[in] user_clb User callback for retireving schema data.
599 * @param[in] user_data User data for @p user_clb.
600 * @param[in] has_get_schema Whether the server supports get-schema.
601 * @param[out] mod Loaded module.
602 * @return 0 on success.
603 * @return -1 on error.
604 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100605static int
Radek Krejci65ef6d52018-08-16 16:35:02 +0200606nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, struct schema_info *schemas,
Michal Vasko77367452021-02-16 16:32:18 +0100607 ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod)
Michal Vaskoceae0152018-02-14 16:03:59 +0100608{
609 int ret = 0;
610 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200611 const char *module_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200612 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200613
614 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200615 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100616
Radek Krejci65ef6d52018-08-16 16:35:02 +0200617 *mod = NULL;
618 if (revision) {
Michal Vasko77367452021-02-16 16:32:18 +0100619 *mod = ly_ctx_get_module(session->ctx, name, revision);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200620 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100621 if (*mod) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200622 if (!(*mod)->implemented) {
Michal Vaskoceae0152018-02-14 16:03:59 +0100623 /* make the present module implemented */
Michal Vasko77367452021-02-16 16:32:18 +0100624 if (lys_set_implemented(*mod, NULL)) {
Michal Vasko05532772021-06-03 12:12:38 +0200625 ERR(session, "Failed to implement model \"%s\".", (*mod)->name);
Michal Vaskoceae0152018-02-14 16:03:59 +0100626 ret = -1;
627 }
628 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200629 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100630 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200631 clb_data.has_get_schema = has_get_schema;
632 clb_data.schemas = schemas;
633 clb_data.session = session;
634 clb_data.user_clb = user_clb;
635 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100636
637 /* clear all the errors and just collect them for now */
638 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200639 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100640
Radek Krejci65ef6d52018-08-16 16:35:02 +0200641 /* get module data */
Michal Vasko77367452021-02-16 16:32:18 +0100642 retrieve_schema_data(name, revision, NULL, NULL, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100643
Radek Krejci65ef6d52018-08-16 16:35:02 +0200644 if (module_data) {
645 /* parse the schema */
646 ly_ctx_set_module_imp_clb(session->ctx, retrieve_schema_data, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100647
Michal Vasko029c5372021-07-09 10:54:21 +0200648 lys_parse_mem(session->ctx, module_data, format, mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200649 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100650 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100651 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100652
Radek Krejci65ef6d52018-08-16 16:35:02 +0200653 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
654 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100655
Michal Vaskodbf7c272018-02-19 09:07:59 +0100656 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200657 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100658 if (!(*mod)) {
659 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100660 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100661 }
662 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200663 } else {
664 /* print only warnings */
665 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
666 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100667 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200668 }
669 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100670 }
671
Michal Vaskodbf7c272018-02-19 09:07:59 +0100672 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100673 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100674 }
675
676 return ret;
677}
678
Radek Krejci65ef6d52018-08-16 16:35:02 +0200679static void
680free_schema_info(struct schema_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200681{
Michal Vasko77367452021-02-16 16:32:18 +0100682 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200683
Radek Krejci65ef6d52018-08-16 16:35:02 +0200684 if (!list) {
685 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200686 }
687
Radek Krejci65ef6d52018-08-16 16:35:02 +0200688 for (u = 0; list[u].name; ++u) {
689 free(list[u].name);
690 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100691 if (list[u].features) {
692 for (v = 0; list[u].features[v]; ++v) {
693 free(list[u].features[v]);
694 }
695 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200696 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200697 if (list[u].submodules) {
698 for (v = 0; list[u].submodules[v].name; ++v) {
699 free(list[u].submodules[v].name);
700 free(list[u].submodules[v].revision);
701 }
702 free(list[u].submodules);
703 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200704 }
705 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200706}
707
Michal Vaskoc088f982021-10-05 12:23:07 +0200708/**
709 * @brief Build server schema info from ietf-yang-library data.
710 *
711 * @param[in] session NC session.
712 * @param[in] has_get_data Whether get-data RPC is available or only get.
713 * @param[out] result Server schemas.
714 * @return 0 on success.
715 * @return -1 on error.
716 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200717static int
Michal Vasko303263d2021-10-05 12:18:21 +0200718build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema_info **result)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200719{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200720 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100721 struct lyd_node *op = NULL, *envp = NULL;
722 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200723 NC_MSG_TYPE msg;
724 uint64_t msgid;
Radek Krejcia8dfaea2018-08-17 10:55:09 +0200725 struct ly_set *modules = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100726 uint32_t u, v, submodules_count, feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200727 struct lyd_node *iter, *child;
728 struct lys_module *mod;
Michal Vasko303263d2021-10-05 12:18:21 +0200729 int ret = 0;
730 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200731
732 /* get yang-library data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200733 if (has_get_data) {
734 rpc_name = "get-data";
735 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
736 rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
737 NC_PARAMTYPE_CONST);
738 } else {
739 rpc = nc_rpc_getdata("ietf-datastores:operational",
740 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
741 0, NC_PARAMTYPE_CONST);
742 }
Radek Krejci261737f2018-08-21 09:22:34 +0200743 } else {
Michal Vasko303263d2021-10-05 12:18:21 +0200744 rpc_name = "get";
745 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
746 rpc = nc_rpc_get("/ietf-yang-library:*", 0, NC_PARAMTYPE_CONST);
747 } else {
748 rpc = nc_rpc_get("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", 0,
749 NC_PARAMTYPE_CONST);
750 }
Radek Krejci261737f2018-08-21 09:22:34 +0200751 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200752 if (!rpc) {
753 goto cleanup;
754 }
755
756 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
757 usleep(1000);
758 }
759 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200760 WRN(session, "Failed to send request for yang-library data.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200761 goto cleanup;
762 }
763
764 do {
Michal Vasko77367452021-02-16 16:32:18 +0100765 lyd_free_tree(envp);
766 lyd_free_tree(op);
767
768 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200769 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200770 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko303263d2021-10-05 12:18:21 +0200771 WRN(session, "Timeout for receiving reply to a <%s> yang-library data expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200772 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100773 } else if (msg == NC_MSG_ERROR) {
Michal Vasko303263d2021-10-05 12:18:21 +0200774 WRN(session, "Failed to receive a reply to <%s> of yang-library data.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200775 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100776 } else if (!op || !lyd_child(op) || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko303263d2021-10-05 12:18:21 +0200777 WRN(session, "Unexpected reply without data to a yang-library <%s> RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200778 goto cleanup;
779 }
780
Michal Vasko77367452021-02-16 16:32:18 +0100781 data = (struct lyd_node_any *)lyd_child(op);
782 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko303263d2021-10-05 12:18:21 +0200783 WRN(session, "Unexpected data in reply to a yang-library <%s> RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200784 goto cleanup;
785 } else if (!data->value.tree) {
Michal Vasko303263d2021-10-05 12:18:21 +0200786 WRN(session, "No data in reply to a yang-library <%s> RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200787 goto cleanup;
788 }
789
Michal Vasko77367452021-02-16 16:32:18 +0100790 if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
Michal Vasko303263d2021-10-05 12:18:21 +0200791 WRN(session, "No module information in reply to a yang-library <%s> RPC.", rpc_name);
Radek Krejci2d088832018-08-21 13:40:14 +0200792 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200793 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200794
Michal Vasko77367452021-02-16 16:32:18 +0100795 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200796 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200797 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200798 ret = -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200799 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200800 }
801
Michal Vasko77367452021-02-16 16:32:18 +0100802 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200803 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100804 feature_count = 0;
805 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
806 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200807 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200808 /* ignore node from other schemas (augments) */
809 continue;
810 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200811 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200812 /* ignore empty nodes */
813 continue;
814 }
815 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200816 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200817 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200818 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200819 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200820 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200821 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100822 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
823 if (!(*result)[u].features) {
824 ERRMEM;
825 free_schema_info(*result);
826 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200827 ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100828 goto cleanup;
829 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200830 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100831 (*result)[u].features[feature_count + 1] = NULL;
832 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200833 } else if (!strcmp(iter->schema->name, "submodule")) {
834 submodules_count++;
835 }
836 }
837
838 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200839 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
840 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200841 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200842 free_schema_info(*result);
843 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200844 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200845 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200846 } else {
847 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100848 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
849 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200850 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100851 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200852 if (mod != child->schema->module) {
853 continue;
854 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200855 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200856 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200857 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200858 }
859 }
860 }
861 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200862 }
863 }
864 }
865
Radek Krejcifd5b6682017-06-13 15:52:53 +0200866cleanup:
867 nc_rpc_free(rpc);
Michal Vasko77367452021-02-16 16:32:18 +0100868 lyd_free_tree(envp);
869 lyd_free_tree(op);
870 ly_set_free(modules, NULL);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200871
Radek Krejci235d8cb2018-08-17 14:04:32 +0200872 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko05532772021-06-03 12:12:38 +0200873 /* something bad happened, discard the session */
874 ERR(session, "Invalid session, discarding.");
Michal Vaskoc088f982021-10-05 12:23:07 +0200875 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200876 }
877
878 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200879}
880
Michal Vaskoc088f982021-10-05 12:23:07 +0200881/**
882 * @brief Build server schema info from received capabilities.
883 *
884 * @param[in] cpblts Server capabilities.
885 * @param[out] result Server schemas.
886 * @return 0 on success.
887 * @return -1 on error.
888 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200889static int
890build_schema_info_cpblts(char **cpblts, struct schema_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200891{
Michal Vasko77367452021-02-16 16:32:18 +0100892 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200893 char *module_cpblt, *ptr, *ptr2;
894
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200895 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +0200896 (*result) = calloc(u + 1, sizeof **result);
897 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200898 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200899 return -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200900 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200901
902 for (u = v = 0; cpblts[u]; ++u) {
903 module_cpblt = strstr(cpblts[u], "module=");
904 /* this capability requires a module */
905 if (!module_cpblt) {
906 continue;
907 }
908
909 /* get module's name */
910 ptr = (char *)module_cpblt + 7;
911 ptr2 = strchr(ptr, '&');
912 if (!ptr2) {
913 ptr2 = ptr + strlen(ptr);
914 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200915 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200916
917 /* get module's revision */
918 ptr = strstr(module_cpblt, "revision=");
919 if (ptr) {
920 ptr += 9;
921 ptr2 = strchr(ptr, '&');
922 if (!ptr2) {
923 ptr2 = ptr + strlen(ptr);
924 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200925 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200926 }
927
928 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200929 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200930
931 /* get module's features */
932 ptr = strstr(module_cpblt, "features=");
933 if (ptr) {
934 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +0100935 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200936 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
937 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +0100938 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
939 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
940 (*result)[v].features[feature_count + 1] = NULL;
941 ++feature_count;
942
Radek Krejci65ef6d52018-08-16 16:35:02 +0200943 ptr2 = ptr + 1;
944 }
945 }
946 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +0100947 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
948 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
949 (*result)[v].features[feature_count + 1] = NULL;
950 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200951 }
952 ++v;
953 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200954
Michal Vasko303263d2021-10-05 12:18:21 +0200955 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200956}
957
Michal Vaskoc088f982021-10-05 12:23:07 +0200958/**
959 * @brief Fill client context based on server schema info.
960 *
961 * @param[in] session NC session with the context to modify.
962 * @param[in] modules Server schema info.
963 * @param[in] user_clb User callback for retrieving specific schemas.
964 * @param[in] user_data User data for @p user_clb.
965 * @param[in] has_get_schema Whether server supports get-schema RPC.
966 * @return 0 on success.
967 * @return -1 on error.
968 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200969static int
Michal Vasko77367452021-02-16 16:32:18 +0100970nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data,
971 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200972{
Michal Vasko303263d2021-10-05 12:18:21 +0200973 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100974 struct lys_module *mod;
975 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200976
977 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +0200978 /* skip import-only modules */
979 if (!modules[u].implemented) {
980 continue;
981 }
982
Radek Krejci65ef6d52018-08-16 16:35:02 +0200983 /* we can continue even if it fails */
984 nc_ctx_load_module(session, modules[u].name, modules[u].revision, modules, user_clb, user_data, has_get_schema, &mod);
985
986 if (!mod) {
987 if (session->status != NC_STATUS_RUNNING) {
988 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +0200989 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200990 goto cleanup;
991 }
992
993 /* all loading ways failed, the schema will be ignored in the received data */
Michal Vasko05532772021-06-03 12:12:38 +0200994 WRN(session, "Failed to load schema \"%s@%s\".", modules[u].name, modules[u].revision ?
995 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200996 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
997 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100998 /* set the features */
999 lys_set_implemented(mod, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001000 }
Michal Vasko60f66602017-10-17 13:52:18 +02001001 }
1002
Michal Vasko77367452021-02-16 16:32:18 +01001003 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001004 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001005
1006cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001007 return ret;
1008}
1009
Michal Vaskoc088f982021-10-05 12:23:07 +02001010/**
1011 * @brief Fill client context with ietf-netconf schema.
1012 *
1013 * @param[in] session NC session with the context to modify.
1014 * @param[in] modules Server schema info.
1015 * @param[in] user_clb User callback for retrieving specific schemas.
1016 * @param[in] user_data User data for @p user_clb.
1017 * @param[in] has_get_schema Whether server supports get-schema RPC.
1018 * @return 0 on success.
1019 * @return -1 on error.
1020 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001021static int
Michal Vasko77367452021-02-16 16:32:18 +01001022nc_ctx_fill_ietf_netconf(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb,
1023 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001024{
Michal Vasko77367452021-02-16 16:32:18 +01001025 uint32_t u;
1026 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001027
Michal Vasko77367452021-02-16 16:32:18 +01001028 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001029 if (!ietfnc) {
1030 nc_ctx_load_module(session, "ietf-netconf", NULL, modules, user_clb, user_data, has_get_schema, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001031 if (!ietfnc) {
Michal Vasko029c5372021-07-09 10:54:21 +02001032 lys_parse_mem(session->ctx, ietf_netconf_2013_09_29_yang, LYS_IN_YANG, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001033 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001034 }
1035 if (!ietfnc) {
Michal Vasko05532772021-06-03 12:12:38 +02001036 ERR(session, "Loading base NETCONF schema failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001037 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001038 }
1039
1040 /* set supported capabilities from ietf-netconf */
1041 for (u = 0; modules[u].name; ++u) {
1042 if (strcmp(modules[u].name, "ietf-netconf") || !modules[u].implemented) {
1043 continue;
1044 }
1045
Michal Vasko77367452021-02-16 16:32:18 +01001046 lys_set_implemented(ietfnc, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001047 }
1048
1049 return 0;
1050}
1051
Michal Vasko086311b2016-01-08 09:53:11 +01001052int
1053nc_ctx_check_and_fill(struct nc_session *session)
1054{
Michal Vasko303263d2021-10-05 12:18:21 +02001055 int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001056 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001057 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001058 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001059 char *revision;
Radek Krejcib056ff82018-08-20 15:58:39 +02001060 struct schema_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001061
Michal Vasko2e6defd2016-10-07 15:48:15 +02001062 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001063
Radek Krejci65ef6d52018-08-16 16:35:02 +02001064 /* 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 +02001065 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001066
Radek Krejci65ef6d52018-08-16 16:35:02 +02001067 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
1068 * schemas only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001069 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001070
1071 /* our callback is set later with appropriate data */
1072 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1073
1074 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001075 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001076 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001077 get_schema_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +02001078 if (yanglib_support) {
1079 break;
1080 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001081 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001082 yanglib_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +02001083 if (get_schema_support) {
1084 break;
1085 }
Michal Vasko086311b2016-01-08 09:53:11 +01001086 }
1087 }
Michal Vasko31dd4c52020-04-17 10:29:44 +02001088 if (get_schema_support) {
Michal Vasko05532772021-06-03 12:12:38 +02001089 VRB(session, "Capability for <get-schema> support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001090 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001091 VRB(session, "Capability for <get-schema> support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001092 }
1093 if (yanglib_support) {
Michal Vasko05532772021-06-03 12:12:38 +02001094 VRB(session, "Capability for yang-library support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001095 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001096 VRB(session, "Capability for yang-library support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001097 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001098
1099 /* get information about server's schemas from capabilities list until we will have yang-library */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001100 if (build_schema_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
Michal Vasko05532772021-06-03 12:12:38 +02001101 ERR(session, "Unable to get server's schema information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001102 goto cleanup;
1103 }
1104
Michal Vasko086311b2016-01-08 09:53:11 +01001105 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001106 if (get_schema_support && lys_parse_mem(session->ctx, ietf_netconf_monitoring_2010_10_04_yang, LYS_IN_YANG, NULL)) {
Michal Vasko05532772021-06-03 12:12:38 +02001107 WRN(session, "Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001108 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001109 }
1110
1111 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001112 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001113 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001114 }
1115
Radek Krejci65ef6d52018-08-16 16:35:02 +02001116 /* get correct version of ietf-yang-library into context */
1117 if (yanglib_support) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001118 /* use get schema to get server's ietf-yang-library */
1119 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1120 if (!revision) {
Michal Vasko05532772021-06-03 12:12:38 +02001121 WRN(session, "Loading NETCONF ietf-yang-library schema failed, missing revision in NETCONF <hello> message.");
1122 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001123 yanglib_support = 0;
1124 } else {
1125 revision = strndup(&revision[9], 10);
Michal Vasko0d877f92020-04-02 13:52:43 +02001126 if (nc_ctx_load_module(session, "ietf-yang-library", revision, server_modules, old_clb, old_data,
1127 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001128 WRN(session, "Loading NETCONF ietf-yang-library schema failed, unable to use it to learn all "
1129 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001130 yanglib_support = 0;
1131 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001132 if (strcmp(revision, "2019-01-04") >= 0) {
1133 /* we also need ietf-datastores to be implemented */
1134 if (nc_ctx_load_module(session, "ietf-datastores", NULL, server_modules, old_clb, old_data,
1135 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001136 WRN(session, "Loading NETCONF ietf-datastores schema failed, unable to use yang-library "
1137 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001138 yanglib_support = 0;
1139 }
1140 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001141 free(revision);
Michal Vasko77367452021-02-16 16:32:18 +01001142
1143 /* ietf-netconf-nmda is needed to issue get-data */
Michal Vasko303263d2021-10-05 12:18:21 +02001144 if (!nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, server_modules, old_clb, old_data,
Michal Vasko77367452021-02-16 16:32:18 +01001145 get_schema_support, &mod)) {
Michal Vasko303263d2021-10-05 12:18:21 +02001146 VRB(session, "Support for <get-data> from ietf-netcon-nmda found.");
1147 get_data_support = 1;
Michal Vasko77367452021-02-16 16:32:18 +01001148 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001149 }
1150 }
1151
Radek Krejci65ef6d52018-08-16 16:35:02 +02001152 /* prepare structured information about server's schemas */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001153 if (yanglib_support) {
Michal Vasko303263d2021-10-05 12:18:21 +02001154 if (build_schema_info_yl(session, get_data_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001155 goto cleanup;
1156 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001157 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001158 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001159 /* prefer yang-library information, currently we have it from capabilities used for getting correct
1160 * yang-library schema */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001161 free_schema_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001162 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001163 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001164 }
Michal Vaskoef578332016-01-25 13:20:09 +01001165
Radek Krejci65ef6d52018-08-16 16:35:02 +02001166 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1167 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001168 }
1169
Radek Krejcifd5b6682017-06-13 15:52:53 +02001170 /* succsess */
1171 ret = 0;
1172
Michal Vaskoeee99412016-11-21 10:19:43 +01001173 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko05532772021-06-03 12:12:38 +02001174 WRN(session, "Some models failed to be loaded, any data from these models (and any other unknown) will "
1175 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001176 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001177
1178cleanup:
Radek Krejci65ef6d52018-08-16 16:35:02 +02001179 free_schema_info(server_modules);
1180
Radek Krejcifd5b6682017-06-13 15:52:53 +02001181 /* set user callback back */
1182 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001183 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001184
Michal Vaskoef578332016-01-25 13:20:09 +01001185 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001186}
1187
1188API struct nc_session *
1189nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1190{
Michal Vaskod083db62016-01-19 10:31:29 +01001191 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001192
Michal Vasko45e53ae2016-04-07 11:46:03 +02001193 if (fdin < 0) {
1194 ERRARG("fdin");
1195 return NULL;
1196 } else if (fdout < 0) {
1197 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001198 return NULL;
1199 }
1200
1201 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001202 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001203 if (!session) {
1204 ERRMEM;
1205 return NULL;
1206 }
1207 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001208
1209 /* transport specific data */
1210 session->ti_type = NC_TI_FD;
1211 session->ti.fd.in = fdin;
1212 session->ti.fd.out = fdout;
1213
Robin Jarry4e38e292019-10-15 17:14:14 +02001214 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1215 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001216 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001217 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001218
1219 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001220 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001221 goto fail;
1222 }
1223 session->status = NC_STATUS_RUNNING;
1224
Michal Vaskoef578332016-01-25 13:20:09 +01001225 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001226 goto fail;
1227 }
1228
1229 return session;
1230
1231fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001232 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001233 return NULL;
1234}
1235
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001236API struct nc_session *
1237nc_connect_unix(const char *address, struct ly_ctx *ctx)
1238{
1239 struct nc_session *session = NULL;
1240 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001241 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001242 char *username;
1243 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001244 char *buf = NULL;
1245 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001246
1247 if (address == NULL) {
1248 ERRARG("address");
1249 return NULL;
1250 }
1251
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001252 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1253 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001254 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001255 goto fail;
1256 }
1257
1258 memset(&sun, 0, sizeof(sun));
1259 sun.sun_family = AF_UNIX;
1260 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1261
1262 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001263 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001264 goto fail;
1265 }
1266
1267 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001268 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001269 goto fail;
1270 }
1271
1272 /* prepare session structure */
1273 session = nc_new_session(NC_CLIENT, 0);
1274 if (!session) {
1275 ERRMEM;
1276 goto fail;
1277 }
1278 session->status = NC_STATUS_STARTING;
1279
1280 /* transport specific data */
1281 session->ti_type = NC_TI_UNIX;
1282 session->ti.unixsock.sock = sock;
1283 sock = -1; /* do not close sock in fail label anymore */
1284
Robin Jarry4e38e292019-10-15 17:14:14 +02001285 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1286 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001287 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001288 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001289
Michal Vasko93224072021-11-09 12:14:28 +01001290 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001291
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001292 pw = nc_getpwuid(geteuid(), &pw_buf, &buf, &buf_size);
1293 if (!pw) {
1294 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1295 goto fail;
1296 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001297 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001298 free(buf);
1299 if (!username) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001300 ERRMEM;
1301 goto fail;
1302 }
Michal Vasko93224072021-11-09 12:14:28 +01001303 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001304
1305 /* NETCONF handshake */
1306 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1307 goto fail;
1308 }
1309 session->status = NC_STATUS_RUNNING;
1310
1311 if (nc_ctx_check_and_fill(session) == -1) {
1312 goto fail;
1313 }
1314
1315 return session;
1316
1317fail:
1318 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001319 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001320 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001321 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001322 return NULL;
1323}
1324
Frank Rimpler9f838b02018-07-25 06:44:03 +00001325/*
1326 Helper for a non-blocking connect (which is required because of the locking
1327 concept for e.g. call home settings). For more details see nc_sock_connect().
1328 */
1329static int
Michal Vasko5e8d0192019-06-24 19:19:49 +02001330_non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001331{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001332 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001333 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001334 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001335 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001336 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001337 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001338 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001339 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001340
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001341 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001342 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001343 sock = *sock_pending;
1344 } else {
1345 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001346 if (res->ai_family == AF_INET6) {
1347 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001348 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001349 } else {
1350 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001351 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001352 }
1353 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
Michal Vasko05532772021-06-03 12:12:38 +02001354 WRN(NULL, "inet_ntop() failed (%s).", strerror(errno));
Michal Vasko5e8d0192019-06-24 19:19:49 +02001355 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001356 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 +02001357 }
1358
1359 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001360 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1361 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001362 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001363 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001364 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001365 /* make the socket non-blocking */
1366 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001367 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001368 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001369 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001370 /* non-blocking connect! */
1371 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1372 if (errno != EINPROGRESS) {
1373 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001374 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001375 goto cleanup;
1376 }
1377 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001378 }
1379 ts.tv_sec = timeout;
1380 ts.tv_usec = 0;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001381
Frank Rimpler9f838b02018-07-25 06:44:03 +00001382 FD_ZERO(&wset);
1383 FD_SET(sock, &wset);
1384
1385 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001386 ERR(NULL, "select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001387 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001388 }
1389
Michal Vasko5e8d0192019-06-24 19:19:49 +02001390 if (ret == 0) {
1391 /* there was a timeout */
Michal Vasko05532772021-06-03 12:12:38 +02001392 VRB(NULL, "Timed out after %ds (%s).", timeout, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001393 if (sock_pending) {
1394 /* no sock-close, we'll try it again */
1395 *sock_pending = sock;
1396 } else {
1397 close(sock);
1398 }
1399 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001400 }
Radek Krejci782041a2018-08-20 10:09:45 +02001401
1402 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001403 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001404 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001405 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001406 goto cleanup;
1407 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001408 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001409 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001410 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001411 errno = error;
1412 goto cleanup;
1413 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001414
1415 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001416 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001417 goto cleanup;
1418 }
1419
Michal Vasko086311b2016-01-08 09:53:11 +01001420 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001421
Frank Rimpler9f838b02018-07-25 06:44:03 +00001422cleanup:
1423 if (sock_pending) {
1424 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001425 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001426 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001427 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001428}
1429
Frank Rimpler9f838b02018-07-25 06:44:03 +00001430/* A given timeout value limits the time how long the function blocks. If it has to block
1431 only for some seconds, a socket connection might not yet have been fully established.
1432 Therefore the active (pending) socket will be stored in *sock_pending, but the return
1433 value will be -1. In such a case a subsequent invokation is required, by providing the
1434 stored sock_pending, again.
1435 In general, if this function returns -1, when a timeout has been given, this function
1436 has to be invoked, until it returns a valid socket.
1437 */
1438int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001439nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepalives *ka, int *sock_pending, char **ip_host)
Frank Rimpler9f838b02018-07-25 06:44:03 +00001440{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001441 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001442 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001443 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001444 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001445 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001446
Michal Vasko05532772021-06-03 12:12:38 +02001447 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001448
1449 /* no pending socket */
1450 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001451 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001452 snprintf(port_s, 6, "%u", port);
1453 memset(&hints, 0, sizeof hints);
1454 hints.ai_family = AF_UNSPEC;
1455 hints.ai_socktype = SOCK_STREAM;
1456 hints.ai_protocol = IPPROTO_TCP;
1457 i = getaddrinfo(host, port_s, &hints, &res_list);
1458 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001459 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001460 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001461 }
1462
1463 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001464 sock = _non_blocking_connect(timeout, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001465 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001466 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001467 /* try the next resource */
1468 continue;
1469 } else {
1470 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001471 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001472 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001473 }
Michal Vasko05532772021-06-03 12:12:38 +02001474 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 +01001475
1476 opt = 1;
1477 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001478 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001479 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001480 }
1481
Michal Vasko66032bc2019-01-22 15:03:12 +01001482 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1483 buf = malloc(INET6_ADDRSTRLEN);
1484 if (!buf) {
1485 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001486 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001487 }
1488 if (res->ai_family == AF_INET) {
1489 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1490 } else {
1491 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1492 }
1493 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +02001494 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
Michal Vasko66032bc2019-01-22 15:03:12 +01001495 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001496 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001497 }
1498
1499 *ip_host = buf;
1500 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001501 break;
1502 }
1503 freeaddrinfo(res_list);
1504
1505 } else {
1506 /* try to get a connection with the pending socket */
1507 assert(sock_pending);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001508 sock = _non_blocking_connect(timeout, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001509 }
1510
1511 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001512
1513error:
Michal Vasko0be85692021-03-02 08:04:57 +01001514 if (res_list) {
1515 freeaddrinfo(res_list);
1516 }
Michal Vaskocb846632019-12-13 15:12:45 +01001517 if (sock != -1) {
1518 close(sock);
1519 }
1520 if (sock_pending) {
1521 *sock_pending = -1;
1522 }
1523 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001524}
1525
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001526#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001527
Michal Vasko3031aae2016-01-27 16:07:18 +01001528int
1529nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1530{
1531 int sock;
1532
Michal Vasko45e53ae2016-04-07 11:46:03 +02001533 if (!address) {
1534 ERRARG("address");
1535 return -1;
1536 } else if (!port) {
1537 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001538 return -1;
1539 }
1540
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001541 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001542 if (sock == -1) {
1543 return -1;
1544 }
1545
1546 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001547 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1548 if (!client_opts.ch_binds) {
1549 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001550 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001551 return -1;
1552 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001553
Michal Vasko2e6defd2016-10-07 15:48:15 +02001554 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1555 if (!client_opts.ch_bind_ti) {
1556 ERRMEM;
1557 close(sock);
1558 return -1;
1559 }
1560 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1561
Michal Vasko3031aae2016-01-27 16:07:18 +01001562 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001563 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1564 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001565 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001566 return -1;
1567 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001568 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1569 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001570 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001571
1572 return 0;
1573}
1574
1575int
1576nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1577{
1578 uint32_t i;
1579 int ret = -1;
1580
1581 if (!address && !port && !ti) {
1582 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1583 close(client_opts.ch_binds[i].sock);
1584 free((char *)client_opts.ch_binds[i].address);
1585
1586 ret = 0;
1587 }
1588 free(client_opts.ch_binds);
1589 client_opts.ch_binds = NULL;
1590 client_opts.ch_bind_count = 0;
1591 } else {
1592 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001593 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1594 (!port || (client_opts.ch_binds[i].port == port)) &&
1595 (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001596 close(client_opts.ch_binds[i].sock);
1597 free((char *)client_opts.ch_binds[i].address);
1598
1599 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001600 if (!client_opts.ch_bind_count) {
1601 free(client_opts.ch_binds);
1602 client_opts.ch_binds = NULL;
1603 } else if (i < client_opts.ch_bind_count) {
1604 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1605 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1606 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001607
1608 ret = 0;
1609 }
1610 }
1611 }
1612
1613 return ret;
1614}
1615
1616API int
1617nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1618{
1619 int sock;
1620 char *host = NULL;
1621 uint16_t port, idx;
1622
Michal Vasko45e53ae2016-04-07 11:46:03 +02001623 if (!client_opts.ch_binds) {
1624 ERRINIT;
1625 return -1;
1626 } else if (!session) {
1627 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001628 return -1;
1629 }
1630
1631 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1632
Michal Vasko50456e82016-02-02 12:16:08 +01001633 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001634 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001635 return sock;
1636 }
1637
Radek Krejci53691be2016-02-22 13:58:37 +01001638#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001639 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001640 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001641 } else
1642#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001643#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001644 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001645 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001646 } else
1647#endif
1648 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001649 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001650 *session = NULL;
1651 }
1652
1653 free(host);
1654
1655 if (!(*session)) {
1656 return -1;
1657 }
1658
1659 return 1;
1660}
1661
Radek Krejci53691be2016-02-22 13:58:37 +01001662#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001663
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001664API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001665nc_session_get_cpblts(const struct nc_session *session)
1666{
1667 if (!session) {
1668 ERRARG("session");
1669 return NULL;
1670 }
1671
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001672 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001673}
1674
1675API const char *
1676nc_session_cpblt(const struct nc_session *session, const char *capab)
1677{
1678 int i, len;
1679
1680 if (!session) {
1681 ERRARG("session");
1682 return NULL;
1683 } else if (!capab) {
1684 ERRARG("capab");
1685 return NULL;
1686 }
1687
1688 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001689 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1690 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1691 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001692 }
1693 }
1694
1695 return NULL;
1696}
1697
Michal Vasko9cd26a82016-05-31 08:58:48 +02001698API int
1699nc_session_ntf_thread_running(const struct nc_session *session)
1700{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001701 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001702 ERRARG("session");
1703 return 0;
1704 }
1705
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001706 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001707}
1708
Michal Vaskob7558c52016-02-26 15:04:19 +01001709API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001710nc_client_init(void)
1711{
1712 nc_init();
1713}
1714
1715API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001716nc_client_destroy(void)
1717{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001718 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001719#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001720 nc_client_ch_del_bind(NULL, 0, 0);
1721#endif
1722#ifdef NC_ENABLED_SSH
1723 nc_client_ssh_destroy_opts();
1724#endif
1725#ifdef NC_ENABLED_TLS
1726 nc_client_tls_destroy_opts();
1727#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001728 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001729}
1730
Michal Vasko77367452021-02-16 16:32:18 +01001731static NC_MSG_TYPE
1732recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001733{
Michal Vasko77367452021-02-16 16:32:18 +01001734 char *ptr;
1735 struct lyd_attr *attr;
1736 uint64_t cur_msgid;
1737
1738 assert(envp && !envp->schema);
1739
1740 /* find the message-id attribute */
1741 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1742 if (!strcmp(attr->name.name, "message-id")) {
1743 break;
1744 }
1745 }
1746
1747 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001748 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001749 return NC_MSG_REPLY_ERR_MSGID;
1750 }
1751
1752 cur_msgid = strtoul(attr->value, &ptr, 10);
1753 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001754 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1755 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001756 return NC_MSG_REPLY_ERR_MSGID;
1757 }
1758
1759 return NC_MSG_REPLY;
1760}
1761
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001762/**
1763 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1764 *
1765 * @param[in] session NETCONF session used to send error messages.
1766 * @param[in] msg Message to check for type.
1767 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1768 * @return NC_MSG_NOTIF If format roughly matches a notification;
1769 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1770 */
1771static NC_MSG_TYPE
1772get_msg_type(struct nc_session *session, struct ly_in *msg)
1773{
1774 const char *str, *end;
1775
1776 str = ly_in_memory(msg, NULL);
1777
1778 while (*str) {
1779 /* Skip whitespaces */
1780 while (isspace(*str)) {
1781 str++;
1782 }
1783
1784 if (*str == '<') {
1785 str++;
1786 if (!strncmp(str, "!--", 3)) {
1787 /* Skip comments */
1788 end = "-->";
1789 str = strstr(str, end);
1790 } else if (!strncmp(str, "?xml", 4)) {
1791 /* Skip xml declaration */
1792 end = "?>";
1793 str = strstr(str, end);
1794 } else if (!strncmp(str, "rpc-reply", 9)) {
1795 return NC_MSG_REPLY;
1796 } else if (!strncmp(str, "notification", 12)) {
1797 return NC_MSG_NOTIF;
1798 } else {
1799 ERR(session, "Unknown xml element '%.10s'.", str);
1800 return NC_MSG_ERROR;
1801 }
1802 if (!str) {
1803 /* No matching ending tag found */
1804 ERR(session, "No matching ending tag '%s' found in xml message.", end);
1805 return NC_MSG_ERROR;
1806 }
1807 str += strlen(end);
1808 } else {
1809 /* Not a valid xml */
1810 ERR(session, "Unexpected character '%c' in xml message.", *str);
1811 return NC_MSG_ERROR;
1812 }
1813 }
1814
1815 /* Unexpected end of message */
1816 ERR(session, "Unexpected end of xml message.");
1817 return NC_MSG_ERROR;
1818}
1819
1820/**
1821 * @brief Function to receive either replies or notifications.
1822 *
1823 * @param[in] session NETCONF session from which this function receives messages.
1824 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
1825 * @param[in] expected Type of the message the caller desired.
1826 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
1827 * @return NC_MSG_REPLY If a rpc-reply was received;
1828 * @return NC_MSG_NOTIF If a notification was received;
1829 * @return NC_MSG_ERROR If any error occured;
1830 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
1831 */
1832static NC_MSG_TYPE
1833recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
1834{
1835 struct nc_msg_cont **cont_ptr;
1836 struct ly_in *msg = NULL;
1837 struct nc_msg_cont *cont, *prev;
1838 NC_MSG_TYPE ret = NC_MSG_ERROR;
1839 int r;
1840
1841 *message = NULL;
1842
1843 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001844 r = nc_session_client_msgs_lock(session, &timeout, __func__);
1845 if (!r) {
1846 ret = NC_MSG_WOULDBLOCK;
1847 goto cleanup;
1848 } else if (r == -1) {
1849 ret = NC_MSG_ERROR;
1850 goto cleanup;
1851 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001852
1853 /* Find the expected message in the buffer */
1854 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001855 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001856 prev = cont;
1857 }
1858
1859 if (cont) {
1860 /* Remove found message from buffer */
1861 if (prev) {
1862 prev->next = cont->next;
1863 } else {
1864 session->opts.client.msgs = cont->next;
1865 }
1866
1867 /* Use the buffer message */
1868 ret = cont->type;
1869 msg = cont->msg;
1870 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02001871 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001872 }
1873
1874 /* Read a message from the wire */
1875 r = nc_read_msg_poll_io(session, timeout, &msg);
1876 if (!r) {
1877 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02001878 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001879 } else if (r == -1) {
1880 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001881 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001882 }
1883
1884 /* Basic check to determine message type */
1885 ret = get_msg_type(session, msg);
1886 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02001887 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001888 }
1889
1890 /* If received a message of different type store it in the buffer */
1891 if (ret != expected) {
1892 cont_ptr = &session->opts.client.msgs;
1893 while (*cont_ptr) {
1894 cont_ptr = &((*cont_ptr)->next);
1895 }
1896 *cont_ptr = malloc(sizeof **cont_ptr);
1897 if (!*cont_ptr) {
1898 ERRMEM;
1899 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001900 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001901 }
1902 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001903 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001904 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001905 (*cont_ptr)->next = NULL;
1906 }
1907
Michal Vasko01130bd2021-08-26 11:47:38 +02001908cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001909 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001910 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001911
Michal Vasko01130bd2021-08-26 11:47:38 +02001912cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001913 if (ret == expected) {
1914 *message = msg;
1915 } else {
1916 ly_in_free(msg, 1);
1917 }
1918 return ret;
1919}
1920
Michal Vasko77367452021-02-16 16:32:18 +01001921static NC_MSG_TYPE
1922recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
1923{
Michal Vasko77367452021-02-16 16:32:18 +01001924 LY_ERR lyrc;
1925 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001926 NC_MSG_TYPE ret = NC_MSG_ERROR;
1927
1928 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
1929
1930 *envp = NULL;
1931
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001932 /* Receive messages until a rpc-reply is found or a timeout or error reached */
1933 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
1934 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01001935 goto cleanup;
1936 }
1937
1938 /* parse */
1939 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
1940 if (!lyrc) {
1941 ret = recv_reply_check_msgid(session, *envp, msgid);
1942 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001943 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001944 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001945 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001946 goto cleanup;
1947 }
1948
Michal Vasko77367452021-02-16 16:32:18 +01001949cleanup:
1950 ly_in_free(msg, 1);
1951 return ret;
1952}
1953
1954static int
1955recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
1956{
Michal Vasko143aa142021-10-01 15:31:48 +02001957 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01001958 struct nc_rpc_act_generic *rpc_gen;
1959 struct ly_in *in;
1960 struct lyd_node *tree, *op2;
1961 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01001962 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001963
1964 switch (rpc->type) {
1965 case NC_RPC_ACT_GENERIC:
1966 rpc_gen = (struct nc_rpc_act_generic *)rpc;
1967 if (rpc_gen->has_data) {
1968 tree = rpc_gen->content.data;
1969
1970 /* find the operation node */
1971 lyrc = LY_EINVAL;
1972 LYD_TREE_DFS_BEGIN(tree, op2) {
1973 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
1974 lyrc = lyd_dup_single(op2, NULL, 0, op);
1975 break;
1976 }
1977 LYD_TREE_DFS_END(tree, op2);
1978 }
1979 } else {
1980 ly_in_new_memory(rpc_gen->content.xml_str, &in);
1981 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
1982 ly_in_free(in, 0);
1983 if (lyrc) {
1984 return -1;
1985 }
1986
1987 /* we want just the operation node */
1988 lyrc = lyd_dup_single(op2, NULL, 0, op);
1989
1990 lyd_free_tree(tree);
1991 }
1992 break;
1993 case NC_RPC_GETCONFIG:
1994 module_name = "ietf-netconf";
1995 rpc_name = "get-config";
1996 break;
1997 case NC_RPC_EDIT:
1998 module_name = "ietf-netconf";
1999 rpc_name = "edit-config";
2000 break;
2001 case NC_RPC_COPY:
2002 module_name = "ietf-netconf";
2003 rpc_name = "copy-config";
2004 break;
2005 case NC_RPC_DELETE:
2006 module_name = "ietf-netconf";
2007 rpc_name = "delete-config";
2008 break;
2009 case NC_RPC_LOCK:
2010 module_name = "ietf-netconf";
2011 rpc_name = "lock";
2012 break;
2013 case NC_RPC_UNLOCK:
2014 module_name = "ietf-netconf";
2015 rpc_name = "unlock";
2016 break;
2017 case NC_RPC_GET:
2018 module_name = "ietf-netconf";
2019 rpc_name = "get";
2020 break;
2021 case NC_RPC_KILL:
2022 module_name = "ietf-netconf";
2023 rpc_name = "kill-session";
2024 break;
2025 case NC_RPC_COMMIT:
2026 module_name = "ietf-netconf";
2027 rpc_name = "commit";
2028 break;
2029 case NC_RPC_DISCARD:
2030 module_name = "ietf-netconf";
2031 rpc_name = "discard-changes";
2032 break;
2033 case NC_RPC_CANCEL:
2034 module_name = "ietf-netconf";
2035 rpc_name = "cancel-commit";
2036 break;
2037 case NC_RPC_VALIDATE:
2038 module_name = "ietf-netconf";
2039 rpc_name = "validate";
2040 break;
2041 case NC_RPC_GETSCHEMA:
2042 module_name = "ietf-netconf-monitoring";
2043 rpc_name = "get-schema";
2044 break;
2045 case NC_RPC_SUBSCRIBE:
2046 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002047 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002048 break;
2049 case NC_RPC_GETDATA:
2050 module_name = "ietf-netconf-nmda";
2051 rpc_name = "get-data";
2052 break;
2053 case NC_RPC_EDITDATA:
2054 module_name = "ietf-netconf-nmda";
2055 rpc_name = "edit-data";
2056 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002057 case NC_RPC_ESTABLISHSUB:
2058 module_name = "ietf-subscribed-notifications";
2059 rpc_name = "establish-subscription";
2060 break;
2061 case NC_RPC_MODIFYSUB:
2062 module_name = "ietf-subscribed-notifications";
2063 rpc_name = "modify-subscription";
2064 break;
2065 case NC_RPC_DELETESUB:
2066 module_name = "ietf-subscribed-notifications";
2067 rpc_name = "delete-subscription";
2068 break;
2069 case NC_RPC_KILLSUB:
2070 module_name = "ietf-subscribed-notifications";
2071 rpc_name = "kill-subscription";
2072 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002073 case NC_RPC_ESTABLISHPUSH:
2074 module_name = "ietf-subscribed-notifications";
2075 rpc_name = "establish-subscription";
2076 module_check = "ietf-yang-push";
2077 break;
2078 case NC_RPC_MODIFYPUSH:
2079 module_name = "ietf-subscribed-notifications";
2080 rpc_name = "modify-subscription";
2081 module_check = "ietf-yang-push";
2082 break;
2083 case NC_RPC_RESYNCSUB:
2084 module_name = "ietf-yang-push";
2085 rpc_name = "resync-subscription";
2086 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002087 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002088 lyrc = LY_EINT;
2089 break;
2090 }
2091
2092 if (module_name && rpc_name) {
2093 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2094 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002095 ERR(session, "Missing \"%s\" schema in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002096 return -1;
2097 }
2098
2099 /* create the operation node */
2100 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2101 }
Michal Vasko305faca2021-03-25 09:16:02 +01002102 if (module_check) {
2103 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko05532772021-06-03 12:12:38 +02002104 ERR(session, "Missing \"%s\" schema in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002105 return -1;
2106 }
2107 }
Michal Vasko77367452021-02-16 16:32:18 +01002108
2109 if (lyrc) {
2110 return -1;
2111 }
2112 return 0;
2113}
2114
2115API NC_MSG_TYPE
2116nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2117 struct lyd_node **op)
2118{
2119 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002120
Michal Vasko45e53ae2016-04-07 11:46:03 +02002121 if (!session) {
2122 ERRARG("session");
2123 return NC_MSG_ERROR;
2124 } else if (!rpc) {
2125 ERRARG("rpc");
2126 return NC_MSG_ERROR;
Michal Vasko5a2c7162020-01-30 11:24:17 +01002127 } else if (!msgid) {
2128 ERRARG("msgid");
2129 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002130 } else if (!envp) {
2131 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02002132 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002133 } else if (!op) {
2134 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002135 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002136 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002137 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002138 return NC_MSG_ERROR;
2139 }
Michal Vasko77367452021-02-16 16:32:18 +01002140
2141 /* get a duplicate of the RPC node to append reply to */
2142 if (recv_reply_dup_rpc(session, rpc, op)) {
2143 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002144 }
Michal Vasko086311b2016-01-08 09:53:11 +01002145
Michal Vasko77367452021-02-16 16:32:18 +01002146 /* receive a reply */
2147 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002148
Michal Vasko77367452021-02-16 16:32:18 +01002149 /* do not return the RPC copy on error or if the reply includes no data */
2150 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2151 lyd_free_tree(*op);
2152 *op = NULL;
2153 }
2154 return ret;
2155}
2156
2157static NC_MSG_TYPE
2158recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2159{
Michal Vasko77367452021-02-16 16:32:18 +01002160 LY_ERR lyrc;
2161 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002162 NC_MSG_TYPE ret = NC_MSG_ERROR;
2163
2164 *op = NULL;
2165 *envp = NULL;
2166
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002167 /* Receive messages until a notification is found or a timeout or error reached */
2168 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2169 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002170 goto cleanup;
2171 }
2172
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002173 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002174 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2175 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002176 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002177 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002178 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002179 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002180 goto cleanup;
2181 }
2182
Michal Vasko77367452021-02-16 16:32:18 +01002183cleanup:
2184 ly_in_free(msg, 1);
2185 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002186}
2187
2188API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002189nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002190{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002191 if (!session) {
2192 ERRARG("session");
2193 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002194 } else if (!envp) {
2195 ERRARG("envp");
2196 return NC_MSG_ERROR;
2197 } else if (!op) {
2198 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002199 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002200 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002201 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002202 return NC_MSG_ERROR;
2203 }
2204
Michal Vasko77367452021-02-16 16:32:18 +01002205 /* receive a notification */
2206 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002207}
2208
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002209static void *
2210nc_recv_notif_thread(void *arg)
2211{
2212 struct nc_ntf_thread_arg *ntarg;
2213 struct nc_session *session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002214
Michal Vasko77367452021-02-16 16:32:18 +01002215 void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
2216 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002217 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002218
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002219 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002220 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002221
2222 ntarg = (struct nc_ntf_thread_arg *)arg;
2223 session = ntarg->session;
2224 notif_clb = ntarg->notif_clb;
2225 free(ntarg);
2226
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002227 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) == 1) {
Michal Vasko77367452021-02-16 16:32:18 +01002228 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002229 if (msgtype == NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002230 notif_clb(session, envp, op);
2231 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2232 lyd_free_tree(envp);
2233 lyd_free_tree(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002234 break;
2235 }
Michal Vasko77367452021-02-16 16:32:18 +01002236 lyd_free_tree(envp);
2237 lyd_free_tree(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002238 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002239 /* quit this thread once the session is broken */
2240 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002241 }
2242
2243 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2244 }
2245
Michal Vasko05532772021-06-03 12:12:38 +02002246 VRB(session, "Notification thread exit.");
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002247 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002248 return NULL;
2249}
2250
2251API int
Michal Vasko77367452021-02-16 16:32:18 +01002252nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session,
2253 const struct lyd_node *envp, const struct lyd_node *op))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002254{
2255 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002256 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002257 int ret;
2258
Michal Vasko45e53ae2016-04-07 11:46:03 +02002259 if (!session) {
2260 ERRARG("session");
2261 return -1;
2262 } else if (!notif_clb) {
2263 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002264 return -1;
2265 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002266 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002267 return -1;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002268 } else if (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread)) {
Michal Vasko05532772021-06-03 12:12:38 +02002269 ERR(session, "Separate notification thread is already running.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002270 return -1;
2271 }
2272
2273 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002274 if (!ntarg) {
2275 ERRMEM;
2276 return -1;
2277 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002278 ntarg->session = session;
2279 ntarg->notif_clb = notif_clb;
2280
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002281 /* just so that nc_recv_notif_thread() does not immediately exit */
2282 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002283
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002284 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002285 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002286 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002287 free(ntarg);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002288 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002289 return -1;
2290 }
2291
2292 return 0;
2293}
2294
Michal Vasko77367452021-02-16 16:32:18 +01002295static const char *
2296nc_wd2str(NC_WD_MODE wd)
2297{
2298 switch (wd) {
2299 case NC_WD_ALL:
2300 return "report-all";
2301 case NC_WD_ALL_TAG:
2302 return "report-all-tagged";
2303 case NC_WD_TRIM:
2304 return "trim";
2305 case NC_WD_EXPLICIT:
2306 return "explicit";
2307 default:
2308 break;
2309 }
2310
2311 return NULL;
2312}
2313
Michal Vasko086311b2016-01-08 09:53:11 +01002314API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002315nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002316{
2317 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002318 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002319 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002320 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002321 struct nc_rpc_getconfig *rpc_gc;
2322 struct nc_rpc_edit *rpc_e;
2323 struct nc_rpc_copy *rpc_cp;
2324 struct nc_rpc_delete *rpc_del;
2325 struct nc_rpc_lock *rpc_lock;
2326 struct nc_rpc_get *rpc_g;
2327 struct nc_rpc_kill *rpc_k;
2328 struct nc_rpc_commit *rpc_com;
2329 struct nc_rpc_cancel *rpc_can;
2330 struct nc_rpc_validate *rpc_val;
2331 struct nc_rpc_getschema *rpc_gs;
2332 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002333 struct nc_rpc_getdata *rpc_getd;
2334 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002335 struct nc_rpc_establishsub *rpc_estsub;
2336 struct nc_rpc_modifysub *rpc_modsub;
2337 struct nc_rpc_deletesub *rpc_delsub;
2338 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002339 struct nc_rpc_establishpush *rpc_estpush;
2340 struct nc_rpc_modifypush *rpc_modpush;
2341 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002342 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002343 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002344 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002345 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002346 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002347 uint64_t cur_msgid;
2348
Michal Vasko45e53ae2016-04-07 11:46:03 +02002349 if (!session) {
2350 ERRARG("session");
2351 return NC_MSG_ERROR;
2352 } else if (!rpc) {
2353 ERRARG("rpc");
2354 return NC_MSG_ERROR;
2355 } else if (!msgid) {
2356 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002357 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002358 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002359 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002360 return NC_MSG_ERROR;
2361 }
2362
Michal Vaskoc1171a42019-11-05 12:06:46 +01002363 switch (rpc->type) {
2364 case NC_RPC_ACT_GENERIC:
2365 /* checked when parsing */
2366 break;
2367 case NC_RPC_GETCONFIG:
2368 case NC_RPC_EDIT:
2369 case NC_RPC_COPY:
2370 case NC_RPC_DELETE:
2371 case NC_RPC_LOCK:
2372 case NC_RPC_UNLOCK:
2373 case NC_RPC_GET:
2374 case NC_RPC_KILL:
2375 case NC_RPC_COMMIT:
2376 case NC_RPC_DISCARD:
2377 case NC_RPC_CANCEL:
2378 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002379 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002380 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002381 ERR(session, "Missing \"ietf-netconf\" schema in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002382 return NC_MSG_ERROR;
2383 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002384 break;
2385 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002386 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002387 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002388 ERR(session, "Missing \"ietf-netconf-monitoring\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002389 return NC_MSG_ERROR;
2390 }
2391 break;
2392 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002393 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002394 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002395 ERR(session, "Missing \"notifications\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002396 return NC_MSG_ERROR;
2397 }
2398 break;
2399 case NC_RPC_GETDATA:
2400 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002401 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002402 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002403 ERR(session, "Missing \"ietf-netconf-nmda\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002404 return NC_MSG_ERROR;
2405 }
2406 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002407 case NC_RPC_ESTABLISHSUB:
2408 case NC_RPC_MODIFYSUB:
2409 case NC_RPC_DELETESUB:
2410 case NC_RPC_KILLSUB:
2411 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2412 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002413 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002414 return NC_MSG_ERROR;
2415 }
2416 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002417 case NC_RPC_ESTABLISHPUSH:
2418 case NC_RPC_MODIFYPUSH:
2419 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2420 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002421 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002422 return NC_MSG_ERROR;
2423 }
2424 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2425 if (!mod2) {
Michal Vasko05532772021-06-03 12:12:38 +02002426 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002427 return NC_MSG_ERROR;
2428 }
2429 break;
2430 case NC_RPC_RESYNCSUB:
2431 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2432 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002433 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002434 return NC_MSG_ERROR;
2435 }
2436 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002437 case NC_RPC_UNKNOWN:
2438 ERRINT;
2439 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002440 }
2441
Michal Vaskoab9deb62021-05-27 11:37:00 +02002442#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2443
Michal Vasko086311b2016-01-08 09:53:11 +01002444 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002445 case NC_RPC_ACT_GENERIC:
2446 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002447
2448 if (rpc_gen->has_data) {
2449 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002450 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002451 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002452 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2453 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2454 ly_in_free(in, 0);
2455 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002456 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002457 }
Michal Vasko086311b2016-01-08 09:53:11 +01002458 }
2459 break;
2460
2461 case NC_RPC_GETCONFIG:
2462 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2463
Michal Vaskoab9deb62021-05-27 11:37:00 +02002464 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2465 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2466 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002467 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002468 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002469 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2470 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002471 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002472 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2473 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2474 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002475 }
2476 }
2477
2478 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002479 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002480 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002481 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002482 lyrc = LY_ENOTFOUND;
2483 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002484 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002485 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 +01002486 }
2487 break;
2488
2489 case NC_RPC_EDIT:
2490 rpc_e = (struct nc_rpc_edit *)rpc;
2491
Michal Vaskoab9deb62021-05-27 11:37:00 +02002492 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2493 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2494 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002495
2496 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002497 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 +01002498 }
Michal Vasko086311b2016-01-08 09:53:11 +01002499 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002500 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 +01002501 }
Michal Vasko086311b2016-01-08 09:53:11 +01002502 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002503 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 +01002504 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002505 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002506 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 +01002507 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002508 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002509 }
2510 break;
2511
2512 case NC_RPC_COPY:
2513 rpc_cp = (struct nc_rpc_copy *)rpc;
2514
Michal Vaskoab9deb62021-05-27 11:37:00 +02002515 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2516 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002517 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002518 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002519 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002520 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002521 }
2522
Michal Vaskoab9deb62021-05-27 11:37:00 +02002523 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002524 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002525 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002526 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 +01002527 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002528 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002529 }
2530 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002531 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002532 }
2533
2534 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002535 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002536 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002537 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002538 lyrc = LY_ENOTFOUND;
2539 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002540 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002541 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 +01002542 }
2543 break;
2544
2545 case NC_RPC_DELETE:
2546 rpc_del = (struct nc_rpc_delete *)rpc;
2547
Michal Vaskoab9deb62021-05-27 11:37:00 +02002548 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2549 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002550 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002551 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002552 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002553 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002554 }
2555 break;
2556
2557 case NC_RPC_LOCK:
2558 rpc_lock = (struct nc_rpc_lock *)rpc;
2559
Michal Vaskoab9deb62021-05-27 11:37:00 +02002560 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2561 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2562 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002563 break;
2564
2565 case NC_RPC_UNLOCK:
2566 rpc_lock = (struct nc_rpc_lock *)rpc;
2567
Michal Vaskoab9deb62021-05-27 11:37:00 +02002568 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2569 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2570 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002571 break;
2572
2573 case NC_RPC_GET:
2574 rpc_g = (struct nc_rpc_get *)rpc;
2575
Michal Vaskoab9deb62021-05-27 11:37:00 +02002576 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002577 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002578 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002579 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2580 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002581 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002582 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2583 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2584 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002585 }
2586 }
2587
2588 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002589 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002590 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002591 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002592 lyrc = LY_ENOTFOUND;
2593 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002594 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002595 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 +01002596 }
2597 break;
2598
2599 case NC_RPC_KILL:
2600 rpc_k = (struct nc_rpc_kill *)rpc;
2601
Michal Vaskoab9deb62021-05-27 11:37:00 +02002602 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002603 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002604 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002605 break;
2606
2607 case NC_RPC_COMMIT:
2608 rpc_com = (struct nc_rpc_commit *)rpc;
2609
Michal Vaskoab9deb62021-05-27 11:37:00 +02002610 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002611 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002612 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002613 }
2614
2615 if (rpc_com->confirm_timeout) {
2616 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002617 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002618 }
Michal Vasko086311b2016-01-08 09:53:11 +01002619 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002620 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002621 }
Michal Vasko086311b2016-01-08 09:53:11 +01002622 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002623 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002624 }
2625 break;
2626
2627 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002628 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002629 break;
2630
2631 case NC_RPC_CANCEL:
2632 rpc_can = (struct nc_rpc_cancel *)rpc;
2633
Michal Vaskoab9deb62021-05-27 11:37:00 +02002634 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002635 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002636 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002637 }
2638 break;
2639
2640 case NC_RPC_VALIDATE:
2641 rpc_val = (struct nc_rpc_validate *)rpc;
2642
Michal Vaskoab9deb62021-05-27 11:37:00 +02002643 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2644 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002645 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002646 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002647 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 +01002648 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002649 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002650 }
2651 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002652 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002653 }
2654 break;
2655
2656 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002657 rpc_gs = (struct nc_rpc_getschema *)rpc;
2658
Michal Vaskoab9deb62021-05-27 11:37:00 +02002659 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2660 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002661 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002662 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002663 }
2664 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002665 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002666 }
2667 break;
2668
2669 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002670 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2671
Michal Vaskoab9deb62021-05-27 11:37:00 +02002672 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002673 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002674 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002675 }
2676
2677 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002678 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002679 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2680 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002681 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002682 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2683 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2684 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002685 }
2686 }
Michal Vasko086311b2016-01-08 09:53:11 +01002687 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002688 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002689 }
Michal Vasko086311b2016-01-08 09:53:11 +01002690 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002691 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002692 }
2693 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002694
2695 case NC_RPC_GETDATA:
2696 rpc_getd = (struct nc_rpc_getdata *)rpc;
2697
Michal Vaskoab9deb62021-05-27 11:37:00 +02002698 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2699 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2700
Michal Vaskoc1171a42019-11-05 12:06:46 +01002701 if (rpc_getd->filter) {
2702 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002703 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 +01002704 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002705 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002706 }
2707 }
2708 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002709 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002710 }
2711 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002712 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2713 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002714 }
2715 if (rpc_getd->max_depth) {
2716 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002717 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002718 }
2719 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002720 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002721 }
2722
2723 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002724 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002725 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 +01002726 }
2727 break;
2728
2729 case NC_RPC_EDITDATA:
2730 rpc_editd = (struct nc_rpc_editdata *)rpc;
2731
Michal Vaskoab9deb62021-05-27 11:37:00 +02002732 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2733 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002734
2735 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002736 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2737 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002738 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002739 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002740 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 +01002741 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002742 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002743 }
2744 break;
2745
Michal Vasko96f247a2021-03-15 13:32:10 +01002746 case NC_RPC_ESTABLISHSUB:
2747 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2748
Michal Vaskoab9deb62021-05-27 11:37:00 +02002749 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002750
2751 if (rpc_estsub->filter) {
2752 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002753 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2754 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002755 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002756 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002757 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002758 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002759 }
2760 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002761 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002762
2763 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002764 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002765 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002766 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002767 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002768 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002769 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002770 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002771 }
2772 break;
2773
2774 case NC_RPC_MODIFYSUB:
2775 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2776
Michal Vaskoab9deb62021-05-27 11:37:00 +02002777 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002778
2779 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002780 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002781
2782 if (rpc_modsub->filter) {
2783 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002784 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
2785 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002786 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002787 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002788 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002789 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002790 }
2791 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002792 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002793 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002794 }
2795 break;
2796
2797 case NC_RPC_DELETESUB:
2798 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2799
Michal Vaskoab9deb62021-05-27 11:37:00 +02002800 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002801
2802 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002803 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002804 break;
2805
2806 case NC_RPC_KILLSUB:
2807 rpc_killsub = (struct nc_rpc_killsub *)rpc;
2808
Michal Vaskoab9deb62021-05-27 11:37:00 +02002809 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002810
2811 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002812 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002813 break;
2814
Michal Vasko305faca2021-03-25 09:16:02 +01002815 case NC_RPC_ESTABLISHPUSH:
2816 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
2817
Michal Vaskoab9deb62021-05-27 11:37:00 +02002818 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
2819 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002820
2821 if (rpc_estpush->filter) {
2822 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002823 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
2824 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002825 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002826 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002827 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002828 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002829 }
2830 }
2831
2832 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002833 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002834 }
Michal Vasko305faca2021-03-25 09:16:02 +01002835 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002836 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002837 }
2838
2839 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002840 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002841 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002842 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002843 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002844 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002845 }
2846 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002847 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002848 if (rpc_estpush->dampening_period) {
2849 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002850 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002851 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002852 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
2853 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002854 if (rpc_estpush->excluded_change) {
2855 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002856 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
2857 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002858 }
2859 }
2860 }
2861 break;
2862
2863 case NC_RPC_MODIFYPUSH:
2864 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
2865
Michal Vaskoab9deb62021-05-27 11:37:00 +02002866 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002867
2868 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002869 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
2870 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002871
2872 if (rpc_modpush->filter) {
2873 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002874 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
2875 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002876 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002877 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002878 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002879 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002880 }
2881 }
Michal Vasko305faca2021-03-25 09:16:02 +01002882 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002883 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002884 }
2885
2886 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002887 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002888 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002889 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002890 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002891 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002892 }
2893 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002894 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002895 if (rpc_modpush->dampening_period) {
2896 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002897 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002898 }
2899 }
2900 break;
2901
2902 case NC_RPC_RESYNCSUB:
2903 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
2904
Michal Vaskoab9deb62021-05-27 11:37:00 +02002905 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002906 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002907 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002908 break;
2909
Michal Vasko96f247a2021-03-15 13:32:10 +01002910 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002911 ERRINT;
2912 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002913 }
2914
Michal Vaskoab9deb62021-05-27 11:37:00 +02002915#undef CHECK_LYRC_BREAK
2916
2917 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02002918 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002919 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02002920 return NC_MSG_ERROR;
2921 }
2922
Michal Vasko131120a2018-05-29 15:44:02 +02002923 /* send RPC, store its message ID */
2924 r = nc_send_msg_io(session, timeout, data);
2925 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002926
Radek Krejcib4b19062018-02-07 16:33:06 +01002927 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01002928 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01002929 }
Michal Vasko086311b2016-01-08 09:53:11 +01002930
Michal Vasko131120a2018-05-29 15:44:02 +02002931 if (r == NC_MSG_RPC) {
2932 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002933 }
Michal Vasko131120a2018-05-29 15:44:02 +02002934 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01002935}
Michal Vaskode2946c2017-01-12 12:19:26 +01002936
2937API void
2938nc_client_session_set_not_strict(struct nc_session *session)
2939{
2940 if (session->side != NC_CLIENT) {
2941 ERRARG("session");
2942 return;
2943 }
2944
2945 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
2946}