blob: beb7edbc473f3ece75f1e03c9830a52aca62a450 [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 Vasko086311b2016-01-08 09:53:11 +010021#include <pthread.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/socket.h>
25#include <sys/stat.h>
Radek Krejci62aa0642017-05-25 16:33:49 +020026#include <sys/syscall.h>
Michal Vasko086311b2016-01-08 09:53:11 +010027#include <sys/types.h>
28#include <unistd.h>
29#include <arpa/inet.h>
30#include <poll.h>
31
32#include <libyang/libyang.h>
33
Michal Vasko086311b2016-01-08 09:53:11 +010034#include "libnetconf.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010035#include "session_client.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010036#include "messages_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010037
Michal Vasko80ef5d22016-01-18 09:21:02 +010038static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
39
Radek Krejci62aa0642017-05-25 16:33:49 +020040#ifdef NC_ENABLED_SSH
41int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
42char *sshauth_password(const char *username, const char *hostname, void *priv);
43char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
44char *sshauth_privkey_passphrase(const char* privkey_path, void *priv);
45#endif /* NC_ENABLED_SSH */
46
47static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT;
48static pthread_key_t nc_client_context_key;
49#ifdef __linux__
50static struct nc_client_context context_main = {
51 /* .opts zeroed */
52#ifdef NC_ENABLED_SSH
53 .ssh_opts = {
54 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
55 .auth_hostkey_check = sshauth_hostkey_check,
56 .auth_password = sshauth_password,
57 .auth_interactive = sshauth_interactive,
58 .auth_privkey_passphrase = sshauth_privkey_passphrase
59 },
60 .ssh_ch_opts = {
61 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
62 .auth_hostkey_check = sshauth_hostkey_check,
63 .auth_password = sshauth_password,
64 .auth_interactive = sshauth_interactive,
65 .auth_privkey_passphrase = sshauth_privkey_passphrase
Radek Krejci5cebc6b2017-05-26 13:24:38 +020066 },
Radek Krejci62aa0642017-05-25 16:33:49 +020067#endif /* NC_ENABLED_SSH */
68 /* .tls_ structures zeroed */
Radek Krejci5cebc6b2017-05-26 13:24:38 +020069 .refcount = 0
Radek Krejci62aa0642017-05-25 16:33:49 +020070};
71#endif
72
73static void
74nc_client_context_free(void *ptr)
75{
76 struct nc_client_context *c = (struct nc_client_context *)ptr;
77
Radek Krejci5cebc6b2017-05-26 13:24:38 +020078 if (--(c->refcount)) {
79 /* still used */
80 return;
81 }
82
Radek Krejci62aa0642017-05-25 16:33:49 +020083#ifdef __linux__
84 /* in __linux__ we use static memory in the main thread,
85 * so this check is for programs terminating the main()
86 * function by pthread_exit() :)
87 */
88 if (c != &context_main)
89#endif
90 {
Radek Krejci5cebc6b2017-05-26 13:24:38 +020091 /* for the main thread the same is done in nc_client_destroy() */
Radek Krejci62aa0642017-05-25 16:33:49 +020092 nc_client_set_schema_searchpath(NULL);
93#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
94 nc_client_ch_del_bind(NULL, 0, 0);
95#endif
96#ifdef NC_ENABLED_SSH
97 nc_client_ssh_destroy_opts();
98#endif
99#ifdef NC_ENABLED_TLS
100 nc_client_tls_destroy_opts();
101#endif
102 free(c);
103 }
104}
105
106static void
107nc_client_context_createkey(void)
108{
109 int r;
110
111 /* initiate */
112 while ((r = pthread_key_create(&nc_client_context_key, nc_client_context_free)) == EAGAIN);
113 pthread_setspecific(nc_client_context_key, NULL);
114}
115
116struct nc_client_context *
117nc_client_context_location(void)
118{
119 struct nc_client_context *e;
120
121 pthread_once(&nc_client_context_once, nc_client_context_createkey);
122 e = pthread_getspecific(nc_client_context_key);
123 if (!e) {
124 /* prepare ly_err storage */
125#ifdef __linux__
126 if (getpid() == syscall(SYS_gettid)) {
127 /* main thread - use global variable instead of thread-specific variable. */
128 e = &context_main;
129 } else
130#endif /* __linux__ */
131 {
132 e = calloc(1, sizeof *e);
133 /* set default values */
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200134 e->refcount = 1;
Radek Krejci62aa0642017-05-25 16:33:49 +0200135#ifdef NC_ENABLED_SSH
136 e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE;
137 e->ssh_opts.auth_pref[0].value = 3;
138 e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD;
139 e->ssh_opts.auth_pref[1].value = 2;
140 e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY;
141 e->ssh_opts.auth_pref[2].value = 1;
142 e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check;
143 e->ssh_opts.auth_password = sshauth_password;
144 e->ssh_opts.auth_interactive = sshauth_interactive;
145 e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase;
146
147 /* callhome settings are the same except the inverted auth methods preferences */
148 memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts);
149 e->ssh_ch_opts.auth_pref[0].value = 1;
150 e->ssh_ch_opts.auth_pref[1].value = 2;
151 e->ssh_ch_opts.auth_pref[2].value = 3;
152#endif /* NC_ENABLED_SSH */
153 }
154 pthread_setspecific(nc_client_context_key, e);
155 }
156
157 return e;
158}
159
160#define client_opts nc_client_context_location()->opts
Michal Vasko086311b2016-01-08 09:53:11 +0100161
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200162API void *
163nc_client_get_thread_context(void)
164{
165 return nc_client_context_location();
166}
167
168API void
169nc_client_set_thread_context(void *context)
170{
171 struct nc_client_context *old, *new;
172
173 if (!context) {
174 ERRARG(context);
175 return;
176 }
177
178 new = (struct nc_client_context *)context;
179 old = nc_client_context_location();
180 if (old == new) {
181 /* nothing to change */
182 return;
183 }
184
185 /* replace old by new, increase reference counter in the newly set context */
186 nc_client_context_free(old);
187 new->refcount++;
188 pthread_setspecific(nc_client_context_key, new);
189}
190
Radek Krejcifd5b6682017-06-13 15:52:53 +0200191int
192nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx)
193{
194 /* assign context (dicionary needed for handshake) */
195 if (!ctx) {
Radek Krejci3222b7d2017-09-21 16:04:30 +0200196 ctx = ly_ctx_new(NULL, 0);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200197 if (!ctx) {
198 return EXIT_FAILURE;
199 }
200
201 /* user path must be first, the first path is used to store schemas retreived via get-schema */
202 if (client_opts.schema_searchpath) {
203 ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath);
204 }
Michal Vaskoff7e3562018-02-15 13:41:22 +0100205 ly_ctx_set_searchdir(ctx, NC_SCHEMAS_DIR);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200206
207 /* set callback for getting schemas, if provided */
208 ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data);
209 } else {
210 session->flags |= NC_SESSION_SHAREDCTX;
211 }
212
213 session->ctx = ctx;
214
215 return EXIT_SUCCESS;
216}
217
Michal Vasko086311b2016-01-08 09:53:11 +0100218API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100219nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +0100220{
Michal Vasko3031aae2016-01-27 16:07:18 +0100221 if (client_opts.schema_searchpath) {
222 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +0100223 }
Michal Vasko086311b2016-01-08 09:53:11 +0100224
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100225 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100226 client_opts.schema_searchpath = strdup(path);
227 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100228 ERRMEM;
229 return 1;
230 }
231 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100232 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100233 }
234
235 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100236}
237
Michal Vasko7f1c0ef2016-03-11 11:13:06 +0100238API const char *
239nc_client_get_schema_searchpath(void)
240{
241 return client_opts.schema_searchpath;
242}
243
Radek Krejcifd5b6682017-06-13 15:52:53 +0200244API int
245nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data)
Michal Vasko086311b2016-01-08 09:53:11 +0100246{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200247 client_opts.schema_clb = clb;
248 if (clb) {
249 client_opts.schema_clb_data = user_data;
250 } else {
251 client_opts.schema_clb_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100252 }
253
254 return 0;
255}
256
Radek Krejcifd5b6682017-06-13 15:52:53 +0200257API ly_module_imp_clb
258nc_client_get_schema_callback(void **user_data)
259{
260 if (user_data) {
261 (*user_data) = client_opts.schema_clb_data;
262 }
263 return client_opts.schema_clb;
264}
265
Michal Vaskoff7e3562018-02-15 13:41:22 +0100266/* NC_SCHEMAS_DIR used as the last resort */
Michal Vasko086311b2016-01-08 09:53:11 +0100267static int
Michal Vasko1b2ddc92017-05-24 08:59:49 +0200268ctx_check_and_load_ietf_netconf(struct ly_ctx *ctx, char **cpblts)
Michal Vasko086311b2016-01-08 09:53:11 +0100269{
270 int i;
271 const struct lys_module *ietfnc;
272
Radek Krejci3222b7d2017-09-21 16:04:30 +0200273 ietfnc = ly_ctx_get_module(ctx, "ietf-netconf", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +0100274 if (!ietfnc) {
Michal Vasko1aaa6602016-02-09 11:04:33 +0100275 ietfnc = ly_ctx_load_module(ctx, "ietf-netconf", NULL);
276 if (!ietfnc) {
Michal Vaskoff7e3562018-02-15 13:41:22 +0100277 ietfnc = lys_parse_path(ctx, NC_SCHEMAS_DIR"/ietf-netconf.yin", LYS_IN_YIN);
Michal Vasko086311b2016-01-08 09:53:11 +0100278 }
279 }
280 if (!ietfnc) {
281 ERR("Loading base NETCONF schema failed.");
282 return 1;
283 }
284
285 /* set supported capabilities from ietf-netconf */
286 for (i = 0; cpblts[i]; ++i) {
287 if (!strncmp(cpblts[i], "urn:ietf:params:netconf:capability:", 35)) {
288 if (!strncmp(cpblts[i] + 35, "writable-running", 16)) {
289 lys_features_enable(ietfnc, "writable-running");
290 } else if (!strncmp(cpblts[i] + 35, "candidate", 9)) {
291 lys_features_enable(ietfnc, "candidate");
292 } else if (!strcmp(cpblts[i] + 35, "confirmed-commit:1.1")) {
293 lys_features_enable(ietfnc, "confirmed-commit");
294 } else if (!strncmp(cpblts[i] + 35, "rollback-on-error", 17)) {
295 lys_features_enable(ietfnc, "rollback-on-error");
296 } else if (!strcmp(cpblts[i] + 35, "validate:1.1")) {
297 lys_features_enable(ietfnc, "validate");
298 } else if (!strncmp(cpblts[i] + 35, "startup", 7)) {
299 lys_features_enable(ietfnc, "startup");
300 } else if (!strncmp(cpblts[i] + 35, "url", 3)) {
301 lys_features_enable(ietfnc, "url");
302 } else if (!strncmp(cpblts[i] + 35, "xpath", 5)) {
303 lys_features_enable(ietfnc, "xpath");
304 }
305 }
306 }
307
308 return 0;
309}
310
311static char *
Radek Krejcifd5b6682017-06-13 15:52:53 +0200312getschema_module_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *submod_rev,
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200313 void *user_data, LYS_INFORMAT *format, void (**free_model_data)(void *model_data))
Michal Vasko086311b2016-01-08 09:53:11 +0100314{
315 struct nc_session *session = (struct nc_session *)user_data;
316 struct nc_rpc *rpc;
317 struct nc_reply *reply;
318 struct nc_reply_data *data_rpl;
Michal Vasko998ba412016-09-16 12:00:07 +0200319 struct nc_reply_error *error_rpl;
Radek Krejci539efb62016-08-24 15:05:16 +0200320 struct lyd_node_anydata *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100321 NC_MSG_TYPE msg;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200322 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100323 uint64_t msgid;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200324 char *filename = NULL;
325 const char * const *searchdirs;
326 FILE *output;
Michal Vasko086311b2016-01-08 09:53:11 +0100327
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200328 if (submod_name) {
Michal Vasko3e7eaf42016-09-20 10:51:23 +0200329 rpc = nc_rpc_getschema(submod_name, submod_rev, "yang", NC_PARAMTYPE_CONST);
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200330 } else {
Michal Vasko3e7eaf42016-09-20 10:51:23 +0200331 rpc = nc_rpc_getschema(mod_name, mod_rev, "yang", NC_PARAMTYPE_CONST);
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200332 }
Michal Vasko086311b2016-01-08 09:53:11 +0100333
334 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
335 usleep(1000);
336 }
337 if (msg == NC_MSG_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100338 ERR("Session %u: failed to send the <get-schema> RPC.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100339 nc_rpc_free(rpc);
340 return NULL;
341 }
342
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200343 do {
Michal Vaskof471fa02017-02-15 10:48:12 +0100344 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, 0, &reply);
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200345 } while (msg == NC_MSG_NOTIF);
Michal Vasko086311b2016-01-08 09:53:11 +0100346 nc_rpc_free(rpc);
347 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100348 ERR("Session %u: timeout for receiving reply to a <get-schema> expired.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100349 return NULL;
350 } else if (msg == NC_MSG_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100351 ERR("Session %u: failed to receive a reply to <get-schema>.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100352 return NULL;
353 }
354
Michal Vasko998ba412016-09-16 12:00:07 +0200355 switch (reply->type) {
356 case NC_RPL_OK:
357 ERR("Session %u: unexpected reply OK to a <get-schema> RPC.", session->id);
358 nc_reply_free(reply);
359 return NULL;
360 case NC_RPL_DATA:
361 /* fine */
362 break;
363 case NC_RPL_ERROR:
364 error_rpl = (struct nc_reply_error *)reply;
365 if (error_rpl->count) {
366 ERR("Session %u: error reply to a <get-schema> RPC (tag \"%s\", message \"%s\").",
367 session->id, error_rpl->err[0].tag, error_rpl->err[0].message);
368 } else {
369 ERR("Session %u: unexpected reply error to a <get-schema> RPC.", session->id);
370 }
371 nc_reply_free(reply);
372 return NULL;
373 case NC_RPL_NOTIF:
374 ERR("Session %u: unexpected reply notification to a <get-schema> RPC.", session->id);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100375 nc_reply_free(reply);
376 return NULL;
377 }
378
Michal Vasko086311b2016-01-08 09:53:11 +0100379 data_rpl = (struct nc_reply_data *)reply;
Michal Vaskob2583f12016-05-12 11:40:23 +0200380 if ((data_rpl->data->schema->nodetype != LYS_RPC) || strcmp(data_rpl->data->schema->name, "get-schema")
381 || !data_rpl->data->child || (data_rpl->data->child->schema->nodetype != LYS_ANYXML)) {
382 ERR("Session %u: unexpected data in reply to a <get-schema> RPC.", session->id);
383 nc_reply_free(reply);
384 return NULL;
385 }
Radek Krejci539efb62016-08-24 15:05:16 +0200386 get_schema_data = (struct lyd_node_anydata *)data_rpl->data->child;
387 switch (get_schema_data->value_type) {
388 case LYD_ANYDATA_CONSTSTRING:
389 case LYD_ANYDATA_STRING:
Michal Vaskob2583f12016-05-12 11:40:23 +0200390 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200391 break;
392 case LYD_ANYDATA_DATATREE:
393 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYP_WITHSIBLINGS);
394 break;
395 case LYD_ANYDATA_XML:
396 lyxml_print_mem(&model_data, get_schema_data->value.xml, LYXML_PRINT_SIBLINGS);
397 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200398 case LYD_ANYDATA_JSON:
399 case LYD_ANYDATA_JSOND:
Michal Vasko94868ed2016-09-29 10:33:38 +0200400 case LYD_ANYDATA_SXML:
401 case LYD_ANYDATA_SXMLD:
Radek Krejci03438ec2016-09-21 14:12:26 +0200402 ERRINT;
403 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200404 }
Michal Vasko086311b2016-01-08 09:53:11 +0100405 nc_reply_free(reply);
Michal Vaskoca4ad152016-03-03 15:50:45 +0100406 *free_model_data = free;
Michal Vasko3e7eaf42016-09-20 10:51:23 +0200407 *format = LYS_IN_YANG;
Michal Vasko086311b2016-01-08 09:53:11 +0100408
Radek Krejcifd5b6682017-06-13 15:52:53 +0200409 /* try to store the model_data into local schema repository */
410 if (model_data) {
411 searchdirs = ly_ctx_get_searchdirs(session->ctx);
Michal Vaskof945da52018-02-15 08:45:13 +0100412 if (asprintf(&filename, "%s/%s%s%s.yang", searchdirs ? searchdirs[0] : ".", mod_name,
413 mod_rev ? "@" : "", mod_rev ? mod_rev : "") == -1) {
414 ERRMEM;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200415 } else {
Michal Vaskof945da52018-02-15 08:45:13 +0100416 output = fopen(filename, "w");
417 if (!output) {
418 WRN("Unable to store \"%s\" as a local copy of schema retreived via <get-schema> (%s).",
419 filename, strerror(errno));
420 } else {
421 fputs(model_data, output);
422 fclose(output);
423 }
424 free(filename);
Radek Krejcifd5b6682017-06-13 15:52:53 +0200425 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200426 }
427
Michal Vasko086311b2016-01-08 09:53:11 +0100428 return model_data;
429}
430
Michal Vaskoceae0152018-02-14 16:03:59 +0100431static int
432nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, int implement,
433 ly_module_imp_clb user_clb, void *user_data, const struct lys_module **mod)
434{
435 int ret = 0;
436 struct ly_err_item *eitem;
437
438 *mod = ly_ctx_get_module(session->ctx, name, revision, 0);
439 if (*mod) {
440 if (implement && !(*mod)->implemented) {
441 /* make the present module implemented */
442 if (lys_set_implemented(*mod)) {
443 ERR("Failed to implement model \"%s\".", (*mod)->name);
444 ret = -1;
445 }
446 }
447 } else if (!(*mod) && implement) {
448 /* missing implemented module, load it ... */
449
450 /* clear all the errors and just collect them for now */
451 ly_err_clean(session->ctx, NULL);
452 ly_log_options(LY_LOSTORE);
453
454 /* 1) using only searchpaths */
455 *mod = ly_ctx_load_module(session->ctx, name, revision);
456
457 /* 2) using user callback */
458 if (!(*mod) && user_clb) {
459 ly_ctx_set_module_imp_clb(session->ctx, user_clb, user_data);
460 *mod = ly_ctx_load_module(session->ctx, name, revision);
461 }
462
463 /* 3) using get-schema callback */
464 if (!(*mod)) {
465 ly_ctx_set_module_imp_clb(session->ctx, &getschema_module_clb, session);
466 *mod = ly_ctx_load_module(session->ctx, name, revision);
467 }
468
469 /* unset callback back to use searchpath */
470 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL);
471
472 /* print errors on definite failure */
473 if (!(*mod)) {
474 for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) {
475 ly_err_print(eitem);
476 }
477 ret = -1;
478 }
479
480 /* clean the errors and restore logging */
481 ly_err_clean(session->ctx, NULL);
482 ly_log_options(LY_LOLOG | LY_LOSTORE_LAST);
483 }
484
485 return ret;
486}
487
Michal Vaskoff7e3562018-02-15 13:41:22 +0100488/* NC_SCHEMAS_DIR not used (implicitly) */
Radek Krejcifd5b6682017-06-13 15:52:53 +0200489static int
490nc_ctx_fill_cpblts(struct nc_session *session, ly_module_imp_clb user_clb, void *user_data)
491{
492 int ret = 1;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200493 const struct lys_module *mod;
494 char *ptr, *ptr2;
495 const char *module_cpblt;
496 char *name = NULL, *revision = NULL, *features = NULL;
497 unsigned int u;
498
499 for (u = 0; session->opts.client.cpblts[u]; ++u) {
500 module_cpblt = strstr(session->opts.client.cpblts[u], "module=");
501 /* this capability requires a module */
502 if (!module_cpblt) {
503 continue;
504 }
505
506 /* get module name */
507 ptr = (char *)module_cpblt + 7;
508 ptr2 = strchr(ptr, '&');
509 if (!ptr2) {
510 ptr2 = ptr + strlen(ptr);
511 }
512 free(name);
513 name = strndup(ptr, ptr2 - ptr);
514
515 /* get module revision */
516 free(revision); revision = NULL;
517 ptr = strstr(module_cpblt, "revision=");
518 if (ptr) {
519 ptr += 9;
520 ptr2 = strchr(ptr, '&');
521 if (!ptr2) {
522 ptr2 = ptr + strlen(ptr);
523 }
524 revision = strndup(ptr, ptr2 - ptr);
525 }
526
Michal Vaskoceae0152018-02-14 16:03:59 +0100527 if (nc_ctx_load_module(session, name, revision, 1, user_clb, user_data, &mod)) {
528 ret = 1;
529 goto cleanup;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200530 }
531
532 if (!mod) {
Michal Vasko60f66602017-10-17 13:52:18 +0200533 if (session->status != NC_STATUS_RUNNING) {
534 /* something bad heppened, discard the session */
535 ERR("Session %d: invalid session, discarding.", nc_session_get_id(session));
536 ret = 1;
537 goto cleanup;
538 }
539
Radek Krejcifd5b6682017-06-13 15:52:53 +0200540 /* all loading ways failed, the schema will be ignored in the received data */
541 WRN("Failed to load schema \"%s@%s\".", name, revision ? revision : "<latest>");
542 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200543 } else {
544 /* set features - first disable all to enable specified then */
545 lys_features_disable(mod, "*");
546
547 ptr = strstr(module_cpblt, "features=");
548 if (ptr) {
549 ptr += 9;
550 ptr2 = strchr(ptr, '&');
551 if (!ptr2) {
552 ptr2 = ptr + strlen(ptr);
553 }
554 free(features);
555 features = strndup(ptr, ptr2 - ptr);
556
557 /* basically manual strtok_r (to avoid macro) */
558 ptr2 = features;
559 for (ptr = features; *ptr; ++ptr) {
560 if (*ptr == ',') {
561 *ptr = '\0';
562 /* remember last feature */
563 ptr2 = ptr + 1;
564 }
565 }
566
567 ptr = features;
568 while (1) {
569 lys_features_enable(mod, ptr);
570 if (ptr != ptr2) {
571 ptr += strlen(ptr) + 1;
572 } else {
573 break;
574 }
575 }
576 }
577 }
578 }
579
580 ret = 0;
581
582cleanup:
583 free(name);
584 free(revision);
585 free(features);
586
587 return ret;
588}
589
590static int
591nc_ctx_fill_yl(struct nc_session *session, ly_module_imp_clb user_clb, void *user_data)
592{
593 int ret = 1;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200594 struct nc_rpc *rpc = NULL;
595 struct nc_reply *reply = NULL;
596 struct nc_reply_error *error_rpl;
597 struct nc_reply_data *data_rpl;
598 NC_MSG_TYPE msg;
599 uint64_t msgid;
600 struct lyd_node *data = NULL, *iter;
601 struct ly_set *modules = NULL, *imports = NULL, *features = NULL;
602 unsigned int u, v;
603 const char *name, *revision;
604 int implemented, imports_flag = 0;
605 const struct lys_module *mod;
606
607 /* get yang-library data from the server */
608 rpc = nc_rpc_get("/ietf-yang-library:*//.", 0, NC_PARAMTYPE_CONST);
609 if (!rpc) {
610 goto cleanup;
611 }
612
613 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
614 usleep(1000);
615 }
616 if (msg == NC_MSG_ERROR) {
Michal Vasko60f66602017-10-17 13:52:18 +0200617 ERR("Session %u: failed to send request for yang-library data.",
Radek Krejcifd5b6682017-06-13 15:52:53 +0200618 session->id);
619 goto cleanup;
620 }
621
622 do {
623 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, 0, &reply);
624 } while (msg == NC_MSG_NOTIF);
625 if (msg == NC_MSG_WOULDBLOCK) {
626 ERR("Session %u: timeout for receiving reply to a <get> yang-library data expired.", session->id);
627 goto cleanup;
628 } else if (msg == NC_MSG_ERROR) {
629 ERR("Session %u: failed to receive a reply to <get> of yang-library data.", session->id);
630 goto cleanup;
631 }
632
633 switch (reply->type) {
634 case NC_RPL_OK:
635 ERR("Session %u: unexpected reply OK to a yang-library <get> RPC.", session->id);
636 goto cleanup;
637 case NC_RPL_DATA:
638 /* fine */
639 break;
640 case NC_RPL_ERROR:
641 error_rpl = (struct nc_reply_error *)reply;
642 if (error_rpl->count) {
643 ERR("Session %u: error reply to a yang-library <get> RPC (tag \"%s\", message \"%s\").",
644 session->id, error_rpl->err[0].tag, error_rpl->err[0].message);
645 } else {
646 ERR("Session %u: unexpected reply error to a yang-library <get> RPC.", session->id);
647 }
648 goto cleanup;
649 case NC_RPL_NOTIF:
650 ERR("Session %u: unexpected reply notification to a yang-library <get> RPC.", session->id);
651 goto cleanup;
652 }
653
654 data_rpl = (struct nc_reply_data *)reply;
Michal Vasko09319322017-10-02 09:26:54 +0200655 if (!data_rpl->data || strcmp(data_rpl->data->schema->module->name, "ietf-yang-library")) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200656 ERR("Session %u: unexpected data in reply to a yang-library <get> RPC.", session->id);
657 goto cleanup;
658 }
659
Michal Vaskof4c8b792017-07-28 14:57:22 +0200660 modules = lyd_find_path(data_rpl->data, "/ietf-yang-library:modules-state/module");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200661 if (!modules || !modules->number) {
662 ERR("No yang-library modules information for session %u.", session->id);
663 goto cleanup;
664 }
665
666 features = ly_set_new();
667 imports = ly_set_new();
668
669parse:
670 for (u = modules->number - 1; u < modules->number; u--) {
671 name = revision = NULL;
672 ly_set_clean(features);
673 implemented = 0;
674
675 /* store the data */
676 LY_TREE_FOR(modules->set.d[u]->child, iter) {
677 if (!((struct lyd_node_leaf_list *)iter)->value_str || !((struct lyd_node_leaf_list *)iter)->value_str[0]) {
678 /* ignore empty nodes */
679 continue;
680 }
681 if (!strcmp(iter->schema->name, "name")) {
682 name = ((struct lyd_node_leaf_list *)iter)->value_str;
683 } else if (!strcmp(iter->schema->name, "revision")) {
684 revision = ((struct lyd_node_leaf_list *)iter)->value_str;
685 } else if (!strcmp(iter->schema->name, "conformance-type")) {
686 implemented = !strcmp(((struct lyd_node_leaf_list *)iter)->value_str, "implement");
687 } else if (!strcmp(iter->schema->name, "feature")) {
688 ly_set_add(features, (void*)((struct lyd_node_leaf_list *)iter)->value_str, LY_SET_OPT_USEASLIST);
689 }
690 }
691
Michal Vaskoceae0152018-02-14 16:03:59 +0100692 if (nc_ctx_load_module(session, name, revision, implemented, user_clb, user_data, &mod)) {
693 ret = -1;
694 goto cleanup;
695 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200696
Michal Vaskoceae0152018-02-14 16:03:59 +0100697 if (!mod) { /* !mod && !implemented - will be loaded automatically, but remember to set features in the end */
698 assert(!implemented);
Michal Vasko3c556372017-08-09 10:17:34 +0200699 if (imports_flag) {
700 ERR("Module \"%s@%s\" is supposed to be imported, but no other module imports it.",
701 name, revision ? revision : "<latest>");
702 ret = -1;
703 goto cleanup;
704 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200705 ly_set_add(imports, modules->set.d[u], LY_SET_OPT_USEASLIST);
706 continue;
707 }
708
709 if (!mod) {
710 /* all loading ways failed, the schema will be ignored in the received data */
711 WRN("Failed to load schema \"%s@%s\".", name, revision ? revision : "<latest>");
712 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200713 } else {
714 /* set features - first disable all to enable specified then */
715 lys_features_disable(mod, "*");
716 for (v = 0; v < features->number; v++) {
717 lys_features_enable(mod, (const char*)features->set.g[v]);
718 }
719 }
720 }
721
722 if (!imports_flag && imports->number) {
723 /* even imported modules should be now loaded as dependency, so just go through
724 * the parsing again and just set the features */
725 ly_set_free(modules);
726 modules = imports;
727 imports = NULL;
728 imports_flag = 1;
729 goto parse;
730 }
731
732 /* done */
733 ret = 0;
734
735cleanup:
736 nc_rpc_free(rpc);
737 nc_reply_free(reply);
738 lyd_free_withsiblings(data);
739
740 ly_set_free(modules);
741 ly_set_free(imports);
742 ly_set_free(features);
743
Michal Vasko60f66602017-10-17 13:52:18 +0200744 if (session->status != NC_STATUS_RUNNING) {
745 ERR("Session %d: invalid session, discarding.", nc_session_get_id(session));
746 ret = -1;
747 }
748
Radek Krejcifd5b6682017-06-13 15:52:53 +0200749 return ret;
750}
751
Michal Vasko086311b2016-01-08 09:53:11 +0100752int
753nc_ctx_check_and_fill(struct nc_session *session)
754{
Radek Krejcifd5b6682017-06-13 15:52:53 +0200755 int i, get_schema_support = 0, yanglib_support = 0, ret = -1, r;
Michal Vaskocdeee432017-01-13 13:51:01 +0100756 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100757 void *old_data = NULL;
758
Michal Vasko2e6defd2016-10-07 15:48:15 +0200759 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +0100760
Radek Krejcifd5b6682017-06-13 15:52:53 +0200761 /* store the original user's callback, here we will be switching between searchpath, user callback
762 * and get-schema callback */
763 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
764 ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL); /* unset callback, so we prefer local searchpath */
765
Michal Vasko086311b2016-01-08 09:53:11 +0100766 /* check if get-schema is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200767 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200768 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100769 get_schema_support = 1;
Radek Krejcifd5b6682017-06-13 15:52:53 +0200770 if (yanglib_support) {
771 break;
772 }
773 } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-yang-library?", 46)) {
774 yanglib_support = 1;
775 if (get_schema_support) {
776 break;
777 }
Michal Vasko086311b2016-01-08 09:53:11 +0100778 }
779 }
780
781 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200782 if (get_schema_support && !ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL, 1)) {
Michal Vaskoff7e3562018-02-15 13:41:22 +0100783 if (!lys_parse_path(session->ctx, NC_SCHEMAS_DIR"/ietf-netconf-monitoring.yin", LYS_IN_YIN)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100784 WRN("Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
Radek Krejcifd5b6682017-06-13 15:52:53 +0200785 get_schema_support = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100786 }
787 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200788 /* yang-library present does not need to be checked, it is one of the libyang's internal modules,
789 * so it is always present */
Michal Vasko086311b2016-01-08 09:53:11 +0100790
791 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200792 if (ctx_check_and_load_ietf_netconf(session->ctx, session->opts.client.cpblts)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200793 goto cleanup;
Michal Vasko086311b2016-01-08 09:53:11 +0100794 }
795
Radek Krejcifd5b6682017-06-13 15:52:53 +0200796 if (yanglib_support && get_schema_support) {
797 /* load schemas according to the ietf-yang-library data, which are more precise than capabilities list */
798 r = nc_ctx_fill_yl(session, old_clb, old_data);
799 if (r == -1) {
800 goto cleanup;
801 } else if (r == 1) {
Michal Vasko60f66602017-10-17 13:52:18 +0200802 VRB("Session %d: trying to use capabilities instead of ietf-yang-library data.", nc_session_get_id(session));
Radek Krejcifd5b6682017-06-13 15:52:53 +0200803 /* try to use standard capabilities */
804 goto capabilities;
805 }
806 } else {
807capabilities:
Michal Vaskoef578332016-01-25 13:20:09 +0100808
Michal Vasko60f66602017-10-17 13:52:18 +0200809 if (nc_ctx_fill_cpblts(session, old_clb, old_data)) {
Radek Krejcifd5b6682017-06-13 15:52:53 +0200810 goto cleanup;
Michal Vaskoef578332016-01-25 13:20:09 +0100811 }
Michal Vasko086311b2016-01-08 09:53:11 +0100812 }
813
Radek Krejcifd5b6682017-06-13 15:52:53 +0200814 /* succsess */
815 ret = 0;
816
Michal Vaskoeee99412016-11-21 10:19:43 +0100817 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
818 WRN("Some models failed to be loaded, any data from these models (and any other unknown) will be ignored.");
Michal Vaskoef578332016-01-25 13:20:09 +0100819 }
Radek Krejcifd5b6682017-06-13 15:52:53 +0200820
821cleanup:
822 /* set user callback back */
823 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
824
Michal Vaskoef578332016-01-25 13:20:09 +0100825 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100826}
827
828API struct nc_session *
829nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
830{
Michal Vaskod083db62016-01-19 10:31:29 +0100831 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +0100832
Michal Vasko45e53ae2016-04-07 11:46:03 +0200833 if (fdin < 0) {
834 ERRARG("fdin");
835 return NULL;
836 } else if (fdout < 0) {
837 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +0100838 return NULL;
839 }
840
841 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100842 session = nc_new_session(0);
Michal Vasko086311b2016-01-08 09:53:11 +0100843 if (!session) {
844 ERRMEM;
845 return NULL;
846 }
847 session->status = NC_STATUS_STARTING;
848 session->side = NC_CLIENT;
849
850 /* transport specific data */
851 session->ti_type = NC_TI_FD;
Michal Vaskoade892d2017-02-22 13:40:35 +0100852 pthread_mutex_init(session->ti_lock, NULL);
853 pthread_cond_init(session->ti_cond, NULL);
854 *session->ti_inuse = 0;
855
Michal Vasko086311b2016-01-08 09:53:11 +0100856 session->ti.fd.in = fdin;
857 session->ti.fd.out = fdout;
858
859 /* assign context (dicionary needed for handshake) */
860 if (!ctx) {
Michal Vaskoff7e3562018-02-15 13:41:22 +0100861 ctx = ly_ctx_new(NC_SCHEMAS_DIR, 0);
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100862 /* definitely should not happen, but be ready */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200863 if (!ctx && !(ctx = ly_ctx_new(NULL, 0))) {
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100864 /* that's just it */
865 goto fail;
866 }
Michal Vasko086311b2016-01-08 09:53:11 +0100867 } else {
868 session->flags |= NC_SESSION_SHAREDCTX;
869 }
870 session->ctx = ctx;
871
872 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200873 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +0100874 goto fail;
875 }
876 session->status = NC_STATUS_RUNNING;
877
Michal Vaskoef578332016-01-25 13:20:09 +0100878 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +0100879 goto fail;
880 }
881
882 return session;
883
884fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100885 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100886 return NULL;
887}
888
889int
Michal Vaskof05562c2016-01-20 12:06:43 +0100890nc_sock_connect(const char* host, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100891{
Michal Vasko0190bc32016-03-02 15:47:49 +0100892 int i, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100893 struct addrinfo hints, *res_list, *res;
894 char port_s[6]; /* length of string representation of short int */
895
896 snprintf(port_s, 6, "%u", port);
897
898 /* Connect to a server */
899 memset(&hints, 0, sizeof hints);
900 hints.ai_family = AF_UNSPEC;
901 hints.ai_socktype = SOCK_STREAM;
902 hints.ai_protocol = IPPROTO_TCP;
903 i = getaddrinfo(host, port_s, &hints, &res_list);
904 if (i != 0) {
905 ERR("Unable to translate the host address (%s).", gai_strerror(i));
906 return -1;
907 }
908
Michal Vasko1f0b2ac2016-10-13 10:33:50 +0200909 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vasko086311b2016-01-08 09:53:11 +0100910 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
911 if (sock == -1) {
912 /* socket was not created, try another resource */
Michal Vasko1f0b2ac2016-10-13 10:33:50 +0200913 continue;
Michal Vasko086311b2016-01-08 09:53:11 +0100914 }
915
916 if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
917 /* network connection failed, try another resource */
Michal Vasko086311b2016-01-08 09:53:11 +0100918 close(sock);
919 sock = -1;
Michal Vasko1f0b2ac2016-10-13 10:33:50 +0200920 continue;
Michal Vasko086311b2016-01-08 09:53:11 +0100921 }
922
Michal Vasko0190bc32016-03-02 15:47:49 +0100923 /* make the socket non-blocking */
924 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
925 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100926 close(sock);
Michal Vaskoa8a66b62016-10-05 14:14:19 +0200927 freeaddrinfo(res_list);
Michal Vasko0190bc32016-03-02 15:47:49 +0100928 return -1;
929 }
930
Michal Vasko086311b2016-01-08 09:53:11 +0100931 /* we're done, network connection established */
932 break;
Michal Vasko086311b2016-01-08 09:53:11 +0100933 }
934
Michal Vasko29af44b2016-10-13 10:59:55 +0200935 if (sock != -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100936 VRB("Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
Michal Vasko086311b2016-01-08 09:53:11 +0100937 }
938 freeaddrinfo(res_list);
939
940 return sock;
941}
942
Michal Vasko086311b2016-01-08 09:53:11 +0100943static NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100944get_msg(struct nc_session *session, int timeout, uint64_t msgid, struct lyxml_elem **msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100945{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100946 int r;
Michal Vasko086311b2016-01-08 09:53:11 +0100947 char *ptr;
948 const char *str_msgid;
949 uint64_t cur_msgid;
950 struct lyxml_elem *xml;
Michal Vasko2518b6b2016-01-28 13:24:53 +0100951 struct nc_msg_cont *cont, **cont_ptr;
Michal Vasko086311b2016-01-08 09:53:11 +0100952 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
953
Michal Vaskoade892d2017-02-22 13:40:35 +0100954 r = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100955 if (r == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +0100956 /* error */
957 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100958 } else if (!r) {
Michal Vasko086311b2016-01-08 09:53:11 +0100959 /* timeout */
960 return NC_MSG_WOULDBLOCK;
961 }
962
963 /* try to get notification from the session's queue */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200964 if (!msgid && session->opts.client.notifs) {
965 cont = session->opts.client.notifs;
966 session->opts.client.notifs = cont->next;
Michal Vasko086311b2016-01-08 09:53:11 +0100967
Michal Vasko71ba2da2016-05-04 10:53:16 +0200968 xml = cont->msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100969 free(cont);
970
Michal Vasko71ba2da2016-05-04 10:53:16 +0200971 msgtype = NC_MSG_NOTIF;
Michal Vasko086311b2016-01-08 09:53:11 +0100972 }
973
974 /* try to get rpc-reply from the session's queue */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200975 if (msgid && session->opts.client.replies) {
976 cont = session->opts.client.replies;
977 session->opts.client.replies = cont->next;
Michal Vasko2518b6b2016-01-28 13:24:53 +0100978
Michal Vasko71ba2da2016-05-04 10:53:16 +0200979 xml = cont->msg;
980 free(cont);
Michal Vasko086311b2016-01-08 09:53:11 +0100981
Michal Vasko71ba2da2016-05-04 10:53:16 +0200982 msgtype = NC_MSG_REPLY;
Michal Vasko086311b2016-01-08 09:53:11 +0100983 }
984
Michal Vasko71ba2da2016-05-04 10:53:16 +0200985 if (!msgtype) {
986 /* read message from wire */
987 msgtype = nc_read_msg_poll(session, timeout, &xml);
988 }
Michal Vasko086311b2016-01-08 09:53:11 +0100989
990 /* we read rpc-reply, want a notif */
991 if (!msgid && (msgtype == NC_MSG_REPLY)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200992 cont_ptr = &session->opts.client.replies;
Michal Vasko086311b2016-01-08 09:53:11 +0100993 while (*cont_ptr) {
994 cont_ptr = &((*cont_ptr)->next);
995 }
996 *cont_ptr = malloc(sizeof **cont_ptr);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100997 if (!*cont_ptr) {
998 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +0100999 nc_session_unlock(session, timeout, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001000 lyxml_free(session->ctx, xml);
1001 return NC_MSG_ERROR;
1002 }
Michal Vasko086311b2016-01-08 09:53:11 +01001003 (*cont_ptr)->msg = xml;
1004 (*cont_ptr)->next = NULL;
1005 }
1006
1007 /* we read notif, want a rpc-reply */
1008 if (msgid && (msgtype == NC_MSG_NOTIF)) {
Michal Vasko3486a7c2017-03-03 13:28:07 +01001009 if (!session->opts.client.ntf_tid) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001010 pthread_mutex_unlock(session->ti_lock);
Michal Vaskod083db62016-01-19 10:31:29 +01001011 ERR("Session %u: received a <notification> but session is not subscribed.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001012 lyxml_free(session->ctx, xml);
Michal Vasko2518b6b2016-01-28 13:24:53 +01001013 return NC_MSG_ERROR;
Michal Vasko3486a7c2017-03-03 13:28:07 +01001014 }
Michal Vasko086311b2016-01-08 09:53:11 +01001015
Michal Vasko2e6defd2016-10-07 15:48:15 +02001016 cont_ptr = &session->opts.client.notifs;
Michal Vasko086311b2016-01-08 09:53:11 +01001017 while (*cont_ptr) {
1018 cont_ptr = &((*cont_ptr)->next);
1019 }
1020 *cont_ptr = malloc(sizeof **cont_ptr);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001021 if (!cont_ptr) {
1022 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001023 nc_session_unlock(session, timeout, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001024 lyxml_free(session->ctx, xml);
1025 return NC_MSG_ERROR;
1026 }
Michal Vasko086311b2016-01-08 09:53:11 +01001027 (*cont_ptr)->msg = xml;
1028 (*cont_ptr)->next = NULL;
1029 }
1030
Michal Vaskoade892d2017-02-22 13:40:35 +01001031 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +01001032
1033 switch (msgtype) {
1034 case NC_MSG_NOTIF:
Michal Vasko2518b6b2016-01-28 13:24:53 +01001035 if (!msgid) {
1036 *msg = xml;
Michal Vasko086311b2016-01-08 09:53:11 +01001037 }
Michal Vasko086311b2016-01-08 09:53:11 +01001038 break;
1039
1040 case NC_MSG_REPLY:
Michal Vasko2518b6b2016-01-28 13:24:53 +01001041 if (msgid) {
Michal Vasko71ba2da2016-05-04 10:53:16 +02001042 /* check message-id */
1043 str_msgid = lyxml_get_attr(xml, "message-id", NULL);
1044 if (!str_msgid) {
Michal Vasko14fdbb62018-01-18 09:13:20 +01001045 WRN("Session %u: received a <rpc-reply> without a message-id.", session->id);
Michal Vasko71ba2da2016-05-04 10:53:16 +02001046 } else {
1047 cur_msgid = strtoul(str_msgid, &ptr, 10);
1048 if (cur_msgid != msgid) {
1049 ERR("Session %u: received a <rpc-reply> with an unexpected message-id \"%s\".",
1050 session->id, str_msgid);
1051 msgtype = NC_MSG_REPLY_ERR_MSGID;
1052 }
1053 }
Michal Vasko2518b6b2016-01-28 13:24:53 +01001054 *msg = xml;
Michal Vasko086311b2016-01-08 09:53:11 +01001055 }
Michal Vasko086311b2016-01-08 09:53:11 +01001056 break;
1057
1058 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001059 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001060 lyxml_free(session->ctx, xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +02001061 msgtype = NC_MSG_ERROR;
1062 break;
Michal Vasko086311b2016-01-08 09:53:11 +01001063
1064 case NC_MSG_RPC:
Michal Vaskod083db62016-01-19 10:31:29 +01001065 ERR("Session %u: received <rpc> from a NETCONF server.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001066 lyxml_free(session->ctx, xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +02001067 msgtype = NC_MSG_ERROR;
1068 break;
Michal Vasko086311b2016-01-08 09:53:11 +01001069
1070 default:
1071 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
1072 * NC_MSG_NONE is not returned by nc_read_msg()
1073 */
1074 break;
1075 }
1076
1077 return msgtype;
1078}
1079
1080/* cannot strictly fail, but does not need to fill any error parameter at all */
1081static void
1082parse_rpc_error(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_err *err)
1083{
1084 struct lyxml_elem *iter, *next, *info;
1085
1086 LY_TREE_FOR(xml->child, iter) {
1087 if (!iter->ns) {
1088 if (iter->content) {
1089 WRN("<rpc-error> child \"%s\" with value \"%s\" without namespace.", iter->name, iter->content);
1090 } else {
1091 WRN("<rpc-error> child \"%s\" without namespace.", iter->name);
1092 }
1093 continue;
1094 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
1095 if (iter->content) {
1096 WRN("<rpc-error> child \"%s\" with value \"%s\" in an unknown namespace \"%s\".",
1097 iter->name, iter->content, iter->ns->value);
1098 } else {
1099 WRN("<rpc-error> child \"%s\" in an unknown namespace \"%s\".", iter->name, iter->ns->value);
1100 }
1101 continue;
1102 }
1103
1104 if (!strcmp(iter->name, "error-type")) {
1105 if (!iter->content || (strcmp(iter->content, "transport") && strcmp(iter->content, "rpc")
1106 && strcmp(iter->content, "protocol") && strcmp(iter->content, "application"))) {
1107 WRN("<rpc-error> <error-type> unknown value \"%s\".", (iter->content ? iter->content : ""));
1108 } else if (err->type) {
1109 WRN("<rpc-error> <error-type> duplicated.");
1110 } else {
1111 err->type = lydict_insert(ctx, iter->content, 0);
1112 }
1113 } else if (!strcmp(iter->name, "error-tag")) {
1114 if (!iter->content || (strcmp(iter->content, "in-use") && strcmp(iter->content, "invalid-value")
1115 && strcmp(iter->content, "too-big") && strcmp(iter->content, "missing-attribute")
1116 && strcmp(iter->content, "bad-attribute") && strcmp(iter->content, "unknown-attribute")
1117 && strcmp(iter->content, "missing-element") && strcmp(iter->content, "bad-element")
1118 && strcmp(iter->content, "unknown-element") && strcmp(iter->content, "unknown-namespace")
1119 && strcmp(iter->content, "access-denied") && strcmp(iter->content, "lock-denied")
1120 && strcmp(iter->content, "resource-denied") && strcmp(iter->content, "rollback-failed")
1121 && strcmp(iter->content, "data-exists") && strcmp(iter->content, "data-missing")
1122 && strcmp(iter->content, "operation-not-supported") && strcmp(iter->content, "operation-failed")
1123 && strcmp(iter->content, "malformed-message"))) {
1124 WRN("<rpc-error> <error-tag> unknown value \"%s\".", (iter->content ? iter->content : ""));
1125 } else if (err->tag) {
1126 WRN("<rpc-error> <error-tag> duplicated.");
1127 } else {
1128 err->tag = lydict_insert(ctx, iter->content, 0);
1129 }
1130 } else if (!strcmp(iter->name, "error-severity")) {
1131 if (!iter->content || (strcmp(iter->content, "error") && strcmp(iter->content, "warning"))) {
1132 WRN("<rpc-error> <error-severity> unknown value \"%s\".", (iter->content ? iter->content : ""));
1133 } else if (err->severity) {
1134 WRN("<rpc-error> <error-severity> duplicated.");
1135 } else {
1136 err->severity = lydict_insert(ctx, iter->content, 0);
1137 }
1138 } else if (!strcmp(iter->name, "error-app-tag")) {
1139 if (err->apptag) {
1140 WRN("<rpc-error> <error-app-tag> duplicated.");
1141 } else {
1142 err->apptag = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1143 }
1144 } else if (!strcmp(iter->name, "error-path")) {
1145 if (err->path) {
1146 WRN("<rpc-error> <error-path> duplicated.");
1147 } else {
1148 err->path = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1149 }
1150 } else if (!strcmp(iter->name, "error-message")) {
1151 if (err->message) {
1152 WRN("<rpc-error> <error-message> duplicated.");
1153 } else {
1154 err->message_lang = lyxml_get_attr(iter, "xml:lang", NULL);
Michal Vaskob0222c42018-01-03 15:17:12 +01001155 if (err->message_lang) {
1156 err->message_lang = lydict_insert(ctx, err->message_lang, 0);
1157 } else {
Michal Vasko086311b2016-01-08 09:53:11 +01001158 VRB("<rpc-error> <error-message> without the recommended \"xml:lang\" attribute.");
1159 }
1160 err->message = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1161 }
1162 } else if (!strcmp(iter->name, "error-info")) {
1163 LY_TREE_FOR_SAFE(iter->child, next, info) {
1164 if (info->ns && !strcmp(info->ns->value, NC_NS_BASE)) {
1165 if (!strcmp(info->name, "session-id")) {
1166 if (err->sid) {
1167 WRN("<rpc-error> <error-info> <session-id> duplicated.");
1168 } else {
1169 err->sid = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1170 }
Michal Vasko630485f2018-01-03 14:28:32 +01001171 } else if (!strcmp(info->name, "bad-attribute")) {
Michal Vasko086311b2016-01-08 09:53:11 +01001172 ++err->attr_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001173 err->attr = nc_realloc(err->attr, err->attr_count * sizeof *err->attr);
1174 if (!err->attr) {
1175 ERRMEM;
1176 return;
1177 }
Michal Vasko086311b2016-01-08 09:53:11 +01001178 err->attr[err->attr_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1179 } else if (!strcmp(info->name, "bad-element")) {
1180 ++err->elem_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001181 err->elem = nc_realloc(err->elem, err->elem_count * sizeof *err->elem);
1182 if (!err->elem) {
1183 ERRMEM;
1184 return;
1185 }
Michal Vasko086311b2016-01-08 09:53:11 +01001186 err->elem[err->elem_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1187 } else if (!strcmp(info->name, "bad-namespace")) {
1188 ++err->ns_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001189 err->ns = nc_realloc(err->ns, err->ns_count * sizeof *err->ns);
1190 if (!err->ns) {
1191 ERRMEM;
1192 return;
1193 }
Michal Vasko086311b2016-01-08 09:53:11 +01001194 err->ns[err->ns_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1195 } else {
1196 if (info->content) {
1197 WRN("<rpc-error> <error-info> unknown child \"%s\" with value \"%s\".",
1198 info->name, info->content);
1199 } else {
1200 WRN("<rpc-error> <error-info> unknown child \"%s\".", info->name);
1201 }
1202 }
1203 } else {
1204 lyxml_unlink(ctx, info);
1205 ++err->other_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001206 err->other = nc_realloc(err->other, err->other_count * sizeof *err->other);
1207 if (!err->other) {
1208 ERRMEM;
1209 return;
1210 }
Michal Vasko086311b2016-01-08 09:53:11 +01001211 err->other[err->other_count - 1] = info;
1212 }
1213 }
1214 } else {
1215 if (iter->content) {
1216 WRN("<rpc-error> unknown child \"%s\" with value \"%s\".", iter->name, iter->content);
1217 } else {
1218 WRN("<rpc-error> unknown child \"%s\".", iter->name);
1219 }
1220 }
1221 }
1222}
1223
1224static struct nc_reply *
Michal Vaskoeb7080e2016-02-18 13:27:05 +01001225parse_reply(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_rpc *rpc, int parseroptions)
Michal Vasko086311b2016-01-08 09:53:11 +01001226{
1227 struct lyxml_elem *iter;
Michal Vaskoe1708602016-10-18 12:17:22 +02001228 struct lyd_node *data = NULL, *rpc_act = NULL;
Michal Vasko1a38c862016-01-15 15:50:07 +01001229 struct nc_client_reply_error *error_rpl;
Michal Vasko086311b2016-01-08 09:53:11 +01001230 struct nc_reply_data *data_rpl;
1231 struct nc_reply *reply = NULL;
Michal Vasko90e8e692016-07-13 12:27:57 +02001232 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01001233 int i;
1234
1235 if (!xml->child) {
1236 ERR("An empty <rpc-reply>.");
1237 return NULL;
1238 }
1239
1240 /* rpc-error */
1241 if (!strcmp(xml->child->name, "rpc-error") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
1242 /* count and check elements */
1243 i = 0;
1244 LY_TREE_FOR(xml->child, iter) {
1245 if (strcmp(iter->name, "rpc-error")) {
1246 ERR("<rpc-reply> content mismatch (<rpc-error> and <%s>).", iter->name);
1247 return NULL;
1248 } else if (!iter->ns) {
1249 ERR("<rpc-reply> content mismatch (<rpc-error> without namespace).");
1250 return NULL;
1251 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
1252 ERR("<rpc-reply> content mismatch (<rpc-error> with NS \"%s\").", iter->ns->value);
1253 return NULL;
1254 }
1255 ++i;
1256 }
1257
1258 error_rpl = malloc(sizeof *error_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001259 if (!error_rpl) {
1260 ERRMEM;
1261 return NULL;
1262 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001263 error_rpl->type = NC_RPL_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001264 error_rpl->err = calloc(i, sizeof *error_rpl->err);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001265 if (!error_rpl->err) {
1266 ERRMEM;
1267 free(error_rpl);
1268 return NULL;
1269 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001270 error_rpl->count = i;
Michal Vasko1a38c862016-01-15 15:50:07 +01001271 error_rpl->ctx = ctx;
Michal Vasko086311b2016-01-08 09:53:11 +01001272 reply = (struct nc_reply *)error_rpl;
1273
1274 i = 0;
1275 LY_TREE_FOR(xml->child, iter) {
1276 parse_rpc_error(ctx, iter, error_rpl->err + i);
1277 ++i;
1278 }
1279
1280 /* ok */
1281 } else if (!strcmp(xml->child->name, "ok") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
1282 if (xml->child->next) {
1283 ERR("<rpc-reply> content mismatch (<ok> and <%s>).", xml->child->next->name);
1284 return NULL;
1285 }
1286 reply = malloc(sizeof *reply);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001287 if (!reply) {
1288 ERRMEM;
1289 return NULL;
1290 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001291 reply->type = NC_RPL_OK;
Michal Vasko086311b2016-01-08 09:53:11 +01001292
1293 /* some RPC output */
1294 } else {
1295 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001296 case NC_RPC_ACT_GENERIC:
1297 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01001298
1299 if (rpc_gen->has_data) {
Michal Vaskoe1708602016-10-18 12:17:22 +02001300 rpc_act = rpc_gen->content.data;
Michal Vasko086311b2016-01-08 09:53:11 +01001301 } else {
Michal Vaskoeee99412016-11-21 10:19:43 +01001302 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 +02001303 if (!rpc_act) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001304 ERR("Failed to parse a generic RPC/action XML.");
Michal Vasko086311b2016-01-08 09:53:11 +01001305 return NULL;
1306 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001307 }
Michal Vasko086311b2016-01-08 09:53:11 +01001308 break;
1309
1310 case NC_RPC_GETCONFIG:
1311 case NC_RPC_GET:
Michal Vasko13ed2942016-02-29 16:21:00 +01001312 if (!xml->child->child) {
1313 /* we did not receive any data */
1314 data_rpl = malloc(sizeof *data_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001315 if (!data_rpl) {
1316 ERRMEM;
1317 return NULL;
1318 }
Michal Vasko13ed2942016-02-29 16:21:00 +01001319 data_rpl->type = NC_RPL_DATA;
1320 data_rpl->data = NULL;
1321 return (struct nc_reply *)data_rpl;
1322 }
1323
Michal Vasko086311b2016-01-08 09:53:11 +01001324 /* special treatment */
Michal Vasko0a5ae9a2016-11-15 11:54:47 +01001325 data = lyd_parse_xml(ctx, &xml->child->child,
1326 LYD_OPT_DESTRUCT | (rpc->type == NC_RPC_GETCONFIG ? LYD_OPT_GETCONFIG : LYD_OPT_GET)
Michal Vaskoeee99412016-11-21 10:19:43 +01001327 | parseroptions);
Michal Vasko086311b2016-01-08 09:53:11 +01001328 if (!data) {
1329 ERR("Failed to parse <%s> reply.", (rpc->type == NC_RPC_GETCONFIG ? "get-config" : "get"));
1330 return NULL;
1331 }
1332 break;
1333
1334 case NC_RPC_GETSCHEMA:
Radek Krejci3222b7d2017-09-21 16:04:30 +02001335 rpc_act = lyd_new(NULL, ly_ctx_get_module(ctx, "ietf-netconf-monitoring", NULL, 1), "get-schema");
Michal Vaskoe1708602016-10-18 12:17:22 +02001336 if (!rpc_act) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001337 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +01001338 return NULL;
1339 }
1340 break;
1341
1342 case NC_RPC_EDIT:
1343 case NC_RPC_COPY:
1344 case NC_RPC_DELETE:
1345 case NC_RPC_LOCK:
1346 case NC_RPC_UNLOCK:
1347 case NC_RPC_KILL:
1348 case NC_RPC_COMMIT:
1349 case NC_RPC_DISCARD:
1350 case NC_RPC_CANCEL:
1351 case NC_RPC_VALIDATE:
1352 case NC_RPC_SUBSCRIBE:
1353 /* there is no output defined */
1354 ERR("Unexpected data reply (root elem \"%s\").", xml->child->name);
1355 return NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001356 default:
1357 ERRINT;
1358 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001359 }
1360
1361 data_rpl = malloc(sizeof *data_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001362 if (!data_rpl) {
1363 ERRMEM;
1364 return NULL;
1365 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001366 data_rpl->type = NC_RPL_DATA;
Michal Vasko086311b2016-01-08 09:53:11 +01001367 if (!data) {
Michal Vasko68b3f292016-09-16 12:00:32 +02001368 data_rpl->data = lyd_parse_xml(ctx, &xml->child, LYD_OPT_RPCREPLY | LYD_OPT_DESTRUCT | parseroptions,
Michal Vaskocc3d52b2016-10-18 12:17:22 +02001369 rpc_act, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001370 } else {
1371 /* <get>, <get-config> */
1372 data_rpl->data = data;
1373 }
Michal Vaskoe1708602016-10-18 12:17:22 +02001374 lyd_free_withsiblings(rpc_act);
Michal Vasko086311b2016-01-08 09:53:11 +01001375 if (!data_rpl->data) {
1376 ERR("Failed to parse <rpc-reply>.");
1377 free(data_rpl);
1378 return NULL;
1379 }
1380 reply = (struct nc_reply *)data_rpl;
1381 }
1382
1383 return reply;
1384}
1385
Radek Krejci53691be2016-02-22 13:58:37 +01001386#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +01001387
Michal Vasko3031aae2016-01-27 16:07:18 +01001388int
1389nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1390{
1391 int sock;
1392
Michal Vasko45e53ae2016-04-07 11:46:03 +02001393 if (!address) {
1394 ERRARG("address");
1395 return -1;
1396 } else if (!port) {
1397 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +01001398 return -1;
1399 }
1400
1401 sock = nc_sock_listen(address, port);
1402 if (sock == -1) {
1403 return -1;
1404 }
1405
1406 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001407 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
1408 if (!client_opts.ch_binds) {
1409 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001410 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001411 return -1;
1412 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001413
Michal Vasko2e6defd2016-10-07 15:48:15 +02001414 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
1415 if (!client_opts.ch_bind_ti) {
1416 ERRMEM;
1417 close(sock);
1418 return -1;
1419 }
1420 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
1421
Michal Vasko3031aae2016-01-27 16:07:18 +01001422 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001423 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
1424 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001425 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001426 return -1;
1427 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001428 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
1429 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001430 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001431
1432 return 0;
1433}
1434
1435int
1436nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1437{
1438 uint32_t i;
1439 int ret = -1;
1440
1441 if (!address && !port && !ti) {
1442 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1443 close(client_opts.ch_binds[i].sock);
1444 free((char *)client_opts.ch_binds[i].address);
1445
1446 ret = 0;
1447 }
1448 free(client_opts.ch_binds);
1449 client_opts.ch_binds = NULL;
1450 client_opts.ch_bind_count = 0;
1451 } else {
1452 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1453 if ((!address || !strcmp(client_opts.ch_binds[i].address, address))
1454 && (!port || (client_opts.ch_binds[i].port == port))
Michal Vasko2e6defd2016-10-07 15:48:15 +02001455 && (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001456 close(client_opts.ch_binds[i].sock);
1457 free((char *)client_opts.ch_binds[i].address);
1458
1459 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001460 if (!client_opts.ch_bind_count) {
1461 free(client_opts.ch_binds);
1462 client_opts.ch_binds = NULL;
1463 } else if (i < client_opts.ch_bind_count) {
1464 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1465 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1466 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001467
1468 ret = 0;
1469 }
1470 }
1471 }
1472
1473 return ret;
1474}
1475
1476API int
1477nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1478{
1479 int sock;
1480 char *host = NULL;
1481 uint16_t port, idx;
1482
Michal Vasko45e53ae2016-04-07 11:46:03 +02001483 if (!client_opts.ch_binds) {
1484 ERRINIT;
1485 return -1;
1486 } else if (!session) {
1487 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001488 return -1;
1489 }
1490
1491 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1492
Michal Vasko50456e82016-02-02 12:16:08 +01001493 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001494 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001495 return sock;
1496 }
1497
Radek Krejci53691be2016-02-22 13:58:37 +01001498#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001499 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001500 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001501 } else
1502#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001503#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001504 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001505 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001506 } else
1507#endif
1508 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001509 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001510 *session = NULL;
1511 }
1512
1513 free(host);
1514
1515 if (!(*session)) {
1516 return -1;
1517 }
1518
1519 return 1;
1520}
1521
Radek Krejci53691be2016-02-22 13:58:37 +01001522#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001523
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001524API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001525nc_session_get_cpblts(const struct nc_session *session)
1526{
1527 if (!session) {
1528 ERRARG("session");
1529 return NULL;
1530 }
1531
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001532 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001533}
1534
1535API const char *
1536nc_session_cpblt(const struct nc_session *session, const char *capab)
1537{
1538 int i, len;
1539
1540 if (!session) {
1541 ERRARG("session");
1542 return NULL;
1543 } else if (!capab) {
1544 ERRARG("capab");
1545 return NULL;
1546 }
1547
1548 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001549 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1550 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1551 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001552 }
1553 }
1554
1555 return NULL;
1556}
1557
Michal Vasko9cd26a82016-05-31 08:58:48 +02001558API int
1559nc_session_ntf_thread_running(const struct nc_session *session)
1560{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001561 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001562 ERRARG("session");
1563 return 0;
1564 }
1565
Michal Vasko2e6defd2016-10-07 15:48:15 +02001566 return session->opts.client.ntf_tid ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001567}
1568
Michal Vaskob7558c52016-02-26 15:04:19 +01001569API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001570nc_client_init(void)
1571{
1572 nc_init();
1573}
1574
1575API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001576nc_client_destroy(void)
1577{
Radek Krejci5cebc6b2017-05-26 13:24:38 +02001578 nc_client_set_schema_searchpath(NULL);
1579#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
1580 nc_client_ch_del_bind(NULL, 0, 0);
1581#endif
1582#ifdef NC_ENABLED_SSH
1583 nc_client_ssh_destroy_opts();
1584#endif
1585#ifdef NC_ENABLED_TLS
1586 nc_client_tls_destroy_opts();
1587#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001588 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001589}
1590
Michal Vasko086311b2016-01-08 09:53:11 +01001591API NC_MSG_TYPE
Michal Vaskoeb7080e2016-02-18 13:27:05 +01001592nc_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 +01001593{
1594 struct lyxml_elem *xml;
1595 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1596
Michal Vasko45e53ae2016-04-07 11:46:03 +02001597 if (!session) {
1598 ERRARG("session");
1599 return NC_MSG_ERROR;
1600 } else if (!rpc) {
1601 ERRARG("rpc");
1602 return NC_MSG_ERROR;
1603 } else if (!reply) {
1604 ERRARG("reply");
1605 return NC_MSG_ERROR;
1606 } else if (parseroptions & LYD_OPT_TYPEMASK) {
1607 ERRARG("parseroptions");
Michal Vasko086311b2016-01-08 09:53:11 +01001608 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001609 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001610 ERR("Session %u: invalid session to receive RPC replies.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001611 return NC_MSG_ERROR;
1612 }
Michal Vaskoeb7080e2016-02-18 13:27:05 +01001613 parseroptions &= ~(LYD_OPT_DESTRUCT | LYD_OPT_NOSIBLINGS);
Michal Vaskoeee99412016-11-21 10:19:43 +01001614 if (!(session->flags & NC_SESSION_CLIENT_NOT_STRICT)) {
1615 parseroptions &= LYD_OPT_STRICT;
1616 }
Michal Vasko41adf392017-01-17 10:39:04 +01001617 /* no mechanism to check external dependencies is provided */
1618 parseroptions|= LYD_OPT_NOEXTDEPS;
Michal Vasko086311b2016-01-08 09:53:11 +01001619 *reply = NULL;
1620
1621 msgtype = get_msg(session, timeout, msgid, &xml);
Michal Vasko086311b2016-01-08 09:53:11 +01001622
Michal Vasko71ba2da2016-05-04 10:53:16 +02001623 if ((msgtype == NC_MSG_REPLY) || (msgtype == NC_MSG_REPLY_ERR_MSGID)) {
Michal Vaskoeee99412016-11-21 10:19:43 +01001624 *reply = parse_reply(session->ctx, xml, rpc, parseroptions);
Michal Vasko086311b2016-01-08 09:53:11 +01001625 lyxml_free(session->ctx, xml);
1626 if (!(*reply)) {
1627 return NC_MSG_ERROR;
1628 }
1629 }
1630
1631 return msgtype;
1632}
1633
1634API NC_MSG_TYPE
1635nc_recv_notif(struct nc_session *session, int timeout, struct nc_notif **notif)
1636{
1637 struct lyxml_elem *xml, *ev_time;
1638 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1639
Michal Vasko45e53ae2016-04-07 11:46:03 +02001640 if (!session) {
1641 ERRARG("session");
1642 return NC_MSG_ERROR;
1643 } else if (!notif) {
1644 ERRARG("notif");
Michal Vasko086311b2016-01-08 09:53:11 +01001645 return NC_MSG_ERROR;
1646 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01001647 ERR("Session %u: invalid session to receive Notifications.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001648 return NC_MSG_ERROR;
1649 }
1650
1651 msgtype = get_msg(session, timeout, 0, &xml);
1652
1653 if (msgtype == NC_MSG_NOTIF) {
1654 *notif = calloc(1, sizeof **notif);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001655 if (!*notif) {
1656 ERRMEM;
1657 lyxml_free(session->ctx, xml);
1658 return NC_MSG_ERROR;
1659 }
Michal Vasko086311b2016-01-08 09:53:11 +01001660
1661 /* eventTime */
1662 LY_TREE_FOR(xml->child, ev_time) {
1663 if (!strcmp(ev_time->name, "eventTime")) {
1664 (*notif)->datetime = lydict_insert(session->ctx, ev_time->content, 0);
1665 /* lyd_parse does not know this element */
1666 lyxml_free(session->ctx, ev_time);
1667 break;
1668 }
1669 }
1670 if (!(*notif)->datetime) {
Michal Vaskod083db62016-01-19 10:31:29 +01001671 ERR("Session %u: notification is missing the \"eventTime\" element.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001672 goto fail;
1673 }
1674
1675 /* notification body */
Michal Vasko41adf392017-01-17 10:39:04 +01001676 (*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 +01001677 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001678 lyxml_free(session->ctx, xml);
1679 xml = NULL;
1680 if (!(*notif)->tree) {
Michal Vaskod083db62016-01-19 10:31:29 +01001681 ERR("Session %u: failed to parse a new notification.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001682 goto fail;
1683 }
1684 }
1685
1686 return msgtype;
1687
1688fail:
1689 lydict_remove(session->ctx, (*notif)->datetime);
1690 lyd_free((*notif)->tree);
1691 free(*notif);
1692 *notif = NULL;
1693 lyxml_free(session->ctx, xml);
1694
1695 return NC_MSG_ERROR;
1696}
1697
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001698static void *
1699nc_recv_notif_thread(void *arg)
1700{
1701 struct nc_ntf_thread_arg *ntarg;
1702 struct nc_session *session;
1703 void (*notif_clb)(struct nc_session *session, const struct nc_notif *notif);
1704 struct nc_notif *notif;
1705 NC_MSG_TYPE msgtype;
1706
1707 ntarg = (struct nc_ntf_thread_arg *)arg;
1708 session = ntarg->session;
1709 notif_clb = ntarg->notif_clb;
1710 free(ntarg);
1711
Michal Vasko2e6defd2016-10-07 15:48:15 +02001712 while (session->opts.client.ntf_tid) {
Michal vasko4e1a36c2016-10-05 13:04:37 +02001713 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &notif);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001714 if (msgtype == NC_MSG_NOTIF) {
1715 notif_clb(session, notif);
Michal Vaskof0537d82016-01-29 14:42:38 +01001716 if (!strcmp(notif->tree->schema->name, "notificationComplete")
1717 && !strcmp(notif->tree->schema->module->name, "nc-notifications")) {
1718 nc_notif_free(notif);
1719 break;
1720 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001721 nc_notif_free(notif);
Michal Vaskoce326052018-01-04 10:32:03 +01001722 } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) {
Michal Vasko132f7f42017-09-14 13:40:18 +02001723 /* quit this thread once the session is broken */
1724 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001725 }
1726
1727 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
1728 }
1729
Michal Vasko0651c902016-05-19 15:55:42 +02001730 VRB("Session %u: notification thread exit.", session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001731 session->opts.client.ntf_tid = NULL;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001732 return NULL;
1733}
1734
1735API int
1736nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session, const struct nc_notif *notif))
1737{
1738 struct nc_ntf_thread_arg *ntarg;
1739 int ret;
1740
Michal Vasko45e53ae2016-04-07 11:46:03 +02001741 if (!session) {
1742 ERRARG("session");
1743 return -1;
1744 } else if (!notif_clb) {
1745 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001746 return -1;
1747 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
1748 ERR("Session %u: invalid session to receive Notifications.", session->id);
1749 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001750 } else if (session->opts.client.ntf_tid) {
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001751 ERR("Session %u: separate notification thread is already running.", session->id);
1752 return -1;
1753 }
1754
1755 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001756 if (!ntarg) {
1757 ERRMEM;
1758 return -1;
1759 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001760 ntarg->session = session;
1761 ntarg->notif_clb = notif_clb;
1762
1763 /* just so that nc_recv_notif_thread() does not immediately exit, the value does not matter */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001764 session->opts.client.ntf_tid = malloc(sizeof *session->opts.client.ntf_tid);
1765 if (!session->opts.client.ntf_tid) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001766 ERRMEM;
1767 free(ntarg);
1768 return -1;
1769 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001770
Michal Vasko2e6defd2016-10-07 15:48:15 +02001771 ret = pthread_create((pthread_t *)session->opts.client.ntf_tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001772 if (ret) {
1773 ERR("Session %u: failed to create a new thread (%s).", strerror(errno));
1774 free(ntarg);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001775 free((pthread_t *)session->opts.client.ntf_tid);
1776 session->opts.client.ntf_tid = NULL;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001777 return -1;
1778 }
1779
1780 return 0;
1781}
1782
Michal Vasko086311b2016-01-08 09:53:11 +01001783API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001784nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001785{
1786 NC_MSG_TYPE r;
Radek Krejcib4b19062018-02-07 16:33:06 +01001787 int ret, dofree = 1;
Michal Vasko90e8e692016-07-13 12:27:57 +02001788 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01001789 struct nc_rpc_getconfig *rpc_gc;
1790 struct nc_rpc_edit *rpc_e;
1791 struct nc_rpc_copy *rpc_cp;
1792 struct nc_rpc_delete *rpc_del;
1793 struct nc_rpc_lock *rpc_lock;
1794 struct nc_rpc_get *rpc_g;
1795 struct nc_rpc_kill *rpc_k;
1796 struct nc_rpc_commit *rpc_com;
1797 struct nc_rpc_cancel *rpc_can;
1798 struct nc_rpc_validate *rpc_val;
1799 struct nc_rpc_getschema *rpc_gs;
1800 struct nc_rpc_subscribe *rpc_sub;
1801 struct lyd_node *data, *node;
Michal Vasko9d8bee62016-03-03 10:58:24 +01001802 const struct lys_module *ietfnc = NULL, *ietfncmon, *notifs, *ietfncwd = NULL;
Radek Krejci539efb62016-08-24 15:05:16 +02001803 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01001804 uint64_t cur_msgid;
1805
Michal Vasko45e53ae2016-04-07 11:46:03 +02001806 if (!session) {
1807 ERRARG("session");
1808 return NC_MSG_ERROR;
1809 } else if (!rpc) {
1810 ERRARG("rpc");
1811 return NC_MSG_ERROR;
1812 } else if (!msgid) {
1813 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01001814 return NC_MSG_ERROR;
1815 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01001816 ERR("Session %u: invalid session to send RPCs.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001817 return NC_MSG_ERROR;
1818 }
1819
Michal Vasko90e8e692016-07-13 12:27:57 +02001820 if ((rpc->type != NC_RPC_GETSCHEMA) && (rpc->type != NC_RPC_ACT_GENERIC) && (rpc->type != NC_RPC_SUBSCRIBE)) {
Radek Krejci3222b7d2017-09-21 16:04:30 +02001821 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01001822 if (!ietfnc) {
Michal Vasko086cd572017-01-12 12:19:05 +01001823 ERR("Session %u: missing \"ietf-netconf\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001824 return NC_MSG_ERROR;
1825 }
1826 }
1827
1828 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001829 case NC_RPC_ACT_GENERIC:
1830 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01001831
1832 if (rpc_gen->has_data) {
1833 data = rpc_gen->content.data;
Radek Krejcib4b19062018-02-07 16:33:06 +01001834 dofree = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001835 } else {
Michal Vasko41adf392017-01-17 10:39:04 +01001836 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 +01001837 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL);
Michal Vaskoeec410f2017-11-24 09:14:55 +01001838 if (!data) {
1839 return NC_MSG_ERROR;
1840 }
Michal Vasko086311b2016-01-08 09:53:11 +01001841 }
1842 break;
1843
1844 case NC_RPC_GETCONFIG:
1845 rpc_gc = (struct nc_rpc_getconfig *)rpc;
1846
1847 data = lyd_new(NULL, ietfnc, "get-config");
1848 node = lyd_new(data, ietfnc, "source");
1849 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_gc->source], NULL);
1850 if (!node) {
1851 lyd_free(data);
1852 return NC_MSG_ERROR;
1853 }
1854 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01001855 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001856 node = lyd_new_anydata(data, ietfnc, "filter", rpc_gc->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01001857 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01001858 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02001859 node = lyd_new_anydata(data, ietfnc, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001860 lyd_insert_attr(node, NULL, "type", "xpath");
1861 lyd_insert_attr(node, NULL, "select", rpc_gc->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01001862 }
1863 if (!node) {
1864 lyd_free(data);
1865 return NC_MSG_ERROR;
1866 }
1867 }
1868
1869 if (rpc_gc->wd_mode) {
1870 if (!ietfncwd) {
Radek Krejci3222b7d2017-09-21 16:04:30 +02001871 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01001872 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01001873 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001874 return NC_MSG_ERROR;
1875 }
1876 }
1877 switch (rpc_gc->wd_mode) {
1878 case NC_WD_UNKNOWN:
1879 /* cannot get here */
1880 break;
1881 case NC_WD_ALL:
1882 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1883 break;
1884 case NC_WD_ALL_TAG:
1885 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1886 break;
1887 case NC_WD_TRIM:
1888 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1889 break;
1890 case NC_WD_EXPLICIT:
1891 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1892 break;
1893 }
1894 if (!node) {
1895 lyd_free(data);
1896 return NC_MSG_ERROR;
1897 }
1898 }
1899 break;
1900
1901 case NC_RPC_EDIT:
1902 rpc_e = (struct nc_rpc_edit *)rpc;
1903
1904 data = lyd_new(NULL, ietfnc, "edit-config");
1905 node = lyd_new(data, ietfnc, "target");
1906 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_e->target], NULL);
1907 if (!node) {
1908 lyd_free(data);
1909 return NC_MSG_ERROR;
1910 }
1911
1912 if (rpc_e->default_op) {
1913 node = lyd_new_leaf(data, ietfnc, "default-operation", rpcedit_dfltop2str[rpc_e->default_op]);
1914 if (!node) {
1915 lyd_free(data);
1916 return NC_MSG_ERROR;
1917 }
1918 }
1919
1920 if (rpc_e->test_opt) {
1921 node = lyd_new_leaf(data, ietfnc, "test-option", rpcedit_testopt2str[rpc_e->test_opt]);
1922 if (!node) {
1923 lyd_free(data);
1924 return NC_MSG_ERROR;
1925 }
1926 }
1927
1928 if (rpc_e->error_opt) {
1929 node = lyd_new_leaf(data, ietfnc, "error-option", rpcedit_erropt2str[rpc_e->error_opt]);
1930 if (!node) {
1931 lyd_free(data);
1932 return NC_MSG_ERROR;
1933 }
1934 }
1935
Michal Vasko7793bc62016-09-16 11:58:41 +02001936 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001937 node = lyd_new_anydata(data, ietfnc, "config", rpc_e->edit_cont, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01001938 } else {
1939 node = lyd_new_leaf(data, ietfnc, "url", rpc_e->edit_cont);
1940 }
1941 if (!node) {
1942 lyd_free(data);
1943 return NC_MSG_ERROR;
1944 }
1945 break;
1946
1947 case NC_RPC_COPY:
1948 rpc_cp = (struct nc_rpc_copy *)rpc;
1949
1950 data = lyd_new(NULL, ietfnc, "copy-config");
1951 node = lyd_new(data, ietfnc, "target");
1952 if (rpc_cp->url_trg) {
1953 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_trg);
1954 } else {
1955 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->target], NULL);
1956 }
1957 if (!node) {
1958 lyd_free(data);
1959 return NC_MSG_ERROR;
1960 }
1961
1962 node = lyd_new(data, ietfnc, "source");
1963 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02001964 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001965 node = lyd_new_anydata(node, ietfnc, "config", rpc_cp->url_config_src, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01001966 } else {
1967 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_config_src);
1968 }
1969 } else {
1970 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->source], NULL);
1971 }
1972 if (!node) {
1973 lyd_free(data);
1974 return NC_MSG_ERROR;
1975 }
1976
1977 if (rpc_cp->wd_mode) {
1978 if (!ietfncwd) {
Radek Krejci3222b7d2017-09-21 16:04:30 +02001979 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01001980 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01001981 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001982 return NC_MSG_ERROR;
1983 }
1984 }
1985 switch (rpc_cp->wd_mode) {
1986 case NC_WD_UNKNOWN:
1987 /* cannot get here */
1988 break;
1989 case NC_WD_ALL:
1990 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1991 break;
1992 case NC_WD_ALL_TAG:
1993 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1994 break;
1995 case NC_WD_TRIM:
1996 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1997 break;
1998 case NC_WD_EXPLICIT:
1999 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
2000 break;
2001 }
2002 if (!node) {
2003 lyd_free(data);
2004 return NC_MSG_ERROR;
2005 }
2006 }
2007 break;
2008
2009 case NC_RPC_DELETE:
2010 rpc_del = (struct nc_rpc_delete *)rpc;
2011
2012 data = lyd_new(NULL, ietfnc, "delete-config");
2013 node = lyd_new(data, ietfnc, "target");
2014 if (rpc_del->url) {
2015 node = lyd_new_leaf(node, ietfnc, "url", rpc_del->url);
2016 } else {
2017 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_del->target], NULL);
2018 }
2019 if (!node) {
2020 lyd_free(data);
2021 return NC_MSG_ERROR;
2022 }
2023 break;
2024
2025 case NC_RPC_LOCK:
2026 rpc_lock = (struct nc_rpc_lock *)rpc;
2027
2028 data = lyd_new(NULL, ietfnc, "lock");
2029 node = lyd_new(data, ietfnc, "target");
2030 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
2031 if (!node) {
2032 lyd_free(data);
2033 return NC_MSG_ERROR;
2034 }
2035 break;
2036
2037 case NC_RPC_UNLOCK:
2038 rpc_lock = (struct nc_rpc_lock *)rpc;
2039
2040 data = lyd_new(NULL, ietfnc, "unlock");
2041 node = lyd_new(data, ietfnc, "target");
2042 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
2043 if (!node) {
2044 lyd_free(data);
2045 return NC_MSG_ERROR;
2046 }
2047 break;
2048
2049 case NC_RPC_GET:
2050 rpc_g = (struct nc_rpc_get *)rpc;
2051
2052 data = lyd_new(NULL, ietfnc, "get");
2053 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002054 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002055 node = lyd_new_anydata(data, ietfnc, "filter", rpc_g->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01002056 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01002057 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02002058 node = lyd_new_anydata(data, ietfnc, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01002059 lyd_insert_attr(node, NULL, "type", "xpath");
2060 lyd_insert_attr(node, NULL, "select", rpc_g->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01002061 }
2062 if (!node) {
2063 lyd_free(data);
2064 return NC_MSG_ERROR;
2065 }
2066 }
2067
2068 if (rpc_g->wd_mode) {
2069 if (!ietfncwd) {
Radek Krejci3222b7d2017-09-21 16:04:30 +02002070 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002071 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01002072 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vaskoc41fdea2017-08-09 10:18:44 +02002073 lyd_free(data);
Michal Vasko086311b2016-01-08 09:53:11 +01002074 return NC_MSG_ERROR;
2075 }
2076 }
2077 switch (rpc_g->wd_mode) {
2078 case NC_WD_UNKNOWN:
2079 /* cannot get here */
2080 break;
2081 case NC_WD_ALL:
2082 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
2083 break;
2084 case NC_WD_ALL_TAG:
2085 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
2086 break;
2087 case NC_WD_TRIM:
2088 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
2089 break;
2090 case NC_WD_EXPLICIT:
2091 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
2092 break;
2093 }
2094 if (!node) {
2095 lyd_free(data);
2096 return NC_MSG_ERROR;
2097 }
2098 }
2099 break;
2100
2101 case NC_RPC_KILL:
2102 rpc_k = (struct nc_rpc_kill *)rpc;
2103
2104 data = lyd_new(NULL, ietfnc, "kill-session");
2105 sprintf(str, "%u", rpc_k->sid);
2106 lyd_new_leaf(data, ietfnc, "session-id", str);
2107 break;
2108
2109 case NC_RPC_COMMIT:
2110 rpc_com = (struct nc_rpc_commit *)rpc;
2111
2112 data = lyd_new(NULL, ietfnc, "commit");
2113 if (rpc_com->confirmed) {
2114 lyd_new_leaf(data, ietfnc, "confirmed", NULL);
2115 }
2116
2117 if (rpc_com->confirm_timeout) {
2118 sprintf(str, "%u", rpc_com->confirm_timeout);
2119 lyd_new_leaf(data, ietfnc, "confirm-timeout", str);
2120 }
2121
2122 if (rpc_com->persist) {
2123 node = lyd_new_leaf(data, ietfnc, "persist", rpc_com->persist);
2124 if (!node) {
2125 lyd_free(data);
2126 return NC_MSG_ERROR;
2127 }
2128 }
2129
2130 if (rpc_com->persist_id) {
2131 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_com->persist_id);
2132 if (!node) {
2133 lyd_free(data);
2134 return NC_MSG_ERROR;
2135 }
2136 }
2137 break;
2138
2139 case NC_RPC_DISCARD:
2140 data = lyd_new(NULL, ietfnc, "discard-changes");
2141 break;
2142
2143 case NC_RPC_CANCEL:
2144 rpc_can = (struct nc_rpc_cancel *)rpc;
2145
2146 data = lyd_new(NULL, ietfnc, "cancel-commit");
2147 if (rpc_can->persist_id) {
2148 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_can->persist_id);
2149 if (!node) {
2150 lyd_free(data);
2151 return NC_MSG_ERROR;
2152 }
2153 }
2154 break;
2155
2156 case NC_RPC_VALIDATE:
2157 rpc_val = (struct nc_rpc_validate *)rpc;
2158
2159 data = lyd_new(NULL, ietfnc, "validate");
2160 node = lyd_new(data, ietfnc, "source");
2161 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02002162 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002163 node = lyd_new_anydata(node, ietfnc, "config", rpc_val->url_config_src, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01002164 } else {
2165 node = lyd_new_leaf(node, ietfnc, "url", rpc_val->url_config_src);
2166 }
2167 } else {
2168 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_val->source], NULL);
2169 }
2170 if (!node) {
2171 lyd_free(data);
2172 return NC_MSG_ERROR;
2173 }
2174 break;
2175
2176 case NC_RPC_GETSCHEMA:
Radek Krejci3222b7d2017-09-21 16:04:30 +02002177 ietfncmon = ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002178 if (!ietfncmon) {
Michal Vasko086cd572017-01-12 12:19:05 +01002179 ERR("Session %u: missing \"ietf-netconf-monitoring\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002180 return NC_MSG_ERROR;
2181 }
2182
2183 rpc_gs = (struct nc_rpc_getschema *)rpc;
2184
2185 data = lyd_new(NULL, ietfncmon, "get-schema");
2186 node = lyd_new_leaf(data, ietfncmon, "identifier", rpc_gs->identifier);
2187 if (!node) {
2188 lyd_free(data);
2189 return NC_MSG_ERROR;
2190 }
2191 if (rpc_gs->version) {
2192 node = lyd_new_leaf(data, ietfncmon, "version", rpc_gs->version);
2193 if (!node) {
2194 lyd_free(data);
2195 return NC_MSG_ERROR;
2196 }
2197 }
2198 if (rpc_gs->format) {
2199 node = lyd_new_leaf(data, ietfncmon, "format", rpc_gs->format);
2200 if (!node) {
2201 lyd_free(data);
2202 return NC_MSG_ERROR;
2203 }
2204 }
2205 break;
2206
2207 case NC_RPC_SUBSCRIBE:
Radek Krejci3222b7d2017-09-21 16:04:30 +02002208 notifs = ly_ctx_get_module(session->ctx, "notifications", NULL, 1);
Michal Vasko086311b2016-01-08 09:53:11 +01002209 if (!notifs) {
Michal Vasko086cd572017-01-12 12:19:05 +01002210 ERR("Session %u: missing \"notifications\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01002211 return NC_MSG_ERROR;
2212 }
2213
2214 rpc_sub = (struct nc_rpc_subscribe *)rpc;
2215
2216 data = lyd_new(NULL, notifs, "create-subscription");
2217 if (rpc_sub->stream) {
2218 node = lyd_new_leaf(data, notifs, "stream", rpc_sub->stream);
2219 if (!node) {
2220 lyd_free(data);
2221 return NC_MSG_ERROR;
2222 }
2223 }
2224
2225 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01002226 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02002227 node = lyd_new_anydata(data, notifs, "filter", rpc_sub->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01002228 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01002229 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02002230 node = lyd_new_anydata(data, notifs, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01002231 lyd_insert_attr(node, NULL, "type", "xpath");
2232 lyd_insert_attr(node, NULL, "select", rpc_sub->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01002233 }
2234 if (!node) {
2235 lyd_free(data);
2236 return NC_MSG_ERROR;
2237 }
2238 }
2239
2240 if (rpc_sub->start) {
2241 node = lyd_new_leaf(data, notifs, "startTime", rpc_sub->start);
2242 if (!node) {
2243 lyd_free(data);
2244 return NC_MSG_ERROR;
2245 }
2246 }
2247
2248 if (rpc_sub->stop) {
2249 node = lyd_new_leaf(data, notifs, "stopTime", rpc_sub->stop);
2250 if (!node) {
2251 lyd_free(data);
2252 return NC_MSG_ERROR;
2253 }
2254 }
2255 break;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002256 default:
2257 ERRINT;
2258 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01002259 }
2260
Michal Vasko41adf392017-01-17 10:39:04 +01002261 if (lyd_validate(&data, LYD_OPT_RPC | LYD_OPT_NOEXTDEPS
2262 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL)) {
Radek Krejcib4b19062018-02-07 16:33:06 +01002263 if (dofree) {
2264 lyd_free(data);
2265 }
Michal Vasko086311b2016-01-08 09:53:11 +01002266 return NC_MSG_ERROR;
2267 }
2268
Michal Vaskoade892d2017-02-22 13:40:35 +01002269 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01002270 if (ret == -1) {
2271 /* error */
2272 r = NC_MSG_ERROR;
2273 } else if (!ret) {
2274 /* blocking */
Michal Vasko086311b2016-01-08 09:53:11 +01002275 r = NC_MSG_WOULDBLOCK;
2276 } else {
2277 /* send RPC, store its message ID */
2278 r = nc_send_msg(session, data);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002279 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01002280 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002281 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +01002282
Radek Krejcib4b19062018-02-07 16:33:06 +01002283 if (dofree) {
2284 lyd_free(data);
2285 }
Michal Vasko086311b2016-01-08 09:53:11 +01002286
2287 if (r != NC_MSG_RPC) {
2288 return r;
2289 }
2290
2291 *msgid = cur_msgid;
2292 return NC_MSG_RPC;
2293}
Michal Vaskode2946c2017-01-12 12:19:26 +01002294
2295API void
2296nc_client_session_set_not_strict(struct nc_session *session)
2297{
2298 if (session->side != NC_CLIENT) {
2299 ERRARG("session");
2300 return;
2301 }
2302
2303 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
2304}