blob: 662f9e394bec216c313e93143bc92f12492f23c5 [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
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * 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
Michal Vasko6b7c42e2016-03-02 15:46:41 +010034nc_read(struct nc_session *session, char *buf, size_t count, uint16_t *read_timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +020035{
36 size_t size = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010037 ssize_t r = -1;
Michal Vasko6b7c42e2016-03-02 15:46:41 +010038 uint16_t sleep_count = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +020039
40 assert(session);
41 assert(buf);
42
Michal Vasko428087d2016-01-14 16:04:28 +010043 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
44 return -1;
45 }
46
Radek Krejci206fcd62015-10-07 15:42:48 +020047 if (!count) {
48 return 0;
49 }
50
Michal Vasko6b7c42e2016-03-02 15:46:41 +010051 while (count) {
52 switch (session->ti_type) {
53 case NC_TI_NONE:
54 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010055
Michal Vasko6b7c42e2016-03-02 15:46:41 +010056 case NC_TI_FD:
57 /* read via standard file descriptor */
Radek Krejci206fcd62015-10-07 15:42:48 +020058 r = read(session->ti.fd.in, &(buf[size]), count);
59 if (r < 0) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010060 if (errno == EAGAIN) {
61 r = 0;
62 break;
63 } else if (errno == EINTR) {
64 usleep(NC_TIMEOUT_STEP);
Radek Krejci206fcd62015-10-07 15:42:48 +020065 continue;
66 } else {
Michal Vaskod083db62016-01-19 10:31:29 +010067 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
68 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +010069 session->status = NC_STATUS_INVALID;
70 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020071 return -1;
72 }
73 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +010074 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
75 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +010076 session->status = NC_STATUS_INVALID;
77 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +020078 return -1;
79 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +010080 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020081
Radek Krejci53691be2016-02-22 13:58:37 +010082#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +010083 case NC_TI_LIBSSH:
84 /* read via libssh */
Radek Krejci206fcd62015-10-07 15:42:48 +020085 r = ssh_channel_read(session->ti.libssh.channel, &(buf[size]), count, 0);
86 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010087 r = 0;
88 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020089 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +010090 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
91 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +010092 session->status = NC_STATUS_INVALID;
93 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020094 return -1;
95 } else if (r == 0) {
96 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +010097 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +010098 session->status = NC_STATUS_INVALID;
99 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200100 return -1;
101 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100102 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200103 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100104 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200105#endif
106
Radek Krejci53691be2016-02-22 13:58:37 +0100107#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100108 case NC_TI_OPENSSL:
109 /* read via OpenSSL */
Radek Krejcid0046592015-10-08 12:52:02 +0200110 r = SSL_read(session->ti.tls, &(buf[size]), count);
111 if (r <= 0) {
112 int x;
113 switch (x = SSL_get_error(session->ti.tls, r)) {
114 case SSL_ERROR_WANT_READ:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100115 r = 0;
116 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200117 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100118 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100119 session->status = NC_STATUS_INVALID;
120 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200121 return -1;
122 default:
Michal Vaskod083db62016-01-19 10:31:29 +0100123 ERR("Session %u: reading from the TLS session failed (SSL code %d).", session->id, x);
Michal Vasko428087d2016-01-14 16:04:28 +0100124 session->status = NC_STATUS_INVALID;
125 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200126 return -1;
127 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200128 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100129 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200130#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100131 }
132
133 /* nothing read */
134 if (r == 0) {
135 usleep(NC_TIMEOUT_STEP);
136 ++sleep_count;
137 if (1000000 / NC_TIMEOUT_STEP == sleep_count) {
138 /* we slept a full second */
139 --(*read_timeout);
140 sleep_count = 0;
141 }
142 if (!*read_timeout) {
143 ERR("Session %u: reading a full NETCONF message timeout elapsed.", session->id);
144 session->status = NC_STATUS_INVALID;
145 session->term_reason = NC_SESSION_TERM_OTHER;
146 return -1;
147 }
148 continue;
149 }
150
151 size += r;
152 count -= r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200153 }
154
155 return (ssize_t)size;
156}
157
158static ssize_t
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100159nc_read_chunk(struct nc_session *session, size_t len, uint16_t *read_timeout, char **chunk)
Radek Krejci206fcd62015-10-07 15:42:48 +0200160{
161 ssize_t r;
162
163 assert(session);
164 assert(chunk);
165
166 if (!len) {
167 return 0;
168 }
169
Michal Vasko4eb3c312016-03-01 14:09:37 +0100170 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200171 if (!*chunk) {
172 ERRMEM;
173 return -1;
174 }
175
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100176 r = nc_read(session, *chunk, len, read_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200177 if (r <= 0) {
178 free(*chunk);
179 return -1;
180 }
181
182 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200183 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200184
185 return r;
186}
187
188static ssize_t
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100189nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint16_t *read_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200190{
191 char *chunk = NULL;
192 size_t size, count = 0, r, len;
193
194 assert(session);
195 assert(endtag);
196
197 if (limit && limit < BUFFERSIZE) {
198 size = limit;
199 } else {
200 size = BUFFERSIZE;
201 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100202 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200203 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200204 ERRMEM;
205 return -1;
206 }
207
208 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100209 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200210 if (limit && count == limit) {
211 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100212 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
213 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200214 return -1;
215 }
216
217 /* resize buffer if needed */
218 if (count == size) {
219 /* get more memory */
220 size = size + BUFFERSIZE;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100221 chunk = realloc(chunk, (size + 1) * sizeof *chunk);
222 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200223 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200224 return -1;
225 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200226 }
227
228 /* get another character */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100229 r = nc_read(session, &(chunk[count]), 1, read_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200230 if (r != 1) {
231 free(chunk);
232 return -1;
233 }
234
235 count++;
236
237 /* check endtag */
238 if (count >= len) {
239 if (!strncmp(endtag, &(chunk[count - len]), len)) {
240 /* endtag found */
241 break;
242 }
243 }
244 }
245
246 /* terminating null byte */
247 chunk[count] = 0;
248
249 if (result) {
250 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200251 } else {
252 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200253 }
254 return count;
255}
256
Michal Vasko428087d2016-01-14 16:04:28 +0100257/* return NC_MSG_ERROR can change session status */
Radek Krejci206fcd62015-10-07 15:42:48 +0200258NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100259nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
260{
261 int ret;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100262 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100263 uint64_t chunk_len, len = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100264 uint16_t read_timeout = NC_READ_TIMEOUT;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100265 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100266
Michal Vasko428087d2016-01-14 16:04:28 +0100267 assert(session && data);
268 *data = NULL;
269
270 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100271 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100272 return NC_MSG_ERROR;
273 }
274
Michal Vasko05ba9df2016-01-13 14:40:27 +0100275 /* read the message */
276 switch (session->version) {
277 case NC_VERSION_10:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100278 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &read_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100279 if (ret == -1) {
280 goto error;
281 }
282
283 /* cut off the end tag */
284 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
285 break;
286 case NC_VERSION_11:
287 while (1) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100288 ret = nc_read_until(session, "\n#", 0, &read_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100289 if (ret == -1) {
290 goto error;
291 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100292 ret = nc_read_until(session, "\n", 0, &read_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100293 if (ret == -1) {
294 goto error;
295 }
296
297 if (!strcmp(chunk, "#\n")) {
298 /* end of chunked framing message */
299 free(chunk);
300 break;
301 }
302
303 /* convert string to the size of the following chunk */
304 chunk_len = strtoul(chunk, (char **)NULL, 10);
305 free(chunk);
306 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100307 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100308 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100309 }
310
311 /* now we have size of next chunk, so read the chunk */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100312 ret = nc_read_chunk(session, chunk_len, &read_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100313 if (ret == -1) {
314 goto error;
315 }
316
317 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko4eb3c312016-03-01 14:09:37 +0100318 msg = realloc(msg, len + chunk_len + 1);
319 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100320 ERRMEM;
321 goto error;
322 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100323 memcpy(msg + len, chunk, chunk_len);
324 len += chunk_len;
325 msg[len] = '\0';
326 free(chunk);
327 }
328
329 break;
330 }
Michal Vaskod083db62016-01-19 10:31:29 +0100331 DBG("Session %u: received message:\n%s", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100332
333 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100334 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100335 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100336 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100337 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100338 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100339 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100340 }
341 free(msg);
342 msg = NULL;
343
344 /* get and return message type */
345 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
346 if (!strcmp((*data)->name, "rpc")) {
347 return NC_MSG_RPC;
348 } else if (!strcmp((*data)->name, "rpc-reply")) {
349 return NC_MSG_REPLY;
350 } else if (!strcmp((*data)->name, "hello")) {
351 return NC_MSG_HELLO;
352 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100353 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100354 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100355 }
356 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
357 if (!strcmp((*data)->name, "notification")) {
358 return NC_MSG_NOTIF;
359 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100360 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100361 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100362 }
363 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100364 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100365 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100366 }
367
Michal Vasko428087d2016-01-14 16:04:28 +0100368malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100369 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100370 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
371 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100372 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100373
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100374 if (nc_write_msg(session, NC_MSG_REPLY, NULL, reply) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100375 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100376 if (session->status != NC_STATUS_INVALID) {
377 session->status = NC_STATUS_INVALID;
378 session->term_reason = NC_SESSION_TERM_OTHER;
379 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100380 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100381 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100382 }
383
Michal Vasko428087d2016-01-14 16:04:28 +0100384error:
385 /* cleanup */
386 free(msg);
387 free(*data);
388 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389
390 return NC_MSG_ERROR;
391}
392
Michal Vasko428087d2016-01-14 16:04:28 +0100393/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
394static int
395nc_read_poll(struct nc_session *session, int timeout)
396{
Michal Vasko11d142a2016-01-19 15:58:24 +0100397 sigset_t sigmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100398 int ret = -2;
399 struct pollfd fds;
Michal Vasko11d142a2016-01-19 15:58:24 +0100400 struct timespec ts_timeout;
Michal Vasko428087d2016-01-14 16:04:28 +0100401
402 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100403 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100404 return -1;
405 }
406
407 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100408#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100409 case NC_TI_LIBSSH:
410 /* EINTR is handled, it resumes waiting */
411 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
412 if (ret == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100413 ERR("Session %u: polling on the SSH channel failed (%s).", session->id,
414 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100415 session->status = NC_STATUS_INVALID;
416 session->term_reason = NC_SESSION_TERM_OTHER;
417 return -1;
418 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100419 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100420 session->status = NC_STATUS_INVALID;
421 session->term_reason = NC_SESSION_TERM_DROPPED;
422 return -1;
423 } else if (ret > 0) {
424 /* fake it */
425 ret = 1;
426 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100427 } else { /* ret == 0 */
428 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100429 }
430 /* fallthrough */
431#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100432#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100433 case NC_TI_OPENSSL:
434 if (session->ti_type == NC_TI_OPENSSL) {
435 fds.fd = SSL_get_fd(session->ti.tls);
436 }
437 /* fallthrough */
438#endif
439 case NC_TI_FD:
440 if (session->ti_type == NC_TI_FD) {
441 fds.fd = session->ti.fd.in;
442 }
443
444 /* poll only if it is not an SSH session */
445 if (ret == -2) {
446 fds.events = POLLIN;
Michal Vasko11d142a2016-01-19 15:58:24 +0100447 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100448
Michal Vasko11d142a2016-01-19 15:58:24 +0100449 if (timeout > -1) {
Michal Vasko085ff562016-01-20 10:07:30 +0100450 if (!timeout) {
451 ts_timeout.tv_sec = 0;
452 ts_timeout.tv_nsec = 0;
453 } else if (timeout > 0) {
454 ts_timeout.tv_sec = timeout / 1000;
455 ts_timeout.tv_nsec = (timeout % 1000) * 1000000;
Michal Vasko428087d2016-01-14 16:04:28 +0100456 }
457 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100458 sigfillset(&sigmask);
459 ret = ppoll(&fds, 1, (timeout == -1 ? NULL : &ts_timeout), &sigmask);
Michal Vasko428087d2016-01-14 16:04:28 +0100460 }
461
462 break;
463
464 default:
465 ERRINT;
466 return -1;
467 }
468
469 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
470 if (ret < 0) {
471 /* poll failed - something really bad happened, close the session */
Michal Vasko11d142a2016-01-19 15:58:24 +0100472 ERR("Session %u: ppoll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100473 session->status = NC_STATUS_INVALID;
474 session->term_reason = NC_SESSION_TERM_OTHER;
475 return -1;
476 } else { /* status > 0 */
477 /* in case of standard (non-libssh) poll, there still can be an error */
478 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100479 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100480 session->status = NC_STATUS_INVALID;
481 session->term_reason = NC_SESSION_TERM_DROPPED;
482 return -1;
483 }
484 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100485 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100486 session->status = NC_STATUS_INVALID;
487 session->term_reason = NC_SESSION_TERM_OTHER;
488 return -1;
489 }
490 }
491
492 return ret;
493}
494
495/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100496NC_MSG_TYPE
497nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200498{
Michal Vasko428087d2016-01-14 16:04:28 +0100499 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200500
501 assert(data);
502 *data = NULL;
503
Michal Vasko428087d2016-01-14 16:04:28 +0100504 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100505 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100506 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200507 }
508
Michal Vasko428087d2016-01-14 16:04:28 +0100509 ret = nc_read_poll(session, timeout);
510 if (ret == 0) {
511 /* timed out */
512 return NC_MSG_WOULDBLOCK;
513 } else if (ret < 0) {
514 /* poll error, error written */
515 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200516 }
517
Michal Vasko05ba9df2016-01-13 14:40:27 +0100518 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200519}
Radek Krejcife0b3472015-10-12 13:43:42 +0200520
Michal Vasko428087d2016-01-14 16:04:28 +0100521/* does not really log, only fatal errors */
522int
523nc_session_is_connected(struct nc_session *session)
524{
525 int ret;
526 struct pollfd fds;
527
528 switch (session->ti_type) {
529 case NC_TI_FD:
530 fds.fd = session->ti.fd.in;
531 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100532#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100533 case NC_TI_LIBSSH:
534 fds.fd = ssh_get_fd(session->ti.libssh.session);
535 break;
536#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100537#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100538 case NC_TI_OPENSSL:
539 fds.fd = SSL_get_fd(session->ti.tls);
540 break;
541#endif
542 case NC_TI_NONE:
543 ERRINT;
544 return 0;
545 }
546
547 fds.events = POLLIN;
548
549 errno = 0;
550 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
551
552 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100553 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100554 return 0;
555 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
556 return 0;
557 }
558
559 return 1;
560}
561
Radek Krejcife0b3472015-10-12 13:43:42 +0200562#define WRITE_BUFSIZE (2 * BUFFERSIZE)
563struct wclb_arg {
564 struct nc_session *session;
565 char buf[WRITE_BUFSIZE];
566 size_t len;
567};
568
569static ssize_t
Michal Vasko428087d2016-01-14 16:04:28 +0100570nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200571{
Michal Vasko428087d2016-01-14 16:04:28 +0100572 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
573 return -1;
574 }
575
576 /* prevent SIGPIPE this way */
577 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100578 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100579 session->status = NC_STATUS_INVALID;
580 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100581 return -1;
582 }
583
Radek Krejcife0b3472015-10-12 13:43:42 +0200584 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100585 case NC_TI_NONE:
586 return -1;
587
Radek Krejcife0b3472015-10-12 13:43:42 +0200588 case NC_TI_FD:
Michal Vasko086311b2016-01-08 09:53:11 +0100589 return write(session->ti.fd.out, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200590
Radek Krejci53691be2016-02-22 13:58:37 +0100591#ifdef NC_ENABLED_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200592 case NC_TI_LIBSSH:
Michal Vasko454e22b2016-01-21 15:34:08 +0100593 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
594 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
595 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
596 } else {
597 ERR("Session %u: SSH channel unexpected EOF.", session->id);
598 }
599 session->status = NC_STATUS_INVALID;
600 session->term_reason = NC_SESSION_TERM_DROPPED;
601 return -1;
602 }
Michal Vasko086311b2016-01-08 09:53:11 +0100603 return ssh_channel_write(session->ti.libssh.channel, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200604#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100605#ifdef NC_ENABLED_TLS
Radek Krejcife0b3472015-10-12 13:43:42 +0200606 case NC_TI_OPENSSL:
Michal Vasko086311b2016-01-08 09:53:11 +0100607 return SSL_write(session->ti.tls, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200608#endif
609 }
610
611 return -1;
612}
613
Michal Vasko428087d2016-01-14 16:04:28 +0100614static int
615nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100616{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100617 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100618 char chunksize[20];
619
620 if (session->version == NC_VERSION_11) {
621 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100622 ret = nc_write(session, chunksize, strlen(chunksize));
623 if (ret == -1) {
624 return -1;
625 }
Michal Vasko086311b2016-01-08 09:53:11 +0100626 }
Michal Vasko428087d2016-01-14 16:04:28 +0100627
628 c = nc_write(session, buf, count);
629 if (c == -1) {
630 return -1;
631 }
632 ret += c;
633
634 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100635}
636
Radek Krejcife0b3472015-10-12 13:43:42 +0200637static int
Michal Vasko428087d2016-01-14 16:04:28 +0100638nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200639{
Michal Vasko428087d2016-01-14 16:04:28 +0100640 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100641
Michal Vasko428087d2016-01-14 16:04:28 +0100642 if (session->version == NC_VERSION_11) {
643 ret = nc_write(session, "\n##\n", 4);
644 } else {
645 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200646 }
647
Michal Vasko428087d2016-01-14 16:04:28 +0100648 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200649}
650
Michal Vasko428087d2016-01-14 16:04:28 +0100651static int
652nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200653{
Michal Vasko428087d2016-01-14 16:04:28 +0100654 int ret = 0;
655
Radek Krejcife0b3472015-10-12 13:43:42 +0200656 /* flush current buffer */
657 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100658 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200659 warg->len = 0;
660 }
Michal Vasko428087d2016-01-14 16:04:28 +0100661
662 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200663}
664
665static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100666nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200667{
Michal Vasko428087d2016-01-14 16:04:28 +0100668 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100669 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200670 struct wclb_arg *warg = (struct wclb_arg *)arg;
671
672 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100673 c = nc_write_clb_flush(warg);
674 if (c == -1) {
675 return -1;
676 }
677 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200678
679 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100680 c = nc_write_endtag(warg->session);
681 if (c == -1) {
682 return -1;
683 }
684 ret += c;
685
686 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200687 }
688
689 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
690 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100691 c = nc_write_clb_flush(warg);
692 if (c == -1) {
693 return -1;
694 }
695 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200696 }
Michal Vasko428087d2016-01-14 16:04:28 +0100697
Radek Krejci047300e2016-03-08 16:46:58 +0100698 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200699 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100700 c = nc_write_starttag_and_msg(warg->session, buf, count);
701 if (c == -1) {
702 return -1;
703 }
704 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200705 } else {
706 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100707 if (xmlcontent) {
708 for (l = 0; l < count; l++) {
709 if (warg->len + 5 >= WRITE_BUFSIZE) {
710 /* buffer is full */
711 c = nc_write_clb_flush(warg);
712 if (c == -1) {
713 return -1;
714 }
715 }
716
717 switch (((char *)buf)[l]) {
718 case '&':
719 ret += 5;
720 memcpy(&warg->buf[warg->len], "&amp;", 5);
721 warg->len += 5;
722 break;
723 case '<':
724 ret += 4;
725 memcpy(&warg->buf[warg->len], "&lt;", 4);
726 warg->len += 4;
727 break;
728 case '>':
729 /* not needed, just for readability */
730 ret += 4;
731 memcpy(&warg->buf[warg->len], "&gt;", 4);
732 warg->len += 4;
733 break;
734 default:
735 ret++;
736 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
737 warg->len++;
738 }
739 }
740 } else {
741 memcpy(&warg->buf[warg->len], buf, count);
742 warg->len += count; /* is <= WRITE_BUFSIZE */
743 ret += count;
744 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200745 }
746
Michal Vasko428087d2016-01-14 16:04:28 +0100747 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200748}
749
Radek Krejci047300e2016-03-08 16:46:58 +0100750static ssize_t
751nc_write_xmlclb(void *arg, const void *buf, size_t count)
752{
753 return nc_write_clb(arg, buf, count, 0);
754}
755
Michal Vasko05ba9df2016-01-13 14:40:27 +0100756static void
Michal Vasko428087d2016-01-14 16:04:28 +0100757nc_write_error(struct wclb_arg *arg, struct nc_server_error *err)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100758{
759 uint16_t i;
760 char str_sid[11];
761
Radek Krejci047300e2016-03-08 16:46:58 +0100762 nc_write_clb((void *)arg, "<rpc-error>", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100763
Radek Krejci047300e2016-03-08 16:46:58 +0100764 nc_write_clb((void *)arg, "<error-type>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100765 switch (err->type) {
766 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100767 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100768 break;
769 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100770 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100771 break;
772 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100773 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100774 break;
775 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100776 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100777 break;
778 default:
779 ERRINT;
780 return;
781 }
Radek Krejci047300e2016-03-08 16:46:58 +0100782 nc_write_clb((void *)arg, "</error-type>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100783
Radek Krejci047300e2016-03-08 16:46:58 +0100784 nc_write_clb((void *)arg, "<error-tag>", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100785 switch (err->tag) {
786 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100787 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100788 break;
789 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100790 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100791 break;
792 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100793 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100794 break;
795 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100796 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100797 break;
798 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100799 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100800 break;
801 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100802 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100803 break;
804 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100805 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100806 break;
807 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100808 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100809 break;
810 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100811 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100812 break;
813 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100814 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100815 break;
816 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100817 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100818 break;
819 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100820 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100821 break;
822 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100823 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100824 break;
825 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100826 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100827 break;
828 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100829 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100830 break;
831 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100832 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100833 break;
834 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100835 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100836 break;
837 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100838 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100839 break;
840 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100841 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100842 break;
843 default:
844 ERRINT;
845 return;
846 }
Radek Krejci047300e2016-03-08 16:46:58 +0100847 nc_write_clb((void *)arg, "</error-tag>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100848
Radek Krejci047300e2016-03-08 16:46:58 +0100849 nc_write_clb((void *)arg, "<error-severity>error</error-severity>", 38, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100850
851 if (err->apptag) {
Radek Krejci047300e2016-03-08 16:46:58 +0100852 nc_write_clb((void *)arg, "<error-app-tag>", 15, 0);
853 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
854 nc_write_clb((void *)arg, "</error-app-tag>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100855 }
856
857 if (err->path) {
Radek Krejci047300e2016-03-08 16:46:58 +0100858 nc_write_clb((void *)arg, "<error-path>", 12, 0);
859 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
860 nc_write_clb((void *)arg, "</error-path>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100861 }
862
863 if (err->message) {
Radek Krejci047300e2016-03-08 16:46:58 +0100864 nc_write_clb((void *)arg, "<error-message", 14, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100865 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +0100866 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
867 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
868 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100869 }
Radek Krejci047300e2016-03-08 16:46:58 +0100870 nc_write_clb((void *)arg, ">", 1, 0);
871 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
872 nc_write_clb((void *)arg, "</error-message>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100873 }
874
Michal Vasko90920b02016-05-20 14:07:00 +0200875 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Radek Krejci047300e2016-03-08 16:46:58 +0100876 nc_write_clb((void *)arg, "<error-info>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100877
Michal Vasko90920b02016-05-20 14:07:00 +0200878 if (err->sid > -1) {
Radek Krejci047300e2016-03-08 16:46:58 +0100879 nc_write_clb((void *)arg, "<session-id>", 12, 0);
Michal Vasko90920b02016-05-20 14:07:00 +0200880 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +0100881 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
882 nc_write_clb((void *)arg, "</session-id>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100883 }
884
885 for (i = 0; i < err->attr_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100886 nc_write_clb((void *)arg, "<bad-attribute>", 15, 0);
887 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
888 nc_write_clb((void *)arg, "</bad-attribute>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100889 }
890
891 for (i = 0; i < err->elem_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100892 nc_write_clb((void *)arg, "<bad-element>", 13, 0);
893 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
894 nc_write_clb((void *)arg, "</bad-element>", 14, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100895 }
896
897 for (i = 0; i < err->ns_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100898 nc_write_clb((void *)arg, "<bad-namespace>", 15, 0);
899 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
900 nc_write_clb((void *)arg, "</bad-namespace>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100901 }
902
903 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100904 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100905 }
906
Radek Krejci047300e2016-03-08 16:46:58 +0100907 nc_write_clb((void *)arg, "</error-info>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908 }
909
Radek Krejci047300e2016-03-08 16:46:58 +0100910 nc_write_clb((void *)arg, "</rpc-error>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911}
912
Michal Vasko428087d2016-01-14 16:04:28 +0100913/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100914int
915nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200916{
Radek Krejcid116db42016-01-08 15:36:30 +0100917 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100918 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200919 const char *attrs;
920 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100921 struct lyxml_elem *rpc_elem;
922 struct nc_server_reply *reply;
923 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100924 char *buf = NULL;
925 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200926 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100927 uint32_t *sid = NULL, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200928
Michal Vasko428087d2016-01-14 16:04:28 +0100929 assert(session);
930
931 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100932 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100933 return -1;
934 }
935
Radek Krejcid116db42016-01-08 15:36:30 +0100936 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200937
938 arg.session = session;
939 arg.len = 0;
940
941 switch (type) {
942 case NC_MSG_RPC:
943 content = va_arg(ap, struct lyd_node *);
944 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100945
Radek Krejcife0b3472015-10-12 13:43:42 +0200946 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
947 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +0100948 if (count == -1) {
949 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +0100950 va_end(ap);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100951 return -1;
952 }
Radek Krejci047300e2016-03-08 16:46:58 +0100953 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200954 free(buf);
Radek Krejci047300e2016-03-08 16:46:58 +0100955 lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS);
956 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200957
958 session->msgid++;
959 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100960
Radek Krejcife0b3472015-10-12 13:43:42 +0200961 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +0100962 rpc_elem = va_arg(ap, struct lyxml_elem *);
963 reply = va_arg(ap, struct nc_server_reply *);
964
Radek Krejci047300e2016-03-08 16:46:58 +0100965 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100966 /* can be NULL if replying with a malformed-message error */
967 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +0100968 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +0200969 nc_write_clb((void *)&arg, ">", 1, 0);
970 } else {
971 /* but put there at least the correct namespace */
972 nc_write_clb((void *)&arg, "xmlns=\""NC_NS_BASE"\">", 48, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100973 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100974 switch (reply->type) {
975 case NC_RPL_OK:
Radek Krejci047300e2016-03-08 16:46:58 +0100976 nc_write_clb((void *)&arg, "<ok/>", 5, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100977 break;
978 case NC_RPL_DATA:
Michal Vaskob2583f12016-05-12 11:40:23 +0200979 assert(((struct nc_reply_data *)reply)->data->schema->nodetype == LYS_RPC);
980 lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data->child, LYD_XML, LYP_WITHSIBLINGS);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100981 break;
982 case NC_RPL_ERROR:
983 error_rpl = (struct nc_server_reply_error *)reply;
984 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +0100985 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100986 }
987 break;
988 default:
989 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +0100990 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100991 va_end(ap);
992 return -1;
993 }
Radek Krejci047300e2016-03-08 16:46:58 +0100994 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200995 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100996
Radek Krejcife0b3472015-10-12 13:43:42 +0200997 case NC_MSG_NOTIF:
Radek Krejci047300e2016-03-08 16:46:58 +0100998 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200999 /* TODO content */
Radek Krejci047300e2016-03-08 16:46:58 +01001000 nc_write_clb((void *)&arg, "</notification>", 12, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001001 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001002
Radek Krejcid116db42016-01-08 15:36:30 +01001003 case NC_MSG_HELLO:
1004 if (session->version != NC_VERSION_10) {
1005 va_end(ap);
1006 return -1;
1007 }
1008 capabilities = va_arg(ap, const char **);
1009 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001010
Radek Krejcid116db42016-01-08 15:36:30 +01001011 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001012 if (count == -1) {
1013 ERRMEM;
1014 va_end(ap);
1015 return -1;
1016 }
Radek Krejci047300e2016-03-08 16:46:58 +01001017 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001018 free(buf);
1019 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001020 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1021 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1022 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001023 }
1024 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001025 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001026 if (count == -1) {
1027 ERRMEM;
1028 va_end(ap);
1029 return -1;
1030 }
Radek Krejci047300e2016-03-08 16:46:58 +01001031 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001032 free(buf);
1033 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001034 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001035 }
Radek Krejcid116db42016-01-08 15:36:30 +01001036 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001037
Radek Krejcife0b3472015-10-12 13:43:42 +02001038 default:
Radek Krejcid116db42016-01-08 15:36:30 +01001039 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +02001040 return -1;
1041 }
1042
1043 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001044 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001045
1046 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +01001047 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1048 /* error was already written */
1049 return -1;
1050 }
1051
Radek Krejcid116db42016-01-08 15:36:30 +01001052 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001053}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001054
1055void *
1056nc_realloc(void *ptr, size_t size)
1057{
1058 void *ret;
1059
1060 ret = realloc(ptr, size);
1061 if (!ret) {
1062 free(ptr);
1063 }
1064
1065 return ret;
1066}