blob: c2996afb800c23e673d79ed9f7ac72c0b6ed47e3 [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
Radek Krejci206fcd62015-10-07 15:42:48 +020034#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020035
Radek Krejcife0b3472015-10-12 13:43:42 +020036#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020037
38static ssize_t
39nc_read(struct nc_session *session, char *buf, size_t count)
40{
41 size_t size = 0;
42 ssize_t r;
43
44 assert(session);
45 assert(buf);
46
Michal Vasko428087d2016-01-14 16:04:28 +010047 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
48 return -1;
49 }
50
Radek Krejci206fcd62015-10-07 15:42:48 +020051 if (!count) {
52 return 0;
53 }
54
Michal Vasko38a7c6c2015-12-04 12:29:20 +010055 switch (session->ti_type) {
56 case NC_TI_NONE:
57 return 0;
58
Radek Krejci206fcd62015-10-07 15:42:48 +020059 case NC_TI_FD:
60 /* read via standard file descriptor */
Michal Vasko428087d2016-01-14 16:04:28 +010061 while (count) {
Radek Krejci206fcd62015-10-07 15:42:48 +020062 r = read(session->ti.fd.in, &(buf[size]), count);
63 if (r < 0) {
Michal Vasko428087d2016-01-14 16:04:28 +010064 if ((errno == EAGAIN) || (errno == EINTR)) {
Radek Krejci206fcd62015-10-07 15:42:48 +020065 usleep(NC_READ_SLEEP);
66 continue;
67 } else {
Michal Vaskod083db62016-01-19 10:31:29 +010068 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
69 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +010070 session->status = NC_STATUS_INVALID;
71 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020072 return -1;
73 }
74 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +010075 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
76 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +010077 session->status = NC_STATUS_INVALID;
78 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +020079 return -1;
80 }
81
82 size = size + r;
83 count = count - r;
84 }
85 break;
86
Michal Vaskofb2fb762015-10-27 11:44:32 +010087#ifdef ENABLE_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020088 case NC_TI_LIBSSH:
89 /* read via libssh */
Michal Vasko428087d2016-01-14 16:04:28 +010090 while (count) {
Radek Krejci206fcd62015-10-07 15:42:48 +020091 r = ssh_channel_read(session->ti.libssh.channel, &(buf[size]), count, 0);
92 if (r == SSH_AGAIN) {
Michal Vasko428087d2016-01-14 16:04:28 +010093 usleep(NC_READ_SLEEP);
Radek Krejci206fcd62015-10-07 15:42:48 +020094 continue;
95 } else if (r == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +010096 ERR("Session %u: reading from the SSH channel failed (%zd: %s).", session->id,
Radek Krejci206fcd62015-10-07 15:42:48 +020097 ssh_get_error_code(session->ti.libssh.session), ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +010098 session->status = NC_STATUS_INVALID;
99 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200100 return -1;
101 } else if (r == 0) {
102 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100103 ERR("Session %u: communication channel unexpectedly closed (libssh).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100104 session->status = NC_STATUS_INVALID;
105 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200106 return -1;
107 }
Michal Vasko428087d2016-01-14 16:04:28 +0100108 usleep(NC_READ_SLEEP);
Radek Krejci206fcd62015-10-07 15:42:48 +0200109 continue;
110 }
111
112 size = size + r;
113 count = count - r;
114 }
115 break;
116#endif
117
118#ifdef ENABLE_TLS
119 case NC_TI_OPENSSL:
120 /* read via OpenSSL */
Michal Vasko428087d2016-01-14 16:04:28 +0100121 while (count) {
Radek Krejcid0046592015-10-08 12:52:02 +0200122 r = SSL_read(session->ti.tls, &(buf[size]), count);
123 if (r <= 0) {
124 int x;
125 switch (x = SSL_get_error(session->ti.tls, r)) {
126 case SSL_ERROR_WANT_READ:
127 usleep(NC_READ_SLEEP);
128 continue;
129 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100130 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100131 session->status = NC_STATUS_INVALID;
132 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200133 return -1;
134 default:
Michal Vaskod083db62016-01-19 10:31:29 +0100135 ERR("Session %u: reading from the TLS session failed (SSL code %d).", session->id, x);
Michal Vasko428087d2016-01-14 16:04:28 +0100136 session->status = NC_STATUS_INVALID;
137 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200138 return -1;
139 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200140 }
Radek Krejcid0046592015-10-08 12:52:02 +0200141 size = size + r;
142 count = count - r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200143 }
144 break;
145#endif
146 }
147
148 return (ssize_t)size;
149}
150
151static ssize_t
152nc_read_chunk(struct nc_session *session, size_t len, char **chunk)
153{
154 ssize_t r;
155
156 assert(session);
157 assert(chunk);
158
159 if (!len) {
160 return 0;
161 }
162
163 *chunk = malloc ((len + 1) * sizeof **chunk);
164 if (!*chunk) {
165 ERRMEM;
166 return -1;
167 }
168
169 r = nc_read(session, *chunk, len);
170 if (r <= 0) {
171 free(*chunk);
172 return -1;
173 }
174
175 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200176 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200177
178 return r;
179}
180
181static ssize_t
182nc_read_until(struct nc_session *session, const char *endtag, size_t limit, char **result)
183{
184 char *chunk = NULL;
185 size_t size, count = 0, r, len;
186
187 assert(session);
188 assert(endtag);
189
190 if (limit && limit < BUFFERSIZE) {
191 size = limit;
192 } else {
193 size = BUFFERSIZE;
194 }
195 chunk = malloc ((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200196 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200197 ERRMEM;
198 return -1;
199 }
200
201 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100202 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200203 if (limit && count == limit) {
204 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100205 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
206 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200207 return -1;
208 }
209
210 /* resize buffer if needed */
211 if (count == size) {
212 /* get more memory */
213 size = size + BUFFERSIZE;
214 char *tmp = realloc (chunk, (size + 1) * sizeof *tmp);
215 if (!tmp) {
216 ERRMEM;
217 free(chunk);
218 return -1;
219 }
220 chunk = tmp;
221 }
222
223 /* get another character */
224 r = nc_read(session, &(chunk[count]), 1);
225 if (r != 1) {
226 free(chunk);
227 return -1;
228 }
229
230 count++;
231
232 /* check endtag */
233 if (count >= len) {
234 if (!strncmp(endtag, &(chunk[count - len]), len)) {
235 /* endtag found */
236 break;
237 }
238 }
239 }
240
241 /* terminating null byte */
242 chunk[count] = 0;
243
244 if (result) {
245 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200246 } else {
247 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200248 }
249 return count;
250}
251
Michal Vasko428087d2016-01-14 16:04:28 +0100252/* return NC_MSG_ERROR can change session status */
Radek Krejci206fcd62015-10-07 15:42:48 +0200253NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100254nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
255{
256 int ret;
257 char *msg = NULL, *chunk, *aux;
258 uint64_t chunk_len, len = 0;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100259 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100260
Michal Vasko428087d2016-01-14 16:04:28 +0100261 assert(session && data);
262 *data = NULL;
263
264 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100265 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100266 return NC_MSG_ERROR;
267 }
268
Michal Vasko05ba9df2016-01-13 14:40:27 +0100269 /* read the message */
270 switch (session->version) {
271 case NC_VERSION_10:
272 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &msg);
273 if (ret == -1) {
274 goto error;
275 }
276
277 /* cut off the end tag */
278 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
279 break;
280 case NC_VERSION_11:
281 while (1) {
282 ret = nc_read_until(session, "\n#", 0, NULL);
283 if (ret == -1) {
284 goto error;
285 }
286 ret = nc_read_until(session, "\n", 0, &chunk);
287 if (ret == -1) {
288 goto error;
289 }
290
291 if (!strcmp(chunk, "#\n")) {
292 /* end of chunked framing message */
293 free(chunk);
294 break;
295 }
296
297 /* convert string to the size of the following chunk */
298 chunk_len = strtoul(chunk, (char **)NULL, 10);
299 free(chunk);
300 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100301 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100302 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100303 }
304
305 /* now we have size of next chunk, so read the chunk */
306 ret = nc_read_chunk(session, chunk_len, &chunk);
307 if (ret == -1) {
308 goto error;
309 }
310
311 /* realloc message buffer, remember to count terminating null byte */
312 aux = realloc(msg, len + chunk_len + 1);
313 if (!aux) {
314 ERRMEM;
315 goto error;
316 }
317 msg = aux;
318 memcpy(msg + len, chunk, chunk_len);
319 len += chunk_len;
320 msg[len] = '\0';
321 free(chunk);
322 }
323
324 break;
325 }
Michal Vaskod083db62016-01-19 10:31:29 +0100326 DBG("Session %u: received message:\n%s", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327
328 /* build XML tree */
329 *data = lyxml_read_data(session->ctx, msg, 0);
330 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100331 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100332 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100333 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100334 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100335 }
336 free(msg);
337 msg = NULL;
338
339 /* get and return message type */
340 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
341 if (!strcmp((*data)->name, "rpc")) {
342 return NC_MSG_RPC;
343 } else if (!strcmp((*data)->name, "rpc-reply")) {
344 return NC_MSG_REPLY;
345 } else if (!strcmp((*data)->name, "hello")) {
346 return NC_MSG_HELLO;
347 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100348 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100349 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350 }
351 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
352 if (!strcmp((*data)->name, "notification")) {
353 return NC_MSG_NOTIF;
354 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100355 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100356 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100357 }
358 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100359 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100360 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100361 }
362
Michal Vasko428087d2016-01-14 16:04:28 +0100363malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100364 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100365 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
366 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100367 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100368
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100369 if (nc_write_msg(session, NC_MSG_REPLY, NULL, reply) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100370 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100371 if (session->status != NC_STATUS_INVALID) {
372 session->status = NC_STATUS_INVALID;
373 session->term_reason = NC_SESSION_TERM_OTHER;
374 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100375 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100376 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377 }
378
Michal Vasko428087d2016-01-14 16:04:28 +0100379error:
380 /* cleanup */
381 free(msg);
382 free(*data);
383 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384
385 return NC_MSG_ERROR;
386}
387
Michal Vasko428087d2016-01-14 16:04:28 +0100388/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
389static int
390nc_read_poll(struct nc_session *session, int timeout)
391{
392 int ret = -2;
393 struct pollfd fds;
394 struct timespec old_ts, new_ts;
395
396 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100397 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100398 return -1;
399 }
400
401 switch (session->ti_type) {
402#ifdef ENABLE_SSH
403 case NC_TI_LIBSSH:
404 /* EINTR is handled, it resumes waiting */
405 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
406 if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100407 ERR("Session %u: SSH channel error (%s).", session->id, ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100408 session->status = NC_STATUS_INVALID;
409 session->term_reason = NC_SESSION_TERM_OTHER;
410 return -1;
411 } else if (ret == SSH_EOF) {
Michal Vaskod083db62016-01-19 10:31:29 +0100412 ERR("Session %u: communication channel unexpectedly closed (libssh).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100413 session->status = NC_STATUS_INVALID;
414 session->term_reason = NC_SESSION_TERM_DROPPED;
415 return -1;
416 } else if (ret > 0) {
417 /* fake it */
418 ret = 1;
419 fds.revents = POLLIN;
420 }
421 /* fallthrough */
422#endif
423#ifdef ENABLE_TLS
424 case NC_TI_OPENSSL:
425 if (session->ti_type == NC_TI_OPENSSL) {
426 fds.fd = SSL_get_fd(session->ti.tls);
427 }
428 /* fallthrough */
429#endif
430 case NC_TI_FD:
431 if (session->ti_type == NC_TI_FD) {
432 fds.fd = session->ti.fd.in;
433 }
434
435 /* poll only if it is not an SSH session */
436 if (ret == -2) {
437 fds.events = POLLIN;
438
439 errno = 0;
440 while (((ret = poll(&fds, 1, timeout)) == -1) && (errno == EINTR)) {
441 /* poll was interrupted */
442 if (timeout > 0) {
443 clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
444
445 timeout -= (new_ts.tv_sec - old_ts.tv_sec) * 1000;
446 timeout -= (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
447 if (timeout < 0) {
448 ERRINT;
449 return -1;
450 }
451 old_ts = new_ts;
452 }
453 }
454 }
455
456 break;
457
458 default:
459 ERRINT;
460 return -1;
461 }
462
463 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
464 if (ret < 0) {
465 /* poll failed - something really bad happened, close the session */
Michal Vaskod083db62016-01-19 10:31:29 +0100466 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100467 session->status = NC_STATUS_INVALID;
468 session->term_reason = NC_SESSION_TERM_OTHER;
469 return -1;
470 } else { /* status > 0 */
471 /* in case of standard (non-libssh) poll, there still can be an error */
472 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100473 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100474 session->status = NC_STATUS_INVALID;
475 session->term_reason = NC_SESSION_TERM_DROPPED;
476 return -1;
477 }
478 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100479 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100480 session->status = NC_STATUS_INVALID;
481 session->term_reason = NC_SESSION_TERM_OTHER;
482 return -1;
483 }
484 }
485
486 return ret;
487}
488
489/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100490NC_MSG_TYPE
491nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200492{
Michal Vasko428087d2016-01-14 16:04:28 +0100493 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200494
495 assert(data);
496 *data = NULL;
497
Michal Vasko428087d2016-01-14 16:04:28 +0100498 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100499 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100500 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200501 }
502
Michal Vasko428087d2016-01-14 16:04:28 +0100503 ret = nc_read_poll(session, timeout);
504 if (ret == 0) {
505 /* timed out */
506 return NC_MSG_WOULDBLOCK;
507 } else if (ret < 0) {
508 /* poll error, error written */
509 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200510 }
511
Michal Vasko05ba9df2016-01-13 14:40:27 +0100512 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200513}
Radek Krejcife0b3472015-10-12 13:43:42 +0200514
Michal Vasko428087d2016-01-14 16:04:28 +0100515/* does not really log, only fatal errors */
516int
517nc_session_is_connected(struct nc_session *session)
518{
519 int ret;
520 struct pollfd fds;
521
522 switch (session->ti_type) {
523 case NC_TI_FD:
524 fds.fd = session->ti.fd.in;
525 break;
526#ifdef ENABLE_SSH
527 case NC_TI_LIBSSH:
528 fds.fd = ssh_get_fd(session->ti.libssh.session);
529 break;
530#endif
531#ifdef ENABLE_TLS
532 case NC_TI_OPENSSL:
533 fds.fd = SSL_get_fd(session->ti.tls);
534 break;
535#endif
536 case NC_TI_NONE:
537 ERRINT;
538 return 0;
539 }
540
541 fds.events = POLLIN;
542
543 errno = 0;
544 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
545
546 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100547 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100548 return 0;
549 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
550 return 0;
551 }
552
553 return 1;
554}
555
Radek Krejcife0b3472015-10-12 13:43:42 +0200556#define WRITE_BUFSIZE (2 * BUFFERSIZE)
557struct wclb_arg {
558 struct nc_session *session;
559 char buf[WRITE_BUFSIZE];
560 size_t len;
561};
562
563static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100564nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200565{
Michal Vasko428087d2016-01-14 16:04:28 +0100566 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
567 return -1;
568 }
569
570 /* prevent SIGPIPE this way */
571 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100572 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100573 session->status = NC_STATUS_INVALID;
574 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100575 return -1;
576 }
577
Radek Krejcife0b3472015-10-12 13:43:42 +0200578 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100579 case NC_TI_NONE:
580 return -1;
581
Radek Krejcife0b3472015-10-12 13:43:42 +0200582 case NC_TI_FD:
Michal Vasko086311b2016-01-08 09:53:11 +0100583 return write(session->ti.fd.out, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200584
Michal Vaskofb2fb762015-10-27 11:44:32 +0100585#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200586 case NC_TI_LIBSSH:
Michal Vasko086311b2016-01-08 09:53:11 +0100587 return ssh_channel_write(session->ti.libssh.channel, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200588#endif
Radek Krejcife0b3472015-10-12 13:43:42 +0200589#ifdef ENABLE_TLS
590 case NC_TI_OPENSSL:
Michal Vasko086311b2016-01-08 09:53:11 +0100591 return SSL_write(session->ti.tls, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200592#endif
593 }
594
595 return -1;
596}
597
Michal Vasko428087d2016-01-14 16:04:28 +0100598static int
599nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100600{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100601 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100602 char chunksize[20];
603
604 if (session->version == NC_VERSION_11) {
605 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100606 ret = nc_write(session, chunksize, strlen(chunksize));
607 if (ret == -1) {
608 return -1;
609 }
Michal Vasko086311b2016-01-08 09:53:11 +0100610 }
Michal Vasko428087d2016-01-14 16:04:28 +0100611
612 c = nc_write(session, buf, count);
613 if (c == -1) {
614 return -1;
615 }
616 ret += c;
617
618 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100619}
620
Radek Krejcife0b3472015-10-12 13:43:42 +0200621static int
Michal Vasko428087d2016-01-14 16:04:28 +0100622nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200623{
Michal Vasko428087d2016-01-14 16:04:28 +0100624 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100625
Michal Vasko428087d2016-01-14 16:04:28 +0100626 if (session->version == NC_VERSION_11) {
627 ret = nc_write(session, "\n##\n", 4);
628 } else {
629 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200630 }
631
Michal Vasko428087d2016-01-14 16:04:28 +0100632 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200633}
634
Michal Vasko428087d2016-01-14 16:04:28 +0100635static int
636nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200637{
Michal Vasko428087d2016-01-14 16:04:28 +0100638 int ret = 0;
639
Radek Krejcife0b3472015-10-12 13:43:42 +0200640 /* flush current buffer */
641 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100642 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200643 warg->len = 0;
644 }
Michal Vasko428087d2016-01-14 16:04:28 +0100645
646 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200647}
648
649static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100650nc_write_clb(void *arg, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200651{
Michal Vasko428087d2016-01-14 16:04:28 +0100652 int ret = 0, c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200653 struct wclb_arg *warg = (struct wclb_arg *)arg;
654
655 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100656 c = nc_write_clb_flush(warg);
657 if (c == -1) {
658 return -1;
659 }
660 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200661
662 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100663 c = nc_write_endtag(warg->session);
664 if (c == -1) {
665 return -1;
666 }
667 ret += c;
668
669 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200670 }
671
672 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
673 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100674 c = nc_write_clb_flush(warg);
675 if (c == -1) {
676 return -1;
677 }
678 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200679 }
Michal Vasko428087d2016-01-14 16:04:28 +0100680
Radek Krejcife0b3472015-10-12 13:43:42 +0200681 if (count > WRITE_BUFSIZE) {
682 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100683 c = nc_write_starttag_and_msg(warg->session, buf, count);
684 if (c == -1) {
685 return -1;
686 }
687 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200688 } else {
689 /* keep in buffer and write later */
690 memcpy(&warg->buf[warg->len], buf, count);
691 warg->len += count; /* is <= WRITE_BUFSIZE */
Michal Vasko428087d2016-01-14 16:04:28 +0100692 ret += count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200693 }
694
Michal Vasko428087d2016-01-14 16:04:28 +0100695 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200696}
697
Michal Vasko05ba9df2016-01-13 14:40:27 +0100698static void
Michal Vasko428087d2016-01-14 16:04:28 +0100699nc_write_error(struct wclb_arg *arg, struct nc_server_error *err)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100700{
701 uint16_t i;
702 char str_sid[11];
703
Michal Vasko428087d2016-01-14 16:04:28 +0100704 nc_write_clb((void *)arg, "<rpc-error>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100705
Michal Vasko428087d2016-01-14 16:04:28 +0100706 nc_write_clb((void *)arg, "<error-type>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100707 switch (err->type) {
708 case NC_ERR_TYPE_TRAN:
Michal Vasko428087d2016-01-14 16:04:28 +0100709 nc_write_clb((void *)arg, "transport", 9);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100710 break;
711 case NC_ERR_TYPE_RPC:
Michal Vasko428087d2016-01-14 16:04:28 +0100712 nc_write_clb((void *)arg, "rpc", 3);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100713 break;
714 case NC_ERR_TYPE_PROT:
Michal Vasko428087d2016-01-14 16:04:28 +0100715 nc_write_clb((void *)arg, "protocol", 8);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100716 break;
717 case NC_ERR_TYPE_APP:
Michal Vasko428087d2016-01-14 16:04:28 +0100718 nc_write_clb((void *)arg, "application", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100719 break;
720 default:
721 ERRINT;
722 return;
723 }
Michal Vasko428087d2016-01-14 16:04:28 +0100724 nc_write_clb((void *)arg, "</error-type>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100725
Michal Vasko428087d2016-01-14 16:04:28 +0100726 nc_write_clb((void *)arg, "<error-tag>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100727 switch (err->tag) {
728 case NC_ERR_IN_USE:
Michal Vasko428087d2016-01-14 16:04:28 +0100729 nc_write_clb((void *)arg, "in-use", 6);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100730 break;
731 case NC_ERR_INVALID_VALUE:
Michal Vasko428087d2016-01-14 16:04:28 +0100732 nc_write_clb((void *)arg, "invalid-value", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100733 break;
734 case NC_ERR_TOO_BIG:
Michal Vasko428087d2016-01-14 16:04:28 +0100735 nc_write_clb((void *)arg, "too-big", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100736 break;
737 case NC_ERR_MISSING_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100738 nc_write_clb((void *)arg, "missing-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100739 break;
740 case NC_ERR_BAD_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100741 nc_write_clb((void *)arg, "bad-attribute", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100742 break;
743 case NC_ERR_UNKNOWN_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100744 nc_write_clb((void *)arg, "unknown-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100745 break;
746 case NC_ERR_MISSING_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100747 nc_write_clb((void *)arg, "missing-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100748 break;
749 case NC_ERR_BAD_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100750 nc_write_clb((void *)arg, "bad-element", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100751 break;
752 case NC_ERR_UNKNOWN_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100753 nc_write_clb((void *)arg, "unknown-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100754 break;
755 case NC_ERR_UNKNOWN_NS:
Michal Vasko428087d2016-01-14 16:04:28 +0100756 nc_write_clb((void *)arg, "unknown-namespace", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100757 break;
758 case NC_ERR_ACCESS_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100759 nc_write_clb((void *)arg, "access-denied", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100760 break;
761 case NC_ERR_LOCK_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100762 nc_write_clb((void *)arg, "lock-denied", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100763 break;
764 case NC_ERR_RES_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100765 nc_write_clb((void *)arg, "resource-denied", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100766 break;
767 case NC_ERR_ROLLBACK_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100768 nc_write_clb((void *)arg, "rollback-failed", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100769 break;
770 case NC_ERR_DATA_EXISTS:
Michal Vasko428087d2016-01-14 16:04:28 +0100771 nc_write_clb((void *)arg, "data-exists", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100772 break;
773 case NC_ERR_DATA_MISSING:
Michal Vasko428087d2016-01-14 16:04:28 +0100774 nc_write_clb((void *)arg, "data-missing", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100775 break;
776 case NC_ERR_OP_NOT_SUPPORTED:
Michal Vasko428087d2016-01-14 16:04:28 +0100777 nc_write_clb((void *)arg, "operation-not-supported", 23);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100778 break;
779 case NC_ERR_OP_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100780 nc_write_clb((void *)arg, "operation-failed", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100781 break;
782 case NC_ERR_MALFORMED_MSG:
Michal Vasko428087d2016-01-14 16:04:28 +0100783 nc_write_clb((void *)arg, "malformed-message", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100784 break;
785 default:
786 ERRINT;
787 return;
788 }
Michal Vasko428087d2016-01-14 16:04:28 +0100789 nc_write_clb((void *)arg, "</error-tag>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100790
Michal Vasko428087d2016-01-14 16:04:28 +0100791 nc_write_clb((void *)arg, "<error-severity>error</error-severity>", 38);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100792
793 if (err->apptag) {
Michal Vasko428087d2016-01-14 16:04:28 +0100794 nc_write_clb((void *)arg, "<error-app-tag>", 15);
795 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag));
796 nc_write_clb((void *)arg, "</error-app-tag>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100797 }
798
799 if (err->path) {
Michal Vasko428087d2016-01-14 16:04:28 +0100800 nc_write_clb((void *)arg, "<error-path>", 12);
801 nc_write_clb((void *)arg, err->path, strlen(err->path));
802 nc_write_clb((void *)arg, "</error-path>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100803 }
804
805 if (err->message) {
Michal Vasko428087d2016-01-14 16:04:28 +0100806 nc_write_clb((void *)arg, "<error-message", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100807 if (err->message_lang) {
Michal Vasko428087d2016-01-14 16:04:28 +0100808 nc_write_clb((void *)arg, " xml:lang=\"", 11);
809 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang));
810 nc_write_clb((void *)arg, "\"", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100811 }
Michal Vasko428087d2016-01-14 16:04:28 +0100812 nc_write_clb((void *)arg, ">", 1);
813 nc_write_clb((void *)arg, err->message, strlen(err->message));
814 nc_write_clb((void *)arg, "</error-message>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100815 }
816
817 if (err->sid || err->attr || err->elem || err->ns || err->other) {
Michal Vasko428087d2016-01-14 16:04:28 +0100818 nc_write_clb((void *)arg, "<error-info>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100819
820 if (err->sid) {
Michal Vasko428087d2016-01-14 16:04:28 +0100821 nc_write_clb((void *)arg, "<session-id>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100822 sprintf(str_sid, "%u", err->sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100823 nc_write_clb((void *)arg, str_sid, strlen(str_sid));
824 nc_write_clb((void *)arg, "</session-id>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100825 }
826
827 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100828 nc_write_clb((void *)arg, "<bad-attribute>", 15);
829 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]));
830 nc_write_clb((void *)arg, "</bad-attribute>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100831 }
832
833 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100834 nc_write_clb((void *)arg, "<bad-element>", 13);
835 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]));
836 nc_write_clb((void *)arg, "</bad-element>", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100837 }
838
839 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100840 nc_write_clb((void *)arg, "<bad-namespace>", 15);
841 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]));
842 nc_write_clb((void *)arg, "</bad-namespace>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100843 }
844
845 for (i = 0; i < err->other_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100846 lyxml_dump_clb(nc_write_clb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100847 }
848
Michal Vasko428087d2016-01-14 16:04:28 +0100849 nc_write_clb((void *)arg, "</error-info>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100850 }
851
Michal Vasko428087d2016-01-14 16:04:28 +0100852 nc_write_clb((void *)arg, "</rpc-error>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100853}
854
Michal Vasko428087d2016-01-14 16:04:28 +0100855/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100856int
857nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200858{
Radek Krejcid116db42016-01-08 15:36:30 +0100859 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100860 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200861 const char *attrs;
862 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100863 struct lyxml_elem *rpc_elem;
864 struct nc_server_reply *reply;
865 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100866 char *buf = NULL;
867 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200868 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100869 uint32_t *sid = NULL, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200870
Michal Vasko428087d2016-01-14 16:04:28 +0100871 assert(session);
872
873 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100874 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100875 return -1;
876 }
877
Radek Krejcid116db42016-01-08 15:36:30 +0100878 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200879
880 arg.session = session;
881 arg.len = 0;
882
883 switch (type) {
884 case NC_MSG_RPC:
885 content = va_arg(ap, struct lyd_node *);
886 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100887
Radek Krejcife0b3472015-10-12 13:43:42 +0200888 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
889 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
Michal Vasko428087d2016-01-14 16:04:28 +0100890 nc_write_clb((void *)&arg, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200891 free(buf);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100892 lyd_print_clb(nc_write_clb, (void *)&arg, content, LYD_XML, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100893 nc_write_clb((void *)&arg, "</rpc>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200894
895 session->msgid++;
896 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100897
Radek Krejcife0b3472015-10-12 13:43:42 +0200898 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +0100899 rpc_elem = va_arg(ap, struct lyxml_elem *);
900 reply = va_arg(ap, struct nc_server_reply *);
901
Michal Vasko428087d2016-01-14 16:04:28 +0100902 nc_write_clb((void *)&arg, "<rpc-reply", 10);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100903 /* can be NULL if replying with a malformed-message error */
904 if (rpc_elem) {
905 lyxml_dump_clb(nc_write_clb, (void *)&arg, rpc_elem, LYXML_DUMP_ATTRS);
906 }
Michal Vasko428087d2016-01-14 16:04:28 +0100907 nc_write_clb((void *)&arg, ">", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908 switch (reply->type) {
909 case NC_RPL_OK:
Michal Vasko428087d2016-01-14 16:04:28 +0100910 nc_write_clb((void *)&arg, "<ok/>", 5);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 break;
912 case NC_RPL_DATA:
Michal Vasko428087d2016-01-14 16:04:28 +0100913 nc_write_clb((void *)&arg, "<data>", 6);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100914 lyd_print_clb(nc_write_clb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100915 nc_write_clb((void *)&arg, "<data/>", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100916 break;
917 case NC_RPL_ERROR:
918 error_rpl = (struct nc_server_reply_error *)reply;
919 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100920 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100921 }
922 break;
923 default:
924 ERRINT;
Michal Vasko428087d2016-01-14 16:04:28 +0100925 nc_write_clb((void *)&arg, NULL, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100926 va_end(ap);
927 return -1;
928 }
Michal Vasko428087d2016-01-14 16:04:28 +0100929 nc_write_clb((void *)&arg, "</rpc-reply>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200930 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100931
Radek Krejcife0b3472015-10-12 13:43:42 +0200932 case NC_MSG_NOTIF:
Michal Vasko428087d2016-01-14 16:04:28 +0100933 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
Radek Krejcife0b3472015-10-12 13:43:42 +0200934 /* TODO content */
Michal Vasko428087d2016-01-14 16:04:28 +0100935 nc_write_clb((void *)&arg, "</notification>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200936 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100937
Radek Krejcid116db42016-01-08 15:36:30 +0100938 case NC_MSG_HELLO:
939 if (session->version != NC_VERSION_10) {
940 va_end(ap);
941 return -1;
942 }
943 capabilities = va_arg(ap, const char **);
944 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100945
Radek Krejcid116db42016-01-08 15:36:30 +0100946 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko428087d2016-01-14 16:04:28 +0100947 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100948 free(buf);
949 for (i = 0; capabilities[i]; i++) {
950 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
Michal Vasko428087d2016-01-14 16:04:28 +0100951 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100952 free(buf);
953 }
954 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100955 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100956 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100957 free(buf);
958 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100959 nc_write_clb((void *)&arg, "</capabilities></hello>", 23);
Radek Krejcid116db42016-01-08 15:36:30 +0100960 }
Radek Krejcid116db42016-01-08 15:36:30 +0100961 break;
Michal Vaskoed462342016-01-12 12:33:48 +0100962
Radek Krejcife0b3472015-10-12 13:43:42 +0200963 default:
Radek Krejcid116db42016-01-08 15:36:30 +0100964 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +0200965 return -1;
966 }
967
968 /* flush message */
Michal Vasko428087d2016-01-14 16:04:28 +0100969 nc_write_clb((void *)&arg, NULL, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200970
971 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +0100972 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
973 /* error was already written */
974 return -1;
975 }
976
Radek Krejcid116db42016-01-08 15:36:30 +0100977 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200978}