blob: d793d02161f67da2d68014353224e432267ef1d4 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file io.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
Radek Krejcife0b3472015-10-12 13:43:42 +020023#define _GNU_SOURCE /* asprintf */
Radek Krejci206fcd62015-10-07 15:42:48 +020024#include <assert.h>
25#include <errno.h>
26#include <poll.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020027#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020028#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#include <libyang/libyang.h>
33
34#include "config.h"
35#include "libnetconf.h"
36#include "session_p.h"
Radek Krejcife0b3472015-10-12 13:43:42 +020037#include "messages_p.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020038
Radek Krejcife0b3472015-10-12 13:43:42 +020039#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020040
41static ssize_t
42nc_read(struct nc_session *session, char *buf, size_t count)
43{
44 size_t size = 0;
45 ssize_t r;
46
47 assert(session);
48 assert(buf);
49
50 if (!count) {
51 return 0;
52 }
53
Michal Vasko38a7c6c2015-12-04 12:29:20 +010054 switch (session->ti_type) {
55 case NC_TI_NONE:
56 return 0;
57
Radek Krejci206fcd62015-10-07 15:42:48 +020058 case NC_TI_FD:
59 /* read via standard file descriptor */
Radek Krejci206fcd62015-10-07 15:42:48 +020060 while(count) {
61 r = read(session->ti.fd.in, &(buf[size]), count);
62 if (r < 0) {
63 if (errno == EAGAIN) {
64 usleep(NC_READ_SLEEP);
65 continue;
66 } else {
67 ERR("Reading from file descriptor (%d) failed (%s).", session->ti.fd.in, strerror(errno));
68 return -1;
69 }
70 } else if (r == 0) {
71 ERR("Communication file descriptor (%d) unexpectedly closed.", session->ti.fd.in);
72 return -1;
73 }
74
75 size = size + r;
76 count = count - r;
77 }
78 break;
79
Michal Vaskofb2fb762015-10-27 11:44:32 +010080#ifdef ENABLE_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020081 case NC_TI_LIBSSH:
82 /* read via libssh */
83 while(count) {
84 r = ssh_channel_read(session->ti.libssh.channel, &(buf[size]), count, 0);
85 if (r == SSH_AGAIN) {
86 usleep (NC_READ_SLEEP);
87 continue;
88 } else if (r == SSH_ERROR) {
89 ERR("Reading from the SSH channel failed (%zd: %s)",
90 ssh_get_error_code(session->ti.libssh.session), ssh_get_error(session->ti.libssh.session));
91 return -1;
92 } else if (r == 0) {
93 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
94 ERR("Communication socket unexpectedly closed (libssh).");
95 return -1;
96 }
97 usleep (NC_READ_SLEEP);
98 continue;
99 }
100
101 size = size + r;
102 count = count - r;
103 }
104 break;
105#endif
106
107#ifdef ENABLE_TLS
108 case NC_TI_OPENSSL:
109 /* read via OpenSSL */
Radek Krejcid0046592015-10-08 12:52:02 +0200110 while(count) {
111 r = SSL_read(session->ti.tls, &(buf[size]), count);
112 if (r <= 0) {
113 int x;
114 switch (x = SSL_get_error(session->ti.tls, r)) {
115 case SSL_ERROR_WANT_READ:
116 usleep(NC_READ_SLEEP);
117 continue;
118 case SSL_ERROR_ZERO_RETURN:
119 ERR("Communication socket unexpectedly closed (OpenSSL).");
120 return -1;
121 default:
122 ERR("Reading from the TLS session failed (SSL code %d)", x);
123 return -1;
124 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200125 }
Radek Krejcid0046592015-10-08 12:52:02 +0200126 size = size + r;
127 count = count - r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200128 }
129 break;
130#endif
131 }
132
133 return (ssize_t)size;
134}
135
136static ssize_t
137nc_read_chunk(struct nc_session *session, size_t len, char **chunk)
138{
139 ssize_t r;
140
141 assert(session);
142 assert(chunk);
143
144 if (!len) {
145 return 0;
146 }
147
148 *chunk = malloc ((len + 1) * sizeof **chunk);
149 if (!*chunk) {
150 ERRMEM;
151 return -1;
152 }
153
154 r = nc_read(session, *chunk, len);
155 if (r <= 0) {
156 free(*chunk);
157 return -1;
158 }
159
160 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200161 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200162
163 return r;
164}
165
166static ssize_t
167nc_read_until(struct nc_session *session, const char *endtag, size_t limit, char **result)
168{
169 char *chunk = NULL;
170 size_t size, count = 0, r, len;
171
172 assert(session);
173 assert(endtag);
174
175 if (limit && limit < BUFFERSIZE) {
176 size = limit;
177 } else {
178 size = BUFFERSIZE;
179 }
180 chunk = malloc ((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200181 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200182 ERRMEM;
183 return -1;
184 }
185
186 len = strlen(endtag);
187 while(1) {
188 if (limit && count == limit) {
189 free(chunk);
190 WRN("%s: reading limit (%d) reached.", __func__, limit);
191 ERR("Invalid input data (missing \"%s\" sequence).", endtag);
192 return -1;
193 }
194
195 /* resize buffer if needed */
196 if (count == size) {
197 /* get more memory */
198 size = size + BUFFERSIZE;
199 char *tmp = realloc (chunk, (size + 1) * sizeof *tmp);
200 if (!tmp) {
201 ERRMEM;
202 free(chunk);
203 return -1;
204 }
205 chunk = tmp;
206 }
207
208 /* get another character */
209 r = nc_read(session, &(chunk[count]), 1);
210 if (r != 1) {
211 free(chunk);
212 return -1;
213 }
214
215 count++;
216
217 /* check endtag */
218 if (count >= len) {
219 if (!strncmp(endtag, &(chunk[count - len]), len)) {
220 /* endtag found */
221 break;
222 }
223 }
224 }
225
226 /* terminating null byte */
227 chunk[count] = 0;
228
229 if (result) {
230 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200231 } else {
232 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200233 }
234 return count;
235}
236
237NC_MSG_TYPE
238nc_read_msg(struct nc_session* session, int timeout, struct lyxml_elem **data)
239{
240 int status;
241 int revents;
242 struct pollfd fds;
243 const char *emsg = NULL;
244 char *msg = NULL, *chunk, *aux;
Radek Krejcife0b3472015-10-12 13:43:42 +0200245 unsigned long int chunk_len, len = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200246
247 assert(data);
248 *data = NULL;
249
250 /* fill fds structure for poll */
251 if (session->ti_type == NC_TI_FD) {
252 fds.fd = session->ti.fd.in;
253#ifdef ENABLE_TLS
254 } else if (session->ti_type == NC_TI_OPENSSL) {
Radek Krejcid0046592015-10-08 12:52:02 +0200255 fds.fd = SSL_get_fd(session->ti.tls);
Radek Krejci206fcd62015-10-07 15:42:48 +0200256#endif
257 }
258
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100259 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200260 /* poll loop */
261
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100262 switch (session->ti_type) {
263 case NC_TI_NONE:
264 return NC_MSG_ERROR;
265
Michal Vaskofb2fb762015-10-27 11:44:32 +0100266#ifdef ENABLE_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +0200267 case NC_TI_LIBSSH:
268 /* we are getting data from libssh's channel */
269 status = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
270 if (status > 0) {
271 revents = POLLIN;
272 } else if (status == SSH_AGAIN) {
273 /* try again */
274 continue;
275 } else if (status == SSH_EOF) {
276 emsg = "SSH channel closed";
277 } else {
278 if (!session->ti.libssh.channel) {
279 emsg = strerror(errno);
280 } else if (session->ti.libssh.session) {
281 emsg = ssh_get_error(session->ti.libssh.session);
282 } else {
283 emsg = "description not available";
284 }
285 }
286 break;
287#endif
288
289#ifdef ENABLE_TLS
290 case NC_TI_OPENSSL:
291 /* no break - same processing as in case of standard file descriptors */
292#endif
293 case NC_TI_FD:
294 fds.events = POLLIN;
295 fds.revents = 0;
296 status = poll(&fds, 1, timeout);
297 revents = (unsigned long int) fds.revents;
298 break;
299 }
300
301 /* process the poll result */
302 if (status == 0) {
303 /* timed out */
304 return NC_MSG_WOULDBLOCK;
305 } else if ((status == -1) && (errno == EINTR)) {
306 /* poll was interrupted */
307 continue;
308 } else if (status < 0) {
309 /* poll failed - something really bad happened, close the session */
310 ERR("Input channel error (%s).", emsg ? emsg : strerror(errno));
311
312 /* TODO - destroy the session */
313
314 return NC_MSG_ERROR;
315 } else { /* status > 0 */
316 /* in case of standard (non-libssh) poll, there still can be an error */
317 if ((revents & POLLHUP) || (revents & POLLERR)) {
318 /* close client's socket (it's probably already closed by peer */
319 ERR("Input channel closed.");
320
321 /* TODO - destroy the session */
322
323 return NC_MSG_ERROR;
324 }
325
326 /* we have something to read, so get out of the loop */
327 break;
328 }
329 }
330
331 /* read the message */
332 switch (session->version) {
333 case NC_VERSION_10:
334 status = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &msg);
335 if (status == -1) {
336 goto error;
337 }
338
339 /* cut off the end tag */
340 msg[status - NC_VERSION_10_ENDTAG_LEN] = '\0';
341 break;
342 case NC_VERSION_11:
343 while(1) {
344 status = nc_read_until(session, "\n#", 0, NULL);
345 if (status == -1) {
346 goto error;
347 }
348 status = nc_read_until(session, "\n", 0, &chunk);
349 if (status == -1) {
350 goto error;
351 }
352
353 if (!strcmp(chunk, "#\n")) {
354 /* end of chunked framing message */
355 free(chunk);
356 break;
357 }
358
359 /* convert string to the size of the following chunk */
360 chunk_len = strtoul(chunk, (char **) NULL, 10);
Radek Krejcife0b3472015-10-12 13:43:42 +0200361 free (chunk);
362 if (!chunk_len) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200363 ERR("Invalid frame chunk size detected, fatal error.");
364 goto error;
365 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200366
367 /* now we have size of next chunk, so read the chunk */
368 status = nc_read_chunk(session, chunk_len, &chunk);
369 if (status == -1) {
370 goto error;
371 }
372
373 /* realloc message buffer, remember to count terminating null byte */
374 aux = realloc(msg, len + chunk_len + 1);
375 if (!aux) {
376 ERRMEM;
377 goto error;
378 }
379 msg = aux;
380 memcpy(msg + len, chunk, chunk_len);
381 len += chunk_len;
382 msg[len] = '\0';
383 free(chunk);
384 }
385
386 break;
387 }
Michal Vaskoaebea602015-10-26 15:35:34 +0100388 DBG("Received message (session %u): %s", session->id, msg);
Radek Krejci206fcd62015-10-07 15:42:48 +0200389
390 /* build XML tree */
Michal Vasko0dbafd12015-12-10 10:10:37 +0100391 *data = lyxml_read_data(session->ctx, msg, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200392 if (!*data) {
393 goto error;
394 } else if (!(*data)->ns) {
395 ERR("Invalid message root element (invalid namespace)");
396 goto error;
397 }
398 free(msg);
399 msg = NULL;
400
401 /* get and return message type */
402 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
403 if (!strcmp((*data)->name, "rpc")) {
404 return NC_MSG_RPC;
405 } else if (!strcmp((*data)->name, "rpc-reply")) {
406 return NC_MSG_REPLY;
407 } else if (!strcmp((*data)->name, "hello")) {
408 return NC_MSG_HELLO;
409 } else {
410 ERR("Invalid message root element (invalid name \"%s\")", (*data)->name);
411 goto error;
412 }
413 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
414 if (!strcmp((*data)->name, "notification")) {
Radek Krejci5686ff72015-10-09 13:33:56 +0200415 return NC_MSG_NOTIF;
Radek Krejci206fcd62015-10-07 15:42:48 +0200416 } else {
417 ERR("Invalid message root element (invalid name \"%s\")", (*data)->name);
418 goto error;
419 }
420 } else {
421 ERR("Invalid message root element (invalid namespace \"%s\")", (*data)->ns->value);
422 goto error;
423 }
424
425error:
426 /* cleanup */
427 free(msg);
428 free(*data);
429 *data = NULL;
430
Radek Krejci695d4fa2015-10-22 13:23:54 +0200431 if (session->side == NC_SERVER && session->version == NC_VERSION_11) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200432 /* NETCONF version 1.1 define sending error reply from the server */
433 /* TODO
434 reply = nc_reply_error(nc_err_new(NC_ERR_MALFORMED_MSG));
435 if (reply == NULL) {
436 ERROR("Unable to create the \'Malformed message\' reply");
437 nc_session_close(session, NC_SESSION_TERM_OTHER);
438 return (NC_MSG_UNKNOWN);
439 }
440
441 if (nc_session_send_reply(session, NULL, reply) == 0) {
442 ERROR("Unable to send the \'Malformed message\' reply");
443 nc_session_close(session, NC_SESSION_TERM_OTHER);
444 return (NC_MSG_UNKNOWN);
445 }
446 nc_reply_free(reply);
447 */
448 }
449
450 ERR("Malformed message received, closing the session %u.", session->id);
451 /* TODO - destroy the session */
452
453 return NC_MSG_ERROR;
454}
Radek Krejcife0b3472015-10-12 13:43:42 +0200455
456#define WRITE_BUFSIZE (2 * BUFFERSIZE)
457struct wclb_arg {
458 struct nc_session *session;
459 char buf[WRITE_BUFSIZE];
460 size_t len;
461};
462
463static ssize_t
464write_(struct nc_session *session, const void *buf, size_t count)
465{
466 int c = 0;
467 char chunksize[20];
468
469 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100470 case NC_TI_NONE:
471 return -1;
472
Radek Krejcife0b3472015-10-12 13:43:42 +0200473 case NC_TI_FD:
474 if (session->version == NC_VERSION_11) {
475 c = dprintf(session->ti.fd.out, "\n#%zu\n", count);
476 }
477 return write(session->ti.fd.out, buf, count) + c;
478
Michal Vaskofb2fb762015-10-27 11:44:32 +0100479#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200480 case NC_TI_LIBSSH:
481 if (session->version == NC_VERSION_11) {
482 c = snprintf(chunksize, 20, "\n#%zu\n", count);
483 ssh_channel_write(session->ti.libssh.channel, chunksize, c);
484 }
485 return ssh_channel_write(session->ti.libssh.channel, buf, count) + c;
486#endif
487
488#ifdef ENABLE_TLS
489 case NC_TI_OPENSSL:
490 if (session->version == NC_VERSION_11) {
Radek Krejcice74e352015-10-22 16:01:15 +0200491 c = snprintf(chunksize, 20, "\n#%zu\n", count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200492 SSL_write(session->ti.tls, chunksize, c);
493 }
494 return SSL_write(session->ti.tls, buf, count) + c;
495#endif
496 }
497
498 return -1;
499}
500
501static int
502write_endtag(struct nc_session *session)
503{
504 switch(session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100505 case NC_TI_NONE:
506 return 0;
507
Radek Krejcife0b3472015-10-12 13:43:42 +0200508 case NC_TI_FD:
509 if (session->version == NC_VERSION_11) {
510 write(session->ti.fd.out, "\n##\n", 4);
511 } else {
512 write(session->ti.fd.out, "]]>]]>", 6);
513 }
514 break;
515
Michal Vaskofb2fb762015-10-27 11:44:32 +0100516#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200517 case NC_TI_LIBSSH:
518 if (session->version == NC_VERSION_11) {
519 ssh_channel_write(session->ti.libssh.channel, "\n##\n", 4);
520 } else {
521 ssh_channel_write(session->ti.libssh.channel, "]]>]]>", 6);
522 }
523 break;
524#endif
525
526#ifdef ENABLE_TLS
527 case NC_TI_OPENSSL:
528 if (session->version == NC_VERSION_11) {
529 SSL_write(session->ti.tls, "\n##\n", 4);
530 } else {
531 SSL_write(session->ti.tls, "]]>]]>", 6);
532 }
533 break;
534#endif
535 }
536
537 return 0;
538}
539
540static void
541write_clb_flush(struct wclb_arg *warg)
542{
543 /* flush current buffer */
544 if (warg->len) {
545 write_(warg->session, warg->buf, warg->len);
546 warg->len = 0;
547 }
548}
549
550static ssize_t
551write_clb(void *arg, const void *buf, size_t count)
552{
553 struct wclb_arg *warg = (struct wclb_arg *)arg;
554
555 if (!buf) {
556 write_clb_flush(warg);
557
558 /* endtag */
559 write_endtag(warg->session);
560 return 0;
561 }
562
563 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
564 /* dump current buffer */
565 write_clb_flush(warg);
566 }
567 if (count > WRITE_BUFSIZE) {
568 /* write directly */
569 write_(warg->session, buf, count);
570 } else {
571 /* keep in buffer and write later */
572 memcpy(&warg->buf[warg->len], buf, count);
573 warg->len += count; /* is <= WRITE_BUFSIZE */
574 }
575
576 return (ssize_t)count;
577}
578
579/*
580 * NETCONF 1.0 format
581 */
582static int
583write_msg_10(struct nc_session *session, NC_MSG_TYPE type, va_list ap)
584{
Radek Krejci695d4fa2015-10-22 13:23:54 +0200585 int count, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200586 const char *attrs;
587 struct lyd_node *content;
Michal Vaskoad611702015-12-03 13:41:51 +0100588 struct nc_server_rpc *rpc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200589 const char **capabilities;
590 uint32_t *sid = NULL;
Radek Krejcife0b3472015-10-12 13:43:42 +0200591 char *buf = NULL;
592 struct wclb_arg arg;
593
594 arg.session = session;
595 arg.len = 0;
596
597 switch (type) {
598 case NC_MSG_RPC:
599 content = va_arg(ap, struct lyd_node *);
600 attrs = va_arg(ap, const char *);
601 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100602 case NC_TI_NONE:
603 return -1;
604
Radek Krejcife0b3472015-10-12 13:43:42 +0200605 case NC_TI_FD:
606 dprintf(session->ti.fd.out, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
607 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
608 lyd_print_fd(session->ti.fd.out, content, LYD_XML);
609 write(session->ti.fd.out, "</rpc>]]>]]>", 12);
610 break;
611
Michal Vaskofb2fb762015-10-27 11:44:32 +0100612#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200613 case NC_TI_LIBSSH:
614#endif
615#ifdef ENABLE_TLS
616 case NC_TI_OPENSSL:
617#endif
Michal Vaskofb2fb762015-10-27 11:44:32 +0100618#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
Radek Krejcife0b3472015-10-12 13:43:42 +0200619 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
620 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
621 write_clb((void *)&arg, buf, count);
622 free(buf);
623 lyd_print_clb(write_clb, (void *)&arg, content, LYD_XML);
624 write_clb((void *)&arg, "</rpc>", 6);
625
626 /* flush message */
627 write_clb((void *)&arg, NULL, 0);
628 break;
629#endif
630 }
631
632 session->msgid++;
633 break;
634
635 case NC_MSG_REPLY:
Michal Vaskoad611702015-12-03 13:41:51 +0100636 rpc = va_arg(ap, struct nc_server_rpc *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200637 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100638 case NC_TI_NONE:
639 return -1;
640
Radek Krejcife0b3472015-10-12 13:43:42 +0200641 case NC_TI_FD:
642 write(session->ti.fd.out, "<rpc-reply", 10);
643 lyxml_dump_fd(session->ti.fd.out, rpc->root, LYXML_DUMP_ATTRS);
644 write(session->ti.fd.out, ">", 1);
645
646 /* TODO content */
647
648 write(session->ti.fd.out, "</rpc-reply>]]>]]>", 18);
649 break;
650
Michal Vaskofb2fb762015-10-27 11:44:32 +0100651#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200652 case NC_TI_LIBSSH:
653#endif
654#ifdef ENABLE_TLS
655 case NC_TI_OPENSSL:
656#endif
Michal Vaskofb2fb762015-10-27 11:44:32 +0100657#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
Radek Krejcife0b3472015-10-12 13:43:42 +0200658 write_clb((void *)&arg, "<rpc-reply", 10);
659 lyxml_dump_clb(write_clb, (void *)&arg, rpc->root, LYXML_DUMP_ATTRS);
660
661 /* TODO content */
662
663 write_clb((void *)&arg, "</rpc-reply>", 12);
664
665 /* flush message */
666 write_clb((void *)&arg, NULL, 0);
667 break;
668#endif
669 }
670 break;
671
672 case NC_MSG_NOTIF:
673 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100674 case NC_TI_NONE:
675 return -1;
676
Radek Krejcife0b3472015-10-12 13:43:42 +0200677 case NC_TI_FD:
678 write(session->ti.fd.out, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
679
680 /* TODO content */
681
682 write(session->ti.fd.out, "</notification>]]>]]>", 18);
683 break;
684
Michal Vaskofb2fb762015-10-27 11:44:32 +0100685#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200686 case NC_TI_LIBSSH:
687#endif
688#ifdef ENABLE_TLS
689 case NC_TI_OPENSSL:
690#endif
Michal Vaskofb2fb762015-10-27 11:44:32 +0100691#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
Radek Krejcife0b3472015-10-12 13:43:42 +0200692 write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
693
694 /* TODO content */
695
696 write_clb((void *)&arg, "</notification>", 12);
697
698 /* flush message */
699 write_clb((void *)&arg, NULL, 0);
700 break;
701#endif
702 }
703 break;
704
705 case NC_MSG_HELLO:
Radek Krejci695d4fa2015-10-22 13:23:54 +0200706 capabilities = va_arg(ap, const char **);
707 sid = va_arg(ap, uint32_t*);
Radek Krejcife0b3472015-10-12 13:43:42 +0200708 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100709 case NC_TI_NONE:
710 return -1;
711
Radek Krejcife0b3472015-10-12 13:43:42 +0200712 case NC_TI_FD:
Radek Krejci695d4fa2015-10-22 13:23:54 +0200713 dprintf(session->ti.fd.out, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
714 for (i = 0; capabilities[i]; i++) {
715 dprintf(session->ti.fd.out, "<capability>%s</capability>", capabilities[i]);
716 }
717 if (sid) {
718 dprintf(session->ti.fd.out, "</capabilities><session-id>%u</session-id></hello>]]>]]>", *sid);
719 } else {
720 write(session->ti.fd.out, "</capabilities></hello>]]>]]>", 29);
721 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200722 break;
723
Michal Vaskofb2fb762015-10-27 11:44:32 +0100724#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200725 case NC_TI_LIBSSH:
726#endif
727#ifdef ENABLE_TLS
728 case NC_TI_OPENSSL:
729#endif
Michal Vaskofb2fb762015-10-27 11:44:32 +0100730#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200731 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Radek Krejcife0b3472015-10-12 13:43:42 +0200732 write_clb((void *)&arg, buf, count);
733 free(buf);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200734 for (i = 0; capabilities[i]; i++) {
735 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
736 write_clb((void *)&arg, buf, count);
737 free(buf);
738 }
739 if (sid) {
740 asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
741 write_clb((void *)&arg, buf, count);
742 free(buf);
743 } else {
744 write_clb((void *)&arg, "</capabilities></hello>", 23);
745 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200746
747 /* flush message */
748 write_clb((void *)&arg, NULL, 0);
749 break;
750#endif
751 }
752 break;
753
754 default:
755 return -1;
756 }
757
758 return 0;
759}
760
761/*
762 * NETCONF 1.1 format
763 */
764static int
765write_msg_11(struct nc_session *session, NC_MSG_TYPE type, va_list ap)
766{
767 int count;
768 const char *attrs;
769 struct lyd_node *content;
Michal Vaskoad611702015-12-03 13:41:51 +0100770 struct nc_server_rpc *rpc;
Radek Krejcife0b3472015-10-12 13:43:42 +0200771 char *buf = NULL;
772 struct wclb_arg arg;
773
774 arg.session = session;
775 arg.len = 0;
776
777 switch (type) {
778 case NC_MSG_RPC:
779 content = va_arg(ap, struct lyd_node *);
780 attrs = va_arg(ap, const char *);
781 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
782 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
783 write_clb((void *)&arg, buf, count);
784 free(buf);
785 lyd_print_clb(write_clb, (void *)&arg, content, LYD_XML);
786 write_clb((void *)&arg, "</rpc>", 6);
787
788 session->msgid++;
789 break;
790 case NC_MSG_REPLY:
Michal Vaskoad611702015-12-03 13:41:51 +0100791 rpc = va_arg(ap, struct nc_server_rpc *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200792 write_clb((void *)&arg, "<rpc-reply", 10);
793 lyxml_dump_clb(write_clb, (void *)&arg, rpc->root, LYXML_DUMP_ATTRS);
794 write_clb((void *)&arg, ">", 1);
795 /* TODO content */
796 write_clb((void *)&arg, "</rpc-reply>", 12);
797 break;
798 case NC_MSG_NOTIF:
799 write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
800 /* TODO content */
801 write_clb((void *)&arg, "</notification>", 12);
802 break;
803 default:
804 /* just to make compiler quiet */
805 return -1;
806 }
807
808 /* flush message */
809 write_clb((void *)&arg, NULL, 0);
810 return 0;
811}
812
813int
814nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
815{
816 va_list ap;
817 int r;
818
819 va_start(ap, type);
820
821 if (session->version == NC_VERSION_10) {
822 r = write_msg_10(session, type, ap);
823 } else {
824 r = write_msg_11(session, type, ap);
825 }
826
827 va_end(ap);
828
829 return r;
830}