blob: 2620c549f754cc32850be1a2e27ba1799c41795a [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;
Michal Vasko5550cda2016-02-03 15:28:57 +0100425 } else { /* ret == 0 */
426 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100427 }
428 /* fallthrough */
429#endif
430#ifdef ENABLE_TLS
431 case NC_TI_OPENSSL:
432 if (session->ti_type == NC_TI_OPENSSL) {
433 fds.fd = SSL_get_fd(session->ti.tls);
434 }
435 /* fallthrough */
436#endif
437 case NC_TI_FD:
438 if (session->ti_type == NC_TI_FD) {
439 fds.fd = session->ti.fd.in;
440 }
441
442 /* poll only if it is not an SSH session */
443 if (ret == -2) {
444 fds.events = POLLIN;
Michal Vasko11d142a2016-01-19 15:58:24 +0100445 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100446
Michal Vasko11d142a2016-01-19 15:58:24 +0100447 if (timeout > -1) {
Michal Vasko085ff562016-01-20 10:07:30 +0100448 if (!timeout) {
449 ts_timeout.tv_sec = 0;
450 ts_timeout.tv_nsec = 0;
451 } else if (timeout > 0) {
452 ts_timeout.tv_sec = timeout / 1000;
453 ts_timeout.tv_nsec = (timeout % 1000) * 1000000;
Michal Vasko428087d2016-01-14 16:04:28 +0100454 }
455 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100456 sigfillset(&sigmask);
457 ret = ppoll(&fds, 1, (timeout == -1 ? NULL : &ts_timeout), &sigmask);
Michal Vasko428087d2016-01-14 16:04:28 +0100458 }
459
460 break;
461
462 default:
463 ERRINT;
464 return -1;
465 }
466
467 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
468 if (ret < 0) {
469 /* poll failed - something really bad happened, close the session */
Michal Vasko11d142a2016-01-19 15:58:24 +0100470 ERR("Session %u: ppoll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100471 session->status = NC_STATUS_INVALID;
472 session->term_reason = NC_SESSION_TERM_OTHER;
473 return -1;
474 } else { /* status > 0 */
475 /* in case of standard (non-libssh) poll, there still can be an error */
476 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100477 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100478 session->status = NC_STATUS_INVALID;
479 session->term_reason = NC_SESSION_TERM_DROPPED;
480 return -1;
481 }
482 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100483 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100484 session->status = NC_STATUS_INVALID;
485 session->term_reason = NC_SESSION_TERM_OTHER;
486 return -1;
487 }
488 }
489
490 return ret;
491}
492
493/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100494NC_MSG_TYPE
495nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200496{
Michal Vasko428087d2016-01-14 16:04:28 +0100497 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200498
499 assert(data);
500 *data = NULL;
501
Michal Vasko428087d2016-01-14 16:04:28 +0100502 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100503 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100504 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200505 }
506
Michal Vasko428087d2016-01-14 16:04:28 +0100507 ret = nc_read_poll(session, timeout);
508 if (ret == 0) {
509 /* timed out */
510 return NC_MSG_WOULDBLOCK;
511 } else if (ret < 0) {
512 /* poll error, error written */
513 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200514 }
515
Michal Vasko05ba9df2016-01-13 14:40:27 +0100516 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200517}
Radek Krejcife0b3472015-10-12 13:43:42 +0200518
Michal Vasko428087d2016-01-14 16:04:28 +0100519/* does not really log, only fatal errors */
520int
521nc_session_is_connected(struct nc_session *session)
522{
523 int ret;
524 struct pollfd fds;
525
526 switch (session->ti_type) {
527 case NC_TI_FD:
528 fds.fd = session->ti.fd.in;
529 break;
530#ifdef ENABLE_SSH
531 case NC_TI_LIBSSH:
532 fds.fd = ssh_get_fd(session->ti.libssh.session);
533 break;
534#endif
535#ifdef ENABLE_TLS
536 case NC_TI_OPENSSL:
537 fds.fd = SSL_get_fd(session->ti.tls);
538 break;
539#endif
540 case NC_TI_NONE:
541 ERRINT;
542 return 0;
543 }
544
545 fds.events = POLLIN;
546
547 errno = 0;
548 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
549
550 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100551 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100552 return 0;
553 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
554 return 0;
555 }
556
557 return 1;
558}
559
Radek Krejcife0b3472015-10-12 13:43:42 +0200560#define WRITE_BUFSIZE (2 * BUFFERSIZE)
561struct wclb_arg {
562 struct nc_session *session;
563 char buf[WRITE_BUFSIZE];
564 size_t len;
565};
566
567static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100568nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200569{
Michal Vasko428087d2016-01-14 16:04:28 +0100570 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
571 return -1;
572 }
573
574 /* prevent SIGPIPE this way */
575 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100576 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100577 session->status = NC_STATUS_INVALID;
578 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100579 return -1;
580 }
581
Radek Krejcife0b3472015-10-12 13:43:42 +0200582 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100583 case NC_TI_NONE:
584 return -1;
585
Radek Krejcife0b3472015-10-12 13:43:42 +0200586 case NC_TI_FD:
Michal Vasko086311b2016-01-08 09:53:11 +0100587 return write(session->ti.fd.out, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200588
Michal Vaskofb2fb762015-10-27 11:44:32 +0100589#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200590 case NC_TI_LIBSSH:
Michal Vasko454e22b2016-01-21 15:34:08 +0100591 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
592 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
593 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
594 } else {
595 ERR("Session %u: SSH channel unexpected EOF.", session->id);
596 }
597 session->status = NC_STATUS_INVALID;
598 session->term_reason = NC_SESSION_TERM_DROPPED;
599 return -1;
600 }
Michal Vasko086311b2016-01-08 09:53:11 +0100601 return ssh_channel_write(session->ti.libssh.channel, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200602#endif
Radek Krejcife0b3472015-10-12 13:43:42 +0200603#ifdef ENABLE_TLS
604 case NC_TI_OPENSSL:
Michal Vasko086311b2016-01-08 09:53:11 +0100605 return SSL_write(session->ti.tls, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200606#endif
607 }
608
609 return -1;
610}
611
Michal Vasko428087d2016-01-14 16:04:28 +0100612static int
613nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100614{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100615 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100616 char chunksize[20];
617
618 if (session->version == NC_VERSION_11) {
619 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100620 ret = nc_write(session, chunksize, strlen(chunksize));
621 if (ret == -1) {
622 return -1;
623 }
Michal Vasko086311b2016-01-08 09:53:11 +0100624 }
Michal Vasko428087d2016-01-14 16:04:28 +0100625
626 c = nc_write(session, buf, count);
627 if (c == -1) {
628 return -1;
629 }
630 ret += c;
631
632 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100633}
634
Radek Krejcife0b3472015-10-12 13:43:42 +0200635static int
Michal Vasko428087d2016-01-14 16:04:28 +0100636nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200637{
Michal Vasko428087d2016-01-14 16:04:28 +0100638 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100639
Michal Vasko428087d2016-01-14 16:04:28 +0100640 if (session->version == NC_VERSION_11) {
641 ret = nc_write(session, "\n##\n", 4);
642 } else {
643 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200644 }
645
Michal Vasko428087d2016-01-14 16:04:28 +0100646 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200647}
648
Michal Vasko428087d2016-01-14 16:04:28 +0100649static int
650nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200651{
Michal Vasko428087d2016-01-14 16:04:28 +0100652 int ret = 0;
653
Radek Krejcife0b3472015-10-12 13:43:42 +0200654 /* flush current buffer */
655 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100656 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200657 warg->len = 0;
658 }
Michal Vasko428087d2016-01-14 16:04:28 +0100659
660 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200661}
662
663static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100664nc_write_clb(void *arg, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200665{
Michal Vasko428087d2016-01-14 16:04:28 +0100666 int ret = 0, c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200667 struct wclb_arg *warg = (struct wclb_arg *)arg;
668
669 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100670 c = nc_write_clb_flush(warg);
671 if (c == -1) {
672 return -1;
673 }
674 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200675
676 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100677 c = nc_write_endtag(warg->session);
678 if (c == -1) {
679 return -1;
680 }
681 ret += c;
682
683 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200684 }
685
686 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
687 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100688 c = nc_write_clb_flush(warg);
689 if (c == -1) {
690 return -1;
691 }
692 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200693 }
Michal Vasko428087d2016-01-14 16:04:28 +0100694
Radek Krejcife0b3472015-10-12 13:43:42 +0200695 if (count > WRITE_BUFSIZE) {
696 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100697 c = nc_write_starttag_and_msg(warg->session, buf, count);
698 if (c == -1) {
699 return -1;
700 }
701 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200702 } else {
703 /* keep in buffer and write later */
704 memcpy(&warg->buf[warg->len], buf, count);
705 warg->len += count; /* is <= WRITE_BUFSIZE */
Michal Vasko428087d2016-01-14 16:04:28 +0100706 ret += count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200707 }
708
Michal Vasko428087d2016-01-14 16:04:28 +0100709 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200710}
711
Michal Vasko05ba9df2016-01-13 14:40:27 +0100712static void
Michal Vasko428087d2016-01-14 16:04:28 +0100713nc_write_error(struct wclb_arg *arg, struct nc_server_error *err)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100714{
715 uint16_t i;
716 char str_sid[11];
717
Michal Vasko428087d2016-01-14 16:04:28 +0100718 nc_write_clb((void *)arg, "<rpc-error>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100719
Michal Vasko428087d2016-01-14 16:04:28 +0100720 nc_write_clb((void *)arg, "<error-type>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100721 switch (err->type) {
722 case NC_ERR_TYPE_TRAN:
Michal Vasko428087d2016-01-14 16:04:28 +0100723 nc_write_clb((void *)arg, "transport", 9);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100724 break;
725 case NC_ERR_TYPE_RPC:
Michal Vasko428087d2016-01-14 16:04:28 +0100726 nc_write_clb((void *)arg, "rpc", 3);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100727 break;
728 case NC_ERR_TYPE_PROT:
Michal Vasko428087d2016-01-14 16:04:28 +0100729 nc_write_clb((void *)arg, "protocol", 8);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100730 break;
731 case NC_ERR_TYPE_APP:
Michal Vasko428087d2016-01-14 16:04:28 +0100732 nc_write_clb((void *)arg, "application", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100733 break;
734 default:
735 ERRINT;
736 return;
737 }
Michal Vasko428087d2016-01-14 16:04:28 +0100738 nc_write_clb((void *)arg, "</error-type>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100739
Michal Vasko428087d2016-01-14 16:04:28 +0100740 nc_write_clb((void *)arg, "<error-tag>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100741 switch (err->tag) {
742 case NC_ERR_IN_USE:
Michal Vasko428087d2016-01-14 16:04:28 +0100743 nc_write_clb((void *)arg, "in-use", 6);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100744 break;
745 case NC_ERR_INVALID_VALUE:
Michal Vasko428087d2016-01-14 16:04:28 +0100746 nc_write_clb((void *)arg, "invalid-value", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100747 break;
748 case NC_ERR_TOO_BIG:
Michal Vasko428087d2016-01-14 16:04:28 +0100749 nc_write_clb((void *)arg, "too-big", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100750 break;
751 case NC_ERR_MISSING_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100752 nc_write_clb((void *)arg, "missing-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100753 break;
754 case NC_ERR_BAD_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100755 nc_write_clb((void *)arg, "bad-attribute", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100756 break;
757 case NC_ERR_UNKNOWN_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100758 nc_write_clb((void *)arg, "unknown-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100759 break;
760 case NC_ERR_MISSING_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100761 nc_write_clb((void *)arg, "missing-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100762 break;
763 case NC_ERR_BAD_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100764 nc_write_clb((void *)arg, "bad-element", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100765 break;
766 case NC_ERR_UNKNOWN_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100767 nc_write_clb((void *)arg, "unknown-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100768 break;
769 case NC_ERR_UNKNOWN_NS:
Michal Vasko428087d2016-01-14 16:04:28 +0100770 nc_write_clb((void *)arg, "unknown-namespace", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100771 break;
772 case NC_ERR_ACCESS_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100773 nc_write_clb((void *)arg, "access-denied", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100774 break;
775 case NC_ERR_LOCK_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100776 nc_write_clb((void *)arg, "lock-denied", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100777 break;
778 case NC_ERR_RES_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100779 nc_write_clb((void *)arg, "resource-denied", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100780 break;
781 case NC_ERR_ROLLBACK_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100782 nc_write_clb((void *)arg, "rollback-failed", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100783 break;
784 case NC_ERR_DATA_EXISTS:
Michal Vasko428087d2016-01-14 16:04:28 +0100785 nc_write_clb((void *)arg, "data-exists", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100786 break;
787 case NC_ERR_DATA_MISSING:
Michal Vasko428087d2016-01-14 16:04:28 +0100788 nc_write_clb((void *)arg, "data-missing", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100789 break;
790 case NC_ERR_OP_NOT_SUPPORTED:
Michal Vasko428087d2016-01-14 16:04:28 +0100791 nc_write_clb((void *)arg, "operation-not-supported", 23);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100792 break;
793 case NC_ERR_OP_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100794 nc_write_clb((void *)arg, "operation-failed", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100795 break;
796 case NC_ERR_MALFORMED_MSG:
Michal Vasko428087d2016-01-14 16:04:28 +0100797 nc_write_clb((void *)arg, "malformed-message", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100798 break;
799 default:
800 ERRINT;
801 return;
802 }
Michal Vasko428087d2016-01-14 16:04:28 +0100803 nc_write_clb((void *)arg, "</error-tag>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100804
Michal Vasko428087d2016-01-14 16:04:28 +0100805 nc_write_clb((void *)arg, "<error-severity>error</error-severity>", 38);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100806
807 if (err->apptag) {
Michal Vasko428087d2016-01-14 16:04:28 +0100808 nc_write_clb((void *)arg, "<error-app-tag>", 15);
809 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag));
810 nc_write_clb((void *)arg, "</error-app-tag>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100811 }
812
813 if (err->path) {
Michal Vasko428087d2016-01-14 16:04:28 +0100814 nc_write_clb((void *)arg, "<error-path>", 12);
815 nc_write_clb((void *)arg, err->path, strlen(err->path));
816 nc_write_clb((void *)arg, "</error-path>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100817 }
818
819 if (err->message) {
Michal Vasko428087d2016-01-14 16:04:28 +0100820 nc_write_clb((void *)arg, "<error-message", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100821 if (err->message_lang) {
Michal Vasko428087d2016-01-14 16:04:28 +0100822 nc_write_clb((void *)arg, " xml:lang=\"", 11);
823 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang));
824 nc_write_clb((void *)arg, "\"", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100825 }
Michal Vasko428087d2016-01-14 16:04:28 +0100826 nc_write_clb((void *)arg, ">", 1);
827 nc_write_clb((void *)arg, err->message, strlen(err->message));
828 nc_write_clb((void *)arg, "</error-message>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100829 }
830
Michal Vasko11d142a2016-01-19 15:58:24 +0100831 if (err->sid || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko428087d2016-01-14 16:04:28 +0100832 nc_write_clb((void *)arg, "<error-info>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100833
834 if (err->sid) {
Michal Vasko428087d2016-01-14 16:04:28 +0100835 nc_write_clb((void *)arg, "<session-id>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100836 sprintf(str_sid, "%u", err->sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100837 nc_write_clb((void *)arg, str_sid, strlen(str_sid));
838 nc_write_clb((void *)arg, "</session-id>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100839 }
840
841 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100842 nc_write_clb((void *)arg, "<bad-attribute>", 15);
843 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]));
844 nc_write_clb((void *)arg, "</bad-attribute>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100845 }
846
847 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100848 nc_write_clb((void *)arg, "<bad-element>", 13);
849 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]));
850 nc_write_clb((void *)arg, "</bad-element>", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100851 }
852
853 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100854 nc_write_clb((void *)arg, "<bad-namespace>", 15);
855 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]));
856 nc_write_clb((void *)arg, "</bad-namespace>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100857 }
858
859 for (i = 0; i < err->other_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100860 lyxml_dump_clb(nc_write_clb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100861 }
862
Michal Vasko428087d2016-01-14 16:04:28 +0100863 nc_write_clb((void *)arg, "</error-info>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100864 }
865
Michal Vasko428087d2016-01-14 16:04:28 +0100866 nc_write_clb((void *)arg, "</rpc-error>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100867}
868
Michal Vasko428087d2016-01-14 16:04:28 +0100869/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100870int
871nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200872{
Radek Krejcid116db42016-01-08 15:36:30 +0100873 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100874 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200875 const char *attrs;
876 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100877 struct lyxml_elem *rpc_elem;
878 struct nc_server_reply *reply;
879 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100880 char *buf = NULL;
881 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200882 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100883 uint32_t *sid = NULL, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200884
Michal Vasko428087d2016-01-14 16:04:28 +0100885 assert(session);
886
887 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100888 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100889 return -1;
890 }
891
Radek Krejcid116db42016-01-08 15:36:30 +0100892 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200893
894 arg.session = session;
895 arg.len = 0;
896
897 switch (type) {
898 case NC_MSG_RPC:
899 content = va_arg(ap, struct lyd_node *);
900 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100901
Radek Krejcife0b3472015-10-12 13:43:42 +0200902 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
903 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
Michal Vasko428087d2016-01-14 16:04:28 +0100904 nc_write_clb((void *)&arg, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200905 free(buf);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100906 lyd_print_clb(nc_write_clb, (void *)&arg, content, LYD_XML, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100907 nc_write_clb((void *)&arg, "</rpc>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200908
909 session->msgid++;
910 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911
Radek Krejcife0b3472015-10-12 13:43:42 +0200912 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +0100913 rpc_elem = va_arg(ap, struct lyxml_elem *);
914 reply = va_arg(ap, struct nc_server_reply *);
915
Michal Vasko428087d2016-01-14 16:04:28 +0100916 nc_write_clb((void *)&arg, "<rpc-reply", 10);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100917 /* can be NULL if replying with a malformed-message error */
918 if (rpc_elem) {
919 lyxml_dump_clb(nc_write_clb, (void *)&arg, rpc_elem, LYXML_DUMP_ATTRS);
920 }
Michal Vasko428087d2016-01-14 16:04:28 +0100921 nc_write_clb((void *)&arg, ">", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100922 switch (reply->type) {
923 case NC_RPL_OK:
Michal Vasko428087d2016-01-14 16:04:28 +0100924 nc_write_clb((void *)&arg, "<ok/>", 5);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100925 break;
926 case NC_RPL_DATA:
Michal Vasko428087d2016-01-14 16:04:28 +0100927 nc_write_clb((void *)&arg, "<data>", 6);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100928 lyd_print_clb(nc_write_clb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML, 0);
Michal Vasko82b06292016-02-01 10:07:40 +0100929 nc_write_clb((void *)&arg, "</data>", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100930 break;
931 case NC_RPL_ERROR:
932 error_rpl = (struct nc_server_reply_error *)reply;
933 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100934 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100935 }
936 break;
937 default:
938 ERRINT;
Michal Vasko428087d2016-01-14 16:04:28 +0100939 nc_write_clb((void *)&arg, NULL, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100940 va_end(ap);
941 return -1;
942 }
Michal Vasko428087d2016-01-14 16:04:28 +0100943 nc_write_clb((void *)&arg, "</rpc-reply>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200944 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100945
Radek Krejcife0b3472015-10-12 13:43:42 +0200946 case NC_MSG_NOTIF:
Michal Vasko428087d2016-01-14 16:04:28 +0100947 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
Radek Krejcife0b3472015-10-12 13:43:42 +0200948 /* TODO content */
Michal Vasko428087d2016-01-14 16:04:28 +0100949 nc_write_clb((void *)&arg, "</notification>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200950 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100951
Radek Krejcid116db42016-01-08 15:36:30 +0100952 case NC_MSG_HELLO:
953 if (session->version != NC_VERSION_10) {
954 va_end(ap);
955 return -1;
956 }
957 capabilities = va_arg(ap, const char **);
958 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100959
Radek Krejcid116db42016-01-08 15:36:30 +0100960 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko428087d2016-01-14 16:04:28 +0100961 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100962 free(buf);
963 for (i = 0; capabilities[i]; i++) {
964 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
Michal Vasko428087d2016-01-14 16:04:28 +0100965 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100966 free(buf);
967 }
968 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100969 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100970 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100971 free(buf);
972 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100973 nc_write_clb((void *)&arg, "</capabilities></hello>", 23);
Radek Krejcid116db42016-01-08 15:36:30 +0100974 }
Radek Krejcid116db42016-01-08 15:36:30 +0100975 break;
Michal Vaskoed462342016-01-12 12:33:48 +0100976
Radek Krejcife0b3472015-10-12 13:43:42 +0200977 default:
Radek Krejcid116db42016-01-08 15:36:30 +0100978 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +0200979 return -1;
980 }
981
982 /* flush message */
Michal Vasko428087d2016-01-14 16:04:28 +0100983 nc_write_clb((void *)&arg, NULL, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200984
985 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +0100986 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
987 /* error was already written */
988 return -1;
989 }
990
Radek Krejcid116db42016-01-08 15:36:30 +0100991 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200992}