blob: 40fe81531b06b892805d1cfb255adb09530261bd [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;
573
Michal Vasko428087d2016-01-14 16:04:28 +0100574 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
575 return -1;
576 }
577
578 /* prevent SIGPIPE this way */
579 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100580 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100581 session->status = NC_STATUS_INVALID;
582 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100583 return -1;
584 }
585
Michal Vasko81b33fb2016-09-26 14:57:36 +0200586 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200587
Michal Vasko81b33fb2016-09-26 14:57:36 +0200588 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200589 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200590 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200591 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200592 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200593 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200594 return -1;
595 }
596 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200597
Radek Krejci53691be2016-02-22 13:58:37 +0100598#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200599 case NC_TI_LIBSSH:
600 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
601 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
602 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
603 } else {
604 ERR("Session %u: SSH channel unexpected EOF.", session->id);
605 }
606 session->status = NC_STATUS_INVALID;
607 session->term_reason = NC_SESSION_TERM_DROPPED;
608 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100609 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200610 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200611 if ((c == SSH_ERROR) || (c == -1)) {
612 ERR("Session %u: SSH channel write failed.", session->id);
613 return -1;
614 }
615 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200616#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100617#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200618 unsigned long e;
619
620 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200621 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200622 if (c < 1) {
623 switch ((e = SSL_get_error(session->ti.tls, c))) {
624 case SSL_ERROR_ZERO_RETURN:
625 ERR("Session %u: SSL connection was properly closed.", session->id);
626 return -1;
627 case SSL_ERROR_WANT_WRITE:
628 c = 0;
629 break;
630 case SSL_ERROR_SYSCALL:
631 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
632 return -1;
633 case SSL_ERROR_SSL:
634 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
635 return -1;
636 default:
637 ERR("Session %u: unknown SSL error occured.", session->id);
638 return -1;
639 }
640 }
641 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200642#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200643 default:
644 ERRINT;
645 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200646 }
647
648 if (c == 0) {
649 /* we must wait */
650 usleep(NC_TIMEOUT_STEP);
651 }
652
653 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200654 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200655
Michal Vasko964e1732016-09-23 13:39:33 +0200656 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200657}
658
Michal Vasko428087d2016-01-14 16:04:28 +0100659static int
660nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100661{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100662 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100663 char chunksize[20];
664
665 if (session->version == NC_VERSION_11) {
666 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100667 ret = nc_write(session, chunksize, strlen(chunksize));
668 if (ret == -1) {
669 return -1;
670 }
Michal Vasko086311b2016-01-08 09:53:11 +0100671 }
Michal Vasko428087d2016-01-14 16:04:28 +0100672
673 c = nc_write(session, buf, count);
674 if (c == -1) {
675 return -1;
676 }
677 ret += c;
678
679 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100680}
681
Radek Krejcife0b3472015-10-12 13:43:42 +0200682static int
Michal Vasko428087d2016-01-14 16:04:28 +0100683nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200684{
Michal Vasko428087d2016-01-14 16:04:28 +0100685 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100686
Michal Vasko428087d2016-01-14 16:04:28 +0100687 if (session->version == NC_VERSION_11) {
688 ret = nc_write(session, "\n##\n", 4);
689 } else {
690 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200691 }
692
Michal Vasko428087d2016-01-14 16:04:28 +0100693 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200694}
695
Michal Vasko428087d2016-01-14 16:04:28 +0100696static int
697nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200698{
Michal Vasko428087d2016-01-14 16:04:28 +0100699 int ret = 0;
700
Radek Krejcife0b3472015-10-12 13:43:42 +0200701 /* flush current buffer */
702 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100703 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200704 warg->len = 0;
705 }
Michal Vasko428087d2016-01-14 16:04:28 +0100706
707 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200708}
709
710static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100711nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200712{
Michal Vasko428087d2016-01-14 16:04:28 +0100713 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100714 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200715 struct wclb_arg *warg = (struct wclb_arg *)arg;
716
717 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100718 c = nc_write_clb_flush(warg);
719 if (c == -1) {
720 return -1;
721 }
722 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200723
724 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100725 c = nc_write_endtag(warg->session);
726 if (c == -1) {
727 return -1;
728 }
729 ret += c;
730
731 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200732 }
733
734 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
735 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100736 c = nc_write_clb_flush(warg);
737 if (c == -1) {
738 return -1;
739 }
740 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200741 }
Michal Vasko428087d2016-01-14 16:04:28 +0100742
Radek Krejci047300e2016-03-08 16:46:58 +0100743 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200744 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100745 c = nc_write_starttag_and_msg(warg->session, buf, count);
746 if (c == -1) {
747 return -1;
748 }
749 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200750 } else {
751 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100752 if (xmlcontent) {
753 for (l = 0; l < count; l++) {
754 if (warg->len + 5 >= WRITE_BUFSIZE) {
755 /* buffer is full */
756 c = nc_write_clb_flush(warg);
757 if (c == -1) {
758 return -1;
759 }
760 }
761
762 switch (((char *)buf)[l]) {
763 case '&':
764 ret += 5;
765 memcpy(&warg->buf[warg->len], "&amp;", 5);
766 warg->len += 5;
767 break;
768 case '<':
769 ret += 4;
770 memcpy(&warg->buf[warg->len], "&lt;", 4);
771 warg->len += 4;
772 break;
773 case '>':
774 /* not needed, just for readability */
775 ret += 4;
776 memcpy(&warg->buf[warg->len], "&gt;", 4);
777 warg->len += 4;
778 break;
779 default:
780 ret++;
781 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
782 warg->len++;
783 }
784 }
785 } else {
786 memcpy(&warg->buf[warg->len], buf, count);
787 warg->len += count; /* is <= WRITE_BUFSIZE */
788 ret += count;
789 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200790 }
791
Michal Vasko428087d2016-01-14 16:04:28 +0100792 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200793}
794
Radek Krejci047300e2016-03-08 16:46:58 +0100795static ssize_t
796nc_write_xmlclb(void *arg, const void *buf, size_t count)
797{
798 return nc_write_clb(arg, buf, count, 0);
799}
800
Michal Vasko05ba9df2016-01-13 14:40:27 +0100801static void
Michal Vasko428087d2016-01-14 16:04:28 +0100802nc_write_error(struct wclb_arg *arg, struct nc_server_error *err)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100803{
804 uint16_t i;
805 char str_sid[11];
806
Radek Krejci047300e2016-03-08 16:46:58 +0100807 nc_write_clb((void *)arg, "<rpc-error>", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100808
Radek Krejci047300e2016-03-08 16:46:58 +0100809 nc_write_clb((void *)arg, "<error-type>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100810 switch (err->type) {
811 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100812 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100813 break;
814 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100815 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100816 break;
817 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100818 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100819 break;
820 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100821 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100822 break;
823 default:
824 ERRINT;
825 return;
826 }
Radek Krejci047300e2016-03-08 16:46:58 +0100827 nc_write_clb((void *)arg, "</error-type>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100828
Radek Krejci047300e2016-03-08 16:46:58 +0100829 nc_write_clb((void *)arg, "<error-tag>", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100830 switch (err->tag) {
831 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100832 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100833 break;
834 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100835 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100836 break;
837 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100838 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100839 break;
840 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100841 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100842 break;
843 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100844 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100845 break;
846 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100847 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100848 break;
849 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100850 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100851 break;
852 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100853 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100854 break;
855 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100856 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100857 break;
858 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100859 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100860 break;
861 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100862 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100863 break;
864 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100865 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100866 break;
867 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100868 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100869 break;
870 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100871 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100872 break;
873 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100874 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100875 break;
876 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100877 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100878 break;
879 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100880 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100881 break;
882 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100883 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100884 break;
885 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100886 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100887 break;
888 default:
889 ERRINT;
890 return;
891 }
Radek Krejci047300e2016-03-08 16:46:58 +0100892 nc_write_clb((void *)arg, "</error-tag>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100893
Radek Krejci047300e2016-03-08 16:46:58 +0100894 nc_write_clb((void *)arg, "<error-severity>error</error-severity>", 38, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100895
896 if (err->apptag) {
Radek Krejci047300e2016-03-08 16:46:58 +0100897 nc_write_clb((void *)arg, "<error-app-tag>", 15, 0);
898 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
899 nc_write_clb((void *)arg, "</error-app-tag>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100900 }
901
902 if (err->path) {
Radek Krejci047300e2016-03-08 16:46:58 +0100903 nc_write_clb((void *)arg, "<error-path>", 12, 0);
904 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
905 nc_write_clb((void *)arg, "</error-path>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100906 }
907
908 if (err->message) {
Radek Krejci047300e2016-03-08 16:46:58 +0100909 nc_write_clb((void *)arg, "<error-message", 14, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100910 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +0100911 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
912 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
913 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100914 }
Radek Krejci047300e2016-03-08 16:46:58 +0100915 nc_write_clb((void *)arg, ">", 1, 0);
916 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
917 nc_write_clb((void *)arg, "</error-message>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100918 }
919
Michal Vasko90920b02016-05-20 14:07:00 +0200920 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Radek Krejci047300e2016-03-08 16:46:58 +0100921 nc_write_clb((void *)arg, "<error-info>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100922
Michal Vasko90920b02016-05-20 14:07:00 +0200923 if (err->sid > -1) {
Radek Krejci047300e2016-03-08 16:46:58 +0100924 nc_write_clb((void *)arg, "<session-id>", 12, 0);
Michal Vasko90920b02016-05-20 14:07:00 +0200925 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +0100926 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
927 nc_write_clb((void *)arg, "</session-id>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100928 }
929
930 for (i = 0; i < err->attr_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100931 nc_write_clb((void *)arg, "<bad-attribute>", 15, 0);
932 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
933 nc_write_clb((void *)arg, "</bad-attribute>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100934 }
935
936 for (i = 0; i < err->elem_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100937 nc_write_clb((void *)arg, "<bad-element>", 13, 0);
938 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
939 nc_write_clb((void *)arg, "</bad-element>", 14, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100940 }
941
942 for (i = 0; i < err->ns_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100943 nc_write_clb((void *)arg, "<bad-namespace>", 15, 0);
944 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
945 nc_write_clb((void *)arg, "</bad-namespace>", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100946 }
947
948 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100949 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100950 }
951
Radek Krejci047300e2016-03-08 16:46:58 +0100952 nc_write_clb((void *)arg, "</error-info>", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100953 }
954
Radek Krejci047300e2016-03-08 16:46:58 +0100955 nc_write_clb((void *)arg, "</rpc-error>", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100956}
957
Michal Vasko428087d2016-01-14 16:04:28 +0100958/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100959int
960nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200961{
Radek Krejcid116db42016-01-08 15:36:30 +0100962 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100963 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200964 const char *attrs;
965 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100966 struct lyxml_elem *rpc_elem;
967 struct nc_server_reply *reply;
968 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100969 char *buf = NULL;
970 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200971 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100972 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +0200973 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200974
Michal Vasko428087d2016-01-14 16:04:28 +0100975 assert(session);
976
977 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100978 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100979 return -1;
980 }
981
Radek Krejcid116db42016-01-08 15:36:30 +0100982 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200983
984 arg.session = session;
985 arg.len = 0;
986
987 switch (type) {
988 case NC_MSG_RPC:
989 content = va_arg(ap, struct lyd_node *);
990 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100991
Radek Krejcife0b3472015-10-12 13:43:42 +0200992 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
993 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +0100994 if (count == -1) {
995 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +0100996 va_end(ap);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100997 return -1;
998 }
Radek Krejci047300e2016-03-08 16:46:58 +0100999 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001000 free(buf);
Radek Krejci047300e2016-03-08 16:46:58 +01001001 lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS);
1002 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001003
1004 session->msgid++;
1005 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001006
Radek Krejcife0b3472015-10-12 13:43:42 +02001007 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001008 rpc_elem = va_arg(ap, struct lyxml_elem *);
1009 reply = va_arg(ap, struct nc_server_reply *);
1010
Radek Krejci047300e2016-03-08 16:46:58 +01001011 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001012 /* can be NULL if replying with a malformed-message error */
1013 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001014 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001015 nc_write_clb((void *)&arg, ">", 1, 0);
1016 } else {
1017 /* but put there at least the correct namespace */
1018 nc_write_clb((void *)&arg, "xmlns=\""NC_NS_BASE"\">", 48, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001019 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001020 switch (reply->type) {
1021 case NC_RPL_OK:
Radek Krejci047300e2016-03-08 16:46:58 +01001022 nc_write_clb((void *)&arg, "<ok/>", 5, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001023 break;
1024 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001025 assert(((struct nc_server_reply_data *)reply)->data->schema->nodetype == LYS_RPC);
1026 switch(((struct nc_server_reply_data *)reply)->wd) {
1027 case NC_WD_UNKNOWN:
1028 case NC_WD_EXPLICIT:
1029 wd = LYP_WD_EXPLICIT;
1030 break;
1031 case NC_WD_TRIM:
1032 wd = LYP_WD_TRIM;
1033 break;
1034 case NC_WD_ALL:
1035 wd = LYP_WD_ALL;
1036 break;
1037 case NC_WD_ALL_TAG:
1038 wd = LYP_WD_ALL_TAG;
1039 break;
1040 }
1041 lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data->child, LYD_XML,
1042 LYP_WITHSIBLINGS | wd);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001043 break;
1044 case NC_RPL_ERROR:
1045 error_rpl = (struct nc_server_reply_error *)reply;
1046 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko428087d2016-01-14 16:04:28 +01001047 nc_write_error(&arg, error_rpl->err[i]);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001048 }
1049 break;
1050 default:
1051 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001052 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001053 va_end(ap);
1054 return -1;
1055 }
Radek Krejci047300e2016-03-08 16:46:58 +01001056 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001057 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001058
Radek Krejcife0b3472015-10-12 13:43:42 +02001059 case NC_MSG_NOTIF:
Radek Krejci047300e2016-03-08 16:46:58 +01001060 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001061 /* TODO content */
Radek Krejci047300e2016-03-08 16:46:58 +01001062 nc_write_clb((void *)&arg, "</notification>", 12, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001063 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001064
Radek Krejcid116db42016-01-08 15:36:30 +01001065 case NC_MSG_HELLO:
1066 if (session->version != NC_VERSION_10) {
1067 va_end(ap);
1068 return -1;
1069 }
1070 capabilities = va_arg(ap, const char **);
1071 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001072
Radek Krejcid116db42016-01-08 15:36:30 +01001073 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001074 if (count == -1) {
1075 ERRMEM;
1076 va_end(ap);
1077 return -1;
1078 }
Radek Krejci047300e2016-03-08 16:46:58 +01001079 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001080 free(buf);
1081 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001082 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1083 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1084 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001085 }
1086 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001087 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001088 if (count == -1) {
1089 ERRMEM;
1090 va_end(ap);
1091 return -1;
1092 }
Radek Krejci047300e2016-03-08 16:46:58 +01001093 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001094 free(buf);
1095 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001096 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001097 }
Radek Krejcid116db42016-01-08 15:36:30 +01001098 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001099
Radek Krejcife0b3472015-10-12 13:43:42 +02001100 default:
Radek Krejcid116db42016-01-08 15:36:30 +01001101 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +02001102 return -1;
1103 }
1104
1105 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001106 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001107
1108 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +01001109 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1110 /* error was already written */
1111 return -1;
1112 }
1113
Radek Krejcid116db42016-01-08 15:36:30 +01001114 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001115}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001116
1117void *
1118nc_realloc(void *ptr, size_t size)
1119{
1120 void *ret;
1121
1122 ret = realloc(ptr, size);
1123 if (!ret) {
1124 free(ptr);
1125 }
1126
1127 return ret;
1128}