blob: 9f263ffe03c66bf6969e2ebadc398f4cd004c29e [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 Vasko086311b2016-01-08 09:53:11 +010021#include <assert.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010025#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010026#include <netinet/tcp.h>
Michal Vasko086311b2016-01-08 09:53:11 +010027#include <pthread.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/socket.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020031#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010032#include <sys/stat.h>
33#include <sys/types.h>
Michal Vasko16799662020-04-07 13:09:11 +020034#include <sys/select.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035#include <unistd.h>
36#include <arpa/inet.h>
37#include <poll.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020038#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010039
40#include <libyang/libyang.h>
41
Michal Vasko9e8ac262020-04-07 13:06:45 +020042#include "compat.h"
Michal Vasko086311b2016-01-08 09:53:11 +010043#include "libnetconf.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010044#include "session_client.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010045#include "messages_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010046
Michal Vasko8a4e1462020-05-07 11:32:31 +020047#include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h"
48#include "../modules/ietf_netconf@2013-09-29_yang.h"
49
Michal Vasko80ef5d22016-01-18 09:21:02 +010050static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
51
Radek Krejci62aa0642017-05-25 16:33:49 +020052#ifdef NC_ENABLED_SSH
53int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
54char *sshauth_password(const char *username, const char *hostname, void *priv);
55char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
56char *sshauth_privkey_passphrase(const char* privkey_path, void *priv);
57#endif /* NC_ENABLED_SSH */
58
59static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
60static pthread_key_t nc_client_context_key;
61#ifdef __linux__
62static struct nc_client_context context_main = {
Michal Vaskoe49a15f2019-05-27 14:18:36 +020063 .opts.ka = {
64 .enabled = 1,
65 .idle_time = 1,
66 .max_probes = 10,
67 .probe_interval = 5
68 },
Radek Krejci62aa0642017-05-25 16:33:49 +020069#ifdef NC_ENABLED_SSH
70 .ssh_opts = {
71 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
72 .auth_hostkey_check = sshauth_hostkey_check,
73 .auth_password = sshauth_password,
74 .auth_interactive = sshauth_interactive,
75 .auth_privkey_passphrase = sshauth_privkey_passphrase
76 },
77 .ssh_ch_opts = {
78 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
79 .auth_hostkey_check = sshauth_hostkey_check,
80 .auth_password = sshauth_password,
81 .auth_interactive = sshauth_interactive,
82 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020083 },
Radek Krejci62aa0642017-05-25 16:33:49 +020084#endif /* NC_ENABLED_SSH */
85 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020086 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020087};
88#endif
89
90static void
91nc_client_context_free(void *ptr)
92{
93 struct nc_client_context *c = (struct nc_client_context *)ptr;
94
Radek Krejci5cebc6b2017-05-26 13:24:38 +020095 if (--(c->refcount)) {
96 /* still used */
97 return;
98 }
99
Radek Krejci62aa0642017-05-25 16:33:49 +0200100#ifdef __linux__
101 /* in __linux__ we use static memory in the main thread,
102 * so this check is for programs terminating the main()
103 * function by pthread_exit() :)
104 */
105 if (c != &context_main)
106#endif
107 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200108 /* for the main thread the same is done in nc_client_destroy() */
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400109 free(c->opts.schema_searchpath);
110
Radek Krejci62aa0642017-05-25 16:33:49 +0200111#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400112 int i;
113 for (i = 0; i < c->opts.ch_bind_count; ++i) {
114 close(c->opts.ch_binds[i].sock);
115 free((char *)c->opts.ch_binds[i].address);
116 }
117 free(c->opts.ch_binds);
118 c->opts.ch_binds = NULL;
119 c->opts.ch_bind_count = 0;
Radek Krejci62aa0642017-05-25 16:33:49 +0200120#endif
121#ifdef NC_ENABLED_SSH
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400122 _nc_client_ssh_destroy_opts(&c->ssh_opts);
123 _nc_client_ssh_destroy_opts(&c->ssh_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200124#endif
125#ifdef NC_ENABLED_TLS
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400126 _nc_client_tls_destroy_opts(&c->tls_opts);
127 _nc_client_tls_destroy_opts(&c->tls_ch_opts);
Radek Krejci62aa0642017-05-25 16:33:49 +0200128#endif
129 free(c);
130 }
131}
132
133static void
134nc_client_context_createkey(void)
135{
136 int r;
137
138 /* initiate */
139 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN);
140 pthread_setspecific(nc_client_context_key, NULL);
141}
142
143struct nc_client_context *
144nc_client_context_location(void)
145{
146 struct nc_client_context *e;
147
148 pthread_once(&nc_client_context_once, nc_client_context_createkey);
149 e = pthread_getspecific(nc_client_context_key);
150 if (!e) {
151 /* prepare ly_err storage */
152#ifdef __linux__
153 if (getpid() == syscall(SYS_gettid)) {
154 /* main thread - use global variable instead of thread-specific variable. */
155 e = &context_main;
156 } else
157#endif /* __linux__ */
158 {
159 e = calloc(1, sizeof *e);
160 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200161 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200162#ifdef NC_ENABLED_SSH
163 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
164 e->ssh_opts.auth_pref[0].value = 3;
165 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
166 e->ssh_opts.auth_pref[1].value = 2;
167 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
168 e->ssh_opts.auth_pref[2].value = 1;
169 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
170 e->ssh_opts.auth_password = sshauth_password;
171 e->ssh_opts.auth_interactive = sshauth_interactive;
172 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
173
174 /* callhome settings are the same except the inverted auth methods preferences */
175 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
176 e->ssh_ch_opts.auth_pref[0].value = 1;
177 e->ssh_ch_opts.auth_pref[1].value = 2;
178 e->ssh_ch_opts.auth_pref[2].value = 3;
179#endif /* NC_ENABLED_SSH */
180 }
181 pthread_setspecific(nc_client_context_key, e);
182 }
183
184 return e;
185}
186
187#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100188
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200189API void *
190nc_client_get_thread_context(void)
191{
192 return nc_client_context_location();
193}
194
195API void
196nc_client_set_thread_context(void *context)
197{
198 struct nc_client_context *old, *new;
199
200 if (!context) {
201 ERRARG(context);
202 return;
203 }
204
205 new = (struct nc_client_context *)context;
206 old = nc_client_context_location();
207 if (old == new) {
208 /* nothing to change */
209 return;
210 }
211
212 /* replace old by new, increase reference counter in the newly set context */
213 nc_client_context_free(old);
214 new->refcount++;
215 pthread_setspecific(nc_client_context_key, new);
216}
217
Radek Krejcifd5b6682017-06-13 15:52:53 +0200218int
219nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
220{
221 /* assign context (dicionary needed for handshake) */
222 if (!ctx) {
Michal Vasko77367452021-02-16 16:32:18 +0100223 if (ly_ctx_new(NULL, LY_CTX_NO_YANGLIBRARY, &ctx)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200224 return EXIT_FAILURE;
225 }
226
227 /* user path must be first, the first path is used to store schemas retreived via get-schema */
228 if (client_opts.schema_searchpath) {
229 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200230 } else if (!access(NC_YANG_DIR, F_OK)) {
231 ly_ctx_set_searchdir(ctx, NC_YANG_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200232 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200233
234 /* set callback for getting schemas, if provided */
235 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
236 } else {
237 session->flags |= NC_SESSION_SHAREDCTX;
238 }
239
240 session->ctx = ctx;
241
242 return EXIT_SUCCESS;
243}
244
Michal Vasko086311b2016-01-08 09:53:11 +0100245API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100246nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100247{
Michal Vasko3031aae2016-01-27 16:07:18 +0100248 if (client_opts.schema_searchpath) {
249 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100250 }
Michal Vasko086311b2016-01-08 09:53:11 +0100251
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100252 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100253 client_opts.schema_searchpath = strdup(path);
254 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100255 ERRMEM;
256 return 1;
257 }
258 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100259 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100260 }
261
262 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100263}
264
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100265API const char *
266nc_client_get_schema_searchpath(void)
267{
268 return client_opts.schema_searchpath;
269}
270
Radek Krejcifd5b6682017-06-13 15:52:53 +0200271API int
272nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100273{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200274 client_opts.schema_clb = clb;
275 if (clb) {
276 client_opts.schema_clb_data = user_data;
277 } else {
278 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100279 }
280
281 return 0;
282}
283
Radek Krejcifd5b6682017-06-13 15:52:53 +0200284API ly_module_imp_clb
285nc_client_get_schema_callback(void **user_data)
286{
287 if (user_data) {
288 (*user_data) = client_opts.schema_clb_data;
289 }
290 return client_opts.schema_clb;
291}
292
Radek Krejci65ef6d52018-08-16 16:35:02 +0200293struct schema_info {
294 char *name;
295 char *revision;
296 struct {
297 char *name;
298 char *revision;
299 } *submodules;
Michal Vasko77367452021-02-16 16:32:18 +0100300 char **features;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200301 int implemented;
302};
303
304struct clb_data_s {
305 void *user_data;
306 ly_module_imp_clb user_clb;
307 struct schema_info *schemas;
308 struct nc_session *session;
309 int has_get_schema;
310};
311
312static char *
313retrieve_schema_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
314 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100315{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200316 char *localfile = NULL;
317 FILE *f;
318 long length, l;
319 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100320
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200321 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
322 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
323 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200324 return NULL;
325 }
326 if (localfile) {
327 VRB("Session %u: reading schema from localfile \"%s\".", clb_data->session->id, localfile);
328 f = fopen(localfile, "r");
329 if (!f) {
330 ERR("Session %u: unable to open \"%s\" file to get schema (%s).",
331 clb_data->session->id, localfile, strerror(errno));
332 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) {
339 ERR("Session %u: unable to get size of schema file \"%s\".",
340 clb_data->session->id, localfile);
341 free(localfile);
342 fclose(f);
343 return NULL;
344 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200345 fseek(f, 0, SEEK_SET);
346
347 model_data = malloc(length + 1);
348 if (!model_data) {
349 ERRMEM;
350 } else if ((l = fread(model_data, 1, length, f)) != length) {
351 ERR("Session %u: reading schema from \"%s\" failed (%d bytes read, but %d expected).",
352 clb_data->session->id, localfile, l, length);
353 free(model_data);
354 model_data = NULL;
355 } else {
356 /* terminating NULL byte */
357 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100358 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200359 fclose(f);
360 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100361 }
362
Radek Krejci65ef6d52018-08-16 16:35:02 +0200363 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100364}
365
366static char *
Radek Krejci65ef6d52018-08-16 16:35:02 +0200367retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
368 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100369{
Michal Vasko086311b2016-01-08 09:53:11 +0100370 struct nc_rpc *rpc;
Michal Vasko77367452021-02-16 16:32:18 +0100371 struct lyd_node *envp = NULL, *op = NULL;
372 struct lyd_node_any *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100373 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100374 uint64_t msgid;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200375 char *localfile = NULL;
376 FILE *f;
377 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100378
Radek Krejci65ef6d52018-08-16 16:35:02 +0200379 VRB("Session %u: reading schema from server via get-schema.", clb_data->session->id);
380 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100381
Radek Krejci65ef6d52018-08-16 16:35:02 +0200382 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100383 usleep(1000);
384 }
385 if (msg == NC_MSG_ERROR) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200386 ERR("Session %u: failed to send the <get-schema> RPC.", clb_data->session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100387 nc_rpc_free(rpc);
388 return NULL;
389 }
390
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200391 do {
Michal Vasko77367452021-02-16 16:32:18 +0100392 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200393 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Michal Vasko086311b2016-01-08 09:53:11 +0100394 nc_rpc_free(rpc);
395 if (msg == NC_MSG_WOULDBLOCK) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200396 ERR("Session %u: timeout for receiving reply to a <get-schema> expired.", clb_data->session->id);
Michal Vasko77367452021-02-16 16:32:18 +0100397 goto cleanup;
398 } else if (msg == NC_MSG_ERROR || !op) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200399 ERR("Session %u: failed to receive a reply to <get-schema>.", clb_data->session->id);
Michal Vasko77367452021-02-16 16:32:18 +0100400 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100401 }
402
Michal Vasko77367452021-02-16 16:32:18 +0100403 if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200404 ERR("Session %u: unexpected data in reply to a <get-schema> RPC.", clb_data->session->id);
Michal Vasko77367452021-02-16 16:32:18 +0100405 goto cleanup;
Michal Vaskob2583f12016-05-12 11:40:23 +0200406 }
Michal Vasko77367452021-02-16 16:32:18 +0100407 get_schema_data = (struct lyd_node_any *)lyd_child(op);
Radek Krejci539efb62016-08-24 15:05:16 +0200408 switch (get_schema_data->value_type) {
Radek Krejci539efb62016-08-24 15:05:16 +0200409 case LYD_ANYDATA_STRING:
Michal Vasko77367452021-02-16 16:32:18 +0100410 case LYD_ANYDATA_XML:
Michal Vaskob2583f12016-05-12 11:40:23 +0200411 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200412 break;
413 case LYD_ANYDATA_DATATREE:
Michal Vasko77367452021-02-16 16:32:18 +0100414 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
Radek Krejci539efb62016-08-24 15:05:16 +0200415 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200416 case LYD_ANYDATA_JSON:
Michal Vaskod838d292018-08-15 11:39:05 +0200417 case LYD_ANYDATA_LYB:
Radek Krejci03438ec2016-09-21 14:12:26 +0200418 ERRINT;
Michal Vasko77367452021-02-16 16:32:18 +0100419 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200420 }
Michal Vasko086311b2016-01-08 09:53:11 +0100421
Radek Krejci488b0702018-08-29 14:47:15 +0200422 if (model_data && !model_data[0]) {
423 /* empty data */
424 free(model_data);
425 model_data = NULL;
426 }
427
Radek Krejcifd5b6682017-06-13 15:52:53 +0200428 /* try to store the model_data into local schema repository */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200429 if (model_data) {
430 *format = LYS_IN_YANG;
431 if (client_opts.schema_searchpath) {
Michal Vasko77367452021-02-16 16:32:18 +0100432 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "",
433 rev ? rev : "") == -1) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200434 ERRMEM;
Michal Vaskof945da52018-02-15 08:45:13 +0100435 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200436 f = fopen(localfile, "w");
437 if (!f) {
438 WRN("Unable to store \"%s\" as a local copy of schema retrieved via <get-schema> (%s).",
439 localfile, strerror(errno));
440 } else {
441 fputs(model_data, f);
442 fclose(f);
443 }
444 free(localfile);
Michal Vaskof945da52018-02-15 08:45:13 +0100445 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200446 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200447 }
448
Michal Vasko77367452021-02-16 16:32:18 +0100449cleanup:
450 lyd_free_tree(envp);
451 lyd_free_tree(op);
Michal Vasko086311b2016-01-08 09:53:11 +0100452 return model_data;
453}
454
Jan Kundrát35972df2018-09-06 19:00:01 +0200455static void free_with_user_data(void *data, void *user_data)
456{
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 }
479 if (!match || strcmp(mod_rev, clb_data->schemas[u].revision) > 0) {
480 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) */
487 VRB("Session %u: unable to identify revision of the schema \"%s\" from the available server side information.",
Radek Krejci65ef6d52018-08-16 16:35:02 +0200488 clb_data->session->id, mod_name);
489 }
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 Vasko1d2665c2021-02-12 09:28:44 +0100497 VRB("Session %u: Unable to identify revision of the requested submodule \"%s\", in schema \"%s\", from the available server side information.",
Radek Krejci65ef6d52018-08-16 16:35:02 +0200498 clb_data->session->id, 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) {
506 ERR("Session %u: requested submodule \"%s\" is not known for schema \"%s\" on server side.",
507 clb_data->session->id, 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 Vasko77367452021-02-16 16:32:18 +0100517 VRB("Session %u: retrieving data for schema \"%s\", revision \"%s\".", clb_data->session->id, 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) {
550 VRB("Session %u: reading schema via user callback.", clb_data->session->id);
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;
557 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;
Jan Kundrát35972df2018-09-06 19:00:01 +0200568 void (*free_module_data)(void*, void*) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200569 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100570
Radek Krejci65ef6d52018-08-16 16:35:02 +0200571 *mod = NULL;
572 if (revision) {
Michal Vasko77367452021-02-16 16:32:18 +0100573 *mod = ly_ctx_get_module(session->ctx, name, revision);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200574 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100575 if (*mod) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200576 if (!(*mod)->implemented) {
Michal Vaskoceae0152018-02-14 16:03:59 +0100577 /* make the present module implemented */
Michal Vasko77367452021-02-16 16:32:18 +0100578 if (lys_set_implemented(*mod, NULL)) {
Michal Vaskoceae0152018-02-14 16:03:59 +0100579 ERR("Failed to implement model \"%s\".", (*mod)->name);
580 ret = -1;
581 }
582 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200583 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100584 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200585 clb_data.has_get_schema = has_get_schema;
586 clb_data.schemas = schemas;
587 clb_data.session = session;
588 clb_data.user_clb = user_clb;
589 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100590
591 /* clear all the errors and just collect them for now */
592 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200593 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100594
Radek Krejci65ef6d52018-08-16 16:35:02 +0200595 /* get module data */
Michal Vasko77367452021-02-16 16:32:18 +0100596 retrieve_schema_data(name, revision, NULL, NULL, &clb_data, &format, &module_data, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100597
Radek Krejci65ef6d52018-08-16 16:35:02 +0200598 if (module_data) {
599 /* parse the schema */
600 ly_ctx_set_module_imp_clb(session->ctx, retrieve_schema_data, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100601
Michal Vasko77367452021-02-16 16:32:18 +0100602 lys_parse_mem(session->ctx, module_data, format, (const struct lys_module **)mod);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200603 if (*free_module_data) {
Michal Vasko77367452021-02-16 16:32:18 +0100604 (*free_module_data)((char *)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100605 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100606
Radek Krejci65ef6d52018-08-16 16:35:02 +0200607 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
608 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100609
Michal Vaskodbf7c272018-02-19 09:07:59 +0100610 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200611 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100612 if (!(*mod)) {
613 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
Michal Vasko77367452021-02-16 16:32:18 +0100614 ly_err_print(session->ctx, eitem);
Michal Vaskoceae0152018-02-14 16:03:59 +0100615 }
616 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200617 } else {
618 /* print only warnings */
619 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
620 if (eitem->level == LY_LLWRN) {
Michal Vasko77367452021-02-16 16:32:18 +0100621 ly_err_print(session->ctx, eitem);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200622 }
623 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100624 }
625
Michal Vaskodbf7c272018-02-19 09:07:59 +0100626 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100627 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100628 }
629
630 return ret;
631}
632
Radek Krejci65ef6d52018-08-16 16:35:02 +0200633static void
634free_schema_info(struct schema_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200635{
Michal Vasko77367452021-02-16 16:32:18 +0100636 uint32_t u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200637
Radek Krejci65ef6d52018-08-16 16:35:02 +0200638 if (!list) {
639 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200640 }
641
Radek Krejci65ef6d52018-08-16 16:35:02 +0200642 for (u = 0; list[u].name; ++u) {
643 free(list[u].name);
644 free(list[u].revision);
Michal Vasko77367452021-02-16 16:32:18 +0100645 if (list[u].features) {
646 for (v = 0; list[u].features[v]; ++v) {
647 free(list[u].features[v]);
648 }
649 free(list[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200650 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200651 if (list[u].submodules) {
652 for (v = 0; list[u].submodules[v].name; ++v) {
653 free(list[u].submodules[v].name);
654 free(list[u].submodules[v].revision);
655 }
656 free(list[u].submodules);
657 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200658 }
659 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200660}
661
Radek Krejci235d8cb2018-08-17 14:04:32 +0200662
663static int
664build_schema_info_yl(struct nc_session *session, 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;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200675 int ret = EXIT_SUCCESS;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200676
677 /* get yang-library data from the server */
Radek Krejci261737f2018-08-21 09:22:34 +0200678 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
Michal Vasko77367452021-02-16 16:32:18 +0100679 rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0,
680 NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200681 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100682 rpc = nc_rpc_getdata("ietf-datastores:operational",
683 "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", "false", NULL, 0, 0, 0, 0,
684 0, NC_PARAMTYPE_CONST);
Radek Krejci261737f2018-08-21 09:22:34 +0200685 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200686 if (!rpc) {
687 goto cleanup;
688 }
689
690 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
691 usleep(1000);
692 }
693 if (msg == NC_MSG_ERROR) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200694 WRN("Session %u: failed to send request for yang-library data.",
Radek Krejcifd5b6682017-06-13 15:52:53 +0200695 session->id);
696 goto cleanup;
697 }
698
699 do {
Michal Vasko77367452021-02-16 16:32:18 +0100700 lyd_free_tree(envp);
701 lyd_free_tree(op);
702
703 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op);
Robin Jarry52ddb952019-10-15 16:23:01 +0200704 } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200705 if (msg == NC_MSG_WOULDBLOCK) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200706 WRN("Session %u: timeout for receiving reply to a <get> yang-library data expired.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200707 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100708 } else if (msg == NC_MSG_ERROR) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200709 WRN("Session %u: failed to receive a reply to <get> of yang-library data.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200710 goto cleanup;
Michal Vasko77367452021-02-16 16:32:18 +0100711 } else if (!op || !lyd_child(op) || strcmp(lyd_child(op)->schema->name, "data")) {
712 WRN("Session %u: unexpected reply without data to a yang-library <get> RPC.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200713 goto cleanup;
714 }
715
Michal Vasko77367452021-02-16 16:32:18 +0100716 data = (struct lyd_node_any *)lyd_child(op);
717 if (data->value_type != LYD_ANYDATA_DATATREE) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200718 WRN("Session %u: unexpected data in reply to a yang-library <get> RPC.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200719 goto cleanup;
720 }
721
Michal Vasko77367452021-02-16 16:32:18 +0100722 if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) {
Radek Krejci2d088832018-08-21 13:40:14 +0200723 WRN("Session %u: no module information in reply to a yang-library <get> RPC.", session->id);
724 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200725 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200726
Michal Vasko77367452021-02-16 16:32:18 +0100727 (*result) = calloc(modules->count + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200728 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200729 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200730 ret = EXIT_FAILURE;
Radek Krejcic9390502018-08-17 10:23:13 +0200731 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200732 }
733
Michal Vasko77367452021-02-16 16:32:18 +0100734 for (u = 0; u < modules->count; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200735 submodules_count = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100736 feature_count = 0;
737 mod = ((struct lyd_node *)modules->dnodes[u])->schema->module;
738 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200739 if (iter->schema->module != mod) {
740 /* ignore node from other schemas (augments) */
741 continue;
742 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200743 if (!lyd_get_value(iter) || !lyd_get_value(iter)[0]) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200744 /* ignore empty nodes */
745 continue;
746 }
747 if (!strcmp(iter->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200748 (*result)[u].name = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200749 } else if (!strcmp(iter->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200750 (*result)[u].revision = strdup(lyd_get_value(iter));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200751 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200752 (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200753 } else if (!strcmp(iter->schema->name, "feature")) {
Michal Vasko77367452021-02-16 16:32:18 +0100754 (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features);
755 if (!(*result)[u].features) {
756 ERRMEM;
757 free_schema_info(*result);
758 *result = NULL;
759 ret = EXIT_FAILURE;
760 goto cleanup;
761 }
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200762 (*result)[u].features[feature_count] = strdup(lyd_get_value(iter));
Michal Vasko77367452021-02-16 16:32:18 +0100763 (*result)[u].features[feature_count + 1] = NULL;
764 ++feature_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200765 } else if (!strcmp(iter->schema->name, "submodule")) {
766 submodules_count++;
767 }
768 }
769
770 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200771 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
772 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200773 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200774 free_schema_info(*result);
775 *result = NULL;
776 ret = EXIT_FAILURE;
777 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200778 } else {
779 v = 0;
Michal Vasko77367452021-02-16 16:32:18 +0100780 LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) {
781 mod = modules->dnodes[u]->schema->module;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200782 if (mod == iter->schema->module && !strcmp(iter->schema->name, "submodule")) {
Michal Vasko77367452021-02-16 16:32:18 +0100783 LY_LIST_FOR(lyd_child(iter), child) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200784 if (mod != child->schema->module) {
785 continue;
786 } else if (!strcmp(child->schema->name, "name")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200787 (*result)[u].submodules[v].name = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200788 } else if (!strcmp(child->schema->name, "revision")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200789 (*result)[u].submodules[v].revision = strdup(lyd_get_value(child));
Radek Krejci1a7efb42018-08-17 11:51:23 +0200790 }
791 }
792 }
793 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200794 }
795 }
796 }
797
Radek Krejcifd5b6682017-06-13 15:52:53 +0200798cleanup:
799 nc_rpc_free(rpc);
Michal Vasko77367452021-02-16 16:32:18 +0100800 lyd_free_tree(envp);
801 lyd_free_tree(op);
802 ly_set_free(modules, NULL);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200803
Radek Krejci235d8cb2018-08-17 14:04:32 +0200804 if (session->status != NC_STATUS_RUNNING) {
805 /* something bad heppened, discard the session */
806 ERR("Session %d: invalid session, discarding.", nc_session_get_id(session));
807 ret = EXIT_FAILURE;
808 }
809
810 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200811}
812
Radek Krejci235d8cb2018-08-17 14:04:32 +0200813static int
814build_schema_info_cpblts(char **cpblts, struct schema_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200815{
Michal Vasko77367452021-02-16 16:32:18 +0100816 uint32_t u, v, feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200817 char *module_cpblt, *ptr, *ptr2;
818
819 for (u = 0; cpblts[u]; ++u);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200820 (*result) = calloc(u + 1, sizeof **result);
821 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200822 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200823 return EXIT_FAILURE;
Radek Krejcic9390502018-08-17 10:23:13 +0200824 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200825
826 for (u = v = 0; cpblts[u]; ++u) {
827 module_cpblt = strstr(cpblts[u], "module=");
828 /* this capability requires a module */
829 if (!module_cpblt) {
830 continue;
831 }
832
833 /* get module's name */
834 ptr = (char *)module_cpblt + 7;
835 ptr2 = strchr(ptr, '&');
836 if (!ptr2) {
837 ptr2 = ptr + strlen(ptr);
838 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200839 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200840
841 /* get module's revision */
842 ptr = strstr(module_cpblt, "revision=");
843 if (ptr) {
844 ptr += 9;
845 ptr2 = strchr(ptr, '&');
846 if (!ptr2) {
847 ptr2 = ptr + strlen(ptr);
848 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200849 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200850 }
851
852 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200853 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200854
855 /* get module's features */
856 ptr = strstr(module_cpblt, "features=");
857 if (ptr) {
858 ptr += 9;
Michal Vasko77367452021-02-16 16:32:18 +0100859 feature_count = 0;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200860 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
861 if (*ptr == ',') {
Michal Vasko77367452021-02-16 16:32:18 +0100862 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
863 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
864 (*result)[v].features[feature_count + 1] = NULL;
865 ++feature_count;
866
Radek Krejci65ef6d52018-08-16 16:35:02 +0200867 ptr2 = ptr + 1;
868 }
869 }
870 /* the last one */
Michal Vasko77367452021-02-16 16:32:18 +0100871 (*result)[v].features = nc_realloc((*result)[v].features, (feature_count + 2) * sizeof *(*result)[v].features);
872 (*result)[v].features[feature_count] = strndup(ptr2, ptr - ptr2);
873 (*result)[v].features[feature_count + 1] = NULL;
874 ++feature_count;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200875 }
876 ++v;
877 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200878
879 return EXIT_SUCCESS;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200880}
881
882static int
Michal Vasko77367452021-02-16 16:32:18 +0100883nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data,
884 int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200885{
886 int ret = EXIT_FAILURE;
Michal Vasko77367452021-02-16 16:32:18 +0100887 struct lys_module *mod;
888 uint32_t u;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200889
890 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +0200891 /* skip import-only modules */
892 if (!modules[u].implemented) {
893 continue;
894 }
895
Radek Krejci65ef6d52018-08-16 16:35:02 +0200896 /* we can continue even if it fails */
897 nc_ctx_load_module(session, modules[u].name, modules[u].revision, modules, user_clb, user_data, has_get_schema, &mod);
898
899 if (!mod) {
900 if (session->status != NC_STATUS_RUNNING) {
901 /* something bad heppened, discard the session */
902 ERR("Session %d: invalid session, discarding.", nc_session_get_id(session));
903 goto cleanup;
904 }
905
906 /* all loading ways failed, the schema will be ignored in the received data */
907 WRN("Failed to load schema \"%s@%s\".", modules[u].name, modules[u].revision ? modules[u].revision : "<latest>");
908 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
909 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100910 /* set the features */
911 lys_set_implemented(mod, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200912 }
Michal Vasko60f66602017-10-17 13:52:18 +0200913 }
914
Michal Vasko77367452021-02-16 16:32:18 +0100915 /* success */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200916 ret = EXIT_SUCCESS;
917
918cleanup:
Radek Krejcifd5b6682017-06-13 15:52:53 +0200919 return ret;
920}
921
Radek Krejci65ef6d52018-08-16 16:35:02 +0200922static int
Michal Vasko77367452021-02-16 16:32:18 +0100923nc_ctx_fill_ietf_netconf(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb,
924 void *user_data, int has_get_schema)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200925{
Michal Vasko77367452021-02-16 16:32:18 +0100926 uint32_t u;
927 struct lys_module *ietfnc;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200928
Michal Vasko77367452021-02-16 16:32:18 +0100929 ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Radek Krejci65ef6d52018-08-16 16:35:02 +0200930 if (!ietfnc) {
931 nc_ctx_load_module(session, "ietf-netconf", NULL, modules, user_clb, user_data, has_get_schema, &ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200932 if (!ietfnc) {
Michal Vasko77367452021-02-16 16:32:18 +0100933 lys_parse_mem(session->ctx, ietf_netconf_2013_09_29_yang, LYS_IN_YANG, (const struct lys_module **)&ietfnc);
Michal Vasko8a4e1462020-05-07 11:32:31 +0200934 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200935 }
936 if (!ietfnc) {
937 ERR("Loading base NETCONF schema failed.");
938 return 1;
939 }
940
941 /* set supported capabilities from ietf-netconf */
942 for (u = 0; modules[u].name; ++u) {
943 if (strcmp(modules[u].name, "ietf-netconf") || !modules[u].implemented) {
944 continue;
945 }
946
Michal Vasko77367452021-02-16 16:32:18 +0100947 lys_set_implemented(ietfnc, (const char **)modules[u].features);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200948 }
949
950 return 0;
951}
952
Michal Vasko086311b2016-01-08 09:53:11 +0100953int
954nc_ctx_check_and_fill(struct nc_session *session)
955{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200956 int i, get_schema_support = 0, yanglib_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +0100957 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100958 void *old_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100959 struct lys_module *mod = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +0200960 char *revision;
Radek Krejcib056ff82018-08-20 15:58:39 +0200961 struct schema_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100962
Michal Vasko2e6defd2016-10-07 15:48:15 +0200963 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +0100964
Radek Krejci65ef6d52018-08-16 16:35:02 +0200965 /* 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 +0200966 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200967
Radek Krejci65ef6d52018-08-16 16:35:02 +0200968 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
969 * schemas only to those present on the server side */
Michal Vasko77367452021-02-16 16:32:18 +0100970 ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200971
972 /* our callback is set later with appropriate data */
973 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
974
975 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200976 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200977 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200978 get_schema_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200979 if (yanglib_support) {
980 break;
981 }
Michal Vaskof0fba4e2020-02-14 17:15:31 +0100982 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200983 yanglib_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200984 if (get_schema_support) {
985 break;
986 }
Michal Vasko086311b2016-01-08 09:53:11 +0100987 }
988 }
Michal Vasko31dd4c52020-04-17 10:29:44 +0200989 if (get_schema_support) {
990 VRB("Session %u: capability for <get-schema> support found.", session->id);
991 } else {
992 VRB("Session %u: capability for <get-schema> support not found.", session->id);
993 }
994 if (yanglib_support) {
995 VRB("Session %u: capability for yang-library support found.", session->id);
996 } else {
997 VRB("Session %u: capability for yang-library support not found.", session->id);
998 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200999
1000 /* get information about server's schemas from capabilities list until we will have yang-library */
Radek Krejci235d8cb2018-08-17 14:04:32 +02001001 if (build_schema_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
1002 ERR("Session %u: unable to get server's schema information from the <hello>'s capabilities.", session->id);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001003 goto cleanup;
1004 }
1005
Michal Vasko086311b2016-01-08 09:53:11 +01001006 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Michal Vasko77367452021-02-16 16:32:18 +01001007 if (get_schema_support && lys_parse_mem(session->ctx, ietf_netconf_monitoring_2010_10_04_yang, LYS_IN_YANG, NULL)) {
Michal Vasko8a4e1462020-05-07 11:32:31 +02001008 WRN("Session %u: loading NETCONF monitoring schema failed, cannot use <get-schema>.", session->id);
1009 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001010 }
1011
1012 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001013 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001014 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001015 }
1016
Radek Krejci65ef6d52018-08-16 16:35:02 +02001017 /* get correct version of ietf-yang-library into context */
1018 if (yanglib_support) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001019 /* use get schema to get server's ietf-yang-library */
1020 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1021 if (!revision) {
Michal Vasko77367452021-02-16 16:32:18 +01001022 WRN("Session %u: loading NETCONF ietf-yang-library schema failed, missing revision in NETCONF <hello> message.",
1023 session->id);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001024 WRN("Session %u: unable to automatically use <get-schema>.", session->id);
Radek Krejcib1d250e2018-04-12 13:54:18 +02001025 yanglib_support = 0;
1026 } else {
1027 revision = strndup(&revision[9], 10);
Michal Vasko0d877f92020-04-02 13:52:43 +02001028 if (nc_ctx_load_module(session, "ietf-yang-library", revision, server_modules, old_clb, old_data,
1029 get_schema_support, &mod)) {
Michal Vasko77367452021-02-16 16:32:18 +01001030 WRN("Session %u: loading NETCONF ietf-yang-library schema failed, unable to use it to learn all "
1031 "the supported modules.", session->id);
Radek Krejcib1d250e2018-04-12 13:54:18 +02001032 yanglib_support = 0;
1033 }
Michal Vasko0d877f92020-04-02 13:52:43 +02001034 if (strcmp(revision, "2019-01-04") >= 0) {
1035 /* we also need ietf-datastores to be implemented */
1036 if (nc_ctx_load_module(session, "ietf-datastores", NULL, server_modules, old_clb, old_data,
1037 get_schema_support, &mod)) {
Michal Vasko77367452021-02-16 16:32:18 +01001038 WRN("Session %u: loading NETCONF ietf-datastores schema failed, unable to use yang-library "
1039 "to learn all the supported modules.", session->id);
Michal Vasko0d877f92020-04-02 13:52:43 +02001040 yanglib_support = 0;
1041 }
1042 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001043 free(revision);
Michal Vasko77367452021-02-16 16:32:18 +01001044
1045 /* ietf-netconf-nmda is needed to issue get-data */
1046 if (nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, server_modules, old_clb, old_data,
1047 get_schema_support, &mod)) {
1048 WRN("Session %u: loading NETCONF ietf-netconf-nmda schema failed, unable to use get-data to retrieve "
1049 "yang-library data.", session->id);
1050 yanglib_support = 0;
1051 }
Radek Krejcib1d250e2018-04-12 13:54:18 +02001052 }
1053 }
1054
Radek Krejci65ef6d52018-08-16 16:35:02 +02001055 /* prepare structured information about server's schemas */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001056 if (yanglib_support) {
Radek Krejcif0633792018-08-20 15:12:09 +02001057 if (build_schema_info_yl(session, &sm)) {
1058 goto cleanup;
1059 } else if (!sm) {
1060 VRB("Session %u: trying to use capabilities instead of ietf-yang-library data.", session->id);
1061 } else {
Michal Vasko77367452021-02-16 16:32:18 +01001062 /* prefer yang-library information, currently we have it from capabilities used for getting correct
1063 * yang-library schema */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001064 free_schema_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001065 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001066 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001067 }
Michal Vaskoef578332016-01-25 13:20:09 +01001068
Radek Krejci65ef6d52018-08-16 16:35:02 +02001069 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1070 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001071 }
1072
Radek Krejcifd5b6682017-06-13 15:52:53 +02001073 /* succsess */
1074 ret = 0;
1075
Michal Vaskoeee99412016-11-21 10:19:43 +01001076 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Michal Vasko77367452021-02-16 16:32:18 +01001077 WRN("Session %u: some models failed to be loaded, any data from these models (and any other unknown) will "
1078 "be ignored.", session->id);
Michal Vaskoef578332016-01-25 13:20:09 +01001079 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001080
1081cleanup:
Radek Krejci65ef6d52018-08-16 16:35:02 +02001082 free_schema_info(server_modules);
1083
Radek Krejcifd5b6682017-06-13 15:52:53 +02001084 /* set user callback back */
1085 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko77367452021-02-16 16:32:18 +01001086 ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001087
Michal Vaskoef578332016-01-25 13:20:09 +01001088 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001089}
1090
1091API struct nc_session *
1092nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1093{
Michal Vaskod083db62016-01-19 10:31:29 +01001094 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001095
Michal Vasko45e53ae2016-04-07 11:46:03 +02001096 if (fdin < 0) {
1097 ERRARG("fdin");
1098 return NULL;
1099 } else if (fdout < 0) {
1100 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001101 return NULL;
1102 }
1103
1104 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001105 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001106 if (!session) {
1107 ERRMEM;
1108 return NULL;
1109 }
1110 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001111
1112 /* transport specific data */
1113 session->ti_type = NC_TI_FD;
1114 session->ti.fd.in = fdin;
1115 session->ti.fd.out = fdout;
1116
Robin Jarry4e38e292019-10-15 17:14:14 +02001117 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1118 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +01001119 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001120 ctx = session->ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001121
1122 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001123 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001124 goto fail;
1125 }
1126 session->status = NC_STATUS_RUNNING;
1127
Michal Vaskoef578332016-01-25 13:20:09 +01001128 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001129 goto fail;
1130 }
1131
1132 return session;
1133
1134fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001135 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001136 return NULL;
1137}
1138
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001139API struct nc_session *
1140nc_connect_unix(const char *address, struct ly_ctx *ctx)
1141{
1142 struct nc_session *session = NULL;
1143 struct sockaddr_un sun;
1144 const struct passwd *pw;
1145 char *username;
1146 int sock = -1;
1147
1148 if (address == NULL) {
1149 ERRARG("address");
1150 return NULL;
1151 }
1152
1153 pw = getpwuid(geteuid());
1154 if (pw == NULL) {
1155 ERR("Failed to find username for euid=%u.\n", geteuid());
1156 goto fail;
1157 }
1158
1159 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1160 if (sock < 0) {
1161 ERR("Failed to create socket (%s).", strerror(errno));
1162 goto fail;
1163 }
1164
1165 memset(&sun, 0, sizeof(sun));
1166 sun.sun_family = AF_UNIX;
1167 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1168
1169 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
1170 ERR("cannot connect to sock server %s (%s)",
1171 address, strerror(errno));
1172 goto fail;
1173 }
1174
1175 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
1176 ERR("Fcntl failed (%s).", strerror(errno));
1177 goto fail;
1178 }
1179
1180 /* prepare session structure */
1181 session = nc_new_session(NC_CLIENT, 0);
1182 if (!session) {
1183 ERRMEM;
1184 goto fail;
1185 }
1186 session->status = NC_STATUS_STARTING;
1187
1188 /* transport specific data */
1189 session->ti_type = NC_TI_UNIX;
1190 session->ti.unixsock.sock = sock;
1191 sock = -1; /* do not close sock in fail label anymore */
1192
Robin Jarry4e38e292019-10-15 17:14:14 +02001193 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1194 goto fail;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001195 }
Robin Jarry4e38e292019-10-15 17:14:14 +02001196 ctx = session->ctx;
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001197
Michal Vasko77367452021-02-16 16:32:18 +01001198 lydict_insert(ctx, address, 0, &session->path);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001199
1200 username = strdup(pw->pw_name);
1201 if (username == NULL) {
1202 ERRMEM;
1203 goto fail;
1204 }
Michal Vasko77367452021-02-16 16:32:18 +01001205 lydict_insert_zc(ctx, username, &session->username);
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001206
1207 /* NETCONF handshake */
1208 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1209 goto fail;
1210 }
1211 session->status = NC_STATUS_RUNNING;
1212
1213 if (nc_ctx_check_and_fill(session) == -1) {
1214 goto fail;
1215 }
1216
1217 return session;
1218
1219fail:
1220 nc_session_free(session, NULL);
1221 if (sock >= 0)
1222 close(sock);
1223 return NULL;
1224}
1225
Frank Rimpler9f838b02018-07-25 06:44:03 +00001226/*
1227 Helper for a non-blocking connect (which is required because of the locking
1228 concept for e.g. call home settings). For more details see nc_sock_connect().
1229 */
1230static int
Michal Vasko5e8d0192019-06-24 19:19:49 +02001231_non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +01001232{
Michal Vasko5e8d0192019-06-24 19:19:49 +02001233 int flags, ret, error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001234 int sock = -1;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001235 fd_set wset;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001236 struct timeval ts;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001237 socklen_t len = sizeof(int);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001238 struct in_addr *addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001239 uint16_t port;
Michal Vasko5e8d0192019-06-24 19:19:49 +02001240 char str[INET6_ADDRSTRLEN];
Michal Vasko086311b2016-01-08 09:53:11 +01001241
Frank Rimpler9f838b02018-07-25 06:44:03 +00001242 if (sock_pending && *sock_pending != -1) {
Michal Vasko4c612cd2021-02-05 08:53:42 +01001243 VRB("Trying to connect the pending socket %d.", *sock_pending );
Frank Rimpler9f838b02018-07-25 06:44:03 +00001244 sock = *sock_pending;
1245 } else {
1246 assert(res);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001247 if (res->ai_family == AF_INET6) {
1248 addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001249 port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001250 } else {
1251 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001252 port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001253 }
1254 if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) {
1255 WRN("inet_ntop() failed (%s).", strerror(errno));
1256 } else {
Michal Vasko7e7f99d2019-09-12 13:49:46 +02001257 VRB("Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port);
Michal Vasko5e8d0192019-06-24 19:19:49 +02001258 }
1259
1260 /* connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001261 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1262 if (sock == -1) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001263 ERR("Socket could not be created (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001264 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001265 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001266 /* make the socket non-blocking */
1267 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001268 ERR("fcntl() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001269 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001270 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001271 /* non-blocking connect! */
1272 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1273 if (errno != EINPROGRESS) {
1274 /* network connection failed, try another resource */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001275 ERR("connect() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001276 goto cleanup;
1277 }
1278 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001279 }
1280 ts.tv_sec = timeout;
1281 ts.tv_usec = 0;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001282
Frank Rimpler9f838b02018-07-25 06:44:03 +00001283 FD_ZERO(&wset);
1284 FD_SET(sock, &wset);
1285
1286 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001287 ERR("select() failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001288 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001289 }
1290
Michal Vasko5e8d0192019-06-24 19:19:49 +02001291 if (ret == 0) {
1292 /* there was a timeout */
1293 VRB("Timed out after %ds (%s).", timeout, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001294 if (sock_pending) {
1295 /* no sock-close, we'll try it again */
1296 *sock_pending = sock;
1297 } else {
1298 close(sock);
1299 }
1300 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001301 }
Radek Krejci782041a2018-08-20 10:09:45 +02001302
1303 /* check the usability of the socket */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001304 error = 0;
Radek Krejci782041a2018-08-20 10:09:45 +02001305 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
Michal Vasko5e8d0192019-06-24 19:19:49 +02001306 ERR("getsockopt() failed (%s).", strerror(errno));
Radek Krejci782041a2018-08-20 10:09:45 +02001307 goto cleanup;
1308 }
Michal Vaskoe27ab4e2020-07-27 11:10:58 +02001309 if (error) {
Radek Krejci782041a2018-08-20 10:09:45 +02001310 /* network connection failed, try another resource */
Michal Vasko5e8d0192019-06-24 19:19:49 +02001311 VRB("getsockopt() error (%s).", strerror(error));
Radek Krejci782041a2018-08-20 10:09:45 +02001312 errno = error;
1313 goto cleanup;
1314 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001315
1316 /* enable keep-alive */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001317 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vaskobe52dc22018-10-17 09:28:17 +02001318 goto cleanup;
1319 }
1320
Michal Vasko086311b2016-01-08 09:53:11 +01001321 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001322
Frank Rimpler9f838b02018-07-25 06:44:03 +00001323cleanup:
1324 if (sock_pending) {
1325 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001326 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001327 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001328 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001329}
1330
Frank Rimpler9f838b02018-07-25 06:44:03 +00001331/* A given timeout value limits the time how long the function blocks. If it has to block
1332 only for some seconds, a socket connection might not yet have been fully established.
1333 Therefore the active (pending) socket will be stored in *sock_pending, but the return
1334 value will be -1. In such a case a subsequent invokation is required, by providing the
1335 stored sock_pending, again.
1336 In general, if this function returns -1, when a timeout has been given, this function
1337 has to be invoked, until it returns a valid socket.
1338 */
1339int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001340nc_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 +00001341{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001342 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001343 int sock = sock_pending ? *sock_pending : -1;
Michal Vasko0be85692021-03-02 08:04:57 +01001344 struct addrinfo hints, *res_list = NULL, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001345 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001346 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001347
Michal Vasko66032bc2019-01-22 15:03:12 +01001348 DBG("nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001349
1350 /* no pending socket */
1351 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001352 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001353 snprintf(port_s, 6, "%u", port);
1354 memset(&hints, 0, sizeof hints);
1355 hints.ai_family = AF_UNSPEC;
1356 hints.ai_socktype = SOCK_STREAM;
1357 hints.ai_protocol = IPPROTO_TCP;
1358 i = getaddrinfo(host, port_s, &hints, &res_list);
1359 if (i != 0) {
1360 ERR("Unable to translate the host address (%s).", gai_strerror(i));
Michal Vaskocb846632019-12-13 15:12:45 +01001361 goto error;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001362 }
1363
1364 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001365 sock = _non_blocking_connect(timeout, sock_pending, res, ka);
Michal Vasko2af7c382020-07-27 14:21:35 +02001366 if (sock == -1) {
1367 if (!sock_pending || *sock_pending == -1) {
1368 /* try the next resource */
1369 continue;
1370 } else {
1371 /* timeout, keep pending socket */
Michal Vasko0be85692021-03-02 08:04:57 +01001372 break;
Michal Vasko2af7c382020-07-27 14:21:35 +02001373 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001374 }
1375 VRB("Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
Michal Vasko83ad17e2019-01-30 10:11:37 +01001376
1377 opt = 1;
1378 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
1379 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vaskocb846632019-12-13 15:12:45 +01001380 goto error;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001381 }
1382
Michal Vasko66032bc2019-01-22 15:03:12 +01001383 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1384 buf = malloc(INET6_ADDRSTRLEN);
1385 if (!buf) {
1386 ERRMEM;
Michal Vaskocb846632019-12-13 15:12:45 +01001387 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001388 }
1389 if (res->ai_family == AF_INET) {
1390 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1391 } else {
1392 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1393 }
1394 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
1395 ERR("Converting host to IP address failed (%s).", strerror(errno));
1396 free(buf);
Michal Vaskocb846632019-12-13 15:12:45 +01001397 goto error;
Michal Vasko66032bc2019-01-22 15:03:12 +01001398 }
1399
1400 *ip_host = buf;
1401 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001402 break;
1403 }
1404 freeaddrinfo(res_list);
1405
1406 } else {
1407 /* try to get a connection with the pending socket */
1408 assert(sock_pending);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001409 sock = _non_blocking_connect(timeout, sock_pending, NULL, ka);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001410 }
1411
1412 return sock;
Michal Vaskocb846632019-12-13 15:12:45 +01001413
1414error:
Michal Vasko0be85692021-03-02 08:04:57 +01001415 if (res_list) {
1416 freeaddrinfo(res_list);
1417 }
Michal Vaskocb846632019-12-13 15:12:45 +01001418 if (sock != -1) {
1419 close(sock);
1420 }
1421 if (sock_pending) {
1422 *sock_pending = -1;
1423 }
1424 return -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001425}
1426
Radek Krejci53691be2016-02-22 13:58:37 +01001427#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001428
Michal Vasko3031aae2016-01-27 16:07:18 +01001429int
1430nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1431{
1432 int sock;
1433
Michal Vasko45e53ae2016-04-07 11:46:03 +02001434 if (!address) {
1435 ERRARG("address");
1436 return -1;
1437 } else if (!port) {
1438 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001439 return -1;
1440 }
1441
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001442 sock = nc_sock_listen_inet(address, port, &client_opts.ka);
Michal Vasko3031aae2016-01-27 16:07:18 +01001443 if (sock == -1) {
1444 return -1;
1445 }
1446
1447 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001448 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1449 if (!client_opts.ch_binds) {
1450 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001451 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001452 return -1;
1453 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001454
Michal Vasko2e6defd2016-10-07 15:48:15 +02001455 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1456 if (!client_opts.ch_bind_ti) {
1457 ERRMEM;
1458 close(sock);
1459 return -1;
1460 }
1461 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1462
Michal Vasko3031aae2016-01-27 16:07:18 +01001463 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001464 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1465 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001466 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001467 return -1;
1468 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001469 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1470 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001471 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001472
1473 return 0;
1474}
1475
1476int
1477nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1478{
1479 uint32_t i;
1480 int ret = -1;
1481
1482 if (!address && !port && !ti) {
1483 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1484 close(client_opts.ch_binds[i].sock);
1485 free((char *)client_opts.ch_binds[i].address);
1486
1487 ret = 0;
1488 }
1489 free(client_opts.ch_binds);
1490 client_opts.ch_binds = NULL;
1491 client_opts.ch_bind_count = 0;
1492 } else {
1493 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1494 if ((!address || !strcmp(client_opts.ch_binds[i].address, address))
1495 && (!port || (client_opts.ch_binds[i].port == port))
Michal Vasko2e6defd2016-10-07 15:48:15 +02001496 && (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001497 close(client_opts.ch_binds[i].sock);
1498 free((char *)client_opts.ch_binds[i].address);
1499
1500 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001501 if (!client_opts.ch_bind_count) {
1502 free(client_opts.ch_binds);
1503 client_opts.ch_binds = NULL;
1504 } else if (i < client_opts.ch_bind_count) {
1505 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1506 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1507 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001508
1509 ret = 0;
1510 }
1511 }
1512 }
1513
1514 return ret;
1515}
1516
1517API int
1518nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1519{
1520 int sock;
1521 char *host = NULL;
1522 uint16_t port, idx;
1523
Michal Vasko45e53ae2016-04-07 11:46:03 +02001524 if (!client_opts.ch_binds) {
1525 ERRINIT;
1526 return -1;
1527 } else if (!session) {
1528 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001529 return -1;
1530 }
1531
1532 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1533
Michal Vasko50456e82016-02-02 12:16:08 +01001534 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001535 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001536 return sock;
1537 }
1538
Radek Krejci53691be2016-02-22 13:58:37 +01001539#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001540 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001541 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001542 } else
1543#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001544#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001545 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001546 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001547 } else
1548#endif
1549 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001550 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001551 *session = NULL;
1552 }
1553
1554 free(host);
1555
1556 if (!(*session)) {
1557 return -1;
1558 }
1559
1560 return 1;
1561}
1562
Radek Krejci53691be2016-02-22 13:58:37 +01001563#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001564
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001565API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001566nc_session_get_cpblts(const struct nc_session *session)
1567{
1568 if (!session) {
1569 ERRARG("session");
1570 return NULL;
1571 }
1572
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001573 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001574}
1575
1576API const char *
1577nc_session_cpblt(const struct nc_session *session, const char *capab)
1578{
1579 int i, len;
1580
1581 if (!session) {
1582 ERRARG("session");
1583 return NULL;
1584 } else if (!capab) {
1585 ERRARG("capab");
1586 return NULL;
1587 }
1588
1589 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001590 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1591 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1592 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001593 }
1594 }
1595
1596 return NULL;
1597}
1598
Michal Vasko9cd26a82016-05-31 08:58:48 +02001599API int
1600nc_session_ntf_thread_running(const struct nc_session *session)
1601{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001602 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001603 ERRARG("session");
1604 return 0;
1605 }
1606
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02001607 return ATOMIC_LOAD(session->opts.client.ntf_tid) ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001608}
1609
Michal Vaskob7558c52016-02-26 15:04:19 +01001610API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001611nc_client_init(void)
1612{
1613 nc_init();
1614}
1615
1616API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001617nc_client_destroy(void)
1618{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001619 nc_client_set_schema_searchpath(NULL);
1620#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
1621 nc_client_ch_del_bind(NULL, 0, 0);
1622#endif
1623#ifdef NC_ENABLED_SSH
1624 nc_client_ssh_destroy_opts();
1625#endif
1626#ifdef NC_ENABLED_TLS
1627 nc_client_tls_destroy_opts();
1628#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001629 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001630}
1631
Michal Vasko77367452021-02-16 16:32:18 +01001632static NC_MSG_TYPE
1633recv_reply_check_msgid(struct nc_session *session, const struct lyd_node *envp, uint64_t msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001634{
Michal Vasko77367452021-02-16 16:32:18 +01001635 char *ptr;
1636 struct lyd_attr *attr;
1637 uint64_t cur_msgid;
1638
1639 assert(envp && !envp->schema);
1640
1641 /* find the message-id attribute */
1642 LY_LIST_FOR(((struct lyd_node_opaq *)envp)->attr, attr) {
1643 if (!strcmp(attr->name.name, "message-id")) {
1644 break;
1645 }
1646 }
1647
1648 if (!attr) {
1649 ERR("Session %u: received a <rpc-reply> without a message-id.", session->id);
1650 return NC_MSG_REPLY_ERR_MSGID;
1651 }
1652
1653 cur_msgid = strtoul(attr->value, &ptr, 10);
1654 if (cur_msgid != msgid) {
1655 ERR("Session %u: received a <rpc-reply> with an unexpected message-id %" PRIu64 " (expected %" PRIu64 ").",
1656 session->id, cur_msgid, msgid);
1657 return NC_MSG_REPLY_ERR_MSGID;
1658 }
1659
1660 return NC_MSG_REPLY;
1661}
1662
1663static NC_MSG_TYPE
1664recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_t msgid, struct lyd_node **envp)
1665{
1666 int r;
1667 LY_ERR lyrc;
1668 struct ly_in *msg = NULL;
1669 struct nc_msg_cont *cont, **cont_ptr;
1670 NC_MSG_TYPE ret = NC_MSG_ERROR;
1671
1672 assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION)));
1673
1674 *envp = NULL;
1675
1676 /* try to get rpc-reply from the session's queue */
1677 while (session->opts.client.replies) {
1678 cont = session->opts.client.replies;
1679 session->opts.client.replies = cont->next;
1680
1681 msg = cont->msg;
1682 free(cont);
1683
1684 /* parse */
1685 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
1686 if (!lyrc) {
1687 ret = recv_reply_check_msgid(session, *envp, msgid);
1688 goto cleanup;
1689 } else if (lyrc != LY_ENOT) {
1690 lyd_free_tree(*envp);
1691 *envp = NULL;
1692 ERR("Session %u: received an invalid message (%s).", session->id, ly_errmsg(LYD_CTX(op)));
1693 goto cleanup;
1694 } else {
1695 /* it was not a notification so it is nothing known */
1696 ERR("Session %u: received an unexpected message.", session->id);
1697 }
1698
1699 /* try the next message */
1700 ly_in_free(msg, 1);
1701 msg = NULL;
1702 }
1703
1704 /* read message from wire */
1705 r = nc_read_msg_poll_io(session, timeout, &msg);
1706 if (!r) {
1707 ret = NC_MSG_WOULDBLOCK;
1708 goto cleanup;
1709 } else if (r == -1) {
1710 goto cleanup;
1711 }
1712
1713 /* parse */
1714 lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL);
1715 if (!lyrc) {
1716 ret = recv_reply_check_msgid(session, *envp, msgid);
1717 goto cleanup;
1718 } else if (lyrc != LY_ENOT) {
1719 lyd_free_tree(*envp);
1720 *envp = NULL;
1721 ERR("Session %u: received an invalid message (%s).", session->id, ly_errmsg(LYD_CTX(op)));
1722 goto cleanup;
1723 }
1724
Michal Vaskoc5ea6382021-03-15 16:24:08 +01001725 /* assume a notification, reset and store it */
1726 ly_in_reset(msg);
Michal Vasko77367452021-02-16 16:32:18 +01001727 cont_ptr = &session->opts.client.notifs;
1728 while (*cont_ptr) {
1729 cont_ptr = &((*cont_ptr)->next);
1730 }
1731 *cont_ptr = malloc(sizeof **cont_ptr);
1732 if (!*cont_ptr) {
1733 ERRMEM;
1734 goto cleanup;
1735 }
1736 (*cont_ptr)->msg = msg;
Michal Vaskoc5ea6382021-03-15 16:24:08 +01001737 msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001738 (*cont_ptr)->next = NULL;
1739
1740 ret = NC_MSG_NOTIF;
1741
1742cleanup:
1743 ly_in_free(msg, 1);
1744 return ret;
1745}
1746
1747static int
1748recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_node **op)
1749{
1750 LY_ERR lyrc;
1751 struct nc_rpc_act_generic *rpc_gen;
1752 struct ly_in *in;
1753 struct lyd_node *tree, *op2;
1754 const struct lys_module *mod;
Michal Vasko305faca2021-03-25 09:16:02 +01001755 const char *module_name = NULL, *rpc_name = NULL, *module_check = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01001756
1757 switch (rpc->type) {
1758 case NC_RPC_ACT_GENERIC:
1759 rpc_gen = (struct nc_rpc_act_generic *)rpc;
1760 if (rpc_gen->has_data) {
1761 tree = rpc_gen->content.data;
1762
1763 /* find the operation node */
1764 lyrc = LY_EINVAL;
1765 LYD_TREE_DFS_BEGIN(tree, op2) {
1766 if (op2->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
1767 lyrc = lyd_dup_single(op2, NULL, 0, op);
1768 break;
1769 }
1770 LYD_TREE_DFS_END(tree, op2);
1771 }
1772 } else {
1773 ly_in_new_memory(rpc_gen->content.xml_str, &in);
1774 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2);
1775 ly_in_free(in, 0);
1776 if (lyrc) {
1777 return -1;
1778 }
1779
1780 /* we want just the operation node */
1781 lyrc = lyd_dup_single(op2, NULL, 0, op);
1782
1783 lyd_free_tree(tree);
1784 }
1785 break;
1786 case NC_RPC_GETCONFIG:
1787 module_name = "ietf-netconf";
1788 rpc_name = "get-config";
1789 break;
1790 case NC_RPC_EDIT:
1791 module_name = "ietf-netconf";
1792 rpc_name = "edit-config";
1793 break;
1794 case NC_RPC_COPY:
1795 module_name = "ietf-netconf";
1796 rpc_name = "copy-config";
1797 break;
1798 case NC_RPC_DELETE:
1799 module_name = "ietf-netconf";
1800 rpc_name = "delete-config";
1801 break;
1802 case NC_RPC_LOCK:
1803 module_name = "ietf-netconf";
1804 rpc_name = "lock";
1805 break;
1806 case NC_RPC_UNLOCK:
1807 module_name = "ietf-netconf";
1808 rpc_name = "unlock";
1809 break;
1810 case NC_RPC_GET:
1811 module_name = "ietf-netconf";
1812 rpc_name = "get";
1813 break;
1814 case NC_RPC_KILL:
1815 module_name = "ietf-netconf";
1816 rpc_name = "kill-session";
1817 break;
1818 case NC_RPC_COMMIT:
1819 module_name = "ietf-netconf";
1820 rpc_name = "commit";
1821 break;
1822 case NC_RPC_DISCARD:
1823 module_name = "ietf-netconf";
1824 rpc_name = "discard-changes";
1825 break;
1826 case NC_RPC_CANCEL:
1827 module_name = "ietf-netconf";
1828 rpc_name = "cancel-commit";
1829 break;
1830 case NC_RPC_VALIDATE:
1831 module_name = "ietf-netconf";
1832 rpc_name = "validate";
1833 break;
1834 case NC_RPC_GETSCHEMA:
1835 module_name = "ietf-netconf-monitoring";
1836 rpc_name = "get-schema";
1837 break;
1838 case NC_RPC_SUBSCRIBE:
1839 module_name = "notifications";
1840 rpc_name = "subscribe";
1841 break;
1842 case NC_RPC_GETDATA:
1843 module_name = "ietf-netconf-nmda";
1844 rpc_name = "get-data";
1845 break;
1846 case NC_RPC_EDITDATA:
1847 module_name = "ietf-netconf-nmda";
1848 rpc_name = "edit-data";
1849 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01001850 case NC_RPC_ESTABLISHSUB:
1851 module_name = "ietf-subscribed-notifications";
1852 rpc_name = "establish-subscription";
1853 break;
1854 case NC_RPC_MODIFYSUB:
1855 module_name = "ietf-subscribed-notifications";
1856 rpc_name = "modify-subscription";
1857 break;
1858 case NC_RPC_DELETESUB:
1859 module_name = "ietf-subscribed-notifications";
1860 rpc_name = "delete-subscription";
1861 break;
1862 case NC_RPC_KILLSUB:
1863 module_name = "ietf-subscribed-notifications";
1864 rpc_name = "kill-subscription";
1865 break;
Michal Vasko305faca2021-03-25 09:16:02 +01001866 case NC_RPC_ESTABLISHPUSH:
1867 module_name = "ietf-subscribed-notifications";
1868 rpc_name = "establish-subscription";
1869 module_check = "ietf-yang-push";
1870 break;
1871 case NC_RPC_MODIFYPUSH:
1872 module_name = "ietf-subscribed-notifications";
1873 rpc_name = "modify-subscription";
1874 module_check = "ietf-yang-push";
1875 break;
1876 case NC_RPC_RESYNCSUB:
1877 module_name = "ietf-yang-push";
1878 rpc_name = "resync-subscription";
1879 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01001880 case NC_RPC_UNKNOWN:
Michal Vasko77367452021-02-16 16:32:18 +01001881 lyrc = LY_EINT;
1882 break;
1883 }
1884
1885 if (module_name && rpc_name) {
1886 mod = ly_ctx_get_module_implemented(session->ctx, module_name);
1887 if (!mod) {
1888 ERR("Session %u: missing \"%s\" schema in the context.", session->id, module_name);
1889 return -1;
1890 }
1891
1892 /* create the operation node */
1893 lyrc = lyd_new_inner(NULL, mod, rpc_name, 0, op);
1894 }
Michal Vasko305faca2021-03-25 09:16:02 +01001895 if (module_check) {
1896 if (!ly_ctx_get_module_implemented(session->ctx, module_check)) {
1897 ERR("Session %u: missing \"%s\" schema in the context.", session->id, module_check);
1898 return -1;
1899 }
1900 }
Michal Vasko77367452021-02-16 16:32:18 +01001901
1902 if (lyrc) {
1903 return -1;
1904 }
1905 return 0;
1906}
1907
1908API NC_MSG_TYPE
1909nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, struct lyd_node **envp,
1910 struct lyd_node **op)
1911{
1912 NC_MSG_TYPE ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001913
Michal Vasko45e53ae2016-04-07 11:46:03 +02001914 if (!session) {
1915 ERRARG("session");
1916 return NC_MSG_ERROR;
1917 } else if (!rpc) {
1918 ERRARG("rpc");
1919 return NC_MSG_ERROR;
Michal Vasko5a2c7162020-01-30 11:24:17 +01001920 } else if (!msgid) {
1921 ERRARG("msgid");
1922 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001923 } else if (!envp) {
1924 ERRARG("envp");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001925 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001926 } else if (!op) {
1927 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01001928 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001929 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001930 ERR("Session %u: invalid session to receive RPC replies.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001931 return NC_MSG_ERROR;
1932 }
Michal Vasko77367452021-02-16 16:32:18 +01001933
1934 /* get a duplicate of the RPC node to append reply to */
1935 if (recv_reply_dup_rpc(session, rpc, op)) {
1936 return NC_MSG_ERROR;
Michal Vaskoeee99412016-11-21 10:19:43 +01001937 }
Michal Vasko086311b2016-01-08 09:53:11 +01001938
Michal Vasko77367452021-02-16 16:32:18 +01001939 /* receive a reply */
1940 ret = recv_reply(session, timeout, *op, msgid, envp);
Michal Vasko086311b2016-01-08 09:53:11 +01001941
Michal Vasko77367452021-02-16 16:32:18 +01001942 /* do not return the RPC copy on error or if the reply includes no data */
1943 if (((ret != NC_MSG_REPLY) && (ret != NC_MSG_REPLY_ERR_MSGID)) || !lyd_child(*op)) {
1944 lyd_free_tree(*op);
1945 *op = NULL;
1946 }
1947 return ret;
1948}
1949
1950static NC_MSG_TYPE
1951recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
1952{
1953 int r;
1954 LY_ERR lyrc;
1955 struct ly_in *msg = NULL;
1956 struct nc_msg_cont *cont, **cont_ptr;
1957 NC_MSG_TYPE ret = NC_MSG_ERROR;
1958
1959 *op = NULL;
1960 *envp = NULL;
1961
1962 /* try to get notification from the session's queue */
1963 while (session->opts.client.notifs) {
1964 cont = session->opts.client.notifs;
1965 session->opts.client.notifs = cont->next;
1966
1967 msg = cont->msg;
1968 free(cont);
1969
1970 /* parse */
1971 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
1972 if (!lyrc) {
1973 ret = NC_MSG_NOTIF;
1974 goto cleanup;
1975 } else if (lyrc != LY_ENOT) {
1976 lyd_free_tree(*envp);
1977 *envp = NULL;
1978 ERR("Session %u: received an invalid message (%s).", session->id, ly_errmsg(session->ctx));
1979 goto cleanup;
1980 } else {
1981 /* it was not a rpc-reply so it is nothing known */
1982 ERR("Session %u: received an unexpected message.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001983 }
Michal Vasko086311b2016-01-08 09:53:11 +01001984
Michal Vasko77367452021-02-16 16:32:18 +01001985 /* try the next message */
1986 ly_in_free(msg, 1);
1987 msg = NULL;
1988 }
1989
1990 /* read message from wire */
1991 r = nc_read_msg_poll_io(session, timeout, &msg);
1992 if (!r) {
1993 ret = NC_MSG_WOULDBLOCK;
1994 goto cleanup;
1995 } else if (r == -1) {
1996 goto cleanup;
1997 }
1998
1999 /* parse */
2000 lyrc = lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_NOTIF_NETCONF, envp, op);
2001 if (!lyrc) {
2002 ret = NC_MSG_NOTIF;
2003 goto cleanup;
2004 } else if (lyrc != LY_ENOT) {
2005 lyd_free_tree(*envp);
2006 *envp = NULL;
2007 ERR("Session %u: received an invalid message (%s).", session->id, ly_errmsg(session->ctx));
2008 goto cleanup;
2009 }
2010
Michal Vaskoc5ea6382021-03-15 16:24:08 +01002011 /* assume a rpc-reply, reset and store it */
2012 ly_in_reset(msg);
Michal Vasko77367452021-02-16 16:32:18 +01002013 cont_ptr = &session->opts.client.replies;
2014 while (*cont_ptr) {
2015 cont_ptr = &((*cont_ptr)->next);
2016 }
2017 *cont_ptr = malloc(sizeof **cont_ptr);
2018 if (!*cont_ptr) {
2019 ERRMEM;
2020 goto cleanup;
2021 }
2022 (*cont_ptr)->msg = msg;
Michal Vaskoc5ea6382021-03-15 16:24:08 +01002023 msg = NULL;
Michal Vasko77367452021-02-16 16:32:18 +01002024 (*cont_ptr)->next = NULL;
2025
2026 ret = NC_MSG_REPLY;
2027
2028cleanup:
2029 ly_in_free(msg, 1);
2030 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01002031}
2032
2033API NC_MSG_TYPE
Michal Vasko77367452021-02-16 16:32:18 +01002034nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op)
Michal Vasko086311b2016-01-08 09:53:11 +01002035{
Michal Vasko45e53ae2016-04-07 11:46:03 +02002036 if (!session) {
2037 ERRARG("session");
2038 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01002039 } else if (!envp) {
2040 ERRARG("envp");
2041 return NC_MSG_ERROR;
2042 } else if (!op) {
2043 ERRARG("op");
Michal Vasko086311b2016-01-08 09:53:11 +01002044 return NC_MSG_ERROR;
2045 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01002046 ERR("Session %u: invalid session to receive Notifications.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002047 return NC_MSG_ERROR;
2048 }
2049
Michal Vasko77367452021-02-16 16:32:18 +01002050 /* receive a notification */
2051 return recv_notif(session, timeout, envp, op);
Michal Vasko086311b2016-01-08 09:53:11 +01002052}
2053
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002054static void *
2055nc_recv_notif_thread(void *arg)
2056{
2057 struct nc_ntf_thread_arg *ntarg;
2058 struct nc_session *session;
Michal Vasko77367452021-02-16 16:32:18 +01002059 void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
2060 struct lyd_node *envp, *op;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002061 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002062 pthread_t *ntf_tid;
2063
2064 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002065
2066 ntarg = (struct nc_ntf_thread_arg *)arg;
2067 session = ntarg->session;
2068 notif_clb = ntarg->notif_clb;
2069 free(ntarg);
2070
Michal Vasko131120a2018-05-29 15:44:02 +02002071 /* remember our allocated tid, we will be freeing it */
Michal Vaskob639e9c2020-09-09 09:51:48 +02002072 ntf_tid = (pthread_t *)ATOMIC_LOAD(session->opts.client.ntf_tid);
Michal Vasko131120a2018-05-29 15:44:02 +02002073
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002074 while (ATOMIC_LOAD(session->opts.client.ntf_tid)) {
Michal Vasko77367452021-02-16 16:32:18 +01002075 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002076 if (msgtype == NC_MSG_NOTIF) {
Michal Vasko77367452021-02-16 16:32:18 +01002077 notif_clb(session, envp, op);
2078 if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) {
2079 lyd_free_tree(envp);
2080 lyd_free_tree(op);
Michal Vaskof0537d82016-01-29 14:42:38 +01002081 break;
2082 }
Michal Vasko77367452021-02-16 16:32:18 +01002083 lyd_free_tree(envp);
2084 lyd_free_tree(op);
Michal Vaskoce326052018-01-04 10:32:03 +01002085 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002086 /* quit this thread once the session is broken */
2087 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002088 }
2089
2090 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2091 }
2092
Michal Vasko0651c902016-05-19 15:55:42 +02002093 VRB("Session %u: notification thread exit.", session->id);
Michal Vaskob639e9c2020-09-09 09:51:48 +02002094 ATOMIC_STORE(session->opts.client.ntf_tid, (uintptr_t)NULL);
Michal Vasko131120a2018-05-29 15:44:02 +02002095 free(ntf_tid);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002096 return NULL;
2097}
2098
2099API int
Michal Vasko77367452021-02-16 16:32:18 +01002100nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session,
2101 const struct lyd_node *envp, const struct lyd_node *op))
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002102{
2103 struct nc_ntf_thread_arg *ntarg;
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002104 pthread_t *tid;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002105 int ret;
2106
Michal Vasko45e53ae2016-04-07 11:46:03 +02002107 if (!session) {
2108 ERRARG("session");
2109 return -1;
2110 } else if (!notif_clb) {
2111 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002112 return -1;
2113 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
2114 ERR("Session %u: invalid session to receive Notifications.", session->id);
2115 return -1;
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002116 } else if (ATOMIC_LOAD(session->opts.client.ntf_tid)) {
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002117 ERR("Session %u: separate notification thread is already running.", session->id);
2118 return -1;
2119 }
2120
2121 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002122 if (!ntarg) {
2123 ERRMEM;
2124 return -1;
2125 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002126 ntarg->session = session;
2127 ntarg->notif_clb = notif_clb;
2128
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002129 tid = malloc(sizeof *tid);
2130 if (!tid) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01002131 ERRMEM;
2132 free(ntarg);
2133 return -1;
2134 }
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002135 /* just so that nc_recv_notif_thread() does not immediately exit, the value does not matter */
Michal Vaskob639e9c2020-09-09 09:51:48 +02002136 ATOMIC_STORE(session->opts.client.ntf_tid, (uintptr_t)tid);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002137
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002138 ret = pthread_create(tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002139 if (ret) {
2140 ERR("Session %u: failed to create a new thread (%s).", strerror(errno));
2141 free(ntarg);
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002142 free(tid);
Michal Vaskob639e9c2020-09-09 09:51:48 +02002143 ATOMIC_STORE(session->opts.client.ntf_tid, (uintptr_t)NULL);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002144 return -1;
2145 }
2146
2147 return 0;
2148}
2149
Michal Vasko77367452021-02-16 16:32:18 +01002150static const char *
2151nc_wd2str(NC_WD_MODE wd)
2152{
2153 switch (wd) {
2154 case NC_WD_ALL:
2155 return "report-all";
2156 case NC_WD_ALL_TAG:
2157 return "report-all-tagged";
2158 case NC_WD_TRIM:
2159 return "trim";
2160 case NC_WD_EXPLICIT:
2161 return "explicit";
2162 default:
2163 break;
2164 }
2165
2166 return NULL;
2167}
2168
Michal Vasko086311b2016-01-08 09:53:11 +01002169API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002170nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002171{
2172 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002173 int dofree = 1;
Michal Vasko77367452021-02-16 16:32:18 +01002174 struct ly_in *in;
Michal Vasko90e8e692016-07-13 12:27:57 +02002175 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002176 struct nc_rpc_getconfig *rpc_gc;
2177 struct nc_rpc_edit *rpc_e;
2178 struct nc_rpc_copy *rpc_cp;
2179 struct nc_rpc_delete *rpc_del;
2180 struct nc_rpc_lock *rpc_lock;
2181 struct nc_rpc_get *rpc_g;
2182 struct nc_rpc_kill *rpc_k;
2183 struct nc_rpc_commit *rpc_com;
2184 struct nc_rpc_cancel *rpc_can;
2185 struct nc_rpc_validate *rpc_val;
2186 struct nc_rpc_getschema *rpc_gs;
2187 struct nc_rpc_subscribe *rpc_sub;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002188 struct nc_rpc_getdata *rpc_getd;
2189 struct nc_rpc_editdata *rpc_editd;
Michal Vasko96f247a2021-03-15 13:32:10 +01002190 struct nc_rpc_establishsub *rpc_estsub;
2191 struct nc_rpc_modifysub *rpc_modsub;
2192 struct nc_rpc_deletesub *rpc_delsub;
2193 struct nc_rpc_killsub *rpc_killsub;
Michal Vasko305faca2021-03-25 09:16:02 +01002194 struct nc_rpc_establishpush *rpc_estpush;
2195 struct nc_rpc_modifypush *rpc_modpush;
2196 struct nc_rpc_resyncsub *rpc_resyncsub;
2197 struct lyd_node *data, *node, *cont;
2198 const struct lys_module *mod = NULL, *mod2 = NULL, *ietfncwd;
Michal Vasko77367452021-02-16 16:32:18 +01002199 LY_ERR lyrc;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002200 int i;
Radek Krejci539efb62016-08-24 15:05:16 +02002201 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002202 uint64_t cur_msgid;
2203
Michal Vasko45e53ae2016-04-07 11:46:03 +02002204 if (!session) {
2205 ERRARG("session");
2206 return NC_MSG_ERROR;
2207 } else if (!rpc) {
2208 ERRARG("rpc");
2209 return NC_MSG_ERROR;
2210 } else if (!msgid) {
2211 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002212 return NC_MSG_ERROR;
2213 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01002214 ERR("Session %u: invalid session to send RPCs.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002215 return NC_MSG_ERROR;
2216 }
2217
Michal Vaskoc1171a42019-11-05 12:06:46 +01002218 switch (rpc->type) {
2219 case NC_RPC_ACT_GENERIC:
2220 /* checked when parsing */
2221 break;
2222 case NC_RPC_GETCONFIG:
2223 case NC_RPC_EDIT:
2224 case NC_RPC_COPY:
2225 case NC_RPC_DELETE:
2226 case NC_RPC_LOCK:
2227 case NC_RPC_UNLOCK:
2228 case NC_RPC_GET:
2229 case NC_RPC_KILL:
2230 case NC_RPC_COMMIT:
2231 case NC_RPC_DISCARD:
2232 case NC_RPC_CANCEL:
2233 case NC_RPC_VALIDATE:
Michal Vasko77367452021-02-16 16:32:18 +01002234 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002235 if (!mod) {
Michal Vasko086cd572017-01-12 12:19:05 +01002236 ERR("Session %u: missing \"ietf-netconf\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002237 return NC_MSG_ERROR;
2238 }
Michal Vaskoc1171a42019-11-05 12:06:46 +01002239 break;
2240 case NC_RPC_GETSCHEMA:
Michal Vasko77367452021-02-16 16:32:18 +01002241 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002242 if (!mod) {
2243 ERR("Session %u: missing \"ietf-netconf-monitoring\" schema in the context.", session->id);
2244 return NC_MSG_ERROR;
2245 }
2246 break;
2247 case NC_RPC_SUBSCRIBE:
Michal Vasko77367452021-02-16 16:32:18 +01002248 mod = ly_ctx_get_module_implemented(session->ctx, "notifications");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002249 if (!mod) {
2250 ERR("Session %u: missing \"notifications\" schema in the context.", session->id);
2251 return NC_MSG_ERROR;
2252 }
2253 break;
2254 case NC_RPC_GETDATA:
2255 case NC_RPC_EDITDATA:
Michal Vasko77367452021-02-16 16:32:18 +01002256 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda");
Michal Vaskoc1171a42019-11-05 12:06:46 +01002257 if (!mod) {
2258 ERR("Session %u: missing \"ietf-netconf-nmda\" schema in the context.", session->id);
2259 return NC_MSG_ERROR;
2260 }
2261 break;
Michal Vasko96f247a2021-03-15 13:32:10 +01002262 case NC_RPC_ESTABLISHSUB:
2263 case NC_RPC_MODIFYSUB:
2264 case NC_RPC_DELETESUB:
2265 case NC_RPC_KILLSUB:
2266 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2267 if (!mod) {
2268 ERR("Session %u: missing \"ietf-subscribed-notifications\" schema in the context.", session->id);
2269 return NC_MSG_ERROR;
2270 }
2271 break;
Michal Vasko305faca2021-03-25 09:16:02 +01002272 case NC_RPC_ESTABLISHPUSH:
2273 case NC_RPC_MODIFYPUSH:
2274 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications");
2275 if (!mod) {
2276 ERR("Session %u: missing \"ietf-subscribed-notifications\" schema in the context.", session->id);
2277 return NC_MSG_ERROR;
2278 }
2279 mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2280 if (!mod2) {
2281 ERR("Session %u: missing \"ietf-yang-push\" schema in the context.", session->id);
2282 return NC_MSG_ERROR;
2283 }
2284 break;
2285 case NC_RPC_RESYNCSUB:
2286 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push");
2287 if (!mod) {
2288 ERR("Session %u: missing \"ietf-yang-push\" schema in the context.", session->id);
2289 return NC_MSG_ERROR;
2290 }
2291 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002292 case NC_RPC_UNKNOWN:
2293 ERRINT;
2294 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002295 }
2296
2297 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002298 case NC_RPC_ACT_GENERIC:
2299 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002300
2301 if (rpc_gen->has_data) {
2302 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002303 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002304 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002305 ly_in_new_memory(rpc_gen->content.xml_str, &in);
2306 lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &data, NULL);
2307 ly_in_free(in, 0);
2308 if (lyrc) {
Michal Vaskoeec410f2017-11-24 09:14:55 +01002309 return NC_MSG_ERROR;
2310 }
Michal Vasko086311b2016-01-08 09:53:11 +01002311 }
2312 break;
2313
2314 case NC_RPC_GETCONFIG:
2315 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2316
Michal Vasko77367452021-02-16 16:32:18 +01002317 lyd_new_inner(NULL, mod, "get-config", 0, &data);
Michal Vaskod387a1c2021-04-01 15:52:44 +02002318 lyd_new_inner(data, mod, "source", 0, &cont);
2319 if (lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL)) {
Michal Vasko77367452021-02-16 16:32:18 +01002320 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002321 return NC_MSG_ERROR;
2322 }
2323 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002324 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vasko77367452021-02-16 16:32:18 +01002325 lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node);
2326 lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01002327 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002328 lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node);
2329 lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL);
2330 lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01002331 }
2332 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002333 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002334 return NC_MSG_ERROR;
2335 }
2336 }
2337
2338 if (rpc_gc->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002339 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002340 if (!ietfncwd) {
Michal Vaskoc1171a42019-11-05 12:06:46 +01002341 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko77367452021-02-16 16:32:18 +01002342 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002343 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002344 }
Michal Vaskod387a1c2021-04-01 15:52:44 +02002345 if (lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_gc->wd_mode), 0, NULL)) {
Michal Vasko77367452021-02-16 16:32:18 +01002346 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002347 return NC_MSG_ERROR;
2348 }
2349 }
2350 break;
2351
2352 case NC_RPC_EDIT:
2353 rpc_e = (struct nc_rpc_edit *)rpc;
2354
Michal Vasko77367452021-02-16 16:32:18 +01002355 lyd_new_inner(NULL, mod, "edit-config", 0, &data);
Michal Vaskod387a1c2021-04-01 15:52:44 +02002356 lyd_new_inner(data, mod, "target", 0, &cont);
2357 if (lyd_new_term(cont, mod, ncds2str[rpc_e->target], NULL, 0, NULL)) {
Michal Vasko77367452021-02-16 16:32:18 +01002358 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002359 return NC_MSG_ERROR;
2360 }
2361
2362 if (rpc_e->default_op) {
Michal Vasko77367452021-02-16 16:32:18 +01002363 if (lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_e->default_op], 0, NULL)) {
2364 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002365 return NC_MSG_ERROR;
2366 }
2367 }
2368
2369 if (rpc_e->test_opt) {
Michal Vasko77367452021-02-16 16:32:18 +01002370 if (lyd_new_term(data, mod, "test-option", rpcedit_testopt2str[rpc_e->test_opt], 0, NULL)) {
2371 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002372 return NC_MSG_ERROR;
2373 }
2374 }
2375
2376 if (rpc_e->error_opt) {
Michal Vasko77367452021-02-16 16:32:18 +01002377 if (lyd_new_term(data, mod, "error-option", rpcedit_erropt2str[rpc_e->error_opt], 0, NULL)) {
2378 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002379 return NC_MSG_ERROR;
2380 }
2381 }
2382
Michal Vasko7793bc62016-09-16 11:58:41 +02002383 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vasko77367452021-02-16 16:32:18 +01002384 lyd_new_any(data, mod, "config", rpc_e->edit_cont, 0, LYD_ANYDATA_XML, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002385 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002386 lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002387 }
2388 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002389 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002390 return NC_MSG_ERROR;
2391 }
2392 break;
2393
2394 case NC_RPC_COPY:
2395 rpc_cp = (struct nc_rpc_copy *)rpc;
2396
Michal Vasko77367452021-02-16 16:32:18 +01002397 lyd_new_inner(NULL, mod, "copy-config", 0, &data);
Michal Vaskod387a1c2021-04-01 15:52:44 +02002398 lyd_new_inner(data, mod, "target", 0, &cont);
Michal Vasko086311b2016-01-08 09:53:11 +01002399 if (rpc_cp->url_trg) {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002400 lyd_new_term(cont, mod, "url", rpc_cp->url_trg, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002401 } else {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002402 lyd_new_term(cont, mod, ncds2str[rpc_cp->target], NULL, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002403 }
2404 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002405 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002406 return NC_MSG_ERROR;
2407 }
2408
Michal Vaskod387a1c2021-04-01 15:52:44 +02002409 lyd_new_inner(data, mod, "source", 0, &cont);
Michal Vasko086311b2016-01-08 09:53:11 +01002410 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002411 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002412 lyd_new_any(cont, mod, "config", rpc_cp->url_config_src, 0, LYD_ANYDATA_XML, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002413 } else {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002414 lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002415 }
2416 } else {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002417 lyd_new_term(cont, mod, ncds2str[rpc_cp->source], NULL, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002418 }
2419 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002420 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002421 return NC_MSG_ERROR;
2422 }
2423
2424 if (rpc_cp->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002425 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002426 if (!ietfncwd) {
Michal Vaskoc1171a42019-11-05 12:06:46 +01002427 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko77367452021-02-16 16:32:18 +01002428 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002429 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002430 }
Michal Vasko77367452021-02-16 16:32:18 +01002431 if (lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_cp->wd_mode), 0, NULL)) {
2432 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002433 return NC_MSG_ERROR;
2434 }
2435 }
2436 break;
2437
2438 case NC_RPC_DELETE:
2439 rpc_del = (struct nc_rpc_delete *)rpc;
2440
Michal Vasko77367452021-02-16 16:32:18 +01002441 lyd_new_inner(NULL, mod, "delete-config", 0, &data);
Michal Vaskod387a1c2021-04-01 15:52:44 +02002442 lyd_new_inner(data, mod, "target", 0, &cont);
Michal Vasko086311b2016-01-08 09:53:11 +01002443 if (rpc_del->url) {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002444 lyd_new_term(cont, mod, "url", rpc_del->url, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002445 } else {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002446 lyd_new_term(cont, mod, ncds2str[rpc_del->target], NULL, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002447 }
2448 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002449 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002450 return NC_MSG_ERROR;
2451 }
2452 break;
2453
2454 case NC_RPC_LOCK:
2455 rpc_lock = (struct nc_rpc_lock *)rpc;
2456
Michal Vasko77367452021-02-16 16:32:18 +01002457 lyd_new_inner(NULL, mod, "lock", 0, &data);
Michal Vaskod387a1c2021-04-01 15:52:44 +02002458 lyd_new_inner(data, mod, "target", 0, &cont);
2459 if (lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL)) {
Michal Vasko77367452021-02-16 16:32:18 +01002460 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002461 return NC_MSG_ERROR;
2462 }
2463 break;
2464
2465 case NC_RPC_UNLOCK:
2466 rpc_lock = (struct nc_rpc_lock *)rpc;
2467
Michal Vasko77367452021-02-16 16:32:18 +01002468 lyd_new_inner(NULL, mod, "unlock", 0, &data);
Michal Vaskod387a1c2021-04-01 15:52:44 +02002469 lyd_new_inner(data, mod, "target", 0, &cont);
2470 if (lyd_new_term(cont, mod, ncds2str[rpc_lock->target], NULL, 0, NULL)) {
Michal Vasko77367452021-02-16 16:32:18 +01002471 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002472 return NC_MSG_ERROR;
2473 }
2474 break;
2475
2476 case NC_RPC_GET:
2477 rpc_g = (struct nc_rpc_get *)rpc;
2478
Michal Vasko77367452021-02-16 16:32:18 +01002479 lyd_new_inner(NULL, mod, "get", 0, &data);
Michal Vasko086311b2016-01-08 09:53:11 +01002480 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002481 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vasko77367452021-02-16 16:32:18 +01002482 lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node);
2483 lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01002484 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002485 lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node);
2486 lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL);
2487 lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01002488 }
2489 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002490 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002491 return NC_MSG_ERROR;
2492 }
2493 }
2494
2495 if (rpc_g->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002496 ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults");
Michal Vasko086311b2016-01-08 09:53:11 +01002497 if (!ietfncwd) {
Michal Vaskoc1171a42019-11-05 12:06:46 +01002498 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko77367452021-02-16 16:32:18 +01002499 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002500 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002501 }
Michal Vasko77367452021-02-16 16:32:18 +01002502 if (lyd_new_term(data, ietfncwd, "with-defaults", nc_wd2str(rpc_g->wd_mode), 0, NULL)) {
2503 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002504 return NC_MSG_ERROR;
2505 }
2506 }
2507 break;
2508
2509 case NC_RPC_KILL:
2510 rpc_k = (struct nc_rpc_kill *)rpc;
2511
Michal Vasko77367452021-02-16 16:32:18 +01002512 lyd_new_inner(NULL, mod, "kill-session", 0, &data);
Michal Vasko086311b2016-01-08 09:53:11 +01002513 sprintf(str, "%u", rpc_k->sid);
Michal Vasko77367452021-02-16 16:32:18 +01002514 if (lyd_new_term(data, mod, "session-id", str, 0, NULL)) {
2515 lyd_free_tree(data);
Michal Vasko283c3c02021-01-13 14:12:19 +01002516 return NC_MSG_ERROR;
2517 }
Michal Vasko086311b2016-01-08 09:53:11 +01002518 break;
2519
2520 case NC_RPC_COMMIT:
2521 rpc_com = (struct nc_rpc_commit *)rpc;
2522
Michal Vasko77367452021-02-16 16:32:18 +01002523 lyd_new_inner(NULL, mod, "commit", 0, &data);
Michal Vasko086311b2016-01-08 09:53:11 +01002524 if (rpc_com->confirmed) {
Michal Vasko77367452021-02-16 16:32:18 +01002525 if (lyd_new_term(data, mod, "confirmed", NULL, 0, NULL)) {
2526 lyd_free_tree(data);
Michal Vasko283c3c02021-01-13 14:12:19 +01002527 return NC_MSG_ERROR;
2528 }
Michal Vasko086311b2016-01-08 09:53:11 +01002529 }
2530
2531 if (rpc_com->confirm_timeout) {
2532 sprintf(str, "%u", rpc_com->confirm_timeout);
Michal Vasko77367452021-02-16 16:32:18 +01002533 if (lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL)) {
2534 lyd_free_tree(data);
Michal Vasko283c3c02021-01-13 14:12:19 +01002535 return NC_MSG_ERROR;
2536 }
Michal Vasko086311b2016-01-08 09:53:11 +01002537 }
2538
2539 if (rpc_com->persist) {
Michal Vasko77367452021-02-16 16:32:18 +01002540 if (lyd_new_term(data, mod, "persist", rpc_com->persist, 0, NULL)) {
2541 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002542 return NC_MSG_ERROR;
2543 }
2544 }
2545
2546 if (rpc_com->persist_id) {
Michal Vasko77367452021-02-16 16:32:18 +01002547 if (lyd_new_term(data, mod, "persist-id", rpc_com->persist_id, 0, NULL)) {
2548 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002549 return NC_MSG_ERROR;
2550 }
2551 }
2552 break;
2553
2554 case NC_RPC_DISCARD:
Michal Vasko77367452021-02-16 16:32:18 +01002555 lyd_new_inner(NULL, mod, "discard-changes", 0, &data);
Michal Vasko086311b2016-01-08 09:53:11 +01002556 break;
2557
2558 case NC_RPC_CANCEL:
2559 rpc_can = (struct nc_rpc_cancel *)rpc;
2560
Michal Vasko77367452021-02-16 16:32:18 +01002561 lyd_new_inner(NULL, mod, "cancel-commit", 0, &data);
Michal Vasko086311b2016-01-08 09:53:11 +01002562 if (rpc_can->persist_id) {
Michal Vasko77367452021-02-16 16:32:18 +01002563 if (lyd_new_term(data, mod, "persist-id", rpc_can->persist_id, 0, NULL)) {
2564 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002565 return NC_MSG_ERROR;
2566 }
2567 }
2568 break;
2569
2570 case NC_RPC_VALIDATE:
2571 rpc_val = (struct nc_rpc_validate *)rpc;
2572
Michal Vasko77367452021-02-16 16:32:18 +01002573 lyd_new_inner(NULL, mod, "validate", 0, &data);
Michal Vaskod387a1c2021-04-01 15:52:44 +02002574 lyd_new_inner(data, mod, "source", 0, &cont);
Michal Vasko086311b2016-01-08 09:53:11 +01002575 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002576 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002577 lyd_new_any(cont, mod, "config", rpc_val->url_config_src, 0, LYD_ANYDATA_XML, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002578 } else {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002579 lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002580 }
2581 } else {
Michal Vaskod387a1c2021-04-01 15:52:44 +02002582 lyd_new_term(cont, mod, ncds2str[rpc_val->source], NULL, 0, &node);
Michal Vasko086311b2016-01-08 09:53:11 +01002583 }
2584 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002585 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002586 return NC_MSG_ERROR;
2587 }
2588 break;
2589
2590 case NC_RPC_GETSCHEMA:
Michal Vasko086311b2016-01-08 09:53:11 +01002591 rpc_gs = (struct nc_rpc_getschema *)rpc;
2592
Michal Vasko77367452021-02-16 16:32:18 +01002593 lyd_new_inner(NULL, mod, "get-schema", 0, &data);
2594 if (lyd_new_term(data, mod, "identifier", rpc_gs->identifier, 0, NULL)) {
2595 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002596 return NC_MSG_ERROR;
2597 }
2598 if (rpc_gs->version) {
Michal Vasko77367452021-02-16 16:32:18 +01002599 if (lyd_new_term(data, mod, "version", rpc_gs->version, 0, NULL)) {
2600 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002601 return NC_MSG_ERROR;
2602 }
2603 }
2604 if (rpc_gs->format) {
Michal Vasko77367452021-02-16 16:32:18 +01002605 if (lyd_new_term(data, mod, "format", rpc_gs->format, 0, NULL)) {
2606 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002607 return NC_MSG_ERROR;
2608 }
2609 }
2610 break;
2611
2612 case NC_RPC_SUBSCRIBE:
Michal Vasko086311b2016-01-08 09:53:11 +01002613 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2614
Michal Vasko77367452021-02-16 16:32:18 +01002615 lyd_new_inner(NULL, mod, "create-subscription", 0, &data);
Michal Vasko086311b2016-01-08 09:53:11 +01002616 if (rpc_sub->stream) {
Michal Vasko96f247a2021-03-15 13:32:10 +01002617 if (lyd_new_term(data, mod, "stream", rpc_sub->stream, 0, NULL)) {
Michal Vasko77367452021-02-16 16:32:18 +01002618 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002619 return NC_MSG_ERROR;
2620 }
2621 }
2622
2623 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002624 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vasko77367452021-02-16 16:32:18 +01002625 lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node);
2626 lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01002627 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002628 lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node);
2629 lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL);
2630 lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01002631 }
2632 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002633 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002634 return NC_MSG_ERROR;
2635 }
2636 }
2637
2638 if (rpc_sub->start) {
Michal Vasko77367452021-02-16 16:32:18 +01002639 if (lyd_new_term(data, mod, "startTime", rpc_sub->start, 0, NULL)) {
2640 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002641 return NC_MSG_ERROR;
2642 }
2643 }
2644
2645 if (rpc_sub->stop) {
Michal Vasko77367452021-02-16 16:32:18 +01002646 if (lyd_new_term(data, mod, "stopTime", rpc_sub->stop, 0, NULL)) {
2647 lyd_free_tree(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002648 return NC_MSG_ERROR;
2649 }
2650 }
2651 break;
Michal Vaskoc1171a42019-11-05 12:06:46 +01002652
2653 case NC_RPC_GETDATA:
2654 rpc_getd = (struct nc_rpc_getdata *)rpc;
2655
Michal Vasko77367452021-02-16 16:32:18 +01002656 lyd_new_inner(NULL, mod, "get-data", 0, &data);
2657 if (lyd_new_term(data, mod, "datastore", rpc_getd->datastore, 0, NULL)) {
2658 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002659 return NC_MSG_ERROR;
2660 }
2661 if (rpc_getd->filter) {
2662 if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) {
Michal Vasko77367452021-02-16 16:32:18 +01002663 lyd_new_any(data, mod, "subtree-filter", rpc_getd->filter, 0, LYD_ANYDATA_XML, 0, &node);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002664 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002665 lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, &node);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002666 }
2667 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002668 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002669 return NC_MSG_ERROR;
2670 }
2671 }
2672 if (rpc_getd->config_filter) {
Michal Vasko77367452021-02-16 16:32:18 +01002673 if (lyd_new_term(data, mod, "config-filter", rpc_getd->config_filter, 0, NULL)) {
2674 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002675 return NC_MSG_ERROR;
2676 }
2677 }
2678 for (i = 0; i < rpc_getd->origin_filter_count; ++i) {
Michal Vasko77367452021-02-16 16:32:18 +01002679 if (lyd_new_term(data, mod, rpc_getd->negated_origin_filter ? "negated-origin-filter" : "origin-filter",
2680 rpc_getd->origin_filter[i], 0, NULL)) {
2681 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002682 return NC_MSG_ERROR;
2683 }
2684 }
2685 if (rpc_getd->max_depth) {
2686 sprintf(str, "%u", rpc_getd->max_depth);
Michal Vasko77367452021-02-16 16:32:18 +01002687 if (lyd_new_term(data, mod, "max-depth", str, 0, NULL)) {
2688 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002689 return NC_MSG_ERROR;
2690 }
2691 }
2692 if (rpc_getd->with_origin) {
Michal Vasko77367452021-02-16 16:32:18 +01002693 if (lyd_new_term(data, mod, "with-origin", NULL, 0, NULL)) {
2694 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002695 return NC_MSG_ERROR;
2696 }
2697 }
2698
2699 if (rpc_getd->wd_mode) {
Michal Vasko77367452021-02-16 16:32:18 +01002700 /* "with-defaults" are used from a grouping so it belongs to the ietf-netconf-nmda module */
2701 if (lyd_new_term(data, mod, "with-defaults", nc_wd2str(rpc_getd->wd_mode), 0, NULL)) {
2702 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002703 return NC_MSG_ERROR;
2704 }
2705 }
2706 break;
2707
2708 case NC_RPC_EDITDATA:
2709 rpc_editd = (struct nc_rpc_editdata *)rpc;
2710
Michal Vasko77367452021-02-16 16:32:18 +01002711 lyd_new_inner(NULL, mod, "edit-data", 0, &data);
2712 if (lyd_new_term(data, mod, "datastore", rpc_editd->datastore, 0, NULL)) {
2713 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002714 return NC_MSG_ERROR;
2715 }
2716
2717 if (rpc_editd->default_op) {
Michal Vasko77367452021-02-16 16:32:18 +01002718 if (lyd_new_term(data, mod, "default-operation", rpcedit_dfltop2str[rpc_editd->default_op], 0, NULL)) {
2719 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002720 return NC_MSG_ERROR;
2721 }
2722 }
2723
2724 if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) {
Michal Vasko77367452021-02-16 16:32:18 +01002725 lyd_new_any(data, mod, "config", rpc_editd->edit_cont, 0, LYD_ANYDATA_XML, 0, &node);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002726 } else {
Michal Vasko77367452021-02-16 16:32:18 +01002727 lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, &node);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002728 }
2729 if (!node) {
Michal Vasko77367452021-02-16 16:32:18 +01002730 lyd_free_tree(data);
Michal Vaskoc1171a42019-11-05 12:06:46 +01002731 return NC_MSG_ERROR;
2732 }
2733 break;
2734
Michal Vasko96f247a2021-03-15 13:32:10 +01002735 case NC_RPC_ESTABLISHSUB:
2736 rpc_estsub = (struct nc_rpc_establishsub *)rpc;
2737
2738 lyd_new_inner(NULL, mod, "establish-subscription", 0, &data);
2739
2740 if (rpc_estsub->filter) {
2741 if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) {
2742 lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML, 0, &node);
2743 } else if (rpc_estsub->filter[0] == '/') {
2744 lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, &node);
2745 } else {
2746 lyd_new_term(data, mod, "stream-filter-name", rpc_estsub->filter, 0, &node);
2747 }
2748 if (!node) {
2749 lyd_free_tree(data);
2750 return NC_MSG_ERROR;
2751 }
2752 }
2753
2754 if (lyd_new_term(data, mod, "stream", rpc_estsub->stream, 0, NULL)) {
2755 lyd_free_tree(data);
2756 return NC_MSG_ERROR;
2757 }
2758
2759 if (rpc_estsub->start) {
2760 if (lyd_new_term(data, mod, "replay-start-time", rpc_estsub->start, 0, NULL)) {
2761 lyd_free_tree(data);
2762 return NC_MSG_ERROR;
2763 }
2764 }
2765
2766 if (rpc_estsub->stop) {
2767 if (lyd_new_term(data, mod, "stop-time", rpc_estsub->stop, 0, NULL)) {
2768 lyd_free_tree(data);
2769 return NC_MSG_ERROR;
2770 }
2771 }
2772
2773 if (rpc_estsub->encoding) {
2774 if (lyd_new_term(data, mod, "encoding", rpc_estsub->encoding, 0, NULL)) {
2775 lyd_free_tree(data);
2776 return NC_MSG_ERROR;
2777 }
2778 }
2779 break;
2780
2781 case NC_RPC_MODIFYSUB:
2782 rpc_modsub = (struct nc_rpc_modifysub *)rpc;
2783
2784 lyd_new_inner(NULL, mod, "modify-subscription", 0, &data);
2785
2786 sprintf(str, "%u", rpc_modsub->id);
2787 if (lyd_new_term(data, mod, "id", str, 0, NULL)) {
2788 lyd_free_tree(data);
2789 return NC_MSG_ERROR;
2790 }
2791
2792 if (rpc_modsub->filter) {
2793 if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) {
2794 lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML, 0, &node);
2795 } else if (rpc_modsub->filter[0] == '/') {
2796 lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, &node);
2797 } else {
2798 lyd_new_term(data, mod, "stream-filter-name", rpc_modsub->filter, 0, &node);
2799 }
2800 if (!node) {
2801 lyd_free_tree(data);
2802 return NC_MSG_ERROR;
2803 }
2804 }
2805
2806 if (rpc_modsub->stop) {
2807 if (lyd_new_term(data, mod, "stop-time", rpc_modsub->stop, 0, NULL)) {
2808 lyd_free_tree(data);
2809 return NC_MSG_ERROR;
2810 }
2811 }
2812 break;
2813
2814 case NC_RPC_DELETESUB:
2815 rpc_delsub = (struct nc_rpc_deletesub *)rpc;
2816
2817 lyd_new_inner(NULL, mod, "delete-subscription", 0, &data);
2818
2819 sprintf(str, "%u", rpc_delsub->id);
2820 if (lyd_new_term(data, mod, "id", str, 0, NULL)) {
2821 lyd_free_tree(data);
2822 return NC_MSG_ERROR;
2823 }
2824 break;
2825
2826 case NC_RPC_KILLSUB:
2827 rpc_killsub = (struct nc_rpc_killsub *)rpc;
2828
2829 lyd_new_inner(NULL, mod, "kill-subscription", 0, &data);
2830
2831 sprintf(str, "%u", rpc_killsub->id);
2832 if (lyd_new_term(data, mod, "id", str, 0, NULL)) {
2833 lyd_free_tree(data);
2834 return NC_MSG_ERROR;
2835 }
2836 break;
2837
Michal Vasko305faca2021-03-25 09:16:02 +01002838 case NC_RPC_ESTABLISHPUSH:
2839 rpc_estpush = (struct nc_rpc_establishpush *)rpc;
2840
2841 lyd_new_inner(NULL, mod, "establish-subscription", 0, &data);
2842
2843 if (lyd_new_term(data, mod2, "datastore", rpc_estpush->datastore, 0, NULL)) {
2844 lyd_free_tree(data);
2845 return NC_MSG_ERROR;
2846 }
2847
2848 if (rpc_estpush->filter) {
2849 if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) {
2850 lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0, LYD_ANYDATA_XML, 0, &node);
2851 } else if (rpc_estpush->filter[0] == '/') {
2852 lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, &node);
2853 } else {
2854 lyd_new_term(data, mod2, "selection-filter-ref", rpc_estpush->filter, 0, &node);
2855 }
2856 if (!node) {
2857 lyd_free_tree(data);
2858 return NC_MSG_ERROR;
2859 }
2860 }
2861
2862 if (rpc_estpush->stop) {
2863 if (lyd_new_term(data, mod, "stop-time", rpc_estpush->stop, 0, NULL)) {
2864 lyd_free_tree(data);
2865 return NC_MSG_ERROR;
2866 }
2867 }
2868
2869 if (rpc_estpush->encoding) {
2870 if (lyd_new_term(data, mod, "encoding", rpc_estpush->encoding, 0, NULL)) {
2871 lyd_free_tree(data);
2872 return NC_MSG_ERROR;
2873 }
2874 }
2875
2876 if (rpc_estpush->periodic) {
2877 if (lyd_new_inner(data, mod2, "periodic", 0, &cont)) {
2878 lyd_free_tree(data);
2879 return NC_MSG_ERROR;
2880 }
2881
2882 sprintf(str, "%" PRIu32, rpc_estpush->period);
2883 if (lyd_new_term(cont, mod2, "period", str, 0, NULL)) {
2884 lyd_free_tree(data);
2885 return NC_MSG_ERROR;
2886 }
2887
2888 if (rpc_estpush->anchor_time) {
2889 if (lyd_new_term(cont, mod2, "anchor-time", rpc_estpush->anchor_time, 0, NULL)) {
2890 lyd_free_tree(data);
2891 return NC_MSG_ERROR;
2892 }
2893 }
2894 } else {
2895 if (lyd_new_inner(data, mod2, "on-change", 0, &cont)) {
2896 lyd_free_tree(data);
2897 return NC_MSG_ERROR;
2898 }
2899
2900 if (rpc_estpush->dampening_period) {
2901 sprintf(str, "%" PRIu32, rpc_estpush->dampening_period);
2902 if (lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL)) {
2903 lyd_free_tree(data);
2904 return NC_MSG_ERROR;
2905 }
2906 }
2907
2908 if (lyd_new_term(cont, mod2, "sync-on-start", rpc_estpush->sync_on_start ? "true" : "false", 0, NULL)) {
2909 lyd_free_tree(data);
2910 return NC_MSG_ERROR;
2911 }
2912
2913 if (rpc_estpush->excluded_change) {
2914 for (i = 0; rpc_estpush->excluded_change[i]; ++i) {
2915 if (lyd_new_term(cont, mod2, "excluded-change", rpc_estpush->excluded_change[i], 0, NULL)) {
2916 lyd_free_tree(data);
2917 return NC_MSG_ERROR;
2918 }
2919 }
2920 }
2921 }
2922 break;
2923
2924 case NC_RPC_MODIFYPUSH:
2925 rpc_modpush = (struct nc_rpc_modifypush *)rpc;
2926
2927 lyd_new_inner(NULL, mod, "modify-subscription", 0, &data);
2928
2929 sprintf(str, "%u", rpc_modpush->id);
2930 if (lyd_new_term(data, mod, "id", str, 0, NULL)) {
2931 lyd_free_tree(data);
2932 return NC_MSG_ERROR;
2933 }
2934
2935 if (lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL)) {
2936 lyd_free_tree(data);
2937 return NC_MSG_ERROR;
2938 }
2939
2940 if (rpc_modpush->filter) {
2941 if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) {
2942 lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0, LYD_ANYDATA_XML, 0, &node);
2943 } else if (rpc_modpush->filter[0] == '/') {
2944 lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, &node);
2945 } else {
2946 lyd_new_term(data, mod2, "selection-filter-ref", rpc_modpush->filter, 0, &node);
2947 }
2948 if (!node) {
2949 lyd_free_tree(data);
2950 return NC_MSG_ERROR;
2951 }
2952 }
2953
2954 if (rpc_modpush->stop) {
2955 if (lyd_new_term(data, mod, "stop-time", rpc_modpush->stop, 0, NULL)) {
2956 lyd_free_tree(data);
2957 return NC_MSG_ERROR;
2958 }
2959 }
2960
2961 if (rpc_modpush->periodic) {
2962 if (lyd_new_inner(data, mod2, "periodic", 0, &cont)) {
2963 lyd_free_tree(data);
2964 return NC_MSG_ERROR;
2965 }
2966
2967 sprintf(str, "%" PRIu32, rpc_modpush->period);
2968 if (lyd_new_term(cont, mod2, "period", str, 0, NULL)) {
2969 lyd_free_tree(data);
2970 return NC_MSG_ERROR;
2971 }
2972
2973 if (rpc_modpush->anchor_time) {
2974 if (lyd_new_term(cont, mod2, "anchor-time", rpc_modpush->anchor_time, 0, NULL)) {
2975 lyd_free_tree(data);
2976 return NC_MSG_ERROR;
2977 }
2978 }
2979 } else {
2980 if (lyd_new_inner(data, mod2, "on-change", 0, &cont)) {
2981 lyd_free_tree(data);
2982 return NC_MSG_ERROR;
2983 }
2984
2985 if (rpc_modpush->dampening_period) {
2986 sprintf(str, "%" PRIu32, rpc_modpush->dampening_period);
2987 if (lyd_new_term(cont, mod2, "dampening-period", str, 0, NULL)) {
2988 lyd_free_tree(data);
2989 return NC_MSG_ERROR;
2990 }
2991 }
2992 }
2993 break;
2994
2995 case NC_RPC_RESYNCSUB:
2996 rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc;
2997
2998 lyd_new_inner(NULL, mod, "resync-subscription", 0, &data);
2999
3000 sprintf(str, "%u", rpc_resyncsub->id);
3001 if (lyd_new_term(data, mod, "id", str, 0, NULL)) {
3002 lyd_free_tree(data);
3003 return NC_MSG_ERROR;
3004 }
3005 break;
3006
Michal Vasko96f247a2021-03-15 13:32:10 +01003007 case NC_RPC_UNKNOWN:
Michal Vasko7f1c78b2016-01-19 09:52:14 +01003008 ERRINT;
3009 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01003010 }
3011
Michal Vasko131120a2018-05-29 15:44:02 +02003012 if (!data) {
3013 /* error was already printed */
3014 return NC_MSG_ERROR;
3015 }
3016
Michal Vasko77367452021-02-16 16:32:18 +01003017 if (lyd_validate_op(data, NULL, LYD_TYPE_RPC_YANG, NULL)) {
Radek Krejcib4b19062018-02-07 16:33:06 +01003018 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003019 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003020 }
Michal Vasko086311b2016-01-08 09:53:11 +01003021 return NC_MSG_ERROR;
3022 }
3023
Michal Vasko131120a2018-05-29 15:44:02 +02003024 /* send RPC, store its message ID */
3025 r = nc_send_msg_io(session, timeout, data);
3026 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003027
Radek Krejcib4b19062018-02-07 16:33:06 +01003028 if (dofree) {
Michal Vasko77367452021-02-16 16:32:18 +01003029 lyd_free_tree(data);
Radek Krejcib4b19062018-02-07 16:33:06 +01003030 }
Michal Vasko086311b2016-01-08 09:53:11 +01003031
Michal Vasko131120a2018-05-29 15:44:02 +02003032 if (r == NC_MSG_RPC) {
3033 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01003034 }
Michal Vasko131120a2018-05-29 15:44:02 +02003035 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01003036}
Michal Vaskode2946c2017-01-12 12:19:26 +01003037
3038API void
3039nc_client_session_set_not_strict(struct nc_session *session)
3040{
3041 if (session->side != NC_CLIENT) {
3042 ERRARG("session");
3043 return;
3044 }
3045
3046 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
3047}