blob: b90299e797a5a6f9b8692ab3d08747915129db16 [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>
Michal Vasko80cad7f2015-12-08 14:42:27 +010034#include <arpa/inet.h>
35#include <poll.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020036
37#include <libyang/libyang.h>
38
Radek Krejci206fcd62015-10-07 15:42:48 +020039#include "libnetconf.h"
Michal Vaskoad928112015-11-25 15:52:10 +010040#include "messages_p.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020041
42#define TIMEOUT_STEP 50
43
Radek Krejci695d4fa2015-10-22 13:23:54 +020044static NC_MSG_TYPE nc_send_hello_(struct nc_session *session);
45static NC_MSG_TYPE nc_recv_hello(struct nc_session *session);
Michal Vaskoad611702015-12-03 13:41:51 +010046static NC_MSG_TYPE nc_send_msg(struct nc_session *session, struct lyd_node *op);
Radek Krejci695d4fa2015-10-22 13:23:54 +020047
48static char *schema_searchpath = NULL;
49
50/* session configuration */
51static struct {
52 uint16_t hello_timeout; /**< hello-timeout in seconds, default is 600 */
53} cfg = {600};
54
55API int
56nc_schema_searchpath(const char *path)
57{
Michal Vaskoe90a41e2015-11-10 13:08:38 +010058 if (schema_searchpath) {
59 free(schema_searchpath);
60 }
Radek Krejci695d4fa2015-10-22 13:23:54 +020061 schema_searchpath = strdup(path);
62
63 return schema_searchpath ? 0 : 1;
64}
65
Michal Vasko38a7c6c2015-12-04 12:29:20 +010066API NC_STATUS
Michal Vaskoc111ac52015-12-08 14:36:36 +010067nc_session_get_status(const struct nc_session *session)
Michal Vasko38a7c6c2015-12-04 12:29:20 +010068{
69 return session->status;
70}
71
72API uint32_t
Michal Vaskoc111ac52015-12-08 14:36:36 +010073nc_session_get_id(const struct nc_session *session)
Michal Vasko38a7c6c2015-12-04 12:29:20 +010074{
75 return session->id;
76}
77
78API NC_TRANSPORT_IMPL
Michal Vaskoc111ac52015-12-08 14:36:36 +010079nc_session_get_ti(const struct nc_session *session)
Michal Vasko38a7c6c2015-12-04 12:29:20 +010080{
81 return session->ti_type;
82}
83
84API const char *
Michal Vaskoc111ac52015-12-08 14:36:36 +010085nc_session_get_username(const struct nc_session *session)
Michal Vasko38a7c6c2015-12-04 12:29:20 +010086{
87 return session->username;
88}
89
90API const char *
Michal Vaskoc111ac52015-12-08 14:36:36 +010091nc_session_get_host(const struct nc_session *session)
Michal Vasko38a7c6c2015-12-04 12:29:20 +010092{
93 return session->host;
94}
95
96API uint16_t
Michal Vaskoc111ac52015-12-08 14:36:36 +010097nc_session_get_port(const struct nc_session *session)
Michal Vasko38a7c6c2015-12-04 12:29:20 +010098{
99 return session->port;
100}
101
102API const char **
Michal Vaskoc111ac52015-12-08 14:36:36 +0100103nc_session_get_cpblts(const struct nc_session *session)
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100104{
105 return session->cpblts;
106}
107
Radek Krejci5686ff72015-10-09 13:33:56 +0200108/*
109 * @return 0 - success
110 * -1 - timeout
111 * >0 - error
112 */
113static int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100114session_ti_lock(struct nc_session *session, int32_t timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +0200115{
116 int r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200117
118 if (timeout >= 0) {
119 /* limited waiting for lock */
120 do {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200121 r = pthread_mutex_trylock(session->ti_lock);
Radek Krejci206fcd62015-10-07 15:42:48 +0200122 if (r == EBUSY) {
123 /* try later until timeout passes */
124 usleep(TIMEOUT_STEP);
125 timeout = timeout - TIMEOUT_STEP;
126 continue;
127 } else if (r) {
128 /* error */
129 ERR("Acquiring session (%u) TI lock failed (%s).", session->id, strerror(r));
Radek Krejci5686ff72015-10-09 13:33:56 +0200130 return r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200131 } else {
132 /* lock acquired */
Radek Krejci5686ff72015-10-09 13:33:56 +0200133 return 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200134 }
135 } while(timeout > 0);
136
Radek Krejci5686ff72015-10-09 13:33:56 +0200137 /* timeout has passed */
138 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200139 } else {
140 /* infinite waiting for lock */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200141 return pthread_mutex_lock(session->ti_lock);
Radek Krejci5686ff72015-10-09 13:33:56 +0200142 }
143}
144
145static int
146session_ti_unlock(struct nc_session *session)
147{
Radek Krejci695d4fa2015-10-22 13:23:54 +0200148 return pthread_mutex_unlock(session->ti_lock);
149}
150
Radek Krejciac6d3472015-10-22 15:47:18 +0200151int
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100152nc_handshake(struct nc_session *session)
Radek Krejciac6d3472015-10-22 15:47:18 +0200153{
154 NC_MSG_TYPE type;
155
156 type = nc_send_hello_(session);
157 if (type != NC_MSG_HELLO) {
158 return 1;
159 }
160
161 type = nc_recv_hello(session);
162 if (type != NC_MSG_HELLO) {
163 return 1;
164 }
165
166 return 0;
167}
168
Michal Vasko57eb9402015-12-08 14:38:12 +0100169/* SCHEMAS_DIR not used */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200170static int
Michal Vasko57eb9402015-12-08 14:38:12 +0100171ctx_check_and_load_model(struct nc_session *session, const char *cpblt)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200172{
Michal Vaskoad611702015-12-03 13:41:51 +0100173 const struct lys_module *module;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100174 char *ptr, *ptr2;
175 char *model_name, *revision = NULL, *features = NULL;
176
Michal Vaskoad928112015-11-25 15:52:10 +0100177 /* parse module */
178 ptr = strstr(cpblt, "module=");
179 if (!ptr) {
180 WRN("Unknown capability \"%s\" could not be parsed.", cpblt);
181 return 1;
182 }
183 ptr += 7;
184 ptr2 = strchr(ptr, '&');
185 if (!ptr2) {
186 ptr2 = ptr + strlen(ptr);
187 }
188 model_name = strndup(ptr, ptr2 - ptr);
189
190 /* parse revision */
191 ptr = strstr(cpblt, "revision=");
192 if (ptr) {
193 ptr += 9;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100194 ptr2 = strchr(ptr, '&');
195 if (!ptr2) {
196 ptr2 = ptr + strlen(ptr);
197 }
Michal Vaskoad928112015-11-25 15:52:10 +0100198 revision = strndup(ptr, ptr2 - ptr);
199 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100200
Michal Vaskoad611702015-12-03 13:41:51 +0100201 /* load module if needed */
202 module = ly_ctx_get_module(session->ctx, model_name, revision);
203 if (!module) {
204 module = ly_ctx_load_module(session->ctx, model_name, revision);
205 }
Michal Vaskoad928112015-11-25 15:52:10 +0100206
207 free(model_name);
208 free(revision);
209 if (!module) {
210 return 1;
211 }
212
213 /* parse features */
214 ptr = strstr(cpblt, "features=");
215 if (ptr) {
216 ptr += 9;
217 ptr2 = strchr(ptr, '&');
218 if (!ptr2) {
219 ptr2 = ptr + strlen(ptr);
220 }
221 features = strndup(ptr, ptr2 - ptr);
222 }
223
224 /* enable features */
225 if (features) {
226 /* basically manual strtok_r (to avoid macro) */
227 ptr2 = features;
228 for (ptr = features; *ptr; ++ptr) {
229 if (*ptr == ',') {
230 *ptr = '\0';
231 /* remember last feature */
232 ptr2 = ptr + 1;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100233 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100234 }
235
Michal Vaskoad928112015-11-25 15:52:10 +0100236 ptr = features;
237 lys_features_enable(module, ptr);
238 while (ptr != ptr2) {
239 ptr += strlen(ptr) + 1;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100240 lys_features_enable(module, ptr);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100241 }
Michal Vaskoad928112015-11-25 15:52:10 +0100242
243 free(features);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100244 }
245
246 return 0;
247}
248
Michal Vasko57eb9402015-12-08 14:38:12 +0100249/* SCHEMAS_DIR used */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100250static int
Michal Vasko57eb9402015-12-08 14:38:12 +0100251ctx_check_and_load_ietf_netconf(struct ly_ctx *ctx, const char **cpblts, int from_file)
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100252{
Michal Vaskoad611702015-12-03 13:41:51 +0100253 int i;
254 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200255
Michal Vasko57eb9402015-12-08 14:38:12 +0100256 ietfnc = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
257 if (!ietfnc) {
258 if (from_file) {
259 ietfnc = lys_parse_path(ctx, SCHEMAS_DIR"/ietf-netconf.yin", LYS_IN_YIN);
260 } else {
261 ietfnc = ly_ctx_load_module(ctx, "ietf-netconf", NULL);
262 }
263 }
264 if (!ietfnc) {
Michal Vaskoad611702015-12-03 13:41:51 +0100265 ERR("Loading base NETCONF schema failed.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200266 return 1;
267 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200268
269 /* set supported capabilities from ietf-netconf */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100270 for (i = 0; cpblts[i]; ++i) {
271 if (!strncmp(cpblts[i], "urn:ietf:params:netconf:capability:", 35)) {
272 if (!strncmp(cpblts[i] + 35, "writable-running", 16)) {
273 lys_features_enable(ietfnc, "writable-running");
274 } else if (!strncmp(cpblts[i] + 35, "candidate", 9)) {
275 lys_features_enable(ietfnc, "candidate");
276 } else if (!strcmp(cpblts[i] + 35, "confirmed-commit:1.1")) {
277 lys_features_enable(ietfnc, "confirmed-commit");
278 } else if (!strncmp(cpblts[i] + 35, "rollback-on-error", 17)) {
279 lys_features_enable(ietfnc, "rollback-on-error");
280 } else if (!strcmp(cpblts[i] + 35, "validate:1.1")) {
281 lys_features_enable(ietfnc, "validate");
282 } else if (!strncmp(cpblts[i] + 35, "startup", 7)) {
283 lys_features_enable(ietfnc, "startup");
284 } else if (!strncmp(cpblts[i] + 35, "url", 3)) {
285 lys_features_enable(ietfnc, "url");
286 } else if (!strncmp(cpblts[i] + 35, "xpath", 5)) {
287 lys_features_enable(ietfnc, "xpath");
288 }
289 }
290 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200291
292 return 0;
293}
294
Michal Vaskoad928112015-11-25 15:52:10 +0100295static char *
296libyang_module_clb(const char *name, const char *revision, void *user_data, LYS_INFORMAT *format,
297 void (**free_model_data)(char *model_data))
298{
299 struct nc_session *session = (struct nc_session *)user_data;
300 struct nc_rpc *rpc;
301 struct nc_reply *reply;
Michal Vaskoad611702015-12-03 13:41:51 +0100302 struct nc_reply_data *data_rpl;
Michal Vaskoad928112015-11-25 15:52:10 +0100303 NC_MSG_TYPE msg;
304 char *model_data;
Michal Vaskoad611702015-12-03 13:41:51 +0100305 uint64_t msgid;
Michal Vaskoad928112015-11-25 15:52:10 +0100306
307 /* TODO later replace with yang to reduce model size? */
Michal Vaskoad611702015-12-03 13:41:51 +0100308 rpc = nc_rpc_getschema(name, revision, "yin", NC_RPC_PARAMTYPE_CONST);
Michal Vaskoad928112015-11-25 15:52:10 +0100309 *format = LYS_IN_YIN;
310
Michal Vaskoad611702015-12-03 13:41:51 +0100311 while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
Michal Vaskoad928112015-11-25 15:52:10 +0100312 usleep(1000);
313 }
314 if (msg == NC_MSG_ERROR) {
315 ERR("Failed to send the <get-schema> RPC.");
316 nc_rpc_free(rpc);
317 return NULL;
318 }
Michal Vaskoad928112015-11-25 15:52:10 +0100319
Michal Vaskoad611702015-12-03 13:41:51 +0100320 msg = nc_recv_reply(session, rpc, msgid, 250, &reply);
321 nc_rpc_free(rpc);
Michal Vaskoad928112015-11-25 15:52:10 +0100322 if (msg == NC_MSG_WOULDBLOCK) {
323 ERR("Timeout for receiving reply to a <get-schema> expired.");
324 return NULL;
325 } else if (msg == NC_MSG_ERROR) {
326 ERR("Failed to receive a reply to <get-schema>.");
327 return NULL;
328 }
329
Michal Vaskoad611702015-12-03 13:41:51 +0100330 data_rpl = (struct nc_reply_data *)reply;
331 model_data = lyxml_serialize(((struct lyd_node_anyxml *)data_rpl->data)->value);
332 nc_reply_free(reply);
Michal Vaskoad928112015-11-25 15:52:10 +0100333 *free_model_data = NULL;
334
335 return model_data;
336}
337
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100338int
Michal Vasko57eb9402015-12-08 14:38:12 +0100339nc_ctx_check_and_fill(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200340{
Michal Vasko57eb9402015-12-08 14:38:12 +0100341 int i, get_schema_support = 0;
Michal Vaskoad928112015-11-25 15:52:10 +0100342 ly_module_clb old_clb = NULL;
343 void *old_data = NULL;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200344
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100345 assert(session->cpblts && session->ctx);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200346
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100347 /* check if get-schema is supported */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100348 for (i = 0; session->cpblts[i]; ++i) {
349 if (!strncmp(session->cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100350 get_schema_support = 1;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100351 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200352 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200353 }
354
Michal Vasko57eb9402015-12-08 14:38:12 +0100355 /* get-schema is supported, load local ietf-netconf-monitoring so we can create <get-schema> RPCs */
356 if (get_schema_support && !ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL)) {
357 if (lys_parse_path(session->ctx, SCHEMAS_DIR"/ietf-netconf-monitoring.yin", LYS_IN_YIN)) {
358 /* set module retrieval using <get-schema> */
359 old_clb = ly_ctx_get_module_clb(session->ctx, &old_data);
360 ly_ctx_set_module_clb(session->ctx, &libyang_module_clb, session);
361 } else {
362 WRN("Loading NETCONF monitoring schema failed, cannot use <get-schema>.");
363 }
364 }
365
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100366 /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
Michal Vasko57eb9402015-12-08 14:38:12 +0100367 if (ctx_check_and_load_ietf_netconf(session->ctx, session->cpblts, !get_schema_support)) {
368 if (old_clb) {
369 ly_ctx_set_module_clb(session->ctx, old_clb, old_data);
370 }
Michal Vaskoad928112015-11-25 15:52:10 +0100371 return 1;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100372 }
373
374 /* load all other models */
375 for (i = 0; session->cpblts[i]; ++i) {
376 if (!strncmp(session->cpblts[i], "urn:ietf:params:netconf:capability", 34)
377 || !strncmp(session->cpblts[i], "urn:ietf:params:netconf:base", 28)) {
378 continue;
379 }
380
Michal Vasko57eb9402015-12-08 14:38:12 +0100381 ctx_check_and_load_model(session, session->cpblts[i]);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100382 }
383
Michal Vasko57eb9402015-12-08 14:38:12 +0100384 if (old_clb) {
385 ly_ctx_set_module_clb(session->ctx, old_clb, old_data);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100386 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100387 return 0;
Radek Krejciac6d3472015-10-22 15:47:18 +0200388}
389
390API struct nc_session *
391nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
392{
393 struct nc_session *session = NULL;
394
395 if (fdin < 0 || fdout < 0) {
396 ERR("%s: Invalid parameter", __func__);
397 return NULL;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200398 }
399
Radek Krejciac6d3472015-10-22 15:47:18 +0200400 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100401 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +0200402 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100403 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +0200404 return NULL;
405 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100406 session->status = NC_STATUS_STARTING;
407 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +0200408
409 /* transport specific data */
410 session->ti_type = NC_TI_FD;
411 session->ti.fd.in = fdin;
412 session->ti.fd.out = fdout;
413
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100414 /* assign context (dicionary needed for handshake) */
415 if (!ctx) {
416 ctx = ly_ctx_new(SCHEMAS_DIR);
417 } else {
418 session->flags |= NC_SESSION_SHAREDCTX;
419 }
420 session->ctx = ctx;
421
Radek Krejciac6d3472015-10-22 15:47:18 +0200422 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100423 if (nc_handshake(session)) {
424 goto fail;
425 }
Michal Vaskoad611702015-12-03 13:41:51 +0100426 session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100427
Michal Vasko57eb9402015-12-08 14:38:12 +0100428 if (nc_ctx_check_and_fill(session)) {
429 goto fail;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200430 }
431
Radek Krejci695d4fa2015-10-22 13:23:54 +0200432 return session;
433
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100434fail:
Radek Krejci695d4fa2015-10-22 13:23:54 +0200435 nc_session_free(session);
436 return NULL;
437}
438
Radek Krejciac6d3472015-10-22 15:47:18 +0200439int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100440nc_connect_getsocket(const char* host, uint16_t port)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200441{
Michal Vaskoc111ac52015-12-08 14:36:36 +0100442 int i, sock = -1;
Radek Krejciac6d3472015-10-22 15:47:18 +0200443 struct addrinfo hints, *res_list, *res;
444 char port_s[6]; /* length of string representation of short int */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200445
Radek Krejciac6d3472015-10-22 15:47:18 +0200446 snprintf(port_s, 6, "%u", port);
447
448 /* Connect to a server */
449 memset(&hints, 0, sizeof hints);
450 hints.ai_family = AF_UNSPEC;
451 hints.ai_socktype = SOCK_STREAM;
452 hints.ai_protocol = IPPROTO_TCP;
453 i = getaddrinfo(host, port_s, &hints, &res_list);
454 if (i != 0) {
455 ERR("Unable to translate the host address (%s).", gai_strerror(i));
456 return -1;
457 }
458
459 for (i = 0, res = res_list; res != NULL; res = res->ai_next) {
460 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
461 if (sock == -1) {
462 /* socket was not created, try another resource */
463 i = errno;
464 goto errloop;
465 }
466
467 if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
468 /* network connection failed, try another resource */
469 i = errno;
470 close(sock);
471 sock = -1;
472 goto errloop;
473 }
474
475 /* we're done, network connection established */
476 break;
477errloop:
Michal Vaskoaebea602015-10-26 15:35:34 +0100478 VRB("Unable to connect to %s:%s over %s (%s).", host, port_s,
Radek Krejciac6d3472015-10-22 15:47:18 +0200479 (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", strerror(i));
480 continue;
481 }
482
483 if (sock == -1) {
Michal Vaskoaebea602015-10-26 15:35:34 +0100484 ERR("Unable to connect to %s:%s.", host, port_s);
Radek Krejciac6d3472015-10-22 15:47:18 +0200485 } else {
Michal Vaskoaebea602015-10-26 15:35:34 +0100486 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 +0200487 }
488 freeaddrinfo(res_list);
489
490 return sock;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200491}
492
Radek Krejci695d4fa2015-10-22 13:23:54 +0200493API void
494nc_session_free(struct nc_session *session)
495{
496 int r, i;
497 int multisession = 0; /* flag for more NETCONF session on a single SSH session */
498 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100499 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100500 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200501 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100502 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200503 void *p;
504
Michal Vaskoadd4c792015-10-26 15:36:58 +0100505 if (!session || session->status == NC_STATUS_CLOSING) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200506 return;
507 }
508
509 /* mark session for closing */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100510 if (session->ti_lock) {
511 do {
512 r = session_ti_lock(session, 0);
513 } while (r < 0);
514 if (r) {
515 return;
516 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200517 }
518
519 /* stop notifications loop if any */
520 if (session->notif) {
521 pthread_cancel(*session->notif);
522 pthread_join(*session->notif, NULL);
523 }
524
525 if (session->side == NC_CLIENT && session->status == NC_STATUS_RUNNING) {
526 /* cleanup message queues */
527 /* notifications */
Michal Vaskoad611702015-12-03 13:41:51 +0100528 for (contiter = session->notifs; contiter; ) {
529 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200530
Michal Vaskoad611702015-12-03 13:41:51 +0100531 p = contiter;
532 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200533 free(p);
534 }
535
536 /* rpc replies */
Michal Vaskoad611702015-12-03 13:41:51 +0100537 for (contiter = session->replies; contiter; ) {
538 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200539
Michal Vaskoad611702015-12-03 13:41:51 +0100540 p = contiter;
541 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200542 free(p);
543 }
544
545 /* send closing info to the other side */
546 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
547 if (!ietfnc) {
Michal Vaskoad611702015-12-03 13:41:51 +0100548 WRN("%s: Missing ietf-netconf schema in context (session %u), unable to send <close-session\\>", __func__, session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200549 } else {
550 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100551 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200552 lyd_free(close_rpc);
Michal Vaskofad6e912015-10-26 15:37:22 +0100553 switch (nc_read_msg(session, 200, &rpl)) {
554 case NC_MSG_REPLY:
555 LY_TREE_FOR(rpl->child, child) {
556 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
557 break;
558 }
559 }
560 if (!child) {
561 WRN("The reply to <close-session\\> was not <ok\\> as expected.");
562 }
Michal Vaskoad611702015-12-03 13:41:51 +0100563 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100564 break;
565 case NC_MSG_WOULDBLOCK:
566 WRN("Timeout for receiving a reply to <close-session\\> elapsed.");
567 break;
568 case NC_MSG_ERROR:
569 ERR("Failed to receive a reply to <close-session\\>.");
570 break;
571 default:
572 /* cannot happen */
573 break;
574 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200575 }
576
577 /* list of server's capabilities */
578 if (session->cpblts) {
579 for (i = 0; session->cpblts[i]; i++) {
580 lydict_remove(session->ctx, session->cpblts[i]);
581 }
582 free(session->cpblts);
583 }
584 }
585
586 session->status = NC_STATUS_CLOSING;
587
588 /* transport implementation cleanup */
589 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100590 case NC_TI_NONE:
591 break;
592
Radek Krejci695d4fa2015-10-22 13:23:54 +0200593 case NC_TI_FD:
594 /* nothing needed - file descriptors were provided by caller,
595 * so it is up to the caller to close them correctly
596 * TODO use callbacks
597 */
598 break;
599
Michal Vaskofb2fb762015-10-27 11:44:32 +0100600#ifdef ENABLE_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200601 case NC_TI_LIBSSH:
602 ssh_channel_free(session->ti.libssh.channel);
603 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
604 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
605 * it.
606 */
607 if (!session->ti.libssh.next) {
608 ssh_disconnect(session->ti.libssh.session);
609 ssh_free(session->ti.libssh.session);
610 } else {
611 /* multiple NETCONF sessions on a single SSH session */
612 multisession = 1;
613 /* remove the session from the list */
614 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100615 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200616 /* there will be only one session */
617 siter->ti.libssh.next = NULL;
618 } else {
619 /* there are still multiple sessions, keep the ring list */
620 siter->ti.libssh.next = session->ti.libssh.next;
621 }
622 }
623 break;
624#endif
625
626#ifdef ENABLE_TLS
627 case NC_TI_OPENSSL:
628 SSL_shutdown(session->ti.tls);
629 SSL_free(session->ti.tls);
630 break;
631#endif
632 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200633 lydict_remove(session->ctx, session->username);
634 lydict_remove(session->ctx, session->host);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200635
636 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100637 if (session->ti_lock) {
638 if (multisession) {
639 session_ti_unlock(session);
640 } else {
641 pthread_mutex_destroy(session->ti_lock);
642 free(session->ti_lock);
643 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200644 }
645
646 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
647 ly_ctx_destroy(session->ctx);
648 }
649
650 free(session);
651}
652
653static int
654parse_cpblts(struct lyxml_elem *xml, const char ***list)
655{
656 struct lyxml_elem *cpblt;
657 int ver = -1;
658 int i = 0;
659
660 if (list) {
661 /* get the storage for server's capabilities */
662 LY_TREE_FOR(xml->child, cpblt) {
663 i++;
664 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100665 /* last item remains NULL */
666 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200667 if (!*list) {
668 ERRMEM;
669 return -1;
670 }
671 i = 0;
672 }
673
674 LY_TREE_FOR(xml->child, cpblt) {
675 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
676 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
677 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
678 return -1;
679 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
680 continue;
681 }
682
683 /* detect NETCONF version */
684 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
685 ver = 0;
686 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
687 ver = 1;
688 }
689
690 /* store capabilities */
691 if (list) {
692 (*list)[i] = cpblt->content;
693 cpblt->content = NULL;
694 i++;
695 }
696 }
697
698 if (ver == -1) {
699 ERR("Peer does not support compatible NETCONF version.");
700 }
701
702 return ver;
703}
704
705static NC_MSG_TYPE
706nc_recv_hello(struct nc_session *session)
707{
708 struct lyxml_elem *xml = NULL, *node;
709 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
710 int ver = -1;
711 char *str;
712 long long int id;
713 int flag = 0;
714
715 msgtype = nc_read_msg(session, cfg.hello_timeout * 1000, &xml);
716
717 switch(msgtype) {
718 case NC_MSG_HELLO:
719 /* parse <hello> data */
720 if (session->side == NC_SERVER) {
721 /* get know NETCONF version */
722 LY_TREE_FOR(xml->child, node) {
723 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
724 continue;
725 } else if (strcmp(node->name, "capabilities")) {
726 ERR("Unexpected <%s> element in client's <hello>.", node->name);
727 goto error;
728 }
729
730 if (flag) {
731 /* multiple capabilities elements */
732 ERR("Invalid <hello> message (multiple <capabilities> elements)");
733 goto error;
734 }
735 flag = 1;
736
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100737 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200738 goto error;
739 }
740 session->version = ver;
741 }
742 } else { /* NC_CLIENT */
743 LY_TREE_FOR(xml->child, node) {
744 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
745 continue;
746 } else if (!strcmp(node->name, "session-id")) {
747 if (!node->content || !strlen(node->content)) {
748 ERR("No value of <session-id> element in server's <hello>");
749 goto error;
750 }
751 str = NULL;
752 id = strtoll(node->content, &str, 10);
753 if (*str || id < 1 || id > UINT32_MAX) {
754 ERR("Invalid value of <session-id> element in server's <hello>");
755 goto error;
756 }
757 session->id = (uint32_t)id;
758 continue;
759 } else if (strcmp(node->name, "capabilities")) {
760 ERR("Unexpected <%s> element in client's <hello>.", node->name);
761 goto error;
762 }
763
764 if (flag) {
765 /* multiple capabilities elements */
766 ERR("Invalid <hello> message (multiple <capabilities> elements)");
767 goto error;
768 }
769 flag = 1;
770
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100771 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200772 goto error;
773 }
774 session->version = ver;
775 }
776
777 if (!session->id) {
778 ERR("Missing <session-id> in server's <hello>");
779 goto error;
780 }
781 }
782 break;
783 case NC_MSG_ERROR:
784 /* nothing special, just pass it out */
785 break;
786 default:
787 ERR("Unexpected message received instead of <hello>.");
788 msgtype = NC_MSG_ERROR;
789 }
790
791 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100792 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200793
794 return msgtype;
795
796error:
797 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100798 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200799
800 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +0200801}
802
Michal Vaskoad611702015-12-03 13:41:51 +0100803NC_MSG_TYPE
Michal Vaskoc111ac52015-12-08 14:36:36 +0100804nc_recv_rpc(struct nc_session *session, int32_t timeout, struct nc_server_rpc **rpc)
Radek Krejci5686ff72015-10-09 13:33:56 +0200805{
806 int r;
807 struct lyxml_elem *xml = NULL;
808 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
809
810 if (!session || !rpc) {
811 ERR("%s: Invalid parameter", __func__);
812 return NC_MSG_ERROR;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200813 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_SERVER) {
814 ERR("%s: invalid session to receive RPCs.", __func__);
Radek Krejci5686ff72015-10-09 13:33:56 +0200815 return NC_MSG_ERROR;
816 }
817
818 r = session_ti_lock(session, timeout);
819 if (r > 0) {
820 /* error */
821 return NC_MSG_ERROR;
822 } else if (r < 0) {
823 /* timeout */
824 return NC_MSG_WOULDBLOCK;
Radek Krejci206fcd62015-10-07 15:42:48 +0200825 }
826
827 msgtype = nc_read_msg(session, timeout, &xml);
Radek Krejci5686ff72015-10-09 13:33:56 +0200828 session_ti_unlock(session);
Radek Krejci206fcd62015-10-07 15:42:48 +0200829
Radek Krejci5686ff72015-10-09 13:33:56 +0200830 switch(msgtype) {
831 case NC_MSG_RPC:
Radek Krejcia53b3fe2015-10-19 17:25:04 +0200832 *rpc = malloc(sizeof **rpc);
Radek Krejci61a20302015-10-31 22:57:55 +0100833 (*rpc)->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
Michal Vaskoad611702015-12-03 13:41:51 +0100834 lyxml_free(session->ctx, xml);
Radek Krejci5686ff72015-10-09 13:33:56 +0200835 break;
836 case NC_MSG_HELLO:
837 ERR("SESSION %u: Received another <hello> message.", session->id);
838 goto error;
839 case NC_MSG_REPLY:
840 ERR("SESSION %u: Received <rpc-reply> from NETCONF client.", session->id);
841 goto error;
842 case NC_MSG_NOTIF:
843 ERR("SESSION %u: Received <notification> from NETCONF client.", session->id);
844 goto error;
845 default:
846 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
847 * NC_MSG_NONE is not returned by nc_read_msg()
848 */
849 break;
850 }
Radek Krejci0dc69f72015-10-08 15:32:09 +0200851
Radek Krejci206fcd62015-10-07 15:42:48 +0200852 return msgtype;
Radek Krejci5686ff72015-10-09 13:33:56 +0200853
854error:
855
856 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100857 lyxml_free(session->ctx, xml);
Radek Krejci5686ff72015-10-09 13:33:56 +0200858
859 return NC_MSG_ERROR;
860}
861
Michal Vaskoad611702015-12-03 13:41:51 +0100862static NC_MSG_TYPE
Michal Vaskoc111ac52015-12-08 14:36:36 +0100863get_msg(struct nc_session *session, int32_t timeout, uint64_t msgid, struct lyxml_elem **msg)
Radek Krejci8800a492015-10-31 17:51:27 +0100864{
Michal Vaskoad611702015-12-03 13:41:51 +0100865 int r;
866 char *ptr;
867 const char *str_msgid;
868 uint64_t cur_msgid;
869 struct lyxml_elem *xml;
870 struct nc_msg_cont *cont, *prev_cont, **cont_ptr;
871 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
872
873next_message:
874 if (msgtype) {
875 /* second run, wait and give a chance to nc_recv_reply() */
876 usleep(TIMEOUT_STEP);
877 timeout = timeout - (TIMEOUT_STEP);
878 }
879 r = session_ti_lock(session, timeout);
880 if (r > 0) {
881 /* error */
882 return NC_MSG_ERROR;
883 } else if (r < 0) {
884 /* timeout */
885 return NC_MSG_WOULDBLOCK;
886 }
887
888 /* try to get notification from the session's queue */
889 if (!msgid && session->notifs) {
890 cont = session->notifs;
891 session->notifs = cont->next;
892
893 session_ti_unlock(session);
894
895 *msg = cont->msg;
896 free(cont);
897
898 return NC_MSG_NOTIF;
899 }
900
901 /* try to get rpc-reply from the session's queue */
902 if (msgid && session->replies) {
903 prev_cont = NULL;
904 for (cont = session->replies; cont; cont = cont->next) {
905 /* errors checked in the condition below */
906 str_msgid = lyxml_get_attr(cont->msg, "message-id", NULL);
907 cur_msgid = strtoul(str_msgid, &ptr, 10);
908
909 if (cur_msgid == msgid) {
910 if (!prev_cont) {
911 session->replies = cont->next;
912 } else {
913 prev_cont->next = cont->next;
914 }
915 session_ti_unlock(session);
916
917 *msg = cont->msg;
918 free(cont);
919
920 return NC_MSG_REPLY;
921 }
922
923 prev_cont = cont;
924 }
925 }
926
927 /* read message from wire */
928 msgtype = nc_read_msg(session, timeout, &xml);
929
930 /* we read rpc-reply, want a notif */
931 if (!msgid && (msgtype == NC_MSG_REPLY)) {
932 /* just check that message-id is fine */
933 str_msgid = lyxml_get_attr(xml, "message-id", NULL);
934 if (!str_msgid) {
935 session_ti_unlock(session);
936 ERR("SESSION %u: Received a <rpc-reply> with no message-id, discarding.", session->id);
937 lyxml_free(session->ctx, xml);
938 goto next_message;
939 }
940 cur_msgid = strtoul(str_msgid, &ptr, 10);
941 if (ptr[0]) {
942 session_ti_unlock(session);
943 ERR("SESSION %u: Received a <rpc-reply> with an invalid message-id (\"%s\"), discarding.", session->id, str_msgid);
944 lyxml_free(session->ctx, xml);
945 goto next_message;
946 }
947
948 cont_ptr = &session->replies;
949 while (*cont_ptr) {
950 cont_ptr = &((*cont_ptr)->next);
951 }
952 *cont_ptr = malloc(sizeof **cont_ptr);
953 (*cont_ptr)->msg = xml;
954 (*cont_ptr)->next = NULL;
955 }
956
957 /* we read notif, want a rpc-reply */
958 if (msgid && (msgtype == NC_MSG_NOTIF)) {
959 if (!session->notif) {
960 session_ti_unlock(session);
961 ERR("SESSION %u: Received a <notification> but session is not subscribed.", session->id);
962 lyxml_free(session->ctx, xml);
963 goto next_message;
964 }
965
966 cont_ptr = &session->notifs;
967 while (*cont_ptr) {
968 cont_ptr = &((*cont_ptr)->next);
969 }
970 *cont_ptr = malloc(sizeof **cont_ptr);
971 (*cont_ptr)->msg = xml;
972 (*cont_ptr)->next = NULL;
973 }
974
975 session_ti_unlock(session);
976
977 switch (msgtype) {
978 case NC_MSG_NOTIF:
979 /* we want a rpc-reply */
980 if (msgid) {
981 goto next_message;
982 }
983 *msg = xml;
984 break;
985
986 case NC_MSG_REPLY:
987 /* we want a notif */
988 if (!msgid) {
989 goto next_message;
990 }
991 *msg = xml;
992 break;
993
994 case NC_MSG_HELLO:
995 ERR("SESSION %u: Received another <hello> message.", session->id);
996 lyxml_free(session->ctx, xml);
997 goto next_message;
998
999 case NC_MSG_RPC:
1000 ERR("SESSION %u: Received <rpc> from NETCONF server.", session->id);
1001 lyxml_free(session->ctx, xml);
1002 goto next_message;
1003
1004 default:
1005 /* NC_MSG_WOULDBLOCK and NC_MSG_ERROR - pass it out;
1006 * NC_MSG_NONE is not returned by nc_read_msg()
1007 */
1008 break;
1009 }
1010
1011 return msgtype;
1012}
1013
1014/* cannot strictly fail, but does not need to fill any error parameter at all */
1015static void
1016parse_rpc_error(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_err *err)
1017{
1018 struct lyxml_elem *iter, *next, *info;
Radek Krejci8800a492015-10-31 17:51:27 +01001019
1020 LY_TREE_FOR(xml->child, iter) {
Michal Vaskoad611702015-12-03 13:41:51 +01001021 if (!iter->ns) {
1022 if (iter->content) {
1023 WRN("<rpc-error> child \"%s\" with value \"%s\" without namespace.", iter->name, iter->content);
1024 } else {
1025 WRN("<rpc-error> child \"%s\" without namespace.", iter->name);
1026 }
1027 continue;
1028 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
1029 if (iter->content) {
1030 WRN("<rpc-error> child \"%s\" with value \"%s\" in an unknown namespace \"%s\".",
1031 iter->name, iter->content, iter->ns->value);
1032 } else {
1033 WRN("<rpc-error> child \"%s\" in an unknown namespace \"%s\".", iter->name, iter->ns->value);
1034 }
Radek Krejci8800a492015-10-31 17:51:27 +01001035 continue;
1036 }
1037
Michal Vaskoad611702015-12-03 13:41:51 +01001038 if (!strcmp(iter->name, "error-type")) {
1039 if (!iter->content || (strcmp(iter->content, "transport") && strcmp(iter->content, "rpc")
1040 && strcmp(iter->content, "protocol") && strcmp(iter->content, "application"))) {
1041 WRN("<rpc-error> <error-type> unknown value \"%s\".", (iter->content ? iter->content : ""));
1042 } else if (err->type) {
1043 WRN("<rpc-error> <error-type> duplicated.");
1044 } else {
1045 err->type = lydict_insert(ctx, iter->content, 0);
Radek Krejci8800a492015-10-31 17:51:27 +01001046 }
Michal Vaskoad611702015-12-03 13:41:51 +01001047 } else if (!strcmp(iter->name, "error-tag")) {
1048 if (!iter->content || (strcmp(iter->content, "in-use") && strcmp(iter->content, "invalid-value")
1049 && strcmp(iter->content, "too-big") && strcmp(iter->content, "missing-attribute")
1050 && strcmp(iter->content, "bad-attribute") && strcmp(iter->content, "unknown-attribute")
1051 && strcmp(iter->content, "missing-element") && strcmp(iter->content, "bad-element")
1052 && strcmp(iter->content, "unknown-element") && strcmp(iter->content, "unknown-namespace")
1053 && strcmp(iter->content, "access-denied") && strcmp(iter->content, "lock-denied")
1054 && strcmp(iter->content, "resource-denied") && strcmp(iter->content, "rollback-failed")
1055 && strcmp(iter->content, "data-exists") && strcmp(iter->content, "data-missing")
1056 && strcmp(iter->content, "operation-not-supported") && strcmp(iter->content, "operation-failed")
1057 && strcmp(iter->content, "malformed-message"))) {
1058 WRN("<rpc-error> <error-tag> unknown value \"%s\".", (iter->content ? iter->content : ""));
1059 } else if (err->tag) {
1060 WRN("<rpc-error> <error-tag> duplicated.");
1061 } else {
1062 err->tag = lydict_insert(ctx, iter->content, 0);
Radek Krejci8800a492015-10-31 17:51:27 +01001063 }
Michal Vaskoad611702015-12-03 13:41:51 +01001064 } else if (!strcmp(iter->name, "error-severity")) {
1065 if (!iter->content || (strcmp(iter->content, "error") && strcmp(iter->content, "warning"))) {
1066 WRN("<rpc-error> <error-severity> unknown value \"%s\".", (iter->content ? iter->content : ""));
1067 } else if (err->severity) {
1068 WRN("<rpc-error> <error-severity> duplicated.");
1069 } else {
1070 err->severity = lydict_insert(ctx, iter->content, 0);
Radek Krejci8800a492015-10-31 17:51:27 +01001071 }
Michal Vaskoad611702015-12-03 13:41:51 +01001072 } else if (!strcmp(iter->name, "error-app-tag")) {
1073 if (err->apptag) {
1074 WRN("<rpc-error> <error-app-tag> duplicated.");
1075 } else {
1076 err->apptag = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1077 }
1078 } else if (!strcmp(iter->name, "error-path")) {
1079 if (err->path) {
1080 WRN("<rpc-error> <error-path> duplicated.");
1081 } else {
1082 err->path = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1083 }
1084 } else if (!strcmp(iter->name, "error-message")) {
1085 if (err->message) {
1086 WRN("<rpc-error> <error-message> duplicated.");
1087 } else {
1088 err->message_lang = lyxml_get_attr(iter, "xml:lang", NULL);
1089 if (!err->message_lang) {
1090 VRB("<rpc-error> <error-message> without the recommended \"xml:lang\" attribute.");
1091 }
1092 err->message = lydict_insert(ctx, (iter->content ? iter->content : ""), 0);
1093 }
1094 } else if (!strcmp(iter->name, "error-info")) {
1095 LY_TREE_FOR_SAFE(iter->child, next, info) {
1096 if (info->ns && !strcmp(info->ns->value, NC_NS_BASE)) {
1097 if (!strcmp(info->name, "session-id")) {
1098 if (err->sid) {
1099 WRN("<rpc-error> <error-info> <session-id> duplicated.");
1100 } else {
1101 err->sid = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1102 }
1103 } else if (!strcmp(info->name, "bad-attr")) {
1104 ++err->attr_count;
1105 err->attr = realloc(err->attr, err->attr_count * sizeof *err->attr);
1106 err->attr[err->attr_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1107 } else if (!strcmp(info->name, "bad-element")) {
1108 ++err->elem_count;
1109 err->elem = realloc(err->elem, err->elem_count * sizeof *err->elem);
1110 err->elem[err->elem_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1111 } else if (!strcmp(info->name, "bad-namespace")) {
1112 ++err->ns_count;
1113 err->ns = realloc(err->ns, err->ns_count * sizeof *err->ns);
1114 err->ns[err->ns_count - 1] = lydict_insert(ctx, (info->content ? info->content : ""), 0);
1115 } else {
1116 if (info->content) {
1117 WRN("<rpc-error> <error-info> unknown child \"%s\" with value \"%s\".",
1118 info->name, info->content);
1119 } else {
1120 WRN("<rpc-error> <error-info> unknown child \"%s\".", info->name);
1121 }
1122 }
1123 } else {
1124 lyxml_unlink(ctx, info);
1125 ++err->other_count;
1126 err->other = realloc(err->other, err->other_count * sizeof *err->other);
1127 err->other[err->other_count - 1] = info;
1128 }
1129 }
1130 } else {
1131 if (iter->content) {
1132 WRN("<rpc-error> unknown child \"%s\" with value \"%s\".", iter->name, iter->content);
1133 } else {
1134 WRN("<rpc-error> unknown child \"%s\".", iter->name);
1135 }
Radek Krejci8800a492015-10-31 17:51:27 +01001136 }
1137 }
Michal Vaskoad611702015-12-03 13:41:51 +01001138}
Radek Krejci8800a492015-10-31 17:51:27 +01001139
Michal Vaskoad611702015-12-03 13:41:51 +01001140static struct nc_reply *
1141parse_reply(struct ly_ctx *ctx, struct lyxml_elem *xml, struct nc_rpc *rpc)
1142{
1143 struct lyxml_elem *iter;
1144 const struct lys_node *schema;
1145 struct lyd_node *data;
1146 struct nc_reply_error *error_rpl;
1147 struct nc_reply_data *data_rpl;
1148 struct nc_reply *reply = NULL;
1149 int i;
1150
1151 if (!xml->child) {
1152 ERR("An empty <rpc-reply>.");
1153 return NULL;
Radek Krejci8800a492015-10-31 17:51:27 +01001154 }
Michal Vaskoad611702015-12-03 13:41:51 +01001155
1156 /* rpc-error */
1157 if (!strcmp(xml->child->name, "rpc-error") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
1158 /* count and check elements */
1159 i = 0;
1160 LY_TREE_FOR(xml->child, iter) {
1161 if (strcmp(iter->name, "rpc-error")) {
1162 ERR("<rpc-reply> content mismatch (<rpc-error> and <%s>).", iter->name);
1163 return NULL;
1164 } else if (!iter->ns) {
1165 ERR("<rpc-reply> content mismatch (<rpc-error> without namespace).");
1166 return NULL;
1167 } else if (strcmp(iter->ns->value, NC_NS_BASE)) {
1168 ERR("<rpc-reply> content mismatch (<rpc-error> with NS \"%s\").", iter->ns->value);
1169 return NULL;
1170 }
1171 ++i;
1172 }
1173
1174 error_rpl = malloc(sizeof *error_rpl);
1175 error_rpl->type = NC_REPLY_ERROR;
1176 error_rpl->ctx = ctx;
1177 error_rpl->err = calloc(i, sizeof *error_rpl->err);
1178 error_rpl->err_count = i;
1179 reply = (struct nc_reply *)error_rpl;
1180
1181 i = 0;
1182 LY_TREE_FOR(xml->child, iter) {
1183 parse_rpc_error(ctx, iter, error_rpl->err + i);
1184 ++i;
1185 }
1186
1187 /* ok */
1188 } else if (!strcmp(xml->child->name, "ok") && xml->child->ns && !strcmp(xml->child->ns->value, NC_NS_BASE)) {
1189 if (xml->child->next) {
1190 ERR("<rpc-reply> content mismatch (<ok> and <%s>).", xml->child->next->name);
1191 return NULL;
1192 }
1193 reply = malloc(sizeof *reply);
1194 reply->type = NC_REPLY_OK;
1195
1196 /* some RPC output */
1197 } else {
1198 switch (rpc->type) {
1199 case NC_RPC_GENERIC:
1200 schema = ((struct nc_rpc_generic *)rpc)->data->schema;
1201 break;
1202 case NC_RPC_GENERIC_XML:
Michal Vasko5ef882e2015-12-08 14:39:34 +01001203 data = lyd_parse_data(ctx, ((struct nc_rpc_generic_xml *)rpc)->xml_str, LYD_XML, 0);
Michal Vaskoad611702015-12-03 13:41:51 +01001204 if (!data) {
1205 ERR("Failed to parse a generic RPC XML.");
1206 return NULL;
1207 }
1208 schema = data->schema;
1209 lyd_free(data);
1210 break;
1211 case NC_RPC_GETCONFIG:
1212 schema = ly_ctx_get_node(ctx, "/ietf-netconf:get-config");
1213 break;
1214 case NC_RPC_GET:
1215 schema = ly_ctx_get_node(ctx, "/ietf-netconf:get");
1216 break;
1217 case NC_RPC_GETSCHEMA:
1218 schema = ly_ctx_get_node(ctx, "/ietf-netconf-monitoring:get-schema");
1219 break;
1220 case NC_RPC_EDIT:
1221 case NC_RPC_COPY:
1222 case NC_RPC_DELETE:
1223 case NC_RPC_LOCK:
1224 case NC_RPC_UNLOCK:
1225 case NC_RPC_KILL:
1226 case NC_RPC_COMMIT:
1227 case NC_RPC_DISCARD:
1228 case NC_RPC_CANCEL:
1229 case NC_RPC_VALIDATE:
1230 case NC_RPC_SUBSCRIBE:
1231 /* there is no output defined */
1232 break;
1233 }
1234
1235 if (!schema) {
1236 ERR("%s: internal error (%s:%d).", __func__, __FILE__, __LINE__);
1237 return NULL;
1238 }
1239
1240 data_rpl = malloc(sizeof *data_rpl);
1241 data_rpl->type = NC_REPLY_DATA;
1242 data_rpl->data = lyd_parse_output_xml(schema, xml, LYD_OPT_DESTRUCT);
1243 if (!data_rpl->data) {
1244 ERR("Failed to parse <rpc-reply>.");
1245 free(data_rpl);
1246 return NULL;
1247 }
1248 reply = (struct nc_reply *)data_rpl;
1249 }
1250
Radek Krejci8800a492015-10-31 17:51:27 +01001251 return reply;
Radek Krejci8800a492015-10-31 17:51:27 +01001252}
1253
Radek Krejci5686ff72015-10-09 13:33:56 +02001254API NC_MSG_TYPE
Michal Vaskoc111ac52015-12-08 14:36:36 +01001255nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int32_t timeout, struct nc_reply **reply)
Radek Krejci5686ff72015-10-09 13:33:56 +02001256{
Radek Krejci5686ff72015-10-09 13:33:56 +02001257 struct lyxml_elem *xml;
Radek Krejci5686ff72015-10-09 13:33:56 +02001258 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1259
1260 if (!session || !reply) {
1261 ERR("%s: Invalid parameter", __func__);
1262 return NC_MSG_ERROR;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001263 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
1264 ERR("%s: invalid session to receive RPC replies.", __func__);
Radek Krejci5686ff72015-10-09 13:33:56 +02001265 return NC_MSG_ERROR;
1266 }
Radek Krejci8800a492015-10-31 17:51:27 +01001267 *reply = NULL;
Radek Krejci5686ff72015-10-09 13:33:56 +02001268
Michal Vaskoad611702015-12-03 13:41:51 +01001269 msgtype = get_msg(session, timeout, msgid, &xml);
1270 if (msgtype == NC_MSG_WOULDBLOCK) {
1271 return NC_MSG_WOULDBLOCK;
1272 }
1273
1274 if (msgtype == NC_MSG_REPLY) {
1275 *reply = parse_reply(session->ctx, xml, rpc);
Michal Vaskodbcc1142015-12-09 09:36:38 +01001276 lyxml_free(session->ctx, xml);
Michal Vaskoad611702015-12-03 13:41:51 +01001277 if (!(*reply)) {
Radek Krejci5686ff72015-10-09 13:33:56 +02001278 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +02001279 }
Michal Vaskoad611702015-12-03 13:41:51 +01001280 }
Radek Krejci5686ff72015-10-09 13:33:56 +02001281
1282 return msgtype;
Radek Krejci5686ff72015-10-09 13:33:56 +02001283}
1284
1285API NC_MSG_TYPE
Radek Krejci695d4fa2015-10-22 13:23:54 +02001286nc_recv_notif(struct nc_session *session, int timeout, struct nc_notif **notif)
Radek Krejci5686ff72015-10-09 13:33:56 +02001287{
Radek Krejci5686ff72015-10-09 13:33:56 +02001288 struct lyxml_elem *xml;
Radek Krejci5686ff72015-10-09 13:33:56 +02001289 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
1290
1291 if (!session || !notif) {
1292 ERR("%s: Invalid parameter", __func__);
1293 return NC_MSG_ERROR;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001294 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
1295 ERR("%s: invalid session to receive Notifications.", __func__);
Radek Krejci5686ff72015-10-09 13:33:56 +02001296 return NC_MSG_ERROR;
1297 }
1298
Michal Vaskoad611702015-12-03 13:41:51 +01001299 msgtype = get_msg(session, timeout, 0, &xml);
1300 if (msgtype == NC_MSG_WOULDBLOCK) {
1301 return NC_MSG_WOULDBLOCK;
1302 }
Radek Krejci5686ff72015-10-09 13:33:56 +02001303
Michal Vaskoad611702015-12-03 13:41:51 +01001304 if (msgtype == NC_MSG_NOTIF) {
1305 *notif = malloc(sizeof **notif);
1306 (*notif)->tree = lyd_parse_xml(session->ctx, xml, LYD_OPT_DESTRUCT);
1307 lyxml_free(session->ctx, xml);
1308 }
Radek Krejci5686ff72015-10-09 13:33:56 +02001309
1310 return msgtype;
Radek Krejci206fcd62015-10-07 15:42:48 +02001311}
Radek Krejcife0b3472015-10-12 13:43:42 +02001312
Radek Krejci695d4fa2015-10-22 13:23:54 +02001313static NC_MSG_TYPE
1314nc_send_hello_(struct nc_session *session)
1315{
1316 int r;
1317 char **cpblts;
1318
1319 if (session->side == NC_CLIENT) {
1320 /* client side hello - send only NETCONF base capabilities */
1321 cpblts = malloc(3 * sizeof *cpblts);
1322 cpblts[0] = "urn:ietf:params:netconf:base:1.0";
1323 cpblts[1] = "urn:ietf:params:netconf:base:1.1";
1324 cpblts[2] = NULL;
1325
1326 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
1327 free(cpblts);
1328 }
1329
1330
1331 if (r) {
1332 return NC_MSG_ERROR;
1333 } else {
1334 return NC_MSG_HELLO;
1335 }
1336}
1337
1338static NC_MSG_TYPE
Michal Vaskoad611702015-12-03 13:41:51 +01001339nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejcife0b3472015-10-12 13:43:42 +02001340{
1341 int r;
1342
Michal Vaskoad928112015-11-25 15:52:10 +01001343 if (session->ctx != op->schema->module->ctx) {
1344 ERR("RPC \"%s\" was created in different context than that of \"%s\" session %u.",
1345 op->schema->name, session->username, session->id);
1346 return NC_MSG_ERROR;
1347 }
1348
Radek Krejci695d4fa2015-10-22 13:23:54 +02001349 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
Radek Krejcife0b3472015-10-12 13:43:42 +02001350
1351 if (r) {
1352 return NC_MSG_ERROR;
Radek Krejcife0b3472015-10-12 13:43:42 +02001353 }
Michal Vaskoad928112015-11-25 15:52:10 +01001354
1355 return NC_MSG_RPC;
Radek Krejcife0b3472015-10-12 13:43:42 +02001356}
1357
Radek Krejci695d4fa2015-10-22 13:23:54 +02001358API NC_MSG_TYPE
Michal Vaskoc111ac52015-12-08 14:36:36 +01001359nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int32_t timeout, uint64_t *msgid)
Radek Krejci695d4fa2015-10-22 13:23:54 +02001360{
1361 NC_MSG_TYPE r;
Michal Vaskoad928112015-11-25 15:52:10 +01001362 struct nc_rpc_generic *rpc_gen;
1363 struct nc_rpc_generic_xml *rpc_gen_xml;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001364 struct nc_rpc_getconfig *rpc_gc;
Michal Vaskoad928112015-11-25 15:52:10 +01001365 struct nc_rpc_edit *rpc_e;
1366 struct nc_rpc_copy *rpc_cp;
1367 struct nc_rpc_delete *rpc_del;
1368 struct nc_rpc_lock *rpc_lock;
Radek Krejci926a5742015-10-31 17:50:49 +01001369 struct nc_rpc_get *rpc_g;
Michal Vaskoad928112015-11-25 15:52:10 +01001370 struct nc_rpc_kill *rpc_k;
1371 struct nc_rpc_commit *rpc_com;
1372 struct nc_rpc_cancel *rpc_can;
1373 struct nc_rpc_validate *rpc_val;
1374 struct nc_rpc_getschema *rpc_gs;
1375 struct nc_rpc_subscribe *rpc_sub;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001376 struct lyd_node *data, *node;
Michal Vasko807be232015-12-09 15:24:55 +01001377 const struct lys_module *ietfnc, *ietfncmon, *notifs, *ietfncwd = NULL;
Michal Vaskoad928112015-11-25 15:52:10 +01001378 char str[11];
Michal Vaskoad611702015-12-03 13:41:51 +01001379 uint64_t cur_msgid;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001380
Michal Vaskoad611702015-12-03 13:41:51 +01001381 if (!session || !rpc) {
Radek Krejci695d4fa2015-10-22 13:23:54 +02001382 ERR("%s: Invalid parameter", __func__);
1383 return NC_MSG_ERROR;
1384 } else if (session->status != NC_STATUS_RUNNING || session->side != NC_CLIENT) {
1385 ERR("%s: invalid session to send RPCs.", __func__);
1386 return NC_MSG_ERROR;
1387 }
1388
Michal Vaskoad928112015-11-25 15:52:10 +01001389 if ((rpc->type != NC_RPC_GETSCHEMA) && (rpc->type != NC_RPC_GENERIC)
1390 && (rpc->type != NC_RPC_GENERIC_XML) && (rpc->type != NC_RPC_SUBSCRIBE)) {
1391 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
1392 if (!ietfnc) {
1393 ERR("%s: Missing ietf-netconf schema in context (session %u)", session->id);
1394 return NC_MSG_ERROR;
1395 }
Radek Krejci695d4fa2015-10-22 13:23:54 +02001396 }
1397
Michal Vaskoad928112015-11-25 15:52:10 +01001398 switch (rpc->type) {
1399 case NC_RPC_GENERIC:
1400 rpc_gen = (struct nc_rpc_generic *)rpc;
Radek Krejci926a5742015-10-31 17:50:49 +01001401
Michal Vaskoad928112015-11-25 15:52:10 +01001402 data = rpc_gen->data;
Radek Krejci926a5742015-10-31 17:50:49 +01001403 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001404
1405 case NC_RPC_GENERIC_XML:
1406 rpc_gen_xml = (struct nc_rpc_generic_xml *)rpc;
1407
Michal Vasko5ef882e2015-12-08 14:39:34 +01001408 data = lyd_parse_data(session->ctx, rpc_gen_xml->xml_str, LYD_XML, LYD_OPT_STRICT);
Michal Vaskoad928112015-11-25 15:52:10 +01001409 break;
1410
Radek Krejci695d4fa2015-10-22 13:23:54 +02001411 case NC_RPC_GETCONFIG:
1412 rpc_gc = (struct nc_rpc_getconfig *)rpc;
1413
1414 data = lyd_new(NULL, ietfnc, "get-config");
1415 node = lyd_new(data, ietfnc, "source");
Radek Krejcib68f7c22015-10-31 11:04:40 +01001416 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_gc->source], NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001417 if (!node) {
1418 lyd_free(data);
1419 return NC_MSG_ERROR;
1420 }
1421 if (rpc_gc->filter) {
Michal Vaskoad611702015-12-03 13:41:51 +01001422 if (rpc_gc->filter[0] == '<') {
1423 node = lyd_new_anyxml(data, ietfnc, "filter", rpc_gc->filter);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001424 lyd_insert_attr(node, "type", "subtree");
Michal Vaskoad611702015-12-03 13:41:51 +01001425 } else {
Radek Krejci695d4fa2015-10-22 13:23:54 +02001426 node = lyd_new_anyxml(data, ietfnc, "filter", NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001427 lyd_insert_attr(node, "type", "xpath");
Michal Vaskoad611702015-12-03 13:41:51 +01001428 lyd_insert_attr(node, "select", rpc_gc->filter);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001429 }
Michal Vasko807be232015-12-09 15:24:55 +01001430 if (!node) {
1431 lyd_free(data);
1432 return NC_MSG_ERROR;
1433 }
1434 }
1435
1436 if (rpc_gc->wd_mode) {
1437 if (!ietfncwd) {
1438 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1439 if (!ietfncwd) {
1440 ERR("%s: Missing ietf-netconf-with-defaults schema in context (session %u).", __func__, session->id);
1441 return NC_MSG_ERROR;
1442 }
1443 }
1444 switch (rpc_gc->wd_mode) {
1445 case NC_WD_UNKNOWN:
1446 /* cannot get here */
1447 break;
1448 case NC_WD_ALL:
1449 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1450 break;
1451 case NC_WD_ALL_TAG:
1452 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1453 break;
1454 case NC_WD_TRIM:
1455 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1456 break;
1457 case NC_WD_EXPLICIT:
1458 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1459 break;
1460 }
1461 if (!node) {
1462 lyd_free(data);
1463 return NC_MSG_ERROR;
1464 }
Radek Krejci695d4fa2015-10-22 13:23:54 +02001465 }
1466 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001467
1468 case NC_RPC_EDIT:
1469 rpc_e = (struct nc_rpc_edit *)rpc;
1470
1471 data = lyd_new(NULL, ietfnc, "edit-config");
1472 node = lyd_new(data, ietfnc, "target");
1473 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_e->target], NULL);
1474 if (!node) {
1475 lyd_free(data);
1476 return NC_MSG_ERROR;
1477 }
1478
1479 if (rpc_e->default_op) {
1480 node = lyd_new_leaf(data, ietfnc, "default-operation", rpcedit_dfltop2str[rpc_e->default_op]);
1481 if (!node) {
1482 lyd_free(data);
1483 return NC_MSG_ERROR;
1484 }
1485 }
1486
1487 if (rpc_e->test_opt) {
1488 node = lyd_new_leaf(data, ietfnc, "test-option", rpcedit_testopt2str[rpc_e->test_opt]);
1489 if (!node) {
1490 lyd_free(data);
1491 return NC_MSG_ERROR;
1492 }
1493 }
1494
1495 if (rpc_e->error_opt) {
1496 node = lyd_new_leaf(data, ietfnc, "error-option", rpcedit_erropt2str[rpc_e->error_opt]);
1497 if (!node) {
1498 lyd_free(data);
1499 return NC_MSG_ERROR;
1500 }
1501 }
1502
1503 if (rpc_e->edit_cont[0] == '<') {
1504 node = lyd_new_anyxml(data, ietfnc, "config", rpc_e->edit_cont);
1505 } else {
1506 node = lyd_new_leaf(data, ietfnc, "url", rpc_e->edit_cont);
1507 }
1508 if (!node) {
1509 lyd_free(data);
1510 return NC_MSG_ERROR;
1511 }
1512 break;
1513
1514 case NC_RPC_COPY:
1515 rpc_cp = (struct nc_rpc_copy *)rpc;
1516
1517 data = lyd_new(NULL, ietfnc, "copy-config");
1518 node = lyd_new(data, ietfnc, "target");
1519 if (rpc_cp->url_trg) {
1520 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_trg);
1521 } else {
1522 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->target], NULL);
1523 }
1524 if (!node) {
1525 lyd_free(data);
1526 return NC_MSG_ERROR;
1527 }
1528
1529 node = lyd_new(data, ietfnc, "source");
1530 if (rpc_cp->url_config_src) {
1531 if (rpc_cp->url_config_src[0] == '<') {
1532 node = lyd_new_anyxml(node, ietfnc, "config", rpc_cp->url_config_src);
1533 } else {
1534 node = lyd_new_leaf(node, ietfnc, "url", rpc_cp->url_config_src);
1535 }
1536 } else {
1537 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_cp->source], NULL);
1538 }
1539 if (!node) {
1540 lyd_free(data);
1541 return NC_MSG_ERROR;
1542 }
Michal Vasko807be232015-12-09 15:24:55 +01001543
1544 if (rpc_cp->wd_mode) {
1545 if (!ietfncwd) {
1546 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1547 if (!ietfncwd) {
1548 ERR("%s: Missing ietf-netconf-with-defaults schema in context (session %u).", __func__, session->id);
1549 return NC_MSG_ERROR;
1550 }
1551 }
1552 switch (rpc_cp->wd_mode) {
1553 case NC_WD_UNKNOWN:
1554 /* cannot get here */
1555 break;
1556 case NC_WD_ALL:
1557 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1558 break;
1559 case NC_WD_ALL_TAG:
1560 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1561 break;
1562 case NC_WD_TRIM:
1563 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1564 break;
1565 case NC_WD_EXPLICIT:
1566 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1567 break;
1568 }
1569 if (!node) {
1570 lyd_free(data);
1571 return NC_MSG_ERROR;
1572 }
1573 }
Michal Vaskoad928112015-11-25 15:52:10 +01001574 break;
1575
1576 case NC_RPC_DELETE:
1577 rpc_del = (struct nc_rpc_delete *)rpc;
1578
1579 data = lyd_new(NULL, ietfnc, "delete-config");
1580 node = lyd_new(data, ietfnc, "target");
1581 if (rpc_del->url) {
1582 node = lyd_new_leaf(node, ietfnc, "url", rpc_del->url);
1583 } else {
1584 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_del->target], NULL);
1585 }
1586 if (!node) {
1587 lyd_free(data);
1588 return NC_MSG_ERROR;
1589 }
1590 break;
1591
Radek Krejci695d4fa2015-10-22 13:23:54 +02001592 case NC_RPC_LOCK:
1593 rpc_lock = (struct nc_rpc_lock *)rpc;
1594
1595 data = lyd_new(NULL, ietfnc, "lock");
1596 node = lyd_new(data, ietfnc, "target");
Radek Krejcib68f7c22015-10-31 11:04:40 +01001597 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001598 if (!node) {
1599 lyd_free(data);
1600 return NC_MSG_ERROR;
1601 }
1602 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001603
Radek Krejci695d4fa2015-10-22 13:23:54 +02001604 case NC_RPC_UNLOCK:
1605 rpc_lock = (struct nc_rpc_lock *)rpc;
1606
1607 data = lyd_new(NULL, ietfnc, "unlock");
1608 node = lyd_new(data, ietfnc, "target");
Radek Krejcib68f7c22015-10-31 11:04:40 +01001609 node = lyd_new_leaf(node, ietfnc, ncds2str[rpc_lock->target], NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001610 if (!node) {
1611 lyd_free(data);
1612 return NC_MSG_ERROR;
1613 }
1614 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001615
1616 case NC_RPC_GET:
1617 rpc_g = (struct nc_rpc_get *)rpc;
1618
1619 data = lyd_new(NULL, ietfnc, "get");
1620 if (rpc_g->filter) {
Michal Vaskoad611702015-12-03 13:41:51 +01001621 if (rpc_g->filter[0] == '<') {
1622 node = lyd_new_anyxml(data, ietfnc, "filter", rpc_g->filter);
Michal Vaskoad928112015-11-25 15:52:10 +01001623 lyd_insert_attr(node, "type", "subtree");
Michal Vaskoad611702015-12-03 13:41:51 +01001624 } else {
Michal Vaskoad928112015-11-25 15:52:10 +01001625 node = lyd_new_anyxml(data, ietfnc, "filter", NULL);
Michal Vaskoad928112015-11-25 15:52:10 +01001626 lyd_insert_attr(node, "type", "xpath");
Michal Vaskoad611702015-12-03 13:41:51 +01001627 lyd_insert_attr(node, "select", rpc_g->filter);
Michal Vaskoad928112015-11-25 15:52:10 +01001628 }
1629 if (!node) {
1630 lyd_free(data);
1631 return NC_MSG_ERROR;
1632 }
1633 }
Michal Vasko807be232015-12-09 15:24:55 +01001634
1635 if (rpc_g->wd_mode) {
1636 if (!ietfncwd) {
1637 ietfncwd = ly_ctx_get_module(session->ctx, "ietf-netconf-with-defaults", NULL);
1638 if (!ietfncwd) {
1639 ERR("%s: Missing ietf-netconf-with-defaults schema in context (session %u).", __func__, session->id);
1640 return NC_MSG_ERROR;
1641 }
1642 }
1643 switch (rpc_g->wd_mode) {
1644 case NC_WD_UNKNOWN:
1645 /* cannot get here */
1646 break;
1647 case NC_WD_ALL:
1648 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all");
1649 break;
1650 case NC_WD_ALL_TAG:
1651 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "report-all-tagged");
1652 break;
1653 case NC_WD_TRIM:
1654 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "trim");
1655 break;
1656 case NC_WD_EXPLICIT:
1657 node = lyd_new_leaf(data, ietfncwd, "with-defaults", "explicit");
1658 break;
1659 }
1660 if (!node) {
1661 lyd_free(data);
1662 return NC_MSG_ERROR;
1663 }
1664 }
Michal Vaskoad928112015-11-25 15:52:10 +01001665 break;
1666
1667 case NC_RPC_KILL:
1668 rpc_k = (struct nc_rpc_kill *)rpc;
1669
1670 data = lyd_new(NULL, ietfnc, "kill-session");
1671 sprintf(str, "%u", rpc_k->sid);
1672 lyd_new_leaf(data, ietfnc, "session-id", str);
1673 break;
1674
1675 case NC_RPC_COMMIT:
1676 rpc_com = (struct nc_rpc_commit *)rpc;
1677
1678 data = lyd_new(NULL, ietfnc, "commit");
1679 if (rpc_com->confirmed) {
1680 lyd_new_leaf(data, ietfnc, "confirmed", NULL);
1681 }
1682
1683 if (rpc_com->confirm_timeout) {
1684 sprintf(str, "%u", rpc_com->confirm_timeout);
1685 lyd_new_leaf(data, ietfnc, "confirm-timeout", str);
1686 }
1687
1688 if (rpc_com->persist) {
1689 node = lyd_new_leaf(data, ietfnc, "persist", rpc_com->persist);
1690 if (!node) {
1691 lyd_free(data);
1692 return NC_MSG_ERROR;
1693 }
1694 }
1695
1696 if (rpc_com->persist_id) {
1697 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_com->persist_id);
1698 if (!node) {
1699 lyd_free(data);
1700 return NC_MSG_ERROR;
1701 }
1702 }
1703 break;
1704
1705 case NC_RPC_DISCARD:
1706 data = lyd_new(NULL, ietfnc, "discard-changes");
1707 break;
1708
1709 case NC_RPC_CANCEL:
1710 rpc_can = (struct nc_rpc_cancel *)rpc;
1711
1712 data = lyd_new(NULL, ietfnc, "cancel-commit");
1713 if (rpc_can->persist_id) {
1714 node = lyd_new_leaf(data, ietfnc, "persist-id", rpc_can->persist_id);
1715 if (!node) {
1716 lyd_free(data);
1717 return NC_MSG_ERROR;
1718 }
1719 }
1720 break;
1721
1722 case NC_RPC_VALIDATE:
1723 rpc_val = (struct nc_rpc_validate *)rpc;
1724
1725 data = lyd_new(NULL, ietfnc, "validate");
1726 if (rpc_val->url_config_src) {
1727 if (rpc_val->url_config_src[0] == '<') {
1728 node = lyd_new_anyxml(data, ietfnc, "config", rpc_val->url_config_src);
1729 } else {
1730 node = lyd_new_leaf(data, ietfnc, "url", rpc_val->url_config_src);
1731 }
1732 } else {
1733 node = lyd_new_leaf(data, ietfnc, ncds2str[rpc_val->source], NULL);
1734 }
1735 if (!node) {
1736 lyd_free(data);
1737 return NC_MSG_ERROR;
1738 }
1739 break;
1740
1741 case NC_RPC_GETSCHEMA:
1742 ietfncmon = ly_ctx_get_module(session->ctx, "ietf-netconf-monitoring", NULL);
1743 if (!ietfncmon) {
1744 ERR("%s: Missing ietf-netconf-monitoring schema in context (session %u)", session->id);
1745 return NC_MSG_ERROR;
1746 }
1747
1748 rpc_gs = (struct nc_rpc_getschema *)rpc;
1749
1750 data = lyd_new(NULL, ietfncmon, "get-schema");
1751 node = lyd_new_leaf(data, ietfncmon, "identifier", rpc_gs->identifier);
1752 if (!node) {
1753 lyd_free(data);
1754 return NC_MSG_ERROR;
1755 }
1756 if (rpc_gs->version) {
1757 node = lyd_new_leaf(data, ietfncmon, "version", rpc_gs->version);
1758 if (!node) {
1759 lyd_free(data);
1760 return NC_MSG_ERROR;
1761 }
1762 }
1763 if (rpc_gs->format) {
1764 node = lyd_new_leaf(data, ietfncmon, "format", rpc_gs->format);
1765 if (!node) {
1766 lyd_free(data);
1767 return NC_MSG_ERROR;
1768 }
1769 }
1770 break;
1771
1772 case NC_RPC_SUBSCRIBE:
1773 notifs = ly_ctx_get_module(session->ctx, "notifications", NULL);
1774 if (!notifs) {
1775 ERR("%s: Missing notifications schema in context (session %u)", session->id);
1776 return NC_MSG_ERROR;
1777 }
1778
1779 rpc_sub = (struct nc_rpc_subscribe *)rpc;
1780
1781 data = lyd_new(NULL, notifs, "create-subscription");
1782 if (rpc_sub->stream) {
1783 node = lyd_new_leaf(data, notifs, "stream", rpc_sub->stream);
1784 if (!node) {
1785 lyd_free(data);
1786 return NC_MSG_ERROR;
1787 }
1788 }
1789
1790 if (rpc_sub->filter) {
Michal Vaskoad611702015-12-03 13:41:51 +01001791 if (rpc_sub->filter[0] == '<') {
1792 node = lyd_new_anyxml(data, notifs, "filter", rpc_sub->filter);
Michal Vaskoad928112015-11-25 15:52:10 +01001793 lyd_insert_attr(node, "type", "subtree");
Michal Vaskoad611702015-12-03 13:41:51 +01001794 } else {
Michal Vaskoad928112015-11-25 15:52:10 +01001795 node = lyd_new_anyxml(data, notifs, "filter", NULL);
Michal Vaskoad928112015-11-25 15:52:10 +01001796 lyd_insert_attr(node, "type", "xpath");
Michal Vaskoad611702015-12-03 13:41:51 +01001797 lyd_insert_attr(node, "select", rpc_sub->filter);
Michal Vaskoad928112015-11-25 15:52:10 +01001798 }
1799 if (!node) {
1800 lyd_free(data);
1801 return NC_MSG_ERROR;
1802 }
1803 }
1804
1805 if (rpc_sub->start) {
1806 node = lyd_new_leaf(data, notifs, "startTime", rpc_sub->start);
1807 if (!node) {
1808 lyd_free(data);
1809 return NC_MSG_ERROR;
1810 }
1811 }
1812
1813 if (rpc_sub->stop) {
1814 node = lyd_new_leaf(data, notifs, "stopTime", rpc_sub->stop);
1815 if (!node) {
1816 lyd_free(data);
1817 return NC_MSG_ERROR;
1818 }
1819 }
1820 break;
Michal Vaskoad928112015-11-25 15:52:10 +01001821 }
1822
1823 if (lyd_validate(data, LYD_OPT_STRICT)) {
1824 lyd_free(data);
1825 return NC_MSG_ERROR;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001826 }
1827
Michal Vaskoad611702015-12-03 13:41:51 +01001828 r = session_ti_lock(session, timeout);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001829 if (r != 0) {
1830 /* error or blocking */
1831 r = NC_MSG_WOULDBLOCK;
1832 } else {
Michal Vaskoad611702015-12-03 13:41:51 +01001833 /* send RPC, store its message ID */
1834 r = nc_send_msg(session, data);
1835 cur_msgid = session->msgid;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001836 }
1837 session_ti_unlock(session);
1838
1839 lyd_free(data);
Michal Vaskoad611702015-12-03 13:41:51 +01001840
1841 if (r != NC_MSG_RPC) {
1842 return r;
1843 }
1844
1845 *msgid = cur_msgid;
1846 return NC_MSG_RPC;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001847}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001848
1849/* CALL HOME */
1850
1851static int
1852get_listen_socket(const char *address, uint16_t port)
1853{
1854 int sock;
1855 const int optVal = 1;
1856 const socklen_t optLen = sizeof(optVal);
1857 char is_ipv4;
1858 struct sockaddr_storage saddr;
1859
1860 struct sockaddr_in* saddr4;
1861 struct sockaddr_in6* saddr6;
1862
1863 if (!address || !port) {
1864 return -1;
1865 }
1866
1867 if (strchr(address, ':') == NULL) {
1868 is_ipv4 = 1;
1869 } else {
1870 is_ipv4 = 0;
1871 }
1872
1873 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
1874 if (sock == -1) {
1875 ERR("Could not create socket (%s)", strerror(errno));
1876 return -1;
1877 }
1878
1879 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
1880 ERR("Could not set socket SO_REUSEADDR option (%s)", strerror(errno));
1881 close(sock);
1882 return -1;
1883 }
1884
1885 /* TODO may be needed
1886 if (fcntl(sock, F_SETFD, FD_CLOEXEC) != 0) {
1887 nc_verb_error("%s: fcntl failed (%s)", __func__, strerror(errno));
1888 continue;
1889 }*/
1890
1891 bzero(&saddr, sizeof(struct sockaddr_storage));
1892 if (is_ipv4) {
1893 saddr4 = (struct sockaddr_in *)&saddr;
1894
1895 saddr4->sin_family = AF_INET;
1896 saddr4->sin_port = htons(port);
1897
1898 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
1899 ERR("Failed to convert \"%s\" to IPv4 address.", address);
1900 close(sock);
1901 return -1;
1902 }
1903
1904 if (bind(sock, (struct sockaddr*)saddr4, sizeof(struct sockaddr_in)) == -1) {
1905 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
1906 close(sock);
1907 return -1;
1908 }
1909
1910 } else {
1911 saddr6 = (struct sockaddr_in6 *)&saddr;
1912
1913 saddr6->sin6_family = AF_INET6;
1914 saddr6->sin6_port = htons(port);
1915
1916 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
1917 ERR("Failed to convert \"%s\" to IPv6 address.", address);
1918 close(sock);
1919 return -1;
1920 }
1921
1922 if (bind(sock, (struct sockaddr*)saddr6, sizeof(struct sockaddr_in6)) == -1) {
1923 ERR("Could not bind \"%s\" port %d (%s)", __func__, address, port, strerror(errno));
1924 close(sock);
1925 return -1;
1926 }
1927 }
1928
1929 if (listen(sock, NC_REVERSE_QUEUE)) {
1930 ERR("Unable to start listening on \"%s\" port %d (%s)", __func__, address, port, strerror(errno));
1931 close(sock);
1932 return -1;
1933 }
1934
1935 return sock;
1936}
1937
1938int
1939nc_callhome_accept_connection(uint16_t port, int32_t timeout, uint16_t *server_port, char **server_host)
1940{
1941 struct pollfd reverse_listen_socket = {-1, POLLIN, 0};
1942 int sock;
1943 struct sockaddr_storage remote;
1944 socklen_t addr_size = sizeof(remote);
1945 int status;
1946
1947 reverse_listen_socket.fd = get_listen_socket("::0", port);
1948 if (reverse_listen_socket.fd == -1) {
1949 goto fail;
1950 }
1951
1952 reverse_listen_socket.revents = 0;
1953 while (1) {
1954 DBG("Waiting %ums for incoming Call Home connections.", timeout);
1955 status = poll(&reverse_listen_socket, 1, timeout);
1956
1957 if (status == 0) {
1958 /* timeout */
1959 ERR("Timeout for Call Home listen expired.");
1960 goto fail;
1961 } else if ((status == -1) && (errno == EINTR)) {
1962 /* poll was interrupted - try it again */
1963 continue;
1964 } else if (status < 0) {
1965 /* poll failed - something wrong happened */
1966 ERR("Call Home poll failed (%s).", strerror(errno));
1967 goto fail;
1968 } else if (status > 0) {
1969 if (reverse_listen_socket.revents & (POLLHUP | POLLERR)) {
1970 /* close pipe/fd - other side already did it */
1971 ERR("Call Home listening socket was closed.");
1972 goto fail;
1973 } else if (reverse_listen_socket.revents & POLLIN) {
1974 /* accept call home */
1975 sock = accept(reverse_listen_socket.fd, (struct sockaddr *)&remote, &addr_size);
1976 break;
1977 }
1978 }
1979 }
1980
1981 /* we accepted a connection, that's it */
1982 close(reverse_listen_socket.fd);
1983
1984 /* fill some server info, if interested */
1985 if (remote.ss_family == AF_INET) {
1986 struct sockaddr_in *remote_in = (struct sockaddr_in *)&remote;
1987 if (server_port) {
1988 *server_port = ntohs(remote_in->sin_port);
1989 }
1990 if (server_host) {
1991 *server_host = malloc(INET6_ADDRSTRLEN);
1992 inet_ntop(AF_INET, &(remote_in->sin_addr), *server_host, INET6_ADDRSTRLEN);
1993 }
1994 } else if (remote.ss_family == AF_INET6) {
1995 struct sockaddr_in6 *remote_in = (struct sockaddr_in6 *)&remote;
1996 if (server_port) {
1997 *server_port = ntohs(remote_in->sin6_port);
1998 }
1999 if (server_host) {
2000 *server_host = malloc(INET6_ADDRSTRLEN);
2001 inet_ntop(AF_INET6, &(remote_in->sin6_addr), *server_host, INET6_ADDRSTRLEN);
2002 }
2003 }
2004
2005 return sock;
2006
2007fail:
2008 if (reverse_listen_socket.fd != -1) {
2009 close(reverse_listen_socket.fd);
2010 }
2011
2012 return -1;
2013}