blob: 979e84b31122cd5682cb372697ba84931af8509c [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
Radek Krejci5961c702016-07-15 09:15:18 +020015#define _GNU_SOURCE /* asprintf */
Michal Vasko11d142a2016-01-19 15:58:24 +010016#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
Michal Vasko964e1732016-09-23 13:39:33 +020027#ifdef NC_ENABLED_TLS
28# include <openssl/err.h>
29#endif
30
Radek Krejci206fcd62015-10-07 15:42:48 +020031#include <libyang/libyang.h>
32
Radek Krejci206fcd62015-10-07 15:42:48 +020033#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020034
Radek Krejcife0b3472015-10-12 13:43:42 +020035#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020036
37static ssize_t
Michal Vasko6b7c42e2016-03-02 15:46:41 +010038nc_read(struct nc_session *session, char *buf, size_t count, uint16_t *read_timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +020039{
Michal Vasko81b33fb2016-09-26 14:57:36 +020040 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010041 ssize_t r = -1;
Michal Vasko6b7c42e2016-03-02 15:46:41 +010042 uint16_t sleep_count = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +020043
44 assert(session);
45 assert(buf);
46
Michal Vasko428087d2016-01-14 16:04:28 +010047 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
48 return -1;
49 }
50
Radek Krejci206fcd62015-10-07 15:42:48 +020051 if (!count) {
52 return 0;
53 }
54
Michal Vasko81b33fb2016-09-26 14:57:36 +020055 do {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010056 switch (session->ti_type) {
57 case NC_TI_NONE:
58 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010059
Michal Vasko6b7c42e2016-03-02 15:46:41 +010060 case NC_TI_FD:
61 /* read via standard file descriptor */
Michal Vasko81b33fb2016-09-26 14:57:36 +020062 r = read(session->ti.fd.in, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +020063 if (r < 0) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010064 if (errno == EAGAIN) {
65 r = 0;
66 break;
67 } else if (errno == EINTR) {
68 usleep(NC_TIMEOUT_STEP);
Radek Krejci206fcd62015-10-07 15:42:48 +020069 continue;
70 } else {
Michal Vaskod083db62016-01-19 10:31:29 +010071 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
72 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +010073 session->status = NC_STATUS_INVALID;
74 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020075 return -1;
76 }
77 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +010078 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
79 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +010080 session->status = NC_STATUS_INVALID;
81 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +020082 return -1;
83 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +010084 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020085
Radek Krejci53691be2016-02-22 13:58:37 +010086#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +010087 case NC_TI_LIBSSH:
88 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +020089 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +020090 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010091 r = 0;
92 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020093 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +010094 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
95 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +010096 session->status = NC_STATUS_INVALID;
97 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020098 return -1;
99 } else if (r == 0) {
100 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +0100101 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100102 session->status = NC_STATUS_INVALID;
103 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200104 return -1;
105 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100106 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200107 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100108 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200109#endif
110
Radek Krejci53691be2016-02-22 13:58:37 +0100111#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100112 case NC_TI_OPENSSL:
113 /* read via OpenSSL */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200114 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200115 if (r <= 0) {
116 int x;
117 switch (x = SSL_get_error(session->ti.tls, r)) {
118 case SSL_ERROR_WANT_READ:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100119 r = 0;
120 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200121 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100122 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100123 session->status = NC_STATUS_INVALID;
124 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200125 return -1;
126 default:
Michal Vaskod083db62016-01-19 10:31:29 +0100127 ERR("Session %u: reading from the TLS session failed (SSL code %d).", session->id, x);
Michal Vasko428087d2016-01-14 16:04:28 +0100128 session->status = NC_STATUS_INVALID;
129 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200130 return -1;
131 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200132 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100133 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200134#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100135 }
136
137 /* nothing read */
138 if (r == 0) {
139 usleep(NC_TIMEOUT_STEP);
140 ++sleep_count;
141 if (1000000 / NC_TIMEOUT_STEP == sleep_count) {
142 /* we slept a full second */
143 --(*read_timeout);
144 sleep_count = 0;
145 }
146 if (!*read_timeout) {
147 ERR("Session %u: reading a full NETCONF message timeout elapsed.", session->id);
148 session->status = NC_STATUS_INVALID;
149 session->term_reason = NC_SESSION_TERM_OTHER;
150 return -1;
151 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100152 }
153
Michal Vasko81b33fb2016-09-26 14:57:36 +0200154 readd += r;
155 } while (readd < count);
156 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200157
Michal Vasko81b33fb2016-09-26 14:57:36 +0200158 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200159}
160
161static ssize_t
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100162nc_read_chunk(struct nc_session *session, size_t len, uint16_t *read_timeout, char **chunk)
Radek Krejci206fcd62015-10-07 15:42:48 +0200163{
164 ssize_t r;
165
166 assert(session);
167 assert(chunk);
168
169 if (!len) {
170 return 0;
171 }
172
Michal Vasko4eb3c312016-03-01 14:09:37 +0100173 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200174 if (!*chunk) {
175 ERRMEM;
176 return -1;
177 }
178
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100179 r = nc_read(session, *chunk, len, read_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200180 if (r <= 0) {
181 free(*chunk);
182 return -1;
183 }
184
185 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200186 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200187
188 return r;
189}
190
191static ssize_t
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100192nc_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 +0200193{
194 char *chunk = NULL;
195 size_t size, count = 0, r, len;
196
197 assert(session);
198 assert(endtag);
199
200 if (limit && limit < BUFFERSIZE) {
201 size = limit;
202 } else {
203 size = BUFFERSIZE;
204 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100205 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200206 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200207 ERRMEM;
208 return -1;
209 }
210
211 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100212 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200213 if (limit && count == limit) {
214 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100215 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
216 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200217 return -1;
218 }
219
220 /* resize buffer if needed */
221 if (count == size) {
222 /* get more memory */
223 size = size + BUFFERSIZE;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100224 chunk = realloc(chunk, (size + 1) * sizeof *chunk);
225 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200226 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200227 return -1;
228 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200229 }
230
231 /* get another character */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100232 r = nc_read(session, &(chunk[count]), 1, read_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200233 if (r != 1) {
234 free(chunk);
235 return -1;
236 }
237
238 count++;
239
240 /* check endtag */
241 if (count >= len) {
242 if (!strncmp(endtag, &(chunk[count - len]), len)) {
243 /* endtag found */
244 break;
245 }
246 }
247 }
248
249 /* terminating null byte */
250 chunk[count] = 0;
251
252 if (result) {
253 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200254 } else {
255 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200256 }
257 return count;
258}
259
Michal Vasko428087d2016-01-14 16:04:28 +0100260/* return NC_MSG_ERROR can change session status */
Radek Krejci206fcd62015-10-07 15:42:48 +0200261NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100262nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
263{
264 int ret;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100265 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100266 uint64_t chunk_len, len = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100267 uint16_t read_timeout = NC_READ_TIMEOUT;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100268 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100269
Michal Vasko428087d2016-01-14 16:04:28 +0100270 assert(session && data);
271 *data = NULL;
272
273 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100274 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100275 return NC_MSG_ERROR;
276 }
277
Michal Vasko05ba9df2016-01-13 14:40:27 +0100278 /* read the message */
279 switch (session->version) {
280 case NC_VERSION_10:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100281 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &read_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100282 if (ret == -1) {
283 goto error;
284 }
285
286 /* cut off the end tag */
287 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
288 break;
289 case NC_VERSION_11:
290 while (1) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100291 ret = nc_read_until(session, "\n#", 0, &read_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100292 if (ret == -1) {
293 goto error;
294 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100295 ret = nc_read_until(session, "\n", 0, &read_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100296 if (ret == -1) {
297 goto error;
298 }
299
300 if (!strcmp(chunk, "#\n")) {
301 /* end of chunked framing message */
302 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200303 if (!msg) {
304 ERR("Session %u: invalid frame chunk delimiters.", session->id);
305 goto malformed_msg;
306 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100307 break;
308 }
309
310 /* convert string to the size of the following chunk */
311 chunk_len = strtoul(chunk, (char **)NULL, 10);
312 free(chunk);
313 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100314 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100315 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100316 }
317
318 /* now we have size of next chunk, so read the chunk */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100319 ret = nc_read_chunk(session, chunk_len, &read_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100320 if (ret == -1) {
321 goto error;
322 }
323
324 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko4eb3c312016-03-01 14:09:37 +0100325 msg = realloc(msg, len + chunk_len + 1);
326 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327 ERRMEM;
328 goto error;
329 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100330 memcpy(msg + len, chunk, chunk_len);
331 len += chunk_len;
332 msg[len] = '\0';
333 free(chunk);
334 }
335
336 break;
337 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200338 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100339
340 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100341 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100342 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100343 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100344 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100345 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100346 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100347 }
348 free(msg);
349 msg = NULL;
350
351 /* get and return message type */
352 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
353 if (!strcmp((*data)->name, "rpc")) {
354 return NC_MSG_RPC;
355 } else if (!strcmp((*data)->name, "rpc-reply")) {
356 return NC_MSG_REPLY;
357 } else if (!strcmp((*data)->name, "hello")) {
358 return NC_MSG_HELLO;
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 if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
364 if (!strcmp((*data)->name, "notification")) {
365 return NC_MSG_NOTIF;
366 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100367 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100368 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100369 }
370 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100371 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100372 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100373 }
374
Michal Vasko428087d2016-01-14 16:04:28 +0100375malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100376 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100377 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
378 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100379 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100380
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100381 if (nc_write_msg(session, NC_MSG_REPLY, NULL, reply) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100382 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100383 if (session->status != NC_STATUS_INVALID) {
384 session->status = NC_STATUS_INVALID;
385 session->term_reason = NC_SESSION_TERM_OTHER;
386 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100387 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100388 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389 }
390
Michal Vasko428087d2016-01-14 16:04:28 +0100391error:
392 /* cleanup */
393 free(msg);
394 free(*data);
395 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100396
397 return NC_MSG_ERROR;
398}
399
Michal Vasko428087d2016-01-14 16:04:28 +0100400/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
401static int
402nc_read_poll(struct nc_session *session, int timeout)
403{
Radek Krejci5961c702016-07-15 09:15:18 +0200404 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100405 int ret = -2;
406 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100407
408 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100409 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100410 return -1;
411 }
412
413 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100414#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100415 case NC_TI_LIBSSH:
416 /* EINTR is handled, it resumes waiting */
417 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
418 if (ret == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100419 ERR("Session %u: polling on the SSH channel failed (%s).", session->id,
420 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100421 session->status = NC_STATUS_INVALID;
422 session->term_reason = NC_SESSION_TERM_OTHER;
423 return -1;
424 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100425 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100426 session->status = NC_STATUS_INVALID;
427 session->term_reason = NC_SESSION_TERM_DROPPED;
428 return -1;
429 } else if (ret > 0) {
430 /* fake it */
431 ret = 1;
432 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100433 } else { /* ret == 0 */
434 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100435 }
436 /* fallthrough */
437#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100438#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100439 case NC_TI_OPENSSL:
440 if (session->ti_type == NC_TI_OPENSSL) {
441 fds.fd = SSL_get_fd(session->ti.tls);
442 }
443 /* fallthrough */
444#endif
445 case NC_TI_FD:
446 if (session->ti_type == NC_TI_FD) {
447 fds.fd = session->ti.fd.in;
448 }
449
450 /* poll only if it is not an SSH session */
451 if (ret == -2) {
452 fds.events = POLLIN;
Michal Vasko11d142a2016-01-19 15:58:24 +0100453 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100454
Michal Vasko11d142a2016-01-19 15:58:24 +0100455 sigfillset(&sigmask);
Radek Krejci5961c702016-07-15 09:15:18 +0200456 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
457 ret = poll(&fds, 1, timeout);
458 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100459 }
460
461 break;
462
463 default:
464 ERRINT;
465 return -1;
466 }
467
468 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
469 if (ret < 0) {
470 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200471 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100472 session->status = NC_STATUS_INVALID;
473 session->term_reason = NC_SESSION_TERM_OTHER;
474 return -1;
475 } else { /* status > 0 */
476 /* in case of standard (non-libssh) poll, there still can be an error */
477 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100478 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100479 session->status = NC_STATUS_INVALID;
480 session->term_reason = NC_SESSION_TERM_DROPPED;
481 return -1;
482 }
483 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100484 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100485 session->status = NC_STATUS_INVALID;
486 session->term_reason = NC_SESSION_TERM_OTHER;
487 return -1;
488 }
489 }
490
491 return ret;
492}
493
494/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100495NC_MSG_TYPE
496nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200497{
Michal Vasko428087d2016-01-14 16:04:28 +0100498 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200499
500 assert(data);
501 *data = NULL;
502
Michal Vasko428087d2016-01-14 16:04:28 +0100503 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100504 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100505 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200506 }
507
Michal Vasko428087d2016-01-14 16:04:28 +0100508 ret = nc_read_poll(session, timeout);
509 if (ret == 0) {
510 /* timed out */
511 return NC_MSG_WOULDBLOCK;
512 } else if (ret < 0) {
513 /* poll error, error written */
514 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200515 }
516
Michal Vasko05ba9df2016-01-13 14:40:27 +0100517 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200518}
Radek Krejcife0b3472015-10-12 13:43:42 +0200519
Michal Vasko428087d2016-01-14 16:04:28 +0100520/* does not really log, only fatal errors */
521int
522nc_session_is_connected(struct nc_session *session)
523{
524 int ret;
525 struct pollfd fds;
526
527 switch (session->ti_type) {
528 case NC_TI_FD:
529 fds.fd = session->ti.fd.in;
530 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100531#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100532 case NC_TI_LIBSSH:
533 fds.fd = ssh_get_fd(session->ti.libssh.session);
534 break;
535#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100536#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100537 case NC_TI_OPENSSL:
538 fds.fd = SSL_get_fd(session->ti.tls);
539 break;
540#endif
541 case NC_TI_NONE:
542 ERRINT;
543 return 0;
544 }
545
546 fds.events = POLLIN;
547
548 errno = 0;
549 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
550
551 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100552 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100553 return 0;
554 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
555 return 0;
556 }
557
558 return 1;
559}
560
Radek Krejcife0b3472015-10-12 13:43:42 +0200561#define WRITE_BUFSIZE (2 * BUFFERSIZE)
562struct wclb_arg {
563 struct nc_session *session;
564 char buf[WRITE_BUFSIZE];
565 size_t len;
566};
567
Michal Vasko964e1732016-09-23 13:39:33 +0200568static int
Michal Vasko428087d2016-01-14 16:04:28 +0100569nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200570{
Michal Vasko964e1732016-09-23 13:39:33 +0200571 int c;
572 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200573#ifdef NC_ENABLED_TLS
574 unsigned long e;
575#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200576
Michal Vasko428087d2016-01-14 16:04:28 +0100577 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
578 return -1;
579 }
580
581 /* prevent SIGPIPE this way */
582 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100583 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100584 session->status = NC_STATUS_INVALID;
585 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100586 return -1;
587 }
588
Michal Vasko81b33fb2016-09-26 14:57:36 +0200589 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200590
Michal Vasko81b33fb2016-09-26 14:57:36 +0200591 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200592 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200593 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200594 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200595 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200596 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200597 return -1;
598 }
599 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200600
Radek Krejci53691be2016-02-22 13:58:37 +0100601#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200602 case NC_TI_LIBSSH:
603 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
604 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
605 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
606 } else {
607 ERR("Session %u: SSH channel unexpected EOF.", session->id);
608 }
609 session->status = NC_STATUS_INVALID;
610 session->term_reason = NC_SESSION_TERM_DROPPED;
611 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100612 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200613 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200614 if ((c == SSH_ERROR) || (c == -1)) {
615 ERR("Session %u: SSH channel write failed.", session->id);
616 return -1;
617 }
618 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200619#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100620#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200621 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200622 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200623 if (c < 1) {
624 switch ((e = SSL_get_error(session->ti.tls, c))) {
625 case SSL_ERROR_ZERO_RETURN:
626 ERR("Session %u: SSL connection was properly closed.", session->id);
627 return -1;
628 case SSL_ERROR_WANT_WRITE:
629 c = 0;
630 break;
631 case SSL_ERROR_SYSCALL:
632 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
633 return -1;
634 case SSL_ERROR_SSL:
635 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
636 return -1;
637 default:
638 ERR("Session %u: unknown SSL error occured.", session->id);
639 return -1;
640 }
641 }
642 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200643#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200644 default:
645 ERRINT;
646 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200647 }
648
649 if (c == 0) {
650 /* we must wait */
651 usleep(NC_TIMEOUT_STEP);
652 }
653
654 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200655 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200656
Michal Vasko964e1732016-09-23 13:39:33 +0200657 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200658}
659
Michal Vasko428087d2016-01-14 16:04:28 +0100660static int
661nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100662{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100663 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100664 char chunksize[20];
665
666 if (session->version == NC_VERSION_11) {
667 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100668 ret = nc_write(session, chunksize, strlen(chunksize));
669 if (ret == -1) {
670 return -1;
671 }
Michal Vasko086311b2016-01-08 09:53:11 +0100672 }
Michal Vasko428087d2016-01-14 16:04:28 +0100673
674 c = nc_write(session, buf, count);
675 if (c == -1) {
676 return -1;
677 }
678 ret += c;
679
680 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100681}
682
Radek Krejcife0b3472015-10-12 13:43:42 +0200683static int
Michal Vasko428087d2016-01-14 16:04:28 +0100684nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200685{
Michal Vasko428087d2016-01-14 16:04:28 +0100686 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100687
Michal Vasko428087d2016-01-14 16:04:28 +0100688 if (session->version == NC_VERSION_11) {
689 ret = nc_write(session, "\n##\n", 4);
690 } else {
691 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200692 }
693
Michal Vasko428087d2016-01-14 16:04:28 +0100694 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200695}
696
Michal Vasko428087d2016-01-14 16:04:28 +0100697static int
698nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200699{
Michal Vasko428087d2016-01-14 16:04:28 +0100700 int ret = 0;
701
Radek Krejcife0b3472015-10-12 13:43:42 +0200702 /* flush current buffer */
703 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100704 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200705 warg->len = 0;
706 }
Michal Vasko428087d2016-01-14 16:04:28 +0100707
708 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200709}
710
711static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100712nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200713{
Michal Vasko428087d2016-01-14 16:04:28 +0100714 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100715 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200716 struct wclb_arg *warg = (struct wclb_arg *)arg;
717
718 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100719 c = nc_write_clb_flush(warg);
720 if (c == -1) {
721 return -1;
722 }
723 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200724
725 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100726 c = nc_write_endtag(warg->session);
727 if (c == -1) {
728 return -1;
729 }
730 ret += c;
731
732 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200733 }
734
735 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
736 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100737 c = nc_write_clb_flush(warg);
738 if (c == -1) {
739 return -1;
740 }
741 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200742 }
Michal Vasko428087d2016-01-14 16:04:28 +0100743
Radek Krejci047300e2016-03-08 16:46:58 +0100744 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200745 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100746 c = nc_write_starttag_and_msg(warg->session, buf, count);
747 if (c == -1) {
748 return -1;
749 }
750 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200751 } else {
752 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100753 if (xmlcontent) {
754 for (l = 0; l < count; l++) {
755 if (warg->len + 5 >= WRITE_BUFSIZE) {
756 /* buffer is full */
757 c = nc_write_clb_flush(warg);
758 if (c == -1) {
759 return -1;
760 }
761 }
762
763 switch (((char *)buf)[l]) {
764 case '&':
765 ret += 5;
766 memcpy(&warg->buf[warg->len], "&amp;", 5);
767 warg->len += 5;
768 break;
769 case '<':
770 ret += 4;
771 memcpy(&warg->buf[warg->len], "&lt;", 4);
772 warg->len += 4;
773 break;
774 case '>':
775 /* not needed, just for readability */
776 ret += 4;
777 memcpy(&warg->buf[warg->len], "&gt;", 4);
778 warg->len += 4;
779 break;
780 default:
781 ret++;
782 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
783 warg->len++;
784 }
785 }
786 } else {
787 memcpy(&warg->buf[warg->len], buf, count);
788 warg->len += count; /* is <= WRITE_BUFSIZE */
789 ret += count;
790 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200791 }
792
Michal Vasko428087d2016-01-14 16:04:28 +0100793 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200794}
795
Radek Krejci047300e2016-03-08 16:46:58 +0100796static ssize_t
797nc_write_xmlclb(void *arg, const void *buf, size_t count)
798{
799 return nc_write_clb(arg, buf, count, 0);
800}
801
Michal Vasko05ba9df2016-01-13 14:40:27 +0100802static void
Michal Vasko428087d2016-01-14 16:04:28 +0100803nc_write_error(struct wclb_arg *arg, struct nc_server_error *err)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100804{
805 uint16_t i;
806 char str_sid[11];
807
Radek Krejci047300e2016-03-08 16:46:58 +0100808 nc_write_clb((void *)arg, "<rpc-error>", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100809
Radek Krejci047300e2016-03-08 16:46:58 +0100810 nc_write_clb((void *)arg, "<error-type>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100811 switch (err->type) {
812 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100813 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100814 break;
815 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100816 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100817 break;
818 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100819 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100820 break;
821 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100822 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100823 break;
824 default:
825 ERRINT;
826 return;
827 }
Radek Krejci047300e2016-03-08 16:46:58 +0100828 nc_write_clb((void *)arg, "</error-type>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100829
Radek Krejci047300e2016-03-08 16:46:58 +0100830 nc_write_clb((void *)arg, "<error-tag>", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100831 switch (err->tag) {
832 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100833 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100834 break;
835 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100836 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100837 break;
838 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100839 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100840 break;
841 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100842 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100843 break;
844 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100845 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100846 break;
847 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100848 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100849 break;
850 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100851 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100852 break;
853 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100854 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100855 break;
856 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100857 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100858 break;
859 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100860 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100861 break;
862 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100863 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100864 break;
865 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100866 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100867 break;
868 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100869 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100870 break;
871 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100872 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100873 break;
874 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100875 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100876 break;
877 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100878 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100879 break;
880 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100881 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100882 break;
883 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100884 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100885 break;
886 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100887 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100888 break;
889 default:
890 ERRINT;
891 return;
892 }
Radek Krejci047300e2016-03-08 16:46:58 +0100893 nc_write_clb((void *)arg, "</error-tag>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100894
Radek Krejci047300e2016-03-08 16:46:58 +0100895 nc_write_clb((void *)arg, "<error-severity>error</error-severity>", 38, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100896
897 if (err->apptag) {
Radek Krejci047300e2016-03-08 16:46:58 +0100898 nc_write_clb((void *)arg, "<error-app-tag>", 15, 0);
899 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
900 nc_write_clb((void *)arg, "</error-app-tag>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100901 }
902
903 if (err->path) {
Radek Krejci047300e2016-03-08 16:46:58 +0100904 nc_write_clb((void *)arg, "<error-path>", 12, 0);
905 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
906 nc_write_clb((void *)arg, "</error-path>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100907 }
908
909 if (err->message) {
Radek Krejci047300e2016-03-08 16:46:58 +0100910 nc_write_clb((void *)arg, "<error-message", 14, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +0100912 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
913 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
914 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100915 }
Radek Krejci047300e2016-03-08 16:46:58 +0100916 nc_write_clb((void *)arg, ">", 1, 0);
917 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
918 nc_write_clb((void *)arg, "</error-message>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100919 }
920
Michal Vasko90920b02016-05-20 14:07:00 +0200921 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Radek Krejci047300e2016-03-08 16:46:58 +0100922 nc_write_clb((void *)arg, "<error-info>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100923
Michal Vasko90920b02016-05-20 14:07:00 +0200924 if (err->sid > -1) {
Radek Krejci047300e2016-03-08 16:46:58 +0100925 nc_write_clb((void *)arg, "<session-id>", 12, 0);
Michal Vasko90920b02016-05-20 14:07:00 +0200926 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +0100927 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
928 nc_write_clb((void *)arg, "</session-id>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100929 }
930
931 for (i = 0; i < err->attr_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100932 nc_write_clb((void *)arg, "<bad-attribute>", 15, 0);
933 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
934 nc_write_clb((void *)arg, "</bad-attribute>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100935 }
936
937 for (i = 0; i < err->elem_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100938 nc_write_clb((void *)arg, "<bad-element>", 13, 0);
939 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
940 nc_write_clb((void *)arg, "</bad-element>", 14, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100941 }
942
943 for (i = 0; i < err->ns_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100944 nc_write_clb((void *)arg, "<bad-namespace>", 15, 0);
945 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
946 nc_write_clb((void *)arg, "</bad-namespace>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100947 }
948
949 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100950 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100951 }
952
Radek Krejci047300e2016-03-08 16:46:58 +0100953 nc_write_clb((void *)arg, "</error-info>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100954 }
955
Radek Krejci047300e2016-03-08 16:46:58 +0100956 nc_write_clb((void *)arg, "</rpc-error>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100957}
958
Michal Vasko428087d2016-01-14 16:04:28 +0100959/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100960int
961nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200962{
Radek Krejcid116db42016-01-08 15:36:30 +0100963 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100964 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200965 const char *attrs;
966 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100967 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +0200968 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100969 struct nc_server_reply *reply;
970 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100971 char *buf = NULL;
972 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200973 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100974 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +0200975 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200976
Michal Vasko428087d2016-01-14 16:04:28 +0100977 assert(session);
978
979 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100980 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100981 return -1;
982 }
983
Radek Krejcid116db42016-01-08 15:36:30 +0100984 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200985
986 arg.session = session;
987 arg.len = 0;
988
989 switch (type) {
990 case NC_MSG_RPC:
991 content = va_arg(ap, struct lyd_node *);
992 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100993
Radek Krejcife0b3472015-10-12 13:43:42 +0200994 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
995 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +0100996 if (count == -1) {
997 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +0100998 va_end(ap);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100999 return -1;
1000 }
Radek Krejci047300e2016-03-08 16:46:58 +01001001 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001002 free(buf);
Radek Krejci047300e2016-03-08 16:46:58 +01001003 lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS);
1004 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001005
1006 session->msgid++;
1007 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001008
Radek Krejcife0b3472015-10-12 13:43:42 +02001009 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001010 rpc_elem = va_arg(ap, struct lyxml_elem *);
1011 reply = va_arg(ap, struct nc_server_reply *);
1012
Radek Krejci047300e2016-03-08 16:46:58 +01001013 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001014 /* can be NULL if replying with a malformed-message error */
1015 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001016 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001017 nc_write_clb((void *)&arg, ">", 1, 0);
1018 } else {
1019 /* but put there at least the correct namespace */
1020 nc_write_clb((void *)&arg, "xmlns=\""NC_NS_BASE"\">", 48, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001021 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001022 switch (reply->type) {
1023 case NC_RPL_OK:
Radek Krejci047300e2016-03-08 16:46:58 +01001024 nc_write_clb((void *)&arg, "<ok/>", 5, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001025 break;
1026 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001027 assert(((struct nc_server_reply_data *)reply)->data->schema->nodetype == LYS_RPC);
1028 switch(((struct nc_server_reply_data *)reply)->wd) {
1029 case NC_WD_UNKNOWN:
1030 case NC_WD_EXPLICIT:
1031 wd = LYP_WD_EXPLICIT;
1032 break;
1033 case NC_WD_TRIM:
1034 wd = LYP_WD_TRIM;
1035 break;
1036 case NC_WD_ALL:
1037 wd = LYP_WD_ALL;
1038 break;
1039 case NC_WD_ALL_TAG:
1040 wd = LYP_WD_ALL_TAG;
1041 break;
1042 }
1043 lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data->child, LYD_XML,
1044 LYP_WITHSIBLINGS | wd);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001045 break;
1046 case NC_RPL_ERROR:
1047 error_rpl = (struct nc_server_reply_error *)reply;
1048 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +01001049 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001050 }
1051 break;
1052 default:
1053 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001054 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001055 va_end(ap);
1056 return -1;
1057 }
Radek Krejci047300e2016-03-08 16:46:58 +01001058 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001059 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001060
Radek Krejcife0b3472015-10-12 13:43:42 +02001061 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001062 notif = va_arg(ap, struct nc_server_notif *);
1063
1064 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1065 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1066 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1067 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
1068 lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001069 nc_write_clb((void *)&arg, "</notification>", 12, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001070 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001071
Radek Krejcid116db42016-01-08 15:36:30 +01001072 case NC_MSG_HELLO:
1073 if (session->version != NC_VERSION_10) {
1074 va_end(ap);
1075 return -1;
1076 }
1077 capabilities = va_arg(ap, const char **);
1078 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001079
Radek Krejcid116db42016-01-08 15:36:30 +01001080 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001081 if (count == -1) {
1082 ERRMEM;
1083 va_end(ap);
1084 return -1;
1085 }
Radek Krejci047300e2016-03-08 16:46:58 +01001086 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001087 free(buf);
1088 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001089 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1090 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1091 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001092 }
1093 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001094 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001095 if (count == -1) {
1096 ERRMEM;
1097 va_end(ap);
1098 return -1;
1099 }
Radek Krejci047300e2016-03-08 16:46:58 +01001100 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001101 free(buf);
1102 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001103 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001104 }
Radek Krejcid116db42016-01-08 15:36:30 +01001105 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001106
Radek Krejcife0b3472015-10-12 13:43:42 +02001107 default:
Radek Krejcid116db42016-01-08 15:36:30 +01001108 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +02001109 return -1;
1110 }
1111
1112 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001113 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001114
1115 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +01001116 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1117 /* error was already written */
1118 return -1;
1119 }
1120
Radek Krejcid116db42016-01-08 15:36:30 +01001121 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001122}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001123
1124void *
1125nc_realloc(void *ptr, size_t size)
1126{
1127 void *ret;
1128
1129 ret = realloc(ptr, size);
1130 if (!ret) {
1131 free(ptr);
1132 }
1133
1134 return ret;
1135}