blob: 8c574833627c6c5d5c3b240c472a9f25178f77ba [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 Vasko086311b2016-01-08 09:53:11 +010016#include <assert.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010020#include <netinet/in.h>
Michal Vasko83ad17e2019-01-30 10:11:37 +010021#include <netinet/tcp.h>
Michal Vasko086311b2016-01-08 09:53:11 +010022#include <pthread.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/socket.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020026#include <sys/un.h>
Michal Vasko086311b2016-01-08 09:53:11 +010027#include <sys/stat.h>
Radek Krejci62aa0642017-05-25 16:33:49 +020028#include <sys/syscall.h>
Michal Vasko086311b2016-01-08 09:53:11 +010029#include <sys/types.h>
30#include <unistd.h>
31#include <arpa/inet.h>
32#include <poll.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020033#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010034
35#include <libyang/libyang.h>
36
Michal Vasko086311b2016-01-08 09:53:11 +010037#include "libnetconf.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010038#include "session_client.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010039#include "messages_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010040
Michal Vasko80ef5d22016-01-18 09:21:02 +010041static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
42
Radek Krejci62aa0642017-05-25 16:33:49 +020043#ifdef NC_ENABLED_SSH
44int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
45char *sshauth_password(const char *username, const char *hostname, void *priv);
46char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
47char *sshauth_privkey_passphrase(const char* privkey_path, void *priv);
48#endif /* NC_ENABLED_SSH */
49
50static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
51static pthread_key_t nc_client_context_key;
52#ifdef __linux__
53static struct nc_client_context context_main = {
54 /* .opts zeroed */
55#ifdef NC_ENABLED_SSH
56 .ssh_opts = {
57 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
58 .auth_hostkey_check = sshauth_hostkey_check,
59 .auth_password = sshauth_password,
60 .auth_interactive = sshauth_interactive,
61 .auth_privkey_passphrase = sshauth_privkey_passphrase
62 },
63 .ssh_ch_opts = {
64 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
65 .auth_hostkey_check = sshauth_hostkey_check,
66 .auth_password = sshauth_password,
67 .auth_interactive = sshauth_interactive,
68 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020069 },
Radek Krejci62aa0642017-05-25 16:33:49 +020070#endif /* NC_ENABLED_SSH */
71 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020072 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020073};
74#endif
75
76static void
77nc_client_context_free(void *ptr)
78{
79 struct nc_client_context *c = (struct nc_client_context *)ptr;
80
Radek Krejci5cebc6b2017-05-26 13:24:38 +020081 if (--(c->refcount)) {
82 /* still used */
83 return;
84 }
85
Radek Krejci62aa0642017-05-25 16:33:49 +020086#ifdef __linux__
87 /* in __linux__ we use static memory in the main thread,
88 * so this check is for programs terminating the main()
89 * function by pthread_exit() :)
90 */
91 if (c != &context_main)
92#endif
93 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +020094 /* for the main thread the same is done in nc_client_destroy() */
Radek Krejci62aa0642017-05-25 16:33:49 +020095 nc_client_set_schema_searchpath(NULL);
96#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
97 nc_client_ch_del_bind(NULL, 0, 0);
98#endif
99#ifdef NC_ENABLED_SSH
100 nc_client_ssh_destroy_opts();
101#endif
102#ifdef NC_ENABLED_TLS
103 nc_client_tls_destroy_opts();
104#endif
105 free(c);
106 }
107}
108
109static void
110nc_client_context_createkey(void)
111{
112 int r;
113
114 /* initiate */
115 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN);
116 pthread_setspecific(nc_client_context_key, NULL);
117}
118
119struct nc_client_context *
120nc_client_context_location(void)
121{
122 struct nc_client_context *e;
123
124 pthread_once(&nc_client_context_once, nc_client_context_createkey);
125 e = pthread_getspecific(nc_client_context_key);
126 if (!e) {
127 /* prepare ly_err storage */
128#ifdef __linux__
129 if (getpid() == syscall(SYS_gettid)) {
130 /* main thread - use global variable instead of thread-specific variable. */
131 e = &context_main;
132 } else
133#endif /* __linux__ */
134 {
135 e = calloc(1, sizeof *e);
136 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200137 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200138#ifdef NC_ENABLED_SSH
139 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
140 e->ssh_opts.auth_pref[0].value = 3;
141 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
142 e->ssh_opts.auth_pref[1].value = 2;
143 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
144 e->ssh_opts.auth_pref[2].value = 1;
145 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
146 e->ssh_opts.auth_password = sshauth_password;
147 e->ssh_opts.auth_interactive = sshauth_interactive;
148 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
149
150 /* callhome settings are the same except the inverted auth methods preferences */
151 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
152 e->ssh_ch_opts.auth_pref[0].value = 1;
153 e->ssh_ch_opts.auth_pref[1].value = 2;
154 e->ssh_ch_opts.auth_pref[2].value = 3;
155#endif /* NC_ENABLED_SSH */
156 }
157 pthread_setspecific(nc_client_context_key, e);
158 }
159
160 return e;
161}
162
163#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100164
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200165API void *
166nc_client_get_thread_context(void)
167{
168 return nc_client_context_location();
169}
170
171API void
172nc_client_set_thread_context(void *context)
173{
174 struct nc_client_context *old, *new;
175
176 if (!context) {
177 ERRARG(context);
178 return;
179 }
180
181 new = (struct nc_client_context *)context;
182 old = nc_client_context_location();
183 if (old == new) {
184 /* nothing to change */
185 return;
186 }
187
188 /* replace old by new, increase reference counter in the newly set context */
189 nc_client_context_free(old);
190 new->refcount++;
191 pthread_setspecific(nc_client_context_key, new);
192}
193
Radek Krejcifd5b6682017-06-13 15:52:53 +0200194int
195nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
196{
197 /* assign context (dicionary needed for handshake) */
198 if (!ctx) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200199 ctx = ly_ctx_new(NULL, LY_CTX_NOYANGLIBRARY);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200200 if (!ctx) {
201 return EXIT_FAILURE;
202 }
203
204 /* user path must be first, the first path is used to store schemas retreived via get-schema */
205 if (client_opts.schema_searchpath) {
206 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
207 }
Michal Vaskoff7e3562018-02-15 13:41:22 +0100208 ly_ctx_set_searchdir(ctx, NC_SCHEMAS_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200209
210 /* set callback for getting schemas, if provided */
211 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
212 } else {
213 session->flags |= NC_SESSION_SHAREDCTX;
214 }
215
216 session->ctx = ctx;
217
218 return EXIT_SUCCESS;
219}
220
Michal Vasko086311b2016-01-08 09:53:11 +0100221API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100222nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100223{
Michal Vasko3031aae2016-01-27 16:07:18 +0100224 if (client_opts.schema_searchpath) {
225 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100226 }
Michal Vasko086311b2016-01-08 09:53:11 +0100227
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100228 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100229 client_opts.schema_searchpath = strdup(path);
230 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100231 ERRMEM;
232 return 1;
233 }
234 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100235 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100236 }
237
238 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100239}
240
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100241API const char *
242nc_client_get_schema_searchpath(void)
243{
244 return client_opts.schema_searchpath;
245}
246
Radek Krejcifd5b6682017-06-13 15:52:53 +0200247API int
248nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100249{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200250 client_opts.schema_clb = clb;
251 if (clb) {
252 client_opts.schema_clb_data = user_data;
253 } else {
254 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100255 }
256
257 return 0;
258}
259
Radek Krejcifd5b6682017-06-13 15:52:53 +0200260API ly_module_imp_clb
261nc_client_get_schema_callback(void **user_data)
262{
263 if (user_data) {
264 (*user_data) = client_opts.schema_clb_data;
265 }
266 return client_opts.schema_clb;
267}
268
Radek Krejci65ef6d52018-08-16 16:35:02 +0200269
270struct schema_info {
271 char *name;
272 char *revision;
273 struct {
274 char *name;
275 char *revision;
276 } *submodules;
277 struct ly_set features;
278 int implemented;
279};
280
281struct clb_data_s {
282 void *user_data;
283 ly_module_imp_clb user_clb;
284 struct schema_info *schemas;
285 struct nc_session *session;
286 int has_get_schema;
287};
288
289static char *
290retrieve_schema_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data,
291 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100292{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200293 char *localfile = NULL;
294 FILE *f;
295 long length, l;
296 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100297
Radek Krejci3b60e5c2018-08-17 10:10:16 +0200298 if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx),
299 !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD),
300 name, rev, &localfile, format)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200301 return NULL;
302 }
303 if (localfile) {
304 VRB("Session %u: reading schema from localfile \"%s\".", clb_data->session->id, localfile);
305 f = fopen(localfile, "r");
306 if (!f) {
307 ERR("Session %u: unable to open \"%s\" file to get schema (%s).",
308 clb_data->session->id, localfile, strerror(errno));
309 free(localfile);
310 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100311 }
Michal Vasko086311b2016-01-08 09:53:11 +0100312
Radek Krejci65ef6d52018-08-16 16:35:02 +0200313 fseek(f, 0, SEEK_END);
314 length = ftell(f);
Radek Krejci3f499a62018-08-17 10:16:57 +0200315 if (length < 0) {
316 ERR("Session %u: unable to get size of schema file \"%s\".",
317 clb_data->session->id, localfile);
318 free(localfile);
319 fclose(f);
320 return NULL;
321 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200322 fseek(f, 0, SEEK_SET);
323
324 model_data = malloc(length + 1);
325 if (!model_data) {
326 ERRMEM;
327 } else if ((l = fread(model_data, 1, length, f)) != length) {
328 ERR("Session %u: reading schema from \"%s\" failed (%d bytes read, but %d expected).",
329 clb_data->session->id, localfile, l, length);
330 free(model_data);
331 model_data = NULL;
332 } else {
333 /* terminating NULL byte */
334 model_data[length] = '\0';
Michal Vasko086311b2016-01-08 09:53:11 +0100335 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200336 fclose(f);
337 free(localfile);
Michal Vasko086311b2016-01-08 09:53:11 +0100338 }
339
Radek Krejci65ef6d52018-08-16 16:35:02 +0200340 return model_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100341}
342
343static char *
Radek Krejci65ef6d52018-08-16 16:35:02 +0200344retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data,
345 LYS_INFORMAT *format)
Michal Vasko086311b2016-01-08 09:53:11 +0100346{
Michal Vasko086311b2016-01-08 09:53:11 +0100347 struct nc_rpc *rpc;
348 struct nc_reply *reply;
349 struct nc_reply_data *data_rpl;
Michal Vasko998ba412016-09-16 12:00:07 +0200350 struct nc_reply_error *error_rpl;
Radek Krejci539efb62016-08-24 15:05:16 +0200351 struct lyd_node_anydata *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100352 NC_MSG_TYPE msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100353 uint64_t msgid;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200354 char *localfile = NULL;
355 FILE *f;
356 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100357
Radek Krejci65ef6d52018-08-16 16:35:02 +0200358 VRB("Session %u: reading schema from server via get-schema.", clb_data->session->id);
359 rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST);
Michal Vasko086311b2016-01-08 09:53:11 +0100360
Radek Krejci65ef6d52018-08-16 16:35:02 +0200361 while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vasko086311b2016-01-08 09:53:11 +0100362 usleep(1000);
363 }
364 if (msg == NC_MSG_ERROR) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200365 ERR("Session %u: failed to send the <get-schema> RPC.", clb_data->session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100366 nc_rpc_free(rpc);
367 return NULL;
368 }
369
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200370 do {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200371 msg = nc_recv_reply(clb_data->session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, 0, &reply);
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200372 } while (msg == NC_MSG_NOTIF);
Michal Vasko086311b2016-01-08 09:53:11 +0100373 nc_rpc_free(rpc);
374 if (msg == NC_MSG_WOULDBLOCK) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200375 ERR("Session %u: timeout for receiving reply to a <get-schema> expired.", clb_data->session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100376 return NULL;
377 } else if (msg == NC_MSG_ERROR) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200378 ERR("Session %u: failed to receive a reply to <get-schema>.", clb_data->session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100379 return NULL;
380 }
381
Michal Vasko998ba412016-09-16 12:00:07 +0200382 switch (reply->type) {
383 case NC_RPL_OK:
Radek Krejci65ef6d52018-08-16 16:35:02 +0200384 ERR("Session %u: unexpected reply OK to a <get-schema> RPC.", clb_data->session->id);
Michal Vasko998ba412016-09-16 12:00:07 +0200385 nc_reply_free(reply);
386 return NULL;
387 case NC_RPL_DATA:
388 /* fine */
389 break;
390 case NC_RPL_ERROR:
391 error_rpl = (struct nc_reply_error *)reply;
392 if (error_rpl->count) {
393 ERR("Session %u: error reply to a <get-schema> RPC (tag \"%s\", message \"%s\").",
Radek Krejci65ef6d52018-08-16 16:35:02 +0200394 clb_data->session->id, error_rpl->err[0].tag, error_rpl->err[0].message);
Michal Vasko998ba412016-09-16 12:00:07 +0200395 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200396 ERR("Session %u: unexpected reply error to a <get-schema> RPC.", clb_data->session->id);
Michal Vasko998ba412016-09-16 12:00:07 +0200397 }
398 nc_reply_free(reply);
399 return NULL;
400 case NC_RPL_NOTIF:
Radek Krejci65ef6d52018-08-16 16:35:02 +0200401 ERR("Session %u: unexpected reply notification to a <get-schema> RPC.", clb_data->session->id);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100402 nc_reply_free(reply);
403 return NULL;
404 }
405
Michal Vasko086311b2016-01-08 09:53:11 +0100406 data_rpl = (struct nc_reply_data *)reply;
Michal Vasko1c042702018-11-29 08:27:51 +0100407 if (!data_rpl->data || (data_rpl->data->schema->nodetype != LYS_RPC) || strcmp(data_rpl->data->schema->name, "get-schema")
Michal Vaskob2583f12016-05-12 11:40:23 +0200408 || !data_rpl->data->child || (data_rpl->data->child->schema->nodetype != LYS_ANYXML)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200409 ERR("Session %u: unexpected data in reply to a <get-schema> RPC.", clb_data->session->id);
Michal Vaskob2583f12016-05-12 11:40:23 +0200410 nc_reply_free(reply);
411 return NULL;
412 }
Radek Krejci539efb62016-08-24 15:05:16 +0200413 get_schema_data = (struct lyd_node_anydata *)data_rpl->data->child;
414 switch (get_schema_data->value_type) {
415 case LYD_ANYDATA_CONSTSTRING:
416 case LYD_ANYDATA_STRING:
Michal Vaskob2583f12016-05-12 11:40:23 +0200417 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200418 break;
419 case LYD_ANYDATA_DATATREE:
420 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYP_WITHSIBLINGS);
421 break;
422 case LYD_ANYDATA_XML:
423 lyxml_print_mem(&model_data, get_schema_data->value.xml, LYXML_PRINT_SIBLINGS);
424 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200425 case LYD_ANYDATA_JSON:
426 case LYD_ANYDATA_JSOND:
Michal Vasko94868ed2016-09-29 10:33:38 +0200427 case LYD_ANYDATA_SXML:
428 case LYD_ANYDATA_SXMLD:
Michal Vaskod838d292018-08-15 11:39:05 +0200429 case LYD_ANYDATA_LYB:
430 case LYD_ANYDATA_LYBD:
Radek Krejci03438ec2016-09-21 14:12:26 +0200431 ERRINT;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200432 nc_reply_free(reply);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200433 }
Michal Vasko086311b2016-01-08 09:53:11 +0100434 nc_reply_free(reply);
Michal Vasko086311b2016-01-08 09:53:11 +0100435
Radek Krejci488b0702018-08-29 14:47:15 +0200436 if (model_data && !model_data[0]) {
437 /* empty data */
438 free(model_data);
439 model_data = NULL;
440 }
441
Radek Krejcifd5b6682017-06-13 15:52:53 +0200442 /* try to store the model_data into local schema repository */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200443 if (model_data) {
444 *format = LYS_IN_YANG;
445 if (client_opts.schema_searchpath) {
446 if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name,
447 rev ? "@" : "", rev ? rev : "") == -1) {
448 ERRMEM;
Michal Vaskof945da52018-02-15 08:45:13 +0100449 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200450 f = fopen(localfile, "w");
451 if (!f) {
452 WRN("Unable to store \"%s\" as a local copy of schema retrieved via <get-schema> (%s).",
453 localfile, strerror(errno));
454 } else {
455 fputs(model_data, f);
456 fclose(f);
457 }
458 free(localfile);
Michal Vaskof945da52018-02-15 08:45:13 +0100459 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200460 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200461 }
462
Michal Vasko086311b2016-01-08 09:53:11 +0100463 return model_data;
464}
465
Jan Kundrát35972df2018-09-06 19:00:01 +0200466static void free_with_user_data(void *data, void *user_data)
467{
468 free(data);
469 (void)user_data;
470}
471
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200472static const char *
Radek Krejci65ef6d52018-08-16 16:35:02 +0200473retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
Jan Kundrát35972df2018-09-06 19:00:01 +0200474 void *user_data, LYS_INFORMAT *format, void (**free_module_data)(void *model_data, void *user_data))
Radek Krejci65ef6d52018-08-16 16:35:02 +0200475{
476 struct clb_data_s *clb_data = (struct clb_data_s *)user_data;
477 unsigned int u, v, match = 1;
478 const char *name = NULL, *rev = NULL;
479 char *model_data = NULL;
480
481 /* get and check the final name and revision of the schema to be retrieved */
482 if (!mod_rev || !mod_rev[0]) {
483 /* newest revision requested - get the newest revision from the list of available modules on server */
484 match = 0;
485 for (u = 0; clb_data->schemas[u].name; ++u) {
486 if (strcmp(mod_name, clb_data->schemas[u].name)) {
487 continue;
488 }
489 if (!match || strcmp(mod_rev, clb_data->schemas[u].revision) > 0) {
490 mod_rev = clb_data->schemas[u].revision;
491 }
492 match = u + 1;
493 }
494 if (!match) {
495 WRN("Session %u: unable to identify revision of the schema \"%s\" from the available server side information.",
496 clb_data->session->id, mod_name);
497 }
498 }
499 if (submod_name) {
500 name = submod_name;
501 if (sub_rev) {
502 rev = sub_rev;
Radek Krejci6455eb12018-08-17 09:26:29 +0200503 } else if (match) {
504 if (!clb_data->schemas[match - 1].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200505 WRN("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 +0200506 clb_data->session->id, submod_name, mod_name);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200507 } else {
508 for (v = 0; clb_data->schemas[match - 1].submodules[v].name; ++v) {
509 if (!strcmp(submod_name, clb_data->schemas[match - 1].submodules[v].name)) {
510 rev = sub_rev = clb_data->schemas[match - 1].submodules[v].revision;
511 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200512 }
Radek Krejci1a7efb42018-08-17 11:51:23 +0200513 if (!rev) {
514 ERR("Session %u: requested submodule \"%s\" is not known for schema \"%s\" on server side.",
515 clb_data->session->id, submod_name, mod_name);
516 return NULL;
517 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200518 }
519 }
520 } else {
521 name = mod_name;
522 rev = mod_rev;
523 }
524
525 VRB("Session %u: retreiving data for schema \"%s\", revision \"%s\".", clb_data->session->id, name, rev);
526
527 if (match) {
528 /* we have enough information to avoid communication with server and try to get
529 * the schema locally */
530
531 /* 1. try to get data locally */
532 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
533
534 /* 2. try to use <get-schema> */
535 if (!model_data && clb_data->has_get_schema) {
536 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
537 }
538 } else {
539 /* we are unsure which revision of the schema we should load, so first try to get
540 * the newest revision from the server via get-schema and only if the server does not
541 * implement get-schema, try to load the newest revision locally. This is imperfect
542 * solution, but there are situation when a client does not know what revision is
543 * actually implemented by the server. */
544
545 /* 1. try to use <get-schema> */
546 if (clb_data->has_get_schema) {
547 model_data = retrieve_schema_data_getschema(name, rev, clb_data, format);
548 }
549
550 /* 2. try to get data locally */
551 if (!model_data) {
552 model_data = retrieve_schema_data_localfile(name, rev, clb_data, format);
553 }
554 }
555
556 /* 3. try to use user callback */
557 if (!model_data && clb_data->user_clb) {
558 VRB("Session %u: reading schema via user callback.", clb_data->session->id);
559 return clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format, free_module_data);
560 }
561
Jan Kundrát35972df2018-09-06 19:00:01 +0200562 *free_module_data = free_with_user_data;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200563 return model_data;
564}
565
Michal Vaskoceae0152018-02-14 16:03:59 +0100566static int
Radek Krejci65ef6d52018-08-16 16:35:02 +0200567nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, struct schema_info *schemas,
Radek Krejci9aa1e352018-05-16 16:05:42 +0200568 ly_module_imp_clb user_clb, void *user_data, int has_get_schema, const struct lys_module **mod)
Michal Vaskoceae0152018-02-14 16:03:59 +0100569{
570 int ret = 0;
571 struct ly_err_item *eitem;
Jan Kundrát6d7c3962018-09-06 19:01:07 +0200572 const char *module_data = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200573 LYS_INFORMAT format;
Jan Kundrát35972df2018-09-06 19:00:01 +0200574 void (*free_module_data)(void*, void*) = NULL;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200575 struct clb_data_s clb_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100576
Radek Krejci65ef6d52018-08-16 16:35:02 +0200577 *mod = NULL;
578 if (revision) {
579 *mod = ly_ctx_get_module(session->ctx, name, revision, 0);
580 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100581 if (*mod) {
Radek Krejci65ef6d52018-08-16 16:35:02 +0200582 if (!(*mod)->implemented) {
Michal Vaskoceae0152018-02-14 16:03:59 +0100583 /* make the present module implemented */
584 if (lys_set_implemented(*mod)) {
585 ERR("Failed to implement model \"%s\".", (*mod)->name);
586 ret = -1;
587 }
588 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200589 } else {
Michal Vaskoceae0152018-02-14 16:03:59 +0100590 /* missing implemented module, load it ... */
Radek Krejci65ef6d52018-08-16 16:35:02 +0200591 clb_data.has_get_schema = has_get_schema;
592 clb_data.schemas = schemas;
593 clb_data.session = session;
594 clb_data.user_clb = user_clb;
595 clb_data.user_data = user_data;
Michal Vaskoceae0152018-02-14 16:03:59 +0100596
597 /* clear all the errors and just collect them for now */
598 ly_err_clean(session->ctx, NULL);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200599 ly_log_options(LY_LOSTORE);
Michal Vaskoceae0152018-02-14 16:03:59 +0100600
Radek Krejci65ef6d52018-08-16 16:35:02 +0200601 /* get module data */
602 module_data = retrieve_schema_data(name, revision, NULL, NULL, &clb_data, &format, &free_module_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100603
Radek Krejci65ef6d52018-08-16 16:35:02 +0200604 if (module_data) {
605 /* parse the schema */
606 ly_ctx_set_module_imp_clb(session->ctx, retrieve_schema_data, &clb_data);
Michal Vaskoceae0152018-02-14 16:03:59 +0100607
Radek Krejci65ef6d52018-08-16 16:35:02 +0200608 *mod = lys_parse_mem(session->ctx, module_data, format);
609 if (*free_module_data) {
Jan Kundrát35972df2018-09-06 19:00:01 +0200610 (*free_module_data)((char*)module_data, user_data);
Michal Vaskodbf7c272018-02-19 09:07:59 +0100611 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100612
Radek Krejci65ef6d52018-08-16 16:35:02 +0200613 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
614 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100615
Michal Vaskodbf7c272018-02-19 09:07:59 +0100616 /* restore logging options, then print errors on definite failure */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200617 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
Michal Vaskoceae0152018-02-14 16:03:59 +0100618 if (!(*mod)) {
619 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
620 ly_err_print(eitem);
621 }
622 ret = -1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200623 } else {
624 /* print only warnings */
625 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
626 if (eitem->level == LY_LLWRN) {
627 ly_err_print(eitem);
628 }
629 }
Michal Vaskoceae0152018-02-14 16:03:59 +0100630 }
631
Michal Vaskodbf7c272018-02-19 09:07:59 +0100632 /* clean the errors */
Michal Vaskoceae0152018-02-14 16:03:59 +0100633 ly_err_clean(session->ctx, NULL);
Michal Vaskoceae0152018-02-14 16:03:59 +0100634 }
635
636 return ret;
637}
638
Radek Krejci65ef6d52018-08-16 16:35:02 +0200639static void
640free_schema_info(struct schema_info *list)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200641{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200642 unsigned int u, v;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200643
Radek Krejci65ef6d52018-08-16 16:35:02 +0200644 if (!list) {
645 return;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200646 }
647
Radek Krejci65ef6d52018-08-16 16:35:02 +0200648 for (u = 0; list[u].name; ++u) {
649 free(list[u].name);
650 free(list[u].revision);
651 for (v = 0; v < list[u].features.number; ++v) {
652 free(list[u].features.set.g[v]);
653 }
654 free(list[u].features.set.g);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200655 if (list[u].submodules) {
656 for (v = 0; list[u].submodules[v].name; ++v) {
657 free(list[u].submodules[v].name);
658 free(list[u].submodules[v].revision);
659 }
660 free(list[u].submodules);
661 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200662 }
663 free(list);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200664}
665
Radek Krejci235d8cb2018-08-17 14:04:32 +0200666
667static int
668build_schema_info_yl(struct nc_session *session, struct schema_info **result)
Radek Krejcifd5b6682017-06-13 15:52:53 +0200669{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200670 struct nc_rpc *rpc = NULL;
671 struct nc_reply *reply = NULL;
672 struct nc_reply_error *error_rpl;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200673 struct lyd_node *yldata = NULL;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200674 NC_MSG_TYPE msg;
675 uint64_t msgid;
Radek Krejcia8dfaea2018-08-17 10:55:09 +0200676 struct ly_set *modules = NULL;
Radek Krejci2d088832018-08-21 13:40:14 +0200677 unsigned int u, v, submodules_count;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200678 struct lyd_node *iter, *child;
679 struct lys_module *mod;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200680 int ret = EXIT_SUCCESS;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200681
682 /* get yang-library data from the server */
Radek Krejci261737f2018-08-21 09:22:34 +0200683 if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) {
684 rpc = nc_rpc_get("/ietf-yang-library:*", 0, NC_PARAMTYPE_CONST);
685 } else {
686 rpc = nc_rpc_get("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", 0, NC_PARAMTYPE_CONST);
687 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200688 if (!rpc) {
689 goto cleanup;
690 }
691
692 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
693 usleep(1000);
694 }
695 if (msg == NC_MSG_ERROR) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200696 WRN("Session %u: failed to send request for yang-library data.",
Radek Krejcifd5b6682017-06-13 15:52:53 +0200697 session->id);
698 goto cleanup;
699 }
700
701 do {
702 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, 0, &reply);
703 } while (msg == NC_MSG_NOTIF);
704 if (msg == NC_MSG_WOULDBLOCK) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200705 WRN("Session %u: timeout for receiving reply to a <get> yang-library data expired.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200706 goto cleanup;
707 } else if (msg == NC_MSG_ERROR) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200708 WRN("Session %u: failed to receive a reply to <get> of yang-library data.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200709 goto cleanup;
710 }
711
712 switch (reply->type) {
713 case NC_RPL_OK:
Radek Krejci235d8cb2018-08-17 14:04:32 +0200714 WRN("Session %u: unexpected reply OK to a yang-library <get> RPC.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200715 goto cleanup;
716 case NC_RPL_DATA:
717 /* fine */
718 break;
719 case NC_RPL_ERROR:
720 error_rpl = (struct nc_reply_error *)reply;
721 if (error_rpl->count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200722 WRN("Session %u: error reply to a yang-library <get> RPC (tag \"%s\", message \"%s\").",
Radek Krejcifd5b6682017-06-13 15:52:53 +0200723 session->id, error_rpl->err[0].tag, error_rpl->err[0].message);
724 } else {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200725 WRN("Session %u: unexpected reply error to a yang-library <get> RPC.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200726 }
727 goto cleanup;
728 case NC_RPL_NOTIF:
Radek Krejci235d8cb2018-08-17 14:04:32 +0200729 WRN("Session %u: unexpected reply notification to a yang-library <get> RPC.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200730 goto cleanup;
731 }
732
Radek Krejci65ef6d52018-08-16 16:35:02 +0200733 yldata = ((struct nc_reply_data *)reply)->data;
734 if (!yldata || strcmp(yldata->schema->module->name, "ietf-yang-library")) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200735 WRN("Session %u: unexpected data in reply to a yang-library <get> RPC.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200736 goto cleanup;
737 }
738
Radek Krejci65ef6d52018-08-16 16:35:02 +0200739 modules = lyd_find_path(yldata, "/ietf-yang-library:modules-state/module");
Radek Krejciac919522018-08-17 11:00:17 +0200740 if (!modules) {
Radek Krejci2d088832018-08-21 13:40:14 +0200741 WRN("Session %u: no module information in reply to a yang-library <get> RPC.", session->id);
742 goto cleanup;
Radek Krejciac919522018-08-17 11:00:17 +0200743 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200744
Radek Krejci2d088832018-08-21 13:40:14 +0200745 (*result) = calloc(modules->number + 1, sizeof **result);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200746 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200747 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200748 ret = EXIT_FAILURE;
Radek Krejcic9390502018-08-17 10:23:13 +0200749 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200750 }
751
Radek Krejci2d088832018-08-21 13:40:14 +0200752 for (u = 0; u < modules->number; ++u) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200753 submodules_count = 0;
754 mod = ((struct lyd_node *)modules->set.d[u])->schema->module;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200755 LY_TREE_FOR(modules->set.d[u]->child, iter) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200756 if (iter->schema->module != mod) {
757 /* ignore node from other schemas (augments) */
758 continue;
759 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200760 if (!((struct lyd_node_leaf_list *)iter)->value_str || !((struct lyd_node_leaf_list *)iter)->value_str[0]) {
761 /* ignore empty nodes */
762 continue;
763 }
764 if (!strcmp(iter->schema->name, "name")) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200765 (*result)[u].name = strdup(((struct lyd_node_leaf_list *)iter)->value_str);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200766 } else if (!strcmp(iter->schema->name, "revision")) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200767 (*result)[u].revision = strdup(((struct lyd_node_leaf_list *)iter)->value_str);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200768 } else if (!strcmp(iter->schema->name, "conformance-type")) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200769 (*result)[u].implemented = !strcmp(((struct lyd_node_leaf_list *)iter)->value_str, "implement");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200770 } else if (!strcmp(iter->schema->name, "feature")) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200771 ly_set_add(&(*result)[u].features, (void *)strdup(((struct lyd_node_leaf_list *)iter)->value_str), LY_SET_OPT_USEASLIST);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200772 } else if (!strcmp(iter->schema->name, "submodule")) {
773 submodules_count++;
774 }
775 }
776
777 if (submodules_count) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200778 (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules);
779 if (!(*result)[u].submodules) {
Radek Krejci1a7efb42018-08-17 11:51:23 +0200780 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200781 free_schema_info(*result);
782 *result = NULL;
783 ret = EXIT_FAILURE;
784 goto cleanup;
Radek Krejci1a7efb42018-08-17 11:51:23 +0200785 } else {
786 v = 0;
787 LY_TREE_FOR(modules->set.d[u]->child, iter) {
788 mod = ((struct lyd_node *)modules->set.d[u])->schema->module;
789 if (mod == iter->schema->module && !strcmp(iter->schema->name, "submodule")) {
790 LY_TREE_FOR(iter->child, child) {
791 if (mod != child->schema->module) {
792 continue;
793 } else if (!strcmp(child->schema->name, "name")) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200794 (*result)[u].submodules[v].name = strdup(((struct lyd_node_leaf_list *)child)->value_str);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200795 } else if (!strcmp(child->schema->name, "revision")) {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200796 (*result)[u].submodules[v].name = strdup(((struct lyd_node_leaf_list *)child)->value_str);
Radek Krejci1a7efb42018-08-17 11:51:23 +0200797 }
798 }
799 }
800 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200801 }
802 }
803 }
804
Radek Krejcifd5b6682017-06-13 15:52:53 +0200805cleanup:
806 nc_rpc_free(rpc);
807 nc_reply_free(reply);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200808 ly_set_free(modules);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200809
Radek Krejci235d8cb2018-08-17 14:04:32 +0200810 if (session->status != NC_STATUS_RUNNING) {
811 /* something bad heppened, discard the session */
812 ERR("Session %d: invalid session, discarding.", nc_session_get_id(session));
813 ret = EXIT_FAILURE;
814 }
815
816 return ret;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200817}
818
Radek Krejci235d8cb2018-08-17 14:04:32 +0200819static int
820build_schema_info_cpblts(char **cpblts, struct schema_info **result)
Radek Krejci65ef6d52018-08-16 16:35:02 +0200821{
822 unsigned int u, v;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200823 char *module_cpblt, *ptr, *ptr2;
824
825 for (u = 0; cpblts[u]; ++u);
Radek Krejci235d8cb2018-08-17 14:04:32 +0200826 (*result) = calloc(u + 1, sizeof **result);
827 if (!(*result)) {
Radek Krejcic9390502018-08-17 10:23:13 +0200828 ERRMEM;
Radek Krejci235d8cb2018-08-17 14:04:32 +0200829 return EXIT_FAILURE;
Radek Krejcic9390502018-08-17 10:23:13 +0200830 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200831
832 for (u = v = 0; cpblts[u]; ++u) {
833 module_cpblt = strstr(cpblts[u], "module=");
834 /* this capability requires a module */
835 if (!module_cpblt) {
836 continue;
837 }
838
839 /* get module's name */
840 ptr = (char *)module_cpblt + 7;
841 ptr2 = strchr(ptr, '&');
842 if (!ptr2) {
843 ptr2 = ptr + strlen(ptr);
844 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200845 (*result)[v].name = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200846
847 /* get module's revision */
848 ptr = strstr(module_cpblt, "revision=");
849 if (ptr) {
850 ptr += 9;
851 ptr2 = strchr(ptr, '&');
852 if (!ptr2) {
853 ptr2 = ptr + strlen(ptr);
854 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200855 (*result)[v].revision = strndup(ptr, ptr2 - ptr);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200856 }
857
858 /* all are implemented since there is no better information in capabilities list */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200859 (*result)[v].implemented = 1;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200860
861 /* get module's features */
862 ptr = strstr(module_cpblt, "features=");
863 if (ptr) {
864 ptr += 9;
865 for (ptr2 = ptr; *ptr && *ptr != '&'; ++ptr) {
866 if (*ptr == ',') {
Radek Krejci235d8cb2018-08-17 14:04:32 +0200867 ly_set_add(&(*result)[v].features, (void *)strndup(ptr2, ptr - ptr2), LY_SET_OPT_USEASLIST);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200868 ptr2 = ptr + 1;
869 }
870 }
871 /* the last one */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200872 ly_set_add(&(*result)[v].features, (void *)strndup(ptr2, ptr - ptr2), LY_SET_OPT_USEASLIST);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200873 }
874 ++v;
875 }
Radek Krejci235d8cb2018-08-17 14:04:32 +0200876
877 return EXIT_SUCCESS;
Radek Krejci65ef6d52018-08-16 16:35:02 +0200878}
879
880static int
881nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data, int has_get_schema)
882{
883 int ret = EXIT_FAILURE;
884 const struct lys_module *mod;
885 unsigned int u, v;
886
887 for (u = 0; modules[u].name; ++u) {
Michal Vasko488c7f52018-09-19 11:19:44 +0200888 /* skip import-only modules */
889 if (!modules[u].implemented) {
890 continue;
891 }
892
Radek Krejci65ef6d52018-08-16 16:35:02 +0200893 /* we can continue even if it fails */
894 nc_ctx_load_module(session, modules[u].name, modules[u].revision, modules, user_clb, user_data, has_get_schema, &mod);
895
896 if (!mod) {
897 if (session->status != NC_STATUS_RUNNING) {
898 /* something bad heppened, discard the session */
899 ERR("Session %d: invalid session, discarding.", nc_session_get_id(session));
900 goto cleanup;
901 }
902
903 /* all loading ways failed, the schema will be ignored in the received data */
904 WRN("Failed to load schema \"%s@%s\".", modules[u].name, modules[u].revision ? modules[u].revision : "<latest>");
905 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
906 } else {
907 /* set features - first disable all to enable specified then */
908 lys_features_disable(mod, "*");
909 for (v = 0; v < modules[u].features.number; v++) {
910 lys_features_enable(mod, (const char*)modules[u].features.set.g[v]);
911 }
912 }
Michal Vasko60f66602017-10-17 13:52:18 +0200913 }
914
Radek Krejci65ef6d52018-08-16 16:35:02 +0200915 /* done */
916 ret = EXIT_SUCCESS;
917
918cleanup:
919
Radek Krejcifd5b6682017-06-13 15:52:53 +0200920 return ret;
921}
922
Radek Krejci65ef6d52018-08-16 16:35:02 +0200923static int
924nc_ctx_fill_ietf_netconf(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data, int has_get_schema)
925{
926 unsigned int u, v;
927 const struct lys_module *ietfnc;
928
929 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL, 1);
930 if (!ietfnc) {
931 nc_ctx_load_module(session, "ietf-netconf", NULL, modules, user_clb, user_data, has_get_schema, &ietfnc);
932 if (!ietfnc) {
933 WRN("Unable to find correct \"ietf-netconf\" schema, trying to use backup from \"%s\".", NC_SCHEMAS_DIR"/ietf-netconf.yin");
934 ietfnc = lys_parse_path(session->ctx, NC_SCHEMAS_DIR"/ietf-netconf.yin", LYS_IN_YIN);
935 }
936 }
937 if (!ietfnc) {
938 ERR("Loading base NETCONF schema failed.");
939 return 1;
940 }
941
942 /* set supported capabilities from ietf-netconf */
943 for (u = 0; modules[u].name; ++u) {
944 if (strcmp(modules[u].name, "ietf-netconf") || !modules[u].implemented) {
945 continue;
946 }
947
948 lys_features_disable(ietfnc, "*");
949 for (v = 0; v < modules[u].features.number; v++) {
950 lys_features_enable(ietfnc, (const char*)modules[u].features.set.g[v]);
951 }
952 }
953
954 return 0;
955}
956
Michal Vasko086311b2016-01-08 09:53:11 +0100957int
958nc_ctx_check_and_fill(struct nc_session *session)
959{
Radek Krejci65ef6d52018-08-16 16:35:02 +0200960 int i, get_schema_support = 0, yanglib_support = 0, ret = -1;
Michal Vaskocdeee432017-01-13 13:51:01 +0100961 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100962 void *old_data = NULL;
Radek Krejcib1d250e2018-04-12 13:54:18 +0200963 const struct lys_module *mod = NULL;
964 char *revision;
Radek Krejcib056ff82018-08-20 15:58:39 +0200965 struct schema_info *server_modules = NULL, *sm = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100966
Michal Vasko2e6defd2016-10-07 15:48:15 +0200967 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +0100968
Radek Krejci65ef6d52018-08-16 16:35:02 +0200969 /* 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 +0200970 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200971
Radek Krejci65ef6d52018-08-16 16:35:02 +0200972 /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit
973 * schemas only to those present on the server side */
974 ly_ctx_set_disable_searchdirs(session->ctx);
975
976 /* our callback is set later with appropriate data */
977 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
978
979 /* check if get-schema and yang-library is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200980 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200981 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200982 get_schema_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200983 if (yanglib_support) {
984 break;
985 }
Radek Krejcicde410d2018-08-21 09:56:49 +0200986 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:1.0", 51)) {
Radek Krejcib1d250e2018-04-12 13:54:18 +0200987 yanglib_support = 1 + i;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200988 if (get_schema_support) {
989 break;
990 }
Michal Vasko086311b2016-01-08 09:53:11 +0100991 }
992 }
Radek Krejci65ef6d52018-08-16 16:35:02 +0200993
994 /* get information about server's schemas from capabilities list until we will have yang-library */
Radek Krejci235d8cb2018-08-17 14:04:32 +0200995 if (build_schema_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) {
996 ERR("Session %u: unable to get server's schema information from the <hello>'s capabilities.", session->id);
Radek Krejci65ef6d52018-08-16 16:35:02 +0200997 goto cleanup;
998 }
999
Michal Vasko086311b2016-01-08 09:53:11 +01001000 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Radek Krejci3222b7d2017-09-21 16:04:30 +02001001 if (get_schema_support && !ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL, 1)) {
Radek Krejci65ef6d52018-08-16 16:35:02 +02001002 if (nc_ctx_load_module(session, "ietf-netconf-monitoring", NULL, server_modules, old_clb, old_data, 0, &mod)) {
1003 WRN("Session %u: loading NETCONF monitoring schema failed, cannot use <get-schema>.", session->id);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001004 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001005 }
1006 }
1007
1008 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Radek Krejci65ef6d52018-08-16 16:35:02 +02001009 if (nc_ctx_fill_ietf_netconf(session, server_modules, old_clb, old_data, get_schema_support)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001010 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001011 }
1012
Radek Krejci65ef6d52018-08-16 16:35:02 +02001013 /* get correct version of ietf-yang-library into context */
1014 if (yanglib_support) {
Radek Krejcib1d250e2018-04-12 13:54:18 +02001015 /* use get schema to get server's ietf-yang-library */
1016 revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision=");
1017 if (!revision) {
Radek Krejci65ef6d52018-08-16 16:35:02 +02001018 WRN("Session %u: loading NETCONF ietf-yang-library schema failed, missing revision in NETCONF <hello> message.", session->id);
1019 WRN("Session %u: unable to automatically use <get-schema>.", session->id);
Radek Krejcib1d250e2018-04-12 13:54:18 +02001020 yanglib_support = 0;
1021 } else {
1022 revision = strndup(&revision[9], 10);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001023 if (nc_ctx_load_module(session, "ietf-yang-library", revision, server_modules, old_clb, old_data, get_schema_support, &mod)) {
1024 WRN("Session %u: loading NETCONF ietf-yang-library schema failed, unable to automatically use <get-schema>.", session->id);
Radek Krejcib1d250e2018-04-12 13:54:18 +02001025 yanglib_support = 0;
1026 }
1027 free(revision);
1028 }
1029 }
1030
Radek Krejci65ef6d52018-08-16 16:35:02 +02001031 /* prepare structured information about server's schemas */
Radek Krejci9aa1e352018-05-16 16:05:42 +02001032 if (yanglib_support) {
Radek Krejcif0633792018-08-20 15:12:09 +02001033 if (build_schema_info_yl(session, &sm)) {
1034 goto cleanup;
1035 } else if (!sm) {
1036 VRB("Session %u: trying to use capabilities instead of ietf-yang-library data.", session->id);
1037 } else {
Radek Krejci65ef6d52018-08-16 16:35:02 +02001038 /* prefer yang-library information, currently we have it from capabilities used for getting correct yang-library schema */
1039 free_schema_info(server_modules);
Radek Krejcif0633792018-08-20 15:12:09 +02001040 server_modules = sm;
Radek Krejci235d8cb2018-08-17 14:04:32 +02001041 }
Radek Krejci65ef6d52018-08-16 16:35:02 +02001042 }
Michal Vaskoef578332016-01-25 13:20:09 +01001043
Radek Krejci65ef6d52018-08-16 16:35:02 +02001044 if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) {
1045 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001046 }
1047
Radek Krejcifd5b6682017-06-13 15:52:53 +02001048 /* succsess */
1049 ret = 0;
1050
Michal Vaskoeee99412016-11-21 10:19:43 +01001051 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
Radek Krejci65ef6d52018-08-16 16:35:02 +02001052 WRN("Session %u: some models failed to be loaded, any data from these models (and any other unknown) will be ignored.", session->id);
Michal Vaskoef578332016-01-25 13:20:09 +01001053 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001054
1055cleanup:
Radek Krejci65ef6d52018-08-16 16:35:02 +02001056 free_schema_info(server_modules);
1057
Radek Krejcifd5b6682017-06-13 15:52:53 +02001058 /* set user callback back */
1059 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Radek Krejci65ef6d52018-08-16 16:35:02 +02001060 ly_ctx_unset_disable_searchdirs(session->ctx);
Radek Krejcifd5b6682017-06-13 15:52:53 +02001061
Michal Vaskoef578332016-01-25 13:20:09 +01001062 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001063}
1064
1065API struct nc_session *
1066nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
1067{
Michal Vaskod083db62016-01-19 10:31:29 +01001068 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +01001069
Michal Vasko45e53ae2016-04-07 11:46:03 +02001070 if (fdin < 0) {
1071 ERRARG("fdin");
1072 return NULL;
1073 } else if (fdout < 0) {
1074 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +01001075 return NULL;
1076 }
1077
1078 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001079 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001080 if (!session) {
1081 ERRMEM;
1082 return NULL;
1083 }
1084 session->status = NC_STATUS_STARTING;
Michal Vasko086311b2016-01-08 09:53:11 +01001085
1086 /* transport specific data */
1087 session->ti_type = NC_TI_FD;
1088 session->ti.fd.in = fdin;
1089 session->ti.fd.out = fdout;
1090
1091 /* assign context (dicionary needed for handshake) */
1092 if (!ctx) {
Radek Krejci65ef6d52018-08-16 16:35:02 +02001093 ctx = ly_ctx_new(NC_SCHEMAS_DIR, LY_CTX_NOYANGLIBRARY);
Michal Vaskoe035b8e2016-03-11 10:10:03 +01001094 /* definitely should not happen, but be ready */
Radek Krejci3222b7d2017-09-21 16:04:30 +02001095 if (!ctx && !(ctx = ly_ctx_new(NULL, 0))) {
Michal Vaskoe035b8e2016-03-11 10:10:03 +01001096 /* that's just it */
1097 goto fail;
1098 }
Michal Vasko086311b2016-01-08 09:53:11 +01001099 } else {
1100 session->flags |= NC_SESSION_SHAREDCTX;
1101 }
1102 session->ctx = ctx;
1103
1104 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001105 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +01001106 goto fail;
1107 }
1108 session->status = NC_STATUS_RUNNING;
1109
Michal Vaskoef578332016-01-25 13:20:09 +01001110 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +01001111 goto fail;
1112 }
1113
1114 return session;
1115
1116fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001117 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001118 return NULL;
1119}
1120
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001121API struct nc_session *
1122nc_connect_unix(const char *address, struct ly_ctx *ctx)
1123{
1124 struct nc_session *session = NULL;
1125 struct sockaddr_un sun;
1126 const struct passwd *pw;
1127 char *username;
1128 int sock = -1;
1129
1130 if (address == NULL) {
1131 ERRARG("address");
1132 return NULL;
1133 }
1134
1135 pw = getpwuid(geteuid());
1136 if (pw == NULL) {
1137 ERR("Failed to find username for euid=%u.\n", geteuid());
1138 goto fail;
1139 }
1140
1141 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1142 if (sock < 0) {
1143 ERR("Failed to create socket (%s).", strerror(errno));
1144 goto fail;
1145 }
1146
1147 memset(&sun, 0, sizeof(sun));
1148 sun.sun_family = AF_UNIX;
1149 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
1150
1151 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
1152 ERR("cannot connect to sock server %s (%s)",
1153 address, strerror(errno));
1154 goto fail;
1155 }
1156
1157 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
1158 ERR("Fcntl failed (%s).", strerror(errno));
1159 goto fail;
1160 }
1161
1162 /* prepare session structure */
1163 session = nc_new_session(NC_CLIENT, 0);
1164 if (!session) {
1165 ERRMEM;
1166 goto fail;
1167 }
1168 session->status = NC_STATUS_STARTING;
1169
1170 /* transport specific data */
1171 session->ti_type = NC_TI_UNIX;
1172 session->ti.unixsock.sock = sock;
1173 sock = -1; /* do not close sock in fail label anymore */
1174
1175 /* assign context (dictionary needed for handshake) */
1176 if (!ctx) {
1177 ctx = ly_ctx_new(NC_SCHEMAS_DIR, LY_CTX_NOYANGLIBRARY);
1178 /* definitely should not happen, but be ready */
1179 if (!ctx && !(ctx = ly_ctx_new(NULL, 0))) {
1180 /* that's just it */
1181 goto fail;
1182 }
1183 } else {
1184 session->flags |= NC_SESSION_SHAREDCTX;
1185 }
1186 session->ctx = ctx;
1187
1188 session->path = lydict_insert(ctx, address, 0);
1189
1190 username = strdup(pw->pw_name);
1191 if (username == NULL) {
1192 ERRMEM;
1193 goto fail;
1194 }
1195 session->username = lydict_insert_zc(ctx, username);
1196
1197 /* NETCONF handshake */
1198 if (nc_handshake_io(session) != NC_MSG_HELLO) {
1199 goto fail;
1200 }
1201 session->status = NC_STATUS_RUNNING;
1202
1203 if (nc_ctx_check_and_fill(session) == -1) {
1204 goto fail;
1205 }
1206
1207 return session;
1208
1209fail:
1210 nc_session_free(session, NULL);
1211 if (sock >= 0)
1212 close(sock);
1213 return NULL;
1214}
1215
Frank Rimpler9f838b02018-07-25 06:44:03 +00001216/*
1217 Helper for a non-blocking connect (which is required because of the locking
1218 concept for e.g. call home settings). For more details see nc_sock_connect().
1219 */
1220static int
1221_non_blocking_connect(int timeout, int* sock_pending, struct addrinfo *res)
Michal Vasko086311b2016-01-08 09:53:11 +01001222{
Frank Rimplerc7a2acb2018-08-06 07:31:08 +00001223 int flags, ret=0;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001224 int sock = -1;
1225 fd_set wset;
1226 struct timeval ts;
1227 int error = 0;
1228 socklen_t len = sizeof(int);
Michal Vasko086311b2016-01-08 09:53:11 +01001229
Frank Rimpler9f838b02018-07-25 06:44:03 +00001230 if (sock_pending && *sock_pending != -1) {
1231 VRB("Trying to connect the pending socket=%d.", *sock_pending );
1232 sock = *sock_pending;
1233 } else {
1234 assert(res);
1235 VRB("Trying to connect via %s.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
1236 /* Connect to a server */
Michal Vasko086311b2016-01-08 09:53:11 +01001237 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1238 if (sock == -1) {
Frank Rimpler9f838b02018-07-25 06:44:03 +00001239 ERR("socket couldn't be created.", strerror(errno));
1240 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001241 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001242 /* make the socket non-blocking */
1243 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
1244 ERR("Fcntl failed (%s).", strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +00001245 goto cleanup;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001246 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001247 /* non-blocking connect! */
1248 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
1249 if (errno != EINPROGRESS) {
1250 /* network connection failed, try another resource */
1251 ERR("connect failed: (%s).", strerror(errno));
1252 goto cleanup;
1253 }
1254 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001255 }
1256 ts.tv_sec = timeout;
1257 ts.tv_usec = 0;
Michal Vasko7d965dc2018-03-12 14:39:23 +01001258
Frank Rimpler9f838b02018-07-25 06:44:03 +00001259 FD_ZERO(&wset);
1260 FD_SET(sock, &wset);
1261
1262 if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
1263 ERR("select failed: (%s).", strerror(errno));
1264 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +01001265 }
1266
Frank Rimpler9f838b02018-07-25 06:44:03 +00001267 if (ret == 0) { //we had a timeout
1268 VRB("timed out after %ds (%s).", timeout, strerror(errno));
1269 if (sock_pending) {
1270 /* no sock-close, we'll try it again */
1271 *sock_pending = sock;
1272 } else {
1273 close(sock);
1274 }
1275 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001276 }
Radek Krejci782041a2018-08-20 10:09:45 +02001277
1278 /* check the usability of the socket */
1279 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
1280 ERR("getsockopt failed: (%s).", strerror(errno));
1281 goto cleanup;
1282 }
1283 if (error == ECONNREFUSED) {
1284 /* network connection failed, try another resource */
1285 VRB("getsockopt error: (%s).", strerror(error));
1286 errno = error;
1287 goto cleanup;
1288 }
Michal Vaskobe52dc22018-10-17 09:28:17 +02001289
1290 /* enable keep-alive */
1291 if (nc_sock_enable_keepalive(sock)) {
1292 goto cleanup;
1293 }
1294
Michal Vasko086311b2016-01-08 09:53:11 +01001295 return sock;
Michal Vasko06c860d2018-07-09 16:08:52 +02001296
Frank Rimpler9f838b02018-07-25 06:44:03 +00001297cleanup:
1298 if (sock_pending) {
1299 *sock_pending = -1;
Michal Vasko06c860d2018-07-09 16:08:52 +02001300 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001301 close(sock);
Michal Vasko06c860d2018-07-09 16:08:52 +02001302 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001303}
1304
Frank Rimpler9f838b02018-07-25 06:44:03 +00001305/* A given timeout value limits the time how long the function blocks. If it has to block
1306 only for some seconds, a socket connection might not yet have been fully established.
1307 Therefore the active (pending) socket will be stored in *sock_pending, but the return
1308 value will be -1. In such a case a subsequent invokation is required, by providing the
1309 stored sock_pending, again.
1310 In general, if this function returns -1, when a timeout has been given, this function
1311 has to be invoked, until it returns a valid socket.
1312 */
1313int
Michal Vasko66032bc2019-01-22 15:03:12 +01001314nc_sock_connect(const char *host, uint16_t port, int timeout, int *sock_pending, char **ip_host)
Frank Rimpler9f838b02018-07-25 06:44:03 +00001315{
Michal Vasko83ad17e2019-01-30 10:11:37 +01001316 int i, opt;
Michal Vasko66032bc2019-01-22 15:03:12 +01001317 int sock = sock_pending ? *sock_pending : -1;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001318 struct addrinfo hints, *res_list, *res;
Michal Vasko83ad17e2019-01-30 10:11:37 +01001319 char *buf, port_s[6]; /* length of string representation of short int */
Michal Vasko66032bc2019-01-22 15:03:12 +01001320 void *addr;
Frank Rimpler9f838b02018-07-25 06:44:03 +00001321
Michal Vasko66032bc2019-01-22 15:03:12 +01001322 DBG("nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
Frank Rimpler9f838b02018-07-25 06:44:03 +00001323
1324 /* no pending socket */
1325 if (sock == -1) {
Michal Vasko66032bc2019-01-22 15:03:12 +01001326 /* connect to a server */
Frank Rimpler9f838b02018-07-25 06:44:03 +00001327 snprintf(port_s, 6, "%u", port);
1328 memset(&hints, 0, sizeof hints);
1329 hints.ai_family = AF_UNSPEC;
1330 hints.ai_socktype = SOCK_STREAM;
1331 hints.ai_protocol = IPPROTO_TCP;
1332 i = getaddrinfo(host, port_s, &hints, &res_list);
1333 if (i != 0) {
1334 ERR("Unable to translate the host address (%s).", gai_strerror(i));
1335 return -1;
1336 }
1337
1338 for (res = res_list; res != NULL; res = res->ai_next) {
1339 sock = _non_blocking_connect(timeout, sock_pending, res);
1340 if (sock == -1 && (!sock_pending || *sock_pending == -1)) {
1341 /* try the next resource */
1342 continue;
1343 }
1344 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 +01001345
1346 opt = 1;
1347 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
1348 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
1349 close(sock);
1350 return -1;
1351 }
1352
Michal Vasko66032bc2019-01-22 15:03:12 +01001353 if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) {
1354 buf = malloc(INET6_ADDRSTRLEN);
1355 if (!buf) {
1356 ERRMEM;
1357 close(sock);
1358 return -1;
1359 }
1360 if (res->ai_family == AF_INET) {
1361 addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
1362 } else {
1363 addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1364 }
1365 if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) {
1366 ERR("Converting host to IP address failed (%s).", strerror(errno));
1367 free(buf);
1368 close(sock);
1369 return -1;
1370 }
1371
1372 *ip_host = buf;
1373 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00001374 break;
1375 }
1376 freeaddrinfo(res_list);
1377
1378 } else {
1379 /* try to get a connection with the pending socket */
1380 assert(sock_pending);
1381 sock = _non_blocking_connect(timeout, sock_pending, NULL);
1382 }
1383
1384 return sock;
1385}
1386
Michal Vasko086311b2016-01-08 09:53:11 +01001387static NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001388get_msg(struct nc_session *session, int timeout, uint64_t msgid, struct lyxml_elem **msg)
Michal Vasko086311b2016-01-08 09:53:11 +01001389{
Michal Vasko086311b2016-01-08 09:53:11 +01001390 char *ptr;
1391 const char *str_msgid;
1392 uint64_t cur_msgid;
1393 struct lyxml_elem *xml;
Michal Vasko2518b6b2016-01-28 13:24:53 +01001394 struct nc_msg_cont *cont, **cont_ptr;
Michal Vasko086311b2016-01-08 09:53:11 +01001395 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1396
Michal Vasko086311b2016-01-08 09:53:11 +01001397 /* try to get notification from the session's queue */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001398 if (!msgid && session->opts.client.notifs) {
1399 cont = session->opts.client.notifs;
1400 session->opts.client.notifs = cont->next;
Michal Vasko086311b2016-01-08 09:53:11 +01001401
Michal Vasko71ba2da2016-05-04 10:53:16 +02001402 xml = cont->msg;
Michal Vasko086311b2016-01-08 09:53:11 +01001403 free(cont);
1404
Michal Vasko71ba2da2016-05-04 10:53:16 +02001405 msgtype = NC_MSG_NOTIF;
Michal Vasko086311b2016-01-08 09:53:11 +01001406 }
1407
1408 /* try to get rpc-reply from the session's queue */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001409 if (msgid && session->opts.client.replies) {
1410 cont = session->opts.client.replies;
1411 session->opts.client.replies = cont->next;
Michal Vasko2518b6b2016-01-28 13:24:53 +01001412
Michal Vasko71ba2da2016-05-04 10:53:16 +02001413 xml = cont->msg;
1414 free(cont);
Michal Vasko086311b2016-01-08 09:53:11 +01001415
Michal Vasko71ba2da2016-05-04 10:53:16 +02001416 msgtype = NC_MSG_REPLY;
Michal Vasko086311b2016-01-08 09:53:11 +01001417 }
1418
Michal Vasko71ba2da2016-05-04 10:53:16 +02001419 if (!msgtype) {
1420 /* read message from wire */
Michal Vasko131120a2018-05-29 15:44:02 +02001421 msgtype = nc_read_msg_poll_io(session, timeout, &xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +02001422 }
Michal Vasko086311b2016-01-08 09:53:11 +01001423
1424 /* we read rpc-reply, want a notif */
1425 if (!msgid && (msgtype == NC_MSG_REPLY)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001426 cont_ptr = &session->opts.client.replies;
Michal Vasko086311b2016-01-08 09:53:11 +01001427 while (*cont_ptr) {
1428 cont_ptr = &((*cont_ptr)->next);
1429 }
1430 *cont_ptr = malloc(sizeof **cont_ptr);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001431 if (!*cont_ptr) {
1432 ERRMEM;
1433 lyxml_free(session->ctx, xml);
1434 return NC_MSG_ERROR;
1435 }
Michal Vasko086311b2016-01-08 09:53:11 +01001436 (*cont_ptr)->msg = xml;
1437 (*cont_ptr)->next = NULL;
1438 }
1439
1440 /* we read notif, want a rpc-reply */
1441 if (msgid && (msgtype == NC_MSG_NOTIF)) {
Michal Vasko3486a7c2017-03-03 13:28:07 +01001442 if (!session->opts.client.ntf_tid) {
Michal Vaskod083db62016-01-19 10:31:29 +01001443 ERR("Session %u: received a <notification> but session is not subscribed.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001444 lyxml_free(session->ctx, xml);
Michal Vasko2518b6b2016-01-28 13:24:53 +01001445 return NC_MSG_ERROR;
Michal Vasko3486a7c2017-03-03 13:28:07 +01001446 }
Michal Vasko086311b2016-01-08 09:53:11 +01001447
Michal Vasko2e6defd2016-10-07 15:48:15 +02001448 cont_ptr = &session->opts.client.notifs;
Michal Vasko086311b2016-01-08 09:53:11 +01001449 while (*cont_ptr) {
1450 cont_ptr = &((*cont_ptr)->next);
1451 }
1452 *cont_ptr = malloc(sizeof **cont_ptr);
Michal Vasko140eecd2018-05-30 09:55:56 +02001453 if (!*cont_ptr) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001454 ERRMEM;
1455 lyxml_free(session->ctx, xml);
1456 return NC_MSG_ERROR;
1457 }
Michal Vasko086311b2016-01-08 09:53:11 +01001458 (*cont_ptr)->msg = xml;
1459 (*cont_ptr)->next = NULL;
1460 }
1461
Michal Vasko086311b2016-01-08 09:53:11 +01001462 switch (msgtype) {
1463 case NC_MSG_NOTIF:
Michal Vasko2518b6b2016-01-28 13:24:53 +01001464 if (!msgid) {
1465 *msg = xml;
Michal Vasko086311b2016-01-08 09:53:11 +01001466 }
Michal Vasko086311b2016-01-08 09:53:11 +01001467 break;
1468
1469 case NC_MSG_REPLY:
Michal Vasko2518b6b2016-01-28 13:24:53 +01001470 if (msgid) {
Michal Vasko71ba2da2016-05-04 10:53:16 +02001471 /* check message-id */
1472 str_msgid = lyxml_get_attr(xml, "message-id", NULL);
1473 if (!str_msgid) {
Michal Vasko14fdbb62018-01-18 09:13:20 +01001474 WRN("Session %u: received a <rpc-reply> without a message-id.", session->id);
Michal Vasko71ba2da2016-05-04 10:53:16 +02001475 } else {
1476 cur_msgid = strtoul(str_msgid, &ptr, 10);
1477 if (cur_msgid != msgid) {
1478 ERR("Session %u: received a <rpc-reply> with an unexpected message-id \"%s\".",
1479 session->id, str_msgid);
1480 msgtype = NC_MSG_REPLY_ERR_MSGID;
1481 }
1482 }
Michal Vasko2518b6b2016-01-28 13:24:53 +01001483 *msg = xml;
Michal Vasko086311b2016-01-08 09:53:11 +01001484 }
Michal Vasko086311b2016-01-08 09:53:11 +01001485 break;
1486
1487 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001488 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001489 lyxml_free(session->ctx, xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +02001490 msgtype = NC_MSG_ERROR;
1491 break;
Michal Vasko086311b2016-01-08 09:53:11 +01001492
1493 case NC_MSG_RPC:
Michal Vaskod083db62016-01-19 10:31:29 +01001494 ERR("Session %u: received <rpc> from a NETCONF server.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001495 lyxml_free(session->ctx, xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +02001496 msgtype = NC_MSG_ERROR;
1497 break;
Michal Vasko086311b2016-01-08 09:53:11 +01001498
1499 default:
1500 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
1501 * NC_MSG_NONE is not returned by nc_read_msg()
1502 */
1503 break;
1504 }
1505
1506 return msgtype;
1507}
1508
1509/* cannot strictly fail, but does not need to fill any error parameter at all */
1510static void
1511parse_rpc_error(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_err *err)
1512{
1513 struct lyxml_elem *iter, *next, *info;
1514
1515 LY_TREE_FOR(xml->child, iter) {
1516 if (!iter->ns) {
1517 if (iter->content) {
1518 WRN("<rpc-error> child \"%s\" with value \"%s\" without namespace.", iter->name, iter->content);
1519 } else {
1520 WRN("<rpc-error> child \"%s\" without namespace.", iter->name);
1521 }
1522 continue;
1523 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
1524 if (iter->content) {
1525 WRN("<rpc-error> child \"%s\" with value \"%s\" in an unknown namespace \"%s\".",
1526 iter->name, iter->content, iter->ns->value);
1527 } else {
1528 WRN("<rpc-error> child \"%s\" in an unknown namespace \"%s\".", iter->name, iter->ns->value);
1529 }
1530 continue;
1531 }
1532
1533 if (!strcmp(iter->name, "error-type")) {
1534 if (!iter->content || (strcmp(iter->content, "transport") && strcmp(iter->content, "rpc")
1535 && strcmp(iter->content, "protocol") && strcmp(iter->content, "application"))) {
1536 WRN("<rpc-error> <error-type> unknown value \"%s\".", (iter->content ? iter->content : ""));
1537 } else if (err->type) {
1538 WRN("<rpc-error> <error-type> duplicated.");
1539 } else {
1540 err->type = lydict_insert(ctx, iter->content, 0);
1541 }
1542 } else if (!strcmp(iter->name, "error-tag")) {
1543 if (!iter->content || (strcmp(iter->content, "in-use") && strcmp(iter->content, "invalid-value")
1544 && strcmp(iter->content, "too-big") && strcmp(iter->content, "missing-attribute")
1545 && strcmp(iter->content, "bad-attribute") && strcmp(iter->content, "unknown-attribute")
1546 && strcmp(iter->content, "missing-element") && strcmp(iter->content, "bad-element")
1547 && strcmp(iter->content, "unknown-element") && strcmp(iter->content, "unknown-namespace")
1548 && strcmp(iter->content, "access-denied") && strcmp(iter->content, "lock-denied")
1549 && strcmp(iter->content, "resource-denied") && strcmp(iter->content, "rollback-failed")
1550 && strcmp(iter->content, "data-exists") && strcmp(iter->content, "data-missing")
1551 && strcmp(iter->content, "operation-not-supported") && strcmp(iter->content, "operation-failed")
1552 && strcmp(iter->content, "malformed-message"))) {
1553 WRN("<rpc-error> <error-tag> unknown value \"%s\".", (iter->content ? iter->content : ""));
1554 } else if (err->tag) {
1555 WRN("<rpc-error> <error-tag> duplicated.");
1556 } else {
1557 err->tag = lydict_insert(ctx, iter->content, 0);
1558 }
1559 } else if (!strcmp(iter->name, "error-severity")) {
1560 if (!iter->content || (strcmp(iter->content, "error") && strcmp(iter->content, "warning"))) {
1561 WRN("<rpc-error> <error-severity> unknown value \"%s\".", (iter->content ? iter->content : ""));
1562 } else if (err->severity) {
1563 WRN("<rpc-error> <error-severity> duplicated.");
1564 } else {
1565 err->severity = lydict_insert(ctx, iter->content, 0);
1566 }
1567 } else if (!strcmp(iter->name, "error-app-tag")) {
1568 if (err->apptag) {
1569 WRN("<rpc-error> <error-app-tag> duplicated.");
1570 } else {
1571 err->apptag = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1572 }
1573 } else if (!strcmp(iter->name, "error-path")) {
1574 if (err->path) {
1575 WRN("<rpc-error> <error-path> duplicated.");
1576 } else {
1577 err->path = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1578 }
1579 } else if (!strcmp(iter->name, "error-message")) {
1580 if (err->message) {
1581 WRN("<rpc-error> <error-message> duplicated.");
1582 } else {
1583 err->message_lang = lyxml_get_attr(iter, "xml:lang", NULL);
Michal Vaskob0222c42018-01-03 15:17:12 +01001584 if (err->message_lang) {
1585 err->message_lang = lydict_insert(ctx, err->message_lang, 0);
1586 } else {
Michal Vasko086311b2016-01-08 09:53:11 +01001587 VRB("<rpc-error> <error-message> without the recommended \"xml:lang\" attribute.");
1588 }
1589 err->message = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1590 }
1591 } else if (!strcmp(iter->name, "error-info")) {
1592 LY_TREE_FOR_SAFE(iter->child, next, info) {
1593 if (info->ns && !strcmp(info->ns->value, NC_NS_BASE)) {
1594 if (!strcmp(info->name, "session-id")) {
1595 if (err->sid) {
1596 WRN("<rpc-error> <error-info> <session-id> duplicated.");
1597 } else {
1598 err->sid = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1599 }
Michal Vasko630485f2018-01-03 14:28:32 +01001600 } else if (!strcmp(info->name, "bad-attribute")) {
Michal Vasko086311b2016-01-08 09:53:11 +01001601 ++err->attr_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001602 err->attr = nc_realloc(err->attr, err->attr_count * sizeof *err->attr);
1603 if (!err->attr) {
1604 ERRMEM;
1605 return;
1606 }
Michal Vasko086311b2016-01-08 09:53:11 +01001607 err->attr[err->attr_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1608 } else if (!strcmp(info->name, "bad-element")) {
1609 ++err->elem_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001610 err->elem = nc_realloc(err->elem, err->elem_count * sizeof *err->elem);
1611 if (!err->elem) {
1612 ERRMEM;
1613 return;
1614 }
Michal Vasko086311b2016-01-08 09:53:11 +01001615 err->elem[err->elem_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1616 } else if (!strcmp(info->name, "bad-namespace")) {
1617 ++err->ns_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001618 err->ns = nc_realloc(err->ns, err->ns_count * sizeof *err->ns);
1619 if (!err->ns) {
1620 ERRMEM;
1621 return;
1622 }
Michal Vasko086311b2016-01-08 09:53:11 +01001623 err->ns[err->ns_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1624 } else {
1625 if (info->content) {
1626 WRN("<rpc-error> <error-info> unknown child \"%s\" with value \"%s\".",
1627 info->name, info->content);
1628 } else {
1629 WRN("<rpc-error> <error-info> unknown child \"%s\".", info->name);
1630 }
1631 }
1632 } else {
1633 lyxml_unlink(ctx, info);
1634 ++err->other_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001635 err->other = nc_realloc(err->other, err->other_count * sizeof *err->other);
1636 if (!err->other) {
1637 ERRMEM;
1638 return;
1639 }
Michal Vasko086311b2016-01-08 09:53:11 +01001640 err->other[err->other_count - 1] = info;
1641 }
1642 }
1643 } else {
1644 if (iter->content) {
1645 WRN("<rpc-error> unknown child \"%s\" with value \"%s\".", iter->name, iter->content);
1646 } else {
1647 WRN("<rpc-error> unknown child \"%s\".", iter->name);
1648 }
1649 }
1650 }
1651}
1652
1653static struct nc_reply *
Michal Vaskoeb7080e2016-02-18 13:27:05 +01001654parse_reply(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_rpc *rpc, int parseroptions)
Michal Vasko086311b2016-01-08 09:53:11 +01001655{
1656 struct lyxml_elem *iter;
Michal Vaskoe1708602016-10-18 12:17:22 +02001657 struct lyd_node *data = NULL, *rpc_act = NULL;
Michal Vasko1a38c862016-01-15 15:50:07 +01001658 struct nc_client_reply_error *error_rpl;
Michal Vasko086311b2016-01-08 09:53:11 +01001659 struct nc_reply_data *data_rpl;
1660 struct nc_reply *reply = NULL;
Michal Vasko90e8e692016-07-13 12:27:57 +02001661 struct nc_rpc_act_generic *rpc_gen;
Michal Vaskof45e0c12018-11-28 08:38:23 +01001662 int i, data_parsed = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001663
1664 if (!xml->child) {
1665 ERR("An empty <rpc-reply>.");
1666 return NULL;
1667 }
1668
1669 /* rpc-error */
1670 if (!strcmp(xml->child->name, "rpc-error") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
1671 /* count and check elements */
1672 i = 0;
1673 LY_TREE_FOR(xml->child, iter) {
1674 if (strcmp(iter->name, "rpc-error")) {
1675 ERR("<rpc-reply> content mismatch (<rpc-error> and <%s>).", iter->name);
1676 return NULL;
1677 } else if (!iter->ns) {
1678 ERR("<rpc-reply> content mismatch (<rpc-error> without namespace).");
1679 return NULL;
1680 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
1681 ERR("<rpc-reply> content mismatch (<rpc-error> with NS \"%s\").", iter->ns->value);
1682 return NULL;
1683 }
1684 ++i;
1685 }
1686
1687 error_rpl = malloc(sizeof *error_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001688 if (!error_rpl) {
1689 ERRMEM;
1690 return NULL;
1691 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001692 error_rpl->type = NC_RPL_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001693 error_rpl->err = calloc(i, sizeof *error_rpl->err);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001694 if (!error_rpl->err) {
1695 ERRMEM;
1696 free(error_rpl);
1697 return NULL;
1698 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001699 error_rpl->count = i;
Michal Vasko1a38c862016-01-15 15:50:07 +01001700 error_rpl->ctx = ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001701 reply = (struct nc_reply *)error_rpl;
1702
1703 i = 0;
1704 LY_TREE_FOR(xml->child, iter) {
1705 parse_rpc_error(ctx, iter, error_rpl->err + i);
1706 ++i;
1707 }
1708
1709 /* ok */
1710 } else if (!strcmp(xml->child->name, "ok") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
1711 if (xml->child->next) {
1712 ERR("<rpc-reply> content mismatch (<ok> and <%s>).", xml->child->next->name);
1713 return NULL;
1714 }
1715 reply = malloc(sizeof *reply);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001716 if (!reply) {
1717 ERRMEM;
1718 return NULL;
1719 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001720 reply->type = NC_RPL_OK;
Michal Vasko086311b2016-01-08 09:53:11 +01001721
1722 /* some RPC output */
1723 } else {
1724 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001725 case NC_RPC_ACT_GENERIC:
1726 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01001727
1728 if (rpc_gen->has_data) {
Michal Vaskodd3e3142018-07-10 08:30:20 +02001729 rpc_act = lyd_dup(rpc_gen->content.data, 1);
1730 if (!rpc_act) {
1731 ERR("Failed to duplicate a generic RPC/action.");
1732 return NULL;
1733 }
Michal Vasko086311b2016-01-08 09:53:11 +01001734 } else {
Michal Vaskoeee99412016-11-21 10:19:43 +01001735 rpc_act = lyd_parse_mem(ctx, rpc_gen->content.xml_str, LYD_XML, LYD_OPT_RPC | parseroptions, NULL);
Michal Vaskoe1708602016-10-18 12:17:22 +02001736 if (!rpc_act) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001737 ERR("Failed to parse a generic RPC/action XML.");
Michal Vasko086311b2016-01-08 09:53:11 +01001738 return NULL;
1739 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001740 }
Michal Vasko086311b2016-01-08 09:53:11 +01001741 break;
1742
1743 case NC_RPC_GETCONFIG:
1744 case NC_RPC_GET:
Michal Vasko13ed2942016-02-29 16:21:00 +01001745 if (!xml->child->child) {
1746 /* we did not receive any data */
1747 data_rpl = malloc(sizeof *data_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001748 if (!data_rpl) {
1749 ERRMEM;
1750 return NULL;
1751 }
Michal Vasko13ed2942016-02-29 16:21:00 +01001752 data_rpl->type = NC_RPL_DATA;
1753 data_rpl->data = NULL;
1754 return (struct nc_reply *)data_rpl;
1755 }
1756
Michal Vasko086311b2016-01-08 09:53:11 +01001757 /* special treatment */
Michal Vaskof45e0c12018-11-28 08:38:23 +01001758 ly_errno = 0;
Michal Vasko0a5ae9a2016-11-15 11:54:47 +01001759 data = lyd_parse_xml(ctx, &xml->child->child,
1760 LYD_OPT_DESTRUCT | (rpc->type == NC_RPC_GETCONFIG ? LYD_OPT_GETCONFIG : LYD_OPT_GET)
Michal Vaskoeee99412016-11-21 10:19:43 +01001761 | parseroptions);
Michal Vaskof45e0c12018-11-28 08:38:23 +01001762 if (ly_errno) {
Michal Vasko086311b2016-01-08 09:53:11 +01001763 ERR("Failed to parse <%s> reply.", (rpc->type == NC_RPC_GETCONFIG ? "get-config" : "get"));
1764 return NULL;
1765 }
Michal Vaskof45e0c12018-11-28 08:38:23 +01001766 data_parsed = 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001767 break;
1768
1769 case NC_RPC_GETSCHEMA:
Radek Krejci3222b7d2017-09-21 16:04:30 +02001770 rpc_act = lyd_new(NULL, ly_ctx_get_module(ctx, "ietf-netconf-monitoring", NULL, 1), "get-schema");
Michal Vaskoe1708602016-10-18 12:17:22 +02001771 if (!rpc_act) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001772 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +01001773 return NULL;
1774 }
1775 break;
1776
1777 case NC_RPC_EDIT:
1778 case NC_RPC_COPY:
1779 case NC_RPC_DELETE:
1780 case NC_RPC_LOCK:
1781 case NC_RPC_UNLOCK:
1782 case NC_RPC_KILL:
1783 case NC_RPC_COMMIT:
1784 case NC_RPC_DISCARD:
1785 case NC_RPC_CANCEL:
1786 case NC_RPC_VALIDATE:
1787 case NC_RPC_SUBSCRIBE:
1788 /* there is no output defined */
1789 ERR("Unexpected data reply (root elem \"%s\").", xml->child->name);
1790 return NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001791 default:
1792 ERRINT;
1793 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001794 }
1795
1796 data_rpl = malloc(sizeof *data_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001797 if (!data_rpl) {
1798 ERRMEM;
1799 return NULL;
1800 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001801 data_rpl->type = NC_RPL_DATA;
Michal Vaskof45e0c12018-11-28 08:38:23 +01001802
1803 ly_errno = 0;
1804 if (!data_parsed) {
Michal Vasko68b3f292016-09-16 12:00:32 +02001805 data_rpl->data = lyd_parse_xml(ctx, &xml->child, LYD_OPT_RPCREPLY | LYD_OPT_DESTRUCT | parseroptions,
Michal Vaskocc3d52b2016-10-18 12:17:22 +02001806 rpc_act, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001807 } else {
1808 /* <get>, <get-config> */
1809 data_rpl->data = data;
1810 }
Michal Vaskoe1708602016-10-18 12:17:22 +02001811 lyd_free_withsiblings(rpc_act);
Michal Vaskof45e0c12018-11-28 08:38:23 +01001812 if (ly_errno) {
Michal Vasko086311b2016-01-08 09:53:11 +01001813 ERR("Failed to parse <rpc-reply>.");
1814 free(data_rpl);
1815 return NULL;
1816 }
1817 reply = (struct nc_reply *)data_rpl;
1818 }
1819
1820 return reply;
1821}
1822
Radek Krejci53691be2016-02-22 13:58:37 +01001823#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001824
Michal Vasko3031aae2016-01-27 16:07:18 +01001825int
1826nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1827{
1828 int sock;
1829
Michal Vasko45e53ae2016-04-07 11:46:03 +02001830 if (!address) {
1831 ERRARG("address");
1832 return -1;
1833 } else if (!port) {
1834 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001835 return -1;
1836 }
1837
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001838 sock = nc_sock_listen_inet(address, port);
Michal Vasko3031aae2016-01-27 16:07:18 +01001839 if (sock == -1) {
1840 return -1;
1841 }
1842
1843 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001844 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1845 if (!client_opts.ch_binds) {
1846 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001847 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001848 return -1;
1849 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001850
Michal Vasko2e6defd2016-10-07 15:48:15 +02001851 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1852 if (!client_opts.ch_bind_ti) {
1853 ERRMEM;
1854 close(sock);
1855 return -1;
1856 }
1857 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1858
Michal Vasko3031aae2016-01-27 16:07:18 +01001859 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001860 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1861 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001862 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001863 return -1;
1864 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001865 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1866 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001867 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001868
1869 return 0;
1870}
1871
1872int
1873nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1874{
1875 uint32_t i;
1876 int ret = -1;
1877
1878 if (!address && !port && !ti) {
1879 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1880 close(client_opts.ch_binds[i].sock);
1881 free((char *)client_opts.ch_binds[i].address);
1882
1883 ret = 0;
1884 }
1885 free(client_opts.ch_binds);
1886 client_opts.ch_binds = NULL;
1887 client_opts.ch_bind_count = 0;
1888 } else {
1889 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1890 if ((!address || !strcmp(client_opts.ch_binds[i].address, address))
1891 && (!port || (client_opts.ch_binds[i].port == port))
Michal Vasko2e6defd2016-10-07 15:48:15 +02001892 && (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001893 close(client_opts.ch_binds[i].sock);
1894 free((char *)client_opts.ch_binds[i].address);
1895
1896 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001897 if (!client_opts.ch_bind_count) {
1898 free(client_opts.ch_binds);
1899 client_opts.ch_binds = NULL;
1900 } else if (i < client_opts.ch_bind_count) {
1901 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1902 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1903 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001904
1905 ret = 0;
1906 }
1907 }
1908 }
1909
1910 return ret;
1911}
1912
1913API int
1914nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1915{
1916 int sock;
1917 char *host = NULL;
1918 uint16_t port, idx;
1919
Michal Vasko45e53ae2016-04-07 11:46:03 +02001920 if (!client_opts.ch_binds) {
1921 ERRINIT;
1922 return -1;
1923 } else if (!session) {
1924 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001925 return -1;
1926 }
1927
1928 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1929
Michal Vasko50456e82016-02-02 12:16:08 +01001930 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001931 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001932 return sock;
1933 }
1934
Radek Krejci53691be2016-02-22 13:58:37 +01001935#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001936 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001937 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001938 } else
1939#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001940#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001941 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001942 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001943 } else
1944#endif
1945 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001946 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001947 *session = NULL;
1948 }
1949
1950 free(host);
1951
1952 if (!(*session)) {
1953 return -1;
1954 }
1955
1956 return 1;
1957}
1958
Radek Krejci53691be2016-02-22 13:58:37 +01001959#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001960
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001961API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001962nc_session_get_cpblts(const struct nc_session *session)
1963{
1964 if (!session) {
1965 ERRARG("session");
1966 return NULL;
1967 }
1968
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001969 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001970}
1971
1972API const char *
1973nc_session_cpblt(const struct nc_session *session, const char *capab)
1974{
1975 int i, len;
1976
1977 if (!session) {
1978 ERRARG("session");
1979 return NULL;
1980 } else if (!capab) {
1981 ERRARG("capab");
1982 return NULL;
1983 }
1984
1985 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001986 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1987 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1988 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001989 }
1990 }
1991
1992 return NULL;
1993}
1994
Michal Vasko9cd26a82016-05-31 08:58:48 +02001995API int
1996nc_session_ntf_thread_running(const struct nc_session *session)
1997{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001998 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001999 ERRARG("session");
2000 return 0;
2001 }
2002
Michal Vasko2e6defd2016-10-07 15:48:15 +02002003 return session->opts.client.ntf_tid ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02002004}
2005
Michal Vaskob7558c52016-02-26 15:04:19 +01002006API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01002007nc_client_init(void)
2008{
2009 nc_init();
2010}
2011
2012API void
Michal Vaskob7558c52016-02-26 15:04:19 +01002013nc_client_destroy(void)
2014{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02002015 nc_client_set_schema_searchpath(NULL);
2016#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
2017 nc_client_ch_del_bind(NULL, 0, 0);
2018#endif
2019#ifdef NC_ENABLED_SSH
2020 nc_client_ssh_destroy_opts();
2021#endif
2022#ifdef NC_ENABLED_TLS
2023 nc_client_tls_destroy_opts();
2024#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01002025 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01002026}
2027
Michal Vasko086311b2016-01-08 09:53:11 +01002028API NC_MSG_TYPE
Michal Vaskoeb7080e2016-02-18 13:27:05 +01002029nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout, int parseroptions, struct nc_reply **reply)
Michal Vasko086311b2016-01-08 09:53:11 +01002030{
2031 struct lyxml_elem *xml;
2032 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
2033
Michal Vasko45e53ae2016-04-07 11:46:03 +02002034 if (!session) {
2035 ERRARG("session");
2036 return NC_MSG_ERROR;
2037 } else if (!rpc) {
2038 ERRARG("rpc");
2039 return NC_MSG_ERROR;
2040 } else if (!reply) {
2041 ERRARG("reply");
2042 return NC_MSG_ERROR;
2043 } else if (parseroptions & LYD_OPT_TYPEMASK) {
2044 ERRARG("parseroptions");
Michal Vasko086311b2016-01-08 09:53:11 +01002045 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002046 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vaskod083db62016-01-19 10:31:29 +01002047 ERR("Session %u: invalid session to receive RPC replies.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002048 return NC_MSG_ERROR;
2049 }
Michal Vaskoeb7080e2016-02-18 13:27:05 +01002050 parseroptions &= ~(LYD_OPT_DESTRUCT | LYD_OPT_NOSIBLINGS);
Michal Vaskoeee99412016-11-21 10:19:43 +01002051 if (!(session->flags & NC_SESSION_CLIENT_NOT_STRICT)) {
2052 parseroptions &= LYD_OPT_STRICT;
2053 }
Michal Vasko41adf392017-01-17 10:39:04 +01002054 /* no mechanism to check external dependencies is provided */
2055 parseroptions|= LYD_OPT_NOEXTDEPS;
Michal Vasko086311b2016-01-08 09:53:11 +01002056 *reply = NULL;
2057
2058 msgtype = get_msg(session, timeout, msgid, &xml);
Michal Vasko086311b2016-01-08 09:53:11 +01002059
Michal Vasko71ba2da2016-05-04 10:53:16 +02002060 if ((msgtype == NC_MSG_REPLY) || (msgtype == NC_MSG_REPLY_ERR_MSGID)) {
Michal Vaskoeee99412016-11-21 10:19:43 +01002061 *reply = parse_reply(session->ctx, xml, rpc, parseroptions);
Michal Vasko086311b2016-01-08 09:53:11 +01002062 lyxml_free(session->ctx, xml);
2063 if (!(*reply)) {
2064 return NC_MSG_ERROR;
2065 }
2066 }
2067
2068 return msgtype;
2069}
2070
2071API NC_MSG_TYPE
2072nc_recv_notif(struct nc_session *session, int timeout, struct nc_notif **notif)
2073{
2074 struct lyxml_elem *xml, *ev_time;
2075 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
2076
Michal Vasko45e53ae2016-04-07 11:46:03 +02002077 if (!session) {
2078 ERRARG("session");
2079 return NC_MSG_ERROR;
2080 } else if (!notif) {
2081 ERRARG("notif");
Michal Vasko086311b2016-01-08 09:53:11 +01002082 return NC_MSG_ERROR;
2083 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01002084 ERR("Session %u: invalid session to receive Notifications.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002085 return NC_MSG_ERROR;
2086 }
2087
2088 msgtype = get_msg(session, timeout, 0, &xml);
2089
2090 if (msgtype == NC_MSG_NOTIF) {
2091 *notif = calloc(1, sizeof **notif);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002092 if (!*notif) {
2093 ERRMEM;
2094 lyxml_free(session->ctx, xml);
2095 return NC_MSG_ERROR;
2096 }
Michal Vasko086311b2016-01-08 09:53:11 +01002097
2098 /* eventTime */
2099 LY_TREE_FOR(xml->child, ev_time) {
2100 if (!strcmp(ev_time->name, "eventTime")) {
2101 (*notif)->datetime = lydict_insert(session->ctx, ev_time->content, 0);
2102 /* lyd_parse does not know this element */
2103 lyxml_free(session->ctx, ev_time);
2104 break;
2105 }
2106 }
2107 if (!(*notif)->datetime) {
Michal Vaskod083db62016-01-19 10:31:29 +01002108 ERR("Session %u: notification is missing the \"eventTime\" element.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002109 goto fail;
2110 }
2111
2112 /* notification body */
Michal Vasko41adf392017-01-17 10:39:04 +01002113 (*notif)->tree = lyd_parse_xml(session->ctx, &xml->child, LYD_OPT_NOTIF | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS
Michal Vaskoeee99412016-11-21 10:19:43 +01002114 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01002115 lyxml_free(session->ctx, xml);
2116 xml = NULL;
2117 if (!(*notif)->tree) {
Michal Vaskod083db62016-01-19 10:31:29 +01002118 ERR("Session %u: failed to parse a new notification.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002119 goto fail;
2120 }
2121 }
2122
2123 return msgtype;
2124
2125fail:
2126 lydict_remove(session->ctx, (*notif)->datetime);
2127 lyd_free((*notif)->tree);
2128 free(*notif);
2129 *notif = NULL;
2130 lyxml_free(session->ctx, xml);
2131
2132 return NC_MSG_ERROR;
2133}
2134
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002135static void *
2136nc_recv_notif_thread(void *arg)
2137{
2138 struct nc_ntf_thread_arg *ntarg;
2139 struct nc_session *session;
2140 void (*notif_clb)(struct nc_session *session, const struct nc_notif *notif);
2141 struct nc_notif *notif;
2142 NC_MSG_TYPE msgtype;
Michal Vasko131120a2018-05-29 15:44:02 +02002143 pthread_t *ntf_tid;
2144
2145 pthread_detach(pthread_self());
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002146
2147 ntarg = (struct nc_ntf_thread_arg *)arg;
2148 session = ntarg->session;
2149 notif_clb = ntarg->notif_clb;
2150 free(ntarg);
2151
Michal Vasko131120a2018-05-29 15:44:02 +02002152 /* remember our allocated tid, we will be freeing it */
2153 ntf_tid = (pthread_t *)session->opts.client.ntf_tid;
2154
Michal Vasko2e6defd2016-10-07 15:48:15 +02002155 while (session->opts.client.ntf_tid) {
Michal vasko4e1a36c2016-10-05 13:04:37 +02002156 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &notif);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002157 if (msgtype == NC_MSG_NOTIF) {
2158 notif_clb(session, notif);
Michal Vaskof0537d82016-01-29 14:42:38 +01002159 if (!strcmp(notif->tree->schema->name, "notificationComplete")
2160 && !strcmp(notif->tree->schema->module->name, "nc-notifications")) {
2161 nc_notif_free(notif);
2162 break;
2163 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002164 nc_notif_free(notif);
Michal Vaskoce326052018-01-04 10:32:03 +01002165 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02002166 /* quit this thread once the session is broken */
2167 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002168 }
2169
2170 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
2171 }
2172
Michal Vasko0651c902016-05-19 15:55:42 +02002173 VRB("Session %u: notification thread exit.", session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002174 session->opts.client.ntf_tid = NULL;
Michal Vasko131120a2018-05-29 15:44:02 +02002175 free(ntf_tid);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002176 return NULL;
2177}
2178
2179API int
2180nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session, const struct nc_notif *notif))
2181{
2182 struct nc_ntf_thread_arg *ntarg;
2183 int ret;
2184
Michal Vasko45e53ae2016-04-07 11:46:03 +02002185 if (!session) {
2186 ERRARG("session");
2187 return -1;
2188 } else if (!notif_clb) {
2189 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002190 return -1;
2191 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
2192 ERR("Session %u: invalid session to receive Notifications.", session->id);
2193 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002194 } else if (session->opts.client.ntf_tid) {
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002195 ERR("Session %u: separate notification thread is already running.", session->id);
2196 return -1;
2197 }
2198
2199 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01002200 if (!ntarg) {
2201 ERRMEM;
2202 return -1;
2203 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002204 ntarg->session = session;
2205 ntarg->notif_clb = notif_clb;
2206
2207 /* just so that nc_recv_notif_thread() does not immediately exit, the value does not matter */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002208 session->opts.client.ntf_tid = malloc(sizeof *session->opts.client.ntf_tid);
2209 if (!session->opts.client.ntf_tid) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01002210 ERRMEM;
2211 free(ntarg);
2212 return -1;
2213 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002214
Michal Vasko2e6defd2016-10-07 15:48:15 +02002215 ret = pthread_create((pthread_t *)session->opts.client.ntf_tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002216 if (ret) {
2217 ERR("Session %u: failed to create a new thread (%s).", strerror(errno));
2218 free(ntarg);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002219 free((pthread_t *)session->opts.client.ntf_tid);
2220 session->opts.client.ntf_tid = NULL;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01002221 return -1;
2222 }
2223
2224 return 0;
2225}
2226
Michal Vasko086311b2016-01-08 09:53:11 +01002227API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002228nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01002229{
2230 NC_MSG_TYPE r;
Michal Vasko131120a2018-05-29 15:44:02 +02002231 int dofree = 1;
Michal Vasko90e8e692016-07-13 12:27:57 +02002232 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01002233 struct nc_rpc_getconfig *rpc_gc;
2234 struct nc_rpc_edit *rpc_e;
2235 struct nc_rpc_copy *rpc_cp;
2236 struct nc_rpc_delete *rpc_del;
2237 struct nc_rpc_lock *rpc_lock;
2238 struct nc_rpc_get *rpc_g;
2239 struct nc_rpc_kill *rpc_k;
2240 struct nc_rpc_commit *rpc_com;
2241 struct nc_rpc_cancel *rpc_can;
2242 struct nc_rpc_validate *rpc_val;
2243 struct nc_rpc_getschema *rpc_gs;
2244 struct nc_rpc_subscribe *rpc_sub;
2245 struct lyd_node *data, *node;
Michal Vasko9d8bee62016-03-03 10:58:24 +01002246 const struct lys_module *ietfnc = NULL, *ietfncmon, *notifs, *ietfncwd = NULL;
Radek Krejci539efb62016-08-24 15:05:16 +02002247 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01002248 uint64_t cur_msgid;
2249
Michal Vasko45e53ae2016-04-07 11:46:03 +02002250 if (!session) {
2251 ERRARG("session");
2252 return NC_MSG_ERROR;
2253 } else if (!rpc) {
2254 ERRARG("rpc");
2255 return NC_MSG_ERROR;
2256 } else if (!msgid) {
2257 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01002258 return NC_MSG_ERROR;
2259 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01002260 ERR("Session %u: invalid session to send RPCs.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002261 return NC_MSG_ERROR;
2262 }
2263
Michal Vasko90e8e692016-07-13 12:27:57 +02002264 if ((rpc->type != NC_RPC_GETSCHEMA) && (rpc->type != NC_RPC_ACT_GENERIC) && (rpc->type != NC_RPC_SUBSCRIBE)) {
Radek Krejci3222b7d2017-09-21 16:04:30 +02002265 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002266 if (!ietfnc) {
Michal Vasko086cd572017-01-12 12:19:05 +01002267 ERR("Session %u: missing \"ietf-netconf\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002268 return NC_MSG_ERROR;
2269 }
2270 }
2271
2272 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02002273 case NC_RPC_ACT_GENERIC:
2274 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01002275
2276 if (rpc_gen->has_data) {
2277 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01002278 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01002279 } else {
Michal Vasko41adf392017-01-17 10:39:04 +01002280 data = lyd_parse_mem(session->ctx, rpc_gen->content.xml_str, LYD_XML, LYD_OPT_RPC | LYD_OPT_NOEXTDEPS
Michal Vaskoeee99412016-11-21 10:19:43 +01002281 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL);
Michal Vaskoeec410f2017-11-24 09:14:55 +01002282 if (!data) {
2283 return NC_MSG_ERROR;
2284 }
Michal Vasko086311b2016-01-08 09:53:11 +01002285 }
2286 break;
2287
2288 case NC_RPC_GETCONFIG:
2289 rpc_gc = (struct nc_rpc_getconfig *)rpc;
2290
2291 data = lyd_new(NULL, ietfnc, "get-config");
2292 node = lyd_new(data, ietfnc, "source");
2293 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_gc->source], NULL);
2294 if (!node) {
2295 lyd_free(data);
2296 return NC_MSG_ERROR;
2297 }
2298 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002299 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002300 node = lyd_new_anydata(data, ietfnc, "filter", rpc_gc->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01002301 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01002302 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02002303 node = lyd_new_anydata(data, ietfnc, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01002304 lyd_insert_attr(node, NULL, "type", "xpath");
2305 lyd_insert_attr(node, NULL, "select", rpc_gc->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01002306 }
2307 if (!node) {
2308 lyd_free(data);
2309 return NC_MSG_ERROR;
2310 }
2311 }
2312
2313 if (rpc_gc->wd_mode) {
2314 if (!ietfncwd) {
Radek Krejci3222b7d2017-09-21 16:04:30 +02002315 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002316 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01002317 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002318 return NC_MSG_ERROR;
2319 }
2320 }
2321 switch (rpc_gc->wd_mode) {
2322 case NC_WD_UNKNOWN:
2323 /* cannot get here */
2324 break;
2325 case NC_WD_ALL:
2326 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
2327 break;
2328 case NC_WD_ALL_TAG:
2329 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
2330 break;
2331 case NC_WD_TRIM:
2332 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
2333 break;
2334 case NC_WD_EXPLICIT:
2335 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
2336 break;
2337 }
2338 if (!node) {
2339 lyd_free(data);
2340 return NC_MSG_ERROR;
2341 }
2342 }
2343 break;
2344
2345 case NC_RPC_EDIT:
2346 rpc_e = (struct nc_rpc_edit *)rpc;
2347
2348 data = lyd_new(NULL, ietfnc, "edit-config");
2349 node = lyd_new(data, ietfnc, "target");
2350 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_e->target], NULL);
2351 if (!node) {
2352 lyd_free(data);
2353 return NC_MSG_ERROR;
2354 }
2355
2356 if (rpc_e->default_op) {
2357 node = lyd_new_leaf(data, ietfnc, "default-operation", rpcedit_dfltop2str[rpc_e->default_op]);
2358 if (!node) {
2359 lyd_free(data);
2360 return NC_MSG_ERROR;
2361 }
2362 }
2363
2364 if (rpc_e->test_opt) {
2365 node = lyd_new_leaf(data, ietfnc, "test-option", rpcedit_testopt2str[rpc_e->test_opt]);
2366 if (!node) {
2367 lyd_free(data);
2368 return NC_MSG_ERROR;
2369 }
2370 }
2371
2372 if (rpc_e->error_opt) {
2373 node = lyd_new_leaf(data, ietfnc, "error-option", rpcedit_erropt2str[rpc_e->error_opt]);
2374 if (!node) {
2375 lyd_free(data);
2376 return NC_MSG_ERROR;
2377 }
2378 }
2379
Michal Vasko7793bc62016-09-16 11:58:41 +02002380 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002381 node = lyd_new_anydata(data, ietfnc, "config", rpc_e->edit_cont, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01002382 } else {
2383 node = lyd_new_leaf(data, ietfnc, "url", rpc_e->edit_cont);
2384 }
2385 if (!node) {
2386 lyd_free(data);
2387 return NC_MSG_ERROR;
2388 }
2389 break;
2390
2391 case NC_RPC_COPY:
2392 rpc_cp = (struct nc_rpc_copy *)rpc;
2393
2394 data = lyd_new(NULL, ietfnc, "copy-config");
2395 node = lyd_new(data, ietfnc, "target");
2396 if (rpc_cp->url_trg) {
2397 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_trg);
2398 } else {
2399 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->target], NULL);
2400 }
2401 if (!node) {
2402 lyd_free(data);
2403 return NC_MSG_ERROR;
2404 }
2405
2406 node = lyd_new(data, ietfnc, "source");
2407 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002408 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002409 node = lyd_new_anydata(node, ietfnc, "config", rpc_cp->url_config_src, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01002410 } else {
2411 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_config_src);
2412 }
2413 } else {
2414 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->source], NULL);
2415 }
2416 if (!node) {
2417 lyd_free(data);
2418 return NC_MSG_ERROR;
2419 }
2420
2421 if (rpc_cp->wd_mode) {
2422 if (!ietfncwd) {
Radek Krejci3222b7d2017-09-21 16:04:30 +02002423 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002424 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01002425 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002426 return NC_MSG_ERROR;
2427 }
2428 }
2429 switch (rpc_cp->wd_mode) {
2430 case NC_WD_UNKNOWN:
2431 /* cannot get here */
2432 break;
2433 case NC_WD_ALL:
2434 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
2435 break;
2436 case NC_WD_ALL_TAG:
2437 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
2438 break;
2439 case NC_WD_TRIM:
2440 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
2441 break;
2442 case NC_WD_EXPLICIT:
2443 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
2444 break;
2445 }
2446 if (!node) {
2447 lyd_free(data);
2448 return NC_MSG_ERROR;
2449 }
2450 }
2451 break;
2452
2453 case NC_RPC_DELETE:
2454 rpc_del = (struct nc_rpc_delete *)rpc;
2455
2456 data = lyd_new(NULL, ietfnc, "delete-config");
2457 node = lyd_new(data, ietfnc, "target");
2458 if (rpc_del->url) {
2459 node = lyd_new_leaf(node, ietfnc, "url", rpc_del->url);
2460 } else {
2461 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_del->target], NULL);
2462 }
2463 if (!node) {
2464 lyd_free(data);
2465 return NC_MSG_ERROR;
2466 }
2467 break;
2468
2469 case NC_RPC_LOCK:
2470 rpc_lock = (struct nc_rpc_lock *)rpc;
2471
2472 data = lyd_new(NULL, ietfnc, "lock");
2473 node = lyd_new(data, ietfnc, "target");
2474 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
2475 if (!node) {
2476 lyd_free(data);
2477 return NC_MSG_ERROR;
2478 }
2479 break;
2480
2481 case NC_RPC_UNLOCK:
2482 rpc_lock = (struct nc_rpc_lock *)rpc;
2483
2484 data = lyd_new(NULL, ietfnc, "unlock");
2485 node = lyd_new(data, ietfnc, "target");
2486 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
2487 if (!node) {
2488 lyd_free(data);
2489 return NC_MSG_ERROR;
2490 }
2491 break;
2492
2493 case NC_RPC_GET:
2494 rpc_g = (struct nc_rpc_get *)rpc;
2495
2496 data = lyd_new(NULL, ietfnc, "get");
2497 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002498 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002499 node = lyd_new_anydata(data, ietfnc, "filter", rpc_g->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01002500 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01002501 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02002502 node = lyd_new_anydata(data, ietfnc, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01002503 lyd_insert_attr(node, NULL, "type", "xpath");
2504 lyd_insert_attr(node, NULL, "select", rpc_g->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01002505 }
2506 if (!node) {
2507 lyd_free(data);
2508 return NC_MSG_ERROR;
2509 }
2510 }
2511
2512 if (rpc_g->wd_mode) {
2513 if (!ietfncwd) {
Radek Krejci3222b7d2017-09-21 16:04:30 +02002514 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002515 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01002516 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoc41fdea2017-08-09 10:18:44 +02002517 lyd_free(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002518 return NC_MSG_ERROR;
2519 }
2520 }
2521 switch (rpc_g->wd_mode) {
Michal Vasko086311b2016-01-08 09:53:11 +01002522 case NC_WD_ALL:
2523 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
2524 break;
2525 case NC_WD_ALL_TAG:
2526 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
2527 break;
2528 case NC_WD_TRIM:
2529 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
2530 break;
2531 case NC_WD_EXPLICIT:
2532 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
2533 break;
Michal Vasko2260f552018-06-06 10:06:12 +02002534 default:
2535 /* cannot get here */
2536 node = NULL;
2537 break;
Michal Vasko086311b2016-01-08 09:53:11 +01002538 }
2539 if (!node) {
2540 lyd_free(data);
2541 return NC_MSG_ERROR;
2542 }
2543 }
2544 break;
2545
2546 case NC_RPC_KILL:
2547 rpc_k = (struct nc_rpc_kill *)rpc;
2548
2549 data = lyd_new(NULL, ietfnc, "kill-session");
2550 sprintf(str, "%u", rpc_k->sid);
2551 lyd_new_leaf(data, ietfnc, "session-id", str);
2552 break;
2553
2554 case NC_RPC_COMMIT:
2555 rpc_com = (struct nc_rpc_commit *)rpc;
2556
2557 data = lyd_new(NULL, ietfnc, "commit");
2558 if (rpc_com->confirmed) {
2559 lyd_new_leaf(data, ietfnc, "confirmed", NULL);
2560 }
2561
2562 if (rpc_com->confirm_timeout) {
2563 sprintf(str, "%u", rpc_com->confirm_timeout);
2564 lyd_new_leaf(data, ietfnc, "confirm-timeout", str);
2565 }
2566
2567 if (rpc_com->persist) {
2568 node = lyd_new_leaf(data, ietfnc, "persist", rpc_com->persist);
2569 if (!node) {
2570 lyd_free(data);
2571 return NC_MSG_ERROR;
2572 }
2573 }
2574
2575 if (rpc_com->persist_id) {
2576 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_com->persist_id);
2577 if (!node) {
2578 lyd_free(data);
2579 return NC_MSG_ERROR;
2580 }
2581 }
2582 break;
2583
2584 case NC_RPC_DISCARD:
2585 data = lyd_new(NULL, ietfnc, "discard-changes");
2586 break;
2587
2588 case NC_RPC_CANCEL:
2589 rpc_can = (struct nc_rpc_cancel *)rpc;
2590
2591 data = lyd_new(NULL, ietfnc, "cancel-commit");
2592 if (rpc_can->persist_id) {
2593 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_can->persist_id);
2594 if (!node) {
2595 lyd_free(data);
2596 return NC_MSG_ERROR;
2597 }
2598 }
2599 break;
2600
2601 case NC_RPC_VALIDATE:
2602 rpc_val = (struct nc_rpc_validate *)rpc;
2603
2604 data = lyd_new(NULL, ietfnc, "validate");
2605 node = lyd_new(data, ietfnc, "source");
2606 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002607 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002608 node = lyd_new_anydata(node, ietfnc, "config", rpc_val->url_config_src, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01002609 } else {
2610 node = lyd_new_leaf(node, ietfnc, "url", rpc_val->url_config_src);
2611 }
2612 } else {
2613 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_val->source], NULL);
2614 }
2615 if (!node) {
2616 lyd_free(data);
2617 return NC_MSG_ERROR;
2618 }
2619 break;
2620
2621 case NC_RPC_GETSCHEMA:
Radek Krejci3222b7d2017-09-21 16:04:30 +02002622 ietfncmon = ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002623 if (!ietfncmon) {
Michal Vasko086cd572017-01-12 12:19:05 +01002624 ERR("Session %u: missing \"ietf-netconf-monitoring\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002625 return NC_MSG_ERROR;
2626 }
2627
2628 rpc_gs = (struct nc_rpc_getschema *)rpc;
2629
2630 data = lyd_new(NULL, ietfncmon, "get-schema");
2631 node = lyd_new_leaf(data, ietfncmon, "identifier", rpc_gs->identifier);
2632 if (!node) {
2633 lyd_free(data);
2634 return NC_MSG_ERROR;
2635 }
2636 if (rpc_gs->version) {
2637 node = lyd_new_leaf(data, ietfncmon, "version", rpc_gs->version);
2638 if (!node) {
2639 lyd_free(data);
2640 return NC_MSG_ERROR;
2641 }
2642 }
2643 if (rpc_gs->format) {
2644 node = lyd_new_leaf(data, ietfncmon, "format", rpc_gs->format);
2645 if (!node) {
2646 lyd_free(data);
2647 return NC_MSG_ERROR;
2648 }
2649 }
2650 break;
2651
2652 case NC_RPC_SUBSCRIBE:
Radek Krejci3222b7d2017-09-21 16:04:30 +02002653 notifs = ly_ctx_get_module(session->ctx, "notifications", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002654 if (!notifs) {
Michal Vasko086cd572017-01-12 12:19:05 +01002655 ERR("Session %u: missing \"notifications\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002656 return NC_MSG_ERROR;
2657 }
2658
2659 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2660
2661 data = lyd_new(NULL, notifs, "create-subscription");
2662 if (rpc_sub->stream) {
2663 node = lyd_new_leaf(data, notifs, "stream", rpc_sub->stream);
2664 if (!node) {
2665 lyd_free(data);
2666 return NC_MSG_ERROR;
2667 }
2668 }
2669
2670 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002671 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002672 node = lyd_new_anydata(data, notifs, "filter", rpc_sub->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01002673 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01002674 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02002675 node = lyd_new_anydata(data, notifs, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01002676 lyd_insert_attr(node, NULL, "type", "xpath");
2677 lyd_insert_attr(node, NULL, "select", rpc_sub->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01002678 }
2679 if (!node) {
2680 lyd_free(data);
2681 return NC_MSG_ERROR;
2682 }
2683 }
2684
2685 if (rpc_sub->start) {
2686 node = lyd_new_leaf(data, notifs, "startTime", rpc_sub->start);
2687 if (!node) {
2688 lyd_free(data);
2689 return NC_MSG_ERROR;
2690 }
2691 }
2692
2693 if (rpc_sub->stop) {
2694 node = lyd_new_leaf(data, notifs, "stopTime", rpc_sub->stop);
2695 if (!node) {
2696 lyd_free(data);
2697 return NC_MSG_ERROR;
2698 }
2699 }
2700 break;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002701 default:
2702 ERRINT;
2703 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002704 }
2705
Michal Vasko131120a2018-05-29 15:44:02 +02002706 if (!data) {
2707 /* error was already printed */
2708 return NC_MSG_ERROR;
2709 }
2710
Michal Vasko41adf392017-01-17 10:39:04 +01002711 if (lyd_validate(&data, LYD_OPT_RPC | LYD_OPT_NOEXTDEPS
2712 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL)) {
Radek Krejcib4b19062018-02-07 16:33:06 +01002713 if (dofree) {
2714 lyd_free(data);
2715 }
Michal Vasko086311b2016-01-08 09:53:11 +01002716 return NC_MSG_ERROR;
2717 }
2718
Michal Vasko131120a2018-05-29 15:44:02 +02002719 /* send RPC, store its message ID */
2720 r = nc_send_msg_io(session, timeout, data);
2721 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002722
Radek Krejcib4b19062018-02-07 16:33:06 +01002723 if (dofree) {
2724 lyd_free(data);
2725 }
Michal Vasko086311b2016-01-08 09:53:11 +01002726
Michal Vasko131120a2018-05-29 15:44:02 +02002727 if (r == NC_MSG_RPC) {
2728 *msgid = cur_msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002729 }
Michal Vasko131120a2018-05-29 15:44:02 +02002730 return r;
Michal Vasko086311b2016-01-08 09:53:11 +01002731}
Michal Vaskode2946c2017-01-12 12:19:26 +01002732
2733API void
2734nc_client_session_set_not_strict(struct nc_session *session)
2735{
2736 if (session->side != NC_CLIENT) {
2737 ERRARG("session");
2738 return;
2739 }
2740
2741 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
2742}