blob: c29e5643206baab568dd348318175c8b946c49f8 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 session client functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
15
Radek Krejcifd5b6682017-06-13 15:52:53 +020016#define _GNU_SOURCE
Michal Vaskobbf52c82020-04-07 13:08:50 +020017
18#ifdef __linux__
19# include <sys/syscall.h>
20#endif
21
Michal Vaskob83a3fa2021-05-26 09:53:42 +020022#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010023#include <assert.h>
tadeas-vintrlik54f142a2021-08-23 10:36:18 +020024#include <ctype.h>
Michal Vasko086311b2016-01-08 09:53:11 +010025#include <errno.h>
26#include <fcntl.h>
27#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010028#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010029#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020030#include <poll.h>
Michal Vasko086311b2016-01-08 09:53:11 +010031#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020032#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010033#include <stdlib.h>
34#include <string.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020035#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010036#include <sys/socket.h>
37#include <sys/stat.h>
38#include <sys/types.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020039#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010040#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010041
42#include <libyang/libyang.h>
43
Michal Vasko9e8ac262020-04-07 13:06:45 +020044#include "compat.h"
Michal Vasko086311b2016-01-08 09:53:11 +010045#include "libnetconf.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010046#include "messages_client.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020047#include "session_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010048
Michal Vasko8a4e1462020-05-07 11:32:31 +020049#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020050#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020051
Michal Vasko80ef5d22016-01-18 09:21:02 +010052static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
53
Radek Krejci62aa0642017-05-25 16:33:49 +020054#ifdef NC_ENABLED_SSH
55int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
56char *sshauth_password(const char *username, const char *hostname, void *priv);
57char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020058char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
Radek Krejci62aa0642017-05-25 16:33:49 +020059#endif /* NC_ENABLED_SSH */
60
61static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
62static pthread_key_t nc_client_context_key;
63#ifdef __linux__
64static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020065 .opts.ka = {
66 .enabled = 1,
67 .idle_time = 1,
68 .max_probes = 10,
69 .probe_interval = 5
70 },
Radek Krejci62aa0642017-05-25 16:33:49 +020071#ifdef NC_ENABLED_SSH
72 .ssh_opts = {
73 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
74 .auth_hostkey_check = sshauth_hostkey_check,
75 .auth_password = sshauth_password,
76 .auth_interactive = sshauth_interactive,
77 .auth_privkey_passphrase = sshauth_privkey_passphrase
78 },
79 .ssh_ch_opts = {
80 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
81 .auth_hostkey_check = sshauth_hostkey_check,
82 .auth_password = sshauth_password,
83 .auth_interactive = sshauth_interactive,
84 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020085 },
Radek Krejci62aa0642017-05-25 16:33:49 +020086#endif /* NC_ENABLED_SSH */
87 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020088 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020089};
90#endif
91
92static void
93nc_client_context_free(void *ptr)
94{
95 struct nc_client_context *c = (struct nc_client_context *)ptr;
96
Radek Krejci5cebc6b2017-05-26 13:24:38 +020097 if (--(c->refcount)) {
98 /* still used */
99 return;
100 }
101
Radek Krejci62aa0642017-05-25 16:33:49 +0200102#ifdef __linux__
103 /* in __linux__ we use static memory in the main thread,
104 * so this check is for programs terminating the main()
105 * function by pthread_exit() :)
106 */
107 if (c != &context_main)
108#endif
109 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200110 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400111 free(c->opts.schema_searchpath);
112
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200113#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400114 int i;
115 for (i = 0; i < c->opts.ch_bind_count; ++i) {
116 close(c->opts.ch_binds[i].sock);
117 free((char *)c->opts.ch_binds[i].address);
118 }
119 free(c->opts.ch_binds);
120 c->opts.ch_binds = NULL;
121 c->opts.ch_bind_count = 0;
Radek Krejci62aa0642017-05-25 16:33:49 +0200122#endif
123#ifdef NC_ENABLED_SSH
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400124 _nc_client_ssh_destroy_opts(&c->ssh_opts);
125 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200126#endif
127#ifdef NC_ENABLED_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400128 _nc_client_tls_destroy_opts(&c->tls_opts);
129 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200130#endif
131 free(c);
132 }
133}
134
135static void
136nc_client_context_createkey(void)
137{
138 int r;
139
140 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200141 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200142 pthread_setspecific(nc_client_context_key, NULL);
143}
144
145struct nc_client_context *
146nc_client_context_location(void)
147{
148 struct nc_client_context *e;
149
150 pthread_once(&nc_client_context_once, nc_client_context_createkey);
151 e = pthread_getspecific(nc_client_context_key);
152 if (!e) {
153 /* prepare ly_err storage */
154#ifdef __linux__
155 if (getpid() == syscall(SYS_gettid)) {
156 /* main thread - use global variable instead of thread-specific variable. */
157 e = &context_main;
158 } else
159#endif /* __linux__ */
160 {
161 e = calloc(1, sizeof *e);
162 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200163 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200164#ifdef NC_ENABLED_SSH
165 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
166 e->ssh_opts.auth_pref[0].value = 3;
167 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
168 e->ssh_opts.auth_pref[1].value = 2;
169 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
170 e->ssh_opts.auth_pref[2].value = 1;
171 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
172 e->ssh_opts.auth_password = sshauth_password;
173 e->ssh_opts.auth_interactive = sshauth_interactive;
174 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
175
176 /* callhome settings are the same except the inverted auth methods preferences */
177 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
178 e->ssh_ch_opts.auth_pref[0].value = 1;
179 e->ssh_ch_opts.auth_pref[1].value = 2;
180 e->ssh_ch_opts.auth_pref[2].value = 3;
181#endif /* NC_ENABLED_SSH */
182 }
183 pthread_setspecific(nc_client_context_key, e);
184 }
185
186 return e;
187}
188
189#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100190
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200191API void *
192nc_client_get_thread_context(void)
193{
194 return nc_client_context_location();
195}
196
197API void
198nc_client_set_thread_context(void *context)
199{
200 struct nc_client_context *old, *new;
201
202 if (!context) {
203 ERRARG(context);
204 return;
205 }
206
207 new = (struct nc_client_context *)context;
208 old = nc_client_context_location();
209 if (old == new) {
210 /* nothing to change */
211 return;
212 }
213
214 /* replace old by new, increase reference counter in the newly set context */
215 nc_client_context_free(old);
216 new->refcount++;
217 pthread_setspecific(nc_client_context_key, new);
218}
219
Radek Krejcifd5b6682017-06-13 15:52:53 +0200220int
221nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
222{
223 /* assign context (dicionary needed for handshake) */
224 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100225 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200226 return EXIT_FAILURE;
227 }
228
229 /* user path must be first, the first path is used to store schemas retreived via get-schema */
230 if (client_opts.schema_searchpath) {
231 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200232 } else if (!access(NC_YANG_DIR, F_OK)) {
233 ly_ctx_set_searchdir(ctx, NC_YANG_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200234 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200235
236 /* set callback for getting schemas, if provided */
237 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
238 } else {
239 session->flags |= NC_SESSION_SHAREDCTX;
240 }
241
242 session->ctx = ctx;
243
244 return EXIT_SUCCESS;
245}
246
Michal Vasko086311b2016-01-08 09:53:11 +0100247API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100248nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100249{
Michal Vasko3031aae2016-01-27 16:07:18 +0100250 if (client_opts.schema_searchpath) {
251 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100252 }
Michal Vasko086311b2016-01-08 09:53:11 +0100253
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100254 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100255 client_opts.schema_searchpath = strdup(path);
256 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100257 ERRMEM;
258 return 1;
259 }
260 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100261 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100262 }
263
264 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100265}
266
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100267API const char *
268nc_client_get_schema_searchpath(void)
269{
270 return client_opts.schema_searchpath;
271}
272
Radek Krejcifd5b6682017-06-13 15:52:53 +0200273API int
274nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100275{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200276 client_opts.schema_clb = clb;
277 if (clb) {
278 client_opts.schema_clb_data = user_data;
279 } else {
280 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100281 }
282
283 return 0;
284}
285
Radek Krejcifd5b6682017-06-13 15:52:53 +0200286API ly_module_imp_clb
287nc_client_get_schema_callback(void **user_data)
288{
289 if (user_data) {
290 (*user_data) = client_opts.schema_clb_data;
291 }
292 return client_opts.schema_clb;
293}
294
Radek Krejci65ef6d52018-08-16 16:35:02 +0200295struct schema_info {
296 char *name;
297 char *revision;
298 struct {
299 char *name;
300 char *revision;
301 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100302 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200303 int implemented;
304};
305
306struct clb_data_s {
307 void *user_data;
308 ly_module_imp_clb user_clb;
309 struct schema_info *schemas;
310 struct nc_session *session;
311 int has_get_schema;
312};
313
Michal Vaskoc088f982021-10-05 12:23:07 +0200314/**
315 * @brief Retrieve YANG schema content from a local file.
316 *
317 * @param[in] name Schema name.
318 * @param[in] rev Schema revision.
319 * @param[in] clb_data get-schema callback data.
320 * @param[out] format Schema format.
321 * @return Schema content.
322 */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200323static char *
324retrieve_schema_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200325 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100326{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200327 char *localfile = NULL;
328 FILE *f;
329 long length, l;
330 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100331
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200332 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200333 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
334 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200335 return NULL;
336 }
337 if (localfile) {
Michal Vasko05532772021-06-03 12:12:38 +0200338 VRB(clb_data->session, "Reading schema from localfile \"%s\".", localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200339 f = fopen(localfile, "r");
340 if (!f) {
Michal Vasko05532772021-06-03 12:12:38 +0200341 ERR(clb_data->session, "Unable to open \"%s\" file to get schema (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200342 free(localfile);
343 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100344 }
Michal Vasko086311b2016-01-08 09:53:11 +0100345
Radek Krejci65ef6d52018-08-16 16:35:02 +0200346 fseek(f, 0, SEEK_END);
347 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200348 if (length < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200349 ERR(clb_data->session, "Unable to get size of schema file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200350 free(localfile);
351 fclose(f);
352 return NULL;
353 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200354 fseek(f, 0, SEEK_SET);
355
356 model_data = malloc(length + 1);
357 if (!model_data) {
358 ERRMEM;
359 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko05532772021-06-03 12:12:38 +0200360 ERR(clb_data->session, "Reading schema from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
361 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200362 free(model_data);
363 model_data = NULL;
364 } else {
365 /* terminating NULL byte */
366 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100367 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200368 fclose(f);
369 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100370 }
371
Radek Krejci65ef6d52018-08-16 16:35:02 +0200372 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100373}
374
Michal Vaskoc088f982021-10-05 12:23:07 +0200375/**
376 * @brief Retrieve YANG schema content from a reply to get-schema RPC.
377 *
378 * @param[in] name Schema name.
379 * @param[in] rev Schema revision.
380 * @param[in] clb_data get-schema callback data.
381 * @param[out] format Schema format.
382 * @return Schema content.
383 */
Michal Vasko086311b2016-01-08 09:53:11 +0100384static char *
Radek Krejci65ef6d52018-08-16 16:35:02 +0200385retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200386 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100387{
Michal Vasko086311b2016-01-08 09:53:11 +0100388 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100389 struct lyd_node *envp = NULL, *op = NULL;
390 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100391 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100392 uint64_t msgid;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200393 char *localfile = NULL;
394 FILE *f;
395 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100396
Michal Vasko05532772021-06-03 12:12:38 +0200397 VRB(clb_data->session, "Reading schema from server via get-schema.");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200398 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100399
Radek Krejci65ef6d52018-08-16 16:35:02 +0200400 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100401 usleep(1000);
402 }
403 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200404 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100405 nc_rpc_free(rpc);
406 return NULL;
407 }
408
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200409 do {
Michal Vasko77367452021-02-16 16:32:18 +0100410 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200411 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100412 nc_rpc_free(rpc);
413 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200414 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100415 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100416 } else if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200417 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100418 goto cleanup;
Michal Vasko8d0f3b32022-01-12 08:29:14 +0100419 } else if (!op) {
420 WRN(clb_data->session, "Received an unexpected reply to <get-schema>.");
421 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100422 }
423
Michal Vasko77367452021-02-16 16:32:18 +0100424 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200425 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100426 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200427 }
Michal Vasko77367452021-02-16 16:32:18 +0100428 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200429 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200430 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100431 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200432 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200433 break;
434 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100435 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200436 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200437 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200438 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200439 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100440 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200441 }
Michal Vasko086311b2016-01-08 09:53:11 +0100442
Radek Krejci488b0702018-08-29 14:47:15 +0200443 if (model_data && !model_data[0]) {
444 /* empty data */
445 free(model_data);
446 model_data = NULL;
447 }
448
Radek Krejcifd5b6682017-06-13 15:52:53 +0200449 /* try to store the model_data into local schema repository */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200450 if (model_data) {
451 *format = LYS_IN_YANG;
452 if (client_opts.schema_searchpath) {
Michal Vasko77367452021-02-16 16:32:18 +0100453 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
454 rev ? rev : "") == -1) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200455 ERRMEM;
Michal Vaskof945da52018-02-15 08:45:13 +0100456 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200457 f = fopen(localfile, "w");
458 if (!f) {
Michal Vasko05532772021-06-03 12:12:38 +0200459 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 +0200460 localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200461 } else {
462 fputs(model_data, f);
463 fclose(f);
464 }
465 free(localfile);
Michal Vaskof945da52018-02-15 08:45:13 +0100466 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200467 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200468 }
469
Michal Vasko77367452021-02-16 16:32:18 +0100470cleanup:
471 lyd_free_tree(envp);
472 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100473 return model_data;
474}
475
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200476static void
477free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200478{
479 free(data);
480 (void)user_data;
481}
482
Michal Vaskoc088f982021-10-05 12:23:07 +0200483/**
484 * @brief Retrieve YANG schema content.
485 *
486 * @param[in] mod_name Schema name.
487 * @param[in] mod_rev Schema revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100488 * @param[in] user_data get-schema callback data.
489 * @param[out] format Schema format.
490 * @param[out] module_data Schema content.
491 * @param[out] free_module_data Callback for freeing @p module_data.
492 * @return LY_ERR value.
493 */
494static LY_ERR
495retrieve_schema_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format,
496 const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
497{
498 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
499 char *model_data = NULL;
500
501 VRB(clb_data->session, "Retrieving data for schema \"%s\", revision \"%s\".", mod_name, mod_rev ? mod_rev : "<latest>");
502
503 /* 1. try to get data locally */
504 model_data = retrieve_schema_data_localfile(mod_name, mod_rev, clb_data, format);
505
506 /* 2. try to use <get-schema> */
507 if (!model_data && clb_data->has_get_schema) {
508 model_data = retrieve_schema_data_getschema(mod_name, mod_rev, clb_data, format);
509 }
510
511 /* 3. try to use user callback */
512 if (!model_data && clb_data->user_clb) {
513 VRB(clb_data->session, "Reading schema via user callback.");
514 clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data,
515 free_module_data);
516 }
517
518 *free_module_data = free_with_user_data;
519 *module_data = model_data;
520 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
521}
522
523/**
524 * @brief Retrieve YANG import schema content.
525 *
526 * @param[in] mod_name Schema name.
527 * @param[in] mod_rev Schema revision.
Michal Vaskoc088f982021-10-05 12:23:07 +0200528 * @param[in] submod_name Optional submodule name.
529 * @param[in] sub_rev Submodule revision.
530 * @param[in] user_data get-schema callback data.
531 * @param[out] format Schema format.
532 * @param[out] module_data Schema content.
533 * @param[out] free_module_data Callback for freeing @p module_data.
534 * @return LY_ERR value.
535 */
Michal Vasko77367452021-02-16 16:32:18 +0100536static LY_ERR
Michal Vasko5e32c402022-01-12 14:05:09 +0100537retrieve_schema_data_imp(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
Michal Vasko77367452021-02-16 16:32:18 +0100538 void *user_data, LYS_INFORMAT *format, const char **module_data,
539 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200540{
541 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
Michal Vasko5e32c402022-01-12 14:05:09 +0100542 uint32_t u, v, match = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200543 const char *name = NULL, *rev = NULL;
544 char *model_data = NULL;
545
546 /* get and check the final name and revision of the schema to be retrieved */
547 if (!mod_rev || !mod_rev[0]) {
548 /* newest revision requested - get the newest revision from the list of available modules on server */
549 match = 0;
550 for (u = 0; clb_data->schemas[u].name; ++u) {
551 if (strcmp(mod_name, clb_data->schemas[u].name)) {
552 continue;
553 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200554 if (!match || (strcmp(mod_rev, clb_data->schemas[u].revision) > 0)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200555 mod_rev = clb_data->schemas[u].revision;
556 }
557 match = u + 1;
558 }
559 if (!match) {
Michal Vasko1d2665c2021-02-12 09:28:44 +0100560 /* valid situation if we are retrieving YANG 1.1 schema and have only capabilities for now
561 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko5e32c402022-01-12 14:05:09 +0100562 VRB(clb_data->session, "Unable to identify revision of the import schema \"%s\" from "
Michal Vasko05532772021-06-03 12:12:38 +0200563 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200564 }
565 }
566 if (submod_name) {
567 name = submod_name;
568 if (sub_rev) {
569 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200570 } else if (match) {
571 if (!clb_data->schemas[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200572 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
Michal Vasko5e32c402022-01-12 14:05:09 +0100573 "in import schema \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200574 } else {
575 for (v = 0; clb_data->schemas[match - 1].submodules[v].name; ++v) {
576 if (!strcmp(submod_name, clb_data->schemas[match - 1].submodules[v].name)) {
577 rev = sub_rev = clb_data->schemas[match - 1].submodules[v].revision;
578 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200579 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200580 if (!rev) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100581 ERR(clb_data->session, "Requested submodule \"%s\" is not found in import schema \"%s\" on server side.",
Michal Vasko05532772021-06-03 12:12:38 +0200582 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100583 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200584 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200585 }
586 }
587 } else {
588 name = mod_name;
589 rev = mod_rev;
590 }
591
Michal Vasko5e32c402022-01-12 14:05:09 +0100592 VRB(clb_data->session, "Retrieving data for import schema \"%s\", revision \"%s\".", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200593
594 if (match) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100595 /* we have enough information to avoid communication with server and try to get the schema locally */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200596
597 /* 1. try to get data locally */
598 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
599
600 /* 2. try to use <get-schema> */
601 if (!model_data && clb_data->has_get_schema) {
602 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
603 }
604 } else {
605 /* we are unsure which revision of the schema we should load, so first try to get
606 * the newest revision from the server via get-schema and only if the server does not
607 * implement get-schema, try to load the newest revision locally. This is imperfect
608 * solution, but there are situation when a client does not know what revision is
609 * actually implemented by the server. */
610
611 /* 1. try to use <get-schema> */
612 if (clb_data->has_get_schema) {
613 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
614 }
615
616 /* 2. try to get data locally */
617 if (!model_data) {
618 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
619 }
620 }
621
622 /* 3. try to use user callback */
623 if (!model_data && clb_data->user_clb) {
Michal Vasko05532772021-06-03 12:12:38 +0200624 VRB(clb_data->session, "Reading schema via user callback.");
Michal Vasko77367452021-02-16 16:32:18 +0100625 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
626 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200627 }
628
Jan Kundrát35972df2018-09-06 19:00:01 +0200629 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100630 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200631 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200632}
633
Michal Vaskoc088f982021-10-05 12:23:07 +0200634/**
635 * @brief Load a YANG schema into context.
636 *
637 * @param[in] session NC session.
638 * @param[in] name Schema name.
639 * @param[in] revision Schema revision.
Michal Vasko5e32c402022-01-12 14:05:09 +0100640 * @param[in] features Enabled schema features.
Michal Vaskoc088f982021-10-05 12:23:07 +0200641 * @param[in] schemas Server schema info built from capabilities.
642 * @param[in] user_clb User callback for retireving schema data.
643 * @param[in] user_data User data for @p user_clb.
644 * @param[in] has_get_schema Whether the server supports get-schema.
645 * @param[out] mod Loaded module.
646 * @return 0 on success.
647 * @return -1 on error.
648 */
Michal Vaskoceae0152018-02-14 16:03:59 +0100649static int
Michal Vasko5e32c402022-01-12 14:05:09 +0100650nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features,
651 struct schema_info *schemas, ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod)
Michal Vaskoceae0152018-02-14 16:03:59 +0100652{
653 int ret = 0;
654 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200655 const char *module_data = NULL;
Michal Vasko5e32c402022-01-12 14:05:09 +0100656 struct ly_in *in;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200657 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200658
659 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200660 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100661
Michal Vasko5e32c402022-01-12 14:05:09 +0100662 /* try to use a module from the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200663 if (revision) {
Michal Vasko77367452021-02-16 16:32:18 +0100664 *mod = ly_ctx_get_module(session->ctx, name, revision);
Michal Vasko5e32c402022-01-12 14:05:09 +0100665 } else {
666 *mod = ly_ctx_get_module_implemented(session->ctx, name);
667 if (!*mod) {
668 *mod = ly_ctx_get_module_latest(session->ctx, name);
669 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200670 }
Michal Vasko5e32c402022-01-12 14:05:09 +0100671
Michal Vaskoceae0152018-02-14 16:03:59 +0100672 if (*mod) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100673 /* make the present module implemented and/or enable all its features */
674 if (lys_set_implemented(*mod, features)) {
675 ERR(session, "Failed to implement schema \"%s\".", (*mod)->name);
676 ret = -1;
Michal Vaskoceae0152018-02-14 16:03:59 +0100677 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200678 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100679 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200680 clb_data.has_get_schema = has_get_schema;
681 clb_data.schemas = schemas;
682 clb_data.session = session;
683 clb_data.user_clb = user_clb;
684 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100685
686 /* clear all the errors and just collect them for now */
687 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200688 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100689
Radek Krejci65ef6d52018-08-16 16:35:02 +0200690 /* get module data */
Michal Vasko5e32c402022-01-12 14:05:09 +0100691 retrieve_schema_data(name, revision, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100692
Radek Krejci65ef6d52018-08-16 16:35:02 +0200693 if (module_data) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100694 /* set import callback */
695 ly_ctx_set_module_imp_clb(session->ctx, retrieve_schema_data_imp, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100696
Michal Vasko5e32c402022-01-12 14:05:09 +0100697 /* parse the schema */
698 ly_in_new_memory(module_data, &in);
699 lys_parse(session->ctx, in, format, features, mod);
700 ly_in_free(in, 0);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200701 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100702 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100703 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100704
Radek Krejci65ef6d52018-08-16 16:35:02 +0200705 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
706 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100707
Michal Vaskodbf7c272018-02-19 09:07:59 +0100708 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200709 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100710 if (!(*mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +0100711 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100712 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100713 }
714 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200715 } else {
716 /* print only warnings */
Michal Vasko5e32c402022-01-12 14:05:09 +0100717 for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200718 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100719 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200720 }
721 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100722 }
723
Michal Vaskodbf7c272018-02-19 09:07:59 +0100724 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100725 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100726 }
727
728 return ret;
729}
730
Radek Krejci65ef6d52018-08-16 16:35:02 +0200731static void
732free_schema_info(struct schema_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200733{
Michal Vasko77367452021-02-16 16:32:18 +0100734 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200735
Radek Krejci65ef6d52018-08-16 16:35:02 +0200736 if (!list) {
737 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200738 }
739
Radek Krejci65ef6d52018-08-16 16:35:02 +0200740 for (u = 0; list[u].name; ++u) {
741 free(list[u].name);
742 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100743 if (list[u].features) {
744 for (v = 0; list[u].features[v]; ++v) {
745 free(list[u].features[v]);
746 }
747 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200748 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200749 if (list[u].submodules) {
750 for (v = 0; list[u].submodules[v].name; ++v) {
751 free(list[u].submodules[v].name);
752 free(list[u].submodules[v].revision);
753 }
754 free(list[u].submodules);
755 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200756 }
757 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200758}
759
Michal Vaskoc088f982021-10-05 12:23:07 +0200760/**
761 * @brief Build server schema info from ietf-yang-library data.
762 *
763 * @param[in] session NC session.
764 * @param[in] has_get_data Whether get-data RPC is available or only get.
765 * @param[out] result Server schemas.
766 * @return 0 on success.
767 * @return -1 on error.
768 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200769static int
Michal Vasko303263d2021-10-05 12:18:21 +0200770build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema_info **result)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200771{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200772 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100773 struct lyd_node *op = NULL, *envp = NULL;
774 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200775 NC_MSG_TYPE msg;
776 uint64_t msgid;
Radek Krejcia8dfaea2018-08-17 10:55:09 +0200777 struct ly_set *modules = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100778 uint32_t u, v, submodules_count, feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200779 struct lyd_node *iter, *child;
780 struct lys_module *mod;
Michal Vasko303263d2021-10-05 12:18:21 +0200781 int ret = 0;
782 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200783
784 /* get yang-library data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200785 if (has_get_data) {
786 rpc_name = "get-data";
787 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
788 rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
789 NC_PARAMTYPE_CONST);
790 } else {
791 rpc = nc_rpc_getdata("ietf-datastores:operational",
792 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
793 0, NC_PARAMTYPE_CONST);
794 }
Radek Krejci261737f2018-08-21 09:22:34 +0200795 } else {
Michal Vasko303263d2021-10-05 12:18:21 +0200796 rpc_name = "get";
797 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
798 rpc = nc_rpc_get("/ietf-yang-library:*", 0, NC_PARAMTYPE_CONST);
799 } else {
800 rpc = nc_rpc_get("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", 0,
801 NC_PARAMTYPE_CONST);
802 }
Radek Krejci261737f2018-08-21 09:22:34 +0200803 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200804 if (!rpc) {
805 goto cleanup;
806 }
807
808 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
809 usleep(1000);
810 }
811 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200812 WRN(session, "Failed to send request for yang-library data.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200813 goto cleanup;
814 }
815
816 do {
Michal Vasko77367452021-02-16 16:32:18 +0100817 lyd_free_tree(envp);
818 lyd_free_tree(op);
819
820 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200821 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200822 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko303263d2021-10-05 12:18:21 +0200823 WRN(session, "Timeout for receiving reply to a <%s> yang-library data expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200824 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100825 } else if (msg == NC_MSG_ERROR) {
Michal Vasko303263d2021-10-05 12:18:21 +0200826 WRN(session, "Failed to receive a reply to <%s> of yang-library data.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200827 goto cleanup;
Jan Kundrát1d73a5a2022-03-30 16:54:49 +0200828 } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko303263d2021-10-05 12:18:21 +0200829 WRN(session, "Unexpected reply without data to a yang-library <%s> RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200830 goto cleanup;
831 }
832
Michal Vasko77367452021-02-16 16:32:18 +0100833 data = (struct lyd_node_any *)lyd_child(op);
834 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko303263d2021-10-05 12:18:21 +0200835 WRN(session, "Unexpected data in reply to a yang-library <%s> RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200836 goto cleanup;
837 } else if (!data->value.tree) {
Michal Vasko303263d2021-10-05 12:18:21 +0200838 WRN(session, "No data in reply to a yang-library <%s> RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200839 goto cleanup;
840 }
841
Michal Vasko77367452021-02-16 16:32:18 +0100842 if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
Michal Vasko303263d2021-10-05 12:18:21 +0200843 WRN(session, "No module information in reply to a yang-library <%s> RPC.", rpc_name);
Radek Krejci2d088832018-08-21 13:40:14 +0200844 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200845 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200846
Michal Vasko77367452021-02-16 16:32:18 +0100847 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200848 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200849 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200850 ret = -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200851 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200852 }
853
Michal Vasko77367452021-02-16 16:32:18 +0100854 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200855 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100856 feature_count = 0;
857 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
858 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200859 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200860 /* ignore node from other schemas (augments) */
861 continue;
862 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200863 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200864 /* ignore empty nodes */
865 continue;
866 }
867 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200868 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200869 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200870 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200871 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200872 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200873 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100874 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
875 if (!(*result)[u].features) {
876 ERRMEM;
877 free_schema_info(*result);
878 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200879 ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100880 goto cleanup;
881 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200882 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100883 (*result)[u].features[feature_count + 1] = NULL;
884 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200885 } else if (!strcmp(iter->schema->name, "submodule")) {
886 submodules_count++;
887 }
888 }
889
890 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200891 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
892 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200893 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200894 free_schema_info(*result);
895 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200896 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200897 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200898 } else {
899 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100900 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
901 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200902 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100903 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200904 if (mod != child->schema->module) {
905 continue;
906 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200907 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200908 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200909 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200910 }
911 }
912 }
913 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200914 }
915 }
916 }
917
Radek Krejcifd5b6682017-06-13 15:52:53 +0200918cleanup:
919 nc_rpc_free(rpc);
Michal Vasko77367452021-02-16 16:32:18 +0100920 lyd_free_tree(envp);
921 lyd_free_tree(op);
922 ly_set_free(modules, NULL);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200923
Radek Krejci235d8cb2018-08-17 14:04:32 +0200924 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko05532772021-06-03 12:12:38 +0200925 /* something bad happened, discard the session */
926 ERR(session, "Invalid session, discarding.");
Michal Vaskoc088f982021-10-05 12:23:07 +0200927 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200928 }
929
930 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200931}
932
Michal Vaskoc088f982021-10-05 12:23:07 +0200933/**
934 * @brief Build server schema info from received capabilities.
935 *
936 * @param[in] cpblts Server capabilities.
937 * @param[out] result Server schemas.
938 * @return 0 on success.
939 * @return -1 on error.
940 */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200941static int
942build_schema_info_cpblts(char **cpblts, struct schema_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200943{
Michal Vasko77367452021-02-16 16:32:18 +0100944 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200945 char *module_cpblt, *ptr, *ptr2;
946
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200947 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +0200948 (*result) = calloc(u + 1, sizeof **result);
949 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200950 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200951 return -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200952 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200953
954 for (u = v = 0; cpblts[u]; ++u) {
955 module_cpblt = strstr(cpblts[u], "module=");
956 /* this capability requires a module */
957 if (!module_cpblt) {
958 continue;
959 }
960
961 /* get module's name */
962 ptr = (char *)module_cpblt + 7;
963 ptr2 = strchr(ptr, '&');
964 if (!ptr2) {
965 ptr2 = ptr + strlen(ptr);
966 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200967 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200968
969 /* get module's revision */
970 ptr = strstr(module_cpblt, "revision=");
971 if (ptr) {
972 ptr += 9;
973 ptr2 = strchr(ptr, '&');
974 if (!ptr2) {
975 ptr2 = ptr + strlen(ptr);
976 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200977 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200978 }
979
980 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200981 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200982
983 /* get module's features */
984 ptr = strstr(module_cpblt, "features=");
985 if (ptr) {
986 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +0100987 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200988 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
989 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +0100990 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
991 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
992 (*result)[v].features[feature_count + 1] = NULL;
993 ++feature_count;
994
Radek Krejci65ef6d52018-08-16 16:35:02 +0200995 ptr2 = ptr + 1;
996 }
997 }
998 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +0100999 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
1000 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
1001 (*result)[v].features[feature_count + 1] = NULL;
1002 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001003 }
1004 ++v;
1005 }
Radek Krejci235d8cb2018-08-17 14:04:32 +02001006
Michal Vasko303263d2021-10-05 12:18:21 +02001007 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001008}
1009
Michal Vaskoc088f982021-10-05 12:23:07 +02001010/**
1011 * @brief Fill client context based on server schema info.
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(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data,
1023 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001024{
Michal Vasko303263d2021-10-05 12:18:21 +02001025 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +01001026 struct lys_module *mod;
1027 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001028
1029 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +02001030 /* skip import-only modules */
1031 if (!modules[u].implemented) {
1032 continue;
1033 }
1034
Radek Krejci65ef6d52018-08-16 16:35:02 +02001035 /* we can continue even if it fails */
Michal Vasko5e32c402022-01-12 14:05:09 +01001036 nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules,
1037 user_clb, user_data, has_get_schema, &mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001038
1039 if (!mod) {
1040 if (session->status != NC_STATUS_RUNNING) {
1041 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +02001042 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001043 goto cleanup;
1044 }
1045
1046 /* all loading ways failed, the schema will be ignored in the received data */
Michal Vasko05532772021-06-03 12:12:38 +02001047 WRN(session, "Failed to load schema \"%s@%s\".", modules[u].name, modules[u].revision ?
1048 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001049 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001050 }
Michal Vasko60f66602017-10-17 13:52:18 +02001051 }
1052
Michal Vasko77367452021-02-16 16:32:18 +01001053 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +02001054 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001055
1056cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +02001057 return ret;
1058}
1059
Michal Vaskoc088f982021-10-05 12:23:07 +02001060/**
1061 * @brief Fill client context with ietf-netconf schema.
1062 *
1063 * @param[in] session NC session with the context to modify.
1064 * @param[in] modules Server schema info.
1065 * @param[in] user_clb User callback for retrieving specific schemas.
1066 * @param[in] user_data User data for @p user_clb.
1067 * @param[in] has_get_schema Whether server supports get-schema RPC.
1068 * @return 0 on success.
1069 * @return -1 on error.
1070 */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001071static int
Michal Vasko77367452021-02-16 16:32:18 +01001072nc_ctx_fill_ietf_netconf(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb,
1073 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +02001074{
Michal Vasko77367452021-02-16 16:32:18 +01001075 uint32_t u;
Michal Vasko5e32c402022-01-12 14:05:09 +01001076 const char **features = NULL;
1077 struct ly_in *in;
Michal Vasko77367452021-02-16 16:32:18 +01001078 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001079
Michal Vasko5e32c402022-01-12 14:05:09 +01001080 /* find supported features (capabilities) in ietf-netconf */
1081 for (u = 0; modules[u].name; ++u) {
1082 if (!strcmp(modules[u].name, "ietf-netconf")) {
1083 assert(modules[u].implemented);
1084 features = (const char **)modules[u].features;
1085 break;
1086 }
1087 }
1088 if (!modules[u].name) {
1089 ERR(session, "Base NETCONF schema not supported by the server.");
1090 return -1;
1091 }
1092
Michal Vasko77367452021-02-16 16:32:18 +01001093 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vasko5e32c402022-01-12 14:05:09 +01001094 if (ietfnc) {
1095 /* make sure to enable all the features if already loaded */
1096 lys_set_implemented(ietfnc, features);
1097 } else {
1098 /* load the module */
1099 nc_ctx_load_module(session, "ietf-netconf", NULL, features, modules, user_clb, user_data, has_get_schema, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001100 if (!ietfnc) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001101 ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in);
1102 lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc);
1103 ly_in_free(in, 0);
Michal Vasko8a4e1462020-05-07 11:32:31 +02001104 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001105 }
1106 if (!ietfnc) {
Michal Vasko05532772021-06-03 12:12:38 +02001107 ERR(session, "Loading base NETCONF schema failed.");
Michal Vasko303263d2021-10-05 12:18:21 +02001108 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +02001109 }
1110
Radek Krejci65ef6d52018-08-16 16:35:02 +02001111 return 0;
1112}
1113
Michal Vasko086311b2016-01-08 09:53:11 +01001114int
1115nc_ctx_check_and_fill(struct nc_session *session)
1116{
Michal Vasko303263d2021-10-05 12:18:21 +02001117 int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +01001118 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001119 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001120 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +02001121 char *revision;
Radek Krejcib056ff82018-08-20 15:58:39 +02001122 struct schema_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001123
Michal Vasko2e6defd2016-10-07 15:48:15 +02001124 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +01001125
Radek Krejci65ef6d52018-08-16 16:35:02 +02001126 /* 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 +02001127 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001128
Radek Krejci65ef6d52018-08-16 16:35:02 +02001129 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
1130 * schemas only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +01001131 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001132
1133 /* our callback is set later with appropriate data */
1134 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
1135
1136 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001137 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001138 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001139 get_schema_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +02001140 if (yanglib_support) {
1141 break;
1142 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +01001143 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001144 yanglib_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +02001145 if (get_schema_support) {
1146 break;
1147 }
Michal Vasko086311b2016-01-08 09:53:11 +01001148 }
1149 }
Michal Vasko31dd4c52020-04-17 10:29:44 +02001150 if (get_schema_support) {
Michal Vasko05532772021-06-03 12:12:38 +02001151 VRB(session, "Capability for <get-schema> support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001152 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001153 VRB(session, "Capability for <get-schema> support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001154 }
1155 if (yanglib_support) {
Michal Vasko05532772021-06-03 12:12:38 +02001156 VRB(session, "Capability for yang-library support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001157 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001158 VRB(session, "Capability for yang-library support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001159 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001160
1161 /* get information about server's schemas from capabilities list until we will have yang-library */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001162 if (build_schema_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
Michal Vasko05532772021-06-03 12:12:38 +02001163 ERR(session, "Unable to get server's schema information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001164 goto cleanup;
1165 }
1166
Michal Vasko086311b2016-01-08 09:53:11 +01001167 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001168 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 +02001169 WRN(session, "Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001170 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001171 }
1172
1173 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001174 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001175 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001176 }
1177
Radek Krejci65ef6d52018-08-16 16:35:02 +02001178 /* get correct version of ietf-yang-library into context */
1179 if (yanglib_support) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001180 /* use get schema to get server's ietf-yang-library */
1181 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1182 if (!revision) {
Michal Vasko05532772021-06-03 12:12:38 +02001183 WRN(session, "Loading NETCONF ietf-yang-library schema failed, missing revision in NETCONF <hello> message.");
1184 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001185 yanglib_support = 0;
1186 } else {
1187 revision = strndup(&revision[9], 10);
Michal Vasko5e32c402022-01-12 14:05:09 +01001188 if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001189 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001190 WRN(session, "Loading NETCONF ietf-yang-library schema failed, unable to use it to learn all "
1191 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001192 yanglib_support = 0;
1193 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001194 if (strcmp(revision, "2019-01-04") >= 0) {
1195 /* we also need ietf-datastores to be implemented */
Michal Vasko5e32c402022-01-12 14:05:09 +01001196 if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko0d877f92020-04-02 13:52:43 +02001197 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001198 WRN(session, "Loading NETCONF ietf-datastores schema failed, unable to use yang-library "
1199 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001200 yanglib_support = 0;
1201 }
1202 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001203 free(revision);
Michal Vasko77367452021-02-16 16:32:18 +01001204
1205 /* ietf-netconf-nmda is needed to issue get-data */
Michal Vasko5e32c402022-01-12 14:05:09 +01001206 if (!nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb, old_data,
Michal Vasko77367452021-02-16 16:32:18 +01001207 get_schema_support, &mod)) {
Michal Vasko5e32c402022-01-12 14:05:09 +01001208 VRB(session, "Support for <get-data> from ietf-netconf-nmda found.");
Michal Vasko303263d2021-10-05 12:18:21 +02001209 get_data_support = 1;
Michal Vasko5e32c402022-01-12 14:05:09 +01001210 } else {
1211 VRB(session, "Support for <get-data> from ietf-netconf-nmda not found.");
Michal Vasko77367452021-02-16 16:32:18 +01001212 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001213 }
1214 }
1215
Radek Krejci65ef6d52018-08-16 16:35:02 +02001216 /* prepare structured information about server's schemas */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001217 if (yanglib_support) {
Michal Vasko303263d2021-10-05 12:18:21 +02001218 if (build_schema_info_yl(session, get_data_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001219 goto cleanup;
1220 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001221 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001222 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001223 /* prefer yang-library information, currently we have it from capabilities used for getting correct
1224 * yang-library schema */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001225 free_schema_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001226 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001227 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001228 }
Michal Vaskoef578332016-01-25 13:20:09 +01001229
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001230 /* compile all modules at once to avoid invalid errors or warnings */
1231 ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
1232
1233 /* fill the context */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001234 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1235 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001236 }
1237
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001238 /* compile it */
1239 if (ly_ctx_compile(session->ctx)) {
1240 goto cleanup;
1241 }
1242
1243 /* success */
Radek Krejcifd5b6682017-06-13 15:52:53 +02001244 ret = 0;
1245
Michal Vaskoeee99412016-11-21 10:19:43 +01001246 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko05532772021-06-03 12:12:38 +02001247 WRN(session, "Some models failed to be loaded, any data from these models (and any other unknown) will "
1248 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001249 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001250
1251cleanup:
Radek Krejci65ef6d52018-08-16 16:35:02 +02001252 free_schema_info(server_modules);
1253
Radek Krejcifd5b6682017-06-13 15:52:53 +02001254 /* set user callback back */
1255 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001256 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Michal Vaskoa9e9d452022-05-04 15:48:25 +02001257 ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001258
Michal Vaskoef578332016-01-25 13:20:09 +01001259 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001260}
1261
1262API struct nc_session *
1263nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1264{
Michal Vaskod083db62016-01-19 10:31:29 +01001265 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001266
Michal Vasko45e53ae2016-04-07 11:46:03 +02001267 if (fdin < 0) {
1268 ERRARG("fdin");
1269 return NULL;
1270 } else if (fdout < 0) {
1271 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001272 return NULL;
1273 }
1274
1275 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001276 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001277 if (!session) {
1278 ERRMEM;
1279 return NULL;
1280 }
1281 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001282
1283 /* transport specific data */
1284 session->ti_type = NC_TI_FD;
1285 session->ti.fd.in = fdin;
1286 session->ti.fd.out = fdout;
1287
Robin Jarry4e38e292019-10-15 17:14:14 +02001288 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1289 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001290 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001291 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001292
1293 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001294 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001295 goto fail;
1296 }
1297 session->status = NC_STATUS_RUNNING;
1298
Michal Vaskoef578332016-01-25 13:20:09 +01001299 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001300 goto fail;
1301 }
1302
1303 return session;
1304
1305fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001306 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001307 return NULL;
1308}
1309
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001310API struct nc_session *
1311nc_connect_unix(const char *address, struct ly_ctx *ctx)
1312{
1313 struct nc_session *session = NULL;
1314 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001315 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001316 char *username;
1317 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001318 char *buf = NULL;
1319 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001320
1321 if (address == NULL) {
1322 ERRARG("address");
1323 return NULL;
1324 }
1325
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001326 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1327 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001328 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001329 goto fail;
1330 }
1331
1332 memset(&sun, 0, sizeof(sun));
1333 sun.sun_family = AF_UNIX;
1334 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1335
1336 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001337 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001338 goto fail;
1339 }
1340
1341 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001342 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001343 goto fail;
1344 }
1345
1346 /* prepare session structure */
1347 session = nc_new_session(NC_CLIENT, 0);
1348 if (!session) {
1349 ERRMEM;
1350 goto fail;
1351 }
1352 session->status = NC_STATUS_STARTING;
1353
1354 /* transport specific data */
1355 session->ti_type = NC_TI_UNIX;
1356 session->ti.unixsock.sock = sock;
1357 sock = -1; /* do not close sock in fail label anymore */
1358
Robin Jarry4e38e292019-10-15 17:14:14 +02001359 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1360 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001361 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001362 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001363
Michal Vasko93224072021-11-09 12:14:28 +01001364 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001365
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001366 pw = nc_getpwuid(geteuid(), &pw_buf, &buf, &buf_size);
1367 if (!pw) {
1368 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1369 goto fail;
1370 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001371 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001372 free(buf);
1373 if (!username) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001374 ERRMEM;
1375 goto fail;
1376 }
Michal Vasko93224072021-11-09 12:14:28 +01001377 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001378
1379 /* NETCONF handshake */
1380 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1381 goto fail;
1382 }
1383 session->status = NC_STATUS_RUNNING;
1384
1385 if (nc_ctx_check_and_fill(session) == -1) {
1386 goto fail;
1387 }
1388
1389 return session;
1390
1391fail:
1392 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001393 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001394 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001395 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001396 return NULL;
1397}
1398
Frank Rimpler9f838b02018-07-25 06:44:03 +00001399/*
1400 Helper for a non-blocking connect (which is required because of the locking
1401 concept for e.g. call home settings). For more details see nc_sock_connect().
1402 */
1403static int
Michal Vasko5e8d0192019-06-24 19:19:49 +02001404_non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001405{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001406 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001407 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001408 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001409 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001410 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001411 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001412 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001413 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001414
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001415 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001416 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001417 sock = *sock_pending;
1418 } else {
1419 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001420 if (res->ai_family == AF_INET6) {
1421 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001422 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001423 } else {
1424 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001425 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001426 }
1427 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
Michal Vasko05532772021-06-03 12:12:38 +02001428 WRN(NULL, "inet_ntop() failed (%s).", strerror(errno));
Michal Vasko5e8d0192019-06-24 19:19:49 +02001429 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001430 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 +02001431 }
1432
1433 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001434 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1435 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001436 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001437 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001438 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001439 /* make the socket non-blocking */
1440 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001441 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001442 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001443 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001444 /* non-blocking connect! */
1445 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1446 if (errno != EINPROGRESS) {
1447 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001448 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001449 goto cleanup;
1450 }
1451 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001452 }
1453 ts.tv_sec = timeout;
1454 ts.tv_usec = 0;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001455
Frank Rimpler9f838b02018-07-25 06:44:03 +00001456 FD_ZERO(&wset);
1457 FD_SET(sock, &wset);
1458
1459 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001460 ERR(NULL, "select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001461 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001462 }
1463
Michal Vasko5e8d0192019-06-24 19:19:49 +02001464 if (ret == 0) {
1465 /* there was a timeout */
Michal Vasko05532772021-06-03 12:12:38 +02001466 VRB(NULL, "Timed out after %ds (%s).", timeout, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001467 if (sock_pending) {
1468 /* no sock-close, we'll try it again */
1469 *sock_pending = sock;
1470 } else {
1471 close(sock);
1472 }
1473 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001474 }
Radek Krejci782041a2018-08-20 10:09:45 +02001475
1476 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001477 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001478 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001479 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001480 goto cleanup;
1481 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001482 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001483 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001484 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001485 errno = error;
1486 goto cleanup;
1487 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001488
1489 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001490 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001491 goto cleanup;
1492 }
1493
Michal Vasko086311b2016-01-08 09:53:11 +01001494 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001495
Frank Rimpler9f838b02018-07-25 06:44:03 +00001496cleanup:
1497 if (sock_pending) {
1498 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001499 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001500 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001501 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001502}
1503
Frank Rimpler9f838b02018-07-25 06:44:03 +00001504/* A given timeout value limits the time how long the function blocks. If it has to block
1505 only for some seconds, a socket connection might not yet have been fully established.
1506 Therefore the active (pending) socket will be stored in *sock_pending, but the return
1507 value will be -1. In such a case a subsequent invokation is required, by providing the
1508 stored sock_pending, again.
1509 In general, if this function returns -1, when a timeout has been given, this function
1510 has to be invoked, until it returns a valid socket.
1511 */
1512int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001513nc_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 +00001514{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001515 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001516 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001517 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001518 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001519 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001520
Michal Vasko05532772021-06-03 12:12:38 +02001521 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001522
1523 /* no pending socket */
1524 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001525 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001526 snprintf(port_s, 6, "%u", port);
1527 memset(&hints, 0, sizeof hints);
1528 hints.ai_family = AF_UNSPEC;
1529 hints.ai_socktype = SOCK_STREAM;
1530 hints.ai_protocol = IPPROTO_TCP;
1531 i = getaddrinfo(host, port_s, &hints, &res_list);
1532 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001533 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001534 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001535 }
1536
1537 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001538 sock = _non_blocking_connect(timeout, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001539 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001540 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001541 /* try the next resource */
1542 continue;
1543 } else {
1544 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001545 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001546 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001547 }
Michal Vasko05532772021-06-03 12:12:38 +02001548 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 +01001549
1550 opt = 1;
1551 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001552 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001553 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001554 }
1555
Michal Vasko66032bc2019-01-22 15:03:12 +01001556 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1557 buf = malloc(INET6_ADDRSTRLEN);
1558 if (!buf) {
1559 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001560 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001561 }
1562 if (res->ai_family == AF_INET) {
1563 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1564 } else {
1565 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1566 }
1567 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +02001568 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
Michal Vasko66032bc2019-01-22 15:03:12 +01001569 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001570 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001571 }
1572
1573 *ip_host = buf;
1574 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001575 break;
1576 }
1577 freeaddrinfo(res_list);
1578
1579 } else {
1580 /* try to get a connection with the pending socket */
1581 assert(sock_pending);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001582 sock = _non_blocking_connect(timeout, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001583 }
1584
1585 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001586
1587error:
Michal Vasko0be85692021-03-02 08:04:57 +01001588 if (res_list) {
1589 freeaddrinfo(res_list);
1590 }
Michal Vaskocb846632019-12-13 15:12:45 +01001591 if (sock != -1) {
1592 close(sock);
1593 }
1594 if (sock_pending) {
1595 *sock_pending = -1;
1596 }
1597 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001598}
1599
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001600#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001601
Michal Vasko3031aae2016-01-27 16:07:18 +01001602int
1603nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1604{
1605 int sock;
1606
Michal Vasko45e53ae2016-04-07 11:46:03 +02001607 if (!address) {
1608 ERRARG("address");
1609 return -1;
1610 } else if (!port) {
1611 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001612 return -1;
1613 }
1614
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001615 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001616 if (sock == -1) {
1617 return -1;
1618 }
1619
1620 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001621 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1622 if (!client_opts.ch_binds) {
1623 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001624 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001625 return -1;
1626 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001627
Michal Vasko2e6defd2016-10-07 15:48:15 +02001628 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1629 if (!client_opts.ch_bind_ti) {
1630 ERRMEM;
1631 close(sock);
1632 return -1;
1633 }
1634 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1635
Michal Vasko3031aae2016-01-27 16:07:18 +01001636 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001637 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1638 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001639 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001640 return -1;
1641 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001642 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1643 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001644 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001645
1646 return 0;
1647}
1648
1649int
1650nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1651{
1652 uint32_t i;
1653 int ret = -1;
1654
1655 if (!address && !port && !ti) {
1656 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1657 close(client_opts.ch_binds[i].sock);
1658 free((char *)client_opts.ch_binds[i].address);
1659
1660 ret = 0;
1661 }
1662 free(client_opts.ch_binds);
1663 client_opts.ch_binds = NULL;
1664 client_opts.ch_bind_count = 0;
1665 } else {
1666 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001667 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1668 (!port || (client_opts.ch_binds[i].port == port)) &&
1669 (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001670 close(client_opts.ch_binds[i].sock);
1671 free((char *)client_opts.ch_binds[i].address);
1672
1673 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001674 if (!client_opts.ch_bind_count) {
1675 free(client_opts.ch_binds);
1676 client_opts.ch_binds = NULL;
1677 } else if (i < client_opts.ch_bind_count) {
1678 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1679 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1680 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001681
1682 ret = 0;
1683 }
1684 }
1685 }
1686
1687 return ret;
1688}
1689
1690API int
1691nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1692{
1693 int sock;
1694 char *host = NULL;
1695 uint16_t port, idx;
1696
Michal Vasko45e53ae2016-04-07 11:46:03 +02001697 if (!client_opts.ch_binds) {
1698 ERRINIT;
1699 return -1;
1700 } else if (!session) {
1701 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001702 return -1;
1703 }
1704
1705 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1706
Michal Vasko50456e82016-02-02 12:16:08 +01001707 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001708 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001709 return sock;
1710 }
1711
Radek Krejci53691be2016-02-22 13:58:37 +01001712#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001713 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001714 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001715 } else
1716#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001717#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001718 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001719 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001720 } else
1721#endif
1722 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001723 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001724 *session = NULL;
1725 }
1726
1727 free(host);
1728
1729 if (!(*session)) {
1730 return -1;
1731 }
1732
1733 return 1;
1734}
1735
Radek Krejci53691be2016-02-22 13:58:37 +01001736#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001737
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001738API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001739nc_session_get_cpblts(const struct nc_session *session)
1740{
1741 if (!session) {
1742 ERRARG("session");
1743 return NULL;
1744 }
1745
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001746 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001747}
1748
1749API const char *
1750nc_session_cpblt(const struct nc_session *session, const char *capab)
1751{
1752 int i, len;
1753
1754 if (!session) {
1755 ERRARG("session");
1756 return NULL;
1757 } else if (!capab) {
1758 ERRARG("capab");
1759 return NULL;
1760 }
1761
1762 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001763 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1764 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1765 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001766 }
1767 }
1768
1769 return NULL;
1770}
1771
Michal Vasko9cd26a82016-05-31 08:58:48 +02001772API int
1773nc_session_ntf_thread_running(const struct nc_session *session)
1774{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001775 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001776 ERRARG("session");
1777 return 0;
1778 }
1779
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001780 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001781}
1782
Michal Vaskob7558c52016-02-26 15:04:19 +01001783API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001784nc_client_init(void)
1785{
1786 nc_init();
1787}
1788
1789API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001790nc_client_destroy(void)
1791{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001792 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001793#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001794 nc_client_ch_del_bind(NULL, 0, 0);
1795#endif
1796#ifdef NC_ENABLED_SSH
1797 nc_client_ssh_destroy_opts();
1798#endif
1799#ifdef NC_ENABLED_TLS
1800 nc_client_tls_destroy_opts();
1801#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001802 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001803}
1804
Michal Vasko77367452021-02-16 16:32:18 +01001805static NC_MSG_TYPE
1806recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001807{
Michal Vasko77367452021-02-16 16:32:18 +01001808 char *ptr;
1809 struct lyd_attr *attr;
1810 uint64_t cur_msgid;
1811
1812 assert(envp && !envp->schema);
1813
1814 /* find the message-id attribute */
1815 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1816 if (!strcmp(attr->name.name, "message-id")) {
1817 break;
1818 }
1819 }
1820
1821 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001822 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001823 return NC_MSG_REPLY_ERR_MSGID;
1824 }
1825
1826 cur_msgid = strtoul(attr->value, &ptr, 10);
1827 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001828 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1829 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001830 return NC_MSG_REPLY_ERR_MSGID;
1831 }
1832
1833 return NC_MSG_REPLY;
1834}
1835
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001836/**
1837 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1838 *
1839 * @param[in] session NETCONF session used to send error messages.
1840 * @param[in] msg Message to check for type.
1841 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1842 * @return NC_MSG_NOTIF If format roughly matches a notification;
1843 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1844 */
1845static NC_MSG_TYPE
1846get_msg_type(struct nc_session *session, struct ly_in *msg)
1847{
1848 const char *str, *end;
1849
1850 str = ly_in_memory(msg, NULL);
1851
1852 while (*str) {
1853 /* Skip whitespaces */
1854 while (isspace(*str)) {
1855 str++;
1856 }
1857
1858 if (*str == '<') {
1859 str++;
1860 if (!strncmp(str, "!--", 3)) {
1861 /* Skip comments */
1862 end = "-->";
1863 str = strstr(str, end);
1864 } else if (!strncmp(str, "?xml", 4)) {
1865 /* Skip xml declaration */
1866 end = "?>";
1867 str = strstr(str, end);
1868 } else if (!strncmp(str, "rpc-reply", 9)) {
1869 return NC_MSG_REPLY;
1870 } else if (!strncmp(str, "notification", 12)) {
1871 return NC_MSG_NOTIF;
1872 } else {
1873 ERR(session, "Unknown xml element '%.10s'.", str);
1874 return NC_MSG_ERROR;
1875 }
1876 if (!str) {
1877 /* No matching ending tag found */
1878 ERR(session, "No matching ending tag '%s' found in xml message.", end);
1879 return NC_MSG_ERROR;
1880 }
1881 str += strlen(end);
1882 } else {
1883 /* Not a valid xml */
1884 ERR(session, "Unexpected character '%c' in xml message.", *str);
1885 return NC_MSG_ERROR;
1886 }
1887 }
1888
1889 /* Unexpected end of message */
1890 ERR(session, "Unexpected end of xml message.");
1891 return NC_MSG_ERROR;
1892}
1893
1894/**
1895 * @brief Function to receive either replies or notifications.
1896 *
1897 * @param[in] session NETCONF session from which this function receives messages.
1898 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
1899 * @param[in] expected Type of the message the caller desired.
1900 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
1901 * @return NC_MSG_REPLY If a rpc-reply was received;
1902 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01001903 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001904 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
1905 */
1906static NC_MSG_TYPE
1907recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
1908{
1909 struct nc_msg_cont **cont_ptr;
1910 struct ly_in *msg = NULL;
1911 struct nc_msg_cont *cont, *prev;
1912 NC_MSG_TYPE ret = NC_MSG_ERROR;
1913 int r;
1914
1915 *message = NULL;
1916
1917 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001918 r = nc_session_client_msgs_lock(session, &timeout, __func__);
1919 if (!r) {
1920 ret = NC_MSG_WOULDBLOCK;
1921 goto cleanup;
1922 } else if (r == -1) {
1923 ret = NC_MSG_ERROR;
1924 goto cleanup;
1925 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001926
1927 /* Find the expected message in the buffer */
1928 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001929 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001930 prev = cont;
1931 }
1932
1933 if (cont) {
1934 /* Remove found message from buffer */
1935 if (prev) {
1936 prev->next = cont->next;
1937 } else {
1938 session->opts.client.msgs = cont->next;
1939 }
1940
1941 /* Use the buffer message */
1942 ret = cont->type;
1943 msg = cont->msg;
1944 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02001945 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001946 }
1947
1948 /* Read a message from the wire */
1949 r = nc_read_msg_poll_io(session, timeout, &msg);
1950 if (!r) {
1951 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02001952 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001953 } else if (r == -1) {
1954 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001955 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001956 }
1957
1958 /* Basic check to determine message type */
1959 ret = get_msg_type(session, msg);
1960 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02001961 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001962 }
1963
1964 /* If received a message of different type store it in the buffer */
1965 if (ret != expected) {
1966 cont_ptr = &session->opts.client.msgs;
1967 while (*cont_ptr) {
1968 cont_ptr = &((*cont_ptr)->next);
1969 }
1970 *cont_ptr = malloc(sizeof **cont_ptr);
1971 if (!*cont_ptr) {
1972 ERRMEM;
1973 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001974 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001975 }
1976 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001977 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001978 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001979 (*cont_ptr)->next = NULL;
1980 }
1981
Michal Vasko01130bd2021-08-26 11:47:38 +02001982cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001983 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001984 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001985
Michal Vasko01130bd2021-08-26 11:47:38 +02001986cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001987 if (ret == expected) {
1988 *message = msg;
1989 } else {
1990 ly_in_free(msg, 1);
1991 }
1992 return ret;
1993}
1994
Michal Vasko77367452021-02-16 16:32:18 +01001995static NC_MSG_TYPE
1996recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
1997{
Michal Vasko77367452021-02-16 16:32:18 +01001998 LY_ERR lyrc;
1999 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002000 NC_MSG_TYPE ret = NC_MSG_ERROR;
2001
2002 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
2003
2004 *envp = NULL;
2005
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002006 /* Receive messages until a rpc-reply is found or a timeout or error reached */
2007 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
2008 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01002009 goto cleanup;
2010 }
2011
2012 /* parse */
2013 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
2014 if (!lyrc) {
2015 ret = recv_reply_check_msgid(session, *envp, msgid);
2016 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002017 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002018 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
Michal Vasko9a108052022-04-01 12:06:54 +02002019 lyd_free_tree(*envp);
2020 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002021 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002022 goto cleanup;
2023 }
2024
Michal Vasko77367452021-02-16 16:32:18 +01002025cleanup:
2026 ly_in_free(msg, 1);
2027 return ret;
2028}
2029
2030static int
2031recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2032{
Michal Vasko143aa142021-10-01 15:31:48 +02002033 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002034 struct nc_rpc_act_generic *rpc_gen;
2035 struct ly_in *in;
2036 struct lyd_node *tree, *op2;
2037 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002038 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002039
2040 switch (rpc->type) {
2041 case NC_RPC_ACT_GENERIC:
2042 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2043 if (rpc_gen->has_data) {
2044 tree = rpc_gen->content.data;
2045
2046 /* find the operation node */
2047 lyrc = LY_EINVAL;
2048 LYD_TREE_DFS_BEGIN(tree, op2) {
2049 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2050 lyrc = lyd_dup_single(op2, NULL, 0, op);
2051 break;
2052 }
2053 LYD_TREE_DFS_END(tree, op2);
2054 }
2055 } else {
2056 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2057 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2058 ly_in_free(in, 0);
2059 if (lyrc) {
Michal Vasko9a108052022-04-01 12:06:54 +02002060 lyd_free_tree(tree);
Michal Vasko77367452021-02-16 16:32:18 +01002061 return -1;
2062 }
2063
2064 /* we want just the operation node */
2065 lyrc = lyd_dup_single(op2, NULL, 0, op);
2066
2067 lyd_free_tree(tree);
2068 }
2069 break;
2070 case NC_RPC_GETCONFIG:
2071 module_name = "ietf-netconf";
2072 rpc_name = "get-config";
2073 break;
2074 case NC_RPC_EDIT:
2075 module_name = "ietf-netconf";
2076 rpc_name = "edit-config";
2077 break;
2078 case NC_RPC_COPY:
2079 module_name = "ietf-netconf";
2080 rpc_name = "copy-config";
2081 break;
2082 case NC_RPC_DELETE:
2083 module_name = "ietf-netconf";
2084 rpc_name = "delete-config";
2085 break;
2086 case NC_RPC_LOCK:
2087 module_name = "ietf-netconf";
2088 rpc_name = "lock";
2089 break;
2090 case NC_RPC_UNLOCK:
2091 module_name = "ietf-netconf";
2092 rpc_name = "unlock";
2093 break;
2094 case NC_RPC_GET:
2095 module_name = "ietf-netconf";
2096 rpc_name = "get";
2097 break;
2098 case NC_RPC_KILL:
2099 module_name = "ietf-netconf";
2100 rpc_name = "kill-session";
2101 break;
2102 case NC_RPC_COMMIT:
2103 module_name = "ietf-netconf";
2104 rpc_name = "commit";
2105 break;
2106 case NC_RPC_DISCARD:
2107 module_name = "ietf-netconf";
2108 rpc_name = "discard-changes";
2109 break;
2110 case NC_RPC_CANCEL:
2111 module_name = "ietf-netconf";
2112 rpc_name = "cancel-commit";
2113 break;
2114 case NC_RPC_VALIDATE:
2115 module_name = "ietf-netconf";
2116 rpc_name = "validate";
2117 break;
2118 case NC_RPC_GETSCHEMA:
2119 module_name = "ietf-netconf-monitoring";
2120 rpc_name = "get-schema";
2121 break;
2122 case NC_RPC_SUBSCRIBE:
2123 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002124 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002125 break;
2126 case NC_RPC_GETDATA:
2127 module_name = "ietf-netconf-nmda";
2128 rpc_name = "get-data";
2129 break;
2130 case NC_RPC_EDITDATA:
2131 module_name = "ietf-netconf-nmda";
2132 rpc_name = "edit-data";
2133 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002134 case NC_RPC_ESTABLISHSUB:
2135 module_name = "ietf-subscribed-notifications";
2136 rpc_name = "establish-subscription";
2137 break;
2138 case NC_RPC_MODIFYSUB:
2139 module_name = "ietf-subscribed-notifications";
2140 rpc_name = "modify-subscription";
2141 break;
2142 case NC_RPC_DELETESUB:
2143 module_name = "ietf-subscribed-notifications";
2144 rpc_name = "delete-subscription";
2145 break;
2146 case NC_RPC_KILLSUB:
2147 module_name = "ietf-subscribed-notifications";
2148 rpc_name = "kill-subscription";
2149 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002150 case NC_RPC_ESTABLISHPUSH:
2151 module_name = "ietf-subscribed-notifications";
2152 rpc_name = "establish-subscription";
2153 module_check = "ietf-yang-push";
2154 break;
2155 case NC_RPC_MODIFYPUSH:
2156 module_name = "ietf-subscribed-notifications";
2157 rpc_name = "modify-subscription";
2158 module_check = "ietf-yang-push";
2159 break;
2160 case NC_RPC_RESYNCSUB:
2161 module_name = "ietf-yang-push";
2162 rpc_name = "resync-subscription";
2163 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002164 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002165 lyrc = LY_EINT;
2166 break;
2167 }
2168
2169 if (module_name && rpc_name) {
2170 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2171 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002172 ERR(session, "Missing \"%s\" schema in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002173 return -1;
2174 }
2175
2176 /* create the operation node */
2177 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2178 }
Michal Vasko305faca2021-03-25 09:16:02 +01002179 if (module_check) {
2180 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko05532772021-06-03 12:12:38 +02002181 ERR(session, "Missing \"%s\" schema in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002182 return -1;
2183 }
2184 }
Michal Vasko77367452021-02-16 16:32:18 +01002185
2186 if (lyrc) {
2187 return -1;
2188 }
2189 return 0;
2190}
2191
2192API NC_MSG_TYPE
2193nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2194 struct lyd_node **op)
2195{
2196 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002197
Michal Vasko45e53ae2016-04-07 11:46:03 +02002198 if (!session) {
2199 ERRARG("session");
2200 return NC_MSG_ERROR;
2201 } else if (!rpc) {
2202 ERRARG("rpc");
2203 return NC_MSG_ERROR;
Michal Vasko5a2c7162020-01-30 11:24:17 +01002204 } else if (!msgid) {
2205 ERRARG("msgid");
2206 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002207 } else if (!envp) {
2208 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02002209 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002210 } else if (!op) {
2211 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002212 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002213 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002214 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002215 return NC_MSG_ERROR;
2216 }
Michal Vasko77367452021-02-16 16:32:18 +01002217
2218 /* get a duplicate of the RPC node to append reply to */
2219 if (recv_reply_dup_rpc(session, rpc, op)) {
2220 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002221 }
Michal Vasko086311b2016-01-08 09:53:11 +01002222
Michal Vasko77367452021-02-16 16:32:18 +01002223 /* receive a reply */
2224 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002225
Michal Vasko77367452021-02-16 16:32:18 +01002226 /* do not return the RPC copy on error or if the reply includes no data */
2227 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2228 lyd_free_tree(*op);
2229 *op = NULL;
2230 }
2231 return ret;
2232}
2233
2234static NC_MSG_TYPE
2235recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2236{
Michal Vasko77367452021-02-16 16:32:18 +01002237 LY_ERR lyrc;
2238 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002239 NC_MSG_TYPE ret = NC_MSG_ERROR;
2240
2241 *op = NULL;
2242 *envp = NULL;
2243
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002244 /* Receive messages until a notification is found or a timeout or error reached */
2245 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2246 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002247 goto cleanup;
2248 }
2249
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002250 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002251 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2252 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002253 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002254 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002255 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
Michal Vasko9a108052022-04-01 12:06:54 +02002256 lyd_free_tree(*envp);
2257 *envp = NULL;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002258 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002259 goto cleanup;
2260 }
2261
Michal Vasko77367452021-02-16 16:32:18 +01002262cleanup:
2263 ly_in_free(msg, 1);
2264 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002265}
2266
2267API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002268nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002269{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002270 if (!session) {
2271 ERRARG("session");
2272 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002273 } else if (!envp) {
2274 ERRARG("envp");
2275 return NC_MSG_ERROR;
2276 } else if (!op) {
2277 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002278 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002279 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002280 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002281 return NC_MSG_ERROR;
2282 }
2283
Michal Vasko77367452021-02-16 16:32:18 +01002284 /* receive a notification */
2285 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002286}
2287
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002288static void *
2289nc_recv_notif_thread(void *arg)
2290{
2291 struct nc_ntf_thread_arg *ntarg;
2292 struct nc_session *session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002293
Michal Vasko77367452021-02-16 16:32:18 +01002294 void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
2295 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002296 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002297
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002298 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002299 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002300
2301 ntarg = (struct nc_ntf_thread_arg *)arg;
2302 session = ntarg->session;
2303 notif_clb = ntarg->notif_clb;
2304 free(ntarg);
2305
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002306 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) == 1) {
Michal Vasko77367452021-02-16 16:32:18 +01002307 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002308 if (msgtype == NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002309 notif_clb(session, envp, op);
2310 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2311 lyd_free_tree(envp);
2312 lyd_free_tree(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002313 break;
2314 }
Michal Vasko77367452021-02-16 16:32:18 +01002315 lyd_free_tree(envp);
2316 lyd_free_tree(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002317 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002318 /* quit this thread once the session is broken */
2319 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002320 }
2321
2322 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2323 }
2324
Michal Vasko05532772021-06-03 12:12:38 +02002325 VRB(session, "Notification thread exit.");
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002326 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002327 return NULL;
2328}
2329
2330API int
Michal Vasko77367452021-02-16 16:32:18 +01002331nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session,
2332 const struct lyd_node *envp, const struct lyd_node *op))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002333{
2334 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002335 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002336 int ret;
2337
Michal Vasko45e53ae2016-04-07 11:46:03 +02002338 if (!session) {
2339 ERRARG("session");
2340 return -1;
2341 } else if (!notif_clb) {
2342 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002343 return -1;
2344 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002345 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002346 return -1;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002347 } else if (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread)) {
Michal Vasko05532772021-06-03 12:12:38 +02002348 ERR(session, "Separate notification thread is already running.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002349 return -1;
2350 }
2351
2352 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002353 if (!ntarg) {
2354 ERRMEM;
2355 return -1;
2356 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002357 ntarg->session = session;
2358 ntarg->notif_clb = notif_clb;
2359
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002360 /* just so that nc_recv_notif_thread() does not immediately exit */
2361 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002362
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002363 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002364 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002365 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002366 free(ntarg);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002367 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002368 return -1;
2369 }
2370
2371 return 0;
2372}
2373
Michal Vasko77367452021-02-16 16:32:18 +01002374static const char *
2375nc_wd2str(NC_WD_MODE wd)
2376{
2377 switch (wd) {
2378 case NC_WD_ALL:
2379 return "report-all";
2380 case NC_WD_ALL_TAG:
2381 return "report-all-tagged";
2382 case NC_WD_TRIM:
2383 return "trim";
2384 case NC_WD_EXPLICIT:
2385 return "explicit";
2386 default:
2387 break;
2388 }
2389
2390 return NULL;
2391}
2392
Michal Vasko086311b2016-01-08 09:53:11 +01002393API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002394nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002395{
2396 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002397 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002398 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002399 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002400 struct nc_rpc_getconfig *rpc_gc;
2401 struct nc_rpc_edit *rpc_e;
2402 struct nc_rpc_copy *rpc_cp;
2403 struct nc_rpc_delete *rpc_del;
2404 struct nc_rpc_lock *rpc_lock;
2405 struct nc_rpc_get *rpc_g;
2406 struct nc_rpc_kill *rpc_k;
2407 struct nc_rpc_commit *rpc_com;
2408 struct nc_rpc_cancel *rpc_can;
2409 struct nc_rpc_validate *rpc_val;
2410 struct nc_rpc_getschema *rpc_gs;
2411 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002412 struct nc_rpc_getdata *rpc_getd;
2413 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002414 struct nc_rpc_establishsub *rpc_estsub;
2415 struct nc_rpc_modifysub *rpc_modsub;
2416 struct nc_rpc_deletesub *rpc_delsub;
2417 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002418 struct nc_rpc_establishpush *rpc_estpush;
2419 struct nc_rpc_modifypush *rpc_modpush;
2420 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002421 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002422 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002423 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002424 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002425 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002426 uint64_t cur_msgid;
2427
Michal Vasko45e53ae2016-04-07 11:46:03 +02002428 if (!session) {
2429 ERRARG("session");
2430 return NC_MSG_ERROR;
2431 } else if (!rpc) {
2432 ERRARG("rpc");
2433 return NC_MSG_ERROR;
2434 } else if (!msgid) {
2435 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002436 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002437 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002438 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002439 return NC_MSG_ERROR;
2440 }
2441
Michal Vaskoc1171a42019-11-05 12:06:46 +01002442 switch (rpc->type) {
2443 case NC_RPC_ACT_GENERIC:
2444 /* checked when parsing */
2445 break;
2446 case NC_RPC_GETCONFIG:
2447 case NC_RPC_EDIT:
2448 case NC_RPC_COPY:
2449 case NC_RPC_DELETE:
2450 case NC_RPC_LOCK:
2451 case NC_RPC_UNLOCK:
2452 case NC_RPC_GET:
2453 case NC_RPC_KILL:
2454 case NC_RPC_COMMIT:
2455 case NC_RPC_DISCARD:
2456 case NC_RPC_CANCEL:
2457 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002458 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002459 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002460 ERR(session, "Missing \"ietf-netconf\" schema in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002461 return NC_MSG_ERROR;
2462 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002463 break;
2464 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002465 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002466 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002467 ERR(session, "Missing \"ietf-netconf-monitoring\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002468 return NC_MSG_ERROR;
2469 }
2470 break;
2471 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002472 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002473 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002474 ERR(session, "Missing \"notifications\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002475 return NC_MSG_ERROR;
2476 }
2477 break;
2478 case NC_RPC_GETDATA:
2479 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002480 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002481 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002482 ERR(session, "Missing \"ietf-netconf-nmda\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002483 return NC_MSG_ERROR;
2484 }
2485 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002486 case NC_RPC_ESTABLISHSUB:
2487 case NC_RPC_MODIFYSUB:
2488 case NC_RPC_DELETESUB:
2489 case NC_RPC_KILLSUB:
2490 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2491 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002492 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002493 return NC_MSG_ERROR;
2494 }
2495 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002496 case NC_RPC_ESTABLISHPUSH:
2497 case NC_RPC_MODIFYPUSH:
2498 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2499 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002500 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002501 return NC_MSG_ERROR;
2502 }
2503 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2504 if (!mod2) {
Michal Vasko05532772021-06-03 12:12:38 +02002505 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002506 return NC_MSG_ERROR;
2507 }
2508 break;
2509 case NC_RPC_RESYNCSUB:
2510 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2511 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002512 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002513 return NC_MSG_ERROR;
2514 }
2515 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002516 case NC_RPC_UNKNOWN:
2517 ERRINT;
2518 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002519 }
2520
Michal Vaskoab9deb62021-05-27 11:37:00 +02002521#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2522
Michal Vasko086311b2016-01-08 09:53:11 +01002523 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002524 case NC_RPC_ACT_GENERIC:
2525 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002526
2527 if (rpc_gen->has_data) {
2528 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002529 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002530 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002531 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2532 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2533 ly_in_free(in, 0);
2534 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002535 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002536 }
Michal Vasko086311b2016-01-08 09:53:11 +01002537 }
2538 break;
2539
2540 case NC_RPC_GETCONFIG:
2541 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2542
Michal Vaskoab9deb62021-05-27 11:37:00 +02002543 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2544 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2545 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002546 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002547 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002548 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2549 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002550 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002551 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2552 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2553 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002554 }
2555 }
2556
2557 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002558 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002559 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002560 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002561 lyrc = LY_ENOTFOUND;
2562 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002563 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002564 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 +01002565 }
2566 break;
2567
2568 case NC_RPC_EDIT:
2569 rpc_e = (struct nc_rpc_edit *)rpc;
2570
Michal Vaskoab9deb62021-05-27 11:37:00 +02002571 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2572 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2573 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002574
2575 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002576 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 +01002577 }
Michal Vasko086311b2016-01-08 09:53:11 +01002578 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002579 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 +01002580 }
Michal Vasko086311b2016-01-08 09:53:11 +01002581 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002582 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 +01002583 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002584 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002585 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 +01002586 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002587 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002588 }
2589 break;
2590
2591 case NC_RPC_COPY:
2592 rpc_cp = (struct nc_rpc_copy *)rpc;
2593
Michal Vaskoab9deb62021-05-27 11:37:00 +02002594 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2595 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002596 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002597 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002598 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002599 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002600 }
2601
Michal Vaskoab9deb62021-05-27 11:37:00 +02002602 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002603 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002604 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002605 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 +01002606 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002607 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002608 }
2609 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002610 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002611 }
2612
2613 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002614 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002615 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002616 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002617 lyrc = LY_ENOTFOUND;
2618 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002619 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002620 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 +01002621 }
2622 break;
2623
2624 case NC_RPC_DELETE:
2625 rpc_del = (struct nc_rpc_delete *)rpc;
2626
Michal Vaskoab9deb62021-05-27 11:37:00 +02002627 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2628 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002629 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002630 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002631 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002632 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002633 }
2634 break;
2635
2636 case NC_RPC_LOCK:
2637 rpc_lock = (struct nc_rpc_lock *)rpc;
2638
Michal Vaskoab9deb62021-05-27 11:37:00 +02002639 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2640 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2641 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002642 break;
2643
2644 case NC_RPC_UNLOCK:
2645 rpc_lock = (struct nc_rpc_lock *)rpc;
2646
Michal Vaskoab9deb62021-05-27 11:37:00 +02002647 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2648 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2649 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002650 break;
2651
2652 case NC_RPC_GET:
2653 rpc_g = (struct nc_rpc_get *)rpc;
2654
Michal Vaskoab9deb62021-05-27 11:37:00 +02002655 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002656 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002657 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002658 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2659 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002660 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002661 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2662 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2663 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002664 }
2665 }
2666
2667 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002668 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002669 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002670 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002671 lyrc = LY_ENOTFOUND;
2672 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002673 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002674 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 +01002675 }
2676 break;
2677
2678 case NC_RPC_KILL:
2679 rpc_k = (struct nc_rpc_kill *)rpc;
2680
Michal Vaskoab9deb62021-05-27 11:37:00 +02002681 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002682 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002683 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002684 break;
2685
2686 case NC_RPC_COMMIT:
2687 rpc_com = (struct nc_rpc_commit *)rpc;
2688
Michal Vaskoab9deb62021-05-27 11:37:00 +02002689 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002690 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002691 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002692 }
2693
2694 if (rpc_com->confirm_timeout) {
2695 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002696 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002697 }
Michal Vasko086311b2016-01-08 09:53:11 +01002698 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002699 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002700 }
Michal Vasko086311b2016-01-08 09:53:11 +01002701 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002702 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002703 }
2704 break;
2705
2706 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002707 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002708 break;
2709
2710 case NC_RPC_CANCEL:
2711 rpc_can = (struct nc_rpc_cancel *)rpc;
2712
Michal Vaskoab9deb62021-05-27 11:37:00 +02002713 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002714 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002715 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002716 }
2717 break;
2718
2719 case NC_RPC_VALIDATE:
2720 rpc_val = (struct nc_rpc_validate *)rpc;
2721
Michal Vaskoab9deb62021-05-27 11:37:00 +02002722 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2723 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002724 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002725 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002726 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 +01002727 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002728 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002729 }
2730 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002731 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002732 }
2733 break;
2734
2735 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002736 rpc_gs = (struct nc_rpc_getschema *)rpc;
2737
Michal Vaskoab9deb62021-05-27 11:37:00 +02002738 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2739 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002740 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002741 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002742 }
2743 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002744 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002745 }
2746 break;
2747
2748 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002749 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2750
Michal Vaskoab9deb62021-05-27 11:37:00 +02002751 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002752 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002753 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002754 }
2755
2756 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002757 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002758 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2759 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002760 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002761 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2762 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2763 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002764 }
2765 }
Michal Vasko086311b2016-01-08 09:53:11 +01002766 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002767 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002768 }
Michal Vasko086311b2016-01-08 09:53:11 +01002769 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002770 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002771 }
2772 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002773
2774 case NC_RPC_GETDATA:
2775 rpc_getd = (struct nc_rpc_getdata *)rpc;
2776
Michal Vaskoab9deb62021-05-27 11:37:00 +02002777 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2778 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2779
Michal Vaskoc1171a42019-11-05 12:06:46 +01002780 if (rpc_getd->filter) {
2781 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002782 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 +01002783 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002784 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002785 }
2786 }
2787 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002788 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002789 }
2790 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002791 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2792 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002793 }
2794 if (rpc_getd->max_depth) {
2795 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002796 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002797 }
2798 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002799 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002800 }
2801
2802 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002803 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002804 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 +01002805 }
2806 break;
2807
2808 case NC_RPC_EDITDATA:
2809 rpc_editd = (struct nc_rpc_editdata *)rpc;
2810
Michal Vaskoab9deb62021-05-27 11:37:00 +02002811 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2812 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002813
2814 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002815 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2816 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002817 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002818 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002819 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 +01002820 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002821 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002822 }
2823 break;
2824
Michal Vasko96f247a2021-03-15 13:32:10 +01002825 case NC_RPC_ESTABLISHSUB:
2826 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2827
Michal Vaskoab9deb62021-05-27 11:37:00 +02002828 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002829
2830 if (rpc_estsub->filter) {
2831 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002832 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2833 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002834 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002835 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002836 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002837 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002838 }
2839 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002840 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002841
2842 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002843 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002844 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002845 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002846 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002847 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002848 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002849 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002850 }
2851 break;
2852
2853 case NC_RPC_MODIFYSUB:
2854 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2855
Michal Vaskoab9deb62021-05-27 11:37:00 +02002856 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002857
2858 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002859 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002860
2861 if (rpc_modsub->filter) {
2862 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002863 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
2864 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002865 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002866 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002867 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002868 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002869 }
2870 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002871 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002872 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002873 }
2874 break;
2875
2876 case NC_RPC_DELETESUB:
2877 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2878
Michal Vaskoab9deb62021-05-27 11:37:00 +02002879 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002880
2881 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002882 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002883 break;
2884
2885 case NC_RPC_KILLSUB:
2886 rpc_killsub = (struct nc_rpc_killsub *)rpc;
2887
Michal Vaskoab9deb62021-05-27 11:37:00 +02002888 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002889
2890 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002891 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002892 break;
2893
Michal Vasko305faca2021-03-25 09:16:02 +01002894 case NC_RPC_ESTABLISHPUSH:
2895 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
2896
Michal Vaskoab9deb62021-05-27 11:37:00 +02002897 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
2898 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002899
2900 if (rpc_estpush->filter) {
2901 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002902 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
2903 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002904 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002905 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002906 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002907 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002908 }
2909 }
2910
2911 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002912 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002913 }
Michal Vasko305faca2021-03-25 09:16:02 +01002914 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002915 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002916 }
2917
2918 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002919 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002920 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002921 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002922 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002923 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002924 }
2925 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002926 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002927 if (rpc_estpush->dampening_period) {
2928 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002929 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002930 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002931 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
2932 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002933 if (rpc_estpush->excluded_change) {
2934 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002935 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
2936 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002937 }
2938 }
2939 }
2940 break;
2941
2942 case NC_RPC_MODIFYPUSH:
2943 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
2944
Michal Vaskoab9deb62021-05-27 11:37:00 +02002945 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002946
2947 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002948 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
2949 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002950
2951 if (rpc_modpush->filter) {
2952 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002953 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
2954 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002955 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002956 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002957 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002958 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002959 }
2960 }
Michal Vasko305faca2021-03-25 09:16:02 +01002961 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002962 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002963 }
2964
2965 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002966 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002967 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002968 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002969 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002970 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002971 }
2972 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002973 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002974 if (rpc_modpush->dampening_period) {
2975 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002976 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002977 }
2978 }
2979 break;
2980
2981 case NC_RPC_RESYNCSUB:
2982 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
2983
Michal Vaskoab9deb62021-05-27 11:37:00 +02002984 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002985 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002986 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002987 break;
2988
Michal Vasko96f247a2021-03-15 13:32:10 +01002989 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002990 ERRINT;
2991 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002992 }
2993
Michal Vaskoab9deb62021-05-27 11:37:00 +02002994#undef CHECK_LYRC_BREAK
2995
2996 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02002997 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002998 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02002999 return NC_MSG_ERROR;
3000 }
3001
Michal Vasko131120a2018-05-29 15:44:02 +02003002 /* send RPC, store its message ID */
3003 r = nc_send_msg_io(session, timeout, data);
3004 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003005
Radek Krejcib4b19062018-02-07 16:33:06 +01003006 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003007 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003008 }
Michal Vasko086311b2016-01-08 09:53:11 +01003009
Michal Vasko131120a2018-05-29 15:44:02 +02003010 if (r == NC_MSG_RPC) {
3011 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003012 }
Michal Vasko131120a2018-05-29 15:44:02 +02003013 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003014}
Michal Vaskode2946c2017-01-12 12:19:26 +01003015
3016API void
3017nc_client_session_set_not_strict(struct nc_session *session)
3018{
3019 if (session->side != NC_CLIENT) {
3020 ERRARG("session");
3021 return;
3022 }
3023
3024 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3025}