blob: e89c45667c5c47856064637c4cbd2fedefd986b6 [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
Michal Vasko428087d2016-01-14 16:04:28 +010050 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
51 return -1;
52 }
53
Radek Krejci206fcd62015-10-07 15:42:48 +020054 if (!count) {
55 return 0;
56 }
57
Michal Vasko38a7c6c2015-12-04 12:29:20 +010058 switch (session->ti_type) {
59 case NC_TI_NONE:
60 return 0;
61
Radek Krejci206fcd62015-10-07 15:42:48 +020062 case NC_TI_FD:
63 /* read via standard file descriptor */
Michal Vasko428087d2016-01-14 16:04:28 +010064 while (count) {
Radek Krejci206fcd62015-10-07 15:42:48 +020065 r = read(session->ti.fd.in, &(buf[size]), count);
66 if (r < 0) {
Michal Vasko428087d2016-01-14 16:04:28 +010067 if ((errno == EAGAIN) || (errno == EINTR)) {
Radek Krejci206fcd62015-10-07 15:42:48 +020068 usleep(NC_READ_SLEEP);
69 continue;
70 } else {
Michal Vasko428087d2016-01-14 16:04:28 +010071 ERR("%s: session %u: reading from file descriptor (%d) failed (%s).",
72 __func__, session->id, session->ti.fd.in, strerror(errno));
73 session->status = NC_STATUS_INVALID;
74 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020075 return -1;
76 }
77 } else if (r == 0) {
Michal Vasko428087d2016-01-14 16:04:28 +010078 ERR("%s: session %u: communication file descriptor (%d) unexpectedly closed.",
79 __func__, session->id, session->ti.fd.in);
80 session->status = NC_STATUS_INVALID;
81 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +020082 return -1;
83 }
84
85 size = size + r;
86 count = count - r;
87 }
88 break;
89
Michal Vaskofb2fb762015-10-27 11:44:32 +010090#ifdef ENABLE_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020091 case NC_TI_LIBSSH:
92 /* read via libssh */
Michal Vasko428087d2016-01-14 16:04:28 +010093 while (count) {
Radek Krejci206fcd62015-10-07 15:42:48 +020094 r = ssh_channel_read(session->ti.libssh.channel, &(buf[size]), count, 0);
95 if (r == SSH_AGAIN) {
Michal Vasko428087d2016-01-14 16:04:28 +010096 usleep(NC_READ_SLEEP);
Radek Krejci206fcd62015-10-07 15:42:48 +020097 continue;
98 } else if (r == SSH_ERROR) {
Michal Vasko428087d2016-01-14 16:04:28 +010099 ERR("%s: session %u: reading from the SSH channel failed (%zd: %s).", __func__, session->id,
Radek Krejci206fcd62015-10-07 15:42:48 +0200100 ssh_get_error_code(session->ti.libssh.session), ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100101 session->status = NC_STATUS_INVALID;
102 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200103 return -1;
104 } else if (r == 0) {
105 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko428087d2016-01-14 16:04:28 +0100106 ERR("%s: session %u: communication socket unexpectedly closed (libssh).", __func__, session->id);
107 session->status = NC_STATUS_INVALID;
108 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200109 return -1;
110 }
Michal Vasko428087d2016-01-14 16:04:28 +0100111 usleep(NC_READ_SLEEP);
Radek Krejci206fcd62015-10-07 15:42:48 +0200112 continue;
113 }
114
115 size = size + r;
116 count = count - r;
117 }
118 break;
119#endif
120
121#ifdef ENABLE_TLS
122 case NC_TI_OPENSSL:
123 /* read via OpenSSL */
Michal Vasko428087d2016-01-14 16:04:28 +0100124 while (count) {
Radek Krejcid0046592015-10-08 12:52:02 +0200125 r = SSL_read(session->ti.tls, &(buf[size]), count);
126 if (r <= 0) {
127 int x;
128 switch (x = SSL_get_error(session->ti.tls, r)) {
129 case SSL_ERROR_WANT_READ:
130 usleep(NC_READ_SLEEP);
131 continue;
132 case SSL_ERROR_ZERO_RETURN:
Michal Vasko428087d2016-01-14 16:04:28 +0100133 ERR("%s: session %u: communication socket unexpectedly closed (OpenSSL).", __func__, session->id);
134 session->status = NC_STATUS_INVALID;
135 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200136 return -1;
137 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100138 ERR("%s: session %u: reading from the TLS session failed (SSL code %d).", __func__, session->id, x);
139 session->status = NC_STATUS_INVALID;
140 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200141 return -1;
142 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200143 }
Radek Krejcid0046592015-10-08 12:52:02 +0200144 size = size + r;
145 count = count - r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200146 }
147 break;
148#endif
149 }
150
151 return (ssize_t)size;
152}
153
154static ssize_t
155nc_read_chunk(struct nc_session *session, size_t len, char **chunk)
156{
157 ssize_t r;
158
159 assert(session);
160 assert(chunk);
161
162 if (!len) {
163 return 0;
164 }
165
166 *chunk = malloc ((len + 1) * sizeof **chunk);
167 if (!*chunk) {
168 ERRMEM;
169 return -1;
170 }
171
172 r = nc_read(session, *chunk, len);
173 if (r <= 0) {
174 free(*chunk);
175 return -1;
176 }
177
178 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200179 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200180
181 return r;
182}
183
184static ssize_t
185nc_read_until(struct nc_session *session, const char *endtag, size_t limit, char **result)
186{
187 char *chunk = NULL;
188 size_t size, count = 0, r, len;
189
190 assert(session);
191 assert(endtag);
192
193 if (limit && limit < BUFFERSIZE) {
194 size = limit;
195 } else {
196 size = BUFFERSIZE;
197 }
198 chunk = malloc ((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200199 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200200 ERRMEM;
201 return -1;
202 }
203
204 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100205 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200206 if (limit && count == limit) {
207 free(chunk);
Michal Vasko428087d2016-01-14 16:04:28 +0100208 WRN("%s: session %u: reading limit (%d) reached.", __func__, session->id, limit);
209 ERR("%s: session %u: invalid input data (missing \"%s\" sequence).", __func__, session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200210 return -1;
211 }
212
213 /* resize buffer if needed */
214 if (count == size) {
215 /* get more memory */
216 size = size + BUFFERSIZE;
217 char *tmp = realloc (chunk, (size + 1) * sizeof *tmp);
218 if (!tmp) {
219 ERRMEM;
220 free(chunk);
221 return -1;
222 }
223 chunk = tmp;
224 }
225
226 /* get another character */
227 r = nc_read(session, &(chunk[count]), 1);
228 if (r != 1) {
229 free(chunk);
230 return -1;
231 }
232
233 count++;
234
235 /* check endtag */
236 if (count >= len) {
237 if (!strncmp(endtag, &(chunk[count - len]), len)) {
238 /* endtag found */
239 break;
240 }
241 }
242 }
243
244 /* terminating null byte */
245 chunk[count] = 0;
246
247 if (result) {
248 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200249 } else {
250 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200251 }
252 return count;
253}
254
Michal Vasko428087d2016-01-14 16:04:28 +0100255/* return NC_MSG_ERROR can change session status */
Radek Krejci206fcd62015-10-07 15:42:48 +0200256NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100257nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
258{
259 int ret;
260 char *msg = NULL, *chunk, *aux;
261 uint64_t chunk_len, len = 0;
262
Michal Vasko428087d2016-01-14 16:04:28 +0100263 assert(session && data);
264 *data = NULL;
265
266 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
267 ERR("%s: session %u: invalid session to read from to.", __func__, session->id);
268 return NC_MSG_ERROR;
269 }
270
Michal Vasko05ba9df2016-01-13 14:40:27 +0100271 /* read the message */
272 switch (session->version) {
273 case NC_VERSION_10:
274 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &msg);
275 if (ret == -1) {
276 goto error;
277 }
278
279 /* cut off the end tag */
280 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
281 break;
282 case NC_VERSION_11:
283 while (1) {
284 ret = nc_read_until(session, "\n#", 0, NULL);
285 if (ret == -1) {
286 goto error;
287 }
288 ret = nc_read_until(session, "\n", 0, &chunk);
289 if (ret == -1) {
290 goto error;
291 }
292
293 if (!strcmp(chunk, "#\n")) {
294 /* end of chunked framing message */
295 free(chunk);
296 break;
297 }
298
299 /* convert string to the size of the following chunk */
300 chunk_len = strtoul(chunk, (char **)NULL, 10);
301 free(chunk);
302 if (!chunk_len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100303 ERR("%s: session %u: invalid frame chunk size detected, fatal error.", __func__, session->id);
304 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100305 }
306
307 /* now we have size of next chunk, so read the chunk */
308 ret = nc_read_chunk(session, chunk_len, &chunk);
309 if (ret == -1) {
310 goto error;
311 }
312
313 /* realloc message buffer, remember to count terminating null byte */
314 aux = realloc(msg, len + chunk_len + 1);
315 if (!aux) {
316 ERRMEM;
317 goto error;
318 }
319 msg = aux;
320 memcpy(msg + len, chunk, chunk_len);
321 len += chunk_len;
322 msg[len] = '\0';
323 free(chunk);
324 }
325
326 break;
327 }
Michal Vasko428087d2016-01-14 16:04:28 +0100328 DBG("%s: session %u: received message:\n%s", __func__, session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100329
330 /* build XML tree */
331 *data = lyxml_read_data(session->ctx, msg, 0);
332 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100333 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100334 } else if (!(*data)->ns) {
Michal Vasko428087d2016-01-14 16:04:28 +0100335 ERR("%s: session %u: invalid message root element (invalid namespace).", __func__, session->id);
336 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100337 }
338 free(msg);
339 msg = NULL;
340
341 /* get and return message type */
342 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
343 if (!strcmp((*data)->name, "rpc")) {
344 return NC_MSG_RPC;
345 } else if (!strcmp((*data)->name, "rpc-reply")) {
346 return NC_MSG_REPLY;
347 } else if (!strcmp((*data)->name, "hello")) {
348 return NC_MSG_HELLO;
349 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100350 ERR("%s: session %u: invalid message root element (invalid name \"%s\").", __func__, session->id, (*data)->name);
351 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100352 }
353 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
354 if (!strcmp((*data)->name, "notification")) {
355 return NC_MSG_NOTIF;
356 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100357 ERR("%s: session %u: invalid message root element (invalid name \"%s\").", __func__, session->id, (*data)->name);
358 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100359 }
360 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100361 ERR("%s: session %u: invalid message root element (invalid namespace \"%s\").", __func__, session->id, (*data)->ns->value);
362 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100363 }
364
Michal Vasko428087d2016-01-14 16:04:28 +0100365malformed_msg:
366 ERR("%s: session %u: malformed message received.", __func__, session->id);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100367 if (session->side == NC_SERVER && session->version == NC_VERSION_11) {
368 /* NETCONF version 1.1 define sending error reply from the server */
Michal Vasko428087d2016-01-14 16:04:28 +0100369 /* TODO what about message id?
370
371 RFC 6241 text
372 If a peer receives an <rpc> message that is not well-
373 formed XML or not encoded in UTF-8, it SHOULD reply with a
374 "malformed-message" error. If a reply cannot be sent for any reason,
375 the server MUST terminate the session.
376
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377 reply = nc_reply_error(nc_err_new(NC_ERR_MALFORMED_MSG));
378 if (reply == NULL) {
379 ERROR("Unable to create the \'Malformed message\' reply");
380 nc_session_close(session, NC_SESSION_TERM_OTHER);
381 return (NC_MSG_UNKNOWN);
382 }
383
384 if (nc_session_send_reply(session, NULL, reply) == 0) {
385 ERROR("Unable to send the \'Malformed message\' reply");
386 nc_session_close(session, NC_SESSION_TERM_OTHER);
387 return (NC_MSG_UNKNOWN);
388 }
389 nc_reply_free(reply);
390 */
391 }
392
Michal Vasko428087d2016-01-14 16:04:28 +0100393error:
394 /* cleanup */
395 free(msg);
396 free(*data);
397 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100398
399 return NC_MSG_ERROR;
400}
401
Michal Vasko428087d2016-01-14 16:04:28 +0100402/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
403static int
404nc_read_poll(struct nc_session *session, int timeout)
405{
406 int ret = -2;
407 struct pollfd fds;
408 struct timespec old_ts, new_ts;
409
410 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
411 ERR("%s: session %u: invalid session to poll.", __func__, session->id);
412 return -1;
413 }
414
415 switch (session->ti_type) {
416#ifdef ENABLE_SSH
417 case NC_TI_LIBSSH:
418 /* EINTR is handled, it resumes waiting */
419 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
420 if (ret == SSH_ERROR) {
421 ERR("%s: session %u: SSH channel error (%s).", __func__, session->id, ssh_get_error(session->ti.libssh.session));
422 session->status = NC_STATUS_INVALID;
423 session->term_reason = NC_SESSION_TERM_OTHER;
424 return -1;
425 } else if (ret == SSH_EOF) {
426 ERR("%s: session %u: communication channel unexpectedly closed (libssh).", __func__, session->id);
427 session->status = NC_STATUS_INVALID;
428 session->term_reason = NC_SESSION_TERM_DROPPED;
429 return -1;
430 } else if (ret > 0) {
431 /* fake it */
432 ret = 1;
433 fds.revents = POLLIN;
434 }
435 /* fallthrough */
436#endif
437#ifdef ENABLE_TLS
438 case NC_TI_OPENSSL:
439 if (session->ti_type == NC_TI_OPENSSL) {
440 fds.fd = SSL_get_fd(session->ti.tls);
441 }
442 /* fallthrough */
443#endif
444 case NC_TI_FD:
445 if (session->ti_type == NC_TI_FD) {
446 fds.fd = session->ti.fd.in;
447 }
448
449 /* poll only if it is not an SSH session */
450 if (ret == -2) {
451 fds.events = POLLIN;
452
453 errno = 0;
454 while (((ret = poll(&fds, 1, timeout)) == -1) && (errno == EINTR)) {
455 /* poll was interrupted */
456 if (timeout > 0) {
457 clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
458
459 timeout -= (new_ts.tv_sec - old_ts.tv_sec) * 1000;
460 timeout -= (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
461 if (timeout < 0) {
462 ERRINT;
463 return -1;
464 }
465 old_ts = new_ts;
466 }
467 }
468 }
469
470 break;
471
472 default:
473 ERRINT;
474 return -1;
475 }
476
477 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
478 if (ret < 0) {
479 /* poll failed - something really bad happened, close the session */
480 ERR("%s: session %u: poll error (%s).", __func__, session->id, strerror(errno));
481 session->status = NC_STATUS_INVALID;
482 session->term_reason = NC_SESSION_TERM_OTHER;
483 return -1;
484 } else { /* status > 0 */
485 /* in case of standard (non-libssh) poll, there still can be an error */
486 if (fds.revents & POLLHUP) {
487 ERR("%s: session %u: communication channel unexpectedly closed.", __func__, session->id);
488 session->status = NC_STATUS_INVALID;
489 session->term_reason = NC_SESSION_TERM_DROPPED;
490 return -1;
491 }
492 if (fds.revents & POLLERR) {
493 ERR("%s: session %u: communication channel error.", __func__, session->id);
494 session->status = NC_STATUS_INVALID;
495 session->term_reason = NC_SESSION_TERM_OTHER;
496 return -1;
497 }
498 }
499
500 return ret;
501}
502
503/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100504NC_MSG_TYPE
505nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200506{
Michal Vasko428087d2016-01-14 16:04:28 +0100507 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200508
509 assert(data);
510 *data = NULL;
511
Michal Vasko428087d2016-01-14 16:04:28 +0100512 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
513 ERR("%s: session %u: invalid session to read from.", __func__, session->id);
514 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200515 }
516
Michal Vasko428087d2016-01-14 16:04:28 +0100517 ret = nc_read_poll(session, timeout);
518 if (ret == 0) {
519 /* timed out */
520 return NC_MSG_WOULDBLOCK;
521 } else if (ret < 0) {
522 /* poll error, error written */
523 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200524 }
525
Michal Vasko05ba9df2016-01-13 14:40:27 +0100526 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200527}
Radek Krejcife0b3472015-10-12 13:43:42 +0200528
Michal Vasko428087d2016-01-14 16:04:28 +0100529/* does not really log, only fatal errors */
530int
531nc_session_is_connected(struct nc_session *session)
532{
533 int ret;
534 struct pollfd fds;
535
536 switch (session->ti_type) {
537 case NC_TI_FD:
538 fds.fd = session->ti.fd.in;
539 break;
540#ifdef ENABLE_SSH
541 case NC_TI_LIBSSH:
542 fds.fd = ssh_get_fd(session->ti.libssh.session);
543 break;
544#endif
545#ifdef ENABLE_TLS
546 case NC_TI_OPENSSL:
547 fds.fd = SSL_get_fd(session->ti.tls);
548 break;
549#endif
550 case NC_TI_NONE:
551 ERRINT;
552 return 0;
553 }
554
555 fds.events = POLLIN;
556
557 errno = 0;
558 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
559
560 if (ret == -1) {
561 ERR("%s: session %u: poll failed (%s).", __func__, session->id, strerror(errno));
562 return 0;
563 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
564 return 0;
565 }
566
567 return 1;
568}
569
Radek Krejcife0b3472015-10-12 13:43:42 +0200570#define WRITE_BUFSIZE (2 * BUFFERSIZE)
571struct wclb_arg {
572 struct nc_session *session;
573 char buf[WRITE_BUFSIZE];
574 size_t len;
575};
576
577static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100578nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200579{
Michal Vasko428087d2016-01-14 16:04:28 +0100580 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
581 return -1;
582 }
583
584 /* prevent SIGPIPE this way */
585 if (!nc_session_is_connected(session)) {
586 return -1;
587 }
588
Radek Krejcife0b3472015-10-12 13:43:42 +0200589 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100590 case NC_TI_NONE:
591 return -1;
592
Radek Krejcife0b3472015-10-12 13:43:42 +0200593 case NC_TI_FD:
Michal Vasko086311b2016-01-08 09:53:11 +0100594 return write(session->ti.fd.out, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200595
Michal Vaskofb2fb762015-10-27 11:44:32 +0100596#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200597 case NC_TI_LIBSSH:
Michal Vasko086311b2016-01-08 09:53:11 +0100598 return ssh_channel_write(session->ti.libssh.channel, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200599#endif
Radek Krejcife0b3472015-10-12 13:43:42 +0200600#ifdef ENABLE_TLS
601 case NC_TI_OPENSSL:
Michal Vasko086311b2016-01-08 09:53:11 +0100602 return SSL_write(session->ti.tls, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200603#endif
604 }
605
606 return -1;
607}
608
Michal Vasko428087d2016-01-14 16:04:28 +0100609static int
610nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100611{
Michal Vasko428087d2016-01-14 16:04:28 +0100612 int ret, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100613 char chunksize[20];
614
615 if (session->version == NC_VERSION_11) {
616 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100617 ret = nc_write(session, chunksize, strlen(chunksize));
618 if (ret == -1) {
619 return -1;
620 }
Michal Vasko086311b2016-01-08 09:53:11 +0100621 }
Michal Vasko428087d2016-01-14 16:04:28 +0100622
623 c = nc_write(session, buf, count);
624 if (c == -1) {
625 return -1;
626 }
627 ret += c;
628
629 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100630}
631
Radek Krejcife0b3472015-10-12 13:43:42 +0200632static int
Michal Vasko428087d2016-01-14 16:04:28 +0100633nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200634{
Michal Vasko428087d2016-01-14 16:04:28 +0100635 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100636
Michal Vasko428087d2016-01-14 16:04:28 +0100637 if (session->version == NC_VERSION_11) {
638 ret = nc_write(session, "\n##\n", 4);
639 } else {
640 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200641 }
642
Michal Vasko428087d2016-01-14 16:04:28 +0100643 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200644}
645
Michal Vasko428087d2016-01-14 16:04:28 +0100646static int
647nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200648{
Michal Vasko428087d2016-01-14 16:04:28 +0100649 int ret = 0;
650
Radek Krejcife0b3472015-10-12 13:43:42 +0200651 /* flush current buffer */
652 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100653 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200654 warg->len = 0;
655 }
Michal Vasko428087d2016-01-14 16:04:28 +0100656
657 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200658}
659
660static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100661nc_write_clb(void *arg, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200662{
Michal Vasko428087d2016-01-14 16:04:28 +0100663 int ret = 0, c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200664 struct wclb_arg *warg = (struct wclb_arg *)arg;
665
666 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100667 c = nc_write_clb_flush(warg);
668 if (c == -1) {
669 return -1;
670 }
671 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200672
673 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100674 c = nc_write_endtag(warg->session);
675 if (c == -1) {
676 return -1;
677 }
678 ret += c;
679
680 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200681 }
682
683 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
684 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100685 c = nc_write_clb_flush(warg);
686 if (c == -1) {
687 return -1;
688 }
689 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200690 }
Michal Vasko428087d2016-01-14 16:04:28 +0100691
Radek Krejcife0b3472015-10-12 13:43:42 +0200692 if (count > WRITE_BUFSIZE) {
693 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100694 c = nc_write_starttag_and_msg(warg->session, buf, count);
695 if (c == -1) {
696 return -1;
697 }
698 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200699 } else {
700 /* keep in buffer and write later */
701 memcpy(&warg->buf[warg->len], buf, count);
702 warg->len += count; /* is <= WRITE_BUFSIZE */
Michal Vasko428087d2016-01-14 16:04:28 +0100703 ret += count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200704 }
705
Michal Vasko428087d2016-01-14 16:04:28 +0100706 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200707}
708
Michal Vasko05ba9df2016-01-13 14:40:27 +0100709static void
Michal Vasko428087d2016-01-14 16:04:28 +0100710nc_write_error(struct wclb_arg *arg, struct nc_server_error *err)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100711{
712 uint16_t i;
713 char str_sid[11];
714
Michal Vasko428087d2016-01-14 16:04:28 +0100715 nc_write_clb((void *)arg, "<rpc-error>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100716
Michal Vasko428087d2016-01-14 16:04:28 +0100717 nc_write_clb((void *)arg, "<error-type>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100718 switch (err->type) {
719 case NC_ERR_TYPE_TRAN:
Michal Vasko428087d2016-01-14 16:04:28 +0100720 nc_write_clb((void *)arg, "transport", 9);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100721 break;
722 case NC_ERR_TYPE_RPC:
Michal Vasko428087d2016-01-14 16:04:28 +0100723 nc_write_clb((void *)arg, "rpc", 3);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100724 break;
725 case NC_ERR_TYPE_PROT:
Michal Vasko428087d2016-01-14 16:04:28 +0100726 nc_write_clb((void *)arg, "protocol", 8);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100727 break;
728 case NC_ERR_TYPE_APP:
Michal Vasko428087d2016-01-14 16:04:28 +0100729 nc_write_clb((void *)arg, "application", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100730 break;
731 default:
732 ERRINT;
733 return;
734 }
Michal Vasko428087d2016-01-14 16:04:28 +0100735 nc_write_clb((void *)arg, "</error-type>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100736
Michal Vasko428087d2016-01-14 16:04:28 +0100737 nc_write_clb((void *)arg, "<error-tag>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100738 switch (err->tag) {
739 case NC_ERR_IN_USE:
Michal Vasko428087d2016-01-14 16:04:28 +0100740 nc_write_clb((void *)arg, "in-use", 6);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100741 break;
742 case NC_ERR_INVALID_VALUE:
Michal Vasko428087d2016-01-14 16:04:28 +0100743 nc_write_clb((void *)arg, "invalid-value", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100744 break;
745 case NC_ERR_TOO_BIG:
Michal Vasko428087d2016-01-14 16:04:28 +0100746 nc_write_clb((void *)arg, "too-big", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100747 break;
748 case NC_ERR_MISSING_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100749 nc_write_clb((void *)arg, "missing-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100750 break;
751 case NC_ERR_BAD_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100752 nc_write_clb((void *)arg, "bad-attribute", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100753 break;
754 case NC_ERR_UNKNOWN_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100755 nc_write_clb((void *)arg, "unknown-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100756 break;
757 case NC_ERR_MISSING_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100758 nc_write_clb((void *)arg, "missing-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100759 break;
760 case NC_ERR_BAD_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100761 nc_write_clb((void *)arg, "bad-element", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100762 break;
763 case NC_ERR_UNKNOWN_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100764 nc_write_clb((void *)arg, "unknown-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100765 break;
766 case NC_ERR_UNKNOWN_NS:
Michal Vasko428087d2016-01-14 16:04:28 +0100767 nc_write_clb((void *)arg, "unknown-namespace", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100768 break;
769 case NC_ERR_ACCESS_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100770 nc_write_clb((void *)arg, "access-denied", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100771 break;
772 case NC_ERR_LOCK_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100773 nc_write_clb((void *)arg, "lock-denied", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100774 break;
775 case NC_ERR_RES_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100776 nc_write_clb((void *)arg, "resource-denied", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100777 break;
778 case NC_ERR_ROLLBACK_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100779 nc_write_clb((void *)arg, "rollback-failed", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100780 break;
781 case NC_ERR_DATA_EXISTS:
Michal Vasko428087d2016-01-14 16:04:28 +0100782 nc_write_clb((void *)arg, "data-exists", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100783 break;
784 case NC_ERR_DATA_MISSING:
Michal Vasko428087d2016-01-14 16:04:28 +0100785 nc_write_clb((void *)arg, "data-missing", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100786 break;
787 case NC_ERR_OP_NOT_SUPPORTED:
Michal Vasko428087d2016-01-14 16:04:28 +0100788 nc_write_clb((void *)arg, "operation-not-supported", 23);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100789 break;
790 case NC_ERR_OP_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100791 nc_write_clb((void *)arg, "operation-failed", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100792 break;
793 case NC_ERR_MALFORMED_MSG:
Michal Vasko428087d2016-01-14 16:04:28 +0100794 nc_write_clb((void *)arg, "malformed-message", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100795 break;
796 default:
797 ERRINT;
798 return;
799 }
Michal Vasko428087d2016-01-14 16:04:28 +0100800 nc_write_clb((void *)arg, "</error-tag>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100801
Michal Vasko428087d2016-01-14 16:04:28 +0100802 nc_write_clb((void *)arg, "<error-severity>error</error-severity>", 38);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100803
804 if (err->apptag) {
Michal Vasko428087d2016-01-14 16:04:28 +0100805 nc_write_clb((void *)arg, "<error-app-tag>", 15);
806 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag));
807 nc_write_clb((void *)arg, "</error-app-tag>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100808 }
809
810 if (err->path) {
Michal Vasko428087d2016-01-14 16:04:28 +0100811 nc_write_clb((void *)arg, "<error-path>", 12);
812 nc_write_clb((void *)arg, err->path, strlen(err->path));
813 nc_write_clb((void *)arg, "</error-path>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100814 }
815
816 if (err->message) {
Michal Vasko428087d2016-01-14 16:04:28 +0100817 nc_write_clb((void *)arg, "<error-message", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100818 if (err->message_lang) {
Michal Vasko428087d2016-01-14 16:04:28 +0100819 nc_write_clb((void *)arg, " xml:lang=\"", 11);
820 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang));
821 nc_write_clb((void *)arg, "\"", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100822 }
Michal Vasko428087d2016-01-14 16:04:28 +0100823 nc_write_clb((void *)arg, ">", 1);
824 nc_write_clb((void *)arg, err->message, strlen(err->message));
825 nc_write_clb((void *)arg, "</error-message>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100826 }
827
828 if (err->sid || err->attr || err->elem || err->ns || err->other) {
Michal Vasko428087d2016-01-14 16:04:28 +0100829 nc_write_clb((void *)arg, "<error-info>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100830
831 if (err->sid) {
Michal Vasko428087d2016-01-14 16:04:28 +0100832 nc_write_clb((void *)arg, "<session-id>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100833 sprintf(str_sid, "%u", err->sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100834 nc_write_clb((void *)arg, str_sid, strlen(str_sid));
835 nc_write_clb((void *)arg, "</session-id>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100836 }
837
838 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100839 nc_write_clb((void *)arg, "<bad-attribute>", 15);
840 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]));
841 nc_write_clb((void *)arg, "</bad-attribute>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100842 }
843
844 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100845 nc_write_clb((void *)arg, "<bad-element>", 13);
846 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]));
847 nc_write_clb((void *)arg, "</bad-element>", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100848 }
849
850 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100851 nc_write_clb((void *)arg, "<bad-namespace>", 15);
852 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]));
853 nc_write_clb((void *)arg, "</bad-namespace>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100854 }
855
856 for (i = 0; i < err->other_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100857 lyxml_dump_clb(nc_write_clb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100858 }
859
Michal Vasko428087d2016-01-14 16:04:28 +0100860 nc_write_clb((void *)arg, "</error-info>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100861 }
862
Michal Vasko428087d2016-01-14 16:04:28 +0100863 nc_write_clb((void *)arg, "</rpc-error>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100864}
865
Michal Vasko428087d2016-01-14 16:04:28 +0100866/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100867int
868nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200869{
Radek Krejcid116db42016-01-08 15:36:30 +0100870 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100871 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200872 const char *attrs;
873 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100874 struct lyxml_elem *rpc_elem;
875 struct nc_server_reply *reply;
876 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100877 char *buf = NULL;
878 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200879 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100880 uint32_t *sid = NULL, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200881
Michal Vasko428087d2016-01-14 16:04:28 +0100882 assert(session);
883
884 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
885 ERR("%s: session %u: invalid session to write to.", __func__, session->id);
886 return -1;
887 }
888
Radek Krejcid116db42016-01-08 15:36:30 +0100889 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200890
891 arg.session = session;
892 arg.len = 0;
893
894 switch (type) {
895 case NC_MSG_RPC:
896 content = va_arg(ap, struct lyd_node *);
897 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100898
Radek Krejcife0b3472015-10-12 13:43:42 +0200899 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
900 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
Michal Vasko428087d2016-01-14 16:04:28 +0100901 nc_write_clb((void *)&arg, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200902 free(buf);
Michal Vasko428087d2016-01-14 16:04:28 +0100903 lyd_print_clb(nc_write_clb, (void *)&arg, content, LYD_XML);
904 nc_write_clb((void *)&arg, "</rpc>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200905
906 session->msgid++;
907 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908
Radek Krejcife0b3472015-10-12 13:43:42 +0200909 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +0100910 rpc_elem = va_arg(ap, struct lyxml_elem *);
911 reply = va_arg(ap, struct nc_server_reply *);
912
Michal Vasko428087d2016-01-14 16:04:28 +0100913 nc_write_clb((void *)&arg, "<rpc-reply", 10);
914 lyxml_dump_clb(nc_write_clb, (void *)&arg, rpc_elem, LYXML_DUMP_ATTRS);
915 nc_write_clb((void *)&arg, ">", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100916 switch (reply->type) {
917 case NC_RPL_OK:
Michal Vasko428087d2016-01-14 16:04:28 +0100918 nc_write_clb((void *)&arg, "<ok/>", 5);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100919 break;
920 case NC_RPL_DATA:
Michal Vasko428087d2016-01-14 16:04:28 +0100921 nc_write_clb((void *)&arg, "<data>", 6);
922 lyd_print_clb(nc_write_clb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML);
923 nc_write_clb((void *)&arg, "<data/>", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100924 break;
925 case NC_RPL_ERROR:
926 error_rpl = (struct nc_server_reply_error *)reply;
927 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100928 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100929 }
930 break;
931 default:
932 ERRINT;
Michal Vasko428087d2016-01-14 16:04:28 +0100933 nc_write_clb((void *)&arg, NULL, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100934 va_end(ap);
935 return -1;
936 }
Michal Vasko428087d2016-01-14 16:04:28 +0100937 nc_write_clb((void *)&arg, "</rpc-reply>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200938 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100939
Radek Krejcife0b3472015-10-12 13:43:42 +0200940 case NC_MSG_NOTIF:
Michal Vasko428087d2016-01-14 16:04:28 +0100941 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
Radek Krejcife0b3472015-10-12 13:43:42 +0200942 /* TODO content */
Michal Vasko428087d2016-01-14 16:04:28 +0100943 nc_write_clb((void *)&arg, "</notification>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200944 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100945
Radek Krejcid116db42016-01-08 15:36:30 +0100946 case NC_MSG_HELLO:
947 if (session->version != NC_VERSION_10) {
948 va_end(ap);
949 return -1;
950 }
951 capabilities = va_arg(ap, const char **);
952 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100953
Radek Krejcid116db42016-01-08 15:36:30 +0100954 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko428087d2016-01-14 16:04:28 +0100955 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100956 free(buf);
957 for (i = 0; capabilities[i]; i++) {
958 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
Michal Vasko428087d2016-01-14 16:04:28 +0100959 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100960 free(buf);
961 }
962 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100963 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100964 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100965 free(buf);
966 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100967 nc_write_clb((void *)&arg, "</capabilities></hello>", 23);
Radek Krejcid116db42016-01-08 15:36:30 +0100968 }
Radek Krejcid116db42016-01-08 15:36:30 +0100969 break;
Michal Vaskoed462342016-01-12 12:33:48 +0100970
Radek Krejcife0b3472015-10-12 13:43:42 +0200971 default:
Radek Krejcid116db42016-01-08 15:36:30 +0100972 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +0200973 return -1;
974 }
975
976 /* flush message */
Michal Vasko428087d2016-01-14 16:04:28 +0100977 nc_write_clb((void *)&arg, NULL, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200978
979 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +0100980 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
981 /* error was already written */
982 return -1;
983 }
984
Radek Krejcid116db42016-01-08 15:36:30 +0100985 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200986}