blob: ada8020ff3d3f0e8db6c788623e05cbbe93ad3fb [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
Miroslav Mareš9563b812017-08-19 17:45:36 +020015#define _GNU_SOURCE /* asprintf, signals */
Radek Krejci206fcd62015-10-07 15:42:48 +020016#include <assert.h>
17#include <errno.h>
18#include <poll.h>
Michal Vasko3512e402016-01-28 16:22:34 +010019#include <inttypes.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020020#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020021#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010024#include <signal.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010025#include <time.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
Michal Vasko90a87d92018-12-10 15:53:44 +010037#ifdef NC_ENABLED_TLS
38
39static char *
40nc_ssl_error_get_reasons(void)
41{
42 unsigned int e;
43 int reason_size, reason_len;
44 char *reasons = NULL;
45
46 reason_size = 1;
47 reason_len = 0;
48 while ((e = ERR_get_error())) {
49 if (reason_len) {
50 /* add "; " */
51 reason_size += 2;
52 reasons = nc_realloc(reasons, reason_size);
53 if (!reasons) {
54 ERRMEM;
55 return NULL;
56 }
57 reason_len += sprintf(reasons + reason_len, "; ");
58 }
59 reason_size += strlen(ERR_reason_error_string(e));
60 reasons = nc_realloc(reasons, reason_size);
61 if (!reasons) {
62 ERRMEM;
63 return NULL;
64 }
65 reason_len += sprintf(reasons + reason_len, ERR_reason_error_string(e));
66 }
67
68 return reasons;
69}
70
71#endif
72
Radek Krejci206fcd62015-10-07 15:42:48 +020073static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010074nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +020075{
Michal Vasko81b33fb2016-09-26 14:57:36 +020076 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010077 ssize_t r = -1;
Michal Vasko36c7be82017-02-22 13:37:59 +010078 struct timespec ts_cur, ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020079
80 assert(session);
81 assert(buf);
82
Michal Vasko428087d2016-01-14 16:04:28 +010083 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
84 return -1;
85 }
86
Radek Krejci206fcd62015-10-07 15:42:48 +020087 if (!count) {
88 return 0;
89 }
90
Michal Vasko77a6abe2017-10-05 10:02:20 +020091 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +010092 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +020093 do {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010094 switch (session->ti_type) {
95 case NC_TI_NONE:
96 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010097
Michal Vasko6b7c42e2016-03-02 15:46:41 +010098 case NC_TI_FD:
99 /* read via standard file descriptor */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200100 r = read(session->ti.fd.in, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200101 if (r < 0) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100102 if ((errno == EAGAIN) || (errno == EINTR)) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100103 r = 0;
104 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200105 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100106 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
107 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100108 session->status = NC_STATUS_INVALID;
109 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200110 return -1;
111 }
112 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100113 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
114 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +0100115 session->status = NC_STATUS_INVALID;
116 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200117 return -1;
118 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100119 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200120
Radek Krejci53691be2016-02-22 13:58:37 +0100121#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100122 case NC_TI_LIBSSH:
123 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200124 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200125 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100126 r = 0;
127 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200128 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100129 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
130 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100131 session->status = NC_STATUS_INVALID;
132 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200133 return -1;
134 } else if (r == 0) {
135 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +0100136 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100137 session->status = NC_STATUS_INVALID;
138 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200139 return -1;
140 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100141 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200142 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100143 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200144#endif
145
Radek Krejci53691be2016-02-22 13:58:37 +0100146#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100147 case NC_TI_OPENSSL:
148 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100149 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200150 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200151 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100152 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100153 char *reasons;
154
Michal Vasko0abba6d2018-12-10 14:09:39 +0100155 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200156 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100157 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100158 r = 0;
159 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200160 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100161 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100162 session->status = NC_STATUS_INVALID;
163 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200164 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100165 case SSL_ERROR_SYSCALL:
166 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
167 session->status = NC_STATUS_INVALID;
168 session->term_reason = NC_SESSION_TERM_OTHER;
169 return -1;
170 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100171 reasons = nc_ssl_error_get_reasons();
172 ERR("Session %u: SSL error (%s).", session->id, reasons);
173 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100174 session->status = NC_STATUS_INVALID;
175 session->term_reason = NC_SESSION_TERM_OTHER;
176 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200177 default:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100178 ERR("Session %u: unknown SSL error occured (err code %d).", session->id, e);
Michal Vasko428087d2016-01-14 16:04:28 +0100179 session->status = NC_STATUS_INVALID;
180 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200181 return -1;
182 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200183 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100184 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200185#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100186 }
187
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100188 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100189 /* nothing read */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100190 usleep(NC_TIMEOUT_STEP);
Michal Vasko77a6abe2017-10-05 10:02:20 +0200191 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100192 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
193 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100194 ERR("Session %u: inactive read timeout elapsed.", session->id);
195 } else {
196 ERR("Session %u: active read timeout elapsed.", session->id);
197 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100198 session->status = NC_STATUS_INVALID;
199 session->term_reason = NC_SESSION_TERM_OTHER;
200 return -1;
201 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100202 } else {
203 /* something read */
204 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100205
206 /* reset inactive timeout */
Michal Vasko77a6abe2017-10-05 10:02:20 +0200207 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100208 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100209 }
210
Michal Vasko81b33fb2016-09-26 14:57:36 +0200211 } while (readd < count);
212 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200213
Michal Vasko81b33fb2016-09-26 14:57:36 +0200214 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200215}
216
217static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100218nc_read_chunk(struct nc_session *session, size_t len, uint32_t inact_timeout, struct timespec *ts_act_timeout, char **chunk)
Radek Krejci206fcd62015-10-07 15:42:48 +0200219{
220 ssize_t r;
221
222 assert(session);
223 assert(chunk);
224
225 if (!len) {
226 return 0;
227 }
228
Michal Vasko4eb3c312016-03-01 14:09:37 +0100229 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200230 if (!*chunk) {
231 ERRMEM;
232 return -1;
233 }
234
Michal Vasko36c7be82017-02-22 13:37:59 +0100235 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200236 if (r <= 0) {
237 free(*chunk);
238 return -1;
239 }
240
241 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200242 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200243
244 return r;
245}
246
247static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100248nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
249 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200250{
251 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200252 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200253
254 assert(session);
255 assert(endtag);
256
257 if (limit && limit < BUFFERSIZE) {
258 size = limit;
259 } else {
260 size = BUFFERSIZE;
261 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100262 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200263 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200264 ERRMEM;
265 return -1;
266 }
267
268 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100269 while (1) {
David Sedlák15dad862018-07-04 11:25:55 +0200270 if (limit && count == limit) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200271 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100272 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
273 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200274 return -1;
275 }
276
277 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200278 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200279 /* get more memory */
280 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200281 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100282 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200283 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200284 return -1;
285 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200286 }
287
288 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200289 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
290 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200291 free(chunk);
292 return -1;
293 }
294
David Sedlákfedbc792018-07-04 11:07:07 +0200295 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200296
David Sedlákfedbc792018-07-04 11:07:07 +0200297 for (i = len - matched; i > 0; i--) {
298 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
299 /*part of endtag found */
300 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200301 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200302 } else {
303 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200304 }
305 }
David Sedlákfedbc792018-07-04 11:07:07 +0200306
307 /* whole endtag found */
308 if (matched == len) {
309 break;
310 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200311 }
312
313 /* terminating null byte */
314 chunk[count] = 0;
315
316 if (result) {
317 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200318 } else {
319 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200320 }
321 return count;
322}
323
Michal Vasko131120a2018-05-29 15:44:02 +0200324/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Radek Krejci206fcd62015-10-07 15:42:48 +0200325NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200326nc_read_msg_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data, int passing_io_lock)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327{
Michal Vasko131120a2018-05-29 15:44:02 +0200328 int ret, io_locked = passing_io_lock;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100329 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100330 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100331 /* use timeout in milliseconds instead seconds */
332 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
333 struct timespec ts_act_timeout;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100334 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100335
Michal Vasko428087d2016-01-14 16:04:28 +0100336 assert(session && data);
337 *data = NULL;
338
339 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100340 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +0200341 ret = NC_MSG_ERROR;
342 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100343 }
344
Michal Vasko77a6abe2017-10-05 10:02:20 +0200345 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100346 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
347
Michal Vasko131120a2018-05-29 15:44:02 +0200348 if (!io_locked) {
349 /* SESSION IO LOCK */
350 ret = nc_session_io_lock(session, io_timeout, __func__);
351 if (ret < 0) {
352 ret = NC_MSG_ERROR;
353 goto cleanup;
354 } else if (!ret) {
355 ret = NC_MSG_WOULDBLOCK;
356 goto cleanup;
357 }
358 io_locked = 1;
359 }
360
Michal Vasko05ba9df2016-01-13 14:40:27 +0100361 /* read the message */
362 switch (session->version) {
363 case NC_VERSION_10:
Michal Vasko36c7be82017-02-22 13:37:59 +0100364 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100365 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200366 ret = NC_MSG_ERROR;
367 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100368 }
369
370 /* cut off the end tag */
371 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
372 break;
373 case NC_VERSION_11:
374 while (1) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100375 ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100376 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200377 ret = NC_MSG_ERROR;
378 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100380 ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100381 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200382 ret = NC_MSG_ERROR;
383 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 }
385
386 if (!strcmp(chunk, "#\n")) {
387 /* end of chunked framing message */
388 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200389 if (!msg) {
390 ERR("Session %u: invalid frame chunk delimiters.", session->id);
391 goto malformed_msg;
392 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100393 break;
394 }
395
396 /* convert string to the size of the following chunk */
397 chunk_len = strtoul(chunk, (char **)NULL, 10);
398 free(chunk);
399 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100400 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100401 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100402 }
403
404 /* now we have size of next chunk, so read the chunk */
Michal Vasko36c7be82017-02-22 13:37:59 +0100405 ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100406 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200407 ret = NC_MSG_ERROR;
408 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100409 }
410
411 /* realloc message buffer, remember to count terminating null byte */
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200412 msg = nc_realloc(msg, len + chunk_len + 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100413 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100414 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200415 ret = NC_MSG_ERROR;
416 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100417 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100418 memcpy(msg + len, chunk, chunk_len);
419 len += chunk_len;
420 msg[len] = '\0';
421 free(chunk);
422 }
423
424 break;
425 }
Michal Vasko131120a2018-05-29 15:44:02 +0200426
427 /* SESSION IO UNLOCK */
428 assert(io_locked);
429 nc_session_io_unlock(session, __func__);
430 io_locked = 0;
431
Michal Vasko81b33fb2016-09-26 14:57:36 +0200432 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100433
434 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100435 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100436 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100437 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100438 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100439 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100440 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100441 }
442 free(msg);
443 msg = NULL;
444
445 /* get and return message type */
446 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
447 if (!strcmp((*data)->name, "rpc")) {
448 return NC_MSG_RPC;
449 } else if (!strcmp((*data)->name, "rpc-reply")) {
450 return NC_MSG_REPLY;
451 } else if (!strcmp((*data)->name, "hello")) {
452 return NC_MSG_HELLO;
453 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100454 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100455 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456 }
457 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
458 if (!strcmp((*data)->name, "notification")) {
459 return NC_MSG_NOTIF;
460 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100461 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100462 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100463 }
464 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100465 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100466 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100467 }
468
Michal Vasko428087d2016-01-14 16:04:28 +0100469malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100470 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100471 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
472 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100473 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100474
Michal Vaskofc5d07e2018-08-17 10:23:48 +0200475 if (io_locked) {
476 /* nc_write_msg_io locks and unlocks the lock by itself */
477 nc_session_io_unlock(session, __func__);
478 io_locked = 0;
479 }
480
Michal Vasko131120a2018-05-29 15:44:02 +0200481 if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) {
Michal Vaskod083db62016-01-19 10:31:29 +0100482 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100483 if (session->status != NC_STATUS_INVALID) {
484 session->status = NC_STATUS_INVALID;
485 session->term_reason = NC_SESSION_TERM_OTHER;
486 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100487 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100488 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100489 }
Michal Vasko131120a2018-05-29 15:44:02 +0200490 ret = NC_MSG_ERROR;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100491
Michal Vasko131120a2018-05-29 15:44:02 +0200492cleanup:
493 if (io_locked) {
494 nc_session_io_unlock(session, __func__);
495 }
Michal Vasko428087d2016-01-14 16:04:28 +0100496 free(msg);
497 free(*data);
498 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100499
Michal Vasko131120a2018-05-29 15:44:02 +0200500 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100501}
502
Michal Vasko428087d2016-01-14 16:04:28 +0100503/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
504static int
Michal Vasko131120a2018-05-29 15:44:02 +0200505nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100506{
Radek Krejci5961c702016-07-15 09:15:18 +0200507 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100508 int ret = -2;
509 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100510
511 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100512 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100513 return -1;
514 }
515
516 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100517#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100518 case NC_TI_LIBSSH:
519 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200520 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100521 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100522 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100523 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100524 session->status = NC_STATUS_INVALID;
525 session->term_reason = NC_SESSION_TERM_OTHER;
526 return -1;
527 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100528 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100529 session->status = NC_STATUS_INVALID;
530 session->term_reason = NC_SESSION_TERM_DROPPED;
531 return -1;
532 } else if (ret > 0) {
533 /* fake it */
534 ret = 1;
535 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100536 } else { /* ret == 0 */
537 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100538 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100539 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100540#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100541#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100542 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100543 ret = SSL_pending(session->ti.tls);
544 if (ret) {
545 /* some buffered TLS data available */
546 ret = 1;
547 fds.revents = POLLIN;
548 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100549 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100550
551 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100552#endif
Michal Vaskob983c002017-11-02 13:10:57 +0100553 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100554 case NC_TI_FD:
555 if (session->ti_type == NC_TI_FD) {
556 fds.fd = session->ti.fd.in;
557 }
558
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100559 fds.events = POLLIN;
560 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100561
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100562 sigfillset(&sigmask);
563 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200564 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100565 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100566
567 break;
568
569 default:
570 ERRINT;
571 return -1;
572 }
573
574 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
575 if (ret < 0) {
576 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200577 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100578 session->status = NC_STATUS_INVALID;
579 session->term_reason = NC_SESSION_TERM_OTHER;
580 return -1;
581 } else { /* status > 0 */
582 /* in case of standard (non-libssh) poll, there still can be an error */
583 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100584 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100585 session->status = NC_STATUS_INVALID;
586 session->term_reason = NC_SESSION_TERM_DROPPED;
587 return -1;
588 }
589 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100590 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100591 session->status = NC_STATUS_INVALID;
592 session->term_reason = NC_SESSION_TERM_OTHER;
593 return -1;
594 }
595 }
596
597 return ret;
598}
599
Michal Vasko131120a2018-05-29 15:44:02 +0200600/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100601NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200602nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200603{
Michal Vasko428087d2016-01-14 16:04:28 +0100604 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200605
606 assert(data);
607 *data = NULL;
608
Michal Vasko428087d2016-01-14 16:04:28 +0100609 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100610 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100611 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200612 }
613
Michal Vasko131120a2018-05-29 15:44:02 +0200614 /* SESSION IO LOCK */
615 ret = nc_session_io_lock(session, io_timeout, __func__);
616 if (ret < 0) {
617 return NC_MSG_ERROR;
618 } else if (!ret) {
619 return NC_MSG_WOULDBLOCK;
620 }
621
622 ret = nc_read_poll(session, io_timeout);
Michal Vasko428087d2016-01-14 16:04:28 +0100623 if (ret == 0) {
624 /* timed out */
Michal Vasko131120a2018-05-29 15:44:02 +0200625
626 /* SESSION IO UNLOCK */
627 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100628 return NC_MSG_WOULDBLOCK;
629 } else if (ret < 0) {
630 /* poll error, error written */
Michal Vasko131120a2018-05-29 15:44:02 +0200631
632 /* SESSION IO UNLOCK */
633 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100634 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200635 }
636
Michal Vasko131120a2018-05-29 15:44:02 +0200637 /* SESSION IO LOCK passed down */
638 return nc_read_msg_io(session, io_timeout, data, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200639}
Radek Krejcife0b3472015-10-12 13:43:42 +0200640
Michal Vasko428087d2016-01-14 16:04:28 +0100641/* does not really log, only fatal errors */
642int
643nc_session_is_connected(struct nc_session *session)
644{
645 int ret;
646 struct pollfd fds;
647
648 switch (session->ti_type) {
649 case NC_TI_FD:
650 fds.fd = session->ti.fd.in;
651 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100652#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100653 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100654 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100655#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100656#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100657 case NC_TI_OPENSSL:
658 fds.fd = SSL_get_fd(session->ti.tls);
659 break;
660#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100661 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100662 return 0;
663 }
664
Michal Vasko840a8a62017-02-07 10:56:34 +0100665 if (fds.fd == -1) {
666 return 0;
667 }
668
Michal Vasko428087d2016-01-14 16:04:28 +0100669 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100670 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100671
672 errno = 0;
673 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
674
675 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100676 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100677 return 0;
678 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
679 return 0;
680 }
681
682 return 1;
683}
684
Radek Krejcife0b3472015-10-12 13:43:42 +0200685#define WRITE_BUFSIZE (2 * BUFFERSIZE)
686struct wclb_arg {
687 struct nc_session *session;
688 char buf[WRITE_BUFSIZE];
689 size_t len;
690};
691
Michal Vasko964e1732016-09-23 13:39:33 +0200692static int
Michal Vasko428087d2016-01-14 16:04:28 +0100693nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200694{
Michal Vasko964e1732016-09-23 13:39:33 +0200695 int c;
696 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200697#ifdef NC_ENABLED_TLS
698 unsigned long e;
699#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200700
Michal Vasko428087d2016-01-14 16:04:28 +0100701 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
702 return -1;
703 }
704
705 /* prevent SIGPIPE this way */
706 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100707 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100708 session->status = NC_STATUS_INVALID;
709 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100710 return -1;
711 }
712
Michal Vasko81b33fb2016-09-26 14:57:36 +0200713 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200714
Michal Vasko81b33fb2016-09-26 14:57:36 +0200715 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200716 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200717 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200718 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200719 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200720 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200721 return -1;
722 }
723 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200724
Radek Krejci53691be2016-02-22 13:58:37 +0100725#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200726 case NC_TI_LIBSSH:
727 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
728 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
729 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
730 } else {
731 ERR("Session %u: SSH channel unexpected EOF.", session->id);
732 }
733 session->status = NC_STATUS_INVALID;
734 session->term_reason = NC_SESSION_TERM_DROPPED;
735 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100736 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200737 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200738 if ((c == SSH_ERROR) || (c == -1)) {
739 ERR("Session %u: SSH channel write failed.", session->id);
740 return -1;
741 }
742 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200743#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100744#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200745 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200746 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200747 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100748 char *reasons;
749
Michal Vasko964e1732016-09-23 13:39:33 +0200750 switch ((e = SSL_get_error(session->ti.tls, c))) {
751 case SSL_ERROR_ZERO_RETURN:
752 ERR("Session %u: SSL connection was properly closed.", session->id);
753 return -1;
754 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100755 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200756 c = 0;
757 break;
758 case SSL_ERROR_SYSCALL:
759 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
760 return -1;
761 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100762 reasons = nc_ssl_error_get_reasons();
763 ERR("Session %u: SSL error (%s).", session->id, reasons);
764 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200765 return -1;
766 default:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100767 ERR("Session %u: unknown SSL error occured (err code %d).", session->id, e);
Michal Vasko964e1732016-09-23 13:39:33 +0200768 return -1;
769 }
770 }
771 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200772#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200773 default:
774 ERRINT;
775 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200776 }
777
778 if (c == 0) {
779 /* we must wait */
780 usleep(NC_TIMEOUT_STEP);
781 }
782
783 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200784 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200785
Michal Vasko964e1732016-09-23 13:39:33 +0200786 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200787}
788
Michal Vasko428087d2016-01-14 16:04:28 +0100789static int
790nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100791{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100792 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100793 char chunksize[20];
794
795 if (session->version == NC_VERSION_11) {
796 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100797 ret = nc_write(session, chunksize, strlen(chunksize));
798 if (ret == -1) {
799 return -1;
800 }
Michal Vasko086311b2016-01-08 09:53:11 +0100801 }
Michal Vasko428087d2016-01-14 16:04:28 +0100802
803 c = nc_write(session, buf, count);
804 if (c == -1) {
805 return -1;
806 }
807 ret += c;
808
809 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100810}
811
Radek Krejcife0b3472015-10-12 13:43:42 +0200812static int
Michal Vasko428087d2016-01-14 16:04:28 +0100813nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200814{
Michal Vasko428087d2016-01-14 16:04:28 +0100815 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100816
Michal Vasko428087d2016-01-14 16:04:28 +0100817 if (session->version == NC_VERSION_11) {
818 ret = nc_write(session, "\n##\n", 4);
819 } else {
820 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200821 }
822
Michal Vasko428087d2016-01-14 16:04:28 +0100823 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200824}
825
Michal Vasko428087d2016-01-14 16:04:28 +0100826static int
827nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200828{
Michal Vasko428087d2016-01-14 16:04:28 +0100829 int ret = 0;
830
Radek Krejcife0b3472015-10-12 13:43:42 +0200831 /* flush current buffer */
832 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100833 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200834 warg->len = 0;
835 }
Michal Vasko428087d2016-01-14 16:04:28 +0100836
837 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200838}
839
840static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100841nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200842{
Michal Vasko428087d2016-01-14 16:04:28 +0100843 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100844 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200845 struct wclb_arg *warg = (struct wclb_arg *)arg;
846
847 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100848 c = nc_write_clb_flush(warg);
849 if (c == -1) {
850 return -1;
851 }
852 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200853
854 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100855 c = nc_write_endtag(warg->session);
856 if (c == -1) {
857 return -1;
858 }
859 ret += c;
860
861 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200862 }
863
864 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
865 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100866 c = nc_write_clb_flush(warg);
867 if (c == -1) {
868 return -1;
869 }
870 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200871 }
Michal Vasko428087d2016-01-14 16:04:28 +0100872
Radek Krejci047300e2016-03-08 16:46:58 +0100873 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200874 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100875 c = nc_write_starttag_and_msg(warg->session, buf, count);
876 if (c == -1) {
877 return -1;
878 }
879 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200880 } else {
881 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100882 if (xmlcontent) {
883 for (l = 0; l < count; l++) {
884 if (warg->len + 5 >= WRITE_BUFSIZE) {
885 /* buffer is full */
886 c = nc_write_clb_flush(warg);
887 if (c == -1) {
888 return -1;
889 }
890 }
891
892 switch (((char *)buf)[l]) {
893 case '&':
894 ret += 5;
895 memcpy(&warg->buf[warg->len], "&amp;", 5);
896 warg->len += 5;
897 break;
898 case '<':
899 ret += 4;
900 memcpy(&warg->buf[warg->len], "&lt;", 4);
901 warg->len += 4;
902 break;
903 case '>':
904 /* not needed, just for readability */
905 ret += 4;
906 memcpy(&warg->buf[warg->len], "&gt;", 4);
907 warg->len += 4;
908 break;
909 default:
910 ret++;
911 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
912 warg->len++;
913 }
914 }
915 } else {
916 memcpy(&warg->buf[warg->len], buf, count);
917 warg->len += count; /* is <= WRITE_BUFSIZE */
918 ret += count;
919 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200920 }
921
Michal Vasko428087d2016-01-14 16:04:28 +0100922 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200923}
924
Radek Krejci047300e2016-03-08 16:46:58 +0100925static ssize_t
926nc_write_xmlclb(void *arg, const void *buf, size_t count)
927{
928 return nc_write_clb(arg, buf, count, 0);
929}
930
Michal Vasko05ba9df2016-01-13 14:40:27 +0100931static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100932nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
933 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100934{
Michal Vasko08611b32016-12-05 13:30:37 +0100935 if (open) {
936 nc_write_clb((void *)arg, "<", 1, 0);
937 } else {
938 nc_write_clb((void *)arg, "</", 2, 0);
939 }
940
941 if (prefix) {
942 nc_write_clb((void *)arg, prefix, pref_len, 0);
943 nc_write_clb((void *)arg, ":", 1, 0);
944 }
945
946 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100947 if (!open || !no_attr) {
948 nc_write_clb((void *)arg, ">", 1, 0);
949 }
Michal Vasko08611b32016-12-05 13:30:37 +0100950}
951
952static void
953nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
954{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100955 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100956 char str_sid[11];
957
Michal Vasko08611b32016-12-05 13:30:37 +0100958 if (prefix) {
959 pref_len = strlen(prefix);
960 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100961
Michal Vasko52bd9492016-12-08 09:37:43 +0100962 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100963
Michal Vasko52bd9492016-12-08 09:37:43 +0100964 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100965 switch (err->type) {
966 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100967 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100968 break;
969 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100970 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100971 break;
972 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100973 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100974 break;
975 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100976 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100977 break;
978 default:
979 ERRINT;
980 return;
981 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100982
Michal Vasko52bd9492016-12-08 09:37:43 +0100983 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100984
Michal Vasko52bd9492016-12-08 09:37:43 +0100985 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100986 switch (err->tag) {
987 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100988 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100989 break;
990 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100991 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100992 break;
993 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100994 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100995 break;
996 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100997 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100998 break;
999 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001000 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001001 break;
1002 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001003 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001004 break;
1005 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001006 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001007 break;
1008 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001009 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001010 break;
1011 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001012 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001013 break;
1014 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +01001015 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001016 break;
1017 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001018 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001019 break;
1020 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001021 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001022 break;
1023 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001024 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001025 break;
1026 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +01001027 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001028 break;
1029 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +01001030 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001031 break;
1032 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +01001033 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001034 break;
1035 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +01001036 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001037 break;
1038 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +01001039 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001040 break;
1041 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +01001042 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001043 break;
1044 default:
1045 ERRINT;
1046 return;
1047 }
Michal Vasko52bd9492016-12-08 09:37:43 +01001048 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001049
Michal Vasko52bd9492016-12-08 09:37:43 +01001050 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001051 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001052 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001053
1054 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001055 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001056 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001057 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001058 }
1059
1060 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001061 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001062 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001063 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001064 }
1065
1066 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001067 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001068 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +01001069 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
1070 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
1071 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001072 }
Radek Krejci047300e2016-03-08 16:46:58 +01001073 nc_write_clb((void *)arg, ">", 1, 0);
1074 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001075 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001076 }
1077
Michal Vasko90920b02016-05-20 14:07:00 +02001078 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001079 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001080
Michal Vasko90920b02016-05-20 14:07:00 +02001081 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001082 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +02001083 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +01001084 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001085 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001086 }
1087
1088 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001089 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001090 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001091 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001092 }
1093
1094 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001095 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001096 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001097 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001098 }
1099
1100 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001101 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001102 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001103 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001104 }
1105
1106 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +01001107 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001108 }
1109
Michal Vasko52bd9492016-12-08 09:37:43 +01001110 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001111 }
1112
Michal Vasko52bd9492016-12-08 09:37:43 +01001113 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001114}
1115
Michal Vasko131120a2018-05-29 15:44:02 +02001116/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
1117NC_MSG_TYPE
1118nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001119{
Radek Krejcid116db42016-01-08 15:36:30 +01001120 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +02001121 int count, ret;
Michal Vasko08611b32016-12-05 13:30:37 +01001122 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001123 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001124 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001125 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001126 struct nc_server_reply *reply;
1127 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001128 char *buf = NULL;
1129 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001130 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001131 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001132 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001133
Michal Vasko428087d2016-01-14 16:04:28 +01001134 assert(session);
1135
1136 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001137 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +02001138 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001139 }
1140
Radek Krejcife0b3472015-10-12 13:43:42 +02001141 arg.session = session;
1142 arg.len = 0;
1143
Michal Vasko131120a2018-05-29 15:44:02 +02001144 /* SESSION IO LOCK */
1145 ret = nc_session_io_lock(session, io_timeout, __func__);
1146 if (ret < 0) {
1147 return NC_MSG_ERROR;
1148 } else if (!ret) {
1149 return NC_MSG_WOULDBLOCK;
1150 }
1151
1152 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +02001153
Radek Krejcife0b3472015-10-12 13:43:42 +02001154 switch (type) {
1155 case NC_MSG_RPC:
1156 content = va_arg(ap, struct lyd_node *);
1157 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001158
Radek Krejcife0b3472015-10-12 13:43:42 +02001159 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001160 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001161 if (count == -1) {
1162 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001163 ret = NC_MSG_ERROR;
1164 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001165 }
Radek Krejci047300e2016-03-08 16:46:58 +01001166 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001167 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001168
Michal Vasko5a91ce72017-10-19 11:30:02 +02001169 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001170 ret = NC_MSG_ERROR;
1171 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001172 }
Radek Krejci047300e2016-03-08 16:46:58 +01001173 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001174
Michal Vasko2e6defd2016-10-07 15:48:15 +02001175 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001176 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001177
Radek Krejcife0b3472015-10-12 13:43:42 +02001178 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001179 rpc_elem = va_arg(ap, struct lyxml_elem *);
1180 reply = va_arg(ap, struct nc_server_reply *);
1181
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001182 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1183 nc_write_clb((void *)&arg, "<", 1, 0);
1184 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1185 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001186 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001187 }
1188 else {
1189 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001190 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001191 }
1192
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001193 /* can be NULL if replying with a malformed-message error */
1194 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001195 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001196 nc_write_clb((void *)&arg, ">", 1, 0);
1197 } else {
1198 /* but put there at least the correct namespace */
Michal Vaskobaaa9f02018-01-18 09:12:58 +01001199 nc_write_clb((void *)&arg, " xmlns=\""NC_NS_BASE"\">", 49, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001200 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001201 switch (reply->type) {
1202 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001203 nc_write_clb((void *)&arg, "<", 1, 0);
1204 if (base_prefix) {
1205 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1206 nc_write_clb((void *)&arg, ":", 1, 0);
1207 }
1208 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001209 break;
1210 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001211 switch(((struct nc_server_reply_data *)reply)->wd) {
1212 case NC_WD_UNKNOWN:
1213 case NC_WD_EXPLICIT:
1214 wd = LYP_WD_EXPLICIT;
1215 break;
1216 case NC_WD_TRIM:
1217 wd = LYP_WD_TRIM;
1218 break;
1219 case NC_WD_ALL:
1220 wd = LYP_WD_ALL;
1221 break;
1222 case NC_WD_ALL_TAG:
1223 wd = LYP_WD_ALL_TAG;
1224 break;
1225 }
Michal Vasko5a91ce72017-10-19 11:30:02 +02001226 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
1227 LYP_WITHSIBLINGS | LYP_NETCONF | wd)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001228 ret = NC_MSG_ERROR;
1229 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001230 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001231 break;
1232 case NC_RPL_ERROR:
1233 error_rpl = (struct nc_server_reply_error *)reply;
1234 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001235 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001236 }
1237 break;
1238 default:
1239 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001240 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001241 ret = NC_MSG_ERROR;
1242 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001243 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001244 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1245 nc_write_clb((void *)&arg, "</", 2, 0);
1246 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1247 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1248 }
1249 else {
1250 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1251 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001252 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001253
Radek Krejcife0b3472015-10-12 13:43:42 +02001254 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001255 notif = va_arg(ap, struct nc_server_notif *);
1256
1257 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1258 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1259 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1260 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001261 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001262 ret = NC_MSG_ERROR;
1263 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001264 }
mohitarora24878b2962016-11-09 18:45:33 -05001265 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001266 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001267
Radek Krejcid116db42016-01-08 15:36:30 +01001268 case NC_MSG_HELLO:
1269 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001270 ret = NC_MSG_ERROR;
1271 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001272 }
1273 capabilities = va_arg(ap, const char **);
1274 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001275
Radek Krejcid116db42016-01-08 15:36:30 +01001276 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001277 if (count == -1) {
1278 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001279 ret = NC_MSG_ERROR;
1280 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001281 }
Radek Krejci047300e2016-03-08 16:46:58 +01001282 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001283 free(buf);
1284 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001285 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1286 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1287 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001288 }
1289 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001290 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001291 if (count == -1) {
1292 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001293 ret = NC_MSG_ERROR;
1294 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001295 }
Radek Krejci047300e2016-03-08 16:46:58 +01001296 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001297 free(buf);
1298 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001299 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001300 }
Radek Krejcid116db42016-01-08 15:36:30 +01001301 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001302
Radek Krejcife0b3472015-10-12 13:43:42 +02001303 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001304 ret = NC_MSG_ERROR;
1305 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001306 }
1307
1308 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001309 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001310
Michal Vasko428087d2016-01-14 16:04:28 +01001311 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1312 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001313 ret = NC_MSG_ERROR;
1314 } else {
1315 /* specific message successfully sent */
1316 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001317 }
1318
Michal Vasko131120a2018-05-29 15:44:02 +02001319cleanup:
1320 va_end(ap);
1321 nc_session_io_unlock(session, __func__);
1322 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001323}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001324
1325void *
1326nc_realloc(void *ptr, size_t size)
1327{
1328 void *ret;
1329
1330 ret = realloc(ptr, size);
1331 if (!ret) {
1332 free(ptr);
1333 }
1334
1335 return ret;
1336}
David Sedlákfedbc792018-07-04 11:07:07 +02001337