blob: 6a81932914efb9e454bd1fe0dbfd535321e1d7a6 [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 Vaskob5a58fa2017-01-31 09:47:50 +0100419 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100420 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 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100436 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100437#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:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100440 ret = SSL_pending(session->ti.tls);
441 if (ret) {
442 /* some buffered TLS data available */
443 ret = 1;
444 fds.revents = POLLIN;
445 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100446 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100447
448 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100449 /* fallthrough */
450#endif
451 case NC_TI_FD:
452 if (session->ti_type == NC_TI_FD) {
453 fds.fd = session->ti.fd.in;
454 }
455
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100456 fds.events = POLLIN;
457 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100458
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100459 sigfillset(&sigmask);
460 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
461 ret = poll(&fds, 1, timeout);
462 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100463
464 break;
465
466 default:
467 ERRINT;
468 return -1;
469 }
470
471 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
472 if (ret < 0) {
473 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200474 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100475 session->status = NC_STATUS_INVALID;
476 session->term_reason = NC_SESSION_TERM_OTHER;
477 return -1;
478 } else { /* status > 0 */
479 /* in case of standard (non-libssh) poll, there still can be an error */
480 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100481 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100482 session->status = NC_STATUS_INVALID;
483 session->term_reason = NC_SESSION_TERM_DROPPED;
484 return -1;
485 }
486 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100487 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100488 session->status = NC_STATUS_INVALID;
489 session->term_reason = NC_SESSION_TERM_OTHER;
490 return -1;
491 }
492 }
493
494 return ret;
495}
496
497/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100498NC_MSG_TYPE
499nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200500{
Michal Vasko428087d2016-01-14 16:04:28 +0100501 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200502
503 assert(data);
504 *data = NULL;
505
Michal Vasko428087d2016-01-14 16:04:28 +0100506 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100507 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100508 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200509 }
510
Michal Vasko428087d2016-01-14 16:04:28 +0100511 ret = nc_read_poll(session, timeout);
512 if (ret == 0) {
513 /* timed out */
514 return NC_MSG_WOULDBLOCK;
515 } else if (ret < 0) {
516 /* poll error, error written */
517 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200518 }
519
Michal Vasko05ba9df2016-01-13 14:40:27 +0100520 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200521}
Radek Krejcife0b3472015-10-12 13:43:42 +0200522
Michal Vasko428087d2016-01-14 16:04:28 +0100523/* does not really log, only fatal errors */
524int
525nc_session_is_connected(struct nc_session *session)
526{
527 int ret;
528 struct pollfd fds;
529
530 switch (session->ti_type) {
531 case NC_TI_FD:
532 fds.fd = session->ti.fd.in;
533 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100534#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100535 case NC_TI_LIBSSH:
536 fds.fd = ssh_get_fd(session->ti.libssh.session);
537 break;
538#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100539#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100540 case NC_TI_OPENSSL:
541 fds.fd = SSL_get_fd(session->ti.tls);
542 break;
543#endif
544 case NC_TI_NONE:
545 ERRINT;
546 return 0;
547 }
548
549 fds.events = POLLIN;
550
551 errno = 0;
552 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
553
554 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100555 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100556 return 0;
557 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
558 return 0;
559 }
560
561 return 1;
562}
563
Radek Krejcife0b3472015-10-12 13:43:42 +0200564#define WRITE_BUFSIZE (2 * BUFFERSIZE)
565struct wclb_arg {
566 struct nc_session *session;
567 char buf[WRITE_BUFSIZE];
568 size_t len;
569};
570
Michal Vasko964e1732016-09-23 13:39:33 +0200571static int
Michal Vasko428087d2016-01-14 16:04:28 +0100572nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200573{
Michal Vasko964e1732016-09-23 13:39:33 +0200574 int c;
575 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200576#ifdef NC_ENABLED_TLS
577 unsigned long e;
578#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200579
Michal Vasko428087d2016-01-14 16:04:28 +0100580 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
581 return -1;
582 }
583
584 /* prevent SIGPIPE this way */
585 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100586 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100587 session->status = NC_STATUS_INVALID;
588 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100589 return -1;
590 }
591
Michal Vasko81b33fb2016-09-26 14:57:36 +0200592 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200593
Michal Vasko81b33fb2016-09-26 14:57:36 +0200594 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200595 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200596 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200597 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200598 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200599 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200600 return -1;
601 }
602 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200603
Radek Krejci53691be2016-02-22 13:58:37 +0100604#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200605 case NC_TI_LIBSSH:
606 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
607 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
608 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
609 } else {
610 ERR("Session %u: SSH channel unexpected EOF.", session->id);
611 }
612 session->status = NC_STATUS_INVALID;
613 session->term_reason = NC_SESSION_TERM_DROPPED;
614 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100615 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200616 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200617 if ((c == SSH_ERROR) || (c == -1)) {
618 ERR("Session %u: SSH channel write failed.", session->id);
619 return -1;
620 }
621 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200622#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100623#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200624 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200625 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200626 if (c < 1) {
627 switch ((e = SSL_get_error(session->ti.tls, c))) {
628 case SSL_ERROR_ZERO_RETURN:
629 ERR("Session %u: SSL connection was properly closed.", session->id);
630 return -1;
631 case SSL_ERROR_WANT_WRITE:
632 c = 0;
633 break;
634 case SSL_ERROR_SYSCALL:
635 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
636 return -1;
637 case SSL_ERROR_SSL:
638 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
639 return -1;
640 default:
641 ERR("Session %u: unknown SSL error occured.", session->id);
642 return -1;
643 }
644 }
645 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200646#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200647 default:
648 ERRINT;
649 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200650 }
651
652 if (c == 0) {
653 /* we must wait */
654 usleep(NC_TIMEOUT_STEP);
655 }
656
657 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200658 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200659
Michal Vasko964e1732016-09-23 13:39:33 +0200660 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200661}
662
Michal Vasko428087d2016-01-14 16:04:28 +0100663static int
664nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100665{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100666 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100667 char chunksize[20];
668
669 if (session->version == NC_VERSION_11) {
670 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100671 ret = nc_write(session, chunksize, strlen(chunksize));
672 if (ret == -1) {
673 return -1;
674 }
Michal Vasko086311b2016-01-08 09:53:11 +0100675 }
Michal Vasko428087d2016-01-14 16:04:28 +0100676
677 c = nc_write(session, buf, count);
678 if (c == -1) {
679 return -1;
680 }
681 ret += c;
682
683 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100684}
685
Radek Krejcife0b3472015-10-12 13:43:42 +0200686static int
Michal Vasko428087d2016-01-14 16:04:28 +0100687nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200688{
Michal Vasko428087d2016-01-14 16:04:28 +0100689 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100690
Michal Vasko428087d2016-01-14 16:04:28 +0100691 if (session->version == NC_VERSION_11) {
692 ret = nc_write(session, "\n##\n", 4);
693 } else {
694 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200695 }
696
Michal Vasko428087d2016-01-14 16:04:28 +0100697 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200698}
699
Michal Vasko428087d2016-01-14 16:04:28 +0100700static int
701nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200702{
Michal Vasko428087d2016-01-14 16:04:28 +0100703 int ret = 0;
704
Radek Krejcife0b3472015-10-12 13:43:42 +0200705 /* flush current buffer */
706 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100707 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200708 warg->len = 0;
709 }
Michal Vasko428087d2016-01-14 16:04:28 +0100710
711 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200712}
713
714static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100715nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200716{
Michal Vasko428087d2016-01-14 16:04:28 +0100717 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100718 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200719 struct wclb_arg *warg = (struct wclb_arg *)arg;
720
721 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100722 c = nc_write_clb_flush(warg);
723 if (c == -1) {
724 return -1;
725 }
726 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200727
728 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100729 c = nc_write_endtag(warg->session);
730 if (c == -1) {
731 return -1;
732 }
733 ret += c;
734
735 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200736 }
737
738 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
739 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100740 c = nc_write_clb_flush(warg);
741 if (c == -1) {
742 return -1;
743 }
744 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200745 }
Michal Vasko428087d2016-01-14 16:04:28 +0100746
Radek Krejci047300e2016-03-08 16:46:58 +0100747 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200748 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100749 c = nc_write_starttag_and_msg(warg->session, buf, count);
750 if (c == -1) {
751 return -1;
752 }
753 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200754 } else {
755 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100756 if (xmlcontent) {
757 for (l = 0; l < count; l++) {
758 if (warg->len + 5 >= WRITE_BUFSIZE) {
759 /* buffer is full */
760 c = nc_write_clb_flush(warg);
761 if (c == -1) {
762 return -1;
763 }
764 }
765
766 switch (((char *)buf)[l]) {
767 case '&':
768 ret += 5;
769 memcpy(&warg->buf[warg->len], "&amp;", 5);
770 warg->len += 5;
771 break;
772 case '<':
773 ret += 4;
774 memcpy(&warg->buf[warg->len], "&lt;", 4);
775 warg->len += 4;
776 break;
777 case '>':
778 /* not needed, just for readability */
779 ret += 4;
780 memcpy(&warg->buf[warg->len], "&gt;", 4);
781 warg->len += 4;
782 break;
783 default:
784 ret++;
785 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
786 warg->len++;
787 }
788 }
789 } else {
790 memcpy(&warg->buf[warg->len], buf, count);
791 warg->len += count; /* is <= WRITE_BUFSIZE */
792 ret += count;
793 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200794 }
795
Michal Vasko428087d2016-01-14 16:04:28 +0100796 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200797}
798
Radek Krejci047300e2016-03-08 16:46:58 +0100799static ssize_t
800nc_write_xmlclb(void *arg, const void *buf, size_t count)
801{
802 return nc_write_clb(arg, buf, count, 0);
803}
804
Michal Vasko05ba9df2016-01-13 14:40:27 +0100805static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100806nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
807 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100808{
Michal Vasko08611b32016-12-05 13:30:37 +0100809 if (open) {
810 nc_write_clb((void *)arg, "<", 1, 0);
811 } else {
812 nc_write_clb((void *)arg, "</", 2, 0);
813 }
814
815 if (prefix) {
816 nc_write_clb((void *)arg, prefix, pref_len, 0);
817 nc_write_clb((void *)arg, ":", 1, 0);
818 }
819
820 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100821 if (!open || !no_attr) {
822 nc_write_clb((void *)arg, ">", 1, 0);
823 }
Michal Vasko08611b32016-12-05 13:30:37 +0100824}
825
826static void
827nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
828{
829 uint16_t i, pref_len;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100830 char str_sid[11];
831
Michal Vasko08611b32016-12-05 13:30:37 +0100832 if (prefix) {
833 pref_len = strlen(prefix);
834 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100835
Michal Vasko52bd9492016-12-08 09:37:43 +0100836 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100837
Michal Vasko52bd9492016-12-08 09:37:43 +0100838 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100839 switch (err->type) {
840 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100841 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100842 break;
843 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100844 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100845 break;
846 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100847 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100848 break;
849 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100850 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100851 break;
852 default:
853 ERRINT;
854 return;
855 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100856
Michal Vasko52bd9492016-12-08 09:37:43 +0100857 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100858
Michal Vasko52bd9492016-12-08 09:37:43 +0100859 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100860 switch (err->tag) {
861 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100862 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100863 break;
864 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100865 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100866 break;
867 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100868 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100869 break;
870 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100871 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100872 break;
873 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100874 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100875 break;
876 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100877 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100878 break;
879 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100880 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100881 break;
882 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100883 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100884 break;
885 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100886 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100887 break;
888 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100889 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100890 break;
891 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100892 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100893 break;
894 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100895 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100896 break;
897 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100898 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100899 break;
900 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100901 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100902 break;
903 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100904 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100905 break;
906 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100907 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908 break;
909 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100910 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 break;
912 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100913 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100914 break;
915 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100916 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100917 break;
918 default:
919 ERRINT;
920 return;
921 }
Michal Vasko52bd9492016-12-08 09:37:43 +0100922 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100923
Michal Vasko52bd9492016-12-08 09:37:43 +0100924 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100925 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100926 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100927
928 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100929 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100930 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100931 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100932 }
933
934 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100935 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100936 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100937 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100938 }
939
940 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100941 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100942 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +0100943 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
944 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
945 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100946 }
Radek Krejci047300e2016-03-08 16:46:58 +0100947 nc_write_clb((void *)arg, ">", 1, 0);
948 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100949 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100950 }
951
Michal Vasko90920b02016-05-20 14:07:00 +0200952 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100953 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100954
Michal Vasko90920b02016-05-20 14:07:00 +0200955 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100956 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +0200957 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +0100958 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100959 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100960 }
961
962 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100963 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100964 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100965 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100966 }
967
968 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100969 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100970 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100971 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100972 }
973
974 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100975 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100976 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100977 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100978 }
979
980 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100981 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100982 }
983
Michal Vasko52bd9492016-12-08 09:37:43 +0100984 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100985 }
986
Michal Vasko52bd9492016-12-08 09:37:43 +0100987 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100988}
989
Michal Vasko428087d2016-01-14 16:04:28 +0100990/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100991int
Radek Krejci127f8952016-10-12 14:57:16 +0200992nc_write_msg(struct nc_session *session, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200993{
Radek Krejcid116db42016-01-08 15:36:30 +0100994 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100995 int count;
Michal Vasko08611b32016-12-05 13:30:37 +0100996 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +0200997 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100998 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +0200999 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001000 struct nc_server_reply *reply;
1001 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001002 char *buf = NULL;
1003 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001004 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001005 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001006 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001007
Michal Vasko428087d2016-01-14 16:04:28 +01001008 assert(session);
1009
1010 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001011 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +01001012 return -1;
1013 }
1014
Radek Krejcid116db42016-01-08 15:36:30 +01001015 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +02001016
1017 arg.session = session;
1018 arg.len = 0;
1019
Radek Krejci127f8952016-10-12 14:57:16 +02001020
Radek Krejcife0b3472015-10-12 13:43:42 +02001021 switch (type) {
1022 case NC_MSG_RPC:
1023 content = va_arg(ap, struct lyd_node *);
1024 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001025
Radek Krejcife0b3472015-10-12 13:43:42 +02001026 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001027 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001028 if (count == -1) {
1029 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001030 va_end(ap);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001031 return -1;
1032 }
Radek Krejci047300e2016-03-08 16:46:58 +01001033 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001034 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001035
Michal Vaskoe6e8b852016-10-20 14:07:29 +02001036 lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF);
Radek Krejci047300e2016-03-08 16:46:58 +01001037 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001038
Michal Vasko2e6defd2016-10-07 15:48:15 +02001039 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001040 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001041
Radek Krejcife0b3472015-10-12 13:43:42 +02001042 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001043 rpc_elem = va_arg(ap, struct lyxml_elem *);
1044 reply = va_arg(ap, struct nc_server_reply *);
1045
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001046 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1047 nc_write_clb((void *)&arg, "<", 1, 0);
1048 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1049 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001050 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001051 }
1052 else {
1053 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001054 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001055 }
1056
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001057 /* can be NULL if replying with a malformed-message error */
1058 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001059 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001060 nc_write_clb((void *)&arg, ">", 1, 0);
1061 } else {
1062 /* but put there at least the correct namespace */
1063 nc_write_clb((void *)&arg, "xmlns=\""NC_NS_BASE"\">", 48, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001064 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001065 switch (reply->type) {
1066 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001067 nc_write_clb((void *)&arg, "<", 1, 0);
1068 if (base_prefix) {
1069 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1070 nc_write_clb((void *)&arg, ":", 1, 0);
1071 }
1072 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001073 break;
1074 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001075 assert(((struct nc_server_reply_data *)reply)->data->schema->nodetype == LYS_RPC);
1076 switch(((struct nc_server_reply_data *)reply)->wd) {
1077 case NC_WD_UNKNOWN:
1078 case NC_WD_EXPLICIT:
1079 wd = LYP_WD_EXPLICIT;
1080 break;
1081 case NC_WD_TRIM:
1082 wd = LYP_WD_TRIM;
1083 break;
1084 case NC_WD_ALL:
1085 wd = LYP_WD_ALL;
1086 break;
1087 case NC_WD_ALL_TAG:
1088 wd = LYP_WD_ALL_TAG;
1089 break;
1090 }
Michal Vaskoe1708602016-10-18 12:17:22 +02001091 lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
Michal Vaskoe6e8b852016-10-20 14:07:29 +02001092 LYP_WITHSIBLINGS | LYP_NETCONF | wd);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001093 break;
1094 case NC_RPL_ERROR:
1095 error_rpl = (struct nc_server_reply_error *)reply;
1096 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001097 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001098 }
1099 break;
1100 default:
1101 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001102 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001103 va_end(ap);
1104 return -1;
1105 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001106 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1107 nc_write_clb((void *)&arg, "</", 2, 0);
1108 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1109 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1110 }
1111 else {
1112 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1113 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001114 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001115
Radek Krejcife0b3472015-10-12 13:43:42 +02001116 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001117 notif = va_arg(ap, struct nc_server_notif *);
1118
1119 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1120 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1121 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1122 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
1123 lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0);
mohitarora24878b2962016-11-09 18:45:33 -05001124 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001125 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001126
Radek Krejcid116db42016-01-08 15:36:30 +01001127 case NC_MSG_HELLO:
1128 if (session->version != NC_VERSION_10) {
1129 va_end(ap);
1130 return -1;
1131 }
1132 capabilities = va_arg(ap, const char **);
1133 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001134
Radek Krejcid116db42016-01-08 15:36:30 +01001135 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001136 if (count == -1) {
1137 ERRMEM;
1138 va_end(ap);
1139 return -1;
1140 }
Radek Krejci047300e2016-03-08 16:46:58 +01001141 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001142 free(buf);
1143 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001144 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1145 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1146 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001147 }
1148 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001149 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001150 if (count == -1) {
1151 ERRMEM;
1152 va_end(ap);
1153 return -1;
1154 }
Radek Krejci047300e2016-03-08 16:46:58 +01001155 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001156 free(buf);
1157 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001158 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001159 }
Radek Krejcid116db42016-01-08 15:36:30 +01001160 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001161
Radek Krejcife0b3472015-10-12 13:43:42 +02001162 default:
Radek Krejcid116db42016-01-08 15:36:30 +01001163 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +02001164 return -1;
1165 }
1166
1167 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001168 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001169
1170 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +01001171 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1172 /* error was already written */
1173 return -1;
1174 }
1175
Radek Krejcid116db42016-01-08 15:36:30 +01001176 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001177}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001178
1179void *
1180nc_realloc(void *ptr, size_t size)
1181{
1182 void *ret;
1183
1184 ret = realloc(ptr, size);
1185 if (!ret) {
1186 free(ptr);
1187 }
1188
1189 return ret;
1190}