blob: a0fc9dca677f1e3c03f8aadede2f23391fe73e9a [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;
David Sedlákfedbc792018-07-04 11:07:07 +0200200 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200201
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) {
David Sedlák15dad862018-07-04 11:25:55 +0200218 if (limit && count == limit) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200219 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 */
David Sedlákfedbc792018-07-04 11:07:07 +0200226 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200227 /* get more memory */
228 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200229 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100230 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 */
David Sedlákfedbc792018-07-04 11:07:07 +0200237 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
238 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200239 free(chunk);
240 return -1;
241 }
242
David Sedlákfedbc792018-07-04 11:07:07 +0200243 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200244
David Sedlákfedbc792018-07-04 11:07:07 +0200245 for (i = len - matched; i > 0; i--) {
246 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
247 /*part of endtag found */
248 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200249 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200250 } else {
251 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200252 }
253 }
David Sedlákfedbc792018-07-04 11:07:07 +0200254
255 /* whole endtag found */
256 if (matched == len) {
257 break;
258 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200259 }
260
261 /* terminating null byte */
262 chunk[count] = 0;
263
264 if (result) {
265 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200266 } else {
267 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200268 }
269 return count;
270}
271
Michal Vasko131120a2018-05-29 15:44:02 +0200272/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Radek Krejci206fcd62015-10-07 15:42:48 +0200273NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200274nc_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 +0100275{
Michal Vasko131120a2018-05-29 15:44:02 +0200276 int ret, io_locked = passing_io_lock;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100277 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100278 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100279 /* use timeout in milliseconds instead seconds */
280 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
281 struct timespec ts_act_timeout;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100282 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100283
Michal Vasko428087d2016-01-14 16:04:28 +0100284 assert(session && data);
285 *data = NULL;
286
287 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100288 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +0200289 ret = NC_MSG_ERROR;
290 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100291 }
292
Michal Vasko77a6abe2017-10-05 10:02:20 +0200293 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100294 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
295
Michal Vasko131120a2018-05-29 15:44:02 +0200296 if (!io_locked) {
297 /* SESSION IO LOCK */
298 ret = nc_session_io_lock(session, io_timeout, __func__);
299 if (ret < 0) {
300 ret = NC_MSG_ERROR;
301 goto cleanup;
302 } else if (!ret) {
303 ret = NC_MSG_WOULDBLOCK;
304 goto cleanup;
305 }
306 io_locked = 1;
307 }
308
Michal Vasko05ba9df2016-01-13 14:40:27 +0100309 /* read the message */
310 switch (session->version) {
311 case NC_VERSION_10:
Michal Vasko36c7be82017-02-22 13:37:59 +0100312 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100313 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200314 ret = NC_MSG_ERROR;
315 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100316 }
317
318 /* cut off the end tag */
319 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
320 break;
321 case NC_VERSION_11:
322 while (1) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100323 ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100324 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200325 ret = NC_MSG_ERROR;
326 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100328 ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100329 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200330 ret = NC_MSG_ERROR;
331 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100332 }
333
334 if (!strcmp(chunk, "#\n")) {
335 /* end of chunked framing message */
336 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200337 if (!msg) {
338 ERR("Session %u: invalid frame chunk delimiters.", session->id);
339 goto malformed_msg;
340 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100341 break;
342 }
343
344 /* convert string to the size of the following chunk */
345 chunk_len = strtoul(chunk, (char **)NULL, 10);
346 free(chunk);
347 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100348 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100349 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350 }
351
352 /* now we have size of next chunk, so read the chunk */
Michal Vasko36c7be82017-02-22 13:37:59 +0100353 ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100354 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200355 ret = NC_MSG_ERROR;
356 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100357 }
358
359 /* realloc message buffer, remember to count terminating null byte */
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200360 msg = nc_realloc(msg, len + chunk_len + 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100361 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100362 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200363 ret = NC_MSG_ERROR;
364 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100365 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100366 memcpy(msg + len, chunk, chunk_len);
367 len += chunk_len;
368 msg[len] = '\0';
369 free(chunk);
370 }
371
372 break;
373 }
Michal Vasko131120a2018-05-29 15:44:02 +0200374
375 /* SESSION IO UNLOCK */
376 assert(io_locked);
377 nc_session_io_unlock(session, __func__);
378 io_locked = 0;
379
Michal Vasko81b33fb2016-09-26 14:57:36 +0200380 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100381
382 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100383 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100385 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100387 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100388 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389 }
390 free(msg);
391 msg = NULL;
392
393 /* get and return message type */
394 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
395 if (!strcmp((*data)->name, "rpc")) {
396 return NC_MSG_RPC;
397 } else if (!strcmp((*data)->name, "rpc-reply")) {
398 return NC_MSG_REPLY;
399 } else if (!strcmp((*data)->name, "hello")) {
400 return NC_MSG_HELLO;
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 if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
406 if (!strcmp((*data)->name, "notification")) {
407 return NC_MSG_NOTIF;
408 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100409 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100410 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100411 }
412 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100413 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100414 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100415 }
416
Michal Vasko428087d2016-01-14 16:04:28 +0100417malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100418 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100419 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
420 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100421 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100422
Michal Vaskofc5d07e2018-08-17 10:23:48 +0200423 if (io_locked) {
424 /* nc_write_msg_io locks and unlocks the lock by itself */
425 nc_session_io_unlock(session, __func__);
426 io_locked = 0;
427 }
428
Michal Vasko131120a2018-05-29 15:44:02 +0200429 if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) {
Michal Vaskod083db62016-01-19 10:31:29 +0100430 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100431 if (session->status != NC_STATUS_INVALID) {
432 session->status = NC_STATUS_INVALID;
433 session->term_reason = NC_SESSION_TERM_OTHER;
434 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100435 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100436 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100437 }
Michal Vasko131120a2018-05-29 15:44:02 +0200438 ret = NC_MSG_ERROR;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100439
Michal Vasko131120a2018-05-29 15:44:02 +0200440cleanup:
441 if (io_locked) {
442 nc_session_io_unlock(session, __func__);
443 }
Michal Vasko428087d2016-01-14 16:04:28 +0100444 free(msg);
445 free(*data);
446 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100447
Michal Vasko131120a2018-05-29 15:44:02 +0200448 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100449}
450
Michal Vasko428087d2016-01-14 16:04:28 +0100451/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
452static int
Michal Vasko131120a2018-05-29 15:44:02 +0200453nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100454{
Radek Krejci5961c702016-07-15 09:15:18 +0200455 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100456 int ret = -2;
457 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100458
459 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100460 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100461 return -1;
462 }
463
464 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100465#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100466 case NC_TI_LIBSSH:
467 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200468 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100469 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100470 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100471 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100472 session->status = NC_STATUS_INVALID;
473 session->term_reason = NC_SESSION_TERM_OTHER;
474 return -1;
475 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100476 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100477 session->status = NC_STATUS_INVALID;
478 session->term_reason = NC_SESSION_TERM_DROPPED;
479 return -1;
480 } else if (ret > 0) {
481 /* fake it */
482 ret = 1;
483 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100484 } else { /* ret == 0 */
485 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100486 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100487 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100488#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100489#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100490 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100491 ret = SSL_pending(session->ti.tls);
492 if (ret) {
493 /* some buffered TLS data available */
494 ret = 1;
495 fds.revents = POLLIN;
496 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100497 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100498
499 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100500#endif
Michal Vaskob983c002017-11-02 13:10:57 +0100501 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100502 case NC_TI_FD:
503 if (session->ti_type == NC_TI_FD) {
504 fds.fd = session->ti.fd.in;
505 }
506
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100507 fds.events = POLLIN;
508 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100509
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100510 sigfillset(&sigmask);
511 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200512 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100513 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100514
515 break;
516
517 default:
518 ERRINT;
519 return -1;
520 }
521
522 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
523 if (ret < 0) {
524 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200525 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
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 } else { /* status > 0 */
530 /* in case of standard (non-libssh) poll, there still can be an error */
531 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100532 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100533 session->status = NC_STATUS_INVALID;
534 session->term_reason = NC_SESSION_TERM_DROPPED;
535 return -1;
536 }
537 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100538 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100539 session->status = NC_STATUS_INVALID;
540 session->term_reason = NC_SESSION_TERM_OTHER;
541 return -1;
542 }
543 }
544
545 return ret;
546}
547
Michal Vasko131120a2018-05-29 15:44:02 +0200548/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100549NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200550nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200551{
Michal Vasko428087d2016-01-14 16:04:28 +0100552 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200553
554 assert(data);
555 *data = NULL;
556
Michal Vasko428087d2016-01-14 16:04:28 +0100557 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100558 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100559 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200560 }
561
Michal Vasko131120a2018-05-29 15:44:02 +0200562 /* SESSION IO LOCK */
563 ret = nc_session_io_lock(session, io_timeout, __func__);
564 if (ret < 0) {
565 return NC_MSG_ERROR;
566 } else if (!ret) {
567 return NC_MSG_WOULDBLOCK;
568 }
569
570 ret = nc_read_poll(session, io_timeout);
Michal Vasko428087d2016-01-14 16:04:28 +0100571 if (ret == 0) {
572 /* timed out */
Michal Vasko131120a2018-05-29 15:44:02 +0200573
574 /* SESSION IO UNLOCK */
575 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100576 return NC_MSG_WOULDBLOCK;
577 } else if (ret < 0) {
578 /* poll error, error written */
Michal Vasko131120a2018-05-29 15:44:02 +0200579
580 /* SESSION IO UNLOCK */
581 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100582 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200583 }
584
Michal Vasko131120a2018-05-29 15:44:02 +0200585 /* SESSION IO LOCK passed down */
586 return nc_read_msg_io(session, io_timeout, data, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200587}
Radek Krejcife0b3472015-10-12 13:43:42 +0200588
Michal Vasko428087d2016-01-14 16:04:28 +0100589/* does not really log, only fatal errors */
590int
591nc_session_is_connected(struct nc_session *session)
592{
593 int ret;
594 struct pollfd fds;
595
596 switch (session->ti_type) {
597 case NC_TI_FD:
598 fds.fd = session->ti.fd.in;
599 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100600#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100601 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100602 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100603#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100604#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100605 case NC_TI_OPENSSL:
606 fds.fd = SSL_get_fd(session->ti.tls);
607 break;
608#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100609 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100610 return 0;
611 }
612
Michal Vasko840a8a62017-02-07 10:56:34 +0100613 if (fds.fd == -1) {
614 return 0;
615 }
616
Michal Vasko428087d2016-01-14 16:04:28 +0100617 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100618 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100619
620 errno = 0;
621 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
622
623 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100624 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100625 return 0;
626 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
627 return 0;
628 }
629
630 return 1;
631}
632
Radek Krejcife0b3472015-10-12 13:43:42 +0200633#define WRITE_BUFSIZE (2 * BUFFERSIZE)
634struct wclb_arg {
635 struct nc_session *session;
636 char buf[WRITE_BUFSIZE];
637 size_t len;
638};
639
Michal Vasko964e1732016-09-23 13:39:33 +0200640static int
Michal Vasko428087d2016-01-14 16:04:28 +0100641nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200642{
Michal Vasko964e1732016-09-23 13:39:33 +0200643 int c;
644 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200645#ifdef NC_ENABLED_TLS
646 unsigned long e;
647#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200648
Michal Vasko428087d2016-01-14 16:04:28 +0100649 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
650 return -1;
651 }
652
653 /* prevent SIGPIPE this way */
654 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100655 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100656 session->status = NC_STATUS_INVALID;
657 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100658 return -1;
659 }
660
Michal Vasko81b33fb2016-09-26 14:57:36 +0200661 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200662
Michal Vasko81b33fb2016-09-26 14:57:36 +0200663 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200664 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200665 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200666 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200667 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200668 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200669 return -1;
670 }
671 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200672
Radek Krejci53691be2016-02-22 13:58:37 +0100673#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200674 case NC_TI_LIBSSH:
675 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
676 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
677 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
678 } else {
679 ERR("Session %u: SSH channel unexpected EOF.", session->id);
680 }
681 session->status = NC_STATUS_INVALID;
682 session->term_reason = NC_SESSION_TERM_DROPPED;
683 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100684 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200685 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200686 if ((c == SSH_ERROR) || (c == -1)) {
687 ERR("Session %u: SSH channel write failed.", session->id);
688 return -1;
689 }
690 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200691#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100692#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200693 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200694 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200695 if (c < 1) {
696 switch ((e = SSL_get_error(session->ti.tls, c))) {
697 case SSL_ERROR_ZERO_RETURN:
698 ERR("Session %u: SSL connection was properly closed.", session->id);
699 return -1;
700 case SSL_ERROR_WANT_WRITE:
701 c = 0;
702 break;
703 case SSL_ERROR_SYSCALL:
704 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
705 return -1;
706 case SSL_ERROR_SSL:
707 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
708 return -1;
709 default:
710 ERR("Session %u: unknown SSL error occured.", session->id);
711 return -1;
712 }
713 }
714 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200715#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200716 default:
717 ERRINT;
718 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200719 }
720
721 if (c == 0) {
722 /* we must wait */
723 usleep(NC_TIMEOUT_STEP);
724 }
725
726 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200727 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200728
Michal Vasko964e1732016-09-23 13:39:33 +0200729 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200730}
731
Michal Vasko428087d2016-01-14 16:04:28 +0100732static int
733nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100734{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100735 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100736 char chunksize[20];
737
738 if (session->version == NC_VERSION_11) {
739 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100740 ret = nc_write(session, chunksize, strlen(chunksize));
741 if (ret == -1) {
742 return -1;
743 }
Michal Vasko086311b2016-01-08 09:53:11 +0100744 }
Michal Vasko428087d2016-01-14 16:04:28 +0100745
746 c = nc_write(session, buf, count);
747 if (c == -1) {
748 return -1;
749 }
750 ret += c;
751
752 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100753}
754
Radek Krejcife0b3472015-10-12 13:43:42 +0200755static int
Michal Vasko428087d2016-01-14 16:04:28 +0100756nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200757{
Michal Vasko428087d2016-01-14 16:04:28 +0100758 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100759
Michal Vasko428087d2016-01-14 16:04:28 +0100760 if (session->version == NC_VERSION_11) {
761 ret = nc_write(session, "\n##\n", 4);
762 } else {
763 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200764 }
765
Michal Vasko428087d2016-01-14 16:04:28 +0100766 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200767}
768
Michal Vasko428087d2016-01-14 16:04:28 +0100769static int
770nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200771{
Michal Vasko428087d2016-01-14 16:04:28 +0100772 int ret = 0;
773
Radek Krejcife0b3472015-10-12 13:43:42 +0200774 /* flush current buffer */
775 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100776 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200777 warg->len = 0;
778 }
Michal Vasko428087d2016-01-14 16:04:28 +0100779
780 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200781}
782
783static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100784nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200785{
Michal Vasko428087d2016-01-14 16:04:28 +0100786 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100787 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200788 struct wclb_arg *warg = (struct wclb_arg *)arg;
789
790 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100791 c = nc_write_clb_flush(warg);
792 if (c == -1) {
793 return -1;
794 }
795 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200796
797 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100798 c = nc_write_endtag(warg->session);
799 if (c == -1) {
800 return -1;
801 }
802 ret += c;
803
804 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200805 }
806
807 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
808 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100809 c = nc_write_clb_flush(warg);
810 if (c == -1) {
811 return -1;
812 }
813 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200814 }
Michal Vasko428087d2016-01-14 16:04:28 +0100815
Radek Krejci047300e2016-03-08 16:46:58 +0100816 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200817 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100818 c = nc_write_starttag_and_msg(warg->session, buf, count);
819 if (c == -1) {
820 return -1;
821 }
822 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200823 } else {
824 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100825 if (xmlcontent) {
826 for (l = 0; l < count; l++) {
827 if (warg->len + 5 >= WRITE_BUFSIZE) {
828 /* buffer is full */
829 c = nc_write_clb_flush(warg);
830 if (c == -1) {
831 return -1;
832 }
833 }
834
835 switch (((char *)buf)[l]) {
836 case '&':
837 ret += 5;
838 memcpy(&warg->buf[warg->len], "&amp;", 5);
839 warg->len += 5;
840 break;
841 case '<':
842 ret += 4;
843 memcpy(&warg->buf[warg->len], "&lt;", 4);
844 warg->len += 4;
845 break;
846 case '>':
847 /* not needed, just for readability */
848 ret += 4;
849 memcpy(&warg->buf[warg->len], "&gt;", 4);
850 warg->len += 4;
851 break;
852 default:
853 ret++;
854 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
855 warg->len++;
856 }
857 }
858 } else {
859 memcpy(&warg->buf[warg->len], buf, count);
860 warg->len += count; /* is <= WRITE_BUFSIZE */
861 ret += count;
862 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200863 }
864
Michal Vasko428087d2016-01-14 16:04:28 +0100865 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200866}
867
Radek Krejci047300e2016-03-08 16:46:58 +0100868static ssize_t
869nc_write_xmlclb(void *arg, const void *buf, size_t count)
870{
871 return nc_write_clb(arg, buf, count, 0);
872}
873
Michal Vasko05ba9df2016-01-13 14:40:27 +0100874static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100875nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
876 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100877{
Michal Vasko08611b32016-12-05 13:30:37 +0100878 if (open) {
879 nc_write_clb((void *)arg, "<", 1, 0);
880 } else {
881 nc_write_clb((void *)arg, "</", 2, 0);
882 }
883
884 if (prefix) {
885 nc_write_clb((void *)arg, prefix, pref_len, 0);
886 nc_write_clb((void *)arg, ":", 1, 0);
887 }
888
889 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100890 if (!open || !no_attr) {
891 nc_write_clb((void *)arg, ">", 1, 0);
892 }
Michal Vasko08611b32016-12-05 13:30:37 +0100893}
894
895static void
896nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
897{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100898 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100899 char str_sid[11];
900
Michal Vasko08611b32016-12-05 13:30:37 +0100901 if (prefix) {
902 pref_len = strlen(prefix);
903 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100904
Michal Vasko52bd9492016-12-08 09:37:43 +0100905 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100906
Michal Vasko52bd9492016-12-08 09:37:43 +0100907 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908 switch (err->type) {
909 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100910 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 break;
912 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100913 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100914 break;
915 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100916 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100917 break;
918 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100919 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100920 break;
921 default:
922 ERRINT;
923 return;
924 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100925
Michal Vasko52bd9492016-12-08 09:37:43 +0100926 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100927
Michal Vasko52bd9492016-12-08 09:37:43 +0100928 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100929 switch (err->tag) {
930 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100931 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100932 break;
933 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100934 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100935 break;
936 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100937 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100938 break;
939 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100940 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100941 break;
942 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100943 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100944 break;
945 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100946 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100947 break;
948 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100949 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100950 break;
951 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100952 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100953 break;
954 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100955 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100956 break;
957 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100958 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100959 break;
960 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100961 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100962 break;
963 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100964 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100965 break;
966 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100967 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100968 break;
969 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100970 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100971 break;
972 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100973 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100974 break;
975 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100976 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100977 break;
978 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100979 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100980 break;
981 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100982 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100983 break;
984 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100985 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100986 break;
987 default:
988 ERRINT;
989 return;
990 }
Michal Vasko52bd9492016-12-08 09:37:43 +0100991 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100992
Michal Vasko52bd9492016-12-08 09:37:43 +0100993 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100994 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100995 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100996
997 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100998 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100999 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001000 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001001 }
1002
1003 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001004 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001005 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001006 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001007 }
1008
1009 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001010 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001011 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +01001012 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
1013 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
1014 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001015 }
Radek Krejci047300e2016-03-08 16:46:58 +01001016 nc_write_clb((void *)arg, ">", 1, 0);
1017 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001018 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001019 }
1020
Michal Vasko90920b02016-05-20 14:07:00 +02001021 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001022 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001023
Michal Vasko90920b02016-05-20 14:07:00 +02001024 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001025 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +02001026 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +01001027 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001028 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001029 }
1030
1031 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001032 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001033 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001034 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001035 }
1036
1037 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001038 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001039 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001040 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001041 }
1042
1043 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001044 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001045 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001046 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001047 }
1048
1049 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +01001050 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001051 }
1052
Michal Vasko52bd9492016-12-08 09:37:43 +01001053 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001054 }
1055
Michal Vasko52bd9492016-12-08 09:37:43 +01001056 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001057}
1058
Michal Vasko131120a2018-05-29 15:44:02 +02001059/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
1060NC_MSG_TYPE
1061nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001062{
Radek Krejcid116db42016-01-08 15:36:30 +01001063 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +02001064 int count, ret;
Michal Vasko08611b32016-12-05 13:30:37 +01001065 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001066 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001067 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001068 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001069 struct nc_server_reply *reply;
1070 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001071 char *buf = NULL;
1072 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001073 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001074 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001075 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001076
Michal Vasko428087d2016-01-14 16:04:28 +01001077 assert(session);
1078
1079 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001080 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +02001081 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001082 }
1083
Radek Krejcife0b3472015-10-12 13:43:42 +02001084 arg.session = session;
1085 arg.len = 0;
1086
Michal Vasko131120a2018-05-29 15:44:02 +02001087 /* SESSION IO LOCK */
1088 ret = nc_session_io_lock(session, io_timeout, __func__);
1089 if (ret < 0) {
1090 return NC_MSG_ERROR;
1091 } else if (!ret) {
1092 return NC_MSG_WOULDBLOCK;
1093 }
1094
1095 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +02001096
Radek Krejcife0b3472015-10-12 13:43:42 +02001097 switch (type) {
1098 case NC_MSG_RPC:
1099 content = va_arg(ap, struct lyd_node *);
1100 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001101
Radek Krejcife0b3472015-10-12 13:43:42 +02001102 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001103 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001104 if (count == -1) {
1105 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001106 ret = NC_MSG_ERROR;
1107 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001108 }
Radek Krejci047300e2016-03-08 16:46:58 +01001109 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001110 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001111
Michal Vasko5a91ce72017-10-19 11:30:02 +02001112 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001113 ret = NC_MSG_ERROR;
1114 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001115 }
Radek Krejci047300e2016-03-08 16:46:58 +01001116 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001117
Michal Vasko2e6defd2016-10-07 15:48:15 +02001118 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001119 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001120
Radek Krejcife0b3472015-10-12 13:43:42 +02001121 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001122 rpc_elem = va_arg(ap, struct lyxml_elem *);
1123 reply = va_arg(ap, struct nc_server_reply *);
1124
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001125 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1126 nc_write_clb((void *)&arg, "<", 1, 0);
1127 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1128 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001129 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001130 }
1131 else {
1132 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001133 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001134 }
1135
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001136 /* can be NULL if replying with a malformed-message error */
1137 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001138 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001139 nc_write_clb((void *)&arg, ">", 1, 0);
1140 } else {
1141 /* but put there at least the correct namespace */
Michal Vaskobaaa9f02018-01-18 09:12:58 +01001142 nc_write_clb((void *)&arg, " xmlns=\""NC_NS_BASE"\">", 49, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001143 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001144 switch (reply->type) {
1145 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001146 nc_write_clb((void *)&arg, "<", 1, 0);
1147 if (base_prefix) {
1148 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1149 nc_write_clb((void *)&arg, ":", 1, 0);
1150 }
1151 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001152 break;
1153 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001154 switch(((struct nc_server_reply_data *)reply)->wd) {
1155 case NC_WD_UNKNOWN:
1156 case NC_WD_EXPLICIT:
1157 wd = LYP_WD_EXPLICIT;
1158 break;
1159 case NC_WD_TRIM:
1160 wd = LYP_WD_TRIM;
1161 break;
1162 case NC_WD_ALL:
1163 wd = LYP_WD_ALL;
1164 break;
1165 case NC_WD_ALL_TAG:
1166 wd = LYP_WD_ALL_TAG;
1167 break;
1168 }
Michal Vasko5a91ce72017-10-19 11:30:02 +02001169 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
1170 LYP_WITHSIBLINGS | LYP_NETCONF | wd)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001171 ret = NC_MSG_ERROR;
1172 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001173 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001174 break;
1175 case NC_RPL_ERROR:
1176 error_rpl = (struct nc_server_reply_error *)reply;
1177 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001178 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001179 }
1180 break;
1181 default:
1182 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001183 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001184 ret = NC_MSG_ERROR;
1185 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001186 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001187 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1188 nc_write_clb((void *)&arg, "</", 2, 0);
1189 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1190 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1191 }
1192 else {
1193 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1194 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001195 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001196
Radek Krejcife0b3472015-10-12 13:43:42 +02001197 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001198 notif = va_arg(ap, struct nc_server_notif *);
1199
1200 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1201 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1202 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1203 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001204 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001205 ret = NC_MSG_ERROR;
1206 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001207 }
mohitarora24878b2962016-11-09 18:45:33 -05001208 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001209 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001210
Radek Krejcid116db42016-01-08 15:36:30 +01001211 case NC_MSG_HELLO:
1212 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001213 ret = NC_MSG_ERROR;
1214 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001215 }
1216 capabilities = va_arg(ap, const char **);
1217 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001218
Radek Krejcid116db42016-01-08 15:36:30 +01001219 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001220 if (count == -1) {
1221 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001222 ret = NC_MSG_ERROR;
1223 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001224 }
Radek Krejci047300e2016-03-08 16:46:58 +01001225 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001226 free(buf);
1227 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001228 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1229 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1230 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001231 }
1232 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001233 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001234 if (count == -1) {
1235 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001236 ret = NC_MSG_ERROR;
1237 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001238 }
Radek Krejci047300e2016-03-08 16:46:58 +01001239 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001240 free(buf);
1241 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001242 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001243 }
Radek Krejcid116db42016-01-08 15:36:30 +01001244 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001245
Radek Krejcife0b3472015-10-12 13:43:42 +02001246 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001247 ret = NC_MSG_ERROR;
1248 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001249 }
1250
1251 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001252 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001253
Michal Vasko428087d2016-01-14 16:04:28 +01001254 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1255 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001256 ret = NC_MSG_ERROR;
1257 } else {
1258 /* specific message successfully sent */
1259 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001260 }
1261
Michal Vasko131120a2018-05-29 15:44:02 +02001262cleanup:
1263 va_end(ap);
1264 nc_session_io_unlock(session, __func__);
1265 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001266}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001267
1268void *
1269nc_realloc(void *ptr, size_t size)
1270{
1271 void *ret;
1272
1273 ret = realloc(ptr, size);
1274 if (!ret) {
1275 free(ptr);
1276 }
1277
1278 return ret;
1279}
David Sedlákfedbc792018-07-04 11:07:07 +02001280