blob: 80331e4dd401dba85c4e3f6ccd3e7071e808c903 [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 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020013 */
14
Michal Vasko11d142a2016-01-19 15:58:24 +010015#define _GNU_SOURCE /* asprintf, ppoll */
16#define _POSIX_SOUCE /* signals */
Radek Krejci206fcd62015-10-07 15:42:48 +020017#include <assert.h>
18#include <errno.h>
19#include <poll.h>
Michal Vasko3512e402016-01-28 16:22:34 +010020#include <inttypes.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020021#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020022#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010025#include <signal.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020026
27#include <libyang/libyang.h>
28
Radek Krejci206fcd62015-10-07 15:42:48 +020029#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020030
Radek Krejcife0b3472015-10-12 13:43:42 +020031#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020032
33static ssize_t
34nc_read(struct nc_session *session, char *buf, size_t count)
35{
36 size_t size = 0;
37 ssize_t r;
38
39 assert(session);
40 assert(buf);
41
Michal Vasko428087d2016-01-14 16:04:28 +010042 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
43 return -1;
44 }
45
Radek Krejci206fcd62015-10-07 15:42:48 +020046 if (!count) {
47 return 0;
48 }
49
Michal Vasko38a7c6c2015-12-04 12:29:20 +010050 switch (session->ti_type) {
51 case NC_TI_NONE:
52 return 0;
53
Radek Krejci206fcd62015-10-07 15:42:48 +020054 case NC_TI_FD:
55 /* read via standard file descriptor */
Michal Vasko428087d2016-01-14 16:04:28 +010056 while (count) {
Radek Krejci206fcd62015-10-07 15:42:48 +020057 r = read(session->ti.fd.in, &(buf[size]), count);
58 if (r < 0) {
Michal Vasko428087d2016-01-14 16:04:28 +010059 if ((errno == EAGAIN) || (errno == EINTR)) {
Radek Krejci206fcd62015-10-07 15:42:48 +020060 usleep(NC_READ_SLEEP);
61 continue;
62 } else {
Michal Vaskod083db62016-01-19 10:31:29 +010063 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
64 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +010065 session->status = NC_STATUS_INVALID;
66 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020067 return -1;
68 }
69 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +010070 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
71 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +010072 session->status = NC_STATUS_INVALID;
73 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +020074 return -1;
75 }
76
77 size = size + r;
78 count = count - r;
79 }
80 break;
81
Radek Krejci53691be2016-02-22 13:58:37 +010082#ifdef NC_ENABLED_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020083 case NC_TI_LIBSSH:
84 /* read via libssh */
Michal Vasko428087d2016-01-14 16:04:28 +010085 while (count) {
Radek Krejci206fcd62015-10-07 15:42:48 +020086 r = ssh_channel_read(session->ti.libssh.channel, &(buf[size]), count, 0);
87 if (r == SSH_AGAIN) {
Michal Vasko428087d2016-01-14 16:04:28 +010088 usleep(NC_READ_SLEEP);
Radek Krejci206fcd62015-10-07 15:42:48 +020089 continue;
90 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +010091 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
92 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +010093 session->status = NC_STATUS_INVALID;
94 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020095 return -1;
96 } else if (r == 0) {
97 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +010098 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +010099 session->status = NC_STATUS_INVALID;
100 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200101 return -1;
102 }
Michal Vasko428087d2016-01-14 16:04:28 +0100103 usleep(NC_READ_SLEEP);
Radek Krejci206fcd62015-10-07 15:42:48 +0200104 continue;
105 }
106
107 size = size + r;
108 count = count - r;
109 }
110 break;
111#endif
112
Radek Krejci53691be2016-02-22 13:58:37 +0100113#ifdef NC_ENABLED_TLS
Radek Krejci206fcd62015-10-07 15:42:48 +0200114 case NC_TI_OPENSSL:
115 /* read via OpenSSL */
Michal Vasko428087d2016-01-14 16:04:28 +0100116 while (count) {
Radek Krejcid0046592015-10-08 12:52:02 +0200117 r = SSL_read(session->ti.tls, &(buf[size]), count);
118 if (r <= 0) {
119 int x;
120 switch (x = SSL_get_error(session->ti.tls, r)) {
121 case SSL_ERROR_WANT_READ:
122 usleep(NC_READ_SLEEP);
123 continue;
124 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100125 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100126 session->status = NC_STATUS_INVALID;
127 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200128 return -1;
129 default:
Michal Vaskod083db62016-01-19 10:31:29 +0100130 ERR("Session %u: reading from the TLS session failed (SSL code %d).", session->id, x);
Michal Vasko428087d2016-01-14 16:04:28 +0100131 session->status = NC_STATUS_INVALID;
132 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200133 return -1;
134 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200135 }
Radek Krejcid0046592015-10-08 12:52:02 +0200136 size = size + r;
137 count = count - r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200138 }
139 break;
140#endif
141 }
142
143 return (ssize_t)size;
144}
145
146static ssize_t
147nc_read_chunk(struct nc_session *session, size_t len, char **chunk)
148{
149 ssize_t r;
150
151 assert(session);
152 assert(chunk);
153
154 if (!len) {
155 return 0;
156 }
157
158 *chunk = malloc ((len + 1) * sizeof **chunk);
159 if (!*chunk) {
160 ERRMEM;
161 return -1;
162 }
163
164 r = nc_read(session, *chunk, len);
165 if (r <= 0) {
166 free(*chunk);
167 return -1;
168 }
169
170 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200171 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200172
173 return r;
174}
175
176static ssize_t
177nc_read_until(struct nc_session *session, const char *endtag, size_t limit, char **result)
178{
179 char *chunk = NULL;
180 size_t size, count = 0, r, len;
181
182 assert(session);
183 assert(endtag);
184
185 if (limit && limit < BUFFERSIZE) {
186 size = limit;
187 } else {
188 size = BUFFERSIZE;
189 }
190 chunk = malloc ((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200191 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200192 ERRMEM;
193 return -1;
194 }
195
196 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100197 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200198 if (limit && count == limit) {
199 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100200 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
201 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200202 return -1;
203 }
204
205 /* resize buffer if needed */
206 if (count == size) {
207 /* get more memory */
208 size = size + BUFFERSIZE;
209 char *tmp = realloc (chunk, (size + 1) * sizeof *tmp);
210 if (!tmp) {
211 ERRMEM;
212 free(chunk);
213 return -1;
214 }
215 chunk = tmp;
216 }
217
218 /* get another character */
219 r = nc_read(session, &(chunk[count]), 1);
220 if (r != 1) {
221 free(chunk);
222 return -1;
223 }
224
225 count++;
226
227 /* check endtag */
228 if (count >= len) {
229 if (!strncmp(endtag, &(chunk[count - len]), len)) {
230 /* endtag found */
231 break;
232 }
233 }
234 }
235
236 /* terminating null byte */
237 chunk[count] = 0;
238
239 if (result) {
240 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200241 } else {
242 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200243 }
244 return count;
245}
246
Michal Vasko428087d2016-01-14 16:04:28 +0100247/* return NC_MSG_ERROR can change session status */
Radek Krejci206fcd62015-10-07 15:42:48 +0200248NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100249nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
250{
251 int ret;
252 char *msg = NULL, *chunk, *aux;
253 uint64_t chunk_len, len = 0;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100254 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100255
Michal Vasko428087d2016-01-14 16:04:28 +0100256 assert(session && data);
257 *data = NULL;
258
259 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100260 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100261 return NC_MSG_ERROR;
262 }
263
Michal Vasko05ba9df2016-01-13 14:40:27 +0100264 /* read the message */
265 switch (session->version) {
266 case NC_VERSION_10:
267 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &msg);
268 if (ret == -1) {
269 goto error;
270 }
271
272 /* cut off the end tag */
273 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
274 break;
275 case NC_VERSION_11:
276 while (1) {
277 ret = nc_read_until(session, "\n#", 0, NULL);
278 if (ret == -1) {
279 goto error;
280 }
281 ret = nc_read_until(session, "\n", 0, &chunk);
282 if (ret == -1) {
283 goto error;
284 }
285
286 if (!strcmp(chunk, "#\n")) {
287 /* end of chunked framing message */
288 free(chunk);
289 break;
290 }
291
292 /* convert string to the size of the following chunk */
293 chunk_len = strtoul(chunk, (char **)NULL, 10);
294 free(chunk);
295 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100296 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100297 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100298 }
299
300 /* now we have size of next chunk, so read the chunk */
301 ret = nc_read_chunk(session, chunk_len, &chunk);
302 if (ret == -1) {
303 goto error;
304 }
305
306 /* realloc message buffer, remember to count terminating null byte */
307 aux = realloc(msg, len + chunk_len + 1);
308 if (!aux) {
309 ERRMEM;
310 goto error;
311 }
312 msg = aux;
313 memcpy(msg + len, chunk, chunk_len);
314 len += chunk_len;
315 msg[len] = '\0';
316 free(chunk);
317 }
318
319 break;
320 }
Michal Vaskod083db62016-01-19 10:31:29 +0100321 DBG("Session %u: received message:\n%s", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100322
323 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100324 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100325 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100326 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100328 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100329 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100330 }
331 free(msg);
332 msg = NULL;
333
334 /* get and return message type */
335 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
336 if (!strcmp((*data)->name, "rpc")) {
337 return NC_MSG_RPC;
338 } else if (!strcmp((*data)->name, "rpc-reply")) {
339 return NC_MSG_REPLY;
340 } else if (!strcmp((*data)->name, "hello")) {
341 return NC_MSG_HELLO;
342 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100343 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100344 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100345 }
346 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
347 if (!strcmp((*data)->name, "notification")) {
348 return NC_MSG_NOTIF;
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 {
Michal Vaskod083db62016-01-19 10:31:29 +0100354 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100355 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100356 }
357
Michal Vasko428087d2016-01-14 16:04:28 +0100358malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100359 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100360 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
361 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100362 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100363
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100364 if (nc_write_msg(session, NC_MSG_REPLY, NULL, reply) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100365 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100366 if (session->status != NC_STATUS_INVALID) {
367 session->status = NC_STATUS_INVALID;
368 session->term_reason = NC_SESSION_TERM_OTHER;
369 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100370 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100371 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100372 }
373
Michal Vasko428087d2016-01-14 16:04:28 +0100374error:
375 /* cleanup */
376 free(msg);
377 free(*data);
378 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379
380 return NC_MSG_ERROR;
381}
382
Michal Vasko428087d2016-01-14 16:04:28 +0100383/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
384static int
385nc_read_poll(struct nc_session *session, int timeout)
386{
Michal Vasko11d142a2016-01-19 15:58:24 +0100387 sigset_t sigmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100388 int ret = -2;
389 struct pollfd fds;
Michal Vasko11d142a2016-01-19 15:58:24 +0100390 struct timespec ts_timeout;
Michal Vasko428087d2016-01-14 16:04:28 +0100391
392 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100393 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100394 return -1;
395 }
396
397 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100398#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100399 case NC_TI_LIBSSH:
400 /* EINTR is handled, it resumes waiting */
401 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
402 if (ret == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100403 ERR("Session %u: polling on the SSH channel failed (%s).", session->id,
404 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100405 session->status = NC_STATUS_INVALID;
406 session->term_reason = NC_SESSION_TERM_OTHER;
407 return -1;
408 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100409 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100410 session->status = NC_STATUS_INVALID;
411 session->term_reason = NC_SESSION_TERM_DROPPED;
412 return -1;
413 } else if (ret > 0) {
414 /* fake it */
415 ret = 1;
416 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100417 } else { /* ret == 0 */
418 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100419 }
420 /* fallthrough */
421#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100422#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100423 case NC_TI_OPENSSL:
424 if (session->ti_type == NC_TI_OPENSSL) {
425 fds.fd = SSL_get_fd(session->ti.tls);
426 }
427 /* fallthrough */
428#endif
429 case NC_TI_FD:
430 if (session->ti_type == NC_TI_FD) {
431 fds.fd = session->ti.fd.in;
432 }
433
434 /* poll only if it is not an SSH session */
435 if (ret == -2) {
436 fds.events = POLLIN;
Michal Vasko11d142a2016-01-19 15:58:24 +0100437 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100438
Michal Vasko11d142a2016-01-19 15:58:24 +0100439 if (timeout > -1) {
Michal Vasko085ff562016-01-20 10:07:30 +0100440 if (!timeout) {
441 ts_timeout.tv_sec = 0;
442 ts_timeout.tv_nsec = 0;
443 } else if (timeout > 0) {
444 ts_timeout.tv_sec = timeout / 1000;
445 ts_timeout.tv_nsec = (timeout % 1000) * 1000000;
Michal Vasko428087d2016-01-14 16:04:28 +0100446 }
447 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100448 sigfillset(&sigmask);
449 ret = ppoll(&fds, 1, (timeout == -1 ? NULL : &ts_timeout), &sigmask);
Michal Vasko428087d2016-01-14 16:04:28 +0100450 }
451
452 break;
453
454 default:
455 ERRINT;
456 return -1;
457 }
458
459 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
460 if (ret < 0) {
461 /* poll failed - something really bad happened, close the session */
Michal Vasko11d142a2016-01-19 15:58:24 +0100462 ERR("Session %u: ppoll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100463 session->status = NC_STATUS_INVALID;
464 session->term_reason = NC_SESSION_TERM_OTHER;
465 return -1;
466 } else { /* status > 0 */
467 /* in case of standard (non-libssh) poll, there still can be an error */
468 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100469 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100470 session->status = NC_STATUS_INVALID;
471 session->term_reason = NC_SESSION_TERM_DROPPED;
472 return -1;
473 }
474 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100475 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100476 session->status = NC_STATUS_INVALID;
477 session->term_reason = NC_SESSION_TERM_OTHER;
478 return -1;
479 }
480 }
481
482 return ret;
483}
484
485/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100486NC_MSG_TYPE
487nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200488{
Michal Vasko428087d2016-01-14 16:04:28 +0100489 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200490
491 assert(data);
492 *data = NULL;
493
Michal Vasko428087d2016-01-14 16:04:28 +0100494 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100495 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100496 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200497 }
498
Michal Vasko428087d2016-01-14 16:04:28 +0100499 ret = nc_read_poll(session, timeout);
500 if (ret == 0) {
501 /* timed out */
502 return NC_MSG_WOULDBLOCK;
503 } else if (ret < 0) {
504 /* poll error, error written */
505 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200506 }
507
Michal Vasko05ba9df2016-01-13 14:40:27 +0100508 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200509}
Radek Krejcife0b3472015-10-12 13:43:42 +0200510
Michal Vasko428087d2016-01-14 16:04:28 +0100511/* does not really log, only fatal errors */
512int
513nc_session_is_connected(struct nc_session *session)
514{
515 int ret;
516 struct pollfd fds;
517
518 switch (session->ti_type) {
519 case NC_TI_FD:
520 fds.fd = session->ti.fd.in;
521 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100522#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100523 case NC_TI_LIBSSH:
524 fds.fd = ssh_get_fd(session->ti.libssh.session);
525 break;
526#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100527#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100528 case NC_TI_OPENSSL:
529 fds.fd = SSL_get_fd(session->ti.tls);
530 break;
531#endif
532 case NC_TI_NONE:
533 ERRINT;
534 return 0;
535 }
536
537 fds.events = POLLIN;
538
539 errno = 0;
540 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
541
542 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100543 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100544 return 0;
545 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
546 return 0;
547 }
548
549 return 1;
550}
551
Radek Krejcife0b3472015-10-12 13:43:42 +0200552#define WRITE_BUFSIZE (2 * BUFFERSIZE)
553struct wclb_arg {
554 struct nc_session *session;
555 char buf[WRITE_BUFSIZE];
556 size_t len;
557};
558
559static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100560nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200561{
Michal Vasko428087d2016-01-14 16:04:28 +0100562 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
563 return -1;
564 }
565
566 /* prevent SIGPIPE this way */
567 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100568 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100569 session->status = NC_STATUS_INVALID;
570 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100571 return -1;
572 }
573
Radek Krejcife0b3472015-10-12 13:43:42 +0200574 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100575 case NC_TI_NONE:
576 return -1;
577
Radek Krejcife0b3472015-10-12 13:43:42 +0200578 case NC_TI_FD:
Michal Vasko086311b2016-01-08 09:53:11 +0100579 return write(session->ti.fd.out, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200580
Radek Krejci53691be2016-02-22 13:58:37 +0100581#ifdef NC_ENABLED_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200582 case NC_TI_LIBSSH:
Michal Vasko454e22b2016-01-21 15:34:08 +0100583 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
584 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
585 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
586 } else {
587 ERR("Session %u: SSH channel unexpected EOF.", session->id);
588 }
589 session->status = NC_STATUS_INVALID;
590 session->term_reason = NC_SESSION_TERM_DROPPED;
591 return -1;
592 }
Michal Vasko086311b2016-01-08 09:53:11 +0100593 return ssh_channel_write(session->ti.libssh.channel, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200594#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100595#ifdef NC_ENABLED_TLS
Radek Krejcife0b3472015-10-12 13:43:42 +0200596 case NC_TI_OPENSSL:
Michal Vasko086311b2016-01-08 09:53:11 +0100597 return SSL_write(session->ti.tls, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200598#endif
599 }
600
601 return -1;
602}
603
Michal Vasko428087d2016-01-14 16:04:28 +0100604static int
605nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100606{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100607 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100608 char chunksize[20];
609
610 if (session->version == NC_VERSION_11) {
611 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100612 ret = nc_write(session, chunksize, strlen(chunksize));
613 if (ret == -1) {
614 return -1;
615 }
Michal Vasko086311b2016-01-08 09:53:11 +0100616 }
Michal Vasko428087d2016-01-14 16:04:28 +0100617
618 c = nc_write(session, buf, count);
619 if (c == -1) {
620 return -1;
621 }
622 ret += c;
623
624 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100625}
626
Radek Krejcife0b3472015-10-12 13:43:42 +0200627static int
Michal Vasko428087d2016-01-14 16:04:28 +0100628nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200629{
Michal Vasko428087d2016-01-14 16:04:28 +0100630 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100631
Michal Vasko428087d2016-01-14 16:04:28 +0100632 if (session->version == NC_VERSION_11) {
633 ret = nc_write(session, "\n##\n", 4);
634 } else {
635 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200636 }
637
Michal Vasko428087d2016-01-14 16:04:28 +0100638 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200639}
640
Michal Vasko428087d2016-01-14 16:04:28 +0100641static int
642nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200643{
Michal Vasko428087d2016-01-14 16:04:28 +0100644 int ret = 0;
645
Radek Krejcife0b3472015-10-12 13:43:42 +0200646 /* flush current buffer */
647 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100648 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200649 warg->len = 0;
650 }
Michal Vasko428087d2016-01-14 16:04:28 +0100651
652 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200653}
654
655static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100656nc_write_clb(void *arg, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200657{
Michal Vasko428087d2016-01-14 16:04:28 +0100658 int ret = 0, c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200659 struct wclb_arg *warg = (struct wclb_arg *)arg;
660
661 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100662 c = nc_write_clb_flush(warg);
663 if (c == -1) {
664 return -1;
665 }
666 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200667
668 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100669 c = nc_write_endtag(warg->session);
670 if (c == -1) {
671 return -1;
672 }
673 ret += c;
674
675 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200676 }
677
678 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
679 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100680 c = nc_write_clb_flush(warg);
681 if (c == -1) {
682 return -1;
683 }
684 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200685 }
Michal Vasko428087d2016-01-14 16:04:28 +0100686
Radek Krejcife0b3472015-10-12 13:43:42 +0200687 if (count > WRITE_BUFSIZE) {
688 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100689 c = nc_write_starttag_and_msg(warg->session, buf, count);
690 if (c == -1) {
691 return -1;
692 }
693 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200694 } else {
695 /* keep in buffer and write later */
696 memcpy(&warg->buf[warg->len], buf, count);
697 warg->len += count; /* is <= WRITE_BUFSIZE */
Michal Vasko428087d2016-01-14 16:04:28 +0100698 ret += count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200699 }
700
Michal Vasko428087d2016-01-14 16:04:28 +0100701 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200702}
703
Michal Vasko05ba9df2016-01-13 14:40:27 +0100704static void
Michal Vasko428087d2016-01-14 16:04:28 +0100705nc_write_error(struct wclb_arg *arg, struct nc_server_error *err)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100706{
707 uint16_t i;
708 char str_sid[11];
709
Michal Vasko428087d2016-01-14 16:04:28 +0100710 nc_write_clb((void *)arg, "<rpc-error>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100711
Michal Vasko428087d2016-01-14 16:04:28 +0100712 nc_write_clb((void *)arg, "<error-type>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100713 switch (err->type) {
714 case NC_ERR_TYPE_TRAN:
Michal Vasko428087d2016-01-14 16:04:28 +0100715 nc_write_clb((void *)arg, "transport", 9);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100716 break;
717 case NC_ERR_TYPE_RPC:
Michal Vasko428087d2016-01-14 16:04:28 +0100718 nc_write_clb((void *)arg, "rpc", 3);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100719 break;
720 case NC_ERR_TYPE_PROT:
Michal Vasko428087d2016-01-14 16:04:28 +0100721 nc_write_clb((void *)arg, "protocol", 8);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100722 break;
723 case NC_ERR_TYPE_APP:
Michal Vasko428087d2016-01-14 16:04:28 +0100724 nc_write_clb((void *)arg, "application", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100725 break;
726 default:
727 ERRINT;
728 return;
729 }
Michal Vasko428087d2016-01-14 16:04:28 +0100730 nc_write_clb((void *)arg, "</error-type>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100731
Michal Vasko428087d2016-01-14 16:04:28 +0100732 nc_write_clb((void *)arg, "<error-tag>", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100733 switch (err->tag) {
734 case NC_ERR_IN_USE:
Michal Vasko428087d2016-01-14 16:04:28 +0100735 nc_write_clb((void *)arg, "in-use", 6);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100736 break;
737 case NC_ERR_INVALID_VALUE:
Michal Vasko428087d2016-01-14 16:04:28 +0100738 nc_write_clb((void *)arg, "invalid-value", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100739 break;
740 case NC_ERR_TOO_BIG:
Michal Vasko428087d2016-01-14 16:04:28 +0100741 nc_write_clb((void *)arg, "too-big", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100742 break;
743 case NC_ERR_MISSING_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100744 nc_write_clb((void *)arg, "missing-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100745 break;
746 case NC_ERR_BAD_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100747 nc_write_clb((void *)arg, "bad-attribute", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100748 break;
749 case NC_ERR_UNKNOWN_ATTR:
Michal Vasko428087d2016-01-14 16:04:28 +0100750 nc_write_clb((void *)arg, "unknown-attribute", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100751 break;
752 case NC_ERR_MISSING_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100753 nc_write_clb((void *)arg, "missing-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100754 break;
755 case NC_ERR_BAD_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100756 nc_write_clb((void *)arg, "bad-element", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100757 break;
758 case NC_ERR_UNKNOWN_ELEM:
Michal Vasko428087d2016-01-14 16:04:28 +0100759 nc_write_clb((void *)arg, "unknown-element", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100760 break;
761 case NC_ERR_UNKNOWN_NS:
Michal Vasko428087d2016-01-14 16:04:28 +0100762 nc_write_clb((void *)arg, "unknown-namespace", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100763 break;
764 case NC_ERR_ACCESS_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100765 nc_write_clb((void *)arg, "access-denied", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100766 break;
767 case NC_ERR_LOCK_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100768 nc_write_clb((void *)arg, "lock-denied", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100769 break;
770 case NC_ERR_RES_DENIED:
Michal Vasko428087d2016-01-14 16:04:28 +0100771 nc_write_clb((void *)arg, "resource-denied", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100772 break;
773 case NC_ERR_ROLLBACK_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100774 nc_write_clb((void *)arg, "rollback-failed", 15);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100775 break;
776 case NC_ERR_DATA_EXISTS:
Michal Vasko428087d2016-01-14 16:04:28 +0100777 nc_write_clb((void *)arg, "data-exists", 11);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100778 break;
779 case NC_ERR_DATA_MISSING:
Michal Vasko428087d2016-01-14 16:04:28 +0100780 nc_write_clb((void *)arg, "data-missing", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100781 break;
782 case NC_ERR_OP_NOT_SUPPORTED:
Michal Vasko428087d2016-01-14 16:04:28 +0100783 nc_write_clb((void *)arg, "operation-not-supported", 23);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100784 break;
785 case NC_ERR_OP_FAILED:
Michal Vasko428087d2016-01-14 16:04:28 +0100786 nc_write_clb((void *)arg, "operation-failed", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100787 break;
788 case NC_ERR_MALFORMED_MSG:
Michal Vasko428087d2016-01-14 16:04:28 +0100789 nc_write_clb((void *)arg, "malformed-message", 17);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100790 break;
791 default:
792 ERRINT;
793 return;
794 }
Michal Vasko428087d2016-01-14 16:04:28 +0100795 nc_write_clb((void *)arg, "</error-tag>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100796
Michal Vasko428087d2016-01-14 16:04:28 +0100797 nc_write_clb((void *)arg, "<error-severity>error</error-severity>", 38);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100798
799 if (err->apptag) {
Michal Vasko428087d2016-01-14 16:04:28 +0100800 nc_write_clb((void *)arg, "<error-app-tag>", 15);
801 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag));
802 nc_write_clb((void *)arg, "</error-app-tag>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100803 }
804
805 if (err->path) {
Michal Vasko428087d2016-01-14 16:04:28 +0100806 nc_write_clb((void *)arg, "<error-path>", 12);
807 nc_write_clb((void *)arg, err->path, strlen(err->path));
808 nc_write_clb((void *)arg, "</error-path>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100809 }
810
811 if (err->message) {
Michal Vasko428087d2016-01-14 16:04:28 +0100812 nc_write_clb((void *)arg, "<error-message", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100813 if (err->message_lang) {
Michal Vasko428087d2016-01-14 16:04:28 +0100814 nc_write_clb((void *)arg, " xml:lang=\"", 11);
815 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang));
816 nc_write_clb((void *)arg, "\"", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100817 }
Michal Vasko428087d2016-01-14 16:04:28 +0100818 nc_write_clb((void *)arg, ">", 1);
819 nc_write_clb((void *)arg, err->message, strlen(err->message));
820 nc_write_clb((void *)arg, "</error-message>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100821 }
822
Michal Vasko11d142a2016-01-19 15:58:24 +0100823 if (err->sid || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko428087d2016-01-14 16:04:28 +0100824 nc_write_clb((void *)arg, "<error-info>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100825
826 if (err->sid) {
Michal Vasko428087d2016-01-14 16:04:28 +0100827 nc_write_clb((void *)arg, "<session-id>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100828 sprintf(str_sid, "%u", err->sid);
Michal Vasko428087d2016-01-14 16:04:28 +0100829 nc_write_clb((void *)arg, str_sid, strlen(str_sid));
830 nc_write_clb((void *)arg, "</session-id>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100831 }
832
833 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100834 nc_write_clb((void *)arg, "<bad-attribute>", 15);
835 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]));
836 nc_write_clb((void *)arg, "</bad-attribute>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100837 }
838
839 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100840 nc_write_clb((void *)arg, "<bad-element>", 13);
841 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]));
842 nc_write_clb((void *)arg, "</bad-element>", 14);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100843 }
844
845 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100846 nc_write_clb((void *)arg, "<bad-namespace>", 15);
847 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]));
848 nc_write_clb((void *)arg, "</bad-namespace>", 16);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100849 }
850
851 for (i = 0; i < err->other_count; ++i) {
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100852 lyxml_print_clb(nc_write_clb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100853 }
854
Michal Vasko428087d2016-01-14 16:04:28 +0100855 nc_write_clb((void *)arg, "</error-info>", 13);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100856 }
857
Michal Vasko428087d2016-01-14 16:04:28 +0100858 nc_write_clb((void *)arg, "</rpc-error>", 12);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100859}
860
Michal Vasko428087d2016-01-14 16:04:28 +0100861/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100862int
863nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200864{
Radek Krejcid116db42016-01-08 15:36:30 +0100865 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100866 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200867 const char *attrs;
868 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100869 struct lyxml_elem *rpc_elem;
870 struct nc_server_reply *reply;
871 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100872 char *buf = NULL;
873 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200874 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100875 uint32_t *sid = NULL, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200876
Michal Vasko428087d2016-01-14 16:04:28 +0100877 assert(session);
878
879 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100880 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100881 return -1;
882 }
883
Radek Krejcid116db42016-01-08 15:36:30 +0100884 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200885
886 arg.session = session;
887 arg.len = 0;
888
889 switch (type) {
890 case NC_MSG_RPC:
891 content = va_arg(ap, struct lyd_node *);
892 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100893
Radek Krejcife0b3472015-10-12 13:43:42 +0200894 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
895 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
Michal Vasko428087d2016-01-14 16:04:28 +0100896 nc_write_clb((void *)&arg, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200897 free(buf);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100898 lyd_print_clb(nc_write_clb, (void *)&arg, content, LYD_XML, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100899 nc_write_clb((void *)&arg, "</rpc>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200900
901 session->msgid++;
902 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100903
Radek Krejcife0b3472015-10-12 13:43:42 +0200904 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +0100905 rpc_elem = va_arg(ap, struct lyxml_elem *);
906 reply = va_arg(ap, struct nc_server_reply *);
907
Michal Vasko428087d2016-01-14 16:04:28 +0100908 nc_write_clb((void *)&arg, "<rpc-reply", 10);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100909 /* can be NULL if replying with a malformed-message error */
910 if (rpc_elem) {
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100911 lyxml_print_clb(nc_write_clb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100912 }
Michal Vasko428087d2016-01-14 16:04:28 +0100913 nc_write_clb((void *)&arg, ">", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100914 switch (reply->type) {
915 case NC_RPL_OK:
Michal Vasko428087d2016-01-14 16:04:28 +0100916 nc_write_clb((void *)&arg, "<ok/>", 5);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100917 break;
918 case NC_RPL_DATA:
Michal Vasko428087d2016-01-14 16:04:28 +0100919 nc_write_clb((void *)&arg, "<data>", 6);
Michal Vasko0473c4c2016-01-19 10:40:06 +0100920 lyd_print_clb(nc_write_clb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML, 0);
Michal Vasko82b06292016-02-01 10:07:40 +0100921 nc_write_clb((void *)&arg, "</data>", 7);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100922 break;
923 case NC_RPL_ERROR:
924 error_rpl = (struct nc_server_reply_error *)reply;
925 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100926 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100927 }
928 break;
929 default:
930 ERRINT;
Michal Vasko428087d2016-01-14 16:04:28 +0100931 nc_write_clb((void *)&arg, NULL, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100932 va_end(ap);
933 return -1;
934 }
Michal Vasko428087d2016-01-14 16:04:28 +0100935 nc_write_clb((void *)&arg, "</rpc-reply>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200936 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100937
Radek Krejcife0b3472015-10-12 13:43:42 +0200938 case NC_MSG_NOTIF:
Michal Vasko428087d2016-01-14 16:04:28 +0100939 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
Radek Krejcife0b3472015-10-12 13:43:42 +0200940 /* TODO content */
Michal Vasko428087d2016-01-14 16:04:28 +0100941 nc_write_clb((void *)&arg, "</notification>", 12);
Radek Krejcife0b3472015-10-12 13:43:42 +0200942 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100943
Radek Krejcid116db42016-01-08 15:36:30 +0100944 case NC_MSG_HELLO:
945 if (session->version != NC_VERSION_10) {
946 va_end(ap);
947 return -1;
948 }
949 capabilities = va_arg(ap, const char **);
950 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100951
Radek Krejcid116db42016-01-08 15:36:30 +0100952 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko428087d2016-01-14 16:04:28 +0100953 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100954 free(buf);
955 for (i = 0; capabilities[i]; i++) {
956 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
Michal Vasko428087d2016-01-14 16:04:28 +0100957 nc_write_clb((void *)&arg, buf, count);
Radek Krejcid116db42016-01-08 15:36:30 +0100958 free(buf);
959 }
960 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100961 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
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 } else {
Michal Vasko428087d2016-01-14 16:04:28 +0100965 nc_write_clb((void *)&arg, "</capabilities></hello>", 23);
Radek Krejcid116db42016-01-08 15:36:30 +0100966 }
Radek Krejcid116db42016-01-08 15:36:30 +0100967 break;
Michal Vaskoed462342016-01-12 12:33:48 +0100968
Radek Krejcife0b3472015-10-12 13:43:42 +0200969 default:
Radek Krejcid116db42016-01-08 15:36:30 +0100970 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +0200971 return -1;
972 }
973
974 /* flush message */
Michal Vasko428087d2016-01-14 16:04:28 +0100975 nc_write_clb((void *)&arg, NULL, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200976
977 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +0100978 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
979 /* error was already written */
980 return -1;
981 }
982
Radek Krejcid116db42016-01-08 15:36:30 +0100983 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200984}