blob: 524bfc75e3ec0c4beeecbe80382bf99c0144c101 [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
68ctx_check_and_load_model(struct nc_session *session, const char *cpblt)
69{
70 const struct lys_module *module;
71 char *ptr, *ptr2;
72 char *model_name, *revision = NULL, *features = NULL;
73
74 /* parse module */
75 ptr = strstr(cpblt, "module=");
76 if (!ptr) {
Michal Vaskoef578332016-01-25 13:20:09 +010077 ERR("Unknown capability \"%s\" could not be parsed.", cpblt);
78 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010079 }
80 ptr += 7;
81 ptr2 = strchr(ptr, '&');
82 if (!ptr2) {
83 ptr2 = ptr + strlen(ptr);
84 }
85 model_name = strndup(ptr, ptr2 - ptr);
86
87 /* parse revision */
88 ptr = strstr(cpblt, "revision=");
89 if (ptr) {
90 ptr += 9;
91 ptr2 = strchr(ptr, '&');
92 if (!ptr2) {
93 ptr2 = ptr + strlen(ptr);
94 }
95 revision = strndup(ptr, ptr2 - ptr);
96 }
97
98 /* load module if needed */
99 module = ly_ctx_get_module(session->ctx, model_name, revision);
100 if (!module) {
101 module = ly_ctx_load_module(session->ctx, model_name, revision);
102 }
103
Michal Vasko086311b2016-01-08 09:53:11 +0100104 free(revision);
105 if (!module) {
Michal Vaskoef578332016-01-25 13:20:09 +0100106 WRN("Failed to load model \"%s\".", model_name);
107 free(model_name);
Michal Vasko086311b2016-01-08 09:53:11 +0100108 return 1;
109 }
Michal Vaskoef578332016-01-25 13:20:09 +0100110 free(model_name);
Michal Vasko086311b2016-01-08 09:53:11 +0100111
112 /* parse features */
113 ptr = strstr(cpblt, "features=");
114 if (ptr) {
115 ptr += 9;
116 ptr2 = strchr(ptr, '&');
117 if (!ptr2) {
118 ptr2 = ptr + strlen(ptr);
119 }
120 features = strndup(ptr, ptr2 - ptr);
121 }
122
123 /* enable features */
124 if (features) {
125 /* basically manual strtok_r (to avoid macro) */
126 ptr2 = features;
127 for (ptr = features; *ptr; ++ptr) {
128 if (*ptr == ',') {
129 *ptr = '\0';
130 /* remember last feature */
131 ptr2 = ptr + 1;
132 }
133 }
134
135 ptr = features;
136 lys_features_enable(module, ptr);
137 while (ptr != ptr2) {
138 ptr += strlen(ptr) + 1;
139 lys_features_enable(module, ptr);
140 }
141
142 free(features);
143 }
144
145 return 0;
146}
147
Michal Vasko1aaa6602016-02-09 11:04:33 +0100148/* SCHEMAS_DIR used as the last resort */
Michal Vasko086311b2016-01-08 09:53:11 +0100149static int
Michal Vasko1aaa6602016-02-09 11:04:33 +0100150ctx_check_and_load_ietf_netconf(struct ly_ctx *ctx, const char **cpblts)
Michal Vasko086311b2016-01-08 09:53:11 +0100151{
152 int i;
153 const struct lys_module *ietfnc;
154
155 ietfnc = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
156 if (!ietfnc) {
Michal Vasko1aaa6602016-02-09 11:04:33 +0100157 ietfnc = ly_ctx_load_module(ctx, "ietf-netconf", NULL);
158 if (!ietfnc) {
Michal Vasko086311b2016-01-08 09:53:11 +0100159 ietfnc = lys_parse_path(ctx, SCHEMAS_DIR"/ietf-netconf.yin", LYS_IN_YIN);
Michal Vasko086311b2016-01-08 09:53:11 +0100160 }
161 }
162 if (!ietfnc) {
163 ERR("Loading base NETCONF schema failed.");
164 return 1;
165 }
166
167 /* set supported capabilities from ietf-netconf */
168 for (i = 0; cpblts[i]; ++i) {
169 if (!strncmp(cpblts[i], "urn:ietf:params:netconf:capability:", 35)) {
170 if (!strncmp(cpblts[i] + 35, "writable-running", 16)) {
171 lys_features_enable(ietfnc, "writable-running");
172 } else if (!strncmp(cpblts[i] + 35, "candidate", 9)) {
173 lys_features_enable(ietfnc, "candidate");
174 } else if (!strcmp(cpblts[i] + 35, "confirmed-commit:1.1")) {
175 lys_features_enable(ietfnc, "confirmed-commit");
176 } else if (!strncmp(cpblts[i] + 35, "rollback-on-error", 17)) {
177 lys_features_enable(ietfnc, "rollback-on-error");
178 } else if (!strcmp(cpblts[i] + 35, "validate:1.1")) {
179 lys_features_enable(ietfnc, "validate");
180 } else if (!strncmp(cpblts[i] + 35, "startup", 7)) {
181 lys_features_enable(ietfnc, "startup");
182 } else if (!strncmp(cpblts[i] + 35, "url", 3)) {
183 lys_features_enable(ietfnc, "url");
184 } else if (!strncmp(cpblts[i] + 35, "xpath", 5)) {
185 lys_features_enable(ietfnc, "xpath");
186 }
187 }
188 }
189
190 return 0;
191}
192
193static char *
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200194libyang_module_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *submod_rev,
195 void *user_data, LYS_INFORMAT *format, void (**free_model_data)(void *model_data))
Michal Vasko086311b2016-01-08 09:53:11 +0100196{
197 struct nc_session *session = (struct nc_session *)user_data;
198 struct nc_rpc *rpc;
199 struct nc_reply *reply;
200 struct nc_reply_data *data_rpl;
Michal Vasko998ba412016-09-16 12:00:07 +0200201 struct nc_reply_error *error_rpl;
Radek Krejci539efb62016-08-24 15:05:16 +0200202 struct lyd_node_anydata *get_schema_data;
Michal Vasko086311b2016-01-08 09:53:11 +0100203 NC_MSG_TYPE msg;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200204 char *model_data = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100205 uint64_t msgid;
206
207 /* TODO later replace with yang to reduce model size? */
Michal Vaskod5ad5f72016-07-25 16:17:46 +0200208 if (submod_name) {
209 rpc = nc_rpc_getschema(submod_name, submod_rev, "yin", NC_PARAMTYPE_CONST);
210 } else {
211 rpc = nc_rpc_getschema(mod_name, mod_rev, "yin", NC_PARAMTYPE_CONST);
212 }
Michal Vasko086311b2016-01-08 09:53:11 +0100213 *format = LYS_IN_YIN;
214
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 Vasko36b17ba2016-05-03 11:54:21 +0200224 msg = nc_recv_reply(session, rpc, msgid, 1000, 0, &reply);
Michal Vasko086311b2016-01-08 09:53:11 +0100225 nc_rpc_free(rpc);
226 if (msg == NC_MSG_WOULDBLOCK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100227 ERR("Session %u: timeout for receiving reply to a <get-schema> expired.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100228 return NULL;
229 } else if (msg == NC_MSG_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100230 ERR("Session %u: failed to receive a reply to <get-schema>.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100231 return NULL;
232 }
233
Michal Vasko998ba412016-09-16 12:00:07 +0200234 switch (reply->type) {
235 case NC_RPL_OK:
236 ERR("Session %u: unexpected reply OK to a <get-schema> RPC.", session->id);
237 nc_reply_free(reply);
238 return NULL;
239 case NC_RPL_DATA:
240 /* fine */
241 break;
242 case NC_RPL_ERROR:
243 error_rpl = (struct nc_reply_error *)reply;
244 if (error_rpl->count) {
245 ERR("Session %u: error reply to a <get-schema> RPC (tag \"%s\", message \"%s\").",
246 session->id, error_rpl->err[0].tag, error_rpl->err[0].message);
247 } else {
248 ERR("Session %u: unexpected reply error to a <get-schema> RPC.", session->id);
249 }
250 nc_reply_free(reply);
251 return NULL;
252 case NC_RPL_NOTIF:
253 ERR("Session %u: unexpected reply notification to a <get-schema> RPC.", session->id);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100254 nc_reply_free(reply);
255 return NULL;
256 }
257
Michal Vasko086311b2016-01-08 09:53:11 +0100258 data_rpl = (struct nc_reply_data *)reply;
Michal Vaskob2583f12016-05-12 11:40:23 +0200259 if ((data_rpl->data->schema->nodetype != LYS_RPC) || strcmp(data_rpl->data->schema->name, "get-schema")
260 || !data_rpl->data->child || (data_rpl->data->child->schema->nodetype != LYS_ANYXML)) {
261 ERR("Session %u: unexpected data in reply to a <get-schema> RPC.", session->id);
262 nc_reply_free(reply);
263 return NULL;
264 }
Radek Krejci539efb62016-08-24 15:05:16 +0200265 get_schema_data = (struct lyd_node_anydata *)data_rpl->data->child;
266 switch (get_schema_data->value_type) {
267 case LYD_ANYDATA_CONSTSTRING:
268 case LYD_ANYDATA_STRING:
Michal Vaskob2583f12016-05-12 11:40:23 +0200269 model_data = strdup(get_schema_data->value.str);
Radek Krejci539efb62016-08-24 15:05:16 +0200270 break;
271 case LYD_ANYDATA_DATATREE:
272 lyd_print_mem(&model_data, get_schema_data->value.tree, LYD_XML, LYP_WITHSIBLINGS);
273 break;
274 case LYD_ANYDATA_XML:
275 lyxml_print_mem(&model_data, get_schema_data->value.xml, LYXML_PRINT_SIBLINGS);
276 break;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200277 }
Michal Vasko086311b2016-01-08 09:53:11 +0100278 nc_reply_free(reply);
Michal Vaskoca4ad152016-03-03 15:50:45 +0100279 *free_model_data = free;
Michal Vasko086311b2016-01-08 09:53:11 +0100280
Michal Vasko086311b2016-01-08 09:53:11 +0100281 return model_data;
282}
283
Michal Vaskoef578332016-01-25 13:20:09 +0100284/* return 0 - ok, 1 - some models failed to load, -1 - error */
Michal Vasko086311b2016-01-08 09:53:11 +0100285int
286nc_ctx_check_and_fill(struct nc_session *session)
287{
Michal Vaskoef578332016-01-25 13:20:09 +0100288 int i, get_schema_support = 0, ret = 0, r;
Michal Vasko086311b2016-01-08 09:53:11 +0100289 ly_module_clb old_clb = NULL;
290 void *old_data = NULL;
291
292 assert(session->cpblts && session->ctx);
293
294 /* check if get-schema is supported */
295 for (i = 0; session->cpblts[i]; ++i) {
296 if (!strncmp(session->cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
297 get_schema_support = 1;
298 break;
299 }
300 }
301
302 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
303 if (get_schema_support && !ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL)) {
304 if (lys_parse_path(session->ctx, SCHEMAS_DIR"/ietf-netconf-monitoring.yin", LYS_IN_YIN)) {
305 /* set module retrieval using <get-schema> */
306 old_clb = ly_ctx_get_module_clb(session->ctx, &old_data);
Michal Vaskoca4ad152016-03-03 15:50:45 +0100307 ly_ctx_set_module_clb(session->ctx, libyang_module_clb, session);
Michal Vasko086311b2016-01-08 09:53:11 +0100308 } else {
309 WRN("Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
310 }
311 }
312
313 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Michal Vasko1aaa6602016-02-09 11:04:33 +0100314 if (ctx_check_and_load_ietf_netconf(session->ctx, session->cpblts)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100315 if (old_clb) {
316 ly_ctx_set_module_clb(session->ctx, old_clb, old_data);
317 }
Michal Vaskoef578332016-01-25 13:20:09 +0100318 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100319 }
320
321 /* load all other models */
322 for (i = 0; session->cpblts[i]; ++i) {
323 if (!strncmp(session->cpblts[i], "urn:ietf:params:netconf:capability", 34)
324 || !strncmp(session->cpblts[i], "urn:ietf:params:netconf:base", 28)) {
325 continue;
326 }
327
Michal Vaskoef578332016-01-25 13:20:09 +0100328 r = ctx_check_and_load_model(session, session->cpblts[i]);
329 if (r == -1) {
330 ret = -1;
331 break;
332 }
333
334 /* failed to load schema, but let's try to find it using user callback (or locally, if not set),
335 * if it was using get-schema */
336 if (r == 1) {
337 if (get_schema_support) {
338 VRB("Trying to load the schema from a different source.");
339 /* works even if old_clb is NULL */
340 ly_ctx_set_module_clb(session->ctx, old_clb, old_data);
341 r = ctx_check_and_load_model(session, session->cpblts[i]);
342 }
343
344 /* fail again (or no other way to try), too bad */
345 if (r) {
346 ret = 1;
347 }
348
349 /* set get-schema callback back */
350 ly_ctx_set_module_clb(session->ctx, &libyang_module_clb, session);
351 }
Michal Vasko086311b2016-01-08 09:53:11 +0100352 }
353
354 if (old_clb) {
355 ly_ctx_set_module_clb(session->ctx, old_clb, old_data);
356 }
Michal Vaskoef578332016-01-25 13:20:09 +0100357 if (ret == 1) {
358 WRN("Some models failed to be loaded, any data from these models will be ignored.");
359 }
360 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100361}
362
363API struct nc_session *
364nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
365{
Michal Vaskod083db62016-01-19 10:31:29 +0100366 struct nc_session *session;
Michal Vasko086311b2016-01-08 09:53:11 +0100367
Michal Vasko45e53ae2016-04-07 11:46:03 +0200368 if (fdin < 0) {
369 ERRARG("fdin");
370 return NULL;
371 } else if (fdout < 0) {
372 ERRARG("fdout");
Michal Vasko086311b2016-01-08 09:53:11 +0100373 return NULL;
374 }
375
376 /* prepare session structure */
377 session = calloc(1, sizeof *session);
378 if (!session) {
379 ERRMEM;
380 return NULL;
381 }
382 session->status = NC_STATUS_STARTING;
383 session->side = NC_CLIENT;
384
385 /* transport specific data */
386 session->ti_type = NC_TI_FD;
387 session->ti.fd.in = fdin;
388 session->ti.fd.out = fdout;
389
390 /* assign context (dicionary needed for handshake) */
391 if (!ctx) {
392 ctx = ly_ctx_new(SCHEMAS_DIR);
Michal Vaskoe035b8e2016-03-11 10:10:03 +0100393 /* definitely should not happen, but be ready */
394 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
395 /* that's just it */
396 goto fail;
397 }
Michal Vasko086311b2016-01-08 09:53:11 +0100398 } else {
399 session->flags |= NC_SESSION_SHAREDCTX;
400 }
401 session->ctx = ctx;
402
403 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200404 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko086311b2016-01-08 09:53:11 +0100405 goto fail;
406 }
407 session->status = NC_STATUS_RUNNING;
408
Michal Vaskoef578332016-01-25 13:20:09 +0100409 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +0100410 goto fail;
411 }
412
413 return session;
414
415fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100416 nc_session_free(session, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100417 return NULL;
418}
419
420int
Michal Vaskof05562c2016-01-20 12:06:43 +0100421nc_sock_connect(const char* host, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100422{
Michal Vasko0190bc32016-03-02 15:47:49 +0100423 int i, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100424 struct addrinfo hints, *res_list, *res;
425 char port_s[6]; /* length of string representation of short int */
426
427 snprintf(port_s, 6, "%u", port);
428
429 /* Connect to a server */
430 memset(&hints, 0, sizeof hints);
431 hints.ai_family = AF_UNSPEC;
432 hints.ai_socktype = SOCK_STREAM;
433 hints.ai_protocol = IPPROTO_TCP;
434 i = getaddrinfo(host, port_s, &hints, &res_list);
435 if (i != 0) {
436 ERR("Unable to translate the host address (%s).", gai_strerror(i));
437 return -1;
438 }
439
440 for (i = 0, res = res_list; res != NULL; res = res->ai_next) {
441 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
442 if (sock == -1) {
443 /* socket was not created, try another resource */
444 i = errno;
445 goto errloop;
446 }
447
448 if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
449 /* network connection failed, try another resource */
450 i = errno;
451 close(sock);
452 sock = -1;
453 goto errloop;
454 }
455
Michal Vasko0190bc32016-03-02 15:47:49 +0100456 /* make the socket non-blocking */
457 if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
458 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100459 close(sock);
Michal Vasko0190bc32016-03-02 15:47:49 +0100460 return -1;
461 }
462
Michal Vasko086311b2016-01-08 09:53:11 +0100463 /* we're done, network connection established */
464 break;
465errloop:
466 VRB("Unable to connect to %s:%s over %s (%s).", host, port_s,
467 (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", strerror(i));
468 continue;
469 }
470
471 if (sock == -1) {
472 ERR("Unable to connect to %s:%s.", host, port_s);
473 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100474 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 +0100475 }
476 freeaddrinfo(res_list);
477
478 return sock;
479}
480
Michal Vasko086311b2016-01-08 09:53:11 +0100481static NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100482get_msg(struct nc_session *session, int timeout, uint64_t msgid, struct lyxml_elem **msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100483{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100484 int r;
Michal Vasko086311b2016-01-08 09:53:11 +0100485 char *ptr;
486 const char *str_msgid;
487 uint64_t cur_msgid;
488 struct lyxml_elem *xml;
Michal Vasko2518b6b2016-01-28 13:24:53 +0100489 struct nc_msg_cont *cont, **cont_ptr;
Michal Vasko086311b2016-01-08 09:53:11 +0100490 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
491
Michal Vasko62be1ce2016-03-03 13:24:52 +0100492 r = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100493 if (r == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +0100494 /* error */
495 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100496 } else if (!r) {
Michal Vasko086311b2016-01-08 09:53:11 +0100497 /* timeout */
498 return NC_MSG_WOULDBLOCK;
499 }
500
501 /* try to get notification from the session's queue */
502 if (!msgid && session->notifs) {
503 cont = session->notifs;
504 session->notifs = cont->next;
505
Michal Vasko71ba2da2016-05-04 10:53:16 +0200506 xml = cont->msg;
Michal Vasko086311b2016-01-08 09:53:11 +0100507 free(cont);
508
Michal Vasko71ba2da2016-05-04 10:53:16 +0200509 msgtype = NC_MSG_NOTIF;
Michal Vasko086311b2016-01-08 09:53:11 +0100510 }
511
512 /* try to get rpc-reply from the session's queue */
513 if (msgid && session->replies) {
Michal Vasko71ba2da2016-05-04 10:53:16 +0200514 cont = session->replies;
515 session->replies = cont->next;
Michal Vasko2518b6b2016-01-28 13:24:53 +0100516
Michal Vasko71ba2da2016-05-04 10:53:16 +0200517 xml = cont->msg;
518 free(cont);
Michal Vasko086311b2016-01-08 09:53:11 +0100519
Michal Vasko71ba2da2016-05-04 10:53:16 +0200520 msgtype = NC_MSG_REPLY;
Michal Vasko086311b2016-01-08 09:53:11 +0100521 }
522
Michal Vasko71ba2da2016-05-04 10:53:16 +0200523 if (!msgtype) {
524 /* read message from wire */
525 msgtype = nc_read_msg_poll(session, timeout, &xml);
526 }
Michal Vasko086311b2016-01-08 09:53:11 +0100527
528 /* we read rpc-reply, want a notif */
529 if (!msgid && (msgtype == NC_MSG_REPLY)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100530 cont_ptr = &session->replies;
531 while (*cont_ptr) {
532 cont_ptr = &((*cont_ptr)->next);
533 }
534 *cont_ptr = malloc(sizeof **cont_ptr);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100535 if (!*cont_ptr) {
536 ERRMEM;
Michal Vasko71ba2da2016-05-04 10:53:16 +0200537 pthread_mutex_unlock(session->ti_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100538 lyxml_free(session->ctx, xml);
539 return NC_MSG_ERROR;
540 }
Michal Vasko086311b2016-01-08 09:53:11 +0100541 (*cont_ptr)->msg = xml;
542 (*cont_ptr)->next = NULL;
543 }
544
545 /* we read notif, want a rpc-reply */
546 if (msgid && (msgtype == NC_MSG_NOTIF)) {
Michal Vaskoa8ad4482016-01-28 14:25:54 +0100547 /* TODO check whether the session is even subscribed */
548 /*if (!session->notif) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100549 pthread_mutex_unlock(session->ti_lock);
Michal Vaskod083db62016-01-19 10:31:29 +0100550 ERR("Session %u: received a <notification> but session is not subscribed.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100551 lyxml_free(session->ctx, xml);
Michal Vasko2518b6b2016-01-28 13:24:53 +0100552 return NC_MSG_ERROR;
Michal Vaskoa8ad4482016-01-28 14:25:54 +0100553 }*/
Michal Vasko086311b2016-01-08 09:53:11 +0100554
555 cont_ptr = &session->notifs;
556 while (*cont_ptr) {
557 cont_ptr = &((*cont_ptr)->next);
558 }
559 *cont_ptr = malloc(sizeof **cont_ptr);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100560 if (!cont_ptr) {
561 ERRMEM;
Michal Vasko71ba2da2016-05-04 10:53:16 +0200562 pthread_mutex_unlock(session->ti_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100563 lyxml_free(session->ctx, xml);
564 return NC_MSG_ERROR;
565 }
Michal Vasko086311b2016-01-08 09:53:11 +0100566 (*cont_ptr)->msg = xml;
567 (*cont_ptr)->next = NULL;
568 }
569
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100570 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100571
572 switch (msgtype) {
573 case NC_MSG_NOTIF:
Michal Vasko2518b6b2016-01-28 13:24:53 +0100574 if (!msgid) {
575 *msg = xml;
Michal Vasko086311b2016-01-08 09:53:11 +0100576 }
Michal Vasko086311b2016-01-08 09:53:11 +0100577 break;
578
579 case NC_MSG_REPLY:
Michal Vasko2518b6b2016-01-28 13:24:53 +0100580 if (msgid) {
Michal Vasko71ba2da2016-05-04 10:53:16 +0200581 /* check message-id */
582 str_msgid = lyxml_get_attr(xml, "message-id", NULL);
583 if (!str_msgid) {
584 ERR("Session %u: received a <rpc-reply> without a message-id.", session->id);
585 msgtype = NC_MSG_REPLY_ERR_MSGID;
586 } else {
587 cur_msgid = strtoul(str_msgid, &ptr, 10);
588 if (cur_msgid != msgid) {
589 ERR("Session %u: received a <rpc-reply> with an unexpected message-id \"%s\".",
590 session->id, str_msgid);
591 msgtype = NC_MSG_REPLY_ERR_MSGID;
592 }
593 }
Michal Vasko2518b6b2016-01-28 13:24:53 +0100594 *msg = xml;
Michal Vasko086311b2016-01-08 09:53:11 +0100595 }
Michal Vasko086311b2016-01-08 09:53:11 +0100596 break;
597
598 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100599 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100600 lyxml_free(session->ctx, xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +0200601 msgtype = NC_MSG_ERROR;
602 break;
Michal Vasko086311b2016-01-08 09:53:11 +0100603
604 case NC_MSG_RPC:
Michal Vaskod083db62016-01-19 10:31:29 +0100605 ERR("Session %u: received <rpc> from a NETCONF server.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100606 lyxml_free(session->ctx, xml);
Michal Vasko71ba2da2016-05-04 10:53:16 +0200607 msgtype = NC_MSG_ERROR;
608 break;
Michal Vasko086311b2016-01-08 09:53:11 +0100609
610 default:
611 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
612 * NC_MSG_NONE is not returned by nc_read_msg()
613 */
614 break;
615 }
616
617 return msgtype;
618}
619
620/* cannot strictly fail, but does not need to fill any error parameter at all */
621static void
622parse_rpc_error(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_err *err)
623{
624 struct lyxml_elem *iter, *next, *info;
625
626 LY_TREE_FOR(xml->child, iter) {
627 if (!iter->ns) {
628 if (iter->content) {
629 WRN("<rpc-error> child \"%s\" with value \"%s\" without namespace.", iter->name, iter->content);
630 } else {
631 WRN("<rpc-error> child \"%s\" without namespace.", iter->name);
632 }
633 continue;
634 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
635 if (iter->content) {
636 WRN("<rpc-error> child \"%s\" with value \"%s\" in an unknown namespace \"%s\".",
637 iter->name, iter->content, iter->ns->value);
638 } else {
639 WRN("<rpc-error> child \"%s\" in an unknown namespace \"%s\".", iter->name, iter->ns->value);
640 }
641 continue;
642 }
643
644 if (!strcmp(iter->name, "error-type")) {
645 if (!iter->content || (strcmp(iter->content, "transport") && strcmp(iter->content, "rpc")
646 && strcmp(iter->content, "protocol") && strcmp(iter->content, "application"))) {
647 WRN("<rpc-error> <error-type> unknown value \"%s\".", (iter->content ? iter->content : ""));
648 } else if (err->type) {
649 WRN("<rpc-error> <error-type> duplicated.");
650 } else {
651 err->type = lydict_insert(ctx, iter->content, 0);
652 }
653 } else if (!strcmp(iter->name, "error-tag")) {
654 if (!iter->content || (strcmp(iter->content, "in-use") && strcmp(iter->content, "invalid-value")
655 && strcmp(iter->content, "too-big") && strcmp(iter->content, "missing-attribute")
656 && strcmp(iter->content, "bad-attribute") && strcmp(iter->content, "unknown-attribute")
657 && strcmp(iter->content, "missing-element") && strcmp(iter->content, "bad-element")
658 && strcmp(iter->content, "unknown-element") && strcmp(iter->content, "unknown-namespace")
659 && strcmp(iter->content, "access-denied") && strcmp(iter->content, "lock-denied")
660 && strcmp(iter->content, "resource-denied") && strcmp(iter->content, "rollback-failed")
661 && strcmp(iter->content, "data-exists") && strcmp(iter->content, "data-missing")
662 && strcmp(iter->content, "operation-not-supported") && strcmp(iter->content, "operation-failed")
663 && strcmp(iter->content, "malformed-message"))) {
664 WRN("<rpc-error> <error-tag> unknown value \"%s\".", (iter->content ? iter->content : ""));
665 } else if (err->tag) {
666 WRN("<rpc-error> <error-tag> duplicated.");
667 } else {
668 err->tag = lydict_insert(ctx, iter->content, 0);
669 }
670 } else if (!strcmp(iter->name, "error-severity")) {
671 if (!iter->content || (strcmp(iter->content, "error") && strcmp(iter->content, "warning"))) {
672 WRN("<rpc-error> <error-severity> unknown value \"%s\".", (iter->content ? iter->content : ""));
673 } else if (err->severity) {
674 WRN("<rpc-error> <error-severity> duplicated.");
675 } else {
676 err->severity = lydict_insert(ctx, iter->content, 0);
677 }
678 } else if (!strcmp(iter->name, "error-app-tag")) {
679 if (err->apptag) {
680 WRN("<rpc-error> <error-app-tag> duplicated.");
681 } else {
682 err->apptag = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
683 }
684 } else if (!strcmp(iter->name, "error-path")) {
685 if (err->path) {
686 WRN("<rpc-error> <error-path> duplicated.");
687 } else {
688 err->path = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
689 }
690 } else if (!strcmp(iter->name, "error-message")) {
691 if (err->message) {
692 WRN("<rpc-error> <error-message> duplicated.");
693 } else {
694 err->message_lang = lyxml_get_attr(iter, "xml:lang", NULL);
695 if (!err->message_lang) {
696 VRB("<rpc-error> <error-message> without the recommended \"xml:lang\" attribute.");
697 }
698 err->message = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
699 }
700 } else if (!strcmp(iter->name, "error-info")) {
701 LY_TREE_FOR_SAFE(iter->child, next, info) {
702 if (info->ns && !strcmp(info->ns->value, NC_NS_BASE)) {
703 if (!strcmp(info->name, "session-id")) {
704 if (err->sid) {
705 WRN("<rpc-error> <error-info> <session-id> duplicated.");
706 } else {
707 err->sid = lydict_insert(ctx, (info->content ? info->content : ""), 0);
708 }
709 } else if (!strcmp(info->name, "bad-attr")) {
710 ++err->attr_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100711 err->attr = nc_realloc(err->attr, err->attr_count * sizeof *err->attr);
712 if (!err->attr) {
713 ERRMEM;
714 return;
715 }
Michal Vasko086311b2016-01-08 09:53:11 +0100716 err->attr[err->attr_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
717 } else if (!strcmp(info->name, "bad-element")) {
718 ++err->elem_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100719 err->elem = nc_realloc(err->elem, err->elem_count * sizeof *err->elem);
720 if (!err->elem) {
721 ERRMEM;
722 return;
723 }
Michal Vasko086311b2016-01-08 09:53:11 +0100724 err->elem[err->elem_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
725 } else if (!strcmp(info->name, "bad-namespace")) {
726 ++err->ns_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100727 err->ns = nc_realloc(err->ns, err->ns_count * sizeof *err->ns);
728 if (!err->ns) {
729 ERRMEM;
730 return;
731 }
Michal Vasko086311b2016-01-08 09:53:11 +0100732 err->ns[err->ns_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
733 } else {
734 if (info->content) {
735 WRN("<rpc-error> <error-info> unknown child \"%s\" with value \"%s\".",
736 info->name, info->content);
737 } else {
738 WRN("<rpc-error> <error-info> unknown child \"%s\".", info->name);
739 }
740 }
741 } else {
742 lyxml_unlink(ctx, info);
743 ++err->other_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100744 err->other = nc_realloc(err->other, err->other_count * sizeof *err->other);
745 if (!err->other) {
746 ERRMEM;
747 return;
748 }
Michal Vasko086311b2016-01-08 09:53:11 +0100749 err->other[err->other_count - 1] = info;
750 }
751 }
752 } else {
753 if (iter->content) {
754 WRN("<rpc-error> unknown child \"%s\" with value \"%s\".", iter->name, iter->content);
755 } else {
756 WRN("<rpc-error> unknown child \"%s\".", iter->name);
757 }
758 }
759 }
760}
761
762static struct nc_reply *
Michal Vaskoeb7080e2016-02-18 13:27:05 +0100763parse_reply(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_rpc *rpc, int parseroptions)
Michal Vasko086311b2016-01-08 09:53:11 +0100764{
765 struct lyxml_elem *iter;
Michal Vasko0473c4c2016-01-19 10:40:06 +0100766 const struct lys_node *schema = NULL;
Michal Vasko90e8e692016-07-13 12:27:57 +0200767 struct lyd_node *data = NULL, *next, *elem;
Michal Vasko1a38c862016-01-15 15:50:07 +0100768 struct nc_client_reply_error *error_rpl;
Michal Vasko086311b2016-01-08 09:53:11 +0100769 struct nc_reply_data *data_rpl;
770 struct nc_reply *reply = NULL;
Michal Vasko90e8e692016-07-13 12:27:57 +0200771 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +0100772 int i;
773
774 if (!xml->child) {
775 ERR("An empty <rpc-reply>.");
776 return NULL;
777 }
778
779 /* rpc-error */
780 if (!strcmp(xml->child->name, "rpc-error") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
781 /* count and check elements */
782 i = 0;
783 LY_TREE_FOR(xml->child, iter) {
784 if (strcmp(iter->name, "rpc-error")) {
785 ERR("<rpc-reply> content mismatch (<rpc-error> and <%s>).", iter->name);
786 return NULL;
787 } else if (!iter->ns) {
788 ERR("<rpc-reply> content mismatch (<rpc-error> without namespace).");
789 return NULL;
790 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
791 ERR("<rpc-reply> content mismatch (<rpc-error> with NS \"%s\").", iter->ns->value);
792 return NULL;
793 }
794 ++i;
795 }
796
797 error_rpl = malloc(sizeof *error_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100798 if (!error_rpl) {
799 ERRMEM;
800 return NULL;
801 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100802 error_rpl->type = NC_RPL_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100803 error_rpl->err = calloc(i, sizeof *error_rpl->err);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100804 if (!error_rpl->err) {
805 ERRMEM;
806 free(error_rpl);
807 return NULL;
808 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100809 error_rpl->count = i;
Michal Vasko1a38c862016-01-15 15:50:07 +0100810 error_rpl->ctx = ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100811 reply = (struct nc_reply *)error_rpl;
812
813 i = 0;
814 LY_TREE_FOR(xml->child, iter) {
815 parse_rpc_error(ctx, iter, error_rpl->err + i);
816 ++i;
817 }
818
819 /* ok */
820 } else if (!strcmp(xml->child->name, "ok") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
821 if (xml->child->next) {
822 ERR("<rpc-reply> content mismatch (<ok> and <%s>).", xml->child->next->name);
823 return NULL;
824 }
825 reply = malloc(sizeof *reply);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100826 if (!reply) {
827 ERRMEM;
828 return NULL;
829 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100830 reply->type = NC_RPL_OK;
Michal Vasko086311b2016-01-08 09:53:11 +0100831
832 /* some RPC output */
833 } else {
834 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +0200835 case NC_RPC_ACT_GENERIC:
836 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +0100837
838 if (rpc_gen->has_data) {
Michal Vasko90e8e692016-07-13 12:27:57 +0200839 data = rpc_gen->content.data;
Michal Vasko086311b2016-01-08 09:53:11 +0100840 } else {
Michal Vasko68b3f292016-09-16 12:00:32 +0200841 data = lyd_parse_mem(ctx, rpc_gen->content.xml_str, LYD_XML, LYD_OPT_RPC | parseroptions, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100842 if (!data) {
Michal Vasko90e8e692016-07-13 12:27:57 +0200843 ERR("Failed to parse a generic RPC/action XML.");
Michal Vasko086311b2016-01-08 09:53:11 +0100844 return NULL;
845 }
Michal Vasko90e8e692016-07-13 12:27:57 +0200846 }
847 if (data->schema->nodetype == LYS_RPC) {
848 /* RPC */
Michal Vasko086311b2016-01-08 09:53:11 +0100849 schema = data->schema;
Michal Vasko90e8e692016-07-13 12:27:57 +0200850 } else {
851 /* action */
852 LY_TREE_DFS_BEGIN(data, next, elem) {
853 if (elem->schema->nodetype == LYS_ACTION) {
854 schema = elem->schema;
855 break;
856 }
857 LY_TREE_DFS_END(data, next, elem);
858 }
859 }
860
861 /* cleanup */
862 if (data != rpc_gen->content.data) {
Michal Vasko086311b2016-01-08 09:53:11 +0100863 lyd_free(data);
864 data = NULL;
865 }
866 if (!schema) {
Michal Vasko90e8e692016-07-13 12:27:57 +0200867 /* only with action, if there is no action, it should not have gotten this far */
Michal Vasko9e036d52016-01-08 10:49:26 +0100868 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100869 return NULL;
870 }
871 break;
872
873 case NC_RPC_GETCONFIG:
874 case NC_RPC_GET:
Michal Vasko13ed2942016-02-29 16:21:00 +0100875 if (!xml->child->child) {
876 /* we did not receive any data */
877 data_rpl = malloc(sizeof *data_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100878 if (!data_rpl) {
879 ERRMEM;
880 return NULL;
881 }
Michal Vasko13ed2942016-02-29 16:21:00 +0100882 data_rpl->type = NC_RPL_DATA;
883 data_rpl->data = NULL;
884 return (struct nc_reply *)data_rpl;
885 }
886
Michal Vasko086311b2016-01-08 09:53:11 +0100887 /* special treatment */
888 data = lyd_parse_xml(ctx, &xml->child->child, LYD_OPT_DESTRUCT
Michal Vaskoeb7080e2016-02-18 13:27:05 +0100889 | (rpc->type == NC_RPC_GETCONFIG ? LYD_OPT_GETCONFIG : LYD_OPT_GET) | parseroptions);
Michal Vasko086311b2016-01-08 09:53:11 +0100890 if (!data) {
891 ERR("Failed to parse <%s> reply.", (rpc->type == NC_RPC_GETCONFIG ? "get-config" : "get"));
892 return NULL;
893 }
894 break;
895
896 case NC_RPC_GETSCHEMA:
Michal Vasko303245c2016-03-24 15:20:03 +0100897 schema = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vasko086311b2016-01-08 09:53:11 +0100898 if (!schema) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100899 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100900 return NULL;
901 }
902 break;
903
904 case NC_RPC_EDIT:
905 case NC_RPC_COPY:
906 case NC_RPC_DELETE:
907 case NC_RPC_LOCK:
908 case NC_RPC_UNLOCK:
909 case NC_RPC_KILL:
910 case NC_RPC_COMMIT:
911 case NC_RPC_DISCARD:
912 case NC_RPC_CANCEL:
913 case NC_RPC_VALIDATE:
914 case NC_RPC_SUBSCRIBE:
915 /* there is no output defined */
916 ERR("Unexpected data reply (root elem \"%s\").", xml->child->name);
917 return NULL;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100918 default:
919 ERRINT;
920 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100921 }
922
923 data_rpl = malloc(sizeof *data_rpl);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100924 if (!data_rpl) {
925 ERRMEM;
926 return NULL;
927 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100928 data_rpl->type = NC_RPL_DATA;
Michal Vasko086311b2016-01-08 09:53:11 +0100929 if (!data) {
Michal Vasko68b3f292016-09-16 12:00:32 +0200930 data_rpl->data = lyd_parse_xml(ctx, &xml->child, LYD_OPT_RPCREPLY | LYD_OPT_DESTRUCT | parseroptions,
931 schema, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100932 } else {
933 /* <get>, <get-config> */
934 data_rpl->data = data;
935 }
936 if (!data_rpl->data) {
937 ERR("Failed to parse <rpc-reply>.");
938 free(data_rpl);
939 return NULL;
940 }
941 reply = (struct nc_reply *)data_rpl;
942 }
943
944 return reply;
945}
946
Radek Krejci53691be2016-02-22 13:58:37 +0100947#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3d865d22016-01-28 16:00:53 +0100948
Michal Vasko3031aae2016-01-27 16:07:18 +0100949int
950nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
951{
952 int sock;
953
Michal Vasko45e53ae2016-04-07 11:46:03 +0200954 if (!address) {
955 ERRARG("address");
956 return -1;
957 } else if (!port) {
958 ERRARG("port");
Michal Vasko3031aae2016-01-27 16:07:18 +0100959 return -1;
960 }
961
962 sock = nc_sock_listen(address, port);
963 if (sock == -1) {
964 return -1;
965 }
966
967 ++client_opts.ch_bind_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100968 client_opts.ch_binds = nc_realloc(client_opts.ch_binds, client_opts.ch_bind_count * sizeof *client_opts.ch_binds);
969 if (!client_opts.ch_binds) {
970 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +0100971 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100972 return -1;
973 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100974
975 client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100976 if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) {
977 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +0100978 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100979 return -1;
980 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100981 client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port;
982 client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock;
983 client_opts.ch_binds[client_opts.ch_bind_count - 1].ti = ti;
984
985 return 0;
986}
987
988int
989nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
990{
991 uint32_t i;
992 int ret = -1;
993
994 if (!address && !port && !ti) {
995 for (i = 0; i < client_opts.ch_bind_count; ++i) {
996 close(client_opts.ch_binds[i].sock);
997 free((char *)client_opts.ch_binds[i].address);
998
999 ret = 0;
1000 }
1001 free(client_opts.ch_binds);
1002 client_opts.ch_binds = NULL;
1003 client_opts.ch_bind_count = 0;
1004 } else {
1005 for (i = 0; i < client_opts.ch_bind_count; ++i) {
1006 if ((!address || !strcmp(client_opts.ch_binds[i].address, address))
1007 && (!port || (client_opts.ch_binds[i].port == port))
1008 && (!ti || (client_opts.ch_binds[i].ti == ti))) {
1009 close(client_opts.ch_binds[i].sock);
1010 free((char *)client_opts.ch_binds[i].address);
1011
1012 --client_opts.ch_bind_count;
1013 memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds);
1014
1015 ret = 0;
1016 }
1017 }
1018 }
1019
1020 return ret;
1021}
1022
1023API int
1024nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session)
1025{
1026 int sock;
1027 char *host = NULL;
1028 uint16_t port, idx;
1029
Michal Vasko45e53ae2016-04-07 11:46:03 +02001030 if (!client_opts.ch_binds) {
1031 ERRINIT;
1032 return -1;
1033 } else if (!session) {
1034 ERRARG("session");
Michal Vasko3031aae2016-01-27 16:07:18 +01001035 return -1;
1036 }
1037
1038 sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx);
1039
Michal Vasko50456e82016-02-02 12:16:08 +01001040 if (sock < 1) {
Michal Vaskob737d752016-02-09 09:01:27 +01001041 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001042 return sock;
1043 }
1044
Radek Krejci53691be2016-02-22 13:58:37 +01001045#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001046 if (client_opts.ch_binds[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001047 *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001048 } else
1049#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001050#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001051 if (client_opts.ch_binds[idx].ti == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001052 *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT);
Michal Vasko3d865d22016-01-28 16:00:53 +01001053 } else
1054#endif
1055 {
Michal Vaskofee717c2016-02-01 13:25:43 +01001056 close(sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001057 *session = NULL;
1058 }
1059
1060 free(host);
1061
1062 if (!(*session)) {
1063 return -1;
1064 }
1065
1066 return 1;
1067}
1068
Radek Krejci53691be2016-02-22 13:58:37 +01001069#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vasko3d865d22016-01-28 16:00:53 +01001070
Michal Vaskobdfb5242016-05-24 09:11:01 +02001071API const char **
1072nc_session_get_cpblts(const struct nc_session *session)
1073{
1074 if (!session) {
1075 ERRARG("session");
1076 return NULL;
1077 }
1078
1079 return session->cpblts;
1080}
1081
1082API const char *
1083nc_session_cpblt(const struct nc_session *session, const char *capab)
1084{
1085 int i, len;
1086
1087 if (!session) {
1088 ERRARG("session");
1089 return NULL;
1090 } else if (!capab) {
1091 ERRARG("capab");
1092 return NULL;
1093 }
1094
1095 len = strlen(capab);
1096 for (i = 0; session->cpblts[i]; ++i) {
1097 if (!strncmp(session->cpblts[i], capab, len)) {
1098 return session->cpblts[i];
1099 }
1100 }
1101
1102 return NULL;
1103}
1104
Michal Vasko9cd26a82016-05-31 08:58:48 +02001105API int
1106nc_session_ntf_thread_running(const struct nc_session *session)
1107{
1108 if (!session) {
1109 ERRARG("session");
1110 return 0;
1111 }
1112
1113 return session->ntf_tid ? 1 : 0;
1114}
1115
Michal Vaskob7558c52016-02-26 15:04:19 +01001116API void
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001117nc_client_init(void)
1118{
1119 nc_init();
1120}
1121
1122API void
Michal Vaskob7558c52016-02-26 15:04:19 +01001123nc_client_destroy(void)
1124{
Michal Vasko7f1c0ef2016-03-11 11:13:06 +01001125 nc_client_set_schema_searchpath(NULL);
Michal Vaskob7558c52016-02-26 15:04:19 +01001126#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
1127 nc_client_ch_del_bind(NULL, 0, 0);
1128#endif
1129#ifdef NC_ENABLED_SSH
1130 nc_client_ssh_destroy_opts();
1131#endif
Michal Vaskoc979d3a2016-02-26 15:26:21 +01001132#ifdef NC_ENABLED_TLS
Michal Vaskob7558c52016-02-26 15:04:19 +01001133 nc_client_tls_destroy_opts();
1134#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001135 nc_destroy();
Michal Vaskob7558c52016-02-26 15:04:19 +01001136}
1137
Michal Vasko086311b2016-01-08 09:53:11 +01001138API NC_MSG_TYPE
Michal Vaskoeb7080e2016-02-18 13:27:05 +01001139nc_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 +01001140{
1141 struct lyxml_elem *xml;
1142 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1143
Michal Vasko45e53ae2016-04-07 11:46:03 +02001144 if (!session) {
1145 ERRARG("session");
1146 return NC_MSG_ERROR;
1147 } else if (!rpc) {
1148 ERRARG("rpc");
1149 return NC_MSG_ERROR;
1150 } else if (!reply) {
1151 ERRARG("reply");
1152 return NC_MSG_ERROR;
1153 } else if (parseroptions & LYD_OPT_TYPEMASK) {
1154 ERRARG("parseroptions");
Michal Vasko086311b2016-01-08 09:53:11 +01001155 return NC_MSG_ERROR;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001156 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001157 ERR("Session %u: invalid session to receive RPC replies.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001158 return NC_MSG_ERROR;
1159 }
Michal Vaskoeb7080e2016-02-18 13:27:05 +01001160 parseroptions &= ~(LYD_OPT_DESTRUCT | LYD_OPT_NOSIBLINGS);
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 Vaskoeb7080e2016-02-18 13:27:05 +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 Vasko68b3f292016-09-16 12:00:32 +02001218 (*notif)->tree = lyd_parse_xml(session->ctx, &xml->child, LYD_OPT_NOTIF | LYD_OPT_DESTRUCT, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001219 lyxml_free(session->ctx, xml);
1220 xml = NULL;
1221 if (!(*notif)->tree) {
Michal Vaskod083db62016-01-19 10:31:29 +01001222 ERR("Session %u: failed to parse a new notification.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001223 goto fail;
1224 }
1225 }
1226
1227 return msgtype;
1228
1229fail:
1230 lydict_remove(session->ctx, (*notif)->datetime);
1231 lyd_free((*notif)->tree);
1232 free(*notif);
1233 *notif = NULL;
1234 lyxml_free(session->ctx, xml);
1235
1236 return NC_MSG_ERROR;
1237}
1238
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001239static void *
1240nc_recv_notif_thread(void *arg)
1241{
1242 struct nc_ntf_thread_arg *ntarg;
1243 struct nc_session *session;
1244 void (*notif_clb)(struct nc_session *session, const struct nc_notif *notif);
1245 struct nc_notif *notif;
1246 NC_MSG_TYPE msgtype;
1247
1248 ntarg = (struct nc_ntf_thread_arg *)arg;
1249 session = ntarg->session;
1250 notif_clb = ntarg->notif_clb;
1251 free(ntarg);
1252
1253 while (session->ntf_tid) {
1254 msgtype = nc_recv_notif(session, 0, &notif);
1255 if (msgtype == NC_MSG_NOTIF) {
1256 notif_clb(session, notif);
Michal Vaskof0537d82016-01-29 14:42:38 +01001257 if (!strcmp(notif->tree->schema->name, "notificationComplete")
1258 && !strcmp(notif->tree->schema->module->name, "nc-notifications")) {
1259 nc_notif_free(notif);
1260 break;
1261 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001262 nc_notif_free(notif);
Michal Vasko0651c902016-05-19 15:55:42 +02001263 } else if (msgtype == NC_MSG_ERROR) {
1264 break;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001265 }
1266
1267 usleep(NC_CLIENT_NOTIF_THREAD_SLEEP);
1268 }
1269
Michal Vasko0651c902016-05-19 15:55:42 +02001270 VRB("Session %u: notification thread exit.", session->id);
1271 session->ntf_tid = NULL;
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001272 return NULL;
1273}
1274
1275API int
1276nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session, const struct nc_notif *notif))
1277{
1278 struct nc_ntf_thread_arg *ntarg;
1279 int ret;
1280
Michal Vasko45e53ae2016-04-07 11:46:03 +02001281 if (!session) {
1282 ERRARG("session");
1283 return -1;
1284 } else if (!notif_clb) {
1285 ERRARG("notif_clb");
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001286 return -1;
1287 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) {
1288 ERR("Session %u: invalid session to receive Notifications.", session->id);
1289 return -1;
1290 } else if (session->ntf_tid) {
1291 ERR("Session %u: separate notification thread is already running.", session->id);
1292 return -1;
1293 }
1294
1295 ntarg = malloc(sizeof *ntarg);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001296 if (!ntarg) {
1297 ERRMEM;
1298 return -1;
1299 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001300 ntarg->session = session;
1301 ntarg->notif_clb = notif_clb;
1302
1303 /* just so that nc_recv_notif_thread() does not immediately exit, the value does not matter */
1304 session->ntf_tid = malloc(sizeof *session->ntf_tid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001305 if (!session->ntf_tid) {
1306 ERRMEM;
1307 free(ntarg);
1308 return -1;
1309 }
Michal Vaskoa8ad4482016-01-28 14:25:54 +01001310
1311 ret = pthread_create((pthread_t *)session->ntf_tid, NULL, nc_recv_notif_thread, ntarg);
1312 if (ret) {
1313 ERR("Session %u: failed to create a new thread (%s).", strerror(errno));
1314 free(ntarg);
1315 free((pthread_t *)session->ntf_tid);
1316 session->ntf_tid = NULL;
1317 return -1;
1318 }
1319
1320 return 0;
1321}
1322
Michal Vasko086311b2016-01-08 09:53:11 +01001323API NC_MSG_TYPE
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001324nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid)
Michal Vasko086311b2016-01-08 09:53:11 +01001325{
1326 NC_MSG_TYPE r;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001327 int ret;
Michal Vasko90e8e692016-07-13 12:27:57 +02001328 struct nc_rpc_act_generic *rpc_gen;
Michal Vasko086311b2016-01-08 09:53:11 +01001329 struct nc_rpc_getconfig *rpc_gc;
1330 struct nc_rpc_edit *rpc_e;
1331 struct nc_rpc_copy *rpc_cp;
1332 struct nc_rpc_delete *rpc_del;
1333 struct nc_rpc_lock *rpc_lock;
1334 struct nc_rpc_get *rpc_g;
1335 struct nc_rpc_kill *rpc_k;
1336 struct nc_rpc_commit *rpc_com;
1337 struct nc_rpc_cancel *rpc_can;
1338 struct nc_rpc_validate *rpc_val;
1339 struct nc_rpc_getschema *rpc_gs;
1340 struct nc_rpc_subscribe *rpc_sub;
1341 struct lyd_node *data, *node;
Michal Vasko9d8bee62016-03-03 10:58:24 +01001342 const struct lys_module *ietfnc = NULL, *ietfncmon, *notifs, *ietfncwd = NULL;
Radek Krejci539efb62016-08-24 15:05:16 +02001343 char str[11];
Michal Vasko086311b2016-01-08 09:53:11 +01001344 uint64_t cur_msgid;
1345
Michal Vasko45e53ae2016-04-07 11:46:03 +02001346 if (!session) {
1347 ERRARG("session");
1348 return NC_MSG_ERROR;
1349 } else if (!rpc) {
1350 ERRARG("rpc");
1351 return NC_MSG_ERROR;
1352 } else if (!msgid) {
1353 ERRARG("msgid");
Michal Vasko086311b2016-01-08 09:53:11 +01001354 return NC_MSG_ERROR;
1355 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
Michal Vaskod083db62016-01-19 10:31:29 +01001356 ERR("Session %u: invalid session to send RPCs.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001357 return NC_MSG_ERROR;
1358 }
1359
Michal Vasko90e8e692016-07-13 12:27:57 +02001360 if ((rpc->type != NC_RPC_GETSCHEMA) && (rpc->type != NC_RPC_ACT_GENERIC) && (rpc->type != NC_RPC_SUBSCRIBE)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001361 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
1362 if (!ietfnc) {
Michal Vaskod083db62016-01-19 10:31:29 +01001363 ERR("Session %u: missing ietf-netconf schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001364 return NC_MSG_ERROR;
1365 }
1366 }
1367
1368 switch (rpc->type) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001369 case NC_RPC_ACT_GENERIC:
1370 rpc_gen = (struct nc_rpc_act_generic *)rpc;
Michal Vasko086311b2016-01-08 09:53:11 +01001371
1372 if (rpc_gen->has_data) {
1373 data = rpc_gen->content.data;
1374 } else {
Michal Vasko68b3f292016-09-16 12:00:32 +02001375 data = lyd_parse_mem(session->ctx, rpc_gen->content.xml_str, LYD_XML, LYD_OPT_RPC | LYD_OPT_STRICT, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001376 }
1377 break;
1378
1379 case NC_RPC_GETCONFIG:
1380 rpc_gc = (struct nc_rpc_getconfig *)rpc;
1381
1382 data = lyd_new(NULL, ietfnc, "get-config");
1383 node = lyd_new(data, ietfnc, "source");
1384 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_gc->source], NULL);
1385 if (!node) {
1386 lyd_free(data);
1387 return NC_MSG_ERROR;
1388 }
1389 if (rpc_gc->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01001390 if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) {
Radek Krejci539efb62016-08-24 15:05:16 +02001391 node = lyd_new_anydata(data, ietfnc, "filter", rpc_gc->filter, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001392 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01001393 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02001394 node = lyd_new_anydata(data, ietfnc, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001395 lyd_insert_attr(node, NULL, "type", "xpath");
1396 lyd_insert_attr(node, NULL, "select", rpc_gc->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01001397 }
1398 if (!node) {
1399 lyd_free(data);
1400 return NC_MSG_ERROR;
1401 }
1402 }
1403
1404 if (rpc_gc->wd_mode) {
1405 if (!ietfncwd) {
1406 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1407 if (!ietfncwd) {
Michal Vaskod083db62016-01-19 10:31:29 +01001408 ERR("Session %u: missing ietf-netconf-with-defaults schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001409 return NC_MSG_ERROR;
1410 }
1411 }
1412 switch (rpc_gc->wd_mode) {
1413 case NC_WD_UNKNOWN:
1414 /* cannot get here */
1415 break;
1416 case NC_WD_ALL:
1417 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1418 break;
1419 case NC_WD_ALL_TAG:
1420 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1421 break;
1422 case NC_WD_TRIM:
1423 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1424 break;
1425 case NC_WD_EXPLICIT:
1426 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1427 break;
1428 }
1429 if (!node) {
1430 lyd_free(data);
1431 return NC_MSG_ERROR;
1432 }
1433 }
1434 break;
1435
1436 case NC_RPC_EDIT:
1437 rpc_e = (struct nc_rpc_edit *)rpc;
1438
1439 data = lyd_new(NULL, ietfnc, "edit-config");
1440 node = lyd_new(data, ietfnc, "target");
1441 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_e->target], NULL);
1442 if (!node) {
1443 lyd_free(data);
1444 return NC_MSG_ERROR;
1445 }
1446
1447 if (rpc_e->default_op) {
1448 node = lyd_new_leaf(data, ietfnc, "default-operation", rpcedit_dfltop2str[rpc_e->default_op]);
1449 if (!node) {
1450 lyd_free(data);
1451 return NC_MSG_ERROR;
1452 }
1453 }
1454
1455 if (rpc_e->test_opt) {
1456 node = lyd_new_leaf(data, ietfnc, "test-option", rpcedit_testopt2str[rpc_e->test_opt]);
1457 if (!node) {
1458 lyd_free(data);
1459 return NC_MSG_ERROR;
1460 }
1461 }
1462
1463 if (rpc_e->error_opt) {
1464 node = lyd_new_leaf(data, ietfnc, "error-option", rpcedit_erropt2str[rpc_e->error_opt]);
1465 if (!node) {
1466 lyd_free(data);
1467 return NC_MSG_ERROR;
1468 }
1469 }
1470
Michal Vasko7793bc62016-09-16 11:58:41 +02001471 if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) {
Radek Krejci539efb62016-08-24 15:05:16 +02001472 node = lyd_new_anydata(data, ietfnc, "config", rpc_e->edit_cont, LYD_ANYDATA_CONSTSTRING);
Michal Vasko086311b2016-01-08 09:53:11 +01001473 } else {
1474 node = lyd_new_leaf(data, ietfnc, "url", rpc_e->edit_cont);
1475 }
1476 if (!node) {
1477 lyd_free(data);
1478 return NC_MSG_ERROR;
1479 }
1480 break;
1481
1482 case NC_RPC_COPY:
1483 rpc_cp = (struct nc_rpc_copy *)rpc;
1484
1485 data = lyd_new(NULL, ietfnc, "copy-config");
1486 node = lyd_new(data, ietfnc, "target");
1487 if (rpc_cp->url_trg) {
1488 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_trg);
1489 } else {
1490 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->target], NULL);
1491 }
1492 if (!node) {
1493 lyd_free(data);
1494 return NC_MSG_ERROR;
1495 }
1496
1497 node = lyd_new(data, ietfnc, "source");
1498 if (rpc_cp->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02001499 if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) {
Radek Krejci539efb62016-08-24 15:05:16 +02001500 node = lyd_new_anydata(node, ietfnc, "config", rpc_cp->url_config_src, LYD_ANYDATA_CONSTSTRING);
Michal Vasko086311b2016-01-08 09:53:11 +01001501 } else {
1502 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_config_src);
1503 }
1504 } else {
1505 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->source], NULL);
1506 }
1507 if (!node) {
1508 lyd_free(data);
1509 return NC_MSG_ERROR;
1510 }
1511
1512 if (rpc_cp->wd_mode) {
1513 if (!ietfncwd) {
1514 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1515 if (!ietfncwd) {
Michal Vaskod083db62016-01-19 10:31:29 +01001516 ERR("Session %u: missing ietf-netconf-with-defaults schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001517 return NC_MSG_ERROR;
1518 }
1519 }
1520 switch (rpc_cp->wd_mode) {
1521 case NC_WD_UNKNOWN:
1522 /* cannot get here */
1523 break;
1524 case NC_WD_ALL:
1525 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1526 break;
1527 case NC_WD_ALL_TAG:
1528 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1529 break;
1530 case NC_WD_TRIM:
1531 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1532 break;
1533 case NC_WD_EXPLICIT:
1534 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1535 break;
1536 }
1537 if (!node) {
1538 lyd_free(data);
1539 return NC_MSG_ERROR;
1540 }
1541 }
1542 break;
1543
1544 case NC_RPC_DELETE:
1545 rpc_del = (struct nc_rpc_delete *)rpc;
1546
1547 data = lyd_new(NULL, ietfnc, "delete-config");
1548 node = lyd_new(data, ietfnc, "target");
1549 if (rpc_del->url) {
1550 node = lyd_new_leaf(node, ietfnc, "url", rpc_del->url);
1551 } else {
1552 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_del->target], NULL);
1553 }
1554 if (!node) {
1555 lyd_free(data);
1556 return NC_MSG_ERROR;
1557 }
1558 break;
1559
1560 case NC_RPC_LOCK:
1561 rpc_lock = (struct nc_rpc_lock *)rpc;
1562
1563 data = lyd_new(NULL, ietfnc, "lock");
1564 node = lyd_new(data, ietfnc, "target");
1565 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
1566 if (!node) {
1567 lyd_free(data);
1568 return NC_MSG_ERROR;
1569 }
1570 break;
1571
1572 case NC_RPC_UNLOCK:
1573 rpc_lock = (struct nc_rpc_lock *)rpc;
1574
1575 data = lyd_new(NULL, ietfnc, "unlock");
1576 node = lyd_new(data, ietfnc, "target");
1577 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
1578 if (!node) {
1579 lyd_free(data);
1580 return NC_MSG_ERROR;
1581 }
1582 break;
1583
1584 case NC_RPC_GET:
1585 rpc_g = (struct nc_rpc_get *)rpc;
1586
1587 data = lyd_new(NULL, ietfnc, "get");
1588 if (rpc_g->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01001589 if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) {
Radek Krejci539efb62016-08-24 15:05:16 +02001590 node = lyd_new_anydata(data, ietfnc, "filter", rpc_g->filter, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001591 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01001592 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02001593 node = lyd_new_anydata(data, ietfnc, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001594 lyd_insert_attr(node, NULL, "type", "xpath");
1595 lyd_insert_attr(node, NULL, "select", rpc_g->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01001596 }
1597 if (!node) {
1598 lyd_free(data);
1599 return NC_MSG_ERROR;
1600 }
1601 }
1602
1603 if (rpc_g->wd_mode) {
1604 if (!ietfncwd) {
1605 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1606 if (!ietfncwd) {
Michal Vaskod083db62016-01-19 10:31:29 +01001607 ERR("Session %u: missing ietf-netconf-with-defaults schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001608 return NC_MSG_ERROR;
1609 }
1610 }
1611 switch (rpc_g->wd_mode) {
1612 case NC_WD_UNKNOWN:
1613 /* cannot get here */
1614 break;
1615 case NC_WD_ALL:
1616 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1617 break;
1618 case NC_WD_ALL_TAG:
1619 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1620 break;
1621 case NC_WD_TRIM:
1622 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1623 break;
1624 case NC_WD_EXPLICIT:
1625 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1626 break;
1627 }
1628 if (!node) {
1629 lyd_free(data);
1630 return NC_MSG_ERROR;
1631 }
1632 }
1633 break;
1634
1635 case NC_RPC_KILL:
1636 rpc_k = (struct nc_rpc_kill *)rpc;
1637
1638 data = lyd_new(NULL, ietfnc, "kill-session");
1639 sprintf(str, "%u", rpc_k->sid);
1640 lyd_new_leaf(data, ietfnc, "session-id", str);
1641 break;
1642
1643 case NC_RPC_COMMIT:
1644 rpc_com = (struct nc_rpc_commit *)rpc;
1645
1646 data = lyd_new(NULL, ietfnc, "commit");
1647 if (rpc_com->confirmed) {
1648 lyd_new_leaf(data, ietfnc, "confirmed", NULL);
1649 }
1650
1651 if (rpc_com->confirm_timeout) {
1652 sprintf(str, "%u", rpc_com->confirm_timeout);
1653 lyd_new_leaf(data, ietfnc, "confirm-timeout", str);
1654 }
1655
1656 if (rpc_com->persist) {
1657 node = lyd_new_leaf(data, ietfnc, "persist", rpc_com->persist);
1658 if (!node) {
1659 lyd_free(data);
1660 return NC_MSG_ERROR;
1661 }
1662 }
1663
1664 if (rpc_com->persist_id) {
1665 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_com->persist_id);
1666 if (!node) {
1667 lyd_free(data);
1668 return NC_MSG_ERROR;
1669 }
1670 }
1671 break;
1672
1673 case NC_RPC_DISCARD:
1674 data = lyd_new(NULL, ietfnc, "discard-changes");
1675 break;
1676
1677 case NC_RPC_CANCEL:
1678 rpc_can = (struct nc_rpc_cancel *)rpc;
1679
1680 data = lyd_new(NULL, ietfnc, "cancel-commit");
1681 if (rpc_can->persist_id) {
1682 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_can->persist_id);
1683 if (!node) {
1684 lyd_free(data);
1685 return NC_MSG_ERROR;
1686 }
1687 }
1688 break;
1689
1690 case NC_RPC_VALIDATE:
1691 rpc_val = (struct nc_rpc_validate *)rpc;
1692
1693 data = lyd_new(NULL, ietfnc, "validate");
1694 node = lyd_new(data, ietfnc, "source");
1695 if (rpc_val->url_config_src) {
Michal Vasko7793bc62016-09-16 11:58:41 +02001696 if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) {
Radek Krejci539efb62016-08-24 15:05:16 +02001697 node = lyd_new_anydata(node, ietfnc, "config", rpc_val->url_config_src, LYD_ANYDATA_CONSTSTRING);
Michal Vasko086311b2016-01-08 09:53:11 +01001698 } else {
1699 node = lyd_new_leaf(node, ietfnc, "url", rpc_val->url_config_src);
1700 }
1701 } else {
1702 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_val->source], NULL);
1703 }
1704 if (!node) {
1705 lyd_free(data);
1706 return NC_MSG_ERROR;
1707 }
1708 break;
1709
1710 case NC_RPC_GETSCHEMA:
1711 ietfncmon = ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL);
1712 if (!ietfncmon) {
Michal Vaskod083db62016-01-19 10:31:29 +01001713 ERR("Session %u: missing ietf-netconf-monitoring schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001714 return NC_MSG_ERROR;
1715 }
1716
1717 rpc_gs = (struct nc_rpc_getschema *)rpc;
1718
1719 data = lyd_new(NULL, ietfncmon, "get-schema");
1720 node = lyd_new_leaf(data, ietfncmon, "identifier", rpc_gs->identifier);
1721 if (!node) {
1722 lyd_free(data);
1723 return NC_MSG_ERROR;
1724 }
1725 if (rpc_gs->version) {
1726 node = lyd_new_leaf(data, ietfncmon, "version", rpc_gs->version);
1727 if (!node) {
1728 lyd_free(data);
1729 return NC_MSG_ERROR;
1730 }
1731 }
1732 if (rpc_gs->format) {
1733 node = lyd_new_leaf(data, ietfncmon, "format", rpc_gs->format);
1734 if (!node) {
1735 lyd_free(data);
1736 return NC_MSG_ERROR;
1737 }
1738 }
1739 break;
1740
1741 case NC_RPC_SUBSCRIBE:
1742 notifs = ly_ctx_get_module(session->ctx, "notifications", NULL);
1743 if (!notifs) {
Michal Vaskod083db62016-01-19 10:31:29 +01001744 ERR("Session %u: missing notifications schema in the context.", session->id);
Michal Vasko086311b2016-01-08 09:53:11 +01001745 return NC_MSG_ERROR;
1746 }
1747
1748 rpc_sub = (struct nc_rpc_subscribe *)rpc;
1749
1750 data = lyd_new(NULL, notifs, "create-subscription");
1751 if (rpc_sub->stream) {
1752 node = lyd_new_leaf(data, notifs, "stream", rpc_sub->stream);
1753 if (!node) {
1754 lyd_free(data);
1755 return NC_MSG_ERROR;
1756 }
1757 }
1758
1759 if (rpc_sub->filter) {
Michal Vaskof3c647b2016-03-08 12:17:33 +01001760 if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) {
Radek Krejci539efb62016-08-24 15:05:16 +02001761 node = lyd_new_anydata(data, notifs, "filter", rpc_sub->filter, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001762 lyd_insert_attr(node, NULL, "type", "subtree");
Michal Vasko086311b2016-01-08 09:53:11 +01001763 } else {
Radek Krejci539efb62016-08-24 15:05:16 +02001764 node = lyd_new_anydata(data, notifs, "filter", NULL, LYD_ANYDATA_CONSTSTRING);
Michal Vasko303245c2016-03-24 15:20:03 +01001765 lyd_insert_attr(node, NULL, "type", "xpath");
1766 lyd_insert_attr(node, NULL, "select", rpc_sub->filter);
Michal Vasko086311b2016-01-08 09:53:11 +01001767 }
1768 if (!node) {
1769 lyd_free(data);
1770 return NC_MSG_ERROR;
1771 }
1772 }
1773
1774 if (rpc_sub->start) {
1775 node = lyd_new_leaf(data, notifs, "startTime", rpc_sub->start);
1776 if (!node) {
1777 lyd_free(data);
1778 return NC_MSG_ERROR;
1779 }
1780 }
1781
1782 if (rpc_sub->stop) {
1783 node = lyd_new_leaf(data, notifs, "stopTime", rpc_sub->stop);
1784 if (!node) {
1785 lyd_free(data);
1786 return NC_MSG_ERROR;
1787 }
1788 }
1789 break;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001790 default:
1791 ERRINT;
1792 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001793 }
1794
Michal Vasko49a0cf92016-09-14 09:15:32 +02001795 if (lyd_validate(&data, LYD_OPT_RPC | LYD_OPT_STRICT, NULL)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001796 lyd_free(data);
1797 return NC_MSG_ERROR;
1798 }
1799
Michal Vasko62be1ce2016-03-03 13:24:52 +01001800 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001801 if (ret == -1) {
1802 /* error */
1803 r = NC_MSG_ERROR;
1804 } else if (!ret) {
1805 /* blocking */
Michal Vasko086311b2016-01-08 09:53:11 +01001806 r = NC_MSG_WOULDBLOCK;
1807 } else {
1808 /* send RPC, store its message ID */
1809 r = nc_send_msg(session, data);
1810 cur_msgid = session->msgid;
1811 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001812 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +01001813
1814 lyd_free(data);
1815
1816 if (r != NC_MSG_RPC) {
1817 return r;
1818 }
1819
1820 *msgid = cur_msgid;
1821 return NC_MSG_RPC;
1822}