blob: ae9b689dd53c8e8a9a7b207c5f0d0ba0de611c06 [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
15#include <assert.h>
16#include <errno.h>
17#include <fcntl.h>
18#include <netdb.h>
Radek Krejci4cf58ec2016-02-26 15:04:52 +010019#include <netinet/in.h>
Michal Vasko086311b2016-01-08 09:53:11 +010020#include <pthread.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/socket.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <unistd.h>
27#include <arpa/inet.h>
28#include <poll.h>
29
30#include <libyang/libyang.h>
31
Michal Vasko086311b2016-01-08 09:53:11 +010032#include "libnetconf.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010033#include "session_client.h"
Michal Vaskoa8ad4482016-01-28 14:25:54 +010034#include "messages_client.h"
Michal Vasko086311b2016-01-08 09:53:11 +010035
Michal Vasko80ef5d22016-01-18 09:21:02 +010036static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
37
Michal Vaskodaf9a092016-02-09 10:42:05 +010038struct nc_client_opts client_opts;
Michal Vasko086311b2016-01-08 09:53:11 +010039
40API int
Michal Vasko7f1c0ef2016-03-11 11:13:06 +010041nc_client_set_schema_searchpath(const char *path)
Michal Vasko086311b2016-01-08 09:53:11 +010042{
Michal Vasko3031aae2016-01-27 16:07:18 +010043 if (client_opts.schema_searchpath) {
44 free(client_opts.schema_searchpath);
Michal Vasko086311b2016-01-08 09:53:11 +010045 }
Michal Vasko086311b2016-01-08 09:53:11 +010046
Michal Vasko7f1c78b2016-01-19 09:52:14 +010047 if (path) {
Michal Vasko3031aae2016-01-27 16:07:18 +010048 client_opts.schema_searchpath = strdup(path);
49 if (!client_opts.schema_searchpath) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +010050 ERRMEM;
51 return 1;
52 }
53 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +010054 client_opts.schema_searchpath = NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +010055 }
56
57 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +010058}
59
Michal Vasko7f1c0ef2016-03-11 11:13:06 +010060API const char *
61nc_client_get_schema_searchpath(void)
62{
63 return client_opts.schema_searchpath;
64}
65
Michal Vasko3031aae2016-01-27 16:07:18 +010066/* SCHEMAS_DIR not used (implicitly) */
Michal Vasko086311b2016-01-08 09:53:11 +010067static int
Michal Vaskoeee99412016-11-21 10:19:43 +010068ctx_check_and_load_model(struct nc_session *session, const char *module_cpblt)
Michal Vasko086311b2016-01-08 09:53:11 +010069{
70 const struct lys_module *module;
71 char *ptr, *ptr2;
72 char *model_name, *revision = NULL, *features = NULL;
73
Michal Vaskoeee99412016-11-21 10:19:43 +010074 assert(!strncmp(module_cpblt, "module=", 7));
75
76 ptr = (char *)module_cpblt + 7;
Michal Vasko086311b2016-01-08 09:53:11 +010077 ptr2 = strchr(ptr, '&');
78 if (!ptr2) {
79 ptr2 = ptr + strlen(ptr);
80 }
81 model_name = strndup(ptr, ptr2 - ptr);
82
83 /* parse revision */
Michal Vaskoeee99412016-11-21 10:19:43 +010084 ptr = strstr(module_cpblt, "revision=");
Michal Vasko086311b2016-01-08 09:53:11 +010085 if (ptr) {
86 ptr += 9;
87 ptr2 = strchr(ptr, '&');
88 if (!ptr2) {
89 ptr2 = ptr + strlen(ptr);
90 }
91 revision = strndup(ptr, ptr2 - ptr);
92 }
93
94 /* load module if needed */
95 module = ly_ctx_get_module(session->ctx, model_name, revision);
96 if (!module) {
97 module = ly_ctx_load_module(session->ctx, model_name, revision);
98 }
99
Michal Vasko086311b2016-01-08 09:53:11 +0100100 free(revision);
101 if (!module) {
Michal Vaskoef578332016-01-25 13:20:09 +0100102 WRN("Failed to load model \"%s\".", model_name);
103 free(model_name);
Michal Vasko086311b2016-01-08 09:53:11 +0100104 return 1;
105 }
Michal Vaskoef578332016-01-25 13:20:09 +0100106 free(model_name);
Michal Vasko086311b2016-01-08 09:53:11 +0100107
Michal Vasko2a54a6d2017-04-26 14:26:41 +0200108 /* make it implemented */
109 if (!module->implemented && lys_set_implemented(module)) {
110 WRN("Failed to implement model \"%s\".", module->name);
111 return 1;
112 }
113
Michal Vasko086311b2016-01-08 09:53:11 +0100114 /* parse features */
Michal Vaskoeee99412016-11-21 10:19:43 +0100115 ptr = strstr(module_cpblt, "features=");
Michal Vasko086311b2016-01-08 09:53:11 +0100116 if (ptr) {
117 ptr += 9;
118 ptr2 = strchr(ptr, '&');
119 if (!ptr2) {
120 ptr2 = ptr + strlen(ptr);
121 }
122 features = strndup(ptr, ptr2 - ptr);
123 }
124
125 /* enable features */
126 if (features) {
127 /* basically manual strtok_r (to avoid macro) */
128 ptr2 = features;
129 for (ptr = features; *ptr; ++ptr) {
130 if (*ptr == ',') {
131 *ptr = '\0';
132 /* remember last feature */
133 ptr2 = ptr + 1;
134 }
135 }
136
137 ptr = features;
138 lys_features_enable(module, ptr);
139 while (ptr != ptr2) {
140 ptr += strlen(ptr) + 1;
141 lys_features_enable(module, ptr);
142 }
143
144 free(features);
145 }
146
147 return 0;
148}
149
Michal Vasko1aaa6602016-02-09 11:04:33 +0100150/* SCHEMAS_DIR used as the last resort */
Michal Vasko086311b2016-01-08 09:53:11 +0100151static int
Michal Vasko1b2ddc92017-05-24 08:59:49 +0200152ctx_check_and_load_ietf_netconf(struct ly_ctx *ctx, char **cpblts)
Michal Vasko086311b2016-01-08 09:53:11 +0100153{
154 int i;
155 const struct lys_module *ietfnc;
156
157 ietfnc = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
158 if (!ietfnc) {
Michal Vasko1aaa6602016-02-09 11:04:33 +0100159 ietfnc = ly_ctx_load_module(ctx, "ietf-netconf", NULL);
160 if (!ietfnc) {
Michal Vasko086311b2016-01-08 09:53:11 +0100161 ietfnc = lys_parse_path(ctx, SCHEMAS_DIR"/ietf-netconf.yin", LYS_IN_YIN);
Michal Vasko086311b2016-01-08 09:53:11 +0100162 }
163 }
164 if (!ietfnc) {
165 ERR("Loading base NETCONF schema failed.");
166 return 1;
167 }
168
169 /* set supported capabilities from ietf-netconf */
170 for (i = 0; cpblts[i]; ++i) {
171 if (!strncmp(cpblts[i], "urn:ietf:params:netconf:capability:", 35)) {
172 if (!strncmp(cpblts[i] + 35, "writable-running", 16)) {
173 lys_features_enable(ietfnc, "writable-running");
174 } else if (!strncmp(cpblts[i] + 35, "candidate", 9)) {
175 lys_features_enable(ietfnc, "candidate");
176 } else if (!strcmp(cpblts[i] + 35, "confirmed-commit:1.1")) {
177 lys_features_enable(ietfnc, "confirmed-commit");
178 } else if (!strncmp(cpblts[i] + 35, "rollback-on-error", 17)) {
179 lys_features_enable(ietfnc, "rollback-on-error");
180 } else if (!strcmp(cpblts[i] + 35, "validate:1.1")) {
181 lys_features_enable(ietfnc, "validate");
182 } else if (!strncmp(cpblts[i] + 35, "startup", 7)) {
183 lys_features_enable(ietfnc, "startup");
184 } else if (!strncmp(cpblts[i] + 35, "url", 3)) {
185 lys_features_enable(ietfnc, "url");
186 } else if (!strncmp(cpblts[i] + 35, "xpath", 5)) {
187 lys_features_enable(ietfnc, "xpath");
188 }
189 }
190 }
191
192 return 0;
193}
194
195static char *
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200196libyang_module_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *submod_rev,
197 void *user_data, LYS_INFORMAT *format, void (**free_model_data)(void *model_data))
Michal Vasko086311b2016-01-08 09:53:11 +0100198{
199 struct nc_session *session = (struct nc_session *)user_data;
200 struct nc_rpc *rpc;
201 struct nc_reply *reply;
202 struct nc_reply_data *data_rpl;
Michal Vasko998ba412016-09-16 12:00:07 +0200203 struct nc_reply_error *error_rpl;
Radek Krejci539efb62016-08-24 15:05:16 +0200204 struct lyd_node_anydata *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100205 NC_MSG_TYPE msg;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200206 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100207 uint64_t msgid;
208
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200209 if (submod_name) {
Michal Vasko3e7eaf42016-09-20 10:51:23 +0200210 rpc = nc_rpc_getschema(submod_name, submod_rev, "yang", NC_PARAMTYPE_CONST);
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200211 } else {
Michal Vasko3e7eaf42016-09-20 10:51:23 +0200212 rpc = nc_rpc_getschema(mod_name, mod_rev, "yang", NC_PARAMTYPE_CONST);
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200213 }
Michal Vasko086311b2016-01-08 09:53:11 +0100214
215 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
216 usleep(1000);
217 }
218 if (msg == NC_MSG_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100219 ERR("Session %u: failed to send the <get-schema> RPC.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100220 nc_rpc_free(rpc);
221 return NULL;
222 }
223
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200224 do {
Michal Vaskof471fa02017-02-15 10:48:12 +0100225 msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, 0, &reply);
Michal Vaskoff8ddc02016-10-03 14:13:29 +0200226 } while (msg == NC_MSG_NOTIF);
Michal Vasko086311b2016-01-08 09:53:11 +0100227 nc_rpc_free(rpc);
228 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100229 ERR("Session %u: timeout for receiving reply to a <get-schema> expired.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100230 return NULL;
231 } else if (msg == NC_MSG_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100232 ERR("Session %u: failed to receive a reply to <get-schema>.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100233 return NULL;
234 }
235
Michal Vasko998ba412016-09-16 12:00:07 +0200236 switch (reply->type) {
237 case NC_RPL_OK:
238 ERR("Session %u: unexpected reply OK to a <get-schema> RPC.", session->id);
239 nc_reply_free(reply);
240 return NULL;
241 case NC_RPL_DATA:
242 /* fine */
243 break;
244 case NC_RPL_ERROR:
245 error_rpl = (struct nc_reply_error *)reply;
246 if (error_rpl->count) {
247 ERR("Session %u: error reply to a <get-schema> RPC (tag \"%s\", message \"%s\").",
248 session->id, error_rpl->err[0].tag, error_rpl->err[0].message);
249 } else {
250 ERR("Session %u: unexpected reply error to a <get-schema> RPC.", session->id);
251 }
252 nc_reply_free(reply);
253 return NULL;
254 case NC_RPL_NOTIF:
255 ERR("Session %u: unexpected reply notification to a <get-schema> RPC.", session->id);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100256 nc_reply_free(reply);
257 return NULL;
258 }
259
Michal Vasko086311b2016-01-08 09:53:11 +0100260 data_rpl = (struct nc_reply_data *)reply;
Michal Vaskob2583f12016-05-12 11:40:23 +0200261 if ((data_rpl->data->schema->nodetype != LYS_RPC) || strcmp(data_rpl->data->schema->name, "get-schema")
262 || !data_rpl->data->child || (data_rpl->data->child->schema->nodetype != LYS_ANYXML)) {
263 ERR("Session %u: unexpected data in reply to a <get-schema> RPC.", session->id);
264 nc_reply_free(reply);
265 return NULL;
266 }
Radek Krejci539efb62016-08-24 15:05:16 +0200267 get_schema_data = (struct lyd_node_anydata *)data_rpl->data->child;
268 switch (get_schema_data->value_type) {
269 case LYD_ANYDATA_CONSTSTRING:
270 case LYD_ANYDATA_STRING:
Michal Vaskob2583f12016-05-12 11:40:23 +0200271 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200272 break;
273 case LYD_ANYDATA_DATATREE:
274 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYP_WITHSIBLINGS);
275 break;
276 case LYD_ANYDATA_XML:
277 lyxml_print_mem(&model_data, get_schema_data->value.xml, LYXML_PRINT_SIBLINGS);
278 break;
Radek Krejci03438ec2016-09-21 14:12:26 +0200279 case LYD_ANYDATA_JSON:
280 case LYD_ANYDATA_JSOND:
Michal Vasko94868ed2016-09-29 10:33:38 +0200281 case LYD_ANYDATA_SXML:
282 case LYD_ANYDATA_SXMLD:
Radek Krejci03438ec2016-09-21 14:12:26 +0200283 ERRINT;
284 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200285 }
Michal Vasko086311b2016-01-08 09:53:11 +0100286 nc_reply_free(reply);
Michal Vaskoca4ad152016-03-03 15:50:45 +0100287 *free_model_data = free;
Michal Vasko3e7eaf42016-09-20 10:51:23 +0200288 *format = LYS_IN_YANG;
Michal Vasko086311b2016-01-08 09:53:11 +0100289
Michal Vasko086311b2016-01-08 09:53:11 +0100290 return model_data;
291}
292
293int
294nc_ctx_check_and_fill(struct nc_session *session)
295{
Michal Vaskoeee99412016-11-21 10:19:43 +0100296 const char *module_cpblt;
Michal Vaskoef578332016-01-25 13:20:09 +0100297 int i, get_schema_support = 0, ret = 0, r;
Michal Vaskocdeee432017-01-13 13:51:01 +0100298 ly_module_imp_clb old_clb = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100299 void *old_data = NULL;
300
Michal Vasko2e6defd2016-10-07 15:48:15 +0200301 assert(session->opts.client.cpblts && session->ctx);
Michal Vasko086311b2016-01-08 09:53:11 +0100302
303 /* check if get-schema is supported */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200304 for (i = 0; session->opts.client.cpblts[i]; ++i) {
305 if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100306 get_schema_support = 1;
307 break;
308 }
309 }
310
311 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
312 if (get_schema_support && !ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL)) {
313 if (lys_parse_path(session->ctx, SCHEMAS_DIR"/ietf-netconf-monitoring.yin", LYS_IN_YIN)) {
314 /* set module retrieval using <get-schema> */
Michal Vaskocdeee432017-01-13 13:51:01 +0100315 old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data);
316 ly_ctx_set_module_imp_clb(session->ctx, libyang_module_clb, session);
Michal Vasko086311b2016-01-08 09:53:11 +0100317 } else {
318 WRN("Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
319 }
320 }
321
322 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200323 if (ctx_check_and_load_ietf_netconf(session->ctx, session->opts.client.cpblts)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100324 if (old_clb) {
Michal Vaskocdeee432017-01-13 13:51:01 +0100325 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100326 }
Michal Vaskoef578332016-01-25 13:20:09 +0100327 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100328 }
329
330 /* load all other models */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200331 for (i = 0; session->opts.client.cpblts[i]; ++i) {
Michal Vaskoeee99412016-11-21 10:19:43 +0100332 module_cpblt = strstr(session->opts.client.cpblts[i], "module=");
333 /* this capability requires a module */
334 if (module_cpblt) {
335 r = ctx_check_and_load_model(session, module_cpblt);
336 if (r == -1) {
337 ret = -1;
338 break;
Michal Vaskoef578332016-01-25 13:20:09 +0100339 }
340
Michal Vaskoeee99412016-11-21 10:19:43 +0100341 /* failed to load schema, but let's try to find it using user callback (or locally, if not set),
342 * if it was using get-schema */
343 if (r == 1) {
344 if (get_schema_support) {
345 VRB("Trying to load the schema from a different source.");
346 /* works even if old_clb is NULL */
Michal Vaskocdeee432017-01-13 13:51:01 +0100347 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vaskoeee99412016-11-21 10:19:43 +0100348 r = ctx_check_and_load_model(session, module_cpblt);
349 }
Michal Vaskoef578332016-01-25 13:20:09 +0100350
Michal Vaskoeee99412016-11-21 10:19:43 +0100351 /* fail again (or no other way to try), too bad */
352 if (r) {
353 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
354 }
355
356 /* set get-schema callback back */
Michal Vaskocdeee432017-01-13 13:51:01 +0100357 ly_ctx_set_module_imp_clb(session->ctx, &libyang_module_clb, session);
Michal Vaskoeee99412016-11-21 10:19:43 +0100358 }
Michal Vaskoef578332016-01-25 13:20:09 +0100359 }
Michal Vasko086311b2016-01-08 09:53:11 +0100360 }
361
362 if (old_clb) {
Michal Vaskocdeee432017-01-13 13:51:01 +0100363 ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100364 }
Michal Vaskoeee99412016-11-21 10:19:43 +0100365 if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) {
366 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 +0100367 }
368 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100369}
370
371API struct nc_session *
372nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
373{
Michal Vaskod083db62016-01-19 10:31:29 +0100374 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +0100375
Michal Vasko45e53ae2016-04-07 11:46:03 +0200376 if (fdin < 0) {
377 ERRARG("fdin");
378 return NULL;
379 } else if (fdout < 0) {
380 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +0100381 return NULL;
382 }
383
384 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100385 session = nc_new_session(0);
Michal Vasko086311b2016-01-08 09:53:11 +0100386 if (!session) {
387 ERRMEM;
388 return NULL;
389 }
390 session->status = NC_STATUS_STARTING;
391 session->side = NC_CLIENT;
392
393 /* transport specific data */
394 session->ti_type = NC_TI_FD;
Michal Vaskoade892d2017-02-22 13:40:35 +0100395 pthread_mutex_init(session->ti_lock, NULL);
396 pthread_cond_init(session->ti_cond, NULL);
397 *session->ti_inuse = 0;
398
Michal Vasko086311b2016-01-08 09:53:11 +0100399 session->ti.fd.in = fdin;
400 session->ti.fd.out = fdout;
401
402 /* assign context (dicionary needed for handshake) */
403 if (!ctx) {
404 ctx = ly_ctx_new(SCHEMAS_DIR);
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100405 /* definitely should not happen, but be ready */
406 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
407 /* that's just it */
408 goto fail;
409 }
Michal Vasko086311b2016-01-08 09:53:11 +0100410 } else {
411 session->flags |= NC_SESSION_SHAREDCTX;
412 }
413 session->ctx = ctx;
414
415 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200416 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +0100417 goto fail;
418 }
419 session->status = NC_STATUS_RUNNING;
420
Michal Vaskoef578332016-01-25 13:20:09 +0100421 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +0100422 goto fail;
423 }
424
425 return session;
426
427fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100428 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100429 return NULL;
430}
431
432int
Michal Vaskof05562c2016-01-20 12:06:43 +0100433nc_sock_connect(const char* host, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100434{
Michal Vasko0190bc32016-03-02 15:47:49 +0100435 int i, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100436 struct addrinfo hints, *res_list, *res;
437 char port_s[6]; /* length of string representation of short int */
438
439 snprintf(port_s, 6, "%u", port);
440
441 /* Connect to a server */
442 memset(&hints, 0, sizeof hints);
443 hints.ai_family = AF_UNSPEC;
444 hints.ai_socktype = SOCK_STREAM;
445 hints.ai_protocol = IPPROTO_TCP;
446 i = getaddrinfo(host, port_s, &hints, &res_list);
447 if (i != 0) {
448 ERR("Unable to translate the host address (%s).", gai_strerror(i));
449 return -1;
450 }
451
Michal Vasko1f0b2ac2016-10-13 10:33:50 +0200452 for (res = res_list; res != NULL; res = res->ai_next) {
Michal Vasko086311b2016-01-08 09:53:11 +0100453 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
454 if (sock == -1) {
455 /* socket was not created, try another resource */
Michal Vasko1f0b2ac2016-10-13 10:33:50 +0200456 continue;
Michal Vasko086311b2016-01-08 09:53:11 +0100457 }
458
459 if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
460 /* network connection failed, try another resource */
Michal Vasko086311b2016-01-08 09:53:11 +0100461 close(sock);
462 sock = -1;
Michal Vasko1f0b2ac2016-10-13 10:33:50 +0200463 continue;
Michal Vasko086311b2016-01-08 09:53:11 +0100464 }
465
Michal Vasko0190bc32016-03-02 15:47:49 +0100466 /* make the socket non-blocking */
467 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
468 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100469 close(sock);
Michal Vaskoa8a66b62016-10-05 14:14:19 +0200470 freeaddrinfo(res_list);
Michal Vasko0190bc32016-03-02 15:47:49 +0100471 return -1;
472 }
473
Michal Vasko086311b2016-01-08 09:53:11 +0100474 /* we're done, network connection established */
475 break;
Michal Vasko086311b2016-01-08 09:53:11 +0100476 }
477
Michal Vasko29af44b2016-10-13 10:59:55 +0200478 if (sock != -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100479 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 +0100480 }
481 freeaddrinfo(res_list);
482
483 return sock;
484}
485
Michal Vasko086311b2016-01-08 09:53:11 +0100486static NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100487get_msg(struct nc_session *session, int timeout, uint64_t msgid, struct lyxml_elem **msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100488{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100489 int r;
Michal Vasko086311b2016-01-08 09:53:11 +0100490 char *ptr;
491 const char *str_msgid;
492 uint64_t cur_msgid;
493 struct lyxml_elem *xml;
Michal Vasko2518b6b2016-01-28 13:24:53 +0100494 struct nc_msg_cont *cont, **cont_ptr;
Michal Vasko086311b2016-01-08 09:53:11 +0100495 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
496
Michal Vaskoade892d2017-02-22 13:40:35 +0100497 r = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100498 if (r == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +0100499 /* error */
500 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100501 } else if (!r) {
Michal Vasko086311b2016-01-08 09:53:11 +0100502 /* timeout */
503 return NC_MSG_WOULDBLOCK;
504 }
505
506 /* try to get notification from the session's queue */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200507 if (!msgid && session->opts.client.notifs) {
508 cont = session->opts.client.notifs;
509 session->opts.client.notifs = cont->next;
Michal Vasko086311b2016-01-08 09:53:11 +0100510
Michal Vasko71ba2da2016-05-04 10:53:16 +0200511 xml = cont->msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100512 free(cont);
513
Michal Vasko71ba2da2016-05-04 10:53:16 +0200514 msgtype = NC_MSG_NOTIF;
Michal Vasko086311b2016-01-08 09:53:11 +0100515 }
516
517 /* try to get rpc-reply from the session's queue */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200518 if (msgid && session->opts.client.replies) {
519 cont = session->opts.client.replies;
520 session->opts.client.replies = cont->next;
Michal Vasko2518b6b2016-01-28 13:24:53 +0100521
Michal Vasko71ba2da2016-05-04 10:53:16 +0200522 xml = cont->msg;
523 free(cont);
Michal Vasko086311b2016-01-08 09:53:11 +0100524
Michal Vasko71ba2da2016-05-04 10:53:16 +0200525 msgtype = NC_MSG_REPLY;
Michal Vasko086311b2016-01-08 09:53:11 +0100526 }
527
Michal Vasko71ba2da2016-05-04 10:53:16 +0200528 if (!msgtype) {
529 /* read message from wire */
530 msgtype = nc_read_msg_poll(session, timeout, &xml);
531 }
Michal Vasko086311b2016-01-08 09:53:11 +0100532
533 /* we read rpc-reply, want a notif */
534 if (!msgid && (msgtype == NC_MSG_REPLY)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200535 cont_ptr = &session->opts.client.replies;
Michal Vasko086311b2016-01-08 09:53:11 +0100536 while (*cont_ptr) {
537 cont_ptr = &((*cont_ptr)->next);
538 }
539 *cont_ptr = malloc(sizeof **cont_ptr);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100540 if (!*cont_ptr) {
541 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +0100542 nc_session_unlock(session, timeout, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100543 lyxml_free(session->ctx, xml);
544 return NC_MSG_ERROR;
545 }
Michal Vasko086311b2016-01-08 09:53:11 +0100546 (*cont_ptr)->msg = xml;
547 (*cont_ptr)->next = NULL;
548 }
549
550 /* we read notif, want a rpc-reply */
551 if (msgid && (msgtype == NC_MSG_NOTIF)) {
Michal Vasko3486a7c2017-03-03 13:28:07 +0100552 if (!session->opts.client.ntf_tid) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100553 pthread_mutex_unlock(session->ti_lock);
Michal Vaskod083db62016-01-19 10:31:29 +0100554 ERR("Session %u: received a <notification> but session is not subscribed.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100555 lyxml_free(session->ctx, xml);
Michal Vasko2518b6b2016-01-28 13:24:53 +0100556 return NC_MSG_ERROR;
Michal Vasko3486a7c2017-03-03 13:28:07 +0100557 }
Michal Vasko086311b2016-01-08 09:53:11 +0100558
Michal Vasko2e6defd2016-10-07 15:48:15 +0200559 cont_ptr = &session->opts.client.notifs;
Michal Vasko086311b2016-01-08 09:53:11 +0100560 while (*cont_ptr) {
561 cont_ptr = &((*cont_ptr)->next);
562 }
563 *cont_ptr = malloc(sizeof **cont_ptr);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100564 if (!cont_ptr) {
565 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +0100566 nc_session_unlock(session, timeout, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100567 lyxml_free(session->ctx, xml);
568 return NC_MSG_ERROR;
569 }
Michal Vasko086311b2016-01-08 09:53:11 +0100570 (*cont_ptr)->msg = xml;
571 (*cont_ptr)->next = NULL;
572 }
573
Michal Vaskoade892d2017-02-22 13:40:35 +0100574 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +0100575
576 switch (msgtype) {
577 case NC_MSG_NOTIF:
Michal Vasko2518b6b2016-01-28 13:24:53 +0100578 if (!msgid) {
579 *msg = xml;
Michal Vasko086311b2016-01-08 09:53:11 +0100580 }
Michal Vasko086311b2016-01-08 09:53:11 +0100581 break;
582
583 case NC_MSG_REPLY:
Michal Vasko2518b6b2016-01-28 13:24:53 +0100584 if (msgid) {
Michal Vasko71ba2da2016-05-04 10:53:16 +0200585 /* check message-id */
586 str_msgid = lyxml_get_attr(xml, "message-id", NULL);
587 if (!str_msgid) {
588 ERR("Session %u: received a <rpc-reply> without a message-id.", session->id);
589 msgtype = NC_MSG_REPLY_ERR_MSGID;
590 } else {
591 cur_msgid = strtoul(str_msgid, &ptr, 10);
592 if (cur_msgid != msgid) {
593 ERR("Session %u: received a <rpc-reply> with an unexpected message-id \"%s\".",
594 session->id, str_msgid);
595 msgtype = NC_MSG_REPLY_ERR_MSGID;
596 }
597 }
Michal Vasko2518b6b2016-01-28 13:24:53 +0100598 *msg = xml;
Michal Vasko086311b2016-01-08 09:53:11 +0100599 }
Michal Vasko086311b2016-01-08 09:53:11 +0100600 break;
601
602 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100603 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100604 lyxml_free(session->ctx, xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +0200605 msgtype = NC_MSG_ERROR;
606 break;
Michal Vasko086311b2016-01-08 09:53:11 +0100607
608 case NC_MSG_RPC:
Michal Vaskod083db62016-01-19 10:31:29 +0100609 ERR("Session %u: received <rpc> from a NETCONF server.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100610 lyxml_free(session->ctx, xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +0200611 msgtype = NC_MSG_ERROR;
612 break;
Michal Vasko086311b2016-01-08 09:53:11 +0100613
614 default:
615 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
616 * NC_MSG_NONE is not returned by nc_read_msg()
617 */
618 break;
619 }
620
621 return msgtype;
622}
623
624/* cannot strictly fail, but does not need to fill any error parameter at all */
625static void
626parse_rpc_error(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_err *err)
627{
628 struct lyxml_elem *iter, *next, *info;
629
630 LY_TREE_FOR(xml->child, iter) {
631 if (!iter->ns) {
632 if (iter->content) {
633 WRN("<rpc-error> child \"%s\" with value \"%s\" without namespace.", iter->name, iter->content);
634 } else {
635 WRN("<rpc-error> child \"%s\" without namespace.", iter->name);
636 }
637 continue;
638 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
639 if (iter->content) {
640 WRN("<rpc-error> child \"%s\" with value \"%s\" in an unknown namespace \"%s\".",
641 iter->name, iter->content, iter->ns->value);
642 } else {
643 WRN("<rpc-error> child \"%s\" in an unknown namespace \"%s\".", iter->name, iter->ns->value);
644 }
645 continue;
646 }
647
648 if (!strcmp(iter->name, "error-type")) {
649 if (!iter->content || (strcmp(iter->content, "transport") && strcmp(iter->content, "rpc")
650 && strcmp(iter->content, "protocol") && strcmp(iter->content, "application"))) {
651 WRN("<rpc-error> <error-type> unknown value \"%s\".", (iter->content ? iter->content : ""));
652 } else if (err->type) {
653 WRN("<rpc-error> <error-type> duplicated.");
654 } else {
655 err->type = lydict_insert(ctx, iter->content, 0);
656 }
657 } else if (!strcmp(iter->name, "error-tag")) {
658 if (!iter->content || (strcmp(iter->content, "in-use") && strcmp(iter->content, "invalid-value")
659 && strcmp(iter->content, "too-big") && strcmp(iter->content, "missing-attribute")
660 && strcmp(iter->content, "bad-attribute") && strcmp(iter->content, "unknown-attribute")
661 && strcmp(iter->content, "missing-element") && strcmp(iter->content, "bad-element")
662 && strcmp(iter->content, "unknown-element") && strcmp(iter->content, "unknown-namespace")
663 && strcmp(iter->content, "access-denied") && strcmp(iter->content, "lock-denied")
664 && strcmp(iter->content, "resource-denied") && strcmp(iter->content, "rollback-failed")
665 && strcmp(iter->content, "data-exists") && strcmp(iter->content, "data-missing")
666 && strcmp(iter->content, "operation-not-supported") && strcmp(iter->content, "operation-failed")
667 && strcmp(iter->content, "malformed-message"))) {
668 WRN("<rpc-error> <error-tag> unknown value \"%s\".", (iter->content ? iter->content : ""));
669 } else if (err->tag) {
670 WRN("<rpc-error> <error-tag> duplicated.");
671 } else {
672 err->tag = lydict_insert(ctx, iter->content, 0);
673 }
674 } else if (!strcmp(iter->name, "error-severity")) {
675 if (!iter->content || (strcmp(iter->content, "error") && strcmp(iter->content, "warning"))) {
676 WRN("<rpc-error> <error-severity> unknown value \"%s\".", (iter->content ? iter->content : ""));
677 } else if (err->severity) {
678 WRN("<rpc-error> <error-severity> duplicated.");
679 } else {
680 err->severity = lydict_insert(ctx, iter->content, 0);
681 }
682 } else if (!strcmp(iter->name, "error-app-tag")) {
683 if (err->apptag) {
684 WRN("<rpc-error> <error-app-tag> duplicated.");
685 } else {
686 err->apptag = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
687 }
688 } else if (!strcmp(iter->name, "error-path")) {
689 if (err->path) {
690 WRN("<rpc-error> <error-path> duplicated.");
691 } else {
692 err->path = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
693 }
694 } else if (!strcmp(iter->name, "error-message")) {
695 if (err->message) {
696 WRN("<rpc-error> <error-message> duplicated.");
697 } else {
698 err->message_lang = lyxml_get_attr(iter, "xml:lang", NULL);
699 if (!err->message_lang) {
700 VRB("<rpc-error> <error-message> without the recommended \"xml:lang\" attribute.");
701 }
702 err->message = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
703 }
704 } else if (!strcmp(iter->name, "error-info")) {
705 LY_TREE_FOR_SAFE(iter->child, next, info) {
706 if (info->ns && !strcmp(info->ns->value, NC_NS_BASE)) {
707 if (!strcmp(info->name, "session-id")) {
708 if (err->sid) {
709 WRN("<rpc-error> <error-info> <session-id> duplicated.");
710 } else {
711 err->sid = lydict_insert(ctx, (info->content ? info->content : ""), 0);
712 }
713 } else if (!strcmp(info->name, "bad-attr")) {
714 ++err->attr_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100715 err->attr = nc_realloc(err->attr, err->attr_count * sizeof *err->attr);
716 if (!err->attr) {
717 ERRMEM;
718 return;
719 }
Michal Vasko086311b2016-01-08 09:53:11 +0100720 err->attr[err->attr_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
721 } else if (!strcmp(info->name, "bad-element")) {
722 ++err->elem_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100723 err->elem = nc_realloc(err->elem, err->elem_count * sizeof *err->elem);
724 if (!err->elem) {
725 ERRMEM;
726 return;
727 }
Michal Vasko086311b2016-01-08 09:53:11 +0100728 err->elem[err->elem_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
729 } else if (!strcmp(info->name, "bad-namespace")) {
730 ++err->ns_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100731 err->ns = nc_realloc(err->ns, err->ns_count * sizeof *err->ns);
732 if (!err->ns) {
733 ERRMEM;
734 return;
735 }
Michal Vasko086311b2016-01-08 09:53:11 +0100736 err->ns[err->ns_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
737 } else {
738 if (info->content) {
739 WRN("<rpc-error> <error-info> unknown child \"%s\" with value \"%s\".",
740 info->name, info->content);
741 } else {
742 WRN("<rpc-error> <error-info> unknown child \"%s\".", info->name);
743 }
744 }
745 } else {
746 lyxml_unlink(ctx, info);
747 ++err->other_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100748 err->other = nc_realloc(err->other, err->other_count * sizeof *err->other);
749 if (!err->other) {
750 ERRMEM;
751 return;
752 }
Michal Vasko086311b2016-01-08 09:53:11 +0100753 err->other[err->other_count - 1] = info;
754 }
755 }
756 } else {
757 if (iter->content) {
758 WRN("<rpc-error> unknown child \"%s\" with value \"%s\".", iter->name, iter->content);
759 } else {
760 WRN("<rpc-error> unknown child \"%s\".", iter->name);
761 }
762 }
763 }
764}
765
766static struct nc_reply *
Michal Vaskoeb7080e2016-02-18 13:27:05 +0100767parse_reply(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_rpc *rpc, int parseroptions)
Michal Vasko086311b2016-01-08 09:53:11 +0100768{
769 struct lyxml_elem *iter;
Michal Vaskoe1708602016-10-18 12:17:22 +0200770 struct lyd_node *data = NULL, *rpc_act = NULL;
Michal Vasko1a38c862016-01-15 15:50:07 +0100771 struct nc_client_reply_error *error_rpl;
Michal Vasko086311b2016-01-08 09:53:11 +0100772 struct nc_reply_data *data_rpl;
773 struct nc_reply *reply = NULL;
Michal Vasko90e8e692016-07-13 12:27:57 +0200774 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +0100775 int i;
776
777 if (!xml->child) {
778 ERR("An empty <rpc-reply>.");
779 return NULL;
780 }
781
782 /* rpc-error */
783 if (!strcmp(xml->child->name, "rpc-error") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
784 /* count and check elements */
785 i = 0;
786 LY_TREE_FOR(xml->child, iter) {
787 if (strcmp(iter->name, "rpc-error")) {
788 ERR("<rpc-reply> content mismatch (<rpc-error> and <%s>).", iter->name);
789 return NULL;
790 } else if (!iter->ns) {
791 ERR("<rpc-reply> content mismatch (<rpc-error> without namespace).");
792 return NULL;
793 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
794 ERR("<rpc-reply> content mismatch (<rpc-error> with NS \"%s\").", iter->ns->value);
795 return NULL;
796 }
797 ++i;
798 }
799
800 error_rpl = malloc(sizeof *error_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100801 if (!error_rpl) {
802 ERRMEM;
803 return NULL;
804 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100805 error_rpl->type = NC_RPL_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100806 error_rpl->err = calloc(i, sizeof *error_rpl->err);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100807 if (!error_rpl->err) {
808 ERRMEM;
809 free(error_rpl);
810 return NULL;
811 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100812 error_rpl->count = i;
Michal Vasko1a38c862016-01-15 15:50:07 +0100813 error_rpl->ctx = ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100814 reply = (struct nc_reply *)error_rpl;
815
816 i = 0;
817 LY_TREE_FOR(xml->child, iter) {
818 parse_rpc_error(ctx, iter, error_rpl->err + i);
819 ++i;
820 }
821
822 /* ok */
823 } else if (!strcmp(xml->child->name, "ok") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
824 if (xml->child->next) {
825 ERR("<rpc-reply> content mismatch (<ok> and <%s>).", xml->child->next->name);
826 return NULL;
827 }
828 reply = malloc(sizeof *reply);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100829 if (!reply) {
830 ERRMEM;
831 return NULL;
832 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100833 reply->type = NC_RPL_OK;
Michal Vasko086311b2016-01-08 09:53:11 +0100834
835 /* some RPC output */
836 } else {
837 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +0200838 case NC_RPC_ACT_GENERIC:
839 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +0100840
841 if (rpc_gen->has_data) {
Michal Vaskoe1708602016-10-18 12:17:22 +0200842 rpc_act = rpc_gen->content.data;
Michal Vasko086311b2016-01-08 09:53:11 +0100843 } else {
Michal Vaskoeee99412016-11-21 10:19:43 +0100844 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 +0200845 if (!rpc_act) {
Michal Vasko90e8e692016-07-13 12:27:57 +0200846 ERR("Failed to parse a generic RPC/action XML.");
Michal Vasko086311b2016-01-08 09:53:11 +0100847 return NULL;
848 }
Michal Vasko90e8e692016-07-13 12:27:57 +0200849 }
Michal Vasko086311b2016-01-08 09:53:11 +0100850 break;
851
852 case NC_RPC_GETCONFIG:
853 case NC_RPC_GET:
Michal Vasko13ed2942016-02-29 16:21:00 +0100854 if (!xml->child->child) {
855 /* we did not receive any data */
856 data_rpl = malloc(sizeof *data_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100857 if (!data_rpl) {
858 ERRMEM;
859 return NULL;
860 }
Michal Vasko13ed2942016-02-29 16:21:00 +0100861 data_rpl->type = NC_RPL_DATA;
862 data_rpl->data = NULL;
863 return (struct nc_reply *)data_rpl;
864 }
865
Michal Vasko086311b2016-01-08 09:53:11 +0100866 /* special treatment */
Michal Vasko0a5ae9a2016-11-15 11:54:47 +0100867 data = lyd_parse_xml(ctx, &xml->child->child,
868 LYD_OPT_DESTRUCT | (rpc->type == NC_RPC_GETCONFIG ? LYD_OPT_GETCONFIG : LYD_OPT_GET)
Michal Vaskoeee99412016-11-21 10:19:43 +0100869 | parseroptions);
Michal Vasko086311b2016-01-08 09:53:11 +0100870 if (!data) {
871 ERR("Failed to parse <%s> reply.", (rpc->type == NC_RPC_GETCONFIG ? "get-config" : "get"));
872 return NULL;
873 }
874 break;
875
876 case NC_RPC_GETSCHEMA:
Michal Vaskoe1708602016-10-18 12:17:22 +0200877 rpc_act = lyd_new(NULL, ly_ctx_get_module(ctx, "ietf-netconf-monitoring", NULL), "get-schema");
878 if (!rpc_act) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100879 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100880 return NULL;
881 }
882 break;
883
884 case NC_RPC_EDIT:
885 case NC_RPC_COPY:
886 case NC_RPC_DELETE:
887 case NC_RPC_LOCK:
888 case NC_RPC_UNLOCK:
889 case NC_RPC_KILL:
890 case NC_RPC_COMMIT:
891 case NC_RPC_DISCARD:
892 case NC_RPC_CANCEL:
893 case NC_RPC_VALIDATE:
894 case NC_RPC_SUBSCRIBE:
895 /* there is no output defined */
896 ERR("Unexpected data reply (root elem \"%s\").", xml->child->name);
897 return NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100898 default:
899 ERRINT;
900 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100901 }
902
903 data_rpl = malloc(sizeof *data_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100904 if (!data_rpl) {
905 ERRMEM;
906 return NULL;
907 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908 data_rpl->type = NC_RPL_DATA;
Michal Vasko086311b2016-01-08 09:53:11 +0100909 if (!data) {
Michal Vasko68b3f292016-09-16 12:00:32 +0200910 data_rpl->data = lyd_parse_xml(ctx, &xml->child, LYD_OPT_RPCREPLY | LYD_OPT_DESTRUCT | parseroptions,
Michal Vaskocc3d52b2016-10-18 12:17:22 +0200911 rpc_act, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100912 } else {
913 /* <get>, <get-config> */
914 data_rpl->data = data;
915 }
Michal Vaskoe1708602016-10-18 12:17:22 +0200916 lyd_free_withsiblings(rpc_act);
Michal Vasko086311b2016-01-08 09:53:11 +0100917 if (!data_rpl->data) {
918 ERR("Failed to parse <rpc-reply>.");
919 free(data_rpl);
920 return NULL;
921 }
922 reply = (struct nc_reply *)data_rpl;
923 }
924
925 return reply;
926}
927
Radek Krejci53691be2016-02-22 13:58:37 +0100928#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +0100929
Michal Vasko3031aae2016-01-27 16:07:18 +0100930int
931nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
932{
933 int sock;
934
Michal Vasko45e53ae2016-04-07 11:46:03 +0200935 if (!address) {
936 ERRARG("address");
937 return -1;
938 } else if (!port) {
939 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +0100940 return -1;
941 }
942
943 sock = nc_sock_listen(address, port);
944 if (sock == -1) {
945 return -1;
946 }
947
948 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100949 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
950 if (!client_opts.ch_binds) {
951 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +0100952 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100953 return -1;
954 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100955
Michal Vasko2e6defd2016-10-07 15:48:15 +0200956 client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti);
957 if (!client_opts.ch_bind_ti) {
958 ERRMEM;
959 close(sock);
960 return -1;
961 }
962 client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti;
963
Michal Vasko3031aae2016-01-27 16:07:18 +0100964 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100965 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
966 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +0100967 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100968 return -1;
969 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100970 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
971 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200972 client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +0100973
974 return 0;
975}
976
977int
978nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
979{
980 uint32_t i;
981 int ret = -1;
982
983 if (!address && !port && !ti) {
984 for (i = 0; i < client_opts.ch_bind_count; ++i) {
985 close(client_opts.ch_binds[i].sock);
986 free((char *)client_opts.ch_binds[i].address);
987
988 ret = 0;
989 }
990 free(client_opts.ch_binds);
991 client_opts.ch_binds = NULL;
992 client_opts.ch_bind_count = 0;
993 } else {
994 for (i = 0; i < client_opts.ch_bind_count; ++i) {
995 if ((!address || !strcmp(client_opts.ch_binds[i].address, address))
996 && (!port || (client_opts.ch_binds[i].port == port))
Michal Vasko2e6defd2016-10-07 15:48:15 +0200997 && (!ti || (client_opts.ch_bind_ti[i] == ti))) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100998 close(client_opts.ch_binds[i].sock);
999 free((char *)client_opts.ch_binds[i].address);
1000
1001 --client_opts.ch_bind_count;
Michal Vasko66c762a2016-10-13 10:34:14 +02001002 if (!client_opts.ch_bind_count) {
1003 free(client_opts.ch_binds);
1004 client_opts.ch_binds = NULL;
1005 } else if (i < client_opts.ch_bind_count) {
1006 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1007 client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count];
1008 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001009
1010 ret = 0;
1011 }
1012 }
1013 }
1014
1015 return ret;
1016}
1017
1018API int
1019nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1020{
1021 int sock;
1022 char *host = NULL;
1023 uint16_t port, idx;
1024
Michal Vasko45e53ae2016-04-07 11:46:03 +02001025 if (!client_opts.ch_binds) {
1026 ERRINIT;
1027 return -1;
1028 } else if (!session) {
1029 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001030 return -1;
1031 }
1032
1033 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1034
Michal Vasko50456e82016-02-02 12:16:08 +01001035 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001036 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001037 return sock;
1038 }
1039
Radek Krejci53691be2016-02-22 13:58:37 +01001040#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001041 if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001042 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001043 } else
1044#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001045#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001046 if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001047 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001048 } else
1049#endif
1050 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001051 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001052 *session = NULL;
1053 }
1054
1055 free(host);
1056
1057 if (!(*session)) {
1058 return -1;
1059 }
1060
1061 return 1;
1062}
1063
Radek Krejci53691be2016-02-22 13:58:37 +01001064#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001065
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001066API const char * const *
Michal Vaskobdfb5242016-05-24 09:11:01 +02001067nc_session_get_cpblts(const struct nc_session *session)
1068{
1069 if (!session) {
1070 ERRARG("session");
1071 return NULL;
1072 }
1073
Michal Vasko1b2ddc92017-05-24 08:59:49 +02001074 return (const char * const *)session->opts.client.cpblts;
Michal Vaskobdfb5242016-05-24 09:11:01 +02001075}
1076
1077API const char *
1078nc_session_cpblt(const struct nc_session *session, const char *capab)
1079{
1080 int i, len;
1081
1082 if (!session) {
1083 ERRARG("session");
1084 return NULL;
1085 } else if (!capab) {
1086 ERRARG("capab");
1087 return NULL;
1088 }
1089
1090 len = strlen(capab);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001091 for (i = 0; session->opts.client.cpblts[i]; ++i) {
1092 if (!strncmp(session->opts.client.cpblts[i], capab, len)) {
1093 return session->opts.client.cpblts[i];
Michal Vaskobdfb5242016-05-24 09:11:01 +02001094 }
1095 }
1096
1097 return NULL;
1098}
1099
Michal Vasko9cd26a82016-05-31 08:58:48 +02001100API int
1101nc_session_ntf_thread_running(const struct nc_session *session)
1102{
Michal Vasko2e6defd2016-10-07 15:48:15 +02001103 if (!session || (session->side != NC_CLIENT)) {
Michal Vasko9cd26a82016-05-31 08:58:48 +02001104 ERRARG("session");
1105 return 0;
1106 }
1107
Michal Vasko2e6defd2016-10-07 15:48:15 +02001108 return session->opts.client.ntf_tid ? 1 : 0;
Michal Vasko9cd26a82016-05-31 08:58:48 +02001109}
1110
Michal Vaskob7558c52016-02-26 15:04:19 +01001111API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001112nc_client_init(void)
1113{
1114 nc_init();
1115}
1116
1117API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001118nc_client_destroy(void)
1119{
Michal Vasko7f1c0ef2016-03-11 11:13:06 +01001120 nc_client_set_schema_searchpath(NULL);
Michal Vaskob7558c52016-02-26 15:04:19 +01001121#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
1122 nc_client_ch_del_bind(NULL, 0, 0);
1123#endif
1124#ifdef NC_ENABLED_SSH
1125 nc_client_ssh_destroy_opts();
1126#endif
Michal Vaskoc979d3a2016-02-26 15:26:21 +01001127#ifdef NC_ENABLED_TLS
Michal Vaskob7558c52016-02-26 15:04:19 +01001128 nc_client_tls_destroy_opts();
1129#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001130 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001131}
1132
Michal Vasko086311b2016-01-08 09:53:11 +01001133API NC_MSG_TYPE
Michal Vaskoeb7080e2016-02-18 13:27:05 +01001134nc_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 +01001135{
1136 struct lyxml_elem *xml;
1137 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1138
Michal Vasko45e53ae2016-04-07 11:46:03 +02001139 if (!session) {
1140 ERRARG("session");
1141 return NC_MSG_ERROR;
1142 } else if (!rpc) {
1143 ERRARG("rpc");
1144 return NC_MSG_ERROR;
1145 } else if (!reply) {
1146 ERRARG("reply");
1147 return NC_MSG_ERROR;
1148 } else if (parseroptions & LYD_OPT_TYPEMASK) {
1149 ERRARG("parseroptions");
Michal Vasko086311b2016-01-08 09:53:11 +01001150 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001151 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001152 ERR("Session %u: invalid session to receive RPC replies.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001153 return NC_MSG_ERROR;
1154 }
Michal Vaskoeb7080e2016-02-18 13:27:05 +01001155 parseroptions &= ~(LYD_OPT_DESTRUCT | LYD_OPT_NOSIBLINGS);
Michal Vaskoeee99412016-11-21 10:19:43 +01001156 if (!(session->flags & NC_SESSION_CLIENT_NOT_STRICT)) {
1157 parseroptions &= LYD_OPT_STRICT;
1158 }
Michal Vasko41adf392017-01-17 10:39:04 +01001159 /* no mechanism to check external dependencies is provided */
1160 parseroptions|= LYD_OPT_NOEXTDEPS;
Michal Vasko086311b2016-01-08 09:53:11 +01001161 *reply = NULL;
1162
1163 msgtype = get_msg(session, timeout, msgid, &xml);
Michal Vasko086311b2016-01-08 09:53:11 +01001164
Michal Vasko71ba2da2016-05-04 10:53:16 +02001165 if ((msgtype == NC_MSG_REPLY) || (msgtype == NC_MSG_REPLY_ERR_MSGID)) {
Michal Vaskoeee99412016-11-21 10:19:43 +01001166 *reply = parse_reply(session->ctx, xml, rpc, parseroptions);
Michal Vasko086311b2016-01-08 09:53:11 +01001167 lyxml_free(session->ctx, xml);
1168 if (!(*reply)) {
1169 return NC_MSG_ERROR;
1170 }
1171 }
1172
1173 return msgtype;
1174}
1175
1176API NC_MSG_TYPE
1177nc_recv_notif(struct nc_session *session, int timeout, struct nc_notif **notif)
1178{
1179 struct lyxml_elem *xml, *ev_time;
1180 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1181
Michal Vasko45e53ae2016-04-07 11:46:03 +02001182 if (!session) {
1183 ERRARG("session");
1184 return NC_MSG_ERROR;
1185 } else if (!notif) {
1186 ERRARG("notif");
Michal Vasko086311b2016-01-08 09:53:11 +01001187 return NC_MSG_ERROR;
1188 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01001189 ERR("Session %u: invalid session to receive Notifications.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001190 return NC_MSG_ERROR;
1191 }
1192
1193 msgtype = get_msg(session, timeout, 0, &xml);
1194
1195 if (msgtype == NC_MSG_NOTIF) {
1196 *notif = calloc(1, sizeof **notif);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001197 if (!*notif) {
1198 ERRMEM;
1199 lyxml_free(session->ctx, xml);
1200 return NC_MSG_ERROR;
1201 }
Michal Vasko086311b2016-01-08 09:53:11 +01001202
1203 /* eventTime */
1204 LY_TREE_FOR(xml->child, ev_time) {
1205 if (!strcmp(ev_time->name, "eventTime")) {
1206 (*notif)->datetime = lydict_insert(session->ctx, ev_time->content, 0);
1207 /* lyd_parse does not know this element */
1208 lyxml_free(session->ctx, ev_time);
1209 break;
1210 }
1211 }
1212 if (!(*notif)->datetime) {
Michal Vaskod083db62016-01-19 10:31:29 +01001213 ERR("Session %u: notification is missing the \"eventTime\" element.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001214 goto fail;
1215 }
1216
1217 /* notification body */
Michal Vasko41adf392017-01-17 10:39:04 +01001218 (*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 +01001219 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001220 lyxml_free(session->ctx, xml);
1221 xml = NULL;
1222 if (!(*notif)->tree) {
Michal Vaskod083db62016-01-19 10:31:29 +01001223 ERR("Session %u: failed to parse a new notification.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001224 goto fail;
1225 }
1226 }
1227
1228 return msgtype;
1229
1230fail:
1231 lydict_remove(session->ctx, (*notif)->datetime);
1232 lyd_free((*notif)->tree);
1233 free(*notif);
1234 *notif = NULL;
1235 lyxml_free(session->ctx, xml);
1236
1237 return NC_MSG_ERROR;
1238}
1239
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001240static void *
1241nc_recv_notif_thread(void *arg)
1242{
1243 struct nc_ntf_thread_arg *ntarg;
1244 struct nc_session *session;
1245 void (*notif_clb)(struct nc_session *session, const struct nc_notif *notif);
1246 struct nc_notif *notif;
1247 NC_MSG_TYPE msgtype;
1248
1249 ntarg = (struct nc_ntf_thread_arg *)arg;
1250 session = ntarg->session;
1251 notif_clb = ntarg->notif_clb;
1252 free(ntarg);
1253
Michal Vasko2e6defd2016-10-07 15:48:15 +02001254 while (session->opts.client.ntf_tid) {
Michal vasko4e1a36c2016-10-05 13:04:37 +02001255 msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &notif);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001256 if (msgtype == NC_MSG_NOTIF) {
1257 notif_clb(session, notif);
Michal Vaskof0537d82016-01-29 14:42:38 +01001258 if (!strcmp(notif->tree->schema->name, "notificationComplete")
1259 && !strcmp(notif->tree->schema->module->name, "nc-notifications")) {
1260 nc_notif_free(notif);
1261 break;
1262 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001263 nc_notif_free(notif);
Michal Vasko0651c902016-05-19 15:55:42 +02001264 } else if (msgtype == NC_MSG_ERROR) {
1265 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001266 }
1267
1268 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
1269 }
1270
Michal Vasko0651c902016-05-19 15:55:42 +02001271 VRB("Session %u: notification thread exit.", session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001272 session->opts.client.ntf_tid = NULL;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001273 return NULL;
1274}
1275
1276API int
1277nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session, const struct nc_notif *notif))
1278{
1279 struct nc_ntf_thread_arg *ntarg;
1280 int ret;
1281
Michal Vasko45e53ae2016-04-07 11:46:03 +02001282 if (!session) {
1283 ERRARG("session");
1284 return -1;
1285 } else if (!notif_clb) {
1286 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001287 return -1;
1288 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
1289 ERR("Session %u: invalid session to receive Notifications.", session->id);
1290 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001291 } else if (session->opts.client.ntf_tid) {
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001292 ERR("Session %u: separate notification thread is already running.", session->id);
1293 return -1;
1294 }
1295
1296 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001297 if (!ntarg) {
1298 ERRMEM;
1299 return -1;
1300 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001301 ntarg->session = session;
1302 ntarg->notif_clb = notif_clb;
1303
1304 /* just so that nc_recv_notif_thread() does not immediately exit, the value does not matter */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001305 session->opts.client.ntf_tid = malloc(sizeof *session->opts.client.ntf_tid);
1306 if (!session->opts.client.ntf_tid) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001307 ERRMEM;
1308 free(ntarg);
1309 return -1;
1310 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001311
Michal Vasko2e6defd2016-10-07 15:48:15 +02001312 ret = pthread_create((pthread_t *)session->opts.client.ntf_tid, NULL, nc_recv_notif_thread, ntarg);
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001313 if (ret) {
1314 ERR("Session %u: failed to create a new thread (%s).", strerror(errno));
1315 free(ntarg);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001316 free((pthread_t *)session->opts.client.ntf_tid);
1317 session->opts.client.ntf_tid = NULL;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001318 return -1;
1319 }
1320
1321 return 0;
1322}
1323
Michal Vasko086311b2016-01-08 09:53:11 +01001324API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001325nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001326{
1327 NC_MSG_TYPE r;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001328 int ret;
Michal Vasko90e8e692016-07-13 12:27:57 +02001329 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01001330 struct nc_rpc_getconfig *rpc_gc;
1331 struct nc_rpc_edit *rpc_e;
1332 struct nc_rpc_copy *rpc_cp;
1333 struct nc_rpc_delete *rpc_del;
1334 struct nc_rpc_lock *rpc_lock;
1335 struct nc_rpc_get *rpc_g;
1336 struct nc_rpc_kill *rpc_k;
1337 struct nc_rpc_commit *rpc_com;
1338 struct nc_rpc_cancel *rpc_can;
1339 struct nc_rpc_validate *rpc_val;
1340 struct nc_rpc_getschema *rpc_gs;
1341 struct nc_rpc_subscribe *rpc_sub;
1342 struct lyd_node *data, *node;
Michal Vasko9d8bee62016-03-03 10:58:24 +01001343 const struct lys_module *ietfnc = NULL, *ietfncmon, *notifs, *ietfncwd = NULL;
Radek Krejci539efb62016-08-24 15:05:16 +02001344 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01001345 uint64_t cur_msgid;
1346
Michal Vasko45e53ae2016-04-07 11:46:03 +02001347 if (!session) {
1348 ERRARG("session");
1349 return NC_MSG_ERROR;
1350 } else if (!rpc) {
1351 ERRARG("rpc");
1352 return NC_MSG_ERROR;
1353 } else if (!msgid) {
1354 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01001355 return NC_MSG_ERROR;
1356 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01001357 ERR("Session %u: invalid session to send RPCs.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001358 return NC_MSG_ERROR;
1359 }
1360
Michal Vasko90e8e692016-07-13 12:27:57 +02001361 if ((rpc->type != NC_RPC_GETSCHEMA) && (rpc->type != NC_RPC_ACT_GENERIC) && (rpc->type != NC_RPC_SUBSCRIBE)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001362 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
1363 if (!ietfnc) {
Michal Vasko086cd572017-01-12 12:19:05 +01001364 ERR("Session %u: missing \"ietf-netconf\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001365 return NC_MSG_ERROR;
1366 }
1367 }
1368
1369 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001370 case NC_RPC_ACT_GENERIC:
1371 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01001372
1373 if (rpc_gen->has_data) {
1374 data = rpc_gen->content.data;
1375 } else {
Michal Vasko41adf392017-01-17 10:39:04 +01001376 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 +01001377 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001378 }
1379 break;
1380
1381 case NC_RPC_GETCONFIG:
1382 rpc_gc = (struct nc_rpc_getconfig *)rpc;
1383
1384 data = lyd_new(NULL, ietfnc, "get-config");
1385 node = lyd_new(data, ietfnc, "source");
1386 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_gc->source], NULL);
1387 if (!node) {
1388 lyd_free(data);
1389 return NC_MSG_ERROR;
1390 }
1391 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01001392 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001393 node = lyd_new_anydata(data, ietfnc, "filter", rpc_gc->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01001394 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01001395 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02001396 node = lyd_new_anydata(data, ietfnc, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001397 lyd_insert_attr(node, NULL, "type", "xpath");
1398 lyd_insert_attr(node, NULL, "select", rpc_gc->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01001399 }
1400 if (!node) {
1401 lyd_free(data);
1402 return NC_MSG_ERROR;
1403 }
1404 }
1405
1406 if (rpc_gc->wd_mode) {
1407 if (!ietfncwd) {
1408 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1409 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01001410 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001411 return NC_MSG_ERROR;
1412 }
1413 }
1414 switch (rpc_gc->wd_mode) {
1415 case NC_WD_UNKNOWN:
1416 /* cannot get here */
1417 break;
1418 case NC_WD_ALL:
1419 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1420 break;
1421 case NC_WD_ALL_TAG:
1422 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1423 break;
1424 case NC_WD_TRIM:
1425 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1426 break;
1427 case NC_WD_EXPLICIT:
1428 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1429 break;
1430 }
1431 if (!node) {
1432 lyd_free(data);
1433 return NC_MSG_ERROR;
1434 }
1435 }
1436 break;
1437
1438 case NC_RPC_EDIT:
1439 rpc_e = (struct nc_rpc_edit *)rpc;
1440
1441 data = lyd_new(NULL, ietfnc, "edit-config");
1442 node = lyd_new(data, ietfnc, "target");
1443 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_e->target], NULL);
1444 if (!node) {
1445 lyd_free(data);
1446 return NC_MSG_ERROR;
1447 }
1448
1449 if (rpc_e->default_op) {
1450 node = lyd_new_leaf(data, ietfnc, "default-operation", rpcedit_dfltop2str[rpc_e->default_op]);
1451 if (!node) {
1452 lyd_free(data);
1453 return NC_MSG_ERROR;
1454 }
1455 }
1456
1457 if (rpc_e->test_opt) {
1458 node = lyd_new_leaf(data, ietfnc, "test-option", rpcedit_testopt2str[rpc_e->test_opt]);
1459 if (!node) {
1460 lyd_free(data);
1461 return NC_MSG_ERROR;
1462 }
1463 }
1464
1465 if (rpc_e->error_opt) {
1466 node = lyd_new_leaf(data, ietfnc, "error-option", rpcedit_erropt2str[rpc_e->error_opt]);
1467 if (!node) {
1468 lyd_free(data);
1469 return NC_MSG_ERROR;
1470 }
1471 }
1472
Michal Vasko7793bc62016-09-16 11:58:41 +02001473 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001474 node = lyd_new_anydata(data, ietfnc, "config", rpc_e->edit_cont, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01001475 } else {
1476 node = lyd_new_leaf(data, ietfnc, "url", rpc_e->edit_cont);
1477 }
1478 if (!node) {
1479 lyd_free(data);
1480 return NC_MSG_ERROR;
1481 }
1482 break;
1483
1484 case NC_RPC_COPY:
1485 rpc_cp = (struct nc_rpc_copy *)rpc;
1486
1487 data = lyd_new(NULL, ietfnc, "copy-config");
1488 node = lyd_new(data, ietfnc, "target");
1489 if (rpc_cp->url_trg) {
1490 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_trg);
1491 } else {
1492 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->target], NULL);
1493 }
1494 if (!node) {
1495 lyd_free(data);
1496 return NC_MSG_ERROR;
1497 }
1498
1499 node = lyd_new(data, ietfnc, "source");
1500 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02001501 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001502 node = lyd_new_anydata(node, ietfnc, "config", rpc_cp->url_config_src, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01001503 } else {
1504 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_config_src);
1505 }
1506 } else {
1507 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->source], NULL);
1508 }
1509 if (!node) {
1510 lyd_free(data);
1511 return NC_MSG_ERROR;
1512 }
1513
1514 if (rpc_cp->wd_mode) {
1515 if (!ietfncwd) {
1516 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1517 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01001518 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001519 return NC_MSG_ERROR;
1520 }
1521 }
1522 switch (rpc_cp->wd_mode) {
1523 case NC_WD_UNKNOWN:
1524 /* cannot get here */
1525 break;
1526 case NC_WD_ALL:
1527 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1528 break;
1529 case NC_WD_ALL_TAG:
1530 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1531 break;
1532 case NC_WD_TRIM:
1533 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1534 break;
1535 case NC_WD_EXPLICIT:
1536 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1537 break;
1538 }
1539 if (!node) {
1540 lyd_free(data);
1541 return NC_MSG_ERROR;
1542 }
1543 }
1544 break;
1545
1546 case NC_RPC_DELETE:
1547 rpc_del = (struct nc_rpc_delete *)rpc;
1548
1549 data = lyd_new(NULL, ietfnc, "delete-config");
1550 node = lyd_new(data, ietfnc, "target");
1551 if (rpc_del->url) {
1552 node = lyd_new_leaf(node, ietfnc, "url", rpc_del->url);
1553 } else {
1554 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_del->target], NULL);
1555 }
1556 if (!node) {
1557 lyd_free(data);
1558 return NC_MSG_ERROR;
1559 }
1560 break;
1561
1562 case NC_RPC_LOCK:
1563 rpc_lock = (struct nc_rpc_lock *)rpc;
1564
1565 data = lyd_new(NULL, ietfnc, "lock");
1566 node = lyd_new(data, ietfnc, "target");
1567 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
1568 if (!node) {
1569 lyd_free(data);
1570 return NC_MSG_ERROR;
1571 }
1572 break;
1573
1574 case NC_RPC_UNLOCK:
1575 rpc_lock = (struct nc_rpc_lock *)rpc;
1576
1577 data = lyd_new(NULL, ietfnc, "unlock");
1578 node = lyd_new(data, ietfnc, "target");
1579 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
1580 if (!node) {
1581 lyd_free(data);
1582 return NC_MSG_ERROR;
1583 }
1584 break;
1585
1586 case NC_RPC_GET:
1587 rpc_g = (struct nc_rpc_get *)rpc;
1588
1589 data = lyd_new(NULL, ietfnc, "get");
1590 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01001591 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001592 node = lyd_new_anydata(data, ietfnc, "filter", rpc_g->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01001593 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01001594 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02001595 node = lyd_new_anydata(data, ietfnc, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001596 lyd_insert_attr(node, NULL, "type", "xpath");
1597 lyd_insert_attr(node, NULL, "select", rpc_g->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01001598 }
1599 if (!node) {
1600 lyd_free(data);
1601 return NC_MSG_ERROR;
1602 }
1603 }
1604
1605 if (rpc_g->wd_mode) {
1606 if (!ietfncwd) {
1607 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1608 if (!ietfncwd) {
Michal Vasko086cd572017-01-12 12:19:05 +01001609 ERR("Session %u: missing \"ietf-netconf-with-defaults\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001610 return NC_MSG_ERROR;
1611 }
1612 }
1613 switch (rpc_g->wd_mode) {
1614 case NC_WD_UNKNOWN:
1615 /* cannot get here */
1616 break;
1617 case NC_WD_ALL:
1618 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1619 break;
1620 case NC_WD_ALL_TAG:
1621 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1622 break;
1623 case NC_WD_TRIM:
1624 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1625 break;
1626 case NC_WD_EXPLICIT:
1627 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1628 break;
1629 }
1630 if (!node) {
1631 lyd_free(data);
1632 return NC_MSG_ERROR;
1633 }
1634 }
1635 break;
1636
1637 case NC_RPC_KILL:
1638 rpc_k = (struct nc_rpc_kill *)rpc;
1639
1640 data = lyd_new(NULL, ietfnc, "kill-session");
1641 sprintf(str, "%u", rpc_k->sid);
1642 lyd_new_leaf(data, ietfnc, "session-id", str);
1643 break;
1644
1645 case NC_RPC_COMMIT:
1646 rpc_com = (struct nc_rpc_commit *)rpc;
1647
1648 data = lyd_new(NULL, ietfnc, "commit");
1649 if (rpc_com->confirmed) {
1650 lyd_new_leaf(data, ietfnc, "confirmed", NULL);
1651 }
1652
1653 if (rpc_com->confirm_timeout) {
1654 sprintf(str, "%u", rpc_com->confirm_timeout);
1655 lyd_new_leaf(data, ietfnc, "confirm-timeout", str);
1656 }
1657
1658 if (rpc_com->persist) {
1659 node = lyd_new_leaf(data, ietfnc, "persist", rpc_com->persist);
1660 if (!node) {
1661 lyd_free(data);
1662 return NC_MSG_ERROR;
1663 }
1664 }
1665
1666 if (rpc_com->persist_id) {
1667 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_com->persist_id);
1668 if (!node) {
1669 lyd_free(data);
1670 return NC_MSG_ERROR;
1671 }
1672 }
1673 break;
1674
1675 case NC_RPC_DISCARD:
1676 data = lyd_new(NULL, ietfnc, "discard-changes");
1677 break;
1678
1679 case NC_RPC_CANCEL:
1680 rpc_can = (struct nc_rpc_cancel *)rpc;
1681
1682 data = lyd_new(NULL, ietfnc, "cancel-commit");
1683 if (rpc_can->persist_id) {
1684 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_can->persist_id);
1685 if (!node) {
1686 lyd_free(data);
1687 return NC_MSG_ERROR;
1688 }
1689 }
1690 break;
1691
1692 case NC_RPC_VALIDATE:
1693 rpc_val = (struct nc_rpc_validate *)rpc;
1694
1695 data = lyd_new(NULL, ietfnc, "validate");
1696 node = lyd_new(data, ietfnc, "source");
1697 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02001698 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001699 node = lyd_new_anydata(node, ietfnc, "config", rpc_val->url_config_src, LYD_ANYDATA_SXML);
Michal Vasko086311b2016-01-08 09:53:11 +01001700 } else {
1701 node = lyd_new_leaf(node, ietfnc, "url", rpc_val->url_config_src);
1702 }
1703 } else {
1704 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_val->source], NULL);
1705 }
1706 if (!node) {
1707 lyd_free(data);
1708 return NC_MSG_ERROR;
1709 }
1710 break;
1711
1712 case NC_RPC_GETSCHEMA:
1713 ietfncmon = ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL);
1714 if (!ietfncmon) {
Michal Vasko086cd572017-01-12 12:19:05 +01001715 ERR("Session %u: missing \"ietf-netconf-monitoring\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001716 return NC_MSG_ERROR;
1717 }
1718
1719 rpc_gs = (struct nc_rpc_getschema *)rpc;
1720
1721 data = lyd_new(NULL, ietfncmon, "get-schema");
1722 node = lyd_new_leaf(data, ietfncmon, "identifier", rpc_gs->identifier);
1723 if (!node) {
1724 lyd_free(data);
1725 return NC_MSG_ERROR;
1726 }
1727 if (rpc_gs->version) {
1728 node = lyd_new_leaf(data, ietfncmon, "version", rpc_gs->version);
1729 if (!node) {
1730 lyd_free(data);
1731 return NC_MSG_ERROR;
1732 }
1733 }
1734 if (rpc_gs->format) {
1735 node = lyd_new_leaf(data, ietfncmon, "format", rpc_gs->format);
1736 if (!node) {
1737 lyd_free(data);
1738 return NC_MSG_ERROR;
1739 }
1740 }
1741 break;
1742
1743 case NC_RPC_SUBSCRIBE:
1744 notifs = ly_ctx_get_module(session->ctx, "notifications", NULL);
1745 if (!notifs) {
Michal Vasko086cd572017-01-12 12:19:05 +01001746 ERR("Session %u: missing \"notifications\" schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001747 return NC_MSG_ERROR;
1748 }
1749
1750 rpc_sub = (struct nc_rpc_subscribe *)rpc;
1751
1752 data = lyd_new(NULL, notifs, "create-subscription");
1753 if (rpc_sub->stream) {
1754 node = lyd_new_leaf(data, notifs, "stream", rpc_sub->stream);
1755 if (!node) {
1756 lyd_free(data);
1757 return NC_MSG_ERROR;
1758 }
1759 }
1760
1761 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01001762 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Michal Vaskoc741ddb2016-10-07 13:34:57 +02001763 node = lyd_new_anydata(data, notifs, "filter", rpc_sub->filter, LYD_ANYDATA_SXML);
Michal Vasko303245c2016-03-24 15:20:03 +01001764 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01001765 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02001766 node = lyd_new_anydata(data, notifs, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001767 lyd_insert_attr(node, NULL, "type", "xpath");
1768 lyd_insert_attr(node, NULL, "select", rpc_sub->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01001769 }
1770 if (!node) {
1771 lyd_free(data);
1772 return NC_MSG_ERROR;
1773 }
1774 }
1775
1776 if (rpc_sub->start) {
1777 node = lyd_new_leaf(data, notifs, "startTime", rpc_sub->start);
1778 if (!node) {
1779 lyd_free(data);
1780 return NC_MSG_ERROR;
1781 }
1782 }
1783
1784 if (rpc_sub->stop) {
1785 node = lyd_new_leaf(data, notifs, "stopTime", rpc_sub->stop);
1786 if (!node) {
1787 lyd_free(data);
1788 return NC_MSG_ERROR;
1789 }
1790 }
1791 break;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001792 default:
1793 ERRINT;
1794 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001795 }
1796
Michal Vasko41adf392017-01-17 10:39:04 +01001797 if (lyd_validate(&data, LYD_OPT_RPC | LYD_OPT_NOEXTDEPS
1798 | (session->flags & NC_SESSION_CLIENT_NOT_STRICT ? 0 : LYD_OPT_STRICT), NULL)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001799 lyd_free(data);
1800 return NC_MSG_ERROR;
1801 }
1802
Michal Vaskoade892d2017-02-22 13:40:35 +01001803 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001804 if (ret == -1) {
1805 /* error */
1806 r = NC_MSG_ERROR;
1807 } else if (!ret) {
1808 /* blocking */
Michal Vasko086311b2016-01-08 09:53:11 +01001809 r = NC_MSG_WOULDBLOCK;
1810 } else {
1811 /* send RPC, store its message ID */
1812 r = nc_send_msg(session, data);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001813 cur_msgid = session->opts.client.msgid;
Michal Vasko086311b2016-01-08 09:53:11 +01001814 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001815 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +01001816
1817 lyd_free(data);
1818
1819 if (r != NC_MSG_RPC) {
1820 return r;
1821 }
1822
1823 *msgid = cur_msgid;
1824 return NC_MSG_RPC;
1825}
Michal Vaskode2946c2017-01-12 12:19:26 +01001826
1827API void
1828nc_client_session_set_not_strict(struct nc_session *session)
1829{
1830 if (session->side != NC_CLIENT) {
1831 ERRARG("session");
1832 return;
1833 }
1834
1835 session->flags |= NC_SESSION_CLIENT_NOT_STRICT;
1836}