blob: a4598ead661f5f8c0c07fc7f86693e869a3ecdb0 [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
37static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010038nc_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 +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 Vasko36c7be82017-02-22 13:37:59 +010042 struct timespec ts_cur, ts_inact_timeout;
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 Vasko77a6abe2017-10-05 10:02:20 +020055 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +010056 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +020057 do {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010058 switch (session->ti_type) {
59 case NC_TI_NONE:
60 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010061
Michal Vasko6b7c42e2016-03-02 15:46:41 +010062 case NC_TI_FD:
63 /* read via standard file descriptor */
Michal Vasko81b33fb2016-09-26 14:57:36 +020064 r = read(session->ti.fd.in, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +020065 if (r < 0) {
Michal Vasko36c7be82017-02-22 13:37:59 +010066 if ((errno == EAGAIN) || (errno == EINTR)) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010067 r = 0;
68 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020069 } else {
Michal Vaskod083db62016-01-19 10:31:29 +010070 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
71 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +010072 session->status = NC_STATUS_INVALID;
73 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020074 return -1;
75 }
76 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +010077 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
78 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +010079 session->status = NC_STATUS_INVALID;
80 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +020081 return -1;
82 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +010083 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020084
Radek Krejci53691be2016-02-22 13:58:37 +010085#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +010086 case NC_TI_LIBSSH:
87 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +020088 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +020089 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010090 r = 0;
91 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020092 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +010093 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
94 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +010095 session->status = NC_STATUS_INVALID;
96 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020097 return -1;
98 } else if (r == 0) {
99 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +0100100 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100101 session->status = NC_STATUS_INVALID;
102 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200103 return -1;
104 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100105 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200106 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100107 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200108#endif
109
Radek Krejci53691be2016-02-22 13:58:37 +0100110#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100111 case NC_TI_OPENSSL:
112 /* read via OpenSSL */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200113 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200114 if (r <= 0) {
115 int x;
116 switch (x = SSL_get_error(session->ti.tls, r)) {
117 case SSL_ERROR_WANT_READ:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100118 r = 0;
119 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200120 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100121 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100122 session->status = NC_STATUS_INVALID;
123 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200124 return -1;
125 default:
Michal Vaskod083db62016-01-19 10:31:29 +0100126 ERR("Session %u: reading from the TLS session failed (SSL code %d).", session->id, x);
Michal Vasko428087d2016-01-14 16:04:28 +0100127 session->status = NC_STATUS_INVALID;
128 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200129 return -1;
130 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200131 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100132 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200133#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100134 }
135
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100136 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100137 /* nothing read */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100138 usleep(NC_TIMEOUT_STEP);
Michal Vasko77a6abe2017-10-05 10:02:20 +0200139 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100140 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
141 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100142 ERR("Session %u: inactive read timeout elapsed.", session->id);
143 } else {
144 ERR("Session %u: active read timeout elapsed.", session->id);
145 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100146 session->status = NC_STATUS_INVALID;
147 session->term_reason = NC_SESSION_TERM_OTHER;
148 return -1;
149 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100150 } else {
151 /* something read */
152 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100153
154 /* reset inactive timeout */
Michal Vasko77a6abe2017-10-05 10:02:20 +0200155 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100156 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100157 }
158
Michal Vasko81b33fb2016-09-26 14:57:36 +0200159 } while (readd < count);
160 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200161
Michal Vasko81b33fb2016-09-26 14:57:36 +0200162 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200163}
164
165static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100166nc_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 +0200167{
168 ssize_t r;
169
170 assert(session);
171 assert(chunk);
172
173 if (!len) {
174 return 0;
175 }
176
Michal Vasko4eb3c312016-03-01 14:09:37 +0100177 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200178 if (!*chunk) {
179 ERRMEM;
180 return -1;
181 }
182
Michal Vasko36c7be82017-02-22 13:37:59 +0100183 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200184 if (r <= 0) {
185 free(*chunk);
186 return -1;
187 }
188
189 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200190 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200191
192 return r;
193}
194
195static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100196nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
197 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200198{
199 char *chunk = NULL;
200 size_t size, count = 0, r, len;
201
202 assert(session);
203 assert(endtag);
204
205 if (limit && limit < BUFFERSIZE) {
206 size = limit;
207 } else {
208 size = BUFFERSIZE;
209 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100210 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200211 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200212 ERRMEM;
213 return -1;
214 }
215
216 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100217 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200218 if (limit && count == limit) {
219 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100220 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
221 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200222 return -1;
223 }
224
225 /* resize buffer if needed */
226 if (count == size) {
227 /* get more memory */
228 size = size + BUFFERSIZE;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100229 chunk = realloc(chunk, (size + 1) * sizeof *chunk);
230 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200231 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200232 return -1;
233 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200234 }
235
236 /* get another character */
Michal Vasko36c7be82017-02-22 13:37:59 +0100237 r = nc_read(session, &(chunk[count]), 1, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200238 if (r != 1) {
239 free(chunk);
240 return -1;
241 }
242
243 count++;
244
245 /* check endtag */
246 if (count >= len) {
247 if (!strncmp(endtag, &(chunk[count - len]), len)) {
248 /* endtag found */
249 break;
250 }
251 }
252 }
253
254 /* terminating null byte */
255 chunk[count] = 0;
256
257 if (result) {
258 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200259 } else {
260 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200261 }
262 return count;
263}
264
Michal Vasko131120a2018-05-29 15:44:02 +0200265/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Radek Krejci206fcd62015-10-07 15:42:48 +0200266NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200267nc_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 +0100268{
Michal Vasko131120a2018-05-29 15:44:02 +0200269 int ret, io_locked = passing_io_lock;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100270 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100271 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100272 /* use timeout in milliseconds instead seconds */
273 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
274 struct timespec ts_act_timeout;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100275 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100276
Michal Vasko428087d2016-01-14 16:04:28 +0100277 assert(session && data);
278 *data = NULL;
279
280 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100281 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +0200282 ret = NC_MSG_ERROR;
283 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100284 }
285
Michal Vasko77a6abe2017-10-05 10:02:20 +0200286 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100287 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
288
Michal Vasko131120a2018-05-29 15:44:02 +0200289 if (!io_locked) {
290 /* SESSION IO LOCK */
291 ret = nc_session_io_lock(session, io_timeout, __func__);
292 if (ret < 0) {
293 ret = NC_MSG_ERROR;
294 goto cleanup;
295 } else if (!ret) {
296 ret = NC_MSG_WOULDBLOCK;
297 goto cleanup;
298 }
299 io_locked = 1;
300 }
301
Michal Vasko05ba9df2016-01-13 14:40:27 +0100302 /* read the message */
303 switch (session->version) {
304 case NC_VERSION_10:
Michal Vasko36c7be82017-02-22 13:37:59 +0100305 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100306 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200307 ret = NC_MSG_ERROR;
308 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100309 }
310
311 /* cut off the end tag */
312 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
313 break;
314 case NC_VERSION_11:
315 while (1) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100316 ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100317 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200318 ret = NC_MSG_ERROR;
319 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100320 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100321 ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100322 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200323 ret = NC_MSG_ERROR;
324 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100325 }
326
327 if (!strcmp(chunk, "#\n")) {
328 /* end of chunked framing message */
329 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200330 if (!msg) {
331 ERR("Session %u: invalid frame chunk delimiters.", session->id);
332 goto malformed_msg;
333 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100334 break;
335 }
336
337 /* convert string to the size of the following chunk */
338 chunk_len = strtoul(chunk, (char **)NULL, 10);
339 free(chunk);
340 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100341 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100342 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100343 }
344
345 /* now we have size of next chunk, so read the chunk */
Michal Vasko36c7be82017-02-22 13:37:59 +0100346 ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100347 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200348 ret = NC_MSG_ERROR;
349 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350 }
351
352 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko4eb3c312016-03-01 14:09:37 +0100353 msg = realloc(msg, len + chunk_len + 1);
354 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100355 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200356 ret = NC_MSG_ERROR;
357 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100358 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100359 memcpy(msg + len, chunk, chunk_len);
360 len += chunk_len;
361 msg[len] = '\0';
362 free(chunk);
363 }
364
365 break;
366 }
Michal Vasko131120a2018-05-29 15:44:02 +0200367
368 /* SESSION IO UNLOCK */
369 assert(io_locked);
370 nc_session_io_unlock(session, __func__);
371 io_locked = 0;
372
Michal Vasko81b33fb2016-09-26 14:57:36 +0200373 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100374
375 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100376 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100378 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100380 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100381 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100382 }
383 free(msg);
384 msg = NULL;
385
386 /* get and return message type */
387 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
388 if (!strcmp((*data)->name, "rpc")) {
389 return NC_MSG_RPC;
390 } else if (!strcmp((*data)->name, "rpc-reply")) {
391 return NC_MSG_REPLY;
392 } else if (!strcmp((*data)->name, "hello")) {
393 return NC_MSG_HELLO;
394 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100395 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100396 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100397 }
398 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
399 if (!strcmp((*data)->name, "notification")) {
400 return NC_MSG_NOTIF;
401 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100402 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100403 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100404 }
405 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100406 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100407 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100408 }
409
Michal Vasko428087d2016-01-14 16:04:28 +0100410malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100411 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100412 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
413 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100414 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100415
Michal Vasko131120a2018-05-29 15:44:02 +0200416 if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) {
Michal Vaskod083db62016-01-19 10:31:29 +0100417 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100418 if (session->status != NC_STATUS_INVALID) {
419 session->status = NC_STATUS_INVALID;
420 session->term_reason = NC_SESSION_TERM_OTHER;
421 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100423 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100424 }
Michal Vasko131120a2018-05-29 15:44:02 +0200425 ret = NC_MSG_ERROR;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100426
Michal Vasko131120a2018-05-29 15:44:02 +0200427cleanup:
428 if (io_locked) {
429 nc_session_io_unlock(session, __func__);
430 }
Michal Vasko428087d2016-01-14 16:04:28 +0100431 free(msg);
432 free(*data);
433 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100434
Michal Vasko131120a2018-05-29 15:44:02 +0200435 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100436}
437
Michal Vasko428087d2016-01-14 16:04:28 +0100438/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
439static int
Michal Vasko131120a2018-05-29 15:44:02 +0200440nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100441{
Radek Krejci5961c702016-07-15 09:15:18 +0200442 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100443 int ret = -2;
444 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100445
446 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100447 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100448 return -1;
449 }
450
451 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100452#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100453 case NC_TI_LIBSSH:
454 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200455 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100456 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100457 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100458 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100459 session->status = NC_STATUS_INVALID;
460 session->term_reason = NC_SESSION_TERM_OTHER;
461 return -1;
462 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100463 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100464 session->status = NC_STATUS_INVALID;
465 session->term_reason = NC_SESSION_TERM_DROPPED;
466 return -1;
467 } else if (ret > 0) {
468 /* fake it */
469 ret = 1;
470 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100471 } else { /* ret == 0 */
472 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100473 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100474 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100475#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100476#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100477 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100478 ret = SSL_pending(session->ti.tls);
479 if (ret) {
480 /* some buffered TLS data available */
481 ret = 1;
482 fds.revents = POLLIN;
483 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100484 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100485
486 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100487#endif
Michal Vaskob983c002017-11-02 13:10:57 +0100488 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100489 case NC_TI_FD:
490 if (session->ti_type == NC_TI_FD) {
491 fds.fd = session->ti.fd.in;
492 }
493
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100494 fds.events = POLLIN;
495 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100496
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100497 sigfillset(&sigmask);
498 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200499 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100500 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100501
502 break;
503
504 default:
505 ERRINT;
506 return -1;
507 }
508
509 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
510 if (ret < 0) {
511 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200512 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100513 session->status = NC_STATUS_INVALID;
514 session->term_reason = NC_SESSION_TERM_OTHER;
515 return -1;
516 } else { /* status > 0 */
517 /* in case of standard (non-libssh) poll, there still can be an error */
518 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100519 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100520 session->status = NC_STATUS_INVALID;
521 session->term_reason = NC_SESSION_TERM_DROPPED;
522 return -1;
523 }
524 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100525 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100526 session->status = NC_STATUS_INVALID;
527 session->term_reason = NC_SESSION_TERM_OTHER;
528 return -1;
529 }
530 }
531
532 return ret;
533}
534
Michal Vasko131120a2018-05-29 15:44:02 +0200535/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100536NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200537nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200538{
Michal Vasko428087d2016-01-14 16:04:28 +0100539 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200540
541 assert(data);
542 *data = NULL;
543
Michal Vasko428087d2016-01-14 16:04:28 +0100544 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100545 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100546 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200547 }
548
Michal Vasko131120a2018-05-29 15:44:02 +0200549 /* SESSION IO LOCK */
550 ret = nc_session_io_lock(session, io_timeout, __func__);
551 if (ret < 0) {
552 return NC_MSG_ERROR;
553 } else if (!ret) {
554 return NC_MSG_WOULDBLOCK;
555 }
556
557 ret = nc_read_poll(session, io_timeout);
Michal Vasko428087d2016-01-14 16:04:28 +0100558 if (ret == 0) {
559 /* timed out */
Michal Vasko131120a2018-05-29 15:44:02 +0200560
561 /* SESSION IO UNLOCK */
562 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100563 return NC_MSG_WOULDBLOCK;
564 } else if (ret < 0) {
565 /* poll error, error written */
Michal Vasko131120a2018-05-29 15:44:02 +0200566
567 /* SESSION IO UNLOCK */
568 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100569 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200570 }
571
Michal Vasko131120a2018-05-29 15:44:02 +0200572 /* SESSION IO LOCK passed down */
573 return nc_read_msg_io(session, io_timeout, data, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200574}
Radek Krejcife0b3472015-10-12 13:43:42 +0200575
Michal Vasko428087d2016-01-14 16:04:28 +0100576/* does not really log, only fatal errors */
577int
578nc_session_is_connected(struct nc_session *session)
579{
580 int ret;
581 struct pollfd fds;
582
583 switch (session->ti_type) {
584 case NC_TI_FD:
585 fds.fd = session->ti.fd.in;
586 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100587#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100588 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100589 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100590#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100591#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100592 case NC_TI_OPENSSL:
593 fds.fd = SSL_get_fd(session->ti.tls);
594 break;
595#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100596 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100597 return 0;
598 }
599
Michal Vasko840a8a62017-02-07 10:56:34 +0100600 if (fds.fd == -1) {
601 return 0;
602 }
603
Michal Vasko428087d2016-01-14 16:04:28 +0100604 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100605 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100606
607 errno = 0;
608 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
609
610 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100611 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100612 return 0;
613 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
614 return 0;
615 }
616
617 return 1;
618}
619
Radek Krejcife0b3472015-10-12 13:43:42 +0200620#define WRITE_BUFSIZE (2 * BUFFERSIZE)
621struct wclb_arg {
622 struct nc_session *session;
623 char buf[WRITE_BUFSIZE];
624 size_t len;
625};
626
Michal Vasko964e1732016-09-23 13:39:33 +0200627static int
Michal Vasko428087d2016-01-14 16:04:28 +0100628nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200629{
Michal Vasko964e1732016-09-23 13:39:33 +0200630 int c;
631 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200632#ifdef NC_ENABLED_TLS
633 unsigned long e;
634#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200635
Michal Vasko428087d2016-01-14 16:04:28 +0100636 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
637 return -1;
638 }
639
640 /* prevent SIGPIPE this way */
641 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100642 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100643 session->status = NC_STATUS_INVALID;
644 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100645 return -1;
646 }
647
Michal Vasko81b33fb2016-09-26 14:57:36 +0200648 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200649
Michal Vasko81b33fb2016-09-26 14:57:36 +0200650 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200651 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200652 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200653 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200654 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200655 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200656 return -1;
657 }
658 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200659
Radek Krejci53691be2016-02-22 13:58:37 +0100660#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200661 case NC_TI_LIBSSH:
662 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
663 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
664 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
665 } else {
666 ERR("Session %u: SSH channel unexpected EOF.", session->id);
667 }
668 session->status = NC_STATUS_INVALID;
669 session->term_reason = NC_SESSION_TERM_DROPPED;
670 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100671 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200672 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200673 if ((c == SSH_ERROR) || (c == -1)) {
674 ERR("Session %u: SSH channel write failed.", session->id);
675 return -1;
676 }
677 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200678#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100679#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200680 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200681 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200682 if (c < 1) {
683 switch ((e = SSL_get_error(session->ti.tls, c))) {
684 case SSL_ERROR_ZERO_RETURN:
685 ERR("Session %u: SSL connection was properly closed.", session->id);
686 return -1;
687 case SSL_ERROR_WANT_WRITE:
688 c = 0;
689 break;
690 case SSL_ERROR_SYSCALL:
691 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
692 return -1;
693 case SSL_ERROR_SSL:
694 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
695 return -1;
696 default:
697 ERR("Session %u: unknown SSL error occured.", session->id);
698 return -1;
699 }
700 }
701 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200702#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200703 default:
704 ERRINT;
705 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200706 }
707
708 if (c == 0) {
709 /* we must wait */
710 usleep(NC_TIMEOUT_STEP);
711 }
712
713 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200714 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200715
Michal Vasko964e1732016-09-23 13:39:33 +0200716 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200717}
718
Michal Vasko428087d2016-01-14 16:04:28 +0100719static int
720nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100721{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100722 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100723 char chunksize[20];
724
725 if (session->version == NC_VERSION_11) {
726 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100727 ret = nc_write(session, chunksize, strlen(chunksize));
728 if (ret == -1) {
729 return -1;
730 }
Michal Vasko086311b2016-01-08 09:53:11 +0100731 }
Michal Vasko428087d2016-01-14 16:04:28 +0100732
733 c = nc_write(session, buf, count);
734 if (c == -1) {
735 return -1;
736 }
737 ret += c;
738
739 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100740}
741
Radek Krejcife0b3472015-10-12 13:43:42 +0200742static int
Michal Vasko428087d2016-01-14 16:04:28 +0100743nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200744{
Michal Vasko428087d2016-01-14 16:04:28 +0100745 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100746
Michal Vasko428087d2016-01-14 16:04:28 +0100747 if (session->version == NC_VERSION_11) {
748 ret = nc_write(session, "\n##\n", 4);
749 } else {
750 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200751 }
752
Michal Vasko428087d2016-01-14 16:04:28 +0100753 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200754}
755
Michal Vasko428087d2016-01-14 16:04:28 +0100756static int
757nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200758{
Michal Vasko428087d2016-01-14 16:04:28 +0100759 int ret = 0;
760
Radek Krejcife0b3472015-10-12 13:43:42 +0200761 /* flush current buffer */
762 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100763 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200764 warg->len = 0;
765 }
Michal Vasko428087d2016-01-14 16:04:28 +0100766
767 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200768}
769
770static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100771nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200772{
Michal Vasko428087d2016-01-14 16:04:28 +0100773 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100774 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200775 struct wclb_arg *warg = (struct wclb_arg *)arg;
776
777 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100778 c = nc_write_clb_flush(warg);
779 if (c == -1) {
780 return -1;
781 }
782 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200783
784 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100785 c = nc_write_endtag(warg->session);
786 if (c == -1) {
787 return -1;
788 }
789 ret += c;
790
791 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200792 }
793
794 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
795 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100796 c = nc_write_clb_flush(warg);
797 if (c == -1) {
798 return -1;
799 }
800 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200801 }
Michal Vasko428087d2016-01-14 16:04:28 +0100802
Radek Krejci047300e2016-03-08 16:46:58 +0100803 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200804 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100805 c = nc_write_starttag_and_msg(warg->session, buf, count);
806 if (c == -1) {
807 return -1;
808 }
809 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200810 } else {
811 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100812 if (xmlcontent) {
813 for (l = 0; l < count; l++) {
814 if (warg->len + 5 >= WRITE_BUFSIZE) {
815 /* buffer is full */
816 c = nc_write_clb_flush(warg);
817 if (c == -1) {
818 return -1;
819 }
820 }
821
822 switch (((char *)buf)[l]) {
823 case '&':
824 ret += 5;
825 memcpy(&warg->buf[warg->len], "&amp;", 5);
826 warg->len += 5;
827 break;
828 case '<':
829 ret += 4;
830 memcpy(&warg->buf[warg->len], "&lt;", 4);
831 warg->len += 4;
832 break;
833 case '>':
834 /* not needed, just for readability */
835 ret += 4;
836 memcpy(&warg->buf[warg->len], "&gt;", 4);
837 warg->len += 4;
838 break;
839 default:
840 ret++;
841 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
842 warg->len++;
843 }
844 }
845 } else {
846 memcpy(&warg->buf[warg->len], buf, count);
847 warg->len += count; /* is <= WRITE_BUFSIZE */
848 ret += count;
849 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200850 }
851
Michal Vasko428087d2016-01-14 16:04:28 +0100852 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200853}
854
Radek Krejci047300e2016-03-08 16:46:58 +0100855static ssize_t
856nc_write_xmlclb(void *arg, const void *buf, size_t count)
857{
858 return nc_write_clb(arg, buf, count, 0);
859}
860
Michal Vasko05ba9df2016-01-13 14:40:27 +0100861static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100862nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
863 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100864{
Michal Vasko08611b32016-12-05 13:30:37 +0100865 if (open) {
866 nc_write_clb((void *)arg, "<", 1, 0);
867 } else {
868 nc_write_clb((void *)arg, "</", 2, 0);
869 }
870
871 if (prefix) {
872 nc_write_clb((void *)arg, prefix, pref_len, 0);
873 nc_write_clb((void *)arg, ":", 1, 0);
874 }
875
876 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100877 if (!open || !no_attr) {
878 nc_write_clb((void *)arg, ">", 1, 0);
879 }
Michal Vasko08611b32016-12-05 13:30:37 +0100880}
881
882static void
883nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
884{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100885 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100886 char str_sid[11];
887
Michal Vasko08611b32016-12-05 13:30:37 +0100888 if (prefix) {
889 pref_len = strlen(prefix);
890 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100891
Michal Vasko52bd9492016-12-08 09:37:43 +0100892 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100893
Michal Vasko52bd9492016-12-08 09:37:43 +0100894 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100895 switch (err->type) {
896 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100897 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100898 break;
899 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100900 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100901 break;
902 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100903 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100904 break;
905 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100906 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100907 break;
908 default:
909 ERRINT;
910 return;
911 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100912
Michal Vasko52bd9492016-12-08 09:37:43 +0100913 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100914
Michal Vasko52bd9492016-12-08 09:37:43 +0100915 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100916 switch (err->tag) {
917 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100918 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100919 break;
920 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100921 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100922 break;
923 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100924 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100925 break;
926 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100927 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100928 break;
929 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100930 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100931 break;
932 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100933 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100934 break;
935 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100936 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100937 break;
938 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100939 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100940 break;
941 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100942 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100943 break;
944 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100945 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100946 break;
947 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100948 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100949 break;
950 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100951 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100952 break;
953 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100954 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100955 break;
956 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100957 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100958 break;
959 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100960 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100961 break;
962 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100963 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100964 break;
965 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100966 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100967 break;
968 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100969 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100970 break;
971 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100972 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100973 break;
974 default:
975 ERRINT;
976 return;
977 }
Michal Vasko52bd9492016-12-08 09:37:43 +0100978 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100979
Michal Vasko52bd9492016-12-08 09:37:43 +0100980 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100981 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100982 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100983
984 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100985 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100986 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100987 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100988 }
989
990 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100991 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100992 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100993 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100994 }
995
996 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100997 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100998 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +0100999 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
1000 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
1001 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001002 }
Radek Krejci047300e2016-03-08 16:46:58 +01001003 nc_write_clb((void *)arg, ">", 1, 0);
1004 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001005 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001006 }
1007
Michal Vasko90920b02016-05-20 14:07:00 +02001008 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001009 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001010
Michal Vasko90920b02016-05-20 14:07:00 +02001011 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001012 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +02001013 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +01001014 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001015 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001016 }
1017
1018 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001019 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001020 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001021 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001022 }
1023
1024 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001025 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001026 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001027 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001028 }
1029
1030 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001031 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001032 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001033 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001034 }
1035
1036 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +01001037 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001038 }
1039
Michal Vasko52bd9492016-12-08 09:37:43 +01001040 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001041 }
1042
Michal Vasko52bd9492016-12-08 09:37:43 +01001043 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001044}
1045
Michal Vasko131120a2018-05-29 15:44:02 +02001046/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
1047NC_MSG_TYPE
1048nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001049{
Radek Krejcid116db42016-01-08 15:36:30 +01001050 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +02001051 int count, ret;
Michal Vasko08611b32016-12-05 13:30:37 +01001052 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001053 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001054 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001055 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001056 struct nc_server_reply *reply;
1057 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001058 char *buf = NULL;
1059 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001060 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001061 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001062 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001063
Michal Vasko428087d2016-01-14 16:04:28 +01001064 assert(session);
1065
1066 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001067 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +02001068 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001069 }
1070
Radek Krejcife0b3472015-10-12 13:43:42 +02001071 arg.session = session;
1072 arg.len = 0;
1073
Michal Vasko131120a2018-05-29 15:44:02 +02001074 /* SESSION IO LOCK */
1075 ret = nc_session_io_lock(session, io_timeout, __func__);
1076 if (ret < 0) {
1077 return NC_MSG_ERROR;
1078 } else if (!ret) {
1079 return NC_MSG_WOULDBLOCK;
1080 }
1081
1082 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +02001083
Radek Krejcife0b3472015-10-12 13:43:42 +02001084 switch (type) {
1085 case NC_MSG_RPC:
1086 content = va_arg(ap, struct lyd_node *);
1087 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001088
Radek Krejcife0b3472015-10-12 13:43:42 +02001089 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001090 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001091 if (count == -1) {
1092 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001093 ret = NC_MSG_ERROR;
1094 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001095 }
Radek Krejci047300e2016-03-08 16:46:58 +01001096 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001097 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001098
Michal Vasko5a91ce72017-10-19 11:30:02 +02001099 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001100 ret = NC_MSG_ERROR;
1101 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001102 }
Radek Krejci047300e2016-03-08 16:46:58 +01001103 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001104
Michal Vasko2e6defd2016-10-07 15:48:15 +02001105 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001106 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001107
Radek Krejcife0b3472015-10-12 13:43:42 +02001108 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001109 rpc_elem = va_arg(ap, struct lyxml_elem *);
1110 reply = va_arg(ap, struct nc_server_reply *);
1111
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001112 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1113 nc_write_clb((void *)&arg, "<", 1, 0);
1114 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1115 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001116 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001117 }
1118 else {
1119 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001120 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001121 }
1122
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001123 /* can be NULL if replying with a malformed-message error */
1124 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001125 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001126 nc_write_clb((void *)&arg, ">", 1, 0);
1127 } else {
1128 /* but put there at least the correct namespace */
Michal Vaskobaaa9f02018-01-18 09:12:58 +01001129 nc_write_clb((void *)&arg, " xmlns=\""NC_NS_BASE"\">", 49, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001130 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001131 switch (reply->type) {
1132 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001133 nc_write_clb((void *)&arg, "<", 1, 0);
1134 if (base_prefix) {
1135 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1136 nc_write_clb((void *)&arg, ":", 1, 0);
1137 }
1138 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001139 break;
1140 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001141 switch(((struct nc_server_reply_data *)reply)->wd) {
1142 case NC_WD_UNKNOWN:
1143 case NC_WD_EXPLICIT:
1144 wd = LYP_WD_EXPLICIT;
1145 break;
1146 case NC_WD_TRIM:
1147 wd = LYP_WD_TRIM;
1148 break;
1149 case NC_WD_ALL:
1150 wd = LYP_WD_ALL;
1151 break;
1152 case NC_WD_ALL_TAG:
1153 wd = LYP_WD_ALL_TAG;
1154 break;
1155 }
Michal Vasko5a91ce72017-10-19 11:30:02 +02001156 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
1157 LYP_WITHSIBLINGS | LYP_NETCONF | wd)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001158 ret = NC_MSG_ERROR;
1159 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001160 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001161 break;
1162 case NC_RPL_ERROR:
1163 error_rpl = (struct nc_server_reply_error *)reply;
1164 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001165 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001166 }
1167 break;
1168 default:
1169 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001170 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001171 ret = NC_MSG_ERROR;
1172 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001173 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001174 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1175 nc_write_clb((void *)&arg, "</", 2, 0);
1176 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1177 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1178 }
1179 else {
1180 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1181 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001182 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001183
Radek Krejcife0b3472015-10-12 13:43:42 +02001184 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001185 notif = va_arg(ap, struct nc_server_notif *);
1186
1187 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1188 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1189 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1190 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001191 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001192 ret = NC_MSG_ERROR;
1193 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001194 }
mohitarora24878b2962016-11-09 18:45:33 -05001195 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001196 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001197
Radek Krejcid116db42016-01-08 15:36:30 +01001198 case NC_MSG_HELLO:
1199 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001200 ret = NC_MSG_ERROR;
1201 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001202 }
1203 capabilities = va_arg(ap, const char **);
1204 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001205
Radek Krejcid116db42016-01-08 15:36:30 +01001206 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001207 if (count == -1) {
1208 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001209 ret = NC_MSG_ERROR;
1210 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001211 }
Radek Krejci047300e2016-03-08 16:46:58 +01001212 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001213 free(buf);
1214 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001215 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1216 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1217 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001218 }
1219 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001220 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001221 if (count == -1) {
1222 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001223 ret = NC_MSG_ERROR;
1224 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001225 }
Radek Krejci047300e2016-03-08 16:46:58 +01001226 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001227 free(buf);
1228 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001229 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001230 }
Radek Krejcid116db42016-01-08 15:36:30 +01001231 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001232
Radek Krejcife0b3472015-10-12 13:43:42 +02001233 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001234 ret = NC_MSG_ERROR;
1235 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001236 }
1237
1238 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001239 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001240
Michal Vasko428087d2016-01-14 16:04:28 +01001241 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1242 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001243 ret = NC_MSG_ERROR;
1244 } else {
1245 /* specific message successfully sent */
1246 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001247 }
1248
Michal Vasko131120a2018-05-29 15:44:02 +02001249cleanup:
1250 va_end(ap);
1251 nc_session_io_unlock(session, __func__);
1252 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001253}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001254
1255void *
1256nc_realloc(void *ptr, size_t size)
1257{
1258 void *ret;
1259
1260 ret = realloc(ptr, size);
1261 if (!ret) {
1262 free(ptr);
1263 }
1264
1265 return ret;
1266}