blob: e7ca1da751b322ae7a32415a0b7b971668146753 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file session.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - input/output functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
23#include <assert.h>
24#include <errno.h>
Radek Krejci695d4fa2015-10-22 13:23:54 +020025#include <fcntl.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020026#include <netdb.h>
27#include <pthread.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020028#include <stdlib.h>
29#include <string.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020030#include <sys/socket.h>
Radek Krejci695d4fa2015-10-22 13:23:54 +020031#include <sys/stat.h>
32#include <sys/types.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020033#include <unistd.h>
34
35#include <libyang/libyang.h>
36
Radek Krejci206fcd62015-10-07 15:42:48 +020037#include "libnetconf.h"
Michal Vaskoad928112015-11-25 15:52:10 +010038#include "messages_p.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020039
40#define TIMEOUT_STEP 50
41
Radek Krejci695d4fa2015-10-22 13:23:54 +020042static NC_MSG_TYPE nc_send_hello_(struct nc_session *session);
43static NC_MSG_TYPE nc_recv_hello(struct nc_session *session);
44static NC_MSG_TYPE nc_send_rpc_(struct nc_session *session, struct lyd_node *op);
45
46static char *schema_searchpath = NULL;
47
48/* session configuration */
49static struct {
50 uint16_t hello_timeout; /**< hello-timeout in seconds, default is 600 */
51} cfg = {600};
52
53API int
54nc_schema_searchpath(const char *path)
55{
Michal Vaskoe90a41e2015-11-10 13:08:38 +010056 if (schema_searchpath) {
57 free(schema_searchpath);
58 }
Radek Krejci695d4fa2015-10-22 13:23:54 +020059 schema_searchpath = strdup(path);
60
61 return schema_searchpath ? 0 : 1;
62}
63
Radek Krejci5686ff72015-10-09 13:33:56 +020064/*
65 * @return 0 - success
66 * -1 - timeout
67 * >0 - error
68 */
69static int
70session_ti_lock(struct nc_session *session, int timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +020071{
72 int r;
Radek Krejci206fcd62015-10-07 15:42:48 +020073
74 if (timeout >= 0) {
75 /* limited waiting for lock */
76 do {
Radek Krejci695d4fa2015-10-22 13:23:54 +020077 r = pthread_mutex_trylock(session->ti_lock);
Radek Krejci206fcd62015-10-07 15:42:48 +020078 if (r == EBUSY) {
79 /* try later until timeout passes */
80 usleep(TIMEOUT_STEP);
81 timeout = timeout - TIMEOUT_STEP;
82 continue;
83 } else if (r) {
84 /* error */
85 ERR("Acquiring session (%u) TI lock failed (%s).", session->id, strerror(r));
Radek Krejci5686ff72015-10-09 13:33:56 +020086 return r;
Radek Krejci206fcd62015-10-07 15:42:48 +020087 } else {
88 /* lock acquired */
Radek Krejci5686ff72015-10-09 13:33:56 +020089 return 0;
Radek Krejci206fcd62015-10-07 15:42:48 +020090 }
91 } while(timeout > 0);
92
Radek Krejci5686ff72015-10-09 13:33:56 +020093 /* timeout has passed */
94 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +020095 } else {
96 /* infinite waiting for lock */
Radek Krejci695d4fa2015-10-22 13:23:54 +020097 return pthread_mutex_lock(session->ti_lock);
Radek Krejci5686ff72015-10-09 13:33:56 +020098 }
99}
100
101static int
102session_ti_unlock(struct nc_session *session)
103{
Radek Krejci695d4fa2015-10-22 13:23:54 +0200104 return pthread_mutex_unlock(session->ti_lock);
105}
106
Radek Krejciac6d3472015-10-22 15:47:18 +0200107int
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100108nc_handshake(struct nc_session *session)
Radek Krejciac6d3472015-10-22 15:47:18 +0200109{
110 NC_MSG_TYPE type;
111
112 type = nc_send_hello_(session);
113 if (type != NC_MSG_HELLO) {
114 return 1;
115 }
116
117 type = nc_recv_hello(session);
118 if (type != NC_MSG_HELLO) {
119 return 1;
120 }
121
122 return 0;
123}
124
Radek Krejci695d4fa2015-10-22 13:23:54 +0200125static int
Michal Vaskoad928112015-11-25 15:52:10 +0100126ctx_load_model(struct nc_session *session, const char *cpblt)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200127{
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100128 struct lys_module *module;
129 char *ptr, *ptr2;
130 char *model_name, *revision = NULL, *features = NULL;
131
Michal Vaskoad928112015-11-25 15:52:10 +0100132 /* parse module */
133 ptr = strstr(cpblt, "module=");
134 if (!ptr) {
135 WRN("Unknown capability \"%s\" could not be parsed.", cpblt);
136 return 1;
137 }
138 ptr += 7;
139 ptr2 = strchr(ptr, '&');
140 if (!ptr2) {
141 ptr2 = ptr + strlen(ptr);
142 }
143 model_name = strndup(ptr, ptr2 - ptr);
144
145 /* parse revision */
146 ptr = strstr(cpblt, "revision=");
147 if (ptr) {
148 ptr += 9;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100149 ptr2 = strchr(ptr, '&');
150 if (!ptr2) {
151 ptr2 = ptr + strlen(ptr);
152 }
Michal Vaskoad928112015-11-25 15:52:10 +0100153 revision = strndup(ptr, ptr2 - ptr);
154 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100155
Michal Vaskoad928112015-11-25 15:52:10 +0100156 /* load module */
157 module = ly_ctx_load_module(session->ctx, model_name, revision);
158
159 free(model_name);
160 free(revision);
161 if (!module) {
162 return 1;
163 }
164
165 /* parse features */
166 ptr = strstr(cpblt, "features=");
167 if (ptr) {
168 ptr += 9;
169 ptr2 = strchr(ptr, '&');
170 if (!ptr2) {
171 ptr2 = ptr + strlen(ptr);
172 }
173 features = strndup(ptr, ptr2 - ptr);
174 }
175
176 /* enable features */
177 if (features) {
178 /* basically manual strtok_r (to avoid macro) */
179 ptr2 = features;
180 for (ptr = features; *ptr; ++ptr) {
181 if (*ptr == ',') {
182 *ptr = '\0';
183 /* remember last feature */
184 ptr2 = ptr + 1;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100185 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100186 }
187
Michal Vaskoad928112015-11-25 15:52:10 +0100188 ptr = features;
189 lys_features_enable(module, ptr);
190 while (ptr != ptr2) {
191 ptr += strlen(ptr) + 1;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100192 lys_features_enable(module, ptr);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100193 }
Michal Vaskoad928112015-11-25 15:52:10 +0100194
195 free(features);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100196 }
197
198 return 0;
199}
200
201static int
202ctx_load_ietf_netconf(struct ly_ctx *ctx, const char **cpblts)
203{
204 int fd, i;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200205 struct lys_module *ietfnc;
206
207 fd = open(SCHEMAS_DIR"ietf-netconf.yin", O_RDONLY);
208 if (fd < 0) {
209 ERR("Loading base NETCONF schema (%s) failed (%s).", SCHEMAS_DIR"ietf-netconf", strerror(errno));
210 return 1;
211 }
212 if (!(ietfnc = lys_read(ctx, fd, LYS_IN_YIN))) {
213 ERR("Loading base NETCONF schema (%s) failed.", SCHEMAS_DIR"ietf-netconf");
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100214 close(fd);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200215 return 1;
216 }
217 close(fd);
218
219 /* set supported capabilities from ietf-netconf */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100220 for (i = 0; cpblts[i]; ++i) {
221 if (!strncmp(cpblts[i], "urn:ietf:params:netconf:capability:", 35)) {
222 if (!strncmp(cpblts[i] + 35, "writable-running", 16)) {
223 lys_features_enable(ietfnc, "writable-running");
224 } else if (!strncmp(cpblts[i] + 35, "candidate", 9)) {
225 lys_features_enable(ietfnc, "candidate");
226 } else if (!strcmp(cpblts[i] + 35, "confirmed-commit:1.1")) {
227 lys_features_enable(ietfnc, "confirmed-commit");
228 } else if (!strncmp(cpblts[i] + 35, "rollback-on-error", 17)) {
229 lys_features_enable(ietfnc, "rollback-on-error");
230 } else if (!strcmp(cpblts[i] + 35, "validate:1.1")) {
231 lys_features_enable(ietfnc, "validate");
232 } else if (!strncmp(cpblts[i] + 35, "startup", 7)) {
233 lys_features_enable(ietfnc, "startup");
234 } else if (!strncmp(cpblts[i] + 35, "url", 3)) {
235 lys_features_enable(ietfnc, "url");
236 } else if (!strncmp(cpblts[i] + 35, "xpath", 5)) {
237 lys_features_enable(ietfnc, "xpath");
238 }
239 }
240 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200241
242 return 0;
243}
244
Michal Vaskoad928112015-11-25 15:52:10 +0100245static char *
246libyang_module_clb(const char *name, const char *revision, void *user_data, LYS_INFORMAT *format,
247 void (**free_model_data)(char *model_data))
248{
249 struct nc_session *session = (struct nc_session *)user_data;
250 struct nc_rpc *rpc;
251 struct nc_reply *reply;
252 NC_MSG_TYPE msg;
253 char *model_data;
254
255 /* TODO later replace with yang to reduce model size? */
256 rpc = nc_rpc_getschema(name, revision, "yin");
257 *format = LYS_IN_YIN;
258
259 while ((msg = nc_send_rpc(session, rpc)) == NC_MSG_WOULDBLOCK) {
260 usleep(1000);
261 }
262 if (msg == NC_MSG_ERROR) {
263 ERR("Failed to send the <get-schema> RPC.");
264 nc_rpc_free(rpc);
265 return NULL;
266 }
267 nc_rpc_free(rpc);
268
269 msg = nc_recv_reply(session, 250, &reply);
270 if (msg == NC_MSG_WOULDBLOCK) {
271 ERR("Timeout for receiving reply to a <get-schema> expired.");
272 return NULL;
273 } else if (msg == NC_MSG_ERROR) {
274 ERR("Failed to receive a reply to <get-schema>.");
275 return NULL;
276 }
277
278 /* TODO get data from reply */
279 model_data = NULL;
280 *free_model_data = NULL;
281
282 return model_data;
283}
284
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100285/* session with an empty context is assumed */
286int
287nc_ctx_fill(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200288{
Michal Vaskoad928112015-11-25 15:52:10 +0100289 int i;
290 ly_module_clb old_clb = NULL;
291 void *old_data = NULL;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200292
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100293 assert(session->cpblts && session->ctx);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200294
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100295 /* check if get-schema is supported */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100296 for (i = 0; session->cpblts[i]; ++i) {
297 if (!strncmp(session->cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
Michal Vaskoad928112015-11-25 15:52:10 +0100298 old_clb = ly_ctx_get_module_clb(session->ctx, &old_data);
299 ly_ctx_set_module_clb(session->ctx, &libyang_module_clb, session);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100300 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200301 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200302 }
303
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100304 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
305 if (ctx_load_ietf_netconf(session->ctx, session->cpblts)) {
Michal Vaskoad928112015-11-25 15:52:10 +0100306 ly_ctx_set_module_clb(session->ctx, old_clb, old_data);
307 return 1;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100308 }
309
310 /* load all other models */
311 for (i = 0; session->cpblts[i]; ++i) {
312 if (!strncmp(session->cpblts[i], "urn:ietf:params:netconf:capability", 34)
313 || !strncmp(session->cpblts[i], "urn:ietf:params:netconf:base", 28)) {
314 continue;
315 }
316
Michal Vaskoad928112015-11-25 15:52:10 +0100317 ctx_load_model(session, session->cpblts[i]);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100318 }
319
Michal Vaskoad928112015-11-25 15:52:10 +0100320 ly_ctx_set_module_clb(session->ctx, old_clb, old_data);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100321 return 0;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100322}
323
324int
325nc_ctx_check(struct nc_session *session)
326{
327 /* check presence of the required base schema */
328 if (!ly_ctx_get_module(session->ctx, "ietf-netconf", NULL)) {
329 if (ctx_load_ietf_netconf(session->ctx, session->cpblts)) {
330 return 1;
331 }
332 }
333
334 return 0;
Radek Krejciac6d3472015-10-22 15:47:18 +0200335}
336
337API struct nc_session *
338nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
339{
340 struct nc_session *session = NULL;
341
342 if (fdin < 0 || fdout < 0) {
343 ERR("%s: Invalid parameter", __func__);
344 return NULL;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200345 }
346
Radek Krejciac6d3472015-10-22 15:47:18 +0200347 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100348 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +0200349 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100350 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +0200351 return NULL;
352 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100353 session->status = NC_STATUS_STARTING;
354 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +0200355
356 /* transport specific data */
357 session->ti_type = NC_TI_FD;
358 session->ti.fd.in = fdin;
359 session->ti.fd.out = fdout;
360
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100361 /* assign context (dicionary needed for handshake) */
362 if (!ctx) {
363 ctx = ly_ctx_new(SCHEMAS_DIR);
364 } else {
365 session->flags |= NC_SESSION_SHAREDCTX;
366 }
367 session->ctx = ctx;
368
Radek Krejciac6d3472015-10-22 15:47:18 +0200369 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100370 if (nc_handshake(session)) {
371 goto fail;
372 }
373
374 /* check/fill libyang context */
375 if (session->flags & NC_SESSION_SHAREDCTX) {
376 if (nc_ctx_check(session)) {
377 goto fail;
378 }
379 } else {
380 if (nc_ctx_fill(session)) {
381 goto fail;
382 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200383 }
384
385 session->status = NC_STATUS_RUNNING;
386 return session;
387
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100388fail:
Radek Krejci695d4fa2015-10-22 13:23:54 +0200389 nc_session_free(session);
390 return NULL;
391}
392
Radek Krejciac6d3472015-10-22 15:47:18 +0200393int
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100394nc_connect_getsocket(const char* host, unsigned short port)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200395{
Radek Krejciac6d3472015-10-22 15:47:18 +0200396 int sock = -1;
397 int i;
398 struct addrinfo hints, *res_list, *res;
399 char port_s[6]; /* length of string representation of short int */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200400
Radek Krejciac6d3472015-10-22 15:47:18 +0200401 snprintf(port_s, 6, "%u", port);
402
403 /* Connect to a server */
404 memset(&hints, 0, sizeof hints);
405 hints.ai_family = AF_UNSPEC;
406 hints.ai_socktype = SOCK_STREAM;
407 hints.ai_protocol = IPPROTO_TCP;
408 i = getaddrinfo(host, port_s, &hints, &res_list);
409 if (i != 0) {
410 ERR("Unable to translate the host address (%s).", gai_strerror(i));
411 return -1;
412 }
413
414 for (i = 0, res = res_list; res != NULL; res = res->ai_next) {
415 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
416 if (sock == -1) {
417 /* socket was not created, try another resource */
418 i = errno;
419 goto errloop;
420 }
421
422 if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
423 /* network connection failed, try another resource */
424 i = errno;
425 close(sock);
426 sock = -1;
427 goto errloop;
428 }
429
430 /* we're done, network connection established */
431 break;
432errloop:
Michal Vaskoaebea602015-10-26 15:35:34 +0100433 VRB("Unable to connect to %s:%s over %s (%s).", host, port_s,
Radek Krejciac6d3472015-10-22 15:47:18 +0200434 (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", strerror(i));
435 continue;
436 }
437
438 if (sock == -1) {
Michal Vaskoaebea602015-10-26 15:35:34 +0100439 ERR("Unable to connect to %s:%s.", host, port_s);
Radek Krejciac6d3472015-10-22 15:47:18 +0200440 } else {
Michal Vaskoaebea602015-10-26 15:35:34 +0100441 VRB("Successfully connected to %s:%s over %s", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
Radek Krejciac6d3472015-10-22 15:47:18 +0200442 }
443 freeaddrinfo(res_list);
444
445 return sock;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200446}
447
Radek Krejci695d4fa2015-10-22 13:23:54 +0200448API void
449nc_session_free(struct nc_session *session)
450{
451 int r, i;
452 int multisession = 0; /* flag for more NETCONF session on a single SSH session */
453 struct nc_session *siter;
454 struct nc_notif_cont *ntfiter;
455 struct nc_reply_cont *rpliter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100456 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200457 struct lyd_node *close_rpc;
458 struct lys_module *ietfnc;
459 void *p;
460
Michal Vaskoadd4c792015-10-26 15:36:58 +0100461 if (!session || session->status == NC_STATUS_CLOSING) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200462 return;
463 }
464
465 /* mark session for closing */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100466 if (session->ti_lock) {
467 do {
468 r = session_ti_lock(session, 0);
469 } while (r < 0);
470 if (r) {
471 return;
472 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200473 }
474
475 /* stop notifications loop if any */
476 if (session->notif) {
477 pthread_cancel(*session->notif);
478 pthread_join(*session->notif, NULL);
479 }
480
481 if (session->side == NC_CLIENT && session->status == NC_STATUS_RUNNING) {
482 /* cleanup message queues */
483 /* notifications */
484 for (ntfiter = session->notifs; ntfiter; ) {
485 nc_notif_free(ntfiter->msg);
486
487 p = ntfiter;
488 ntfiter = ntfiter->next;
489 free(p);
490 }
491
492 /* rpc replies */
493 for (rpliter = session->replies; rpliter; ) {
494 nc_reply_free(rpliter->msg);
495
496 p = rpliter;
497 rpliter = rpliter->next;
498 free(p);
499 }
500
501 /* send closing info to the other side */
502 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
503 if (!ietfnc) {
504 WRN("%s: Missing ietf-netconf schema in context (session %u), unable to send <close-session\\>", session->id);
505 } else {
506 close_rpc = lyd_new(NULL, ietfnc, "close-session");
507 nc_send_rpc_(session, close_rpc);
508 lyd_free(close_rpc);
Michal Vaskofad6e912015-10-26 15:37:22 +0100509 switch (nc_read_msg(session, 200, &rpl)) {
510 case NC_MSG_REPLY:
511 LY_TREE_FOR(rpl->child, child) {
512 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
513 break;
514 }
515 }
516 if (!child) {
517 WRN("The reply to <close-session\\> was not <ok\\> as expected.");
518 }
519 lyxml_free_elem(session->ctx, rpl);
520 break;
521 case NC_MSG_WOULDBLOCK:
522 WRN("Timeout for receiving a reply to <close-session\\> elapsed.");
523 break;
524 case NC_MSG_ERROR:
525 ERR("Failed to receive a reply to <close-session\\>.");
526 break;
527 default:
528 /* cannot happen */
529 break;
530 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200531 }
532
533 /* list of server's capabilities */
534 if (session->cpblts) {
535 for (i = 0; session->cpblts[i]; i++) {
536 lydict_remove(session->ctx, session->cpblts[i]);
537 }
538 free(session->cpblts);
539 }
540 }
541
542 session->status = NC_STATUS_CLOSING;
543
544 /* transport implementation cleanup */
545 switch (session->ti_type) {
546 case NC_TI_FD:
547 /* nothing needed - file descriptors were provided by caller,
548 * so it is up to the caller to close them correctly
549 * TODO use callbacks
550 */
551 break;
552
Michal Vaskofb2fb762015-10-27 11:44:32 +0100553#ifdef ENABLE_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200554 case NC_TI_LIBSSH:
555 ssh_channel_free(session->ti.libssh.channel);
556 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
557 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
558 * it.
559 */
560 if (!session->ti.libssh.next) {
561 ssh_disconnect(session->ti.libssh.session);
562 ssh_free(session->ti.libssh.session);
563 } else {
564 /* multiple NETCONF sessions on a single SSH session */
565 multisession = 1;
566 /* remove the session from the list */
567 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100568 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200569 /* there will be only one session */
570 siter->ti.libssh.next = NULL;
571 } else {
572 /* there are still multiple sessions, keep the ring list */
573 siter->ti.libssh.next = session->ti.libssh.next;
574 }
575 }
576 break;
577#endif
578
579#ifdef ENABLE_TLS
580 case NC_TI_OPENSSL:
581 SSL_shutdown(session->ti.tls);
582 SSL_free(session->ti.tls);
583 break;
584#endif
585 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200586 lydict_remove(session->ctx, session->username);
587 lydict_remove(session->ctx, session->host);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200588
589 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100590 if (session->ti_lock) {
591 if (multisession) {
592 session_ti_unlock(session);
593 } else {
594 pthread_mutex_destroy(session->ti_lock);
595 free(session->ti_lock);
596 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200597 }
598
599 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
600 ly_ctx_destroy(session->ctx);
601 }
602
603 free(session);
604}
605
606static int
607parse_cpblts(struct lyxml_elem *xml, const char ***list)
608{
609 struct lyxml_elem *cpblt;
610 int ver = -1;
611 int i = 0;
612
613 if (list) {
614 /* get the storage for server's capabilities */
615 LY_TREE_FOR(xml->child, cpblt) {
616 i++;
617 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100618 /* last item remains NULL */
619 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200620 if (!*list) {
621 ERRMEM;
622 return -1;
623 }
624 i = 0;
625 }
626
627 LY_TREE_FOR(xml->child, cpblt) {
628 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
629 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
630 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
631 return -1;
632 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
633 continue;
634 }
635
636 /* detect NETCONF version */
637 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
638 ver = 0;
639 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
640 ver = 1;
641 }
642
643 /* store capabilities */
644 if (list) {
645 (*list)[i] = cpblt->content;
646 cpblt->content = NULL;
647 i++;
648 }
649 }
650
651 if (ver == -1) {
652 ERR("Peer does not support compatible NETCONF version.");
653 }
654
655 return ver;
656}
657
658static NC_MSG_TYPE
659nc_recv_hello(struct nc_session *session)
660{
661 struct lyxml_elem *xml = NULL, *node;
662 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
663 int ver = -1;
664 char *str;
665 long long int id;
666 int flag = 0;
667
668 msgtype = nc_read_msg(session, cfg.hello_timeout * 1000, &xml);
669
670 switch(msgtype) {
671 case NC_MSG_HELLO:
672 /* parse <hello> data */
673 if (session->side == NC_SERVER) {
674 /* get know NETCONF version */
675 LY_TREE_FOR(xml->child, node) {
676 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
677 continue;
678 } else if (strcmp(node->name, "capabilities")) {
679 ERR("Unexpected <%s> element in client's <hello>.", node->name);
680 goto error;
681 }
682
683 if (flag) {
684 /* multiple capabilities elements */
685 ERR("Invalid <hello> message (multiple <capabilities> elements)");
686 goto error;
687 }
688 flag = 1;
689
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100690 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200691 goto error;
692 }
693 session->version = ver;
694 }
695 } else { /* NC_CLIENT */
696 LY_TREE_FOR(xml->child, node) {
697 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
698 continue;
699 } else if (!strcmp(node->name, "session-id")) {
700 if (!node->content || !strlen(node->content)) {
701 ERR("No value of <session-id> element in server's <hello>");
702 goto error;
703 }
704 str = NULL;
705 id = strtoll(node->content, &str, 10);
706 if (*str || id < 1 || id > UINT32_MAX) {
707 ERR("Invalid value of <session-id> element in server's <hello>");
708 goto error;
709 }
710 session->id = (uint32_t)id;
711 continue;
712 } else if (strcmp(node->name, "capabilities")) {
713 ERR("Unexpected <%s> element in client's <hello>.", node->name);
714 goto error;
715 }
716
717 if (flag) {
718 /* multiple capabilities elements */
719 ERR("Invalid <hello> message (multiple <capabilities> elements)");
720 goto error;
721 }
722 flag = 1;
723
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100724 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200725 goto error;
726 }
727 session->version = ver;
728 }
729
730 if (!session->id) {
731 ERR("Missing <session-id> in server's <hello>");
732 goto error;
733 }
734 }
735 break;
736 case NC_MSG_ERROR:
737 /* nothing special, just pass it out */
738 break;
739 default:
740 ERR("Unexpected message received instead of <hello>.");
741 msgtype = NC_MSG_ERROR;
742 }
743
744 /* cleanup */
745 lyxml_free_elem(session->ctx, xml);
746
747 return msgtype;
748
749error:
750 /* cleanup */
751 lyxml_free_elem(session->ctx, xml);
752
753 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +0200754}
755
756API NC_MSG_TYPE
Radek Krejci695d4fa2015-10-22 13:23:54 +0200757nc_recv_rpc(struct nc_session *session, int timeout, struct nc_rpc_server **rpc)
Radek Krejci5686ff72015-10-09 13:33:56 +0200758{
759 int r;
760 struct lyxml_elem *xml = NULL;
761 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
762
763 if (!session || !rpc) {
764 ERR("%s: Invalid parameter", __func__);
765 return NC_MSG_ERROR;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200766 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_SERVER) {
767 ERR("%s: invalid session to receive RPCs.", __func__);
Radek Krejci5686ff72015-10-09 13:33:56 +0200768 return NC_MSG_ERROR;
769 }
770
771 r = session_ti_lock(session, timeout);
772 if (r > 0) {
773 /* error */
774 return NC_MSG_ERROR;
775 } else if (r < 0) {
776 /* timeout */
777 return NC_MSG_WOULDBLOCK;
Radek Krejci206fcd62015-10-07 15:42:48 +0200778 }
779
780 msgtype = nc_read_msg(session, timeout, &xml);
Radek Krejci5686ff72015-10-09 13:33:56 +0200781 session_ti_unlock(session);
Radek Krejci206fcd62015-10-07 15:42:48 +0200782
Radek Krejci5686ff72015-10-09 13:33:56 +0200783 switch(msgtype) {
784 case NC_MSG_RPC:
Radek Krejcia53b3fe2015-10-19 17:25:04 +0200785 *rpc = malloc(sizeof **rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200786 (*rpc)->type = NC_RPC_SERVER;
Radek Krejci61a20302015-10-31 22:57:55 +0100787 (*rpc)->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
Radek Krejci5686ff72015-10-09 13:33:56 +0200788 (*rpc)->root = xml;
789 break;
790 case NC_MSG_HELLO:
791 ERR("SESSION %u: Received another <hello> message.", session->id);
792 goto error;
793 case NC_MSG_REPLY:
794 ERR("SESSION %u: Received <rpc-reply> from NETCONF client.", session->id);
795 goto error;
796 case NC_MSG_NOTIF:
797 ERR("SESSION %u: Received <notification> from NETCONF client.", session->id);
798 goto error;
799 default:
800 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
801 * NC_MSG_NONE is not returned by nc_read_msg()
802 */
803 break;
804 }
Radek Krejci0dc69f72015-10-08 15:32:09 +0200805
Radek Krejci206fcd62015-10-07 15:42:48 +0200806 return msgtype;
Radek Krejci5686ff72015-10-09 13:33:56 +0200807
808error:
809
810 /* cleanup */
811 lyxml_free_elem(session->ctx, xml);
812
813 return NC_MSG_ERROR;
814}
815
Radek Krejci8800a492015-10-31 17:51:27 +0100816static struct nc_reply *
817parse_reply(struct ly_ctx *ctx, struct lyxml_elem *xml)
818{
819 struct lyxml_elem *iter;
Michal Vaskoad928112015-11-25 15:52:10 +0100820 struct nc_reply_error *error;
821 struct nc_reply_ok *ok;
822 struct nc_reply_data *data;
823 struct nc_reply *reply;
Radek Krejci8800a492015-10-31 17:51:27 +0100824
825 LY_TREE_FOR(xml->child, iter) {
826 if (!iter->ns || strcmp(iter->ns->value, NC_NS_BASE)) {
827 continue;
828 }
829
830 if (!strcmp(iter->name, "ok")) {
831 if (reply) {
832 ERR("Unexpected content of the <rpc-reply>.");
833 goto error;
834 }
Michal Vaskoad928112015-11-25 15:52:10 +0100835 ok = malloc(sizeof *ok);
836 ok->type = NC_REPLY_OK;
837 ok->ctx = ctx;
838 ok->root = xml;
839 reply = (struct nc_reply *)ok;
Radek Krejci8800a492015-10-31 17:51:27 +0100840 } else if (!strcmp(iter->name, "data")) {
841 if (reply) {
842 ERR("Unexpected content of the <rpc-reply>.");
843 goto error;
844 }
Michal Vaskoad928112015-11-25 15:52:10 +0100845 data = malloc(sizeof *data);
846 data->type = NC_REPLY_DATA;
847 data->root = xml;
848 data->data = lyd_parse_xml(ctx, iter, LYD_OPT_DESTRUCT);
849 reply = (struct nc_reply *)data;
Radek Krejci8800a492015-10-31 17:51:27 +0100850 } else if (!strcmp(iter->name, "rpc-error")) {
Michal Vaskoad928112015-11-25 15:52:10 +0100851 if (reply && (reply->type != NC_REPLY_ERROR)) {
Radek Krejci8800a492015-10-31 17:51:27 +0100852 ERR("<rpc-reply> content mismatch.");
853 goto error;
854 }
Michal Vaskoad928112015-11-25 15:52:10 +0100855 /* TODO */
856 error = malloc(sizeof *error);
857 error->type = NC_REPLY_ERROR;
858 reply = (struct nc_reply *)error;
Radek Krejci8800a492015-10-31 17:51:27 +0100859 }
860 }
861
862 if (!reply) {
863 ERR("Invalid content of the <rpc-reply>.");
864 }
865 return reply;
866
867error:
868 if (reply) {
869 reply->root = NULL;
870 nc_reply_free(reply);
871 }
872 return NULL;
873}
874
Radek Krejci5686ff72015-10-09 13:33:56 +0200875API NC_MSG_TYPE
Radek Krejci695d4fa2015-10-22 13:23:54 +0200876nc_recv_reply(struct nc_session *session, int timeout, struct nc_reply **reply)
Radek Krejci5686ff72015-10-09 13:33:56 +0200877{
878 int r;
879 struct lyxml_elem *xml;
880 struct nc_reply_cont *cont_r;
881 struct nc_notif_cont **cont_n;
882 struct nc_notif *notif;
883 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
884
885 if (!session || !reply) {
886 ERR("%s: Invalid parameter", __func__);
887 return NC_MSG_ERROR;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200888 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
889 ERR("%s: invalid session to receive RPC replies.", __func__);
Radek Krejci5686ff72015-10-09 13:33:56 +0200890 return NC_MSG_ERROR;
891 }
Radek Krejci8800a492015-10-31 17:51:27 +0100892 *reply = NULL;
Radek Krejci5686ff72015-10-09 13:33:56 +0200893
894 do {
895 if (msgtype && session->notif) {
896 /* second run, wait and give a chance to nc_recv_notif() */
897 usleep(TIMEOUT_STEP);
898 timeout = timeout - (TIMEOUT_STEP);
899 }
900 r = session_ti_lock(session, timeout);
901 if (r > 0) {
902 /* error */
903 return NC_MSG_ERROR;
904 } else if (r < 0) {
905 /* timeout */
906 return NC_MSG_WOULDBLOCK;
907 }
908
909 /* try to get message from the session's queue */
910 if (session->notifs) {
911 cont_r = session->replies;
912 session->replies = cont_r->next;
913
914 session_ti_unlock(session);
915
916 *reply = cont_r->msg;
917 free(cont_r);
918
919 return NC_MSG_REPLY;
920 }
921
922 /* read message from wire */
923 msgtype = nc_read_msg(session, timeout, &xml);
924 if (msgtype == NC_MSG_NOTIF) {
925 if (!session->notif) {
926 session_ti_unlock(session);
927 ERR("SESSION %u: Received Notification but session is not subscribed.", session->id);
928 goto error;
929 }
930
931 /* create notification object */
Radek Krejcia53b3fe2015-10-19 17:25:04 +0200932 notif = malloc(sizeof *notif);
Radek Krejci61a20302015-10-31 22:57:55 +0100933 notif->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
Radek Krejci5686ff72015-10-09 13:33:56 +0200934 notif->root = xml;
935
936 /* store the message for nc_recv_notif() */
937 cont_n = &session->notifs;
938 while(*cont_n) {
939 cont_n = &((*cont_n)->next);
940 }
941 *cont_n = malloc(sizeof **cont_n);
942 (*cont_n)->msg = notif;
943 (*cont_n)->next = NULL;
944 }
945
946 session_ti_unlock(session);
947
948 switch(msgtype) {
949 case NC_MSG_REPLY:
Radek Krejci8800a492015-10-31 17:51:27 +0100950 /* distinguish between data / ok / error reply */
951 *reply = parse_reply(session->ctx, xml);
952 if (!(*reply)) {
953 goto error;
954 }
Radek Krejci5686ff72015-10-09 13:33:56 +0200955 break;
956 case NC_MSG_HELLO:
957 ERR("SESSION %u: Received another <hello> message.", session->id);
958 goto error;
959 case NC_MSG_RPC:
960 ERR("SESSION %u: Received <rpc> from NETCONF server.", session->id);
961 goto error;
962 default:
963 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
964 * NC_MSG_NOTIF already handled before the switch;
965 * NC_MSG_NONE is not returned by nc_read_msg()
966 */
967 break;
968 }
969
970 } while(msgtype == NC_MSG_NOTIF);
971
972 return msgtype;
973
974error:
975
976 /* cleanup */
977 lyxml_free_elem(session->ctx, xml);
978
979 return NC_MSG_ERROR;
980}
981
982API NC_MSG_TYPE
Radek Krejci695d4fa2015-10-22 13:23:54 +0200983nc_recv_notif(struct nc_session *session, int timeout, struct nc_notif **notif)
Radek Krejci5686ff72015-10-09 13:33:56 +0200984{
985 int r;
986 struct lyxml_elem *xml;
987 struct nc_notif_cont *cont_n;
988 struct nc_reply_cont **cont_r;
989 struct nc_reply *reply;
990 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
991
992 if (!session || !notif) {
993 ERR("%s: Invalid parameter", __func__);
994 return NC_MSG_ERROR;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200995 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
996 ERR("%s: invalid session to receive Notifications.", __func__);
Radek Krejci5686ff72015-10-09 13:33:56 +0200997 return NC_MSG_ERROR;
998 }
999
1000 do {
1001 if (msgtype) {
1002 /* second run, wait and give a chance to nc_recv_reply() */
1003 usleep(TIMEOUT_STEP);
1004 timeout = timeout - (TIMEOUT_STEP);
1005 }
1006 r = session_ti_lock(session, timeout);
1007 if (r > 0) {
1008 /* error */
1009 return NC_MSG_ERROR;
1010 } else if (r < 0) {
1011 /* timeout */
1012 return NC_MSG_WOULDBLOCK;
1013 }
1014
1015 /* try to get message from the session's queue */
1016 if (session->notifs) {
1017 cont_n = session->notifs;
1018 session->notifs = cont_n->next;
1019
1020 session_ti_unlock(session);
1021
1022 *notif = cont_n->msg;
1023 free(cont_n);
1024
1025 return NC_MSG_NOTIF;
1026 }
1027
1028 /* read message from wire */
1029 msgtype = nc_read_msg(session, timeout, &xml);
1030 if (msgtype == NC_MSG_REPLY) {
Radek Krejci8800a492015-10-31 17:51:27 +01001031 /* distinguish between data / ok / error reply */
1032 reply = parse_reply(session->ctx, xml);
1033 if (!reply) {
1034 goto error;
1035 }
Radek Krejci5686ff72015-10-09 13:33:56 +02001036
1037 /* store the message for nc_recv_reply() */
1038 cont_r = &session->replies;
1039 while(*cont_r) {
1040 cont_r = &((*cont_r)->next);
1041 }
1042 *cont_r = malloc(sizeof **cont_r);
1043 (*cont_r)->msg = reply;
1044 (*cont_r)->next = NULL;
1045 }
1046
1047 session_ti_unlock(session);
1048
1049 switch(msgtype) {
1050 case NC_MSG_NOTIF:
Radek Krejcia53b3fe2015-10-19 17:25:04 +02001051 *notif = malloc(sizeof **notif);
Radek Krejci61a20302015-10-31 22:57:55 +01001052 (*notif)->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
Radek Krejci5686ff72015-10-09 13:33:56 +02001053 (*notif)->root = xml;
1054 break;
1055 case NC_MSG_HELLO:
1056 ERR("SESSION %u: Received another <hello> message.", session->id);
1057 goto error;
1058 case NC_MSG_RPC:
1059 ERR("SESSION %u: Received <rpc> from NETCONF server.", session->id);
1060 goto error;
1061 default:
1062 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
1063 * NC_MSG_REPLY already handled before the switch;
1064 * NC_MSG_NONE is not returned by nc_read_msg()
1065 */
1066 break;
1067 }
1068
Radek Krejci8800a492015-10-31 17:51:27 +01001069 } while(msgtype == NC_MSG_NOTIF);
Radek Krejci5686ff72015-10-09 13:33:56 +02001070
1071 return msgtype;
1072
1073error:
1074
1075 /* cleanup */
1076 lyxml_free_elem(session->ctx, xml);
1077
1078 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +02001079}
Radek Krejcife0b3472015-10-12 13:43:42 +02001080
Radek Krejci695d4fa2015-10-22 13:23:54 +02001081static NC_MSG_TYPE
1082nc_send_hello_(struct nc_session *session)
1083{
1084 int r;
1085 char **cpblts;
1086
1087 if (session->side == NC_CLIENT) {
1088 /* client side hello - send only NETCONF base capabilities */
1089 cpblts = malloc(3 * sizeof *cpblts);
1090 cpblts[0] = "urn:ietf:params:netconf:base:1.0";
1091 cpblts[1] = "urn:ietf:params:netconf:base:1.1";
1092 cpblts[2] = NULL;
1093
1094 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
1095 free(cpblts);
1096 }
1097
1098
1099 if (r) {
1100 return NC_MSG_ERROR;
1101 } else {
1102 return NC_MSG_HELLO;
1103 }
1104}
1105
1106static NC_MSG_TYPE
1107nc_send_rpc_(struct nc_session *session, struct lyd_node *op)
Radek Krejcife0b3472015-10-12 13:43:42 +02001108{
1109 int r;
1110
Michal Vaskoad928112015-11-25 15:52:10 +01001111 if (session->ctx != op->schema->module->ctx) {
1112 ERR("RPC \"%s\" was created in different context than that of \"%s\" session %u.",
1113 op->schema->name, session->username, session->id);
1114 return NC_MSG_ERROR;
1115 }
1116
Radek Krejci695d4fa2015-10-22 13:23:54 +02001117 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
Radek Krejcife0b3472015-10-12 13:43:42 +02001118
1119 if (r) {
1120 return NC_MSG_ERROR;
Radek Krejcife0b3472015-10-12 13:43:42 +02001121 }
Michal Vaskoad928112015-11-25 15:52:10 +01001122
1123 return NC_MSG_RPC;
Radek Krejcife0b3472015-10-12 13:43:42 +02001124}
1125
Radek Krejci695d4fa2015-10-22 13:23:54 +02001126API NC_MSG_TYPE
1127nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc)
1128{
1129 NC_MSG_TYPE r;
Michal Vaskoad928112015-11-25 15:52:10 +01001130 struct nc_rpc_generic *rpc_gen;
1131 struct nc_rpc_generic_xml *rpc_gen_xml;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001132 struct nc_rpc_getconfig *rpc_gc;
Michal Vaskoad928112015-11-25 15:52:10 +01001133 struct nc_rpc_edit *rpc_e;
1134 struct nc_rpc_copy *rpc_cp;
1135 struct nc_rpc_delete *rpc_del;
1136 struct nc_rpc_lock *rpc_lock;
Radek Krejci926a5742015-10-31 17:50:49 +01001137 struct nc_rpc_get *rpc_g;
Michal Vaskoad928112015-11-25 15:52:10 +01001138 struct nc_rpc_kill *rpc_k;
1139 struct nc_rpc_commit *rpc_com;
1140 struct nc_rpc_cancel *rpc_can;
1141 struct nc_rpc_validate *rpc_val;
1142 struct nc_rpc_getschema *rpc_gs;
1143 struct nc_rpc_subscribe *rpc_sub;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001144 struct lyd_node *data, *node;
Michal Vaskoad928112015-11-25 15:52:10 +01001145 struct lys_module *ietfnc, *ietfncmon, *notifs;
1146 char str[11];
Radek Krejci695d4fa2015-10-22 13:23:54 +02001147
1148 if (!session || !rpc || rpc->type == NC_RPC_SERVER) {
1149 ERR("%s: Invalid parameter", __func__);
1150 return NC_MSG_ERROR;
1151 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
1152 ERR("%s: invalid session to send RPCs.", __func__);
1153 return NC_MSG_ERROR;
1154 }
1155
Michal Vaskoad928112015-11-25 15:52:10 +01001156 if ((rpc->type != NC_RPC_GETSCHEMA) && (rpc->type != NC_RPC_GENERIC)
1157 && (rpc->type != NC_RPC_GENERIC_XML) && (rpc->type != NC_RPC_SUBSCRIBE)) {
1158 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
1159 if (!ietfnc) {
1160 ERR("%s: Missing ietf-netconf schema in context (session %u)", session->id);
1161 return NC_MSG_ERROR;
1162 }
Radek Krejci695d4fa2015-10-22 13:23:54 +02001163 }
1164
Michal Vaskoad928112015-11-25 15:52:10 +01001165 switch (rpc->type) {
1166 case NC_RPC_GENERIC:
1167 rpc_gen = (struct nc_rpc_generic *)rpc;
Radek Krejci926a5742015-10-31 17:50:49 +01001168
Michal Vaskoad928112015-11-25 15:52:10 +01001169 data = rpc_gen->data;
Radek Krejci926a5742015-10-31 17:50:49 +01001170 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001171
1172 case NC_RPC_GENERIC_XML:
1173 rpc_gen_xml = (struct nc_rpc_generic_xml *)rpc;
1174
1175 data = lyd_parse(session->ctx, rpc_gen_xml->xml_str, LYD_XML, LYD_OPT_STRICT);
1176 break;
1177
Radek Krejci695d4fa2015-10-22 13:23:54 +02001178 case NC_RPC_GETCONFIG:
1179 rpc_gc = (struct nc_rpc_getconfig *)rpc;
1180
1181 data = lyd_new(NULL, ietfnc, "get-config");
1182 node = lyd_new(data, ietfnc, "source");
Radek Krejcib68f7c22015-10-31 11:04:40 +01001183 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_gc->source], NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001184 if (!node) {
1185 lyd_free(data);
1186 return NC_MSG_ERROR;
1187 }
1188 if (rpc_gc->filter) {
1189 if (rpc_gc->filter->type == NC_FILTER_SUBTREE) {
1190 node = lyd_new_anyxml(data, ietfnc, "filter", rpc_gc->filter->data);
1191 lyd_insert_attr(node, "type", "subtree");
1192 } else if (rpc_gc->filter->type == NC_FILTER_XPATH) {
1193 node = lyd_new_anyxml(data, ietfnc, "filter", NULL);
1194 /* TODO - handle namespaces from XPATH query */
1195 lyd_insert_attr(node, "type", "xpath");
1196 lyd_insert_attr(node, "select", rpc_gc->filter->data);
1197 }
1198 }
1199 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001200
1201 case NC_RPC_EDIT:
1202 rpc_e = (struct nc_rpc_edit *)rpc;
1203
1204 data = lyd_new(NULL, ietfnc, "edit-config");
1205 node = lyd_new(data, ietfnc, "target");
1206 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_e->target], NULL);
1207 if (!node) {
1208 lyd_free(data);
1209 return NC_MSG_ERROR;
1210 }
1211
1212 if (rpc_e->default_op) {
1213 node = lyd_new_leaf(data, ietfnc, "default-operation", rpcedit_dfltop2str[rpc_e->default_op]);
1214 if (!node) {
1215 lyd_free(data);
1216 return NC_MSG_ERROR;
1217 }
1218 }
1219
1220 if (rpc_e->test_opt) {
1221 node = lyd_new_leaf(data, ietfnc, "test-option", rpcedit_testopt2str[rpc_e->test_opt]);
1222 if (!node) {
1223 lyd_free(data);
1224 return NC_MSG_ERROR;
1225 }
1226 }
1227
1228 if (rpc_e->error_opt) {
1229 node = lyd_new_leaf(data, ietfnc, "error-option", rpcedit_erropt2str[rpc_e->error_opt]);
1230 if (!node) {
1231 lyd_free(data);
1232 return NC_MSG_ERROR;
1233 }
1234 }
1235
1236 if (rpc_e->edit_cont[0] == '<') {
1237 node = lyd_new_anyxml(data, ietfnc, "config", rpc_e->edit_cont);
1238 } else {
1239 node = lyd_new_leaf(data, ietfnc, "url", rpc_e->edit_cont);
1240 }
1241 if (!node) {
1242 lyd_free(data);
1243 return NC_MSG_ERROR;
1244 }
1245 break;
1246
1247 case NC_RPC_COPY:
1248 rpc_cp = (struct nc_rpc_copy *)rpc;
1249
1250 data = lyd_new(NULL, ietfnc, "copy-config");
1251 node = lyd_new(data, ietfnc, "target");
1252 if (rpc_cp->url_trg) {
1253 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_trg);
1254 } else {
1255 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->target], NULL);
1256 }
1257 if (!node) {
1258 lyd_free(data);
1259 return NC_MSG_ERROR;
1260 }
1261
1262 node = lyd_new(data, ietfnc, "source");
1263 if (rpc_cp->url_config_src) {
1264 if (rpc_cp->url_config_src[0] == '<') {
1265 node = lyd_new_anyxml(node, ietfnc, "config", rpc_cp->url_config_src);
1266 } else {
1267 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_config_src);
1268 }
1269 } else {
1270 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->source], NULL);
1271 }
1272 if (!node) {
1273 lyd_free(data);
1274 return NC_MSG_ERROR;
1275 }
1276 break;
1277
1278 case NC_RPC_DELETE:
1279 rpc_del = (struct nc_rpc_delete *)rpc;
1280
1281 data = lyd_new(NULL, ietfnc, "delete-config");
1282 node = lyd_new(data, ietfnc, "target");
1283 if (rpc_del->url) {
1284 node = lyd_new_leaf(node, ietfnc, "url", rpc_del->url);
1285 } else {
1286 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_del->target], NULL);
1287 }
1288 if (!node) {
1289 lyd_free(data);
1290 return NC_MSG_ERROR;
1291 }
1292 break;
1293
Radek Krejci695d4fa2015-10-22 13:23:54 +02001294 case NC_RPC_LOCK:
1295 rpc_lock = (struct nc_rpc_lock *)rpc;
1296
1297 data = lyd_new(NULL, ietfnc, "lock");
1298 node = lyd_new(data, ietfnc, "target");
Radek Krejcib68f7c22015-10-31 11:04:40 +01001299 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001300 if (!node) {
1301 lyd_free(data);
1302 return NC_MSG_ERROR;
1303 }
1304 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001305
Radek Krejci695d4fa2015-10-22 13:23:54 +02001306 case NC_RPC_UNLOCK:
1307 rpc_lock = (struct nc_rpc_lock *)rpc;
1308
1309 data = lyd_new(NULL, ietfnc, "unlock");
1310 node = lyd_new(data, ietfnc, "target");
Radek Krejcib68f7c22015-10-31 11:04:40 +01001311 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001312 if (!node) {
1313 lyd_free(data);
1314 return NC_MSG_ERROR;
1315 }
1316 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001317
1318 case NC_RPC_GET:
1319 rpc_g = (struct nc_rpc_get *)rpc;
1320
1321 data = lyd_new(NULL, ietfnc, "get");
1322 if (rpc_g->filter) {
1323 if (rpc_g->filter->type == NC_FILTER_SUBTREE) {
1324 node = lyd_new_anyxml(data, ietfnc, "filter", rpc_g->filter->data);
1325 lyd_insert_attr(node, "type", "subtree");
1326 } else if (rpc_g->filter->type == NC_FILTER_XPATH) {
1327 node = lyd_new_anyxml(data, ietfnc, "filter", NULL);
1328 /* TODO - handle namespaces from XPATH query */
1329 lyd_insert_attr(node, "type", "xpath");
1330 lyd_insert_attr(node, "select", rpc_g->filter->data);
1331 }
1332 if (!node) {
1333 lyd_free(data);
1334 return NC_MSG_ERROR;
1335 }
1336 }
1337 break;
1338
1339 case NC_RPC_KILL:
1340 rpc_k = (struct nc_rpc_kill *)rpc;
1341
1342 data = lyd_new(NULL, ietfnc, "kill-session");
1343 sprintf(str, "%u", rpc_k->sid);
1344 lyd_new_leaf(data, ietfnc, "session-id", str);
1345 break;
1346
1347 case NC_RPC_COMMIT:
1348 rpc_com = (struct nc_rpc_commit *)rpc;
1349
1350 data = lyd_new(NULL, ietfnc, "commit");
1351 if (rpc_com->confirmed) {
1352 lyd_new_leaf(data, ietfnc, "confirmed", NULL);
1353 }
1354
1355 if (rpc_com->confirm_timeout) {
1356 sprintf(str, "%u", rpc_com->confirm_timeout);
1357 lyd_new_leaf(data, ietfnc, "confirm-timeout", str);
1358 }
1359
1360 if (rpc_com->persist) {
1361 node = lyd_new_leaf(data, ietfnc, "persist", rpc_com->persist);
1362 if (!node) {
1363 lyd_free(data);
1364 return NC_MSG_ERROR;
1365 }
1366 }
1367
1368 if (rpc_com->persist_id) {
1369 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_com->persist_id);
1370 if (!node) {
1371 lyd_free(data);
1372 return NC_MSG_ERROR;
1373 }
1374 }
1375 break;
1376
1377 case NC_RPC_DISCARD:
1378 data = lyd_new(NULL, ietfnc, "discard-changes");
1379 break;
1380
1381 case NC_RPC_CANCEL:
1382 rpc_can = (struct nc_rpc_cancel *)rpc;
1383
1384 data = lyd_new(NULL, ietfnc, "cancel-commit");
1385 if (rpc_can->persist_id) {
1386 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_can->persist_id);
1387 if (!node) {
1388 lyd_free(data);
1389 return NC_MSG_ERROR;
1390 }
1391 }
1392 break;
1393
1394 case NC_RPC_VALIDATE:
1395 rpc_val = (struct nc_rpc_validate *)rpc;
1396
1397 data = lyd_new(NULL, ietfnc, "validate");
1398 if (rpc_val->url_config_src) {
1399 if (rpc_val->url_config_src[0] == '<') {
1400 node = lyd_new_anyxml(data, ietfnc, "config", rpc_val->url_config_src);
1401 } else {
1402 node = lyd_new_leaf(data, ietfnc, "url", rpc_val->url_config_src);
1403 }
1404 } else {
1405 node = lyd_new_leaf(data, ietfnc, ncds2str[rpc_val->source], NULL);
1406 }
1407 if (!node) {
1408 lyd_free(data);
1409 return NC_MSG_ERROR;
1410 }
1411 break;
1412
1413 case NC_RPC_GETSCHEMA:
1414 ietfncmon = ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL);
1415 if (!ietfncmon) {
1416 ERR("%s: Missing ietf-netconf-monitoring schema in context (session %u)", session->id);
1417 return NC_MSG_ERROR;
1418 }
1419
1420 rpc_gs = (struct nc_rpc_getschema *)rpc;
1421
1422 data = lyd_new(NULL, ietfncmon, "get-schema");
1423 node = lyd_new_leaf(data, ietfncmon, "identifier", rpc_gs->identifier);
1424 if (!node) {
1425 lyd_free(data);
1426 return NC_MSG_ERROR;
1427 }
1428 if (rpc_gs->version) {
1429 node = lyd_new_leaf(data, ietfncmon, "version", rpc_gs->version);
1430 if (!node) {
1431 lyd_free(data);
1432 return NC_MSG_ERROR;
1433 }
1434 }
1435 if (rpc_gs->format) {
1436 node = lyd_new_leaf(data, ietfncmon, "format", rpc_gs->format);
1437 if (!node) {
1438 lyd_free(data);
1439 return NC_MSG_ERROR;
1440 }
1441 }
1442 break;
1443
1444 case NC_RPC_SUBSCRIBE:
1445 notifs = ly_ctx_get_module(session->ctx, "notifications", NULL);
1446 if (!notifs) {
1447 ERR("%s: Missing notifications schema in context (session %u)", session->id);
1448 return NC_MSG_ERROR;
1449 }
1450
1451 rpc_sub = (struct nc_rpc_subscribe *)rpc;
1452
1453 data = lyd_new(NULL, notifs, "create-subscription");
1454 if (rpc_sub->stream) {
1455 node = lyd_new_leaf(data, notifs, "stream", rpc_sub->stream);
1456 if (!node) {
1457 lyd_free(data);
1458 return NC_MSG_ERROR;
1459 }
1460 }
1461
1462 if (rpc_sub->filter) {
1463 if (rpc_sub->filter->type == NC_FILTER_SUBTREE) {
1464 node = lyd_new_anyxml(data, notifs, "filter", rpc_sub->filter->data);
1465 lyd_insert_attr(node, "type", "subtree");
1466 } else if (rpc_sub->filter->type == NC_FILTER_XPATH) {
1467 node = lyd_new_anyxml(data, notifs, "filter", NULL);
1468 /* TODO - handle namespaces from XPATH query */
1469 lyd_insert_attr(node, "type", "xpath");
1470 lyd_insert_attr(node, "select", rpc_sub->filter->data);
1471 }
1472 if (!node) {
1473 lyd_free(data);
1474 return NC_MSG_ERROR;
1475 }
1476 }
1477
1478 if (rpc_sub->start) {
1479 node = lyd_new_leaf(data, notifs, "startTime", rpc_sub->start);
1480 if (!node) {
1481 lyd_free(data);
1482 return NC_MSG_ERROR;
1483 }
1484 }
1485
1486 if (rpc_sub->stop) {
1487 node = lyd_new_leaf(data, notifs, "stopTime", rpc_sub->stop);
1488 if (!node) {
1489 lyd_free(data);
1490 return NC_MSG_ERROR;
1491 }
1492 }
1493 break;
1494
1495 case NC_RPC_SERVER:
1496 ERR("Internal (%s:%d)", __FILE__, __LINE__);
1497 return NC_MSG_ERROR;
1498 }
1499
1500 if (lyd_validate(data, LYD_OPT_STRICT)) {
1501 lyd_free(data);
1502 return NC_MSG_ERROR;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001503 }
1504
1505 r = session_ti_lock(session, 0);
1506 if (r != 0) {
1507 /* error or blocking */
1508 r = NC_MSG_WOULDBLOCK;
1509 } else {
1510 /* send RPC */
1511 r = nc_send_rpc_(session, data);
1512 }
1513 session_ti_unlock(session);
1514
1515 lyd_free(data);
1516 return r;
1517}