blob: bd09c907e180cd61aca0dade6bd6b9e598dbe30d [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
54 switch(session->ti_type) {
55 case NC_TI_FD:
56 /* read via standard file descriptor */
Radek Krejci206fcd62015-10-07 15:42:48 +020057 while(count) {
58 r = read(session->ti.fd.in, &(buf[size]), count);
59 if (r < 0) {
60 if (errno == EAGAIN) {
61 usleep(NC_READ_SLEEP);
62 continue;
63 } else {
64 ERR("Reading from file descriptor (%d) failed (%s).", session->ti.fd.in, strerror(errno));
65 return -1;
66 }
67 } else if (r == 0) {
68 ERR("Communication file descriptor (%d) unexpectedly closed.", session->ti.fd.in);
69 return -1;
70 }
71
72 size = size + r;
73 count = count - r;
74 }
75 break;
76
77#ifdef ENABLE_LIBSSH
78 case NC_TI_LIBSSH:
79 /* read via libssh */
80 while(count) {
81 r = ssh_channel_read(session->ti.libssh.channel, &(buf[size]), count, 0);
82 if (r == SSH_AGAIN) {
83 usleep (NC_READ_SLEEP);
84 continue;
85 } else if (r == SSH_ERROR) {
86 ERR("Reading from the SSH channel failed (%zd: %s)",
87 ssh_get_error_code(session->ti.libssh.session), ssh_get_error(session->ti.libssh.session));
88 return -1;
89 } else if (r == 0) {
90 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
91 ERR("Communication socket unexpectedly closed (libssh).");
92 return -1;
93 }
94 usleep (NC_READ_SLEEP);
95 continue;
96 }
97
98 size = size + r;
99 count = count - r;
100 }
101 break;
102#endif
103
104#ifdef ENABLE_TLS
105 case NC_TI_OPENSSL:
106 /* read via OpenSSL */
Radek Krejcid0046592015-10-08 12:52:02 +0200107 while(count) {
108 r = SSL_read(session->ti.tls, &(buf[size]), count);
109 if (r <= 0) {
110 int x;
111 switch (x = SSL_get_error(session->ti.tls, r)) {
112 case SSL_ERROR_WANT_READ:
113 usleep(NC_READ_SLEEP);
114 continue;
115 case SSL_ERROR_ZERO_RETURN:
116 ERR("Communication socket unexpectedly closed (OpenSSL).");
117 return -1;
118 default:
119 ERR("Reading from the TLS session failed (SSL code %d)", x);
120 return -1;
121 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200122 }
Radek Krejcid0046592015-10-08 12:52:02 +0200123 size = size + r;
124 count = count - r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200125 }
126 break;
127#endif
128 }
129
130 return (ssize_t)size;
131}
132
133static ssize_t
134nc_read_chunk(struct nc_session *session, size_t len, char **chunk)
135{
136 ssize_t r;
137
138 assert(session);
139 assert(chunk);
140
141 if (!len) {
142 return 0;
143 }
144
145 *chunk = malloc ((len + 1) * sizeof **chunk);
146 if (!*chunk) {
147 ERRMEM;
148 return -1;
149 }
150
151 r = nc_read(session, *chunk, len);
152 if (r <= 0) {
153 free(*chunk);
154 return -1;
155 }
156
157 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200158 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200159
160 return r;
161}
162
163static ssize_t
164nc_read_until(struct nc_session *session, const char *endtag, size_t limit, char **result)
165{
166 char *chunk = NULL;
167 size_t size, count = 0, r, len;
168
169 assert(session);
170 assert(endtag);
171
172 if (limit && limit < BUFFERSIZE) {
173 size = limit;
174 } else {
175 size = BUFFERSIZE;
176 }
177 chunk = malloc ((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200178 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200179 ERRMEM;
180 return -1;
181 }
182
183 len = strlen(endtag);
184 while(1) {
185 if (limit && count == limit) {
186 free(chunk);
187 WRN("%s: reading limit (%d) reached.", __func__, limit);
188 ERR("Invalid input data (missing \"%s\" sequence).", endtag);
189 return -1;
190 }
191
192 /* resize buffer if needed */
193 if (count == size) {
194 /* get more memory */
195 size = size + BUFFERSIZE;
196 char *tmp = realloc (chunk, (size + 1) * sizeof *tmp);
197 if (!tmp) {
198 ERRMEM;
199 free(chunk);
200 return -1;
201 }
202 chunk = tmp;
203 }
204
205 /* get another character */
206 r = nc_read(session, &(chunk[count]), 1);
207 if (r != 1) {
208 free(chunk);
209 return -1;
210 }
211
212 count++;
213
214 /* check endtag */
215 if (count >= len) {
216 if (!strncmp(endtag, &(chunk[count - len]), len)) {
217 /* endtag found */
218 break;
219 }
220 }
221 }
222
223 /* terminating null byte */
224 chunk[count] = 0;
225
226 if (result) {
227 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200228 } else {
229 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200230 }
231 return count;
232}
233
234NC_MSG_TYPE
235nc_read_msg(struct nc_session* session, int timeout, struct lyxml_elem **data)
236{
237 int status;
238 int revents;
239 struct pollfd fds;
240 const char *emsg = NULL;
241 char *msg = NULL, *chunk, *aux;
Radek Krejcife0b3472015-10-12 13:43:42 +0200242 unsigned long int chunk_len, len = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200243
244 assert(data);
245 *data = NULL;
246
247 /* fill fds structure for poll */
248 if (session->ti_type == NC_TI_FD) {
249 fds.fd = session->ti.fd.in;
250#ifdef ENABLE_TLS
251 } else if (session->ti_type == NC_TI_OPENSSL) {
Radek Krejcid0046592015-10-08 12:52:02 +0200252 fds.fd = SSL_get_fd(session->ti.tls);
Radek Krejci206fcd62015-10-07 15:42:48 +0200253#endif
254 }
255
256 while(1) {
257 /* poll loop */
258
259 switch(session->ti_type) {
260#ifdef ENABLE_LIBSSH
261 case NC_TI_LIBSSH:
262 /* we are getting data from libssh's channel */
263 status = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
264 if (status > 0) {
265 revents = POLLIN;
266 } else if (status == SSH_AGAIN) {
267 /* try again */
268 continue;
269 } else if (status == SSH_EOF) {
270 emsg = "SSH channel closed";
271 } else {
272 if (!session->ti.libssh.channel) {
273 emsg = strerror(errno);
274 } else if (session->ti.libssh.session) {
275 emsg = ssh_get_error(session->ti.libssh.session);
276 } else {
277 emsg = "description not available";
278 }
279 }
280 break;
281#endif
282
283#ifdef ENABLE_TLS
284 case NC_TI_OPENSSL:
285 /* no break - same processing as in case of standard file descriptors */
286#endif
287 case NC_TI_FD:
288 fds.events = POLLIN;
289 fds.revents = 0;
290 status = poll(&fds, 1, timeout);
291 revents = (unsigned long int) fds.revents;
292 break;
293 }
294
295 /* process the poll result */
296 if (status == 0) {
297 /* timed out */
298 return NC_MSG_WOULDBLOCK;
299 } else if ((status == -1) && (errno == EINTR)) {
300 /* poll was interrupted */
301 continue;
302 } else if (status < 0) {
303 /* poll failed - something really bad happened, close the session */
304 ERR("Input channel error (%s).", emsg ? emsg : strerror(errno));
305
306 /* TODO - destroy the session */
307
308 return NC_MSG_ERROR;
309 } else { /* status > 0 */
310 /* in case of standard (non-libssh) poll, there still can be an error */
311 if ((revents & POLLHUP) || (revents & POLLERR)) {
312 /* close client's socket (it's probably already closed by peer */
313 ERR("Input channel closed.");
314
315 /* TODO - destroy the session */
316
317 return NC_MSG_ERROR;
318 }
319
320 /* we have something to read, so get out of the loop */
321 break;
322 }
323 }
324
325 /* read the message */
326 switch (session->version) {
327 case NC_VERSION_10:
328 status = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &msg);
329 if (status == -1) {
330 goto error;
331 }
332
333 /* cut off the end tag */
334 msg[status - NC_VERSION_10_ENDTAG_LEN] = '\0';
335 break;
336 case NC_VERSION_11:
337 while(1) {
338 status = nc_read_until(session, "\n#", 0, NULL);
339 if (status == -1) {
340 goto error;
341 }
342 status = nc_read_until(session, "\n", 0, &chunk);
343 if (status == -1) {
344 goto error;
345 }
346
347 if (!strcmp(chunk, "#\n")) {
348 /* end of chunked framing message */
349 free(chunk);
350 break;
351 }
352
353 /* convert string to the size of the following chunk */
354 chunk_len = strtoul(chunk, (char **) NULL, 10);
Radek Krejcife0b3472015-10-12 13:43:42 +0200355 free (chunk);
356 if (!chunk_len) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200357 ERR("Invalid frame chunk size detected, fatal error.");
358 goto error;
359 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200360
361 /* now we have size of next chunk, so read the chunk */
362 status = nc_read_chunk(session, chunk_len, &chunk);
363 if (status == -1) {
364 goto error;
365 }
366
367 /* realloc message buffer, remember to count terminating null byte */
368 aux = realloc(msg, len + chunk_len + 1);
369 if (!aux) {
370 ERRMEM;
371 goto error;
372 }
373 msg = aux;
374 memcpy(msg + len, chunk, chunk_len);
375 len += chunk_len;
376 msg[len] = '\0';
377 free(chunk);
378 }
379
380 break;
381 }
382 DBG("Received message (session %s): %s", session->id, msg);
383
384 /* build XML tree */
385 *data = lyxml_read(session->ctx, msg, 0);
386 if (!*data) {
387 goto error;
388 } else if (!(*data)->ns) {
389 ERR("Invalid message root element (invalid namespace)");
390 goto error;
391 }
392 free(msg);
393 msg = NULL;
394
395 /* get and return message type */
396 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
397 if (!strcmp((*data)->name, "rpc")) {
398 return NC_MSG_RPC;
399 } else if (!strcmp((*data)->name, "rpc-reply")) {
400 return NC_MSG_REPLY;
401 } else if (!strcmp((*data)->name, "hello")) {
402 return NC_MSG_HELLO;
403 } else {
404 ERR("Invalid message root element (invalid name \"%s\")", (*data)->name);
405 goto error;
406 }
407 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
408 if (!strcmp((*data)->name, "notification")) {
Radek Krejci5686ff72015-10-09 13:33:56 +0200409 return NC_MSG_NOTIF;
Radek Krejci206fcd62015-10-07 15:42:48 +0200410 } else {
411 ERR("Invalid message root element (invalid name \"%s\")", (*data)->name);
412 goto error;
413 }
414 } else {
415 ERR("Invalid message root element (invalid namespace \"%s\")", (*data)->ns->value);
416 goto error;
417 }
418
419error:
420 /* cleanup */
421 free(msg);
422 free(*data);
423 *data = NULL;
424
Radek Krejci695d4fa2015-10-22 13:23:54 +0200425 if (session->side == NC_SERVER && session->version == NC_VERSION_11) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200426 /* NETCONF version 1.1 define sending error reply from the server */
427 /* TODO
428 reply = nc_reply_error(nc_err_new(NC_ERR_MALFORMED_MSG));
429 if (reply == NULL) {
430 ERROR("Unable to create the \'Malformed message\' reply");
431 nc_session_close(session, NC_SESSION_TERM_OTHER);
432 return (NC_MSG_UNKNOWN);
433 }
434
435 if (nc_session_send_reply(session, NULL, reply) == 0) {
436 ERROR("Unable to send the \'Malformed message\' reply");
437 nc_session_close(session, NC_SESSION_TERM_OTHER);
438 return (NC_MSG_UNKNOWN);
439 }
440 nc_reply_free(reply);
441 */
442 }
443
444 ERR("Malformed message received, closing the session %u.", session->id);
445 /* TODO - destroy the session */
446
447 return NC_MSG_ERROR;
448}
Radek Krejcife0b3472015-10-12 13:43:42 +0200449
450#define WRITE_BUFSIZE (2 * BUFFERSIZE)
451struct wclb_arg {
452 struct nc_session *session;
453 char buf[WRITE_BUFSIZE];
454 size_t len;
455};
456
457static ssize_t
458write_(struct nc_session *session, const void *buf, size_t count)
459{
460 int c = 0;
461 char chunksize[20];
462
463 switch (session->ti_type) {
464 case NC_TI_FD:
465 if (session->version == NC_VERSION_11) {
466 c = dprintf(session->ti.fd.out, "\n#%zu\n", count);
467 }
468 return write(session->ti.fd.out, buf, count) + c;
469
470#ifdef ENABLE_LIBSSH
471 case NC_TI_LIBSSH:
472 if (session->version == NC_VERSION_11) {
473 c = snprintf(chunksize, 20, "\n#%zu\n", count);
474 ssh_channel_write(session->ti.libssh.channel, chunksize, c);
475 }
476 return ssh_channel_write(session->ti.libssh.channel, buf, count) + c;
477#endif
478
479#ifdef ENABLE_TLS
480 case NC_TI_OPENSSL:
481 if (session->version == NC_VERSION_11) {
Radek Krejcice74e352015-10-22 16:01:15 +0200482 c = snprintf(chunksize, 20, "\n#%zu\n", count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200483 SSL_write(session->ti.tls, chunksize, c);
484 }
485 return SSL_write(session->ti.tls, buf, count) + c;
486#endif
487 }
488
489 return -1;
490}
491
492static int
493write_endtag(struct nc_session *session)
494{
495 switch(session->ti_type) {
496 case NC_TI_FD:
497 if (session->version == NC_VERSION_11) {
498 write(session->ti.fd.out, "\n##\n", 4);
499 } else {
500 write(session->ti.fd.out, "]]>]]>", 6);
501 }
502 break;
503
504#ifdef ENABLE_LIBSSH
505 case NC_TI_LIBSSH:
506 if (session->version == NC_VERSION_11) {
507 ssh_channel_write(session->ti.libssh.channel, "\n##\n", 4);
508 } else {
509 ssh_channel_write(session->ti.libssh.channel, "]]>]]>", 6);
510 }
511 break;
512#endif
513
514#ifdef ENABLE_TLS
515 case NC_TI_OPENSSL:
516 if (session->version == NC_VERSION_11) {
517 SSL_write(session->ti.tls, "\n##\n", 4);
518 } else {
519 SSL_write(session->ti.tls, "]]>]]>", 6);
520 }
521 break;
522#endif
523 }
524
525 return 0;
526}
527
528static void
529write_clb_flush(struct wclb_arg *warg)
530{
531 /* flush current buffer */
532 if (warg->len) {
533 write_(warg->session, warg->buf, warg->len);
534 warg->len = 0;
535 }
536}
537
538static ssize_t
539write_clb(void *arg, const void *buf, size_t count)
540{
541 struct wclb_arg *warg = (struct wclb_arg *)arg;
542
543 if (!buf) {
544 write_clb_flush(warg);
545
546 /* endtag */
547 write_endtag(warg->session);
548 return 0;
549 }
550
551 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
552 /* dump current buffer */
553 write_clb_flush(warg);
554 }
555 if (count > WRITE_BUFSIZE) {
556 /* write directly */
557 write_(warg->session, buf, count);
558 } else {
559 /* keep in buffer and write later */
560 memcpy(&warg->buf[warg->len], buf, count);
561 warg->len += count; /* is <= WRITE_BUFSIZE */
562 }
563
564 return (ssize_t)count;
565}
566
567/*
568 * NETCONF 1.0 format
569 */
570static int
571write_msg_10(struct nc_session *session, NC_MSG_TYPE type, va_list ap)
572{
Radek Krejci695d4fa2015-10-22 13:23:54 +0200573 int count, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200574 const char *attrs;
575 struct lyd_node *content;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200576 struct nc_rpc_server *rpc;
577 const char **capabilities;
578 uint32_t *sid = NULL;
Radek Krejcife0b3472015-10-12 13:43:42 +0200579 char *buf = NULL;
580 struct wclb_arg arg;
581
582 arg.session = session;
583 arg.len = 0;
584
585 switch (type) {
586 case NC_MSG_RPC:
587 content = va_arg(ap, struct lyd_node *);
588 attrs = va_arg(ap, const char *);
589 switch (session->ti_type) {
590 case NC_TI_FD:
591 dprintf(session->ti.fd.out, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
592 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
593 lyd_print_fd(session->ti.fd.out, content, LYD_XML);
594 write(session->ti.fd.out, "</rpc>]]>]]>", 12);
595 break;
596
597#ifdef ENABLE_LIBSSH
598 case NC_TI_LIBSSH:
599#endif
600#ifdef ENABLE_TLS
601 case NC_TI_OPENSSL:
602#endif
603#if defined(ENABLE_LIBSSH) || defined(ENABLE_TLS)
604 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
605 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
606 write_clb((void *)&arg, buf, count);
607 free(buf);
608 lyd_print_clb(write_clb, (void *)&arg, content, LYD_XML);
609 write_clb((void *)&arg, "</rpc>", 6);
610
611 /* flush message */
612 write_clb((void *)&arg, NULL, 0);
613 break;
614#endif
615 }
616
617 session->msgid++;
618 break;
619
620 case NC_MSG_REPLY:
Radek Krejci695d4fa2015-10-22 13:23:54 +0200621 rpc = va_arg(ap, struct nc_rpc_server *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200622 switch (session->ti_type) {
623 case NC_TI_FD:
624 write(session->ti.fd.out, "<rpc-reply", 10);
625 lyxml_dump_fd(session->ti.fd.out, rpc->root, LYXML_DUMP_ATTRS);
626 write(session->ti.fd.out, ">", 1);
627
628 /* TODO content */
629
630 write(session->ti.fd.out, "</rpc-reply>]]>]]>", 18);
631 break;
632
633#ifdef ENABLE_LIBSSH
634 case NC_TI_LIBSSH:
635#endif
636#ifdef ENABLE_TLS
637 case NC_TI_OPENSSL:
638#endif
639#if defined(ENABLE_LIBSSH) || defined(ENABLE_TLS)
640 write_clb((void *)&arg, "<rpc-reply", 10);
641 lyxml_dump_clb(write_clb, (void *)&arg, rpc->root, LYXML_DUMP_ATTRS);
642
643 /* TODO content */
644
645 write_clb((void *)&arg, "</rpc-reply>", 12);
646
647 /* flush message */
648 write_clb((void *)&arg, NULL, 0);
649 break;
650#endif
651 }
652 break;
653
654 case NC_MSG_NOTIF:
655 switch (session->ti_type) {
656 case NC_TI_FD:
657 write(session->ti.fd.out, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
658
659 /* TODO content */
660
661 write(session->ti.fd.out, "</notification>]]>]]>", 18);
662 break;
663
664#ifdef ENABLE_LIBSSH
665 case NC_TI_LIBSSH:
666#endif
667#ifdef ENABLE_TLS
668 case NC_TI_OPENSSL:
669#endif
670#if defined(ENABLE_LIBSSH) || defined(ENABLE_TLS)
671 write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
672
673 /* TODO content */
674
675 write_clb((void *)&arg, "</notification>", 12);
676
677 /* flush message */
678 write_clb((void *)&arg, NULL, 0);
679 break;
680#endif
681 }
682 break;
683
684 case NC_MSG_HELLO:
Radek Krejci695d4fa2015-10-22 13:23:54 +0200685 capabilities = va_arg(ap, const char **);
686 sid = va_arg(ap, uint32_t*);
Radek Krejcife0b3472015-10-12 13:43:42 +0200687 switch (session->ti_type) {
688 case NC_TI_FD:
Radek Krejci695d4fa2015-10-22 13:23:54 +0200689 dprintf(session->ti.fd.out, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
690 for (i = 0; capabilities[i]; i++) {
691 dprintf(session->ti.fd.out, "<capability>%s</capability>", capabilities[i]);
692 }
693 if (sid) {
694 dprintf(session->ti.fd.out, "</capabilities><session-id>%u</session-id></hello>]]>]]>", *sid);
695 } else {
696 write(session->ti.fd.out, "</capabilities></hello>]]>]]>", 29);
697 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200698 break;
699
700#ifdef ENABLE_LIBSSH
701 case NC_TI_LIBSSH:
702#endif
703#ifdef ENABLE_TLS
704 case NC_TI_OPENSSL:
705#endif
706#if defined(ENABLE_LIBSSH) || defined(ENABLE_TLS)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200707 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Radek Krejcife0b3472015-10-12 13:43:42 +0200708 write_clb((void *)&arg, buf, count);
709 free(buf);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200710 for (i = 0; capabilities[i]; i++) {
711 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
712 write_clb((void *)&arg, buf, count);
713 free(buf);
714 }
715 if (sid) {
716 asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
717 write_clb((void *)&arg, buf, count);
718 free(buf);
719 } else {
720 write_clb((void *)&arg, "</capabilities></hello>", 23);
721 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200722
723 /* flush message */
724 write_clb((void *)&arg, NULL, 0);
725 break;
726#endif
727 }
728 break;
729
730 default:
731 return -1;
732 }
733
734 return 0;
735}
736
737/*
738 * NETCONF 1.1 format
739 */
740static int
741write_msg_11(struct nc_session *session, NC_MSG_TYPE type, va_list ap)
742{
743 int count;
744 const char *attrs;
745 struct lyd_node *content;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200746 struct nc_rpc_server *rpc;
Radek Krejcife0b3472015-10-12 13:43:42 +0200747 char *buf = NULL;
748 struct wclb_arg arg;
749
750 arg.session = session;
751 arg.len = 0;
752
753 switch (type) {
754 case NC_MSG_RPC:
755 content = va_arg(ap, struct lyd_node *);
756 attrs = va_arg(ap, const char *);
757 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
758 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
759 write_clb((void *)&arg, buf, count);
760 free(buf);
761 lyd_print_clb(write_clb, (void *)&arg, content, LYD_XML);
762 write_clb((void *)&arg, "</rpc>", 6);
763
764 session->msgid++;
765 break;
766 case NC_MSG_REPLY:
Radek Krejci695d4fa2015-10-22 13:23:54 +0200767 rpc = va_arg(ap, struct nc_rpc_server *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200768 write_clb((void *)&arg, "<rpc-reply", 10);
769 lyxml_dump_clb(write_clb, (void *)&arg, rpc->root, LYXML_DUMP_ATTRS);
770 write_clb((void *)&arg, ">", 1);
771 /* TODO content */
772 write_clb((void *)&arg, "</rpc-reply>", 12);
773 break;
774 case NC_MSG_NOTIF:
775 write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
776 /* TODO content */
777 write_clb((void *)&arg, "</notification>", 12);
778 break;
779 default:
780 /* just to make compiler quiet */
781 return -1;
782 }
783
784 /* flush message */
785 write_clb((void *)&arg, NULL, 0);
786 return 0;
787}
788
789int
790nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
791{
792 va_list ap;
793 int r;
794
795 va_start(ap, type);
796
797 if (session->version == NC_VERSION_10) {
798 r = write_msg_10(session, type, ap);
799 } else {
800 r = write_msg_11(session, type, ap);
801 }
802
803 va_end(ap);
804
805 return r;
806}