blob: b80817dae184c0adfe909b6fdacc73f35c7abe9e [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
Michal Vasko11d142a2016-01-19 15:58:24 +010023#define _GNU_SOURCE /* asprintf, ppoll */
24#define _POSIX_SOUCE /* signals */
Radek Krejci206fcd62015-10-07 15:42:48 +020025#include <assert.h>
26#include <errno.h>
27#include <poll.h>
Michal Vasko3512e402016-01-28 16:22:34 +010028#include <inttypes.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020029#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020030#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010033#include <signal.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020034
35#include <libyang/libyang.h>
36
Radek Krejci206fcd62015-10-07 15:42:48 +020037#include "libnetconf.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 Vaskod083db62016-01-19 10:31:29 +010071 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
72 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +010073 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 Vaskod083db62016-01-19 10:31:29 +010078 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
79 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +010080 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 Vasko051d35b2016-02-03 15:28:37 +010099 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
100 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 Vasko454e22b2016-01-21 15:34:08 +0100106 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100107 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 Vaskod083db62016-01-19 10:31:29 +0100133 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100134 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 Vaskod083db62016-01-19 10:31:29 +0100138 ERR("Session %u: reading from the TLS session failed (SSL code %d).", session->id, x);
Michal Vasko428087d2016-01-14 16:04:28 +0100139 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 Vaskod083db62016-01-19 10:31:29 +0100208 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
209 ERR("Session %u: invalid input data (missing \"%s\" sequence).", 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;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100262 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100263
Michal Vasko428087d2016-01-14 16:04:28 +0100264 assert(session && data);
265 *data = NULL;
266
267 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100268 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100269 return NC_MSG_ERROR;
270 }
271
Michal Vasko05ba9df2016-01-13 14:40:27 +0100272 /* read the message */
273 switch (session->version) {
274 case NC_VERSION_10:
275 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &msg);
276 if (ret == -1) {
277 goto error;
278 }
279
280 /* cut off the end tag */
281 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
282 break;
283 case NC_VERSION_11:
284 while (1) {
285 ret = nc_read_until(session, "\n#", 0, NULL);
286 if (ret == -1) {
287 goto error;
288 }
289 ret = nc_read_until(session, "\n", 0, &chunk);
290 if (ret == -1) {
291 goto error;
292 }
293
294 if (!strcmp(chunk, "#\n")) {
295 /* end of chunked framing message */
296 free(chunk);
297 break;
298 }
299
300 /* convert string to the size of the following chunk */
301 chunk_len = strtoul(chunk, (char **)NULL, 10);
302 free(chunk);
303 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100304 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100305 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100306 }
307
308 /* now we have size of next chunk, so read the chunk */
309 ret = nc_read_chunk(session, chunk_len, &chunk);
310 if (ret == -1) {
311 goto error;
312 }
313
314 /* realloc message buffer, remember to count terminating null byte */
315 aux = realloc(msg, len + chunk_len + 1);
316 if (!aux) {
317 ERRMEM;
318 goto error;
319 }
320 msg = aux;
321 memcpy(msg + len, chunk, chunk_len);
322 len += chunk_len;
323 msg[len] = '\0';
324 free(chunk);
325 }
326
327 break;
328 }
Michal Vaskod083db62016-01-19 10:31:29 +0100329 DBG("Session %u: received message:\n%s", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100330
331 /* build XML tree */
332 *data = lyxml_read_data(session->ctx, msg, 0);
333 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100334 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100335 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100336 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100337 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100338 }
339 free(msg);
340 msg = NULL;
341
342 /* get and return message type */
343 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
344 if (!strcmp((*data)->name, "rpc")) {
345 return NC_MSG_RPC;
346 } else if (!strcmp((*data)->name, "rpc-reply")) {
347 return NC_MSG_REPLY;
348 } else if (!strcmp((*data)->name, "hello")) {
349 return NC_MSG_HELLO;
350 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100351 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100352 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100353 }
354 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
355 if (!strcmp((*data)->name, "notification")) {
356 return NC_MSG_NOTIF;
357 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100358 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100359 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100360 }
361 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100362 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100363 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100364 }
365
Michal Vasko428087d2016-01-14 16:04:28 +0100366malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100367 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100368 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
369 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100370 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100371
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100372 if (nc_write_msg(session, NC_MSG_REPLY, NULL, reply) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100373 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100374 if (session->status != NC_STATUS_INVALID) {
375 session->status = NC_STATUS_INVALID;
376 session->term_reason = NC_SESSION_TERM_OTHER;
377 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100378 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100379 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100380 }
381
Michal Vasko428087d2016-01-14 16:04:28 +0100382error:
383 /* cleanup */
384 free(msg);
385 free(*data);
386 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100387
388 return NC_MSG_ERROR;
389}
390
Michal Vasko428087d2016-01-14 16:04:28 +0100391/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
392static int
393nc_read_poll(struct nc_session *session, int timeout)
394{
Michal Vasko11d142a2016-01-19 15:58:24 +0100395 sigset_t sigmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100396 int ret = -2;
397 struct pollfd fds;
Michal Vasko11d142a2016-01-19 15:58:24 +0100398 struct timespec ts_timeout;
Michal Vasko428087d2016-01-14 16:04:28 +0100399
400 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100401 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100402 return -1;
403 }
404
405 switch (session->ti_type) {
406#ifdef ENABLE_SSH
407 case NC_TI_LIBSSH:
408 /* EINTR is handled, it resumes waiting */
409 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
410 if (ret == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100411 ERR("Session %u: polling on the SSH channel failed (%s).", session->id,
412 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100413 session->status = NC_STATUS_INVALID;
414 session->term_reason = NC_SESSION_TERM_OTHER;
415 return -1;
416 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100417 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100418 session->status = NC_STATUS_INVALID;
419 session->term_reason = NC_SESSION_TERM_DROPPED;
420 return -1;
421 } else if (ret > 0) {
422 /* fake it */
423 ret = 1;
424 fds.revents = POLLIN;
425 }
426 /* fallthrough */
427#endif
428#ifdef ENABLE_TLS
429 case NC_TI_OPENSSL:
430 if (session->ti_type == NC_TI_OPENSSL) {
431 fds.fd = SSL_get_fd(session->ti.tls);
432 }
433 /* fallthrough */
434#endif
435 case NC_TI_FD:
436 if (session->ti_type == NC_TI_FD) {
437 fds.fd = session->ti.fd.in;
438 }
439
440 /* poll only if it is not an SSH session */
441 if (ret == -2) {
442 fds.events = POLLIN;
Michal Vasko11d142a2016-01-19 15:58:24 +0100443 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100444
Michal Vasko11d142a2016-01-19 15:58:24 +0100445 if (timeout > -1) {
Michal Vasko085ff562016-01-20 10:07:30 +0100446 if (!timeout) {
447 ts_timeout.tv_sec = 0;
448 ts_timeout.tv_nsec = 0;
449 } else if (timeout > 0) {
450 ts_timeout.tv_sec = timeout / 1000;
451 ts_timeout.tv_nsec = (timeout % 1000) * 1000000;
Michal Vasko428087d2016-01-14 16:04:28 +0100452 }
453 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100454 sigfillset(&sigmask);
455 ret = ppoll(&fds, 1, (timeout == -1 ? NULL : &ts_timeout), &sigmask);
Michal Vasko428087d2016-01-14 16:04:28 +0100456 }
457
458 break;
459
460 default:
461 ERRINT;
462 return -1;
463 }
464
465 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
466 if (ret < 0) {
467 /* poll failed - something really bad happened, close the session */
Michal Vasko11d142a2016-01-19 15:58:24 +0100468 ERR("Session %u: ppoll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100469 session->status = NC_STATUS_INVALID;
470 session->term_reason = NC_SESSION_TERM_OTHER;
471 return -1;
472 } else { /* status > 0 */
473 /* in case of standard (non-libssh) poll, there still can be an error */
474 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100475 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100476 session->status = NC_STATUS_INVALID;
477 session->term_reason = NC_SESSION_TERM_DROPPED;
478 return -1;
479 }
480 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100481 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100482 session->status = NC_STATUS_INVALID;
483 session->term_reason = NC_SESSION_TERM_OTHER;
484 return -1;
485 }
486 }
487
488 return ret;
489}
490
491/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100492NC_MSG_TYPE
493nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200494{
Michal Vasko428087d2016-01-14 16:04:28 +0100495 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200496
497 assert(data);
498 *data = NULL;
499
Michal Vasko428087d2016-01-14 16:04:28 +0100500 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100501 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100502 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200503 }
504
Michal Vasko428087d2016-01-14 16:04:28 +0100505 ret = nc_read_poll(session, timeout);
506 if (ret == 0) {
507 /* timed out */
508 return NC_MSG_WOULDBLOCK;
509 } else if (ret < 0) {
510 /* poll error, error written */
511 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200512 }
513
Michal Vasko05ba9df2016-01-13 14:40:27 +0100514 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200515}
Radek Krejcife0b3472015-10-12 13:43:42 +0200516
Michal Vasko428087d2016-01-14 16:04:28 +0100517/* does not really log, only fatal errors */
518int
519nc_session_is_connected(struct nc_session *session)
520{
521 int ret;
522 struct pollfd fds;
523
524 switch (session->ti_type) {
525 case NC_TI_FD:
526 fds.fd = session->ti.fd.in;
527 break;
528#ifdef ENABLE_SSH
529 case NC_TI_LIBSSH:
530 fds.fd = ssh_get_fd(session->ti.libssh.session);
531 break;
532#endif
533#ifdef ENABLE_TLS
534 case NC_TI_OPENSSL:
535 fds.fd = SSL_get_fd(session->ti.tls);
536 break;
537#endif
538 case NC_TI_NONE:
539 ERRINT;
540 return 0;
541 }
542
543 fds.events = POLLIN;
544
545 errno = 0;
546 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
547
548 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100549 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100550 return 0;
551 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
552 return 0;
553 }
554
555 return 1;
556}
557
Radek Krejcife0b3472015-10-12 13:43:42 +0200558#define WRITE_BUFSIZE (2 * BUFFERSIZE)
559struct wclb_arg {
560 struct nc_session *session;
561 char buf[WRITE_BUFSIZE];
562 size_t len;
563};
564
565static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100566nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200567{
Michal Vasko428087d2016-01-14 16:04:28 +0100568 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
569 return -1;
570 }
571
572 /* prevent SIGPIPE this way */
573 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100574 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100575 session->status = NC_STATUS_INVALID;
576 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100577 return -1;
578 }
579
Radek Krejcife0b3472015-10-12 13:43:42 +0200580 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100581 case NC_TI_NONE:
582 return -1;
583
Radek Krejcife0b3472015-10-12 13:43:42 +0200584 case NC_TI_FD:
Michal Vasko086311b2016-01-08 09:53:11 +0100585 return write(session->ti.fd.out, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200586
Michal Vaskofb2fb762015-10-27 11:44:32 +0100587#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200588 case NC_TI_LIBSSH:
Michal Vasko454e22b2016-01-21 15:34:08 +0100589 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
590 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
591 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
592 } else {
593 ERR("Session %u: SSH channel unexpected EOF.", session->id);
594 }
595 session->status = NC_STATUS_INVALID;
596 session->term_reason = NC_SESSION_TERM_DROPPED;
597 return -1;
598 }
Michal Vasko086311b2016-01-08 09:53:11 +0100599 return ssh_channel_write(session->ti.libssh.channel, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200600#endif
Radek Krejcife0b3472015-10-12 13:43:42 +0200601#ifdef ENABLE_TLS
602 case NC_TI_OPENSSL:
Michal Vasko086311b2016-01-08 09:53:11 +0100603 return SSL_write(session->ti.tls, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200604#endif
605 }
606
607 return -1;
608}
609
Michal Vasko428087d2016-01-14 16:04:28 +0100610static int
611nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100612{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100613 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100614 char chunksize[20];
615
616 if (session->version == NC_VERSION_11) {
617 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100618 ret = nc_write(session, chunksize, strlen(chunksize));
619 if (ret == -1) {
620 return -1;
621 }
Michal Vasko086311b2016-01-08 09:53:11 +0100622 }
Michal Vasko428087d2016-01-14 16:04:28 +0100623
624 c = nc_write(session, buf, count);
625 if (c == -1) {
626 return -1;
627 }
628 ret += c;
629
630 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100631}
632
Radek Krejcife0b3472015-10-12 13:43:42 +0200633static int
Michal Vasko428087d2016-01-14 16:04:28 +0100634nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200635{
Michal Vasko428087d2016-01-14 16:04:28 +0100636 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100637
Michal Vasko428087d2016-01-14 16:04:28 +0100638 if (session->version == NC_VERSION_11) {
639 ret = nc_write(session, "\n##\n", 4);
640 } else {
641 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200642 }
643
Michal Vasko428087d2016-01-14 16:04:28 +0100644 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200645}
646
Michal Vasko428087d2016-01-14 16:04:28 +0100647static int
648nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200649{
Michal Vasko428087d2016-01-14 16:04:28 +0100650 int ret = 0;
651
Radek Krejcife0b3472015-10-12 13:43:42 +0200652 /* flush current buffer */
653 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100654 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200655 warg->len = 0;
656 }
Michal Vasko428087d2016-01-14 16:04:28 +0100657
658 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200659}
660
661static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100662nc_write_clb(void *arg, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200663{
Michal Vasko428087d2016-01-14 16:04:28 +0100664 int ret = 0, c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200665 struct wclb_arg *warg = (struct wclb_arg *)arg;
666
667 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100668 c = nc_write_clb_flush(warg);
669 if (c == -1) {
670 return -1;
671 }
672 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200673
674 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100675 c = nc_write_endtag(warg->session);
676 if (c == -1) {
677 return -1;
678 }
679 ret += c;
680
681 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200682 }
683
684 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
685 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100686 c = nc_write_clb_flush(warg);
687 if (c == -1) {
688 return -1;
689 }
690 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200691 }
Michal Vasko428087d2016-01-14 16:04:28 +0100692
Radek Krejcife0b3472015-10-12 13:43:42 +0200693 if (count > WRITE_BUFSIZE) {
694 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100695 c = nc_write_starttag_and_msg(warg->session, buf, count);
696 if (c == -1) {
697 return -1;
698 }
699 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200700 } else {
701 /* keep in buffer and write later */
702 memcpy(&warg->buf[warg->len], buf, count);
703 warg->len += count; /* is <= WRITE_BUFSIZE */
Michal Vasko428087d2016-01-14 16:04:28 +0100704 ret += count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200705 }
706
Michal Vasko428087d2016-01-14 16:04:28 +0100707 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200708}
709
Michal Vasko05ba9df2016-01-13 14:40:27 +0100710static void
Michal Vasko428087d2016-01-14 16:04:28 +0100711nc_write_error(struct wclb_arg *arg, struct nc_server_error *err)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100712{
713 uint16_t i;
714 char str_sid[11];
715
Michal Vasko428087d2016-01-14 16:04:28 +0100716 nc_write_clb((void *)arg, "<rpc-error>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100717
Michal Vasko428087d2016-01-14 16:04:28 +0100718 nc_write_clb((void *)arg, "<error-type>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100719 switch (err->type) {
720 case NC_ERR_TYPE_TRAN:
Michal Vasko428087d2016-01-14 16:04:28 +0100721 nc_write_clb((void *)arg, "transport", 9);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100722 break;
723 case NC_ERR_TYPE_RPC:
Michal Vasko428087d2016-01-14 16:04:28 +0100724 nc_write_clb((void *)arg, "rpc", 3);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100725 break;
726 case NC_ERR_TYPE_PROT:
Michal Vasko428087d2016-01-14 16:04:28 +0100727 nc_write_clb((void *)arg, "protocol", 8);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100728 break;
729 case NC_ERR_TYPE_APP:
Michal Vasko428087d2016-01-14 16:04:28 +0100730 nc_write_clb((void *)arg, "application", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100731 break;
732 default:
733 ERRINT;
734 return;
735 }
Michal Vasko428087d2016-01-14 16:04:28 +0100736 nc_write_clb((void *)arg, "</error-type>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100737
Michal Vasko428087d2016-01-14 16:04:28 +0100738 nc_write_clb((void *)arg, "<error-tag>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100739 switch (err->tag) {
740 case NC_ERR_IN_USE:
Michal Vasko428087d2016-01-14 16:04:28 +0100741 nc_write_clb((void *)arg, "in-use", 6);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100742 break;
743 case NC_ERR_INVALID_VALUE:
Michal Vasko428087d2016-01-14 16:04:28 +0100744 nc_write_clb((void *)arg, "invalid-value", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100745 break;
746 case NC_ERR_TOO_BIG:
Michal Vasko428087d2016-01-14 16:04:28 +0100747 nc_write_clb((void *)arg, "too-big", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100748 break;
749 case NC_ERR_MISSING_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100750 nc_write_clb((void *)arg, "missing-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100751 break;
752 case NC_ERR_BAD_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100753 nc_write_clb((void *)arg, "bad-attribute", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100754 break;
755 case NC_ERR_UNKNOWN_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100756 nc_write_clb((void *)arg, "unknown-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100757 break;
758 case NC_ERR_MISSING_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100759 nc_write_clb((void *)arg, "missing-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100760 break;
761 case NC_ERR_BAD_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100762 nc_write_clb((void *)arg, "bad-element", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100763 break;
764 case NC_ERR_UNKNOWN_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100765 nc_write_clb((void *)arg, "unknown-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100766 break;
767 case NC_ERR_UNKNOWN_NS:
Michal Vasko428087d2016-01-14 16:04:28 +0100768 nc_write_clb((void *)arg, "unknown-namespace", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100769 break;
770 case NC_ERR_ACCESS_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100771 nc_write_clb((void *)arg, "access-denied", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100772 break;
773 case NC_ERR_LOCK_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100774 nc_write_clb((void *)arg, "lock-denied", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100775 break;
776 case NC_ERR_RES_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100777 nc_write_clb((void *)arg, "resource-denied", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100778 break;
779 case NC_ERR_ROLLBACK_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100780 nc_write_clb((void *)arg, "rollback-failed", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100781 break;
782 case NC_ERR_DATA_EXISTS:
Michal Vasko428087d2016-01-14 16:04:28 +0100783 nc_write_clb((void *)arg, "data-exists", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100784 break;
785 case NC_ERR_DATA_MISSING:
Michal Vasko428087d2016-01-14 16:04:28 +0100786 nc_write_clb((void *)arg, "data-missing", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100787 break;
788 case NC_ERR_OP_NOT_SUPPORTED:
Michal Vasko428087d2016-01-14 16:04:28 +0100789 nc_write_clb((void *)arg, "operation-not-supported", 23);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100790 break;
791 case NC_ERR_OP_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100792 nc_write_clb((void *)arg, "operation-failed", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100793 break;
794 case NC_ERR_MALFORMED_MSG:
Michal Vasko428087d2016-01-14 16:04:28 +0100795 nc_write_clb((void *)arg, "malformed-message", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100796 break;
797 default:
798 ERRINT;
799 return;
800 }
Michal Vasko428087d2016-01-14 16:04:28 +0100801 nc_write_clb((void *)arg, "</error-tag>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100802
Michal Vasko428087d2016-01-14 16:04:28 +0100803 nc_write_clb((void *)arg, "<error-severity>error</error-severity>", 38);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100804
805 if (err->apptag) {
Michal Vasko428087d2016-01-14 16:04:28 +0100806 nc_write_clb((void *)arg, "<error-app-tag>", 15);
807 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag));
808 nc_write_clb((void *)arg, "</error-app-tag>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100809 }
810
811 if (err->path) {
Michal Vasko428087d2016-01-14 16:04:28 +0100812 nc_write_clb((void *)arg, "<error-path>", 12);
813 nc_write_clb((void *)arg, err->path, strlen(err->path));
814 nc_write_clb((void *)arg, "</error-path>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100815 }
816
817 if (err->message) {
Michal Vasko428087d2016-01-14 16:04:28 +0100818 nc_write_clb((void *)arg, "<error-message", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100819 if (err->message_lang) {
Michal Vasko428087d2016-01-14 16:04:28 +0100820 nc_write_clb((void *)arg, " xml:lang=\"", 11);
821 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang));
822 nc_write_clb((void *)arg, "\"", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100823 }
Michal Vasko428087d2016-01-14 16:04:28 +0100824 nc_write_clb((void *)arg, ">", 1);
825 nc_write_clb((void *)arg, err->message, strlen(err->message));
826 nc_write_clb((void *)arg, "</error-message>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100827 }
828
Michal Vasko11d142a2016-01-19 15:58:24 +0100829 if (err->sid || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko428087d2016-01-14 16:04:28 +0100830 nc_write_clb((void *)arg, "<error-info>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100831
832 if (err->sid) {
Michal Vasko428087d2016-01-14 16:04:28 +0100833 nc_write_clb((void *)arg, "<session-id>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100834 sprintf(str_sid, "%u", err->sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100835 nc_write_clb((void *)arg, str_sid, strlen(str_sid));
836 nc_write_clb((void *)arg, "</session-id>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100837 }
838
839 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100840 nc_write_clb((void *)arg, "<bad-attribute>", 15);
841 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]));
842 nc_write_clb((void *)arg, "</bad-attribute>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100843 }
844
845 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100846 nc_write_clb((void *)arg, "<bad-element>", 13);
847 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]));
848 nc_write_clb((void *)arg, "</bad-element>", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100849 }
850
851 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100852 nc_write_clb((void *)arg, "<bad-namespace>", 15);
853 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]));
854 nc_write_clb((void *)arg, "</bad-namespace>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100855 }
856
857 for (i = 0; i < err->other_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100858 lyxml_dump_clb(nc_write_clb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100859 }
860
Michal Vasko428087d2016-01-14 16:04:28 +0100861 nc_write_clb((void *)arg, "</error-info>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100862 }
863
Michal Vasko428087d2016-01-14 16:04:28 +0100864 nc_write_clb((void *)arg, "</rpc-error>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100865}
866
Michal Vasko428087d2016-01-14 16:04:28 +0100867/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100868int
869nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200870{
Radek Krejcid116db42016-01-08 15:36:30 +0100871 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100872 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200873 const char *attrs;
874 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100875 struct lyxml_elem *rpc_elem;
876 struct nc_server_reply *reply;
877 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100878 char *buf = NULL;
879 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200880 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100881 uint32_t *sid = NULL, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200882
Michal Vasko428087d2016-01-14 16:04:28 +0100883 assert(session);
884
885 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100886 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100887 return -1;
888 }
889
Radek Krejcid116db42016-01-08 15:36:30 +0100890 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200891
892 arg.session = session;
893 arg.len = 0;
894
895 switch (type) {
896 case NC_MSG_RPC:
897 content = va_arg(ap, struct lyd_node *);
898 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100899
Radek Krejcife0b3472015-10-12 13:43:42 +0200900 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
901 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
Michal Vasko428087d2016-01-14 16:04:28 +0100902 nc_write_clb((void *)&arg, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200903 free(buf);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100904 lyd_print_clb(nc_write_clb, (void *)&arg, content, LYD_XML, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100905 nc_write_clb((void *)&arg, "</rpc>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200906
907 session->msgid++;
908 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100909
Radek Krejcife0b3472015-10-12 13:43:42 +0200910 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 rpc_elem = va_arg(ap, struct lyxml_elem *);
912 reply = va_arg(ap, struct nc_server_reply *);
913
Michal Vasko428087d2016-01-14 16:04:28 +0100914 nc_write_clb((void *)&arg, "<rpc-reply", 10);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100915 /* can be NULL if replying with a malformed-message error */
916 if (rpc_elem) {
917 lyxml_dump_clb(nc_write_clb, (void *)&arg, rpc_elem, LYXML_DUMP_ATTRS);
918 }
Michal Vasko428087d2016-01-14 16:04:28 +0100919 nc_write_clb((void *)&arg, ">", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100920 switch (reply->type) {
921 case NC_RPL_OK:
Michal Vasko428087d2016-01-14 16:04:28 +0100922 nc_write_clb((void *)&arg, "<ok/>", 5);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100923 break;
924 case NC_RPL_DATA:
Michal Vasko428087d2016-01-14 16:04:28 +0100925 nc_write_clb((void *)&arg, "<data>", 6);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100926 lyd_print_clb(nc_write_clb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML, 0);
Michal Vasko82b06292016-02-01 10:07:40 +0100927 nc_write_clb((void *)&arg, "</data>", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100928 break;
929 case NC_RPL_ERROR:
930 error_rpl = (struct nc_server_reply_error *)reply;
931 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100932 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100933 }
934 break;
935 default:
936 ERRINT;
Michal Vasko428087d2016-01-14 16:04:28 +0100937 nc_write_clb((void *)&arg, NULL, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100938 va_end(ap);
939 return -1;
940 }
Michal Vasko428087d2016-01-14 16:04:28 +0100941 nc_write_clb((void *)&arg, "</rpc-reply>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200942 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100943
Radek Krejcife0b3472015-10-12 13:43:42 +0200944 case NC_MSG_NOTIF:
Michal Vasko428087d2016-01-14 16:04:28 +0100945 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
Radek Krejcife0b3472015-10-12 13:43:42 +0200946 /* TODO content */
Michal Vasko428087d2016-01-14 16:04:28 +0100947 nc_write_clb((void *)&arg, "</notification>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200948 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100949
Radek Krejcid116db42016-01-08 15:36:30 +0100950 case NC_MSG_HELLO:
951 if (session->version != NC_VERSION_10) {
952 va_end(ap);
953 return -1;
954 }
955 capabilities = va_arg(ap, const char **);
956 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100957
Radek Krejcid116db42016-01-08 15:36:30 +0100958 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
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 for (i = 0; capabilities[i]; i++) {
962 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
Michal Vasko428087d2016-01-14 16:04:28 +0100963 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100964 free(buf);
965 }
966 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100967 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100968 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100969 free(buf);
970 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100971 nc_write_clb((void *)&arg, "</capabilities></hello>", 23);
Radek Krejcid116db42016-01-08 15:36:30 +0100972 }
Radek Krejcid116db42016-01-08 15:36:30 +0100973 break;
Michal Vaskoed462342016-01-12 12:33:48 +0100974
Radek Krejcife0b3472015-10-12 13:43:42 +0200975 default:
Radek Krejcid116db42016-01-08 15:36:30 +0100976 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +0200977 return -1;
978 }
979
980 /* flush message */
Michal Vasko428087d2016-01-14 16:04:28 +0100981 nc_write_clb((void *)&arg, NULL, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200982
983 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +0100984 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
985 /* error was already written */
986 return -1;
987 }
988
Radek Krejcid116db42016-01-08 15:36:30 +0100989 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200990}