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