blob: 16a20a7cda9cdea0d6e6f8b95490d6af2a1e86db [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_client.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 session client functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
Radek Krejcifd5b6682017-06-13 15:52:53 +020015#define _GNU_SOURCE
Michal Vaskobbf52c82020-04-07 13:08:50 +020016
17#ifdef __linux__
18# include <sys/syscall.h>
19#endif
20
Michal Vaskob83a3fa2021-05-26 09:53:42 +020021#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010022#include <assert.h>
tadeas-vintrlik54f142a2021-08-23 10:36:18 +020023#include <ctype.h>
Michal Vasko086311b2016-01-08 09:53:11 +010024#include <errno.h>
25#include <fcntl.h>
26#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010027#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010028#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020029#include <poll.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020031#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010032#include <stdlib.h>
33#include <string.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020034#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035#include <sys/socket.h>
36#include <sys/stat.h>
37#include <sys/types.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020038#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010039#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010040
41#include <libyang/libyang.h>
42
Michal Vasko9e8ac262020-04-07 13:06:45 +020043#include "compat.h"
Michal Vasko086311b2016-01-08 09:53:11 +010044#include "libnetconf.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010045#include "messages_client.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020046#include "session_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010047
Michal Vasko8a4e1462020-05-07 11:32:31 +020048#include "../modules/ietf_netconf@2013-09-29_yang.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020049#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
Michal Vasko8a4e1462020-05-07 11:32:31 +020050
Michal Vasko80ef5d22016-01-18 09:21:02 +010051static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
52
Radek Krejci62aa0642017-05-25 16:33:49 +020053#ifdef NC_ENABLED_SSH
54int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
55char *sshauth_password(const char *username, const char *hostname, void *priv);
56char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
Michal Vaskob83a3fa2021-05-26 09:53:42 +020057char *sshauth_privkey_passphrase(const char *privkey_path, void *priv);
Radek Krejci62aa0642017-05-25 16:33:49 +020058#endif /* NC_ENABLED_SSH */
59
60static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
61static pthread_key_t nc_client_context_key;
62#ifdef __linux__
63static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020064 .opts.ka = {
65 .enabled = 1,
66 .idle_time = 1,
67 .max_probes = 10,
68 .probe_interval = 5
69 },
Radek Krejci62aa0642017-05-25 16:33:49 +020070#ifdef NC_ENABLED_SSH
71 .ssh_opts = {
72 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
73 .auth_hostkey_check = sshauth_hostkey_check,
74 .auth_password = sshauth_password,
75 .auth_interactive = sshauth_interactive,
76 .auth_privkey_passphrase = sshauth_privkey_passphrase
77 },
78 .ssh_ch_opts = {
79 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
80 .auth_hostkey_check = sshauth_hostkey_check,
81 .auth_password = sshauth_password,
82 .auth_interactive = sshauth_interactive,
83 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020084 },
Radek Krejci62aa0642017-05-25 16:33:49 +020085#endif /* NC_ENABLED_SSH */
86 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020087 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020088};
89#endif
90
91static void
92nc_client_context_free(void *ptr)
93{
94 struct nc_client_context *c = (struct nc_client_context *)ptr;
95
Radek Krejci5cebc6b2017-05-26 13:24:38 +020096 if (--(c->refcount)) {
97 /* still used */
98 return;
99 }
100
Radek Krejci62aa0642017-05-25 16:33:49 +0200101#ifdef __linux__
102 /* in __linux__ we use static memory in the main thread,
103 * so this check is for programs terminating the main()
104 * function by pthread_exit() :)
105 */
106 if (c != &context_main)
107#endif
108 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200109 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400110 free(c->opts.schema_searchpath);
111
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200112#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400113 int i;
114 for (i = 0; i < c->opts.ch_bind_count; ++i) {
115 close(c->opts.ch_binds[i].sock);
116 free((char *)c->opts.ch_binds[i].address);
117 }
118 free(c->opts.ch_binds);
119 c->opts.ch_binds = NULL;
120 c->opts.ch_bind_count = 0;
Radek Krejci62aa0642017-05-25 16:33:49 +0200121#endif
122#ifdef NC_ENABLED_SSH
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400123 _nc_client_ssh_destroy_opts(&c->ssh_opts);
124 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200125#endif
126#ifdef NC_ENABLED_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400127 _nc_client_tls_destroy_opts(&c->tls_opts);
128 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200129#endif
130 free(c);
131 }
132}
133
134static void
135nc_client_context_createkey(void)
136{
137 int r;
138
139 /* initiate */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200140 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN) {}
Radek Krejci62aa0642017-05-25 16:33:49 +0200141 pthread_setspecific(nc_client_context_key, NULL);
142}
143
144struct nc_client_context *
145nc_client_context_location(void)
146{
147 struct nc_client_context *e;
148
149 pthread_once(&nc_client_context_once, nc_client_context_createkey);
150 e = pthread_getspecific(nc_client_context_key);
151 if (!e) {
152 /* prepare ly_err storage */
153#ifdef __linux__
154 if (getpid() == syscall(SYS_gettid)) {
155 /* main thread - use global variable instead of thread-specific variable. */
156 e = &context_main;
157 } else
158#endif /* __linux__ */
159 {
160 e = calloc(1, sizeof *e);
161 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200162 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200163#ifdef NC_ENABLED_SSH
164 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
165 e->ssh_opts.auth_pref[0].value = 3;
166 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
167 e->ssh_opts.auth_pref[1].value = 2;
168 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
169 e->ssh_opts.auth_pref[2].value = 1;
170 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
171 e->ssh_opts.auth_password = sshauth_password;
172 e->ssh_opts.auth_interactive = sshauth_interactive;
173 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
174
175 /* callhome settings are the same except the inverted auth methods preferences */
176 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
177 e->ssh_ch_opts.auth_pref[0].value = 1;
178 e->ssh_ch_opts.auth_pref[1].value = 2;
179 e->ssh_ch_opts.auth_pref[2].value = 3;
180#endif /* NC_ENABLED_SSH */
181 }
182 pthread_setspecific(nc_client_context_key, e);
183 }
184
185 return e;
186}
187
188#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100189
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200190API void *
191nc_client_get_thread_context(void)
192{
193 return nc_client_context_location();
194}
195
196API void
197nc_client_set_thread_context(void *context)
198{
199 struct nc_client_context *old, *new;
200
201 if (!context) {
202 ERRARG(context);
203 return;
204 }
205
206 new = (struct nc_client_context *)context;
207 old = nc_client_context_location();
208 if (old == new) {
209 /* nothing to change */
210 return;
211 }
212
213 /* replace old by new, increase reference counter in the newly set context */
214 nc_client_context_free(old);
215 new->refcount++;
216 pthread_setspecific(nc_client_context_key, new);
217}
218
Radek Krejcifd5b6682017-06-13 15:52:53 +0200219int
220nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
221{
222 /* assign context (dicionary needed for handshake) */
223 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100224 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200225 return EXIT_FAILURE;
226 }
227
228 /* user path must be first, the first path is used to store schemas retreived via get-schema */
229 if (client_opts.schema_searchpath) {
230 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200231 } else if (!access(NC_YANG_DIR, F_OK)) {
232 ly_ctx_set_searchdir(ctx, NC_YANG_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200233 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200234
235 /* set callback for getting schemas, if provided */
236 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
237 } else {
238 session->flags |= NC_SESSION_SHAREDCTX;
239 }
240
241 session->ctx = ctx;
242
243 return EXIT_SUCCESS;
244}
245
Michal Vasko086311b2016-01-08 09:53:11 +0100246API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100247nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100248{
Michal Vasko3031aae2016-01-27 16:07:18 +0100249 if (client_opts.schema_searchpath) {
250 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100251 }
Michal Vasko086311b2016-01-08 09:53:11 +0100252
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100253 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100254 client_opts.schema_searchpath = strdup(path);
255 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100256 ERRMEM;
257 return 1;
258 }
259 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100260 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100261 }
262
263 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100264}
265
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100266API const char *
267nc_client_get_schema_searchpath(void)
268{
269 return client_opts.schema_searchpath;
270}
271
Radek Krejcifd5b6682017-06-13 15:52:53 +0200272API int
273nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100274{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200275 client_opts.schema_clb = clb;
276 if (clb) {
277 client_opts.schema_clb_data = user_data;
278 } else {
279 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100280 }
281
282 return 0;
283}
284
Radek Krejcifd5b6682017-06-13 15:52:53 +0200285API ly_module_imp_clb
286nc_client_get_schema_callback(void **user_data)
287{
288 if (user_data) {
289 (*user_data) = client_opts.schema_clb_data;
290 }
291 return client_opts.schema_clb;
292}
293
Radek Krejci65ef6d52018-08-16 16:35:02 +0200294struct schema_info {
295 char *name;
296 char *revision;
297 struct {
298 char *name;
299 char *revision;
300 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100301 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200302 int implemented;
303};
304
305struct clb_data_s {
306 void *user_data;
307 ly_module_imp_clb user_clb;
308 struct schema_info *schemas;
309 struct nc_session *session;
310 int has_get_schema;
311};
312
313static char *
314retrieve_schema_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200315 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100316{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200317 char *localfile = NULL;
318 FILE *f;
319 long length, l;
320 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100321
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200322 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200323 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
324 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200325 return NULL;
326 }
327 if (localfile) {
Michal Vasko05532772021-06-03 12:12:38 +0200328 VRB(clb_data->session, "Reading schema from localfile \"%s\".", localfile);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200329 f = fopen(localfile, "r");
330 if (!f) {
Michal Vasko05532772021-06-03 12:12:38 +0200331 ERR(clb_data->session, "Unable to open \"%s\" file to get schema (%s).", localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200332 free(localfile);
333 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100334 }
Michal Vasko086311b2016-01-08 09:53:11 +0100335
Radek Krejci65ef6d52018-08-16 16:35:02 +0200336 fseek(f, 0, SEEK_END);
337 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200338 if (length < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200339 ERR(clb_data->session, "Unable to get size of schema file \"%s\".", localfile);
Radek Krejci3f499a62018-08-17 10:16:57 +0200340 free(localfile);
341 fclose(f);
342 return NULL;
343 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200344 fseek(f, 0, SEEK_SET);
345
346 model_data = malloc(length + 1);
347 if (!model_data) {
348 ERRMEM;
349 } else if ((l = fread(model_data, 1, length, f)) != length) {
Michal Vasko05532772021-06-03 12:12:38 +0200350 ERR(clb_data->session, "Reading schema from \"%s\" failed (%d bytes read, but %d expected).", localfile, l,
351 length);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200352 free(model_data);
353 model_data = NULL;
354 } else {
355 /* terminating NULL byte */
356 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100357 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200358 fclose(f);
359 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100360 }
361
Radek Krejci65ef6d52018-08-16 16:35:02 +0200362 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100363}
364
365static char *
Radek Krejci65ef6d52018-08-16 16:35:02 +0200366retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200367 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100368{
Michal Vasko086311b2016-01-08 09:53:11 +0100369 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100370 struct lyd_node *envp = NULL, *op = NULL;
371 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100372 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100373 uint64_t msgid;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200374 char *localfile = NULL;
375 FILE *f;
376 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100377
Michal Vasko05532772021-06-03 12:12:38 +0200378 VRB(clb_data->session, "Reading schema from server via get-schema.");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200379 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100380
Radek Krejci65ef6d52018-08-16 16:35:02 +0200381 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100382 usleep(1000);
383 }
384 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200385 ERR(clb_data->session, "Failed to send the <get-schema> RPC.");
Michal Vasko086311b2016-01-08 09:53:11 +0100386 nc_rpc_free(rpc);
387 return NULL;
388 }
389
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200390 do {
Michal Vasko77367452021-02-16 16:32:18 +0100391 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200392 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100393 nc_rpc_free(rpc);
394 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko05532772021-06-03 12:12:38 +0200395 ERR(clb_data->session, "Timeout for receiving reply to a <get-schema> expired.");
Michal Vasko77367452021-02-16 16:32:18 +0100396 goto cleanup;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200397 } else if ((msg == NC_MSG_ERROR) || !op) {
Michal Vasko05532772021-06-03 12:12:38 +0200398 ERR(clb_data->session, "Failed to receive a reply to <get-schema>.");
Michal Vasko77367452021-02-16 16:32:18 +0100399 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100400 }
401
Michal Vasko77367452021-02-16 16:32:18 +0100402 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Michal Vasko05532772021-06-03 12:12:38 +0200403 ERR(clb_data->session, "Unexpected data in reply to a <get-schema> RPC.");
Michal Vasko77367452021-02-16 16:32:18 +0100404 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200405 }
Michal Vasko77367452021-02-16 16:32:18 +0100406 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200407 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200408 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100409 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200410 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200411 break;
412 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100413 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200414 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200415 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200416 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200417 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100418 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200419 }
Michal Vasko086311b2016-01-08 09:53:11 +0100420
Radek Krejci488b0702018-08-29 14:47:15 +0200421 if (model_data && !model_data[0]) {
422 /* empty data */
423 free(model_data);
424 model_data = NULL;
425 }
426
Radek Krejcifd5b6682017-06-13 15:52:53 +0200427 /* try to store the model_data into local schema repository */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200428 if (model_data) {
429 *format = LYS_IN_YANG;
430 if (client_opts.schema_searchpath) {
Michal Vasko77367452021-02-16 16:32:18 +0100431 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
432 rev ? rev : "") == -1) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200433 ERRMEM;
Michal Vaskof945da52018-02-15 08:45:13 +0100434 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200435 f = fopen(localfile, "w");
436 if (!f) {
Michal Vasko05532772021-06-03 12:12:38 +0200437 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 +0200438 localfile, strerror(errno));
Radek Krejci65ef6d52018-08-16 16:35:02 +0200439 } else {
440 fputs(model_data, f);
441 fclose(f);
442 }
443 free(localfile);
Michal Vaskof945da52018-02-15 08:45:13 +0100444 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200445 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200446 }
447
Michal Vasko77367452021-02-16 16:32:18 +0100448cleanup:
449 lyd_free_tree(envp);
450 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100451 return model_data;
452}
453
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200454static void
455free_with_user_data(void *data, void *user_data)
Jan Kundrát35972df2018-09-06 19:00:01 +0200456{
457 free(data);
458 (void)user_data;
459}
460
Michal Vasko77367452021-02-16 16:32:18 +0100461static LY_ERR
Radek Krejci65ef6d52018-08-16 16:35:02 +0200462retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
Michal Vasko77367452021-02-16 16:32:18 +0100463 void *user_data, LYS_INFORMAT *format, const char **module_data,
464 void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200465{
466 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
467 unsigned int u, v, match = 1;
468 const char *name = NULL, *rev = NULL;
469 char *model_data = NULL;
470
471 /* get and check the final name and revision of the schema to be retrieved */
472 if (!mod_rev || !mod_rev[0]) {
473 /* newest revision requested - get the newest revision from the list of available modules on server */
474 match = 0;
475 for (u = 0; clb_data->schemas[u].name; ++u) {
476 if (strcmp(mod_name, clb_data->schemas[u].name)) {
477 continue;
478 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200479 if (!match || (strcmp(mod_rev, clb_data->schemas[u].revision) > 0)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200480 mod_rev = clb_data->schemas[u].revision;
481 }
482 match = u + 1;
483 }
484 if (!match) {
Michal Vasko1d2665c2021-02-12 09:28:44 +0100485 /* valid situation if we are retrieving YANG 1.1 schema and have only capabilities for now
486 * (when loading ietf-datastore for ietf-yang-library) */
Michal Vasko05532772021-06-03 12:12:38 +0200487 VRB(clb_data->session, "Unable to identify revision of the schema \"%s\" from "
488 "the available server side information.", mod_name);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200489 }
490 }
491 if (submod_name) {
492 name = submod_name;
493 if (sub_rev) {
494 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200495 } else if (match) {
496 if (!clb_data->schemas[match - 1].submodules) {
Michal Vasko05532772021-06-03 12:12:38 +0200497 VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", "
498 "in schema \"%s\", from the available server side information.", submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200499 } else {
500 for (v = 0; clb_data->schemas[match - 1].submodules[v].name; ++v) {
501 if (!strcmp(submod_name, clb_data->schemas[match - 1].submodules[v].name)) {
502 rev = sub_rev = clb_data->schemas[match - 1].submodules[v].revision;
503 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200504 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200505 if (!rev) {
Michal Vasko05532772021-06-03 12:12:38 +0200506 ERR(clb_data->session, "Requested submodule \"%s\" is not known for schema \"%s\" on server side.",
507 submod_name, mod_name);
Michal Vasko77367452021-02-16 16:32:18 +0100508 return LY_ENOTFOUND;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200509 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200510 }
511 }
512 } else {
513 name = mod_name;
514 rev = mod_rev;
515 }
516
Michal Vasko05532772021-06-03 12:12:38 +0200517 VRB(clb_data->session, "Retrieving data for schema \"%s\", revision \"%s\".", name, rev ? rev : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200518
519 if (match) {
520 /* we have enough information to avoid communication with server and try to get
521 * the schema locally */
522
523 /* 1. try to get data locally */
524 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
525
526 /* 2. try to use <get-schema> */
527 if (!model_data && clb_data->has_get_schema) {
528 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
529 }
530 } else {
531 /* we are unsure which revision of the schema we should load, so first try to get
532 * the newest revision from the server via get-schema and only if the server does not
533 * implement get-schema, try to load the newest revision locally. This is imperfect
534 * solution, but there are situation when a client does not know what revision is
535 * actually implemented by the server. */
536
537 /* 1. try to use <get-schema> */
538 if (clb_data->has_get_schema) {
539 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
540 }
541
542 /* 2. try to get data locally */
543 if (!model_data) {
544 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
545 }
546 }
547
548 /* 3. try to use user callback */
549 if (!model_data && clb_data->user_clb) {
Michal Vasko05532772021-06-03 12:12:38 +0200550 VRB(clb_data->session, "Reading schema via user callback.");
Michal Vasko77367452021-02-16 16:32:18 +0100551 clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format,
552 (const char **)&model_data, free_module_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200553 }
554
Jan Kundrát35972df2018-09-06 19:00:01 +0200555 *free_module_data = free_with_user_data;
Michal Vasko77367452021-02-16 16:32:18 +0100556 *module_data = model_data;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200557 return *module_data ? LY_SUCCESS : LY_ENOTFOUND;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200558}
559
Michal Vaskoceae0152018-02-14 16:03:59 +0100560static int
Radek Krejci65ef6d52018-08-16 16:35:02 +0200561nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, struct schema_info *schemas,
Michal Vasko77367452021-02-16 16:32:18 +0100562 ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod)
Michal Vaskoceae0152018-02-14 16:03:59 +0100563{
564 int ret = 0;
565 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200566 const char *module_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200567 LYS_INFORMAT format;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200568
569 void (*free_module_data)(void *, void *) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200570 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100571
Radek Krejci65ef6d52018-08-16 16:35:02 +0200572 *mod = NULL;
573 if (revision) {
Michal Vasko77367452021-02-16 16:32:18 +0100574 *mod = ly_ctx_get_module(session->ctx, name, revision);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200575 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100576 if (*mod) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200577 if (!(*mod)->implemented) {
Michal Vaskoceae0152018-02-14 16:03:59 +0100578 /* make the present module implemented */
Michal Vasko77367452021-02-16 16:32:18 +0100579 if (lys_set_implemented(*mod, NULL)) {
Michal Vasko05532772021-06-03 12:12:38 +0200580 ERR(session, "Failed to implement model \"%s\".", (*mod)->name);
Michal Vaskoceae0152018-02-14 16:03:59 +0100581 ret = -1;
582 }
583 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200584 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100585 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200586 clb_data.has_get_schema = has_get_schema;
587 clb_data.schemas = schemas;
588 clb_data.session = session;
589 clb_data.user_clb = user_clb;
590 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100591
592 /* clear all the errors and just collect them for now */
593 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200594 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100595
Radek Krejci65ef6d52018-08-16 16:35:02 +0200596 /* get module data */
Michal Vasko77367452021-02-16 16:32:18 +0100597 retrieve_schema_data(name, revision, NULL, NULL, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100598
Radek Krejci65ef6d52018-08-16 16:35:02 +0200599 if (module_data) {
600 /* parse the schema */
601 ly_ctx_set_module_imp_clb(session->ctx, retrieve_schema_data, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100602
Michal Vasko029c5372021-07-09 10:54:21 +0200603 lys_parse_mem(session->ctx, module_data, format, mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200604 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100605 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100606 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100607
Radek Krejci65ef6d52018-08-16 16:35:02 +0200608 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
609 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100610
Michal Vaskodbf7c272018-02-19 09:07:59 +0100611 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200612 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100613 if (!(*mod)) {
614 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100615 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100616 }
617 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200618 } else {
619 /* print only warnings */
620 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
621 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100622 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200623 }
624 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100625 }
626
Michal Vaskodbf7c272018-02-19 09:07:59 +0100627 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100628 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100629 }
630
631 return ret;
632}
633
Radek Krejci65ef6d52018-08-16 16:35:02 +0200634static void
635free_schema_info(struct schema_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200636{
Michal Vasko77367452021-02-16 16:32:18 +0100637 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200638
Radek Krejci65ef6d52018-08-16 16:35:02 +0200639 if (!list) {
640 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200641 }
642
Radek Krejci65ef6d52018-08-16 16:35:02 +0200643 for (u = 0; list[u].name; ++u) {
644 free(list[u].name);
645 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100646 if (list[u].features) {
647 for (v = 0; list[u].features[v]; ++v) {
648 free(list[u].features[v]);
649 }
650 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200651 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200652 if (list[u].submodules) {
653 for (v = 0; list[u].submodules[v].name; ++v) {
654 free(list[u].submodules[v].name);
655 free(list[u].submodules[v].revision);
656 }
657 free(list[u].submodules);
658 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200659 }
660 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200661}
662
Radek Krejci235d8cb2018-08-17 14:04:32 +0200663static int
Michal Vasko303263d2021-10-05 12:18:21 +0200664build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema_info **result)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200665{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200666 struct nc_rpc *rpc = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100667 struct lyd_node *op = NULL, *envp = NULL;
668 struct lyd_node_any *data;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200669 NC_MSG_TYPE msg;
670 uint64_t msgid;
Radek Krejcia8dfaea2018-08-17 10:55:09 +0200671 struct ly_set *modules = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100672 uint32_t u, v, submodules_count, feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200673 struct lyd_node *iter, *child;
674 struct lys_module *mod;
Michal Vasko303263d2021-10-05 12:18:21 +0200675 int ret = 0;
676 const char *rpc_name;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200677
678 /* get yang-library data from the server */
Michal Vasko303263d2021-10-05 12:18:21 +0200679 if (has_get_data) {
680 rpc_name = "get-data";
681 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
682 rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
683 NC_PARAMTYPE_CONST);
684 } else {
685 rpc = nc_rpc_getdata("ietf-datastores:operational",
686 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
687 0, NC_PARAMTYPE_CONST);
688 }
Radek Krejci261737f2018-08-21 09:22:34 +0200689 } else {
Michal Vasko303263d2021-10-05 12:18:21 +0200690 rpc_name = "get";
691 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
692 rpc = nc_rpc_get("/ietf-yang-library:*", 0, NC_PARAMTYPE_CONST);
693 } else {
694 rpc = nc_rpc_get("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", 0,
695 NC_PARAMTYPE_CONST);
696 }
Radek Krejci261737f2018-08-21 09:22:34 +0200697 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200698 if (!rpc) {
699 goto cleanup;
700 }
701
702 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
703 usleep(1000);
704 }
705 if (msg == NC_MSG_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200706 WRN(session, "Failed to send request for yang-library data.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200707 goto cleanup;
708 }
709
710 do {
Michal Vasko77367452021-02-16 16:32:18 +0100711 lyd_free_tree(envp);
712 lyd_free_tree(op);
713
714 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200715 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200716 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vasko303263d2021-10-05 12:18:21 +0200717 WRN(session, "Timeout for receiving reply to a <%s> yang-library data expired.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200718 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100719 } else if (msg == NC_MSG_ERROR) {
Michal Vasko303263d2021-10-05 12:18:21 +0200720 WRN(session, "Failed to receive a reply to <%s> of yang-library data.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200721 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100722 } else if (!op || !lyd_child(op) || strcmp(lyd_child(op)->schema->name, "data")) {
Michal Vasko303263d2021-10-05 12:18:21 +0200723 WRN(session, "Unexpected reply without data to a yang-library <%s> RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200724 goto cleanup;
725 }
726
Michal Vasko77367452021-02-16 16:32:18 +0100727 data = (struct lyd_node_any *)lyd_child(op);
728 if (data->value_type != LYD_ANYDATA_DATATREE) {
Michal Vasko303263d2021-10-05 12:18:21 +0200729 WRN(session, "Unexpected data in reply to a yang-library <%s> RPC.", rpc_name);
Michal Vasko40528752021-06-21 15:41:51 +0200730 goto cleanup;
731 } else if (!data->value.tree) {
Michal Vasko303263d2021-10-05 12:18:21 +0200732 WRN(session, "No data in reply to a yang-library <%s> RPC.", rpc_name);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200733 goto cleanup;
734 }
735
Michal Vasko77367452021-02-16 16:32:18 +0100736 if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
Michal Vasko303263d2021-10-05 12:18:21 +0200737 WRN(session, "No module information in reply to a yang-library <%s> RPC.", rpc_name);
Radek Krejci2d088832018-08-21 13:40:14 +0200738 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200739 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200740
Michal Vasko77367452021-02-16 16:32:18 +0100741 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200742 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200743 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200744 ret = -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200745 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200746 }
747
Michal Vasko77367452021-02-16 16:32:18 +0100748 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200749 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100750 feature_count = 0;
751 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
752 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Michal Vasko5d9d5972021-06-09 14:17:01 +0200753 if (!iter->schema || (iter->schema->module != mod)) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200754 /* ignore node from other schemas (augments) */
755 continue;
756 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200757 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200758 /* ignore empty nodes */
759 continue;
760 }
761 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200762 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200763 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200764 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200765 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200766 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200767 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100768 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
769 if (!(*result)[u].features) {
770 ERRMEM;
771 free_schema_info(*result);
772 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200773 ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100774 goto cleanup;
775 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200776 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100777 (*result)[u].features[feature_count + 1] = NULL;
778 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200779 } else if (!strcmp(iter->schema->name, "submodule")) {
780 submodules_count++;
781 }
782 }
783
784 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200785 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
786 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200787 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200788 free_schema_info(*result);
789 *result = NULL;
Michal Vasko303263d2021-10-05 12:18:21 +0200790 ret = -1;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200791 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200792 } else {
793 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100794 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
795 mod = modules->dnodes[u]->schema->module;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200796 if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100797 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200798 if (mod != child->schema->module) {
799 continue;
800 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200801 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200802 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200803 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200804 }
805 }
806 }
807 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200808 }
809 }
810 }
811
Radek Krejcifd5b6682017-06-13 15:52:53 +0200812cleanup:
813 nc_rpc_free(rpc);
Michal Vasko77367452021-02-16 16:32:18 +0100814 lyd_free_tree(envp);
815 lyd_free_tree(op);
816 ly_set_free(modules, NULL);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200817
Radek Krejci235d8cb2018-08-17 14:04:32 +0200818 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko05532772021-06-03 12:12:38 +0200819 /* something bad happened, discard the session */
820 ERR(session, "Invalid session, discarding.");
Radek Krejci235d8cb2018-08-17 14:04:32 +0200821 ret = EXIT_FAILURE;
822 }
823
824 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200825}
826
Radek Krejci235d8cb2018-08-17 14:04:32 +0200827static int
828build_schema_info_cpblts(char **cpblts, struct schema_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200829{
Michal Vasko77367452021-02-16 16:32:18 +0100830 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200831 char *module_cpblt, *ptr, *ptr2;
832
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200833 for (u = 0; cpblts[u]; ++u) {}
Radek Krejci235d8cb2018-08-17 14:04:32 +0200834 (*result) = calloc(u + 1, sizeof **result);
835 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200836 ERRMEM;
Michal Vasko303263d2021-10-05 12:18:21 +0200837 return -1;
Radek Krejcic9390502018-08-17 10:23:13 +0200838 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200839
840 for (u = v = 0; cpblts[u]; ++u) {
841 module_cpblt = strstr(cpblts[u], "module=");
842 /* this capability requires a module */
843 if (!module_cpblt) {
844 continue;
845 }
846
847 /* get module's name */
848 ptr = (char *)module_cpblt + 7;
849 ptr2 = strchr(ptr, '&');
850 if (!ptr2) {
851 ptr2 = ptr + strlen(ptr);
852 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200853 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200854
855 /* get module's revision */
856 ptr = strstr(module_cpblt, "revision=");
857 if (ptr) {
858 ptr += 9;
859 ptr2 = strchr(ptr, '&');
860 if (!ptr2) {
861 ptr2 = ptr + strlen(ptr);
862 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200863 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200864 }
865
866 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200867 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200868
869 /* get module's features */
870 ptr = strstr(module_cpblt, "features=");
871 if (ptr) {
872 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +0100873 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200874 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
875 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +0100876 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
877 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
878 (*result)[v].features[feature_count + 1] = NULL;
879 ++feature_count;
880
Radek Krejci65ef6d52018-08-16 16:35:02 +0200881 ptr2 = ptr + 1;
882 }
883 }
884 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +0100885 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
886 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
887 (*result)[v].features[feature_count + 1] = NULL;
888 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200889 }
890 ++v;
891 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200892
Michal Vasko303263d2021-10-05 12:18:21 +0200893 return 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200894}
895
896static int
Michal Vasko77367452021-02-16 16:32:18 +0100897nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data,
898 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200899{
Michal Vasko303263d2021-10-05 12:18:21 +0200900 int ret = -1;
Michal Vasko77367452021-02-16 16:32:18 +0100901 struct lys_module *mod;
902 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200903
904 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +0200905 /* skip import-only modules */
906 if (!modules[u].implemented) {
907 continue;
908 }
909
Radek Krejci65ef6d52018-08-16 16:35:02 +0200910 /* we can continue even if it fails */
911 nc_ctx_load_module(session, modules[u].name, modules[u].revision, modules, user_clb, user_data, has_get_schema, &mod);
912
913 if (!mod) {
914 if (session->status != NC_STATUS_RUNNING) {
915 /* something bad heppened, discard the session */
Michal Vasko05532772021-06-03 12:12:38 +0200916 ERR(session, "Invalid session, discarding.");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200917 goto cleanup;
918 }
919
920 /* all loading ways failed, the schema will be ignored in the received data */
Michal Vasko05532772021-06-03 12:12:38 +0200921 WRN(session, "Failed to load schema \"%s@%s\".", modules[u].name, modules[u].revision ?
922 modules[u].revision : "<latest>");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200923 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
924 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100925 /* set the features */
926 lys_set_implemented(mod, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200927 }
Michal Vasko60f66602017-10-17 13:52:18 +0200928 }
929
Michal Vasko77367452021-02-16 16:32:18 +0100930 /* success */
Michal Vasko303263d2021-10-05 12:18:21 +0200931 ret = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200932
933cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +0200934 return ret;
935}
936
Radek Krejci65ef6d52018-08-16 16:35:02 +0200937static int
Michal Vasko77367452021-02-16 16:32:18 +0100938nc_ctx_fill_ietf_netconf(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb,
939 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200940{
Michal Vasko77367452021-02-16 16:32:18 +0100941 uint32_t u;
942 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200943
Michal Vasko77367452021-02-16 16:32:18 +0100944 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200945 if (!ietfnc) {
946 nc_ctx_load_module(session, "ietf-netconf", NULL, modules, user_clb, user_data, has_get_schema, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200947 if (!ietfnc) {
Michal Vasko029c5372021-07-09 10:54:21 +0200948 lys_parse_mem(session->ctx, ietf_netconf_2013_09_29_yang, LYS_IN_YANG, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200949 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200950 }
951 if (!ietfnc) {
Michal Vasko05532772021-06-03 12:12:38 +0200952 ERR(session, "Loading base NETCONF schema failed.");
Michal Vasko303263d2021-10-05 12:18:21 +0200953 return -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200954 }
955
956 /* set supported capabilities from ietf-netconf */
957 for (u = 0; modules[u].name; ++u) {
958 if (strcmp(modules[u].name, "ietf-netconf") || !modules[u].implemented) {
959 continue;
960 }
961
Michal Vasko77367452021-02-16 16:32:18 +0100962 lys_set_implemented(ietfnc, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200963 }
964
965 return 0;
966}
967
Michal Vasko086311b2016-01-08 09:53:11 +0100968int
969nc_ctx_check_and_fill(struct nc_session *session)
970{
Michal Vasko303263d2021-10-05 12:18:21 +0200971 int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +0100972 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100973 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100974 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +0200975 char *revision;
Radek Krejcib056ff82018-08-20 15:58:39 +0200976 struct schema_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100977
Michal Vasko2e6defd2016-10-07 15:48:15 +0200978 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +0100979
Radek Krejci65ef6d52018-08-16 16:35:02 +0200980 /* 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 +0200981 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200982
Radek Krejci65ef6d52018-08-16 16:35:02 +0200983 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
984 * schemas only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +0100985 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200986
987 /* our callback is set later with appropriate data */
988 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
989
990 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200991 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200992 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200993 get_schema_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200994 if (yanglib_support) {
995 break;
996 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +0100997 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200998 yanglib_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200999 if (get_schema_support) {
1000 break;
1001 }
Michal Vasko086311b2016-01-08 09:53:11 +01001002 }
1003 }
Michal Vasko31dd4c52020-04-17 10:29:44 +02001004 if (get_schema_support) {
Michal Vasko05532772021-06-03 12:12:38 +02001005 VRB(session, "Capability for <get-schema> support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001006 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001007 VRB(session, "Capability for <get-schema> support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001008 }
1009 if (yanglib_support) {
Michal Vasko05532772021-06-03 12:12:38 +02001010 VRB(session, "Capability for yang-library support found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001011 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001012 VRB(session, "Capability for yang-library support not found.");
Michal Vasko31dd4c52020-04-17 10:29:44 +02001013 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001014
1015 /* get information about server's schemas from capabilities list until we will have yang-library */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001016 if (build_schema_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
Michal Vasko05532772021-06-03 12:12:38 +02001017 ERR(session, "Unable to get server's schema information from the <hello>'s capabilities.");
Radek Krejci65ef6d52018-08-16 16:35:02 +02001018 goto cleanup;
1019 }
1020
Michal Vasko086311b2016-01-08 09:53:11 +01001021 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001022 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 +02001023 WRN(session, "Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
Michal Vasko8a4e1462020-05-07 11:32:31 +02001024 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001025 }
1026
1027 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001028 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001029 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001030 }
1031
Radek Krejci65ef6d52018-08-16 16:35:02 +02001032 /* get correct version of ietf-yang-library into context */
1033 if (yanglib_support) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001034 /* use get schema to get server's ietf-yang-library */
1035 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1036 if (!revision) {
Michal Vasko05532772021-06-03 12:12:38 +02001037 WRN(session, "Loading NETCONF ietf-yang-library schema failed, missing revision in NETCONF <hello> message.");
1038 WRN(session, "Unable to automatically use <get-schema>.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001039 yanglib_support = 0;
1040 } else {
1041 revision = strndup(&revision[9], 10);
Michal Vasko0d877f92020-04-02 13:52:43 +02001042 if (nc_ctx_load_module(session, "ietf-yang-library", revision, server_modules, old_clb, old_data,
1043 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001044 WRN(session, "Loading NETCONF ietf-yang-library schema failed, unable to use it to learn all "
1045 "the supported modules.");
Radek Krejcib1d250e2018-04-12 13:54:18 +02001046 yanglib_support = 0;
1047 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001048 if (strcmp(revision, "2019-01-04") >= 0) {
1049 /* we also need ietf-datastores to be implemented */
1050 if (nc_ctx_load_module(session, "ietf-datastores", NULL, server_modules, old_clb, old_data,
1051 get_schema_support, &mod)) {
Michal Vasko05532772021-06-03 12:12:38 +02001052 WRN(session, "Loading NETCONF ietf-datastores schema failed, unable to use yang-library "
1053 "to learn all the supported modules.");
Michal Vasko0d877f92020-04-02 13:52:43 +02001054 yanglib_support = 0;
1055 }
1056 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001057 free(revision);
Michal Vasko77367452021-02-16 16:32:18 +01001058
1059 /* ietf-netconf-nmda is needed to issue get-data */
Michal Vasko303263d2021-10-05 12:18:21 +02001060 if (!nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, server_modules, old_clb, old_data,
Michal Vasko77367452021-02-16 16:32:18 +01001061 get_schema_support, &mod)) {
Michal Vasko303263d2021-10-05 12:18:21 +02001062 VRB(session, "Support for <get-data> from ietf-netcon-nmda found.");
1063 get_data_support = 1;
Michal Vasko77367452021-02-16 16:32:18 +01001064 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001065 }
1066 }
1067
Radek Krejci65ef6d52018-08-16 16:35:02 +02001068 /* prepare structured information about server's schemas */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001069 if (yanglib_support) {
Michal Vasko303263d2021-10-05 12:18:21 +02001070 if (build_schema_info_yl(session, get_data_support, &sm)) {
Radek Krejcif0633792018-08-20 15:12:09 +02001071 goto cleanup;
1072 } else if (!sm) {
Michal Vasko05532772021-06-03 12:12:38 +02001073 VRB(session, "Trying to use capabilities instead of ietf-yang-library data.");
Radek Krejcif0633792018-08-20 15:12:09 +02001074 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001075 /* prefer yang-library information, currently we have it from capabilities used for getting correct
1076 * yang-library schema */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001077 free_schema_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001078 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001079 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001080 }
Michal Vaskoef578332016-01-25 13:20:09 +01001081
Radek Krejci65ef6d52018-08-16 16:35:02 +02001082 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1083 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001084 }
1085
Radek Krejcifd5b6682017-06-13 15:52:53 +02001086 /* succsess */
1087 ret = 0;
1088
Michal Vaskoeee99412016-11-21 10:19:43 +01001089 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko05532772021-06-03 12:12:38 +02001090 WRN(session, "Some models failed to be loaded, any data from these models (and any other unknown) will "
1091 "be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +01001092 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001093
1094cleanup:
Radek Krejci65ef6d52018-08-16 16:35:02 +02001095 free_schema_info(server_modules);
1096
Radek Krejcifd5b6682017-06-13 15:52:53 +02001097 /* set user callback back */
1098 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001099 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001100
Michal Vaskoef578332016-01-25 13:20:09 +01001101 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001102}
1103
1104API struct nc_session *
1105nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1106{
Michal Vaskod083db62016-01-19 10:31:29 +01001107 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001108
Michal Vasko45e53ae2016-04-07 11:46:03 +02001109 if (fdin < 0) {
1110 ERRARG("fdin");
1111 return NULL;
1112 } else if (fdout < 0) {
1113 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001114 return NULL;
1115 }
1116
1117 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001118 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001119 if (!session) {
1120 ERRMEM;
1121 return NULL;
1122 }
1123 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001124
1125 /* transport specific data */
1126 session->ti_type = NC_TI_FD;
1127 session->ti.fd.in = fdin;
1128 session->ti.fd.out = fdout;
1129
Robin Jarry4e38e292019-10-15 17:14:14 +02001130 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1131 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001132 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001133 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001134
1135 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001136 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001137 goto fail;
1138 }
1139 session->status = NC_STATUS_RUNNING;
1140
Michal Vaskoef578332016-01-25 13:20:09 +01001141 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001142 goto fail;
1143 }
1144
1145 return session;
1146
1147fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001148 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001149 return NULL;
1150}
1151
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001152API struct nc_session *
1153nc_connect_unix(const char *address, struct ly_ctx *ctx)
1154{
1155 struct nc_session *session = NULL;
1156 struct sockaddr_un sun;
1157 const struct passwd *pw;
1158 char *username;
1159 int sock = -1;
1160
1161 if (address == NULL) {
1162 ERRARG("address");
1163 return NULL;
1164 }
1165
1166 pw = getpwuid(geteuid());
1167 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001168 ERR(NULL, "Failed to find username for euid=%u.\n", geteuid());
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001169 goto fail;
1170 }
1171
1172 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1173 if (sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001174 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001175 goto fail;
1176 }
1177
1178 memset(&sun, 0, sizeof(sun));
1179 sun.sun_family = AF_UNIX;
1180 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1181
1182 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001183 ERR(NULL, "Cannot connect to sock server %s (%s)", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001184 goto fail;
1185 }
1186
1187 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001188 ERR(NULL, "fcntl failed (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001189 goto fail;
1190 }
1191
1192 /* prepare session structure */
1193 session = nc_new_session(NC_CLIENT, 0);
1194 if (!session) {
1195 ERRMEM;
1196 goto fail;
1197 }
1198 session->status = NC_STATUS_STARTING;
1199
1200 /* transport specific data */
1201 session->ti_type = NC_TI_UNIX;
1202 session->ti.unixsock.sock = sock;
1203 sock = -1; /* do not close sock in fail label anymore */
1204
Robin Jarry4e38e292019-10-15 17:14:14 +02001205 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1206 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001207 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001208 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001209
Michal Vasko77367452021-02-16 16:32:18 +01001210 lydict_insert(ctx, address, 0, &session->path);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001211
1212 username = strdup(pw->pw_name);
1213 if (username == NULL) {
1214 ERRMEM;
1215 goto fail;
1216 }
Michal Vasko77367452021-02-16 16:32:18 +01001217 lydict_insert_zc(ctx, username, &session->username);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001218
1219 /* NETCONF handshake */
1220 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1221 goto fail;
1222 }
1223 session->status = NC_STATUS_RUNNING;
1224
1225 if (nc_ctx_check_and_fill(session) == -1) {
1226 goto fail;
1227 }
1228
1229 return session;
1230
1231fail:
1232 nc_session_free(session, NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001233 if (sock >= 0) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001234 close(sock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001235 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001236 return NULL;
1237}
1238
Frank Rimpler9f838b02018-07-25 06:44:03 +00001239/*
1240 Helper for a non-blocking connect (which is required because of the locking
1241 concept for e.g. call home settings). For more details see nc_sock_connect().
1242 */
1243static int
Michal Vasko5e8d0192019-06-24 19:19:49 +02001244_non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001245{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001246 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001247 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001248 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001249 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001250 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001251 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001252 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001253 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001254
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001255 if (sock_pending && (*sock_pending != -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001256 VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001257 sock = *sock_pending;
1258 } else {
1259 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001260 if (res->ai_family == AF_INET6) {
1261 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001262 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001263 } else {
1264 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001265 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001266 }
1267 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
Michal Vasko05532772021-06-03 12:12:38 +02001268 WRN(NULL, "inet_ntop() failed (%s).", strerror(errno));
Michal Vasko5e8d0192019-06-24 19:19:49 +02001269 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001270 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 +02001271 }
1272
1273 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001274 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1275 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001276 ERR(NULL, "Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001277 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001278 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001279 /* make the socket non-blocking */
1280 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +02001281 ERR(NULL, "fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001282 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001283 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001284 /* non-blocking connect! */
1285 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1286 if (errno != EINPROGRESS) {
1287 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001288 ERR(NULL, "connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001289 goto cleanup;
1290 }
1291 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001292 }
1293 ts.tv_sec = timeout;
1294 ts.tv_usec = 0;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001295
Frank Rimpler9f838b02018-07-25 06:44:03 +00001296 FD_ZERO(&wset);
1297 FD_SET(sock, &wset);
1298
1299 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001300 ERR(NULL, "select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001301 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001302 }
1303
Michal Vasko5e8d0192019-06-24 19:19:49 +02001304 if (ret == 0) {
1305 /* there was a timeout */
Michal Vasko05532772021-06-03 12:12:38 +02001306 VRB(NULL, "Timed out after %ds (%s).", timeout, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001307 if (sock_pending) {
1308 /* no sock-close, we'll try it again */
1309 *sock_pending = sock;
1310 } else {
1311 close(sock);
1312 }
1313 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001314 }
Radek Krejci782041a2018-08-20 10:09:45 +02001315
1316 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001317 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001318 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001319 ERR(NULL, "getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001320 goto cleanup;
1321 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001322 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001323 /* network connection failed, try another resource */
Michal Vasko05532772021-06-03 12:12:38 +02001324 VRB(NULL, "getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001325 errno = error;
1326 goto cleanup;
1327 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001328
1329 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001330 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001331 goto cleanup;
1332 }
1333
Michal Vasko086311b2016-01-08 09:53:11 +01001334 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001335
Frank Rimpler9f838b02018-07-25 06:44:03 +00001336cleanup:
1337 if (sock_pending) {
1338 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001339 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001340 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001341 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001342}
1343
Frank Rimpler9f838b02018-07-25 06:44:03 +00001344/* A given timeout value limits the time how long the function blocks. If it has to block
1345 only for some seconds, a socket connection might not yet have been fully established.
1346 Therefore the active (pending) socket will be stored in *sock_pending, but the return
1347 value will be -1. In such a case a subsequent invokation is required, by providing the
1348 stored sock_pending, again.
1349 In general, if this function returns -1, when a timeout has been given, this function
1350 has to be invoked, until it returns a valid socket.
1351 */
1352int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001353nc_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 +00001354{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001355 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001356 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001357 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001358 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001359 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001360
Michal Vasko05532772021-06-03 12:12:38 +02001361 DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001362
1363 /* no pending socket */
1364 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001365 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001366 snprintf(port_s, 6, "%u", port);
1367 memset(&hints, 0, sizeof hints);
1368 hints.ai_family = AF_UNSPEC;
1369 hints.ai_socktype = SOCK_STREAM;
1370 hints.ai_protocol = IPPROTO_TCP;
1371 i = getaddrinfo(host, port_s, &hints, &res_list);
1372 if (i != 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001373 ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001374 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001375 }
1376
1377 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001378 sock = _non_blocking_connect(timeout, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001379 if (sock == -1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001380 if (!sock_pending || (*sock_pending == -1)) {
Michal Vasko2af7c382020-07-27 14:21:35 +02001381 /* try the next resource */
1382 continue;
1383 } else {
1384 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001385 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001386 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001387 }
Michal Vasko05532772021-06-03 12:12:38 +02001388 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 +01001389
1390 opt = 1;
1391 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001392 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001393 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001394 }
1395
Michal Vasko66032bc2019-01-22 15:03:12 +01001396 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1397 buf = malloc(INET6_ADDRSTRLEN);
1398 if (!buf) {
1399 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001400 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001401 }
1402 if (res->ai_family == AF_INET) {
1403 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1404 } else {
1405 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1406 }
1407 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +02001408 ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno));
Michal Vasko66032bc2019-01-22 15:03:12 +01001409 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001410 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001411 }
1412
1413 *ip_host = buf;
1414 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001415 break;
1416 }
1417 freeaddrinfo(res_list);
1418
1419 } else {
1420 /* try to get a connection with the pending socket */
1421 assert(sock_pending);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001422 sock = _non_blocking_connect(timeout, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001423 }
1424
1425 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001426
1427error:
Michal Vasko0be85692021-03-02 08:04:57 +01001428 if (res_list) {
1429 freeaddrinfo(res_list);
1430 }
Michal Vaskocb846632019-12-13 15:12:45 +01001431 if (sock != -1) {
1432 close(sock);
1433 }
1434 if (sock_pending) {
1435 *sock_pending = -1;
1436 }
1437 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001438}
1439
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001440#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001441
Michal Vasko3031aae2016-01-27 16:07:18 +01001442int
1443nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1444{
1445 int sock;
1446
Michal Vasko45e53ae2016-04-07 11:46:03 +02001447 if (!address) {
1448 ERRARG("address");
1449 return -1;
1450 } else if (!port) {
1451 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001452 return -1;
1453 }
1454
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001455 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001456 if (sock == -1) {
1457 return -1;
1458 }
1459
1460 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001461 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1462 if (!client_opts.ch_binds) {
1463 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001464 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001465 return -1;
1466 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001467
Michal Vasko2e6defd2016-10-07 15:48:15 +02001468 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1469 if (!client_opts.ch_bind_ti) {
1470 ERRMEM;
1471 close(sock);
1472 return -1;
1473 }
1474 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1475
Michal Vasko3031aae2016-01-27 16:07:18 +01001476 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001477 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1478 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001479 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001480 return -1;
1481 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001482 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1483 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001484 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001485
1486 return 0;
1487}
1488
1489int
1490nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1491{
1492 uint32_t i;
1493 int ret = -1;
1494
1495 if (!address && !port && !ti) {
1496 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1497 close(client_opts.ch_binds[i].sock);
1498 free((char *)client_opts.ch_binds[i].address);
1499
1500 ret = 0;
1501 }
1502 free(client_opts.ch_binds);
1503 client_opts.ch_binds = NULL;
1504 client_opts.ch_bind_count = 0;
1505 } else {
1506 for (i = 0; i < client_opts.ch_bind_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001507 if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) &&
1508 (!port || (client_opts.ch_binds[i].port == port)) &&
1509 (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001510 close(client_opts.ch_binds[i].sock);
1511 free((char *)client_opts.ch_binds[i].address);
1512
1513 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001514 if (!client_opts.ch_bind_count) {
1515 free(client_opts.ch_binds);
1516 client_opts.ch_binds = NULL;
1517 } else if (i < client_opts.ch_bind_count) {
1518 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1519 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1520 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001521
1522 ret = 0;
1523 }
1524 }
1525 }
1526
1527 return ret;
1528}
1529
1530API int
1531nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1532{
1533 int sock;
1534 char *host = NULL;
1535 uint16_t port, idx;
1536
Michal Vasko45e53ae2016-04-07 11:46:03 +02001537 if (!client_opts.ch_binds) {
1538 ERRINIT;
1539 return -1;
1540 } else if (!session) {
1541 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001542 return -1;
1543 }
1544
1545 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1546
Michal Vasko50456e82016-02-02 12:16:08 +01001547 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001548 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001549 return sock;
1550 }
1551
Radek Krejci53691be2016-02-22 13:58:37 +01001552#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001553 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001554 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001555 } else
1556#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001557#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001558 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001559 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001560 } else
1561#endif
1562 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001563 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001564 *session = NULL;
1565 }
1566
1567 free(host);
1568
1569 if (!(*session)) {
1570 return -1;
1571 }
1572
1573 return 1;
1574}
1575
Radek Krejci53691be2016-02-22 13:58:37 +01001576#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001577
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001578API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001579nc_session_get_cpblts(const struct nc_session *session)
1580{
1581 if (!session) {
1582 ERRARG("session");
1583 return NULL;
1584 }
1585
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001586 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001587}
1588
1589API const char *
1590nc_session_cpblt(const struct nc_session *session, const char *capab)
1591{
1592 int i, len;
1593
1594 if (!session) {
1595 ERRARG("session");
1596 return NULL;
1597 } else if (!capab) {
1598 ERRARG("capab");
1599 return NULL;
1600 }
1601
1602 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001603 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1604 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1605 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001606 }
1607 }
1608
1609 return NULL;
1610}
1611
Michal Vasko9cd26a82016-05-31 08:58:48 +02001612API int
1613nc_session_ntf_thread_running(const struct nc_session *session)
1614{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001615 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001616 ERRARG("session");
1617 return 0;
1618 }
1619
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001620 return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001621}
1622
Michal Vaskob7558c52016-02-26 15:04:19 +01001623API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001624nc_client_init(void)
1625{
1626 nc_init();
1627}
1628
1629API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001630nc_client_destroy(void)
1631{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001632 nc_client_set_schema_searchpath(NULL);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001633#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001634 nc_client_ch_del_bind(NULL, 0, 0);
1635#endif
1636#ifdef NC_ENABLED_SSH
1637 nc_client_ssh_destroy_opts();
1638#endif
1639#ifdef NC_ENABLED_TLS
1640 nc_client_tls_destroy_opts();
1641#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001642 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001643}
1644
Michal Vasko77367452021-02-16 16:32:18 +01001645static NC_MSG_TYPE
1646recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001647{
Michal Vasko77367452021-02-16 16:32:18 +01001648 char *ptr;
1649 struct lyd_attr *attr;
1650 uint64_t cur_msgid;
1651
1652 assert(envp && !envp->schema);
1653
1654 /* find the message-id attribute */
1655 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1656 if (!strcmp(attr->name.name, "message-id")) {
1657 break;
1658 }
1659 }
1660
1661 if (!attr) {
Michal Vasko05532772021-06-03 12:12:38 +02001662 ERR(session, "Received a <rpc-reply> without a message-id.");
Michal Vasko77367452021-02-16 16:32:18 +01001663 return NC_MSG_REPLY_ERR_MSGID;
1664 }
1665
1666 cur_msgid = strtoul(attr->value, &ptr, 10);
1667 if (cur_msgid != msgid) {
Michal Vasko05532772021-06-03 12:12:38 +02001668 ERR(session, "Received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1669 cur_msgid, msgid);
Michal Vasko77367452021-02-16 16:32:18 +01001670 return NC_MSG_REPLY_ERR_MSGID;
1671 }
1672
1673 return NC_MSG_REPLY;
1674}
1675
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001676/**
1677 * @brief Used to roughly estimate the type of the message, does not actually parse or verify it.
1678 *
1679 * @param[in] session NETCONF session used to send error messages.
1680 * @param[in] msg Message to check for type.
1681 * @return NC_MSG_REPLY If format roughly matches a rpc-reply;
1682 * @return NC_MSG_NOTIF If format roughly matches a notification;
1683 * @return NC_MSG_ERROR If format is malformed or unrecognized.
1684 */
1685static NC_MSG_TYPE
1686get_msg_type(struct nc_session *session, struct ly_in *msg)
1687{
1688 const char *str, *end;
1689
1690 str = ly_in_memory(msg, NULL);
1691
1692 while (*str) {
1693 /* Skip whitespaces */
1694 while (isspace(*str)) {
1695 str++;
1696 }
1697
1698 if (*str == '<') {
1699 str++;
1700 if (!strncmp(str, "!--", 3)) {
1701 /* Skip comments */
1702 end = "-->";
1703 str = strstr(str, end);
1704 } else if (!strncmp(str, "?xml", 4)) {
1705 /* Skip xml declaration */
1706 end = "?>";
1707 str = strstr(str, end);
1708 } else if (!strncmp(str, "rpc-reply", 9)) {
1709 return NC_MSG_REPLY;
1710 } else if (!strncmp(str, "notification", 12)) {
1711 return NC_MSG_NOTIF;
1712 } else {
1713 ERR(session, "Unknown xml element '%.10s'.", str);
1714 return NC_MSG_ERROR;
1715 }
1716 if (!str) {
1717 /* No matching ending tag found */
1718 ERR(session, "No matching ending tag '%s' found in xml message.", end);
1719 return NC_MSG_ERROR;
1720 }
1721 str += strlen(end);
1722 } else {
1723 /* Not a valid xml */
1724 ERR(session, "Unexpected character '%c' in xml message.", *str);
1725 return NC_MSG_ERROR;
1726 }
1727 }
1728
1729 /* Unexpected end of message */
1730 ERR(session, "Unexpected end of xml message.");
1731 return NC_MSG_ERROR;
1732}
1733
1734/**
1735 * @brief Function to receive either replies or notifications.
1736 *
1737 * @param[in] session NETCONF session from which this function receives messages.
1738 * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite.
1739 * @param[in] expected Type of the message the caller desired.
1740 * @param[out] message If receiving a message succeeded this is the message, NULL otherwise.
1741 * @return NC_MSG_REPLY If a rpc-reply was received;
1742 * @return NC_MSG_NOTIF If a notification was received;
1743 * @return NC_MSG_ERROR If any error occured;
1744 * @return NC_MSG_WOULDBLOCK If the timeout was reached.
1745 */
1746static NC_MSG_TYPE
1747recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct ly_in **message)
1748{
1749 struct nc_msg_cont **cont_ptr;
1750 struct ly_in *msg = NULL;
1751 struct nc_msg_cont *cont, *prev;
1752 NC_MSG_TYPE ret = NC_MSG_ERROR;
1753 int r;
1754
1755 *message = NULL;
1756
1757 /* MSGS LOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001758 r = nc_session_client_msgs_lock(session, &timeout, __func__);
1759 if (!r) {
1760 ret = NC_MSG_WOULDBLOCK;
1761 goto cleanup;
1762 } else if (r == -1) {
1763 ret = NC_MSG_ERROR;
1764 goto cleanup;
1765 }
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001766
1767 /* Find the expected message in the buffer */
1768 prev = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001769 for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001770 prev = cont;
1771 }
1772
1773 if (cont) {
1774 /* Remove found message from buffer */
1775 if (prev) {
1776 prev->next = cont->next;
1777 } else {
1778 session->opts.client.msgs = cont->next;
1779 }
1780
1781 /* Use the buffer message */
1782 ret = cont->type;
1783 msg = cont->msg;
1784 free(cont);
Michal Vasko01130bd2021-08-26 11:47:38 +02001785 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001786 }
1787
1788 /* Read a message from the wire */
1789 r = nc_read_msg_poll_io(session, timeout, &msg);
1790 if (!r) {
1791 ret = NC_MSG_WOULDBLOCK;
Michal Vasko01130bd2021-08-26 11:47:38 +02001792 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001793 } else if (r == -1) {
1794 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001795 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001796 }
1797
1798 /* Basic check to determine message type */
1799 ret = get_msg_type(session, msg);
1800 if (ret == NC_MSG_ERROR) {
Michal Vasko01130bd2021-08-26 11:47:38 +02001801 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001802 }
1803
1804 /* If received a message of different type store it in the buffer */
1805 if (ret != expected) {
1806 cont_ptr = &session->opts.client.msgs;
1807 while (*cont_ptr) {
1808 cont_ptr = &((*cont_ptr)->next);
1809 }
1810 *cont_ptr = malloc(sizeof **cont_ptr);
1811 if (!*cont_ptr) {
1812 ERRMEM;
1813 ret = NC_MSG_ERROR;
Michal Vasko01130bd2021-08-26 11:47:38 +02001814 goto cleanup_unlock;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001815 }
1816 (*cont_ptr)->msg = msg;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001817 msg = NULL;
Michal Vasko01130bd2021-08-26 11:47:38 +02001818 (*cont_ptr)->type = ret;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001819 (*cont_ptr)->next = NULL;
1820 }
1821
Michal Vasko01130bd2021-08-26 11:47:38 +02001822cleanup_unlock:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001823 /* MSGS UNLOCK */
Michal Vasko01130bd2021-08-26 11:47:38 +02001824 nc_session_client_msgs_unlock(session, __func__);
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001825
Michal Vasko01130bd2021-08-26 11:47:38 +02001826cleanup:
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001827 if (ret == expected) {
1828 *message = msg;
1829 } else {
1830 ly_in_free(msg, 1);
1831 }
1832 return ret;
1833}
1834
Michal Vasko77367452021-02-16 16:32:18 +01001835static NC_MSG_TYPE
1836recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
1837{
Michal Vasko77367452021-02-16 16:32:18 +01001838 LY_ERR lyrc;
1839 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001840 NC_MSG_TYPE ret = NC_MSG_ERROR;
1841
1842 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
1843
1844 *envp = NULL;
1845
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001846 /* Receive messages until a rpc-reply is found or a timeout or error reached */
1847 ret = recv_msg(session, timeout, NC_MSG_REPLY, &msg);
1848 if (ret != NC_MSG_REPLY) {
Michal Vasko77367452021-02-16 16:32:18 +01001849 goto cleanup;
1850 }
1851
1852 /* parse */
1853 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
1854 if (!lyrc) {
1855 ret = recv_reply_check_msgid(session, *envp, msgid);
1856 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001857 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001858 ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op)));
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02001859 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001860 goto cleanup;
1861 }
1862
Michal Vasko77367452021-02-16 16:32:18 +01001863cleanup:
1864 ly_in_free(msg, 1);
1865 return ret;
1866}
1867
1868static int
1869recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
1870{
Michal Vasko143aa142021-10-01 15:31:48 +02001871 LY_ERR lyrc = LY_SUCCESS;
Michal Vasko77367452021-02-16 16:32:18 +01001872 struct nc_rpc_act_generic *rpc_gen;
1873 struct ly_in *in;
1874 struct lyd_node *tree, *op2;
1875 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01001876 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001877
1878 switch (rpc->type) {
1879 case NC_RPC_ACT_GENERIC:
1880 rpc_gen = (struct nc_rpc_act_generic *)rpc;
1881 if (rpc_gen->has_data) {
1882 tree = rpc_gen->content.data;
1883
1884 /* find the operation node */
1885 lyrc = LY_EINVAL;
1886 LYD_TREE_DFS_BEGIN(tree, op2) {
1887 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
1888 lyrc = lyd_dup_single(op2, NULL, 0, op);
1889 break;
1890 }
1891 LYD_TREE_DFS_END(tree, op2);
1892 }
1893 } else {
1894 ly_in_new_memory(rpc_gen->content.xml_str, &in);
1895 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
1896 ly_in_free(in, 0);
1897 if (lyrc) {
1898 return -1;
1899 }
1900
1901 /* we want just the operation node */
1902 lyrc = lyd_dup_single(op2, NULL, 0, op);
1903
1904 lyd_free_tree(tree);
1905 }
1906 break;
1907 case NC_RPC_GETCONFIG:
1908 module_name = "ietf-netconf";
1909 rpc_name = "get-config";
1910 break;
1911 case NC_RPC_EDIT:
1912 module_name = "ietf-netconf";
1913 rpc_name = "edit-config";
1914 break;
1915 case NC_RPC_COPY:
1916 module_name = "ietf-netconf";
1917 rpc_name = "copy-config";
1918 break;
1919 case NC_RPC_DELETE:
1920 module_name = "ietf-netconf";
1921 rpc_name = "delete-config";
1922 break;
1923 case NC_RPC_LOCK:
1924 module_name = "ietf-netconf";
1925 rpc_name = "lock";
1926 break;
1927 case NC_RPC_UNLOCK:
1928 module_name = "ietf-netconf";
1929 rpc_name = "unlock";
1930 break;
1931 case NC_RPC_GET:
1932 module_name = "ietf-netconf";
1933 rpc_name = "get";
1934 break;
1935 case NC_RPC_KILL:
1936 module_name = "ietf-netconf";
1937 rpc_name = "kill-session";
1938 break;
1939 case NC_RPC_COMMIT:
1940 module_name = "ietf-netconf";
1941 rpc_name = "commit";
1942 break;
1943 case NC_RPC_DISCARD:
1944 module_name = "ietf-netconf";
1945 rpc_name = "discard-changes";
1946 break;
1947 case NC_RPC_CANCEL:
1948 module_name = "ietf-netconf";
1949 rpc_name = "cancel-commit";
1950 break;
1951 case NC_RPC_VALIDATE:
1952 module_name = "ietf-netconf";
1953 rpc_name = "validate";
1954 break;
1955 case NC_RPC_GETSCHEMA:
1956 module_name = "ietf-netconf-monitoring";
1957 rpc_name = "get-schema";
1958 break;
1959 case NC_RPC_SUBSCRIBE:
1960 module_name = "notifications";
Michal Vaskoeed893d2021-05-25 17:11:08 +02001961 rpc_name = "create-subscription";
Michal Vasko77367452021-02-16 16:32:18 +01001962 break;
1963 case NC_RPC_GETDATA:
1964 module_name = "ietf-netconf-nmda";
1965 rpc_name = "get-data";
1966 break;
1967 case NC_RPC_EDITDATA:
1968 module_name = "ietf-netconf-nmda";
1969 rpc_name = "edit-data";
1970 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01001971 case NC_RPC_ESTABLISHSUB:
1972 module_name = "ietf-subscribed-notifications";
1973 rpc_name = "establish-subscription";
1974 break;
1975 case NC_RPC_MODIFYSUB:
1976 module_name = "ietf-subscribed-notifications";
1977 rpc_name = "modify-subscription";
1978 break;
1979 case NC_RPC_DELETESUB:
1980 module_name = "ietf-subscribed-notifications";
1981 rpc_name = "delete-subscription";
1982 break;
1983 case NC_RPC_KILLSUB:
1984 module_name = "ietf-subscribed-notifications";
1985 rpc_name = "kill-subscription";
1986 break;
Michal Vasko305faca2021-03-25 09:16:02 +01001987 case NC_RPC_ESTABLISHPUSH:
1988 module_name = "ietf-subscribed-notifications";
1989 rpc_name = "establish-subscription";
1990 module_check = "ietf-yang-push";
1991 break;
1992 case NC_RPC_MODIFYPUSH:
1993 module_name = "ietf-subscribed-notifications";
1994 rpc_name = "modify-subscription";
1995 module_check = "ietf-yang-push";
1996 break;
1997 case NC_RPC_RESYNCSUB:
1998 module_name = "ietf-yang-push";
1999 rpc_name = "resync-subscription";
2000 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002001 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01002002 lyrc = LY_EINT;
2003 break;
2004 }
2005
2006 if (module_name && rpc_name) {
2007 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
2008 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002009 ERR(session, "Missing \"%s\" schema in the context.", module_name);
Michal Vasko77367452021-02-16 16:32:18 +01002010 return -1;
2011 }
2012
2013 /* create the operation node */
2014 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
2015 }
Michal Vasko305faca2021-03-25 09:16:02 +01002016 if (module_check) {
2017 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
Michal Vasko05532772021-06-03 12:12:38 +02002018 ERR(session, "Missing \"%s\" schema in the context.", module_check);
Michal Vasko305faca2021-03-25 09:16:02 +01002019 return -1;
2020 }
2021 }
Michal Vasko77367452021-02-16 16:32:18 +01002022
2023 if (lyrc) {
2024 return -1;
2025 }
2026 return 0;
2027}
2028
2029API NC_MSG_TYPE
2030nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
2031 struct lyd_node **op)
2032{
2033 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002034
Michal Vasko45e53ae2016-04-07 11:46:03 +02002035 if (!session) {
2036 ERRARG("session");
2037 return NC_MSG_ERROR;
2038 } else if (!rpc) {
2039 ERRARG("rpc");
2040 return NC_MSG_ERROR;
Michal Vasko5a2c7162020-01-30 11:24:17 +01002041 } else if (!msgid) {
2042 ERRARG("msgid");
2043 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002044 } else if (!envp) {
2045 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02002046 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002047 } else if (!op) {
2048 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002049 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002050 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002051 ERR(session, "Invalid session to receive RPC replies.");
Michal Vasko086311b2016-01-08 09:53:11 +01002052 return NC_MSG_ERROR;
2053 }
Michal Vasko77367452021-02-16 16:32:18 +01002054
2055 /* get a duplicate of the RPC node to append reply to */
2056 if (recv_reply_dup_rpc(session, rpc, op)) {
2057 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01002058 }
Michal Vasko086311b2016-01-08 09:53:11 +01002059
Michal Vasko77367452021-02-16 16:32:18 +01002060 /* receive a reply */
2061 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01002062
Michal Vasko77367452021-02-16 16:32:18 +01002063 /* do not return the RPC copy on error or if the reply includes no data */
2064 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
2065 lyd_free_tree(*op);
2066 *op = NULL;
2067 }
2068 return ret;
2069}
2070
2071static NC_MSG_TYPE
2072recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
2073{
Michal Vasko77367452021-02-16 16:32:18 +01002074 LY_ERR lyrc;
2075 struct ly_in *msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002076 NC_MSG_TYPE ret = NC_MSG_ERROR;
2077
2078 *op = NULL;
2079 *envp = NULL;
2080
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002081 /* Receive messages until a notification is found or a timeout or error reached */
2082 ret = recv_msg(session, timeout, NC_MSG_NOTIF, &msg);
2083 if (ret != NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002084 goto cleanup;
2085 }
2086
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002087 /* Parse */
Michal Vasko77367452021-02-16 16:32:18 +01002088 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2089 if (!lyrc) {
Michal Vasko77367452021-02-16 16:32:18 +01002090 goto cleanup;
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002091 } else {
Michal Vasko05532772021-06-03 12:12:38 +02002092 ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx));
tadeas-vintrlik54f142a2021-08-23 10:36:18 +02002093 ret = NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002094 goto cleanup;
2095 }
2096
Michal Vasko77367452021-02-16 16:32:18 +01002097cleanup:
2098 ly_in_free(msg, 1);
2099 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002100}
2101
2102API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002103nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002104{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002105 if (!session) {
2106 ERRARG("session");
2107 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002108 } else if (!envp) {
2109 ERRARG("envp");
2110 return NC_MSG_ERROR;
2111 } else if (!op) {
2112 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002113 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002114 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002115 ERR(session, "Invalid session to receive Notifications.");
Michal Vasko086311b2016-01-08 09:53:11 +01002116 return NC_MSG_ERROR;
2117 }
2118
Michal Vasko77367452021-02-16 16:32:18 +01002119 /* receive a notification */
2120 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002121}
2122
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002123static void *
2124nc_recv_notif_thread(void *arg)
2125{
2126 struct nc_ntf_thread_arg *ntarg;
2127 struct nc_session *session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002128
Michal Vasko77367452021-02-16 16:32:18 +01002129 void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
2130 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002131 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002132
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002133 /* detach ourselves */
Michal Vasko131120a2018-05-29 15:44:02 +02002134 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002135
2136 ntarg = (struct nc_ntf_thread_arg *)arg;
2137 session = ntarg->session;
2138 notif_clb = ntarg->notif_clb;
2139 free(ntarg);
2140
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002141 while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) == 1) {
Michal Vasko77367452021-02-16 16:32:18 +01002142 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002143 if (msgtype == NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002144 notif_clb(session, envp, op);
2145 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2146 lyd_free_tree(envp);
2147 lyd_free_tree(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002148 break;
2149 }
Michal Vasko77367452021-02-16 16:32:18 +01002150 lyd_free_tree(envp);
2151 lyd_free_tree(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002152 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002153 /* quit this thread once the session is broken */
2154 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002155 }
2156
2157 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2158 }
2159
Michal Vasko05532772021-06-03 12:12:38 +02002160 VRB(session, "Notification thread exit.");
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002161 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002162 return NULL;
2163}
2164
2165API int
Michal Vasko77367452021-02-16 16:32:18 +01002166nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session,
2167 const struct lyd_node *envp, const struct lyd_node *op))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002168{
2169 struct nc_ntf_thread_arg *ntarg;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002170 pthread_t tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002171 int ret;
2172
Michal Vasko45e53ae2016-04-07 11:46:03 +02002173 if (!session) {
2174 ERRARG("session");
2175 return -1;
2176 } else if (!notif_clb) {
2177 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002178 return -1;
2179 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002180 ERR(session, "Invalid session to receive Notifications.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002181 return -1;
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002182 } else if (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread)) {
Michal Vasko05532772021-06-03 12:12:38 +02002183 ERR(session, "Separate notification thread is already running.");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002184 return -1;
2185 }
2186
2187 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002188 if (!ntarg) {
2189 ERRMEM;
2190 return -1;
2191 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002192 ntarg->session = session;
2193 ntarg->notif_clb = notif_clb;
2194
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002195 /* just so that nc_recv_notif_thread() does not immediately exit */
2196 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 1);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002197
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002198 ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002199 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02002200 ERR(session, "Failed to create a new thread (%s).", strerror(errno));
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002201 free(ntarg);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002202 ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002203 return -1;
2204 }
2205
2206 return 0;
2207}
2208
Michal Vasko77367452021-02-16 16:32:18 +01002209static const char *
2210nc_wd2str(NC_WD_MODE wd)
2211{
2212 switch (wd) {
2213 case NC_WD_ALL:
2214 return "report-all";
2215 case NC_WD_ALL_TAG:
2216 return "report-all-tagged";
2217 case NC_WD_TRIM:
2218 return "trim";
2219 case NC_WD_EXPLICIT:
2220 return "explicit";
2221 default:
2222 break;
2223 }
2224
2225 return NULL;
2226}
2227
Michal Vasko086311b2016-01-08 09:53:11 +01002228API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002229nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002230{
2231 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002232 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002233 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002234 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002235 struct nc_rpc_getconfig *rpc_gc;
2236 struct nc_rpc_edit *rpc_e;
2237 struct nc_rpc_copy *rpc_cp;
2238 struct nc_rpc_delete *rpc_del;
2239 struct nc_rpc_lock *rpc_lock;
2240 struct nc_rpc_get *rpc_g;
2241 struct nc_rpc_kill *rpc_k;
2242 struct nc_rpc_commit *rpc_com;
2243 struct nc_rpc_cancel *rpc_can;
2244 struct nc_rpc_validate *rpc_val;
2245 struct nc_rpc_getschema *rpc_gs;
2246 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002247 struct nc_rpc_getdata *rpc_getd;
2248 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002249 struct nc_rpc_establishsub *rpc_estsub;
2250 struct nc_rpc_modifysub *rpc_modsub;
2251 struct nc_rpc_deletesub *rpc_delsub;
2252 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002253 struct nc_rpc_establishpush *rpc_estpush;
2254 struct nc_rpc_modifypush *rpc_modpush;
2255 struct nc_rpc_resyncsub *rpc_resyncsub;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002256 struct lyd_node *data = NULL, *node, *cont;
Michal Vasko305faca2021-03-25 09:16:02 +01002257 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vaskoab9deb62021-05-27 11:37:00 +02002258 LY_ERR lyrc = 0;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002259 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002260 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002261 uint64_t cur_msgid;
2262
Michal Vasko45e53ae2016-04-07 11:46:03 +02002263 if (!session) {
2264 ERRARG("session");
2265 return NC_MSG_ERROR;
2266 } else if (!rpc) {
2267 ERRARG("rpc");
2268 return NC_MSG_ERROR;
2269 } else if (!msgid) {
2270 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002271 return NC_MSG_ERROR;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002272 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vasko05532772021-06-03 12:12:38 +02002273 ERR(session, "Invalid session to send RPCs.");
Michal Vasko086311b2016-01-08 09:53:11 +01002274 return NC_MSG_ERROR;
2275 }
2276
Michal Vaskoc1171a42019-11-05 12:06:46 +01002277 switch (rpc->type) {
2278 case NC_RPC_ACT_GENERIC:
2279 /* checked when parsing */
2280 break;
2281 case NC_RPC_GETCONFIG:
2282 case NC_RPC_EDIT:
2283 case NC_RPC_COPY:
2284 case NC_RPC_DELETE:
2285 case NC_RPC_LOCK:
2286 case NC_RPC_UNLOCK:
2287 case NC_RPC_GET:
2288 case NC_RPC_KILL:
2289 case NC_RPC_COMMIT:
2290 case NC_RPC_DISCARD:
2291 case NC_RPC_CANCEL:
2292 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002293 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002294 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002295 ERR(session, "Missing \"ietf-netconf\" schema in the context.");
Michal Vasko086311b2016-01-08 09:53:11 +01002296 return NC_MSG_ERROR;
2297 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002298 break;
2299 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002300 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002301 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002302 ERR(session, "Missing \"ietf-netconf-monitoring\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002303 return NC_MSG_ERROR;
2304 }
2305 break;
2306 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002307 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002308 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002309 ERR(session, "Missing \"notifications\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002310 return NC_MSG_ERROR;
2311 }
2312 break;
2313 case NC_RPC_GETDATA:
2314 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002315 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002316 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002317 ERR(session, "Missing \"ietf-netconf-nmda\" schema in the context.");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002318 return NC_MSG_ERROR;
2319 }
2320 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002321 case NC_RPC_ESTABLISHSUB:
2322 case NC_RPC_MODIFYSUB:
2323 case NC_RPC_DELETESUB:
2324 case NC_RPC_KILLSUB:
2325 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2326 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002327 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko96f247a2021-03-15 13:32:10 +01002328 return NC_MSG_ERROR;
2329 }
2330 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002331 case NC_RPC_ESTABLISHPUSH:
2332 case NC_RPC_MODIFYPUSH:
2333 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2334 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002335 ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002336 return NC_MSG_ERROR;
2337 }
2338 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2339 if (!mod2) {
Michal Vasko05532772021-06-03 12:12:38 +02002340 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002341 return NC_MSG_ERROR;
2342 }
2343 break;
2344 case NC_RPC_RESYNCSUB:
2345 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2346 if (!mod) {
Michal Vasko05532772021-06-03 12:12:38 +02002347 ERR(session, "Missing \"ietf-yang-push\" schema in the context.");
Michal Vasko305faca2021-03-25 09:16:02 +01002348 return NC_MSG_ERROR;
2349 }
2350 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002351 case NC_RPC_UNKNOWN:
2352 ERRINT;
2353 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002354 }
2355
Michal Vaskoab9deb62021-05-27 11:37:00 +02002356#define CHECK_LYRC_BREAK(func_call) if ((lyrc = func_call)) break;
2357
Michal Vasko086311b2016-01-08 09:53:11 +01002358 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002359 case NC_RPC_ACT_GENERIC:
2360 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002361
2362 if (rpc_gen->has_data) {
2363 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002364 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002365 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002366 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2367 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2368 ly_in_free(in, 0);
2369 if (lyrc) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002370 break;
Michal Vaskoeec410f2017-11-24 09:14:55 +01002371 }
Michal Vasko086311b2016-01-08 09:53:11 +01002372 }
2373 break;
2374
2375 case NC_RPC_GETCONFIG:
2376 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2377
Michal Vaskoab9deb62021-05-27 11:37:00 +02002378 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-config", 0, &data));
2379 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
2380 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002381 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002382 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002383 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node));
2384 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002385 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002386 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2387 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2388 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002389 }
2390 }
2391
2392 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002393 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002394 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002395 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002396 lyrc = LY_ENOTFOUND;
2397 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002398 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002399 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 +01002400 }
2401 break;
2402
2403 case NC_RPC_EDIT:
2404 rpc_e = (struct nc_rpc_edit *)rpc;
2405
Michal Vaskoab9deb62021-05-27 11:37:00 +02002406 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-config", 0, &data));
2407 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2408 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002409
2410 if (rpc_e->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002411 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 +01002412 }
Michal Vasko086311b2016-01-08 09:53:11 +01002413 if (rpc_e->test_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002414 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 +01002415 }
Michal Vasko086311b2016-01-08 09:53:11 +01002416 if (rpc_e->error_opt) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002417 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 +01002418 }
Michal Vasko7793bc62016-09-16 11:58:41 +02002419 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002420 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 +01002421 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002422 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002423 }
2424 break;
2425
2426 case NC_RPC_COPY:
2427 rpc_cp = (struct nc_rpc_copy *)rpc;
2428
Michal Vaskoab9deb62021-05-27 11:37:00 +02002429 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "copy-config", 0, &data));
2430 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002431 if (rpc_cp->url_trg) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002432 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002433 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002434 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002435 }
2436
Michal Vaskoab9deb62021-05-27 11:37:00 +02002437 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002438 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002439 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002440 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 +01002441 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002442 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002443 }
2444 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002445 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002446 }
2447
2448 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002449 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002450 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002451 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002452 lyrc = LY_ENOTFOUND;
2453 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002454 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002455 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 +01002456 }
2457 break;
2458
2459 case NC_RPC_DELETE:
2460 rpc_del = (struct nc_rpc_delete *)rpc;
2461
Michal Vaskoab9deb62021-05-27 11:37:00 +02002462 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-config", 0, &data));
2463 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002464 if (rpc_del->url) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002465 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_del->url, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002466 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002467 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002468 }
2469 break;
2470
2471 case NC_RPC_LOCK:
2472 rpc_lock = (struct nc_rpc_lock *)rpc;
2473
Michal Vaskoab9deb62021-05-27 11:37:00 +02002474 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "lock", 0, &data));
2475 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2476 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002477 break;
2478
2479 case NC_RPC_UNLOCK:
2480 rpc_lock = (struct nc_rpc_lock *)rpc;
2481
Michal Vaskoab9deb62021-05-27 11:37:00 +02002482 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "unlock", 0, &data));
2483 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "target", 0, &cont));
2484 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002485 break;
2486
2487 case NC_RPC_GET:
2488 rpc_g = (struct nc_rpc_get *)rpc;
2489
Michal Vaskoab9deb62021-05-27 11:37:00 +02002490 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002491 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002492 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002493 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node));
2494 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002495 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002496 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2497 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2498 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002499 }
2500 }
2501
2502 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002503 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002504 if (!ietfncwd) {
Michal Vasko05532772021-06-03 12:12:38 +02002505 ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002506 lyrc = LY_ENOTFOUND;
2507 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002508 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002509 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 +01002510 }
2511 break;
2512
2513 case NC_RPC_KILL:
2514 rpc_k = (struct nc_rpc_kill *)rpc;
2515
Michal Vaskoab9deb62021-05-27 11:37:00 +02002516 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002517 sprintf(str, "%u", rpc_k->sid);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002518 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002519 break;
2520
2521 case NC_RPC_COMMIT:
2522 rpc_com = (struct nc_rpc_commit *)rpc;
2523
Michal Vaskoab9deb62021-05-27 11:37:00 +02002524 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002525 if (rpc_com->confirmed) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002526 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirmed", NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002527 }
2528
2529 if (rpc_com->confirm_timeout) {
2530 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002531 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002532 }
Michal Vasko086311b2016-01-08 09:53:11 +01002533 if (rpc_com->persist) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002534 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002535 }
Michal Vasko086311b2016-01-08 09:53:11 +01002536 if (rpc_com->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002537 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002538 }
2539 break;
2540
2541 case NC_RPC_DISCARD:
Michal Vaskoab9deb62021-05-27 11:37:00 +02002542 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "discard-changes", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002543 break;
2544
2545 case NC_RPC_CANCEL:
2546 rpc_can = (struct nc_rpc_cancel *)rpc;
2547
Michal Vaskoab9deb62021-05-27 11:37:00 +02002548 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "cancel-commit", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002549 if (rpc_can->persist_id) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002550 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002551 }
2552 break;
2553
2554 case NC_RPC_VALIDATE:
2555 rpc_val = (struct nc_rpc_validate *)rpc;
2556
Michal Vaskoab9deb62021-05-27 11:37:00 +02002557 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "validate", 0, &data));
2558 CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont));
Michal Vasko086311b2016-01-08 09:53:11 +01002559 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002560 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002561 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 +01002562 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002563 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002564 }
2565 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002566 CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002567 }
2568 break;
2569
2570 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002571 rpc_gs = (struct nc_rpc_getschema *)rpc;
2572
Michal Vaskoab9deb62021-05-27 11:37:00 +02002573 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-schema", 0, &data));
2574 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002575 if (rpc_gs->version) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002576 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002577 }
2578 if (rpc_gs->format) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002579 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002580 }
2581 break;
2582
2583 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002584 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2585
Michal Vaskoab9deb62021-05-27 11:37:00 +02002586 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "create-subscription", 0, &data));
Michal Vasko086311b2016-01-08 09:53:11 +01002587 if (rpc_sub->stream) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002588 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002589 }
2590
2591 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002592 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002593 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node));
2594 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002595 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002596 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node));
2597 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL));
2598 CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002599 }
2600 }
Michal Vasko086311b2016-01-08 09:53:11 +01002601 if (rpc_sub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002602 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002603 }
Michal Vasko086311b2016-01-08 09:53:11 +01002604 if (rpc_sub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002605 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL));
Michal Vasko086311b2016-01-08 09:53:11 +01002606 }
2607 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002608
2609 case NC_RPC_GETDATA:
2610 rpc_getd = (struct nc_rpc_getdata *)rpc;
2611
Michal Vaskoab9deb62021-05-27 11:37:00 +02002612 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get-data", 0, &data));
2613 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL));
2614
Michal Vaskoc1171a42019-11-05 12:06:46 +01002615 if (rpc_getd->filter) {
2616 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002617 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 +01002618 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002619 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002620 }
2621 }
2622 if (rpc_getd->config_filter) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002623 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002624 }
2625 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002626 CHECK_LYRC_BREAK(lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" :
2627 "origin-filter", rpc_getd->origin_filter[i], 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002628 }
2629 if (rpc_getd->max_depth) {
2630 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002631 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "max-depth", str, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002632 }
2633 if (rpc_getd->with_origin) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002634 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "with-origin", NULL, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002635 }
2636
2637 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002638 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
Michal Vaskoab9deb62021-05-27 11:37:00 +02002639 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 +01002640 }
2641 break;
2642
2643 case NC_RPC_EDITDATA:
2644 rpc_editd = (struct nc_rpc_editdata *)rpc;
2645
Michal Vaskoab9deb62021-05-27 11:37:00 +02002646 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "edit-data", 0, &data));
2647 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002648
2649 if (rpc_editd->default_op) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002650 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0,
2651 NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002652 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002653 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002654 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 +01002655 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002656 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL));
Michal Vaskoc1171a42019-11-05 12:06:46 +01002657 }
2658 break;
2659
Michal Vasko96f247a2021-03-15 13:32:10 +01002660 case NC_RPC_ESTABLISHSUB:
2661 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2662
Michal Vaskoab9deb62021-05-27 11:37:00 +02002663 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002664
2665 if (rpc_estsub->filter) {
2666 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002667 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML,
2668 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002669 } else if (rpc_estsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002670 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002671 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002672 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002673 }
2674 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002675 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002676
2677 if (rpc_estsub->start) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002678 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002679 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002680 if (rpc_estsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002681 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002682 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002683 if (rpc_estsub->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002684 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002685 }
2686 break;
2687
2688 case NC_RPC_MODIFYSUB:
2689 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2690
Michal Vaskoab9deb62021-05-27 11:37:00 +02002691 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002692
2693 sprintf(str, "%u", rpc_modsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002694 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002695
2696 if (rpc_modsub->filter) {
2697 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002698 CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML,
2699 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002700 } else if (rpc_modsub->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002701 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002702 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002703 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002704 }
2705 }
Michal Vasko96f247a2021-03-15 13:32:10 +01002706 if (rpc_modsub->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002707 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002708 }
2709 break;
2710
2711 case NC_RPC_DELETESUB:
2712 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2713
Michal Vaskoab9deb62021-05-27 11:37:00 +02002714 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002715
2716 sprintf(str, "%u", rpc_delsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002717 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002718 break;
2719
2720 case NC_RPC_KILLSUB:
2721 rpc_killsub = (struct nc_rpc_killsub *)rpc;
2722
Michal Vaskoab9deb62021-05-27 11:37:00 +02002723 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data));
Michal Vasko96f247a2021-03-15 13:32:10 +01002724
2725 sprintf(str, "%u", rpc_killsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002726 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko96f247a2021-03-15 13:32:10 +01002727 break;
2728
Michal Vasko305faca2021-03-25 09:16:02 +01002729 case NC_RPC_ESTABLISHPUSH:
2730 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
2731
Michal Vaskoab9deb62021-05-27 11:37:00 +02002732 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "establish-subscription", 0, &data));
2733 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002734
2735 if (rpc_estpush->filter) {
2736 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002737 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0,
2738 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002739 } else if (rpc_estpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002740 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002741 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002742 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002743 }
2744 }
2745
2746 if (rpc_estpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002747 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002748 }
Michal Vasko305faca2021-03-25 09:16:02 +01002749 if (rpc_estpush->encoding) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002750 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002751 }
2752
2753 if (rpc_estpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002754 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002755 sprintf(str, "%" PRIu32, rpc_estpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002756 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002757 if (rpc_estpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002758 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002759 }
2760 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002761 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002762 if (rpc_estpush->dampening_period) {
2763 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002764 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002765 }
Michal Vaskoab9deb62021-05-27 11:37:00 +02002766 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0,
2767 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002768 if (rpc_estpush->excluded_change) {
2769 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002770 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0,
2771 NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002772 }
2773 }
2774 }
2775 break;
2776
2777 case NC_RPC_MODIFYPUSH:
2778 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
2779
Michal Vaskoab9deb62021-05-27 11:37:00 +02002780 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002781
2782 sprintf(str, "%u", rpc_modpush->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002783 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
2784 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002785
2786 if (rpc_modpush->filter) {
2787 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002788 CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0,
2789 LYD_ANYDATA_XML, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002790 } else if (rpc_modpush->filter[0] == '/') {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002791 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002792 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002793 CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002794 }
2795 }
Michal Vasko305faca2021-03-25 09:16:02 +01002796 if (rpc_modpush->stop) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002797 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002798 }
2799
2800 if (rpc_modpush->periodic) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002801 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "periodic", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002802 sprintf(str, "%" PRIu32, rpc_modpush->period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002803 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002804 if (rpc_modpush->anchor_time) {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002805 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002806 }
2807 } else {
Michal Vaskoab9deb62021-05-27 11:37:00 +02002808 CHECK_LYRC_BREAK(lyd_new_inner(data, mod2, "on-change", 0, &cont));
Michal Vasko305faca2021-03-25 09:16:02 +01002809 if (rpc_modpush->dampening_period) {
2810 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002811 CHECK_LYRC_BREAK(lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002812 }
2813 }
2814 break;
2815
2816 case NC_RPC_RESYNCSUB:
2817 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
2818
Michal Vaskoab9deb62021-05-27 11:37:00 +02002819 CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data));
Michal Vasko305faca2021-03-25 09:16:02 +01002820 sprintf(str, "%u", rpc_resyncsub->id);
Michal Vaskoab9deb62021-05-27 11:37:00 +02002821 CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL));
Michal Vasko305faca2021-03-25 09:16:02 +01002822 break;
2823
Michal Vasko96f247a2021-03-15 13:32:10 +01002824 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002825 ERRINT;
2826 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002827 }
2828
Michal Vaskoab9deb62021-05-27 11:37:00 +02002829#undef CHECK_LYRC_BREAK
2830
2831 if (lyrc) {
Michal Vasko05532772021-06-03 12:12:38 +02002832 ERR(session, "Failed to create RPC, perhaps a required feature is disabled.");
Michal Vaskoab9deb62021-05-27 11:37:00 +02002833 lyd_free_tree(data);
Michal Vasko131120a2018-05-29 15:44:02 +02002834 return NC_MSG_ERROR;
2835 }
2836
Michal Vasko131120a2018-05-29 15:44:02 +02002837 /* send RPC, store its message ID */
2838 r = nc_send_msg_io(session, timeout, data);
2839 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002840
Radek Krejcib4b19062018-02-07 16:33:06 +01002841 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01002842 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01002843 }
Michal Vasko086311b2016-01-08 09:53:11 +01002844
Michal Vasko131120a2018-05-29 15:44:02 +02002845 if (r == NC_MSG_RPC) {
2846 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002847 }
Michal Vasko131120a2018-05-29 15:44:02 +02002848 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01002849}
Michal Vaskode2946c2017-01-12 12:19:26 +01002850
2851API void
2852nc_client_session_set_not_strict(struct nc_session *session)
2853{
2854 if (session->side != NC_CLIENT) {
2855 ERRARG("session");
2856 return;
2857 }
2858
2859 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
2860}