blob: 432ec7024fae61d7076f864756829eb112d258a6 [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 Vaskod083db62016-01-19 10:31:29 +010099 ERR("Session %u: reading from the SSH channel failed (%zd: %s).", 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 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 Vaskod083db62016-01-19 10:31:29 +0100411 ERR("Session %u: SSH channel error (%s).", session->id, ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100412 session->status = NC_STATUS_INVALID;
413 session->term_reason = NC_SESSION_TERM_OTHER;
414 return -1;
415 } else if (ret == SSH_EOF) {
Michal Vaskod083db62016-01-19 10:31:29 +0100416 ERR("Session %u: communication channel unexpectedly closed (libssh).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100417 session->status = NC_STATUS_INVALID;
418 session->term_reason = NC_SESSION_TERM_DROPPED;
419 return -1;
420 } else if (ret > 0) {
421 /* fake it */
422 ret = 1;
423 fds.revents = POLLIN;
424 }
425 /* fallthrough */
426#endif
427#ifdef ENABLE_TLS
428 case NC_TI_OPENSSL:
429 if (session->ti_type == NC_TI_OPENSSL) {
430 fds.fd = SSL_get_fd(session->ti.tls);
431 }
432 /* fallthrough */
433#endif
434 case NC_TI_FD:
435 if (session->ti_type == NC_TI_FD) {
436 fds.fd = session->ti.fd.in;
437 }
438
439 /* poll only if it is not an SSH session */
440 if (ret == -2) {
441 fds.events = POLLIN;
Michal Vasko11d142a2016-01-19 15:58:24 +0100442 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100443
Michal Vasko11d142a2016-01-19 15:58:24 +0100444 if (timeout > -1) {
Michal Vasko085ff562016-01-20 10:07:30 +0100445 if (!timeout) {
446 ts_timeout.tv_sec = 0;
447 ts_timeout.tv_nsec = 0;
448 } else if (timeout > 0) {
449 ts_timeout.tv_sec = timeout / 1000;
450 ts_timeout.tv_nsec = (timeout % 1000) * 1000000;
Michal Vasko428087d2016-01-14 16:04:28 +0100451 }
452 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100453 sigfillset(&sigmask);
454 ret = ppoll(&fds, 1, (timeout == -1 ? NULL : &ts_timeout), &sigmask);
Michal Vasko428087d2016-01-14 16:04:28 +0100455 }
456
457 break;
458
459 default:
460 ERRINT;
461 return -1;
462 }
463
464 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
465 if (ret < 0) {
466 /* poll failed - something really bad happened, close the session */
Michal Vasko11d142a2016-01-19 15:58:24 +0100467 ERR("Session %u: ppoll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100468 session->status = NC_STATUS_INVALID;
469 session->term_reason = NC_SESSION_TERM_OTHER;
470 return -1;
471 } else { /* status > 0 */
472 /* in case of standard (non-libssh) poll, there still can be an error */
473 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100474 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100475 session->status = NC_STATUS_INVALID;
476 session->term_reason = NC_SESSION_TERM_DROPPED;
477 return -1;
478 }
479 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100480 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100481 session->status = NC_STATUS_INVALID;
482 session->term_reason = NC_SESSION_TERM_OTHER;
483 return -1;
484 }
485 }
486
487 return ret;
488}
489
490/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100491NC_MSG_TYPE
492nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200493{
Michal Vasko428087d2016-01-14 16:04:28 +0100494 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200495
496 assert(data);
497 *data = NULL;
498
Michal Vasko428087d2016-01-14 16:04:28 +0100499 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100500 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100501 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200502 }
503
Michal Vasko428087d2016-01-14 16:04:28 +0100504 ret = nc_read_poll(session, timeout);
505 if (ret == 0) {
506 /* timed out */
507 return NC_MSG_WOULDBLOCK;
508 } else if (ret < 0) {
509 /* poll error, error written */
510 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200511 }
512
Michal Vasko05ba9df2016-01-13 14:40:27 +0100513 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200514}
Radek Krejcife0b3472015-10-12 13:43:42 +0200515
Michal Vasko428087d2016-01-14 16:04:28 +0100516/* does not really log, only fatal errors */
517int
518nc_session_is_connected(struct nc_session *session)
519{
520 int ret;
521 struct pollfd fds;
522
523 switch (session->ti_type) {
524 case NC_TI_FD:
525 fds.fd = session->ti.fd.in;
526 break;
527#ifdef ENABLE_SSH
528 case NC_TI_LIBSSH:
529 fds.fd = ssh_get_fd(session->ti.libssh.session);
530 break;
531#endif
532#ifdef ENABLE_TLS
533 case NC_TI_OPENSSL:
534 fds.fd = SSL_get_fd(session->ti.tls);
535 break;
536#endif
537 case NC_TI_NONE:
538 ERRINT;
539 return 0;
540 }
541
542 fds.events = POLLIN;
543
544 errno = 0;
545 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
546
547 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100548 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100549 return 0;
550 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
551 return 0;
552 }
553
554 return 1;
555}
556
Radek Krejcife0b3472015-10-12 13:43:42 +0200557#define WRITE_BUFSIZE (2 * BUFFERSIZE)
558struct wclb_arg {
559 struct nc_session *session;
560 char buf[WRITE_BUFSIZE];
561 size_t len;
562};
563
564static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100565nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200566{
Michal Vasko428087d2016-01-14 16:04:28 +0100567 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
568 return -1;
569 }
570
571 /* prevent SIGPIPE this way */
572 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100573 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100574 session->status = NC_STATUS_INVALID;
575 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100576 return -1;
577 }
578
Radek Krejcife0b3472015-10-12 13:43:42 +0200579 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100580 case NC_TI_NONE:
581 return -1;
582
Radek Krejcife0b3472015-10-12 13:43:42 +0200583 case NC_TI_FD:
Michal Vasko086311b2016-01-08 09:53:11 +0100584 return write(session->ti.fd.out, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200585
Michal Vaskofb2fb762015-10-27 11:44:32 +0100586#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200587 case NC_TI_LIBSSH:
Michal Vasko454e22b2016-01-21 15:34:08 +0100588 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
589 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
590 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
591 } else {
592 ERR("Session %u: SSH channel unexpected EOF.", session->id);
593 }
594 session->status = NC_STATUS_INVALID;
595 session->term_reason = NC_SESSION_TERM_DROPPED;
596 return -1;
597 }
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 Vaskofd2db9b2016-01-14 16:15:16 +0100612 int ret = 0, 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
Michal Vasko11d142a2016-01-19 15:58:24 +0100828 if (err->sid || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
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)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100885 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100886 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 Vasko0473c4c2016-01-19 10:40:06 +0100903 lyd_print_clb(nc_write_clb, (void *)&arg, content, LYD_XML, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100904 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);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100914 /* can be NULL if replying with a malformed-message error */
915 if (rpc_elem) {
916 lyxml_dump_clb(nc_write_clb, (void *)&arg, rpc_elem, LYXML_DUMP_ATTRS);
917 }
Michal Vasko428087d2016-01-14 16:04:28 +0100918 nc_write_clb((void *)&arg, ">", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100919 switch (reply->type) {
920 case NC_RPL_OK:
Michal Vasko428087d2016-01-14 16:04:28 +0100921 nc_write_clb((void *)&arg, "<ok/>", 5);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100922 break;
923 case NC_RPL_DATA:
Michal Vasko428087d2016-01-14 16:04:28 +0100924 nc_write_clb((void *)&arg, "<data>", 6);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100925 lyd_print_clb(nc_write_clb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100926 nc_write_clb((void *)&arg, "<data/>", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100927 break;
928 case NC_RPL_ERROR:
929 error_rpl = (struct nc_server_reply_error *)reply;
930 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100931 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100932 }
933 break;
934 default:
935 ERRINT;
Michal Vasko428087d2016-01-14 16:04:28 +0100936 nc_write_clb((void *)&arg, NULL, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100937 va_end(ap);
938 return -1;
939 }
Michal Vasko428087d2016-01-14 16:04:28 +0100940 nc_write_clb((void *)&arg, "</rpc-reply>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200941 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100942
Radek Krejcife0b3472015-10-12 13:43:42 +0200943 case NC_MSG_NOTIF:
Michal Vasko428087d2016-01-14 16:04:28 +0100944 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
Radek Krejcife0b3472015-10-12 13:43:42 +0200945 /* TODO content */
Michal Vasko428087d2016-01-14 16:04:28 +0100946 nc_write_clb((void *)&arg, "</notification>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200947 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100948
Radek Krejcid116db42016-01-08 15:36:30 +0100949 case NC_MSG_HELLO:
950 if (session->version != NC_VERSION_10) {
951 va_end(ap);
952 return -1;
953 }
954 capabilities = va_arg(ap, const char **);
955 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100956
Radek Krejcid116db42016-01-08 15:36:30 +0100957 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko428087d2016-01-14 16:04:28 +0100958 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100959 free(buf);
960 for (i = 0; capabilities[i]; i++) {
961 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
Michal Vasko428087d2016-01-14 16:04:28 +0100962 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100963 free(buf);
964 }
965 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100966 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100967 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100968 free(buf);
969 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100970 nc_write_clb((void *)&arg, "</capabilities></hello>", 23);
Radek Krejcid116db42016-01-08 15:36:30 +0100971 }
Radek Krejcid116db42016-01-08 15:36:30 +0100972 break;
Michal Vaskoed462342016-01-12 12:33:48 +0100973
Radek Krejcife0b3472015-10-12 13:43:42 +0200974 default:
Radek Krejcid116db42016-01-08 15:36:30 +0100975 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +0200976 return -1;
977 }
978
979 /* flush message */
Michal Vasko428087d2016-01-14 16:04:28 +0100980 nc_write_clb((void *)&arg, NULL, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200981
982 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +0100983 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
984 /* error was already written */
985 return -1;
986 }
987
Radek Krejcid116db42016-01-08 15:36:30 +0100988 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200989}