blob: 41f9a03634d96dbd0b43c25b204d33dab8f41204 [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
Radek Krejci65ef6d52018-08-16 16:35:02 +02001230 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1231 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001232 }
1233
Radek Krejcifd5b6682017-06-13 15:52:53 +02001234 /* succsess */
1235 ret = 0;
1236
Michal Vaskoeee99412016-11-21 10:19:43 +01001237 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko05532772021-06-03 12:12:38 +02001238 WRN(session, "Some models failed to be loaded, any data from these models (and any other unknown) will "
1239 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001240 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001241
1242cleanup:
Radek Krejci65ef6d52018-08-16 16:35:02 +02001243 free_schema_info(server_modules);
1244
Radek Krejcifd5b6682017-06-13 15:52:53 +02001245 /* set user callback back */
1246 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001247 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001248
Michal Vaskoef578332016-01-25 13:20:09 +01001249 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001250}
1251
1252API struct nc_session *
1253nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1254{
Michal Vaskod083db62016-01-19 10:31:29 +01001255 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001256
Michal Vasko45e53ae2016-04-07 11:46:03 +02001257 if (fdin < 0) {
1258 ERRARG("fdin");
1259 return NULL;
1260 } else if (fdout < 0) {
1261 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001262 return NULL;
1263 }
1264
1265 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001266 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001267 if (!session) {
1268 ERRMEM;
1269 return NULL;
1270 }
1271 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001272
1273 /* transport specific data */
1274 session->ti_type = NC_TI_FD;
1275 session->ti.fd.in = fdin;
1276 session->ti.fd.out = fdout;
1277
Robin Jarry4e38e292019-10-15 17:14:14 +02001278 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1279 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001280 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001281 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001282
1283 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001284 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001285 goto fail;
1286 }
1287 session->status = NC_STATUS_RUNNING;
1288
Michal Vaskoef578332016-01-25 13:20:09 +01001289 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001290 goto fail;
1291 }
1292
1293 return session;
1294
1295fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001296 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001297 return NULL;
1298}
1299
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001300API struct nc_session *
1301nc_connect_unix(const char *address, struct ly_ctx *ctx)
1302{
1303 struct nc_session *session = NULL;
1304 struct sockaddr_un sun;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001305 struct passwd *pw, pw_buf;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001306 char *username;
1307 int sock = -1;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001308 char *buf = NULL;
1309 size_t buf_size = 0;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001310
1311 if (address == NULL) {
1312 ERRARG("address");
1313 return NULL;
1314 }
1315
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001316 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1317 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001318 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001319 goto fail;
1320 }
1321
1322 memset(&sun, 0, sizeof(sun));
1323 sun.sun_family = AF_UNIX;
1324 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1325
1326 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001327 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001328 goto fail;
1329 }
1330
1331 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001332 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001333 goto fail;
1334 }
1335
1336 /* prepare session structure */
1337 session = nc_new_session(NC_CLIENT, 0);
1338 if (!session) {
1339 ERRMEM;
1340 goto fail;
1341 }
1342 session->status = NC_STATUS_STARTING;
1343
1344 /* transport specific data */
1345 session->ti_type = NC_TI_UNIX;
1346 session->ti.unixsock.sock = sock;
1347 sock = -1; /* do not close sock in fail label anymore */
1348
Robin Jarry4e38e292019-10-15 17:14:14 +02001349 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1350 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001351 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001352 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001353
Michal Vasko93224072021-11-09 12:14:28 +01001354 session->path = strdup(address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001355
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001356 pw = nc_getpwuid(geteuid(), &pw_buf, &buf, &buf_size);
1357 if (!pw) {
1358 ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid());
1359 goto fail;
1360 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001361 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001362 free(buf);
1363 if (!username) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001364 ERRMEM;
1365 goto fail;
1366 }
Michal Vasko93224072021-11-09 12:14:28 +01001367 session->username = username;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001368
1369 /* NETCONF handshake */
1370 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1371 goto fail;
1372 }
1373 session->status = NC_STATUS_RUNNING;
1374
1375 if (nc_ctx_check_and_fill(session) == -1) {
1376 goto fail;
1377 }
1378
1379 return session;
1380
1381fail:
1382 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001383 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001384 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001385 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001386 return NULL;
1387}
1388
Frank Rimpler9f838b02018-07-25 06:44:03 +00001389/*
1390 Helper for a non-blocking connect (which is required because of the locking
1391 concept for e.g. call home settings). For more details see nc_sock_connect().
1392 */
1393static int
Michal Vasko5e8d0192019-06-24 19:19:49 +02001394_non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001395{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001396 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001397 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001398 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001399 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001400 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001401 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001402 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001403 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001404
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001405 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001406 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001407 sock = *sock_pending;
1408 } else {
1409 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001410 if (res->ai_family == AF_INET6) {
1411 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001412 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001413 } else {
1414 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001415 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001416 }
1417 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
Michal Vasko05532772021-06-03 12:12:38 +02001418 WRN(NULL, "inet_ntop() failed (%s).", strerror(errno));
Michal Vasko5e8d0192019-06-24 19:19:49 +02001419 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001420 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 +02001421 }
1422
1423 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001424 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1425 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001426 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001427 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001428 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001429 /* make the socket non-blocking */
1430 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001431 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001432 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001433 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001434 /* non-blocking connect! */
1435 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1436 if (errno != EINPROGRESS) {
1437 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001438 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001439 goto cleanup;
1440 }
1441 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001442 }
1443 ts.tv_sec = timeout;
1444 ts.tv_usec = 0;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001445
Frank Rimpler9f838b02018-07-25 06:44:03 +00001446 FD_ZERO(&wset);
1447 FD_SET(sock, &wset);
1448
1449 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001450 ERR(NULL, "select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001451 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001452 }
1453
Michal Vasko5e8d0192019-06-24 19:19:49 +02001454 if (ret == 0) {
1455 /* there was a timeout */
Michal Vasko05532772021-06-03 12:12:38 +02001456 VRB(NULL, "Timed out after %ds (%s).", timeout, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001457 if (sock_pending) {
1458 /* no sock-close, we'll try it again */
1459 *sock_pending = sock;
1460 } else {
1461 close(sock);
1462 }
1463 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001464 }
Radek Krejci782041a2018-08-20 10:09:45 +02001465
1466 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001467 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001468 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001469 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001470 goto cleanup;
1471 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001472 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001473 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001474 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001475 errno = error;
1476 goto cleanup;
1477 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001478
1479 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001480 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001481 goto cleanup;
1482 }
1483
Michal Vasko086311b2016-01-08 09:53:11 +01001484 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001485
Frank Rimpler9f838b02018-07-25 06:44:03 +00001486cleanup:
1487 if (sock_pending) {
1488 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001489 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001490 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001491 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001492}
1493
Frank Rimpler9f838b02018-07-25 06:44:03 +00001494/* A given timeout value limits the time how long the function blocks. If it has to block
1495 only for some seconds, a socket connection might not yet have been fully established.
1496 Therefore the active (pending) socket will be stored in *sock_pending, but the return
1497 value will be -1. In such a case a subsequent invokation is required, by providing the
1498 stored sock_pending, again.
1499 In general, if this function returns -1, when a timeout has been given, this function
1500 has to be invoked, until it returns a valid socket.
1501 */
1502int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001503nc_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 +00001504{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001505 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001506 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001507 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001508 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001509 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001510
Michal Vasko05532772021-06-03 12:12:38 +02001511 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001512
1513 /* no pending socket */
1514 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001515 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001516 snprintf(port_s, 6, "%u", port);
1517 memset(&hints, 0, sizeof hints);
1518 hints.ai_family = AF_UNSPEC;
1519 hints.ai_socktype = SOCK_STREAM;
1520 hints.ai_protocol = IPPROTO_TCP;
1521 i = getaddrinfo(host, port_s, &hints, &res_list);
1522 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001523 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001524 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001525 }
1526
1527 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001528 sock = _non_blocking_connect(timeout, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001529 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001530 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001531 /* try the next resource */
1532 continue;
1533 } else {
1534 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001535 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001536 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001537 }
Michal Vasko05532772021-06-03 12:12:38 +02001538 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 +01001539
1540 opt = 1;
1541 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001542 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001543 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001544 }
1545
Michal Vasko66032bc2019-01-22 15:03:12 +01001546 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1547 buf = malloc(INET6_ADDRSTRLEN);
1548 if (!buf) {
1549 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001550 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001551 }
1552 if (res->ai_family == AF_INET) {
1553 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1554 } else {
1555 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1556 }
1557 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +02001558 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
Michal Vasko66032bc2019-01-22 15:03:12 +01001559 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001560 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001561 }
1562
1563 *ip_host = buf;
1564 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001565 break;
1566 }
1567 freeaddrinfo(res_list);
1568
1569 } else {
1570 /* try to get a connection with the pending socket */
1571 assert(sock_pending);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001572 sock = _non_blocking_connect(timeout, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001573 }
1574
1575 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001576
1577error:
Michal Vasko0be85692021-03-02 08:04:57 +01001578 if (res_list) {
1579 freeaddrinfo(res_list);
1580 }
Michal Vaskocb846632019-12-13 15:12:45 +01001581 if (sock != -1) {
1582 close(sock);
1583 }
1584 if (sock_pending) {
1585 *sock_pending = -1;
1586 }
1587 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001588}
1589
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001590#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001591
Michal Vasko3031aae2016-01-27 16:07:18 +01001592int
1593nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1594{
1595 int sock;
1596
Michal Vasko45e53ae2016-04-07 11:46:03 +02001597 if (!address) {
1598 ERRARG("address");
1599 return -1;
1600 } else if (!port) {
1601 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001602 return -1;
1603 }
1604
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001605 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001606 if (sock == -1) {
1607 return -1;
1608 }
1609
1610 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001611 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1612 if (!client_opts.ch_binds) {
1613 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001614 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001615 return -1;
1616 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001617
Michal Vasko2e6defd2016-10-07 15:48:15 +02001618 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1619 if (!client_opts.ch_bind_ti) {
1620 ERRMEM;
1621 close(sock);
1622 return -1;
1623 }
1624 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1625
Michal Vasko3031aae2016-01-27 16:07:18 +01001626 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001627 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1628 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001629 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001630 return -1;
1631 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001632 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1633 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001634 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001635
1636 return 0;
1637}
1638
1639int
1640nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1641{
1642 uint32_t i;
1643 int ret = -1;
1644
1645 if (!address && !port && !ti) {
1646 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1647 close(client_opts.ch_binds[i].sock);
1648 free((char *)client_opts.ch_binds[i].address);
1649
1650 ret = 0;
1651 }
1652 free(client_opts.ch_binds);
1653 client_opts.ch_binds = NULL;
1654 client_opts.ch_bind_count = 0;
1655 } else {
1656 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001657 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1658 (!port || (client_opts.ch_binds[i].port == port)) &&
1659 (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001660 close(client_opts.ch_binds[i].sock);
1661 free((char *)client_opts.ch_binds[i].address);
1662
1663 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001664 if (!client_opts.ch_bind_count) {
1665 free(client_opts.ch_binds);
1666 client_opts.ch_binds = NULL;
1667 } else if (i < client_opts.ch_bind_count) {
1668 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1669 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1670 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001671
1672 ret = 0;
1673 }
1674 }
1675 }
1676
1677 return ret;
1678}
1679
1680API int
1681nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1682{
1683 int sock;
1684 char *host = NULL;
1685 uint16_t port, idx;
1686
Michal Vasko45e53ae2016-04-07 11:46:03 +02001687 if (!client_opts.ch_binds) {
1688 ERRINIT;
1689 return -1;
1690 } else if (!session) {
1691 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001692 return -1;
1693 }
1694
1695 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1696
Michal Vasko50456e82016-02-02 12:16:08 +01001697 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001698 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001699 return sock;
1700 }
1701
Radek Krejci53691be2016-02-22 13:58:37 +01001702#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001703 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001704 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001705 } else
1706#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001707#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001708 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001709 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001710 } else
1711#endif
1712 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001713 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001714 *session = NULL;
1715 }
1716
1717 free(host);
1718
1719 if (!(*session)) {
1720 return -1;
1721 }
1722
1723 return 1;
1724}
1725
Radek Krejci53691be2016-02-22 13:58:37 +01001726#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001727
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001728API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001729nc_session_get_cpblts(const struct nc_session *session)
1730{
1731 if (!session) {
1732 ERRARG("session");
1733 return NULL;
1734 }
1735
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001736 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001737}
1738
1739API const char *
1740nc_session_cpblt(const struct nc_session *session, const char *capab)
1741{
1742 int i, len;
1743
1744 if (!session) {
1745 ERRARG("session");
1746 return NULL;
1747 } else if (!capab) {
1748 ERRARG("capab");
1749 return NULL;
1750 }
1751
1752 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001753 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1754 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1755 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001756 }
1757 }
1758
1759 return NULL;
1760}
1761
Michal Vasko9cd26a82016-05-31 08:58:48 +02001762API int
1763nc_session_ntf_thread_running(const struct nc_session *session)
1764{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001765 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001766 ERRARG("session");
1767 return 0;
1768 }
1769
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001770 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001771}
1772
Michal Vaskob7558c52016-02-26 15:04:19 +01001773API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001774nc_client_init(void)
1775{
1776 nc_init();
1777}
1778
1779API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001780nc_client_destroy(void)
1781{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001782 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001783#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001784 nc_client_ch_del_bind(NULL, 0, 0);
1785#endif
1786#ifdef NC_ENABLED_SSH
1787 nc_client_ssh_destroy_opts();
1788#endif
1789#ifdef NC_ENABLED_TLS
1790 nc_client_tls_destroy_opts();
1791#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001792 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001793}
1794
Michal Vasko77367452021-02-16 16:32:18 +01001795static NC_MSG_TYPE
1796recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001797{
Michal Vasko77367452021-02-16 16:32:18 +01001798 char *ptr;
1799 struct lyd_attr *attr;
1800 uint64_t cur_msgid;
1801
1802 assert(envp && !envp->schema);
1803
1804 /* find the message-id attribute */
1805 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1806 if (!strcmp(attr->name.name, "message-id")) {
1807 break;
1808 }
1809 }
1810
1811 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001812 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001813 return NC_MSG_REPLY_ERR_MSGID;
1814 }
1815
1816 cur_msgid = strtoul(attr->value, &ptr, 10);
1817 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001818 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1819 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001820 return NC_MSG_REPLY_ERR_MSGID;
1821 }
1822
1823 return NC_MSG_REPLY;
1824}
1825
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001826/**
1827 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1828 *
1829 * @param[in] session NETCONF session used to send error messages.
1830 * @param[in] msg Message to check for type.
1831 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1832 * @return NC_MSG_NOTIF If format roughly matches a notification;
1833 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1834 */
1835static NC_MSG_TYPE
1836get_msg_type(struct nc_session *session, struct ly_in *msg)
1837{
1838 const char *str, *end;
1839
1840 str = ly_in_memory(msg, NULL);
1841
1842 while (*str) {
1843 /* Skip whitespaces */
1844 while (isspace(*str)) {
1845 str++;
1846 }
1847
1848 if (*str == '<') {
1849 str++;
1850 if (!strncmp(str, "!--", 3)) {
1851 /* Skip comments */
1852 end = "-->";
1853 str = strstr(str, end);
1854 } else if (!strncmp(str, "?xml", 4)) {
1855 /* Skip xml declaration */
1856 end = "?>";
1857 str = strstr(str, end);
1858 } else if (!strncmp(str, "rpc-reply", 9)) {
1859 return NC_MSG_REPLY;
1860 } else if (!strncmp(str, "notification", 12)) {
1861 return NC_MSG_NOTIF;
1862 } else {
1863 ERR(session, "Unknown xml element '%.10s'.", str);
1864 return NC_MSG_ERROR;
1865 }
1866 if (!str) {
1867 /* No matching ending tag found */
1868 ERR(session, "No matching ending tag '%s' found in xml message.", end);
1869 return NC_MSG_ERROR;
1870 }
1871 str += strlen(end);
1872 } else {
1873 /* Not a valid xml */
1874 ERR(session, "Unexpected character '%c' in xml message.", *str);
1875 return NC_MSG_ERROR;
1876 }
1877 }
1878
1879 /* Unexpected end of message */
1880 ERR(session, "Unexpected end of xml message.");
1881 return NC_MSG_ERROR;
1882}
1883
1884/**
1885 * @brief Function to receive either replies or notifications.
1886 *
1887 * @param[in] session NETCONF session from which this function receives messages.
1888 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
1889 * @param[in] expected Type of the message the caller desired.
1890 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
1891 * @return NC_MSG_REPLY If a rpc-reply was received;
1892 * @return NC_MSG_NOTIF If a notification was received;
Michal Vaskofdba4a32022-01-05 12:13:53 +01001893 * @return NC_MSG_ERROR If any error occurred;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001894 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
1895 */
1896static NC_MSG_TYPE
1897recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
1898{
1899 struct nc_msg_cont **cont_ptr;
1900 struct ly_in *msg = NULL;
1901 struct nc_msg_cont *cont, *prev;
1902 NC_MSG_TYPE ret = NC_MSG_ERROR;
1903 int r;
1904
1905 *message = NULL;
1906
1907 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001908 r = nc_session_client_msgs_lock(session, &timeout, __func__);
1909 if (!r) {
1910 ret = NC_MSG_WOULDBLOCK;
1911 goto cleanup;
1912 } else if (r == -1) {
1913 ret = NC_MSG_ERROR;
1914 goto cleanup;
1915 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001916
1917 /* Find the expected message in the buffer */
1918 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001919 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001920 prev = cont;
1921 }
1922
1923 if (cont) {
1924 /* Remove found message from buffer */
1925 if (prev) {
1926 prev->next = cont->next;
1927 } else {
1928 session->opts.client.msgs = cont->next;
1929 }
1930
1931 /* Use the buffer message */
1932 ret = cont->type;
1933 msg = cont->msg;
1934 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02001935 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001936 }
1937
1938 /* Read a message from the wire */
1939 r = nc_read_msg_poll_io(session, timeout, &msg);
1940 if (!r) {
1941 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02001942 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001943 } else if (r == -1) {
1944 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001945 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001946 }
1947
1948 /* Basic check to determine message type */
1949 ret = get_msg_type(session, msg);
1950 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02001951 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001952 }
1953
1954 /* If received a message of different type store it in the buffer */
1955 if (ret != expected) {
1956 cont_ptr = &session->opts.client.msgs;
1957 while (*cont_ptr) {
1958 cont_ptr = &((*cont_ptr)->next);
1959 }
1960 *cont_ptr = malloc(sizeof **cont_ptr);
1961 if (!*cont_ptr) {
1962 ERRMEM;
1963 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001964 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001965 }
1966 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001967 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001968 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001969 (*cont_ptr)->next = NULL;
1970 }
1971
Michal Vasko01130bd2021-08-26 11:47:38 +02001972cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001973 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001974 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001975
Michal Vasko01130bd2021-08-26 11:47:38 +02001976cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001977 if (ret == expected) {
1978 *message = msg;
1979 } else {
1980 ly_in_free(msg, 1);
1981 }
1982 return ret;
1983}
1984
Michal Vasko77367452021-02-16 16:32:18 +01001985static NC_MSG_TYPE
1986recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
1987{
Michal Vasko77367452021-02-16 16:32:18 +01001988 LY_ERR lyrc;
1989 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001990 NC_MSG_TYPE ret = NC_MSG_ERROR;
1991
1992 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
1993
1994 *envp = NULL;
1995
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001996 /* Receive messages until a rpc-reply is found or a timeout or error reached */
1997 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
1998 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01001999 goto cleanup;
2000 }
2001
2002 /* parse */
2003 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
2004 if (!lyrc) {
2005 ret = recv_reply_check_msgid(session, *envp, msgid);
2006 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002007 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002008 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002009 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002010 goto cleanup;
2011 }
2012
Michal Vasko77367452021-02-16 16:32:18 +01002013cleanup:
2014 ly_in_free(msg, 1);
2015 return ret;
2016}
2017
2018static int
2019recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
2020{
Michal Vasko143aa142021-10-01 15:31:48 +02002021 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01002022 struct nc_rpc_act_generic *rpc_gen;
2023 struct ly_in *in;
2024 struct lyd_node *tree, *op2;
2025 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01002026 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002027
2028 switch (rpc->type) {
2029 case NC_RPC_ACT_GENERIC:
2030 rpc_gen = (struct nc_rpc_act_generic *)rpc;
2031 if (rpc_gen->has_data) {
2032 tree = rpc_gen->content.data;
2033
2034 /* find the operation node */
2035 lyrc = LY_EINVAL;
2036 LYD_TREE_DFS_BEGIN(tree, op2) {
2037 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
2038 lyrc = lyd_dup_single(op2, NULL, 0, op);
2039 break;
2040 }
2041 LYD_TREE_DFS_END(tree, op2);
2042 }
2043 } else {
2044 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2045 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
2046 ly_in_free(in, 0);
2047 if (lyrc) {
2048 return -1;
2049 }
2050
2051 /* we want just the operation node */
2052 lyrc = lyd_dup_single(op2, NULL, 0, op);
2053
2054 lyd_free_tree(tree);
2055 }
2056 break;
2057 case NC_RPC_GETCONFIG:
2058 module_name = "ietf-netconf";
2059 rpc_name = "get-config";
2060 break;
2061 case NC_RPC_EDIT:
2062 module_name = "ietf-netconf";
2063 rpc_name = "edit-config";
2064 break;
2065 case NC_RPC_COPY:
2066 module_name = "ietf-netconf";
2067 rpc_name = "copy-config";
2068 break;
2069 case NC_RPC_DELETE:
2070 module_name = "ietf-netconf";
2071 rpc_name = "delete-config";
2072 break;
2073 case NC_RPC_LOCK:
2074 module_name = "ietf-netconf";
2075 rpc_name = "lock";
2076 break;
2077 case NC_RPC_UNLOCK:
2078 module_name = "ietf-netconf";
2079 rpc_name = "unlock";
2080 break;
2081 case NC_RPC_GET:
2082 module_name = "ietf-netconf";
2083 rpc_name = "get";
2084 break;
2085 case NC_RPC_KILL:
2086 module_name = "ietf-netconf";
2087 rpc_name = "kill-session";
2088 break;
2089 case NC_RPC_COMMIT:
2090 module_name = "ietf-netconf";
2091 rpc_name = "commit";
2092 break;
2093 case NC_RPC_DISCARD:
2094 module_name = "ietf-netconf";
2095 rpc_name = "discard-changes";
2096 break;
2097 case NC_RPC_CANCEL:
2098 module_name = "ietf-netconf";
2099 rpc_name = "cancel-commit";
2100 break;
2101 case NC_RPC_VALIDATE:
2102 module_name = "ietf-netconf";
2103 rpc_name = "validate";
2104 break;
2105 case NC_RPC_GETSCHEMA:
2106 module_name = "ietf-netconf-monitoring";
2107 rpc_name = "get-schema";
2108 break;
2109 case NC_RPC_SUBSCRIBE:
2110 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02002111 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01002112 break;
2113 case NC_RPC_GETDATA:
2114 module_name = "ietf-netconf-nmda";
2115 rpc_name = "get-data";
2116 break;
2117 case NC_RPC_EDITDATA:
2118 module_name = "ietf-netconf-nmda";
2119 rpc_name = "edit-data";
2120 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002121 case NC_RPC_ESTABLISHSUB:
2122 module_name = "ietf-subscribed-notifications";
2123 rpc_name = "establish-subscription";
2124 break;
2125 case NC_RPC_MODIFYSUB:
2126 module_name = "ietf-subscribed-notifications";
2127 rpc_name = "modify-subscription";
2128 break;
2129 case NC_RPC_DELETESUB:
2130 module_name = "ietf-subscribed-notifications";
2131 rpc_name = "delete-subscription";
2132 break;
2133 case NC_RPC_KILLSUB:
2134 module_name = "ietf-subscribed-notifications";
2135 rpc_name = "kill-subscription";
2136 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002137 case NC_RPC_ESTABLISHPUSH:
2138 module_name = "ietf-subscribed-notifications";
2139 rpc_name = "establish-subscription";
2140 module_check = "ietf-yang-push";
2141 break;
2142 case NC_RPC_MODIFYPUSH:
2143 module_name = "ietf-subscribed-notifications";
2144 rpc_name = "modify-subscription";
2145 module_check = "ietf-yang-push";
2146 break;
2147 case NC_RPC_RESYNCSUB:
2148 module_name = "ietf-yang-push";
2149 rpc_name = "resync-subscription";
2150 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002151 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002152 lyrc = LY_EINT;
2153 break;
2154 }
2155
2156 if (module_name && rpc_name) {
2157 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2158 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002159 ERR(session, "Missing \"%s\" schema in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002160 return -1;
2161 }
2162
2163 /* create the operation node */
2164 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2165 }
Michal Vasko305faca2021-03-25 09:16:02 +01002166 if (module_check) {
2167 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko05532772021-06-03 12:12:38 +02002168 ERR(session, "Missing \"%s\" schema in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002169 return -1;
2170 }
2171 }
Michal Vasko77367452021-02-16 16:32:18 +01002172
2173 if (lyrc) {
2174 return -1;
2175 }
2176 return 0;
2177}
2178
2179API NC_MSG_TYPE
2180nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2181 struct lyd_node **op)
2182{
2183 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002184
Michal Vasko45e53ae2016-04-07 11:46:03 +02002185 if (!session) {
2186 ERRARG("session");
2187 return NC_MSG_ERROR;
2188 } else if (!rpc) {
2189 ERRARG("rpc");
2190 return NC_MSG_ERROR;
Michal Vasko5a2c7162020-01-30 11:24:17 +01002191 } else if (!msgid) {
2192 ERRARG("msgid");
2193 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002194 } else if (!envp) {
2195 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02002196 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002197 } else if (!op) {
2198 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002199 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002200 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002201 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002202 return NC_MSG_ERROR;
2203 }
Michal Vasko77367452021-02-16 16:32:18 +01002204
2205 /* get a duplicate of the RPC node to append reply to */
2206 if (recv_reply_dup_rpc(session, rpc, op)) {
2207 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002208 }
Michal Vasko086311b2016-01-08 09:53:11 +01002209
Michal Vasko77367452021-02-16 16:32:18 +01002210 /* receive a reply */
2211 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002212
Michal Vasko77367452021-02-16 16:32:18 +01002213 /* do not return the RPC copy on error or if the reply includes no data */
2214 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2215 lyd_free_tree(*op);
2216 *op = NULL;
2217 }
2218 return ret;
2219}
2220
2221static NC_MSG_TYPE
2222recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2223{
Michal Vasko77367452021-02-16 16:32:18 +01002224 LY_ERR lyrc;
2225 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002226 NC_MSG_TYPE ret = NC_MSG_ERROR;
2227
2228 *op = NULL;
2229 *envp = NULL;
2230
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002231 /* Receive messages until a notification is found or a timeout or error reached */
2232 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2233 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002234 goto cleanup;
2235 }
2236
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002237 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002238 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2239 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002240 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002241 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002242 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002243 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002244 goto cleanup;
2245 }
2246
Michal Vasko77367452021-02-16 16:32:18 +01002247cleanup:
2248 ly_in_free(msg, 1);
2249 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002250}
2251
2252API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002253nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002254{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002255 if (!session) {
2256 ERRARG("session");
2257 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002258 } else if (!envp) {
2259 ERRARG("envp");
2260 return NC_MSG_ERROR;
2261 } else if (!op) {
2262 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002263 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002264 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002265 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002266 return NC_MSG_ERROR;
2267 }
2268
Michal Vasko77367452021-02-16 16:32:18 +01002269 /* receive a notification */
2270 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002271}
2272
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002273static void *
2274nc_recv_notif_thread(void *arg)
2275{
2276 struct nc_ntf_thread_arg *ntarg;
2277 struct nc_session *session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002278
Michal Vasko77367452021-02-16 16:32:18 +01002279 void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
2280 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002281 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002282
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002283 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002284 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002285
2286 ntarg = (struct nc_ntf_thread_arg *)arg;
2287 session = ntarg->session;
2288 notif_clb = ntarg->notif_clb;
2289 free(ntarg);
2290
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002291 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) == 1) {
Michal Vasko77367452021-02-16 16:32:18 +01002292 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002293 if (msgtype == NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002294 notif_clb(session, envp, op);
2295 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2296 lyd_free_tree(envp);
2297 lyd_free_tree(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002298 break;
2299 }
Michal Vasko77367452021-02-16 16:32:18 +01002300 lyd_free_tree(envp);
2301 lyd_free_tree(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002302 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002303 /* quit this thread once the session is broken */
2304 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002305 }
2306
2307 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2308 }
2309
Michal Vasko05532772021-06-03 12:12:38 +02002310 VRB(session, "Notification thread exit.");
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002311 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002312 return NULL;
2313}
2314
2315API int
Michal Vasko77367452021-02-16 16:32:18 +01002316nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session,
2317 const struct lyd_node *envp, const struct lyd_node *op))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002318{
2319 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002320 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002321 int ret;
2322
Michal Vasko45e53ae2016-04-07 11:46:03 +02002323 if (!session) {
2324 ERRARG("session");
2325 return -1;
2326 } else if (!notif_clb) {
2327 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002328 return -1;
2329 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002330 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002331 return -1;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002332 } else if (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread)) {
Michal Vasko05532772021-06-03 12:12:38 +02002333 ERR(session, "Separate notification thread is already running.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002334 return -1;
2335 }
2336
2337 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002338 if (!ntarg) {
2339 ERRMEM;
2340 return -1;
2341 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002342 ntarg->session = session;
2343 ntarg->notif_clb = notif_clb;
2344
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002345 /* just so that nc_recv_notif_thread() does not immediately exit */
2346 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002347
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002348 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002349 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002350 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002351 free(ntarg);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002352 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002353 return -1;
2354 }
2355
2356 return 0;
2357}
2358
Michal Vasko77367452021-02-16 16:32:18 +01002359static const char *
2360nc_wd2str(NC_WD_MODE wd)
2361{
2362 switch (wd) {
2363 case NC_WD_ALL:
2364 return "report-all";
2365 case NC_WD_ALL_TAG:
2366 return "report-all-tagged";
2367 case NC_WD_TRIM:
2368 return "trim";
2369 case NC_WD_EXPLICIT:
2370 return "explicit";
2371 default:
2372 break;
2373 }
2374
2375 return NULL;
2376}
2377
Michal Vasko086311b2016-01-08 09:53:11 +01002378API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002379nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002380{
2381 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002382 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002383 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002384 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002385 struct nc_rpc_getconfig *rpc_gc;
2386 struct nc_rpc_edit *rpc_e;
2387 struct nc_rpc_copy *rpc_cp;
2388 struct nc_rpc_delete *rpc_del;
2389 struct nc_rpc_lock *rpc_lock;
2390 struct nc_rpc_get *rpc_g;
2391 struct nc_rpc_kill *rpc_k;
2392 struct nc_rpc_commit *rpc_com;
2393 struct nc_rpc_cancel *rpc_can;
2394 struct nc_rpc_validate *rpc_val;
2395 struct nc_rpc_getschema *rpc_gs;
2396 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002397 struct nc_rpc_getdata *rpc_getd;
2398 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002399 struct nc_rpc_establishsub *rpc_estsub;
2400 struct nc_rpc_modifysub *rpc_modsub;
2401 struct nc_rpc_deletesub *rpc_delsub;
2402 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002403 struct nc_rpc_establishpush *rpc_estpush;
2404 struct nc_rpc_modifypush *rpc_modpush;
2405 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002406 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002407 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002408 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002409 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002410 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002411 uint64_t cur_msgid;
2412
Michal Vasko45e53ae2016-04-07 11:46:03 +02002413 if (!session) {
2414 ERRARG("session");
2415 return NC_MSG_ERROR;
2416 } else if (!rpc) {
2417 ERRARG("rpc");
2418 return NC_MSG_ERROR;
2419 } else if (!msgid) {
2420 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002421 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002422 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002423 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002424 return NC_MSG_ERROR;
2425 }
2426
Michal Vaskoc1171a42019-11-05 12:06:46 +01002427 switch (rpc->type) {
2428 case NC_RPC_ACT_GENERIC:
2429 /* checked when parsing */
2430 break;
2431 case NC_RPC_GETCONFIG:
2432 case NC_RPC_EDIT:
2433 case NC_RPC_COPY:
2434 case NC_RPC_DELETE:
2435 case NC_RPC_LOCK:
2436 case NC_RPC_UNLOCK:
2437 case NC_RPC_GET:
2438 case NC_RPC_KILL:
2439 case NC_RPC_COMMIT:
2440 case NC_RPC_DISCARD:
2441 case NC_RPC_CANCEL:
2442 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002443 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002444 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002445 ERR(session, "Missing \"ietf-netconf\" schema in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002446 return NC_MSG_ERROR;
2447 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002448 break;
2449 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002450 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002451 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002452 ERR(session, "Missing \"ietf-netconf-monitoring\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002453 return NC_MSG_ERROR;
2454 }
2455 break;
2456 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002457 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002458 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002459 ERR(session, "Missing \"notifications\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002460 return NC_MSG_ERROR;
2461 }
2462 break;
2463 case NC_RPC_GETDATA:
2464 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002465 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002466 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002467 ERR(session, "Missing \"ietf-netconf-nmda\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002468 return NC_MSG_ERROR;
2469 }
2470 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002471 case NC_RPC_ESTABLISHSUB:
2472 case NC_RPC_MODIFYSUB:
2473 case NC_RPC_DELETESUB:
2474 case NC_RPC_KILLSUB:
2475 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2476 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002477 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002478 return NC_MSG_ERROR;
2479 }
2480 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002481 case NC_RPC_ESTABLISHPUSH:
2482 case NC_RPC_MODIFYPUSH:
2483 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2484 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002485 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002486 return NC_MSG_ERROR;
2487 }
2488 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2489 if (!mod2) {
Michal Vasko05532772021-06-03 12:12:38 +02002490 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002491 return NC_MSG_ERROR;
2492 }
2493 break;
2494 case NC_RPC_RESYNCSUB:
2495 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2496 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002497 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002498 return NC_MSG_ERROR;
2499 }
2500 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002501 case NC_RPC_UNKNOWN:
2502 ERRINT;
2503 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002504 }
2505
Michal Vaskoab9deb62021-05-27 11:37:00 +02002506#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2507
Michal Vasko086311b2016-01-08 09:53:11 +01002508 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002509 case NC_RPC_ACT_GENERIC:
2510 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002511
2512 if (rpc_gen->has_data) {
2513 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002514 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002515 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002516 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2517 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2518 ly_in_free(in, 0);
2519 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002520 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002521 }
Michal Vasko086311b2016-01-08 09:53:11 +01002522 }
2523 break;
2524
2525 case NC_RPC_GETCONFIG:
2526 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2527
Michal Vaskoab9deb62021-05-27 11:37:00 +02002528 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2529 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2530 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002531 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002532 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002533 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2534 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002535 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002536 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2537 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2538 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002539 }
2540 }
2541
2542 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002543 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002544 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002545 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002546 lyrc = LY_ENOTFOUND;
2547 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002548 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002549 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 +01002550 }
2551 break;
2552
2553 case NC_RPC_EDIT:
2554 rpc_e = (struct nc_rpc_edit *)rpc;
2555
Michal Vaskoab9deb62021-05-27 11:37:00 +02002556 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2557 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2558 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002559
2560 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002561 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 +01002562 }
Michal Vasko086311b2016-01-08 09:53:11 +01002563 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002564 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 +01002565 }
Michal Vasko086311b2016-01-08 09:53:11 +01002566 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002567 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 +01002568 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002569 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002570 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 +01002571 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002572 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002573 }
2574 break;
2575
2576 case NC_RPC_COPY:
2577 rpc_cp = (struct nc_rpc_copy *)rpc;
2578
Michal Vaskoab9deb62021-05-27 11:37:00 +02002579 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2580 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002581 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002582 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002583 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002584 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002585 }
2586
Michal Vaskoab9deb62021-05-27 11:37:00 +02002587 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002588 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002589 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002590 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 +01002591 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002592 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002593 }
2594 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002595 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002596 }
2597
2598 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002599 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002600 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002601 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002602 lyrc = LY_ENOTFOUND;
2603 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002604 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002605 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 +01002606 }
2607 break;
2608
2609 case NC_RPC_DELETE:
2610 rpc_del = (struct nc_rpc_delete *)rpc;
2611
Michal Vaskoab9deb62021-05-27 11:37:00 +02002612 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2613 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002614 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002615 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002616 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002617 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002618 }
2619 break;
2620
2621 case NC_RPC_LOCK:
2622 rpc_lock = (struct nc_rpc_lock *)rpc;
2623
Michal Vaskoab9deb62021-05-27 11:37:00 +02002624 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2625 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2626 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002627 break;
2628
2629 case NC_RPC_UNLOCK:
2630 rpc_lock = (struct nc_rpc_lock *)rpc;
2631
Michal Vaskoab9deb62021-05-27 11:37:00 +02002632 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2633 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2634 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002635 break;
2636
2637 case NC_RPC_GET:
2638 rpc_g = (struct nc_rpc_get *)rpc;
2639
Michal Vaskoab9deb62021-05-27 11:37:00 +02002640 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002641 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002642 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002643 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2644 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002645 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002646 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2647 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2648 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002649 }
2650 }
2651
2652 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002653 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002654 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002655 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002656 lyrc = LY_ENOTFOUND;
2657 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002658 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002659 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 +01002660 }
2661 break;
2662
2663 case NC_RPC_KILL:
2664 rpc_k = (struct nc_rpc_kill *)rpc;
2665
Michal Vaskoab9deb62021-05-27 11:37:00 +02002666 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002667 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002668 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002669 break;
2670
2671 case NC_RPC_COMMIT:
2672 rpc_com = (struct nc_rpc_commit *)rpc;
2673
Michal Vaskoab9deb62021-05-27 11:37:00 +02002674 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002675 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002676 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002677 }
2678
2679 if (rpc_com->confirm_timeout) {
2680 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002681 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002682 }
Michal Vasko086311b2016-01-08 09:53:11 +01002683 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002684 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002685 }
Michal Vasko086311b2016-01-08 09:53:11 +01002686 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002687 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002688 }
2689 break;
2690
2691 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002692 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002693 break;
2694
2695 case NC_RPC_CANCEL:
2696 rpc_can = (struct nc_rpc_cancel *)rpc;
2697
Michal Vaskoab9deb62021-05-27 11:37:00 +02002698 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002699 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002700 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002701 }
2702 break;
2703
2704 case NC_RPC_VALIDATE:
2705 rpc_val = (struct nc_rpc_validate *)rpc;
2706
Michal Vaskoab9deb62021-05-27 11:37:00 +02002707 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2708 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002709 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002710 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002711 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 +01002712 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002713 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002714 }
2715 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002716 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002717 }
2718 break;
2719
2720 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002721 rpc_gs = (struct nc_rpc_getschema *)rpc;
2722
Michal Vaskoab9deb62021-05-27 11:37:00 +02002723 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2724 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002725 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002726 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002727 }
2728 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002729 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002730 }
2731 break;
2732
2733 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002734 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2735
Michal Vaskoab9deb62021-05-27 11:37:00 +02002736 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002737 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002738 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002739 }
2740
2741 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002742 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002743 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2744 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002745 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002746 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2747 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2748 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002749 }
2750 }
Michal Vasko086311b2016-01-08 09:53:11 +01002751 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002752 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002753 }
Michal Vasko086311b2016-01-08 09:53:11 +01002754 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002755 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002756 }
2757 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002758
2759 case NC_RPC_GETDATA:
2760 rpc_getd = (struct nc_rpc_getdata *)rpc;
2761
Michal Vaskoab9deb62021-05-27 11:37:00 +02002762 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2763 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2764
Michal Vaskoc1171a42019-11-05 12:06:46 +01002765 if (rpc_getd->filter) {
2766 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002767 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 +01002768 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002769 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002770 }
2771 }
2772 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002773 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002774 }
2775 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002776 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2777 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002778 }
2779 if (rpc_getd->max_depth) {
2780 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002781 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002782 }
2783 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002784 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002785 }
2786
2787 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002788 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002789 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 +01002790 }
2791 break;
2792
2793 case NC_RPC_EDITDATA:
2794 rpc_editd = (struct nc_rpc_editdata *)rpc;
2795
Michal Vaskoab9deb62021-05-27 11:37:00 +02002796 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2797 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002798
2799 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002800 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2801 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002802 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002803 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002804 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 +01002805 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002806 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002807 }
2808 break;
2809
Michal Vasko96f247a2021-03-15 13:32:10 +01002810 case NC_RPC_ESTABLISHSUB:
2811 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2812
Michal Vaskoab9deb62021-05-27 11:37:00 +02002813 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002814
2815 if (rpc_estsub->filter) {
2816 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002817 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2818 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002819 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002820 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002821 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002822 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002823 }
2824 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002825 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002826
2827 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002828 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002829 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002830 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002831 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002832 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002833 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002834 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002835 }
2836 break;
2837
2838 case NC_RPC_MODIFYSUB:
2839 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2840
Michal Vaskoab9deb62021-05-27 11:37:00 +02002841 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002842
2843 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002844 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002845
2846 if (rpc_modsub->filter) {
2847 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002848 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
2849 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002850 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002851 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002852 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002853 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002854 }
2855 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002856 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002857 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002858 }
2859 break;
2860
2861 case NC_RPC_DELETESUB:
2862 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2863
Michal Vaskoab9deb62021-05-27 11:37:00 +02002864 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002865
2866 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002867 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002868 break;
2869
2870 case NC_RPC_KILLSUB:
2871 rpc_killsub = (struct nc_rpc_killsub *)rpc;
2872
Michal Vaskoab9deb62021-05-27 11:37:00 +02002873 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002874
2875 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002876 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002877 break;
2878
Michal Vasko305faca2021-03-25 09:16:02 +01002879 case NC_RPC_ESTABLISHPUSH:
2880 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
2881
Michal Vaskoab9deb62021-05-27 11:37:00 +02002882 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
2883 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002884
2885 if (rpc_estpush->filter) {
2886 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002887 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
2888 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002889 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002890 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002891 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002892 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002893 }
2894 }
2895
2896 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002897 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002898 }
Michal Vasko305faca2021-03-25 09:16:02 +01002899 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002900 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002901 }
2902
2903 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002904 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002905 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002906 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002907 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002908 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002909 }
2910 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002911 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002912 if (rpc_estpush->dampening_period) {
2913 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002914 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002915 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002916 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
2917 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002918 if (rpc_estpush->excluded_change) {
2919 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002920 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
2921 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002922 }
2923 }
2924 }
2925 break;
2926
2927 case NC_RPC_MODIFYPUSH:
2928 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
2929
Michal Vaskoab9deb62021-05-27 11:37:00 +02002930 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002931
2932 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002933 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
2934 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002935
2936 if (rpc_modpush->filter) {
2937 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002938 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
2939 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002940 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002941 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002942 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002943 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002944 }
2945 }
Michal Vasko305faca2021-03-25 09:16:02 +01002946 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002947 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002948 }
2949
2950 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002951 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002952 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002953 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002954 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002955 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002956 }
2957 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002958 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002959 if (rpc_modpush->dampening_period) {
2960 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002961 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002962 }
2963 }
2964 break;
2965
2966 case NC_RPC_RESYNCSUB:
2967 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
2968
Michal Vaskoab9deb62021-05-27 11:37:00 +02002969 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002970 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002971 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002972 break;
2973
Michal Vasko96f247a2021-03-15 13:32:10 +01002974 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002975 ERRINT;
2976 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002977 }
2978
Michal Vaskoab9deb62021-05-27 11:37:00 +02002979#undef CHECK_LYRC_BREAK
2980
2981 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02002982 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002983 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02002984 return NC_MSG_ERROR;
2985 }
2986
Michal Vasko131120a2018-05-29 15:44:02 +02002987 /* send RPC, store its message ID */
2988 r = nc_send_msg_io(session, timeout, data);
2989 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002990
Radek Krejcib4b19062018-02-07 16:33:06 +01002991 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01002992 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01002993 }
Michal Vasko086311b2016-01-08 09:53:11 +01002994
Michal Vasko131120a2018-05-29 15:44:02 +02002995 if (r == NC_MSG_RPC) {
2996 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002997 }
Michal Vasko131120a2018-05-29 15:44:02 +02002998 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01002999}
Michal Vaskode2946c2017-01-12 12:19:26 +01003000
3001API void
3002nc_client_session_set_not_strict(struct nc_session *session)
3003{
3004 if (session->side != NC_CLIENT) {
3005 ERRARG("session");
3006 return;
3007 }
3008
3009 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3010}