blob: 1748abf69196f11e7c1af8da3607128b3223d1e2 [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ákfedbc792018-07-04 11:07:07 +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;
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 */
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 */
Michal Vasko4eb3c312016-03-01 14:09:37 +0100360 msg = realloc(msg, len + chunk_len + 1);
361 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 Vasko131120a2018-05-29 15:44:02 +0200423 if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) {
Michal Vaskod083db62016-01-19 10:31:29 +0100424 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100425 if (session->status != NC_STATUS_INVALID) {
426 session->status = NC_STATUS_INVALID;
427 session->term_reason = NC_SESSION_TERM_OTHER;
428 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100429 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100430 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100431 }
Michal Vasko131120a2018-05-29 15:44:02 +0200432 ret = NC_MSG_ERROR;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100433
Michal Vasko131120a2018-05-29 15:44:02 +0200434cleanup:
435 if (io_locked) {
436 nc_session_io_unlock(session, __func__);
437 }
Michal Vasko428087d2016-01-14 16:04:28 +0100438 free(msg);
439 free(*data);
440 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100441
Michal Vasko131120a2018-05-29 15:44:02 +0200442 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100443}
444
Michal Vasko428087d2016-01-14 16:04:28 +0100445/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
446static int
Michal Vasko131120a2018-05-29 15:44:02 +0200447nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100448{
Radek Krejci5961c702016-07-15 09:15:18 +0200449 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100450 int ret = -2;
451 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100452
453 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100454 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100455 return -1;
456 }
457
458 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100459#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100460 case NC_TI_LIBSSH:
461 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200462 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100463 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100464 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100465 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100466 session->status = NC_STATUS_INVALID;
467 session->term_reason = NC_SESSION_TERM_OTHER;
468 return -1;
469 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100470 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100471 session->status = NC_STATUS_INVALID;
472 session->term_reason = NC_SESSION_TERM_DROPPED;
473 return -1;
474 } else if (ret > 0) {
475 /* fake it */
476 ret = 1;
477 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100478 } else { /* ret == 0 */
479 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100480 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100481 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100482#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100483#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100484 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100485 ret = SSL_pending(session->ti.tls);
486 if (ret) {
487 /* some buffered TLS data available */
488 ret = 1;
489 fds.revents = POLLIN;
490 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100491 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100492
493 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100494#endif
Michal Vaskob983c002017-11-02 13:10:57 +0100495 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100496 case NC_TI_FD:
497 if (session->ti_type == NC_TI_FD) {
498 fds.fd = session->ti.fd.in;
499 }
500
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100501 fds.events = POLLIN;
502 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100503
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100504 sigfillset(&sigmask);
505 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200506 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100507 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100508
509 break;
510
511 default:
512 ERRINT;
513 return -1;
514 }
515
516 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
517 if (ret < 0) {
518 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200519 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100520 session->status = NC_STATUS_INVALID;
521 session->term_reason = NC_SESSION_TERM_OTHER;
522 return -1;
523 } else { /* status > 0 */
524 /* in case of standard (non-libssh) poll, there still can be an error */
525 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100526 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100527 session->status = NC_STATUS_INVALID;
528 session->term_reason = NC_SESSION_TERM_DROPPED;
529 return -1;
530 }
531 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100532 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100533 session->status = NC_STATUS_INVALID;
534 session->term_reason = NC_SESSION_TERM_OTHER;
535 return -1;
536 }
537 }
538
539 return ret;
540}
541
Michal Vasko131120a2018-05-29 15:44:02 +0200542/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100543NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200544nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200545{
Michal Vasko428087d2016-01-14 16:04:28 +0100546 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200547
548 assert(data);
549 *data = NULL;
550
Michal Vasko428087d2016-01-14 16:04:28 +0100551 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100552 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100553 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200554 }
555
Michal Vasko131120a2018-05-29 15:44:02 +0200556 /* SESSION IO LOCK */
557 ret = nc_session_io_lock(session, io_timeout, __func__);
558 if (ret < 0) {
559 return NC_MSG_ERROR;
560 } else if (!ret) {
561 return NC_MSG_WOULDBLOCK;
562 }
563
564 ret = nc_read_poll(session, io_timeout);
Michal Vasko428087d2016-01-14 16:04:28 +0100565 if (ret == 0) {
566 /* timed out */
Michal Vasko131120a2018-05-29 15:44:02 +0200567
568 /* SESSION IO UNLOCK */
569 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100570 return NC_MSG_WOULDBLOCK;
571 } else if (ret < 0) {
572 /* poll error, error written */
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_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200577 }
578
Michal Vasko131120a2018-05-29 15:44:02 +0200579 /* SESSION IO LOCK passed down */
580 return nc_read_msg_io(session, io_timeout, data, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200581}
Radek Krejcife0b3472015-10-12 13:43:42 +0200582
Michal Vasko428087d2016-01-14 16:04:28 +0100583/* does not really log, only fatal errors */
584int
585nc_session_is_connected(struct nc_session *session)
586{
587 int ret;
588 struct pollfd fds;
589
590 switch (session->ti_type) {
591 case NC_TI_FD:
592 fds.fd = session->ti.fd.in;
593 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100594#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100595 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100596 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100597#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100598#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100599 case NC_TI_OPENSSL:
600 fds.fd = SSL_get_fd(session->ti.tls);
601 break;
602#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100603 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100604 return 0;
605 }
606
Michal Vasko840a8a62017-02-07 10:56:34 +0100607 if (fds.fd == -1) {
608 return 0;
609 }
610
Michal Vasko428087d2016-01-14 16:04:28 +0100611 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100612 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100613
614 errno = 0;
615 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
616
617 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100618 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100619 return 0;
620 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
621 return 0;
622 }
623
624 return 1;
625}
626
Radek Krejcife0b3472015-10-12 13:43:42 +0200627#define WRITE_BUFSIZE (2 * BUFFERSIZE)
628struct wclb_arg {
629 struct nc_session *session;
630 char buf[WRITE_BUFSIZE];
631 size_t len;
632};
633
Michal Vasko964e1732016-09-23 13:39:33 +0200634static int
Michal Vasko428087d2016-01-14 16:04:28 +0100635nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200636{
Michal Vasko964e1732016-09-23 13:39:33 +0200637 int c;
638 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200639#ifdef NC_ENABLED_TLS
640 unsigned long e;
641#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200642
Michal Vasko428087d2016-01-14 16:04:28 +0100643 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
644 return -1;
645 }
646
647 /* prevent SIGPIPE this way */
648 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100649 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100650 session->status = NC_STATUS_INVALID;
651 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100652 return -1;
653 }
654
Michal Vasko81b33fb2016-09-26 14:57:36 +0200655 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200656
Michal Vasko81b33fb2016-09-26 14:57:36 +0200657 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200658 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200659 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200660 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200661 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200662 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200663 return -1;
664 }
665 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200666
Radek Krejci53691be2016-02-22 13:58:37 +0100667#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200668 case NC_TI_LIBSSH:
669 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
670 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
671 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
672 } else {
673 ERR("Session %u: SSH channel unexpected EOF.", session->id);
674 }
675 session->status = NC_STATUS_INVALID;
676 session->term_reason = NC_SESSION_TERM_DROPPED;
677 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100678 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200679 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200680 if ((c == SSH_ERROR) || (c == -1)) {
681 ERR("Session %u: SSH channel write failed.", session->id);
682 return -1;
683 }
684 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200685#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100686#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200687 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200688 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200689 if (c < 1) {
690 switch ((e = SSL_get_error(session->ti.tls, c))) {
691 case SSL_ERROR_ZERO_RETURN:
692 ERR("Session %u: SSL connection was properly closed.", session->id);
693 return -1;
694 case SSL_ERROR_WANT_WRITE:
695 c = 0;
696 break;
697 case SSL_ERROR_SYSCALL:
698 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
699 return -1;
700 case SSL_ERROR_SSL:
701 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
702 return -1;
703 default:
704 ERR("Session %u: unknown SSL error occured.", session->id);
705 return -1;
706 }
707 }
708 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200709#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200710 default:
711 ERRINT;
712 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200713 }
714
715 if (c == 0) {
716 /* we must wait */
717 usleep(NC_TIMEOUT_STEP);
718 }
719
720 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200721 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200722
Michal Vasko964e1732016-09-23 13:39:33 +0200723 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200724}
725
Michal Vasko428087d2016-01-14 16:04:28 +0100726static int
727nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100728{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100729 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100730 char chunksize[20];
731
732 if (session->version == NC_VERSION_11) {
733 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100734 ret = nc_write(session, chunksize, strlen(chunksize));
735 if (ret == -1) {
736 return -1;
737 }
Michal Vasko086311b2016-01-08 09:53:11 +0100738 }
Michal Vasko428087d2016-01-14 16:04:28 +0100739
740 c = nc_write(session, buf, count);
741 if (c == -1) {
742 return -1;
743 }
744 ret += c;
745
746 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100747}
748
Radek Krejcife0b3472015-10-12 13:43:42 +0200749static int
Michal Vasko428087d2016-01-14 16:04:28 +0100750nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200751{
Michal Vasko428087d2016-01-14 16:04:28 +0100752 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100753
Michal Vasko428087d2016-01-14 16:04:28 +0100754 if (session->version == NC_VERSION_11) {
755 ret = nc_write(session, "\n##\n", 4);
756 } else {
757 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200758 }
759
Michal Vasko428087d2016-01-14 16:04:28 +0100760 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200761}
762
Michal Vasko428087d2016-01-14 16:04:28 +0100763static int
764nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200765{
Michal Vasko428087d2016-01-14 16:04:28 +0100766 int ret = 0;
767
Radek Krejcife0b3472015-10-12 13:43:42 +0200768 /* flush current buffer */
769 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100770 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200771 warg->len = 0;
772 }
Michal Vasko428087d2016-01-14 16:04:28 +0100773
774 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200775}
776
777static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100778nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200779{
Michal Vasko428087d2016-01-14 16:04:28 +0100780 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100781 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200782 struct wclb_arg *warg = (struct wclb_arg *)arg;
783
784 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100785 c = nc_write_clb_flush(warg);
786 if (c == -1) {
787 return -1;
788 }
789 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200790
791 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100792 c = nc_write_endtag(warg->session);
793 if (c == -1) {
794 return -1;
795 }
796 ret += c;
797
798 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200799 }
800
801 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
802 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100803 c = nc_write_clb_flush(warg);
804 if (c == -1) {
805 return -1;
806 }
807 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200808 }
Michal Vasko428087d2016-01-14 16:04:28 +0100809
Radek Krejci047300e2016-03-08 16:46:58 +0100810 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200811 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100812 c = nc_write_starttag_and_msg(warg->session, buf, count);
813 if (c == -1) {
814 return -1;
815 }
816 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200817 } else {
818 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100819 if (xmlcontent) {
820 for (l = 0; l < count; l++) {
821 if (warg->len + 5 >= WRITE_BUFSIZE) {
822 /* buffer is full */
823 c = nc_write_clb_flush(warg);
824 if (c == -1) {
825 return -1;
826 }
827 }
828
829 switch (((char *)buf)[l]) {
830 case '&':
831 ret += 5;
832 memcpy(&warg->buf[warg->len], "&amp;", 5);
833 warg->len += 5;
834 break;
835 case '<':
836 ret += 4;
837 memcpy(&warg->buf[warg->len], "&lt;", 4);
838 warg->len += 4;
839 break;
840 case '>':
841 /* not needed, just for readability */
842 ret += 4;
843 memcpy(&warg->buf[warg->len], "&gt;", 4);
844 warg->len += 4;
845 break;
846 default:
847 ret++;
848 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
849 warg->len++;
850 }
851 }
852 } else {
853 memcpy(&warg->buf[warg->len], buf, count);
854 warg->len += count; /* is <= WRITE_BUFSIZE */
855 ret += count;
856 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200857 }
858
Michal Vasko428087d2016-01-14 16:04:28 +0100859 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200860}
861
Radek Krejci047300e2016-03-08 16:46:58 +0100862static ssize_t
863nc_write_xmlclb(void *arg, const void *buf, size_t count)
864{
865 return nc_write_clb(arg, buf, count, 0);
866}
867
Michal Vasko05ba9df2016-01-13 14:40:27 +0100868static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100869nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
870 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100871{
Michal Vasko08611b32016-12-05 13:30:37 +0100872 if (open) {
873 nc_write_clb((void *)arg, "<", 1, 0);
874 } else {
875 nc_write_clb((void *)arg, "</", 2, 0);
876 }
877
878 if (prefix) {
879 nc_write_clb((void *)arg, prefix, pref_len, 0);
880 nc_write_clb((void *)arg, ":", 1, 0);
881 }
882
883 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100884 if (!open || !no_attr) {
885 nc_write_clb((void *)arg, ">", 1, 0);
886 }
Michal Vasko08611b32016-12-05 13:30:37 +0100887}
888
889static void
890nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
891{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100892 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100893 char str_sid[11];
894
Michal Vasko08611b32016-12-05 13:30:37 +0100895 if (prefix) {
896 pref_len = strlen(prefix);
897 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100898
Michal Vasko52bd9492016-12-08 09:37:43 +0100899 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100900
Michal Vasko52bd9492016-12-08 09:37:43 +0100901 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100902 switch (err->type) {
903 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100904 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100905 break;
906 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100907 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908 break;
909 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100910 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 break;
912 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100913 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100914 break;
915 default:
916 ERRINT;
917 return;
918 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100919
Michal Vasko52bd9492016-12-08 09:37:43 +0100920 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100921
Michal Vasko52bd9492016-12-08 09:37:43 +0100922 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100923 switch (err->tag) {
924 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100925 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100926 break;
927 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100928 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100929 break;
930 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100931 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100932 break;
933 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100934 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100935 break;
936 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100937 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100938 break;
939 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100940 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100941 break;
942 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100943 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100944 break;
945 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100946 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100947 break;
948 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100949 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100950 break;
951 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100952 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100953 break;
954 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100955 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100956 break;
957 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100958 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100959 break;
960 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100961 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100962 break;
963 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100964 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100965 break;
966 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100967 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100968 break;
969 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100970 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100971 break;
972 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100973 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100974 break;
975 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100976 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100977 break;
978 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100979 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100980 break;
981 default:
982 ERRINT;
983 return;
984 }
Michal Vasko52bd9492016-12-08 09:37:43 +0100985 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100986
Michal Vasko52bd9492016-12-08 09:37:43 +0100987 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100988 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100989 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100990
991 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100992 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100993 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100994 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100995 }
996
997 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100998 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100999 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001000 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001001 }
1002
1003 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001004 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001005 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +01001006 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
1007 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
1008 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001009 }
Radek Krejci047300e2016-03-08 16:46:58 +01001010 nc_write_clb((void *)arg, ">", 1, 0);
1011 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001012 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001013 }
1014
Michal Vasko90920b02016-05-20 14:07:00 +02001015 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001016 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001017
Michal Vasko90920b02016-05-20 14:07:00 +02001018 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001019 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +02001020 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +01001021 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001022 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001023 }
1024
1025 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001026 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001027 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001028 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001029 }
1030
1031 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001032 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001033 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001034 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001035 }
1036
1037 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001038 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001039 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001040 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001041 }
1042
1043 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +01001044 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001045 }
1046
Michal Vasko52bd9492016-12-08 09:37:43 +01001047 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001048 }
1049
Michal Vasko52bd9492016-12-08 09:37:43 +01001050 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001051}
1052
Michal Vasko131120a2018-05-29 15:44:02 +02001053/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
1054NC_MSG_TYPE
1055nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001056{
Radek Krejcid116db42016-01-08 15:36:30 +01001057 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +02001058 int count, ret;
Michal Vasko08611b32016-12-05 13:30:37 +01001059 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001060 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001061 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001062 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001063 struct nc_server_reply *reply;
1064 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001065 char *buf = NULL;
1066 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001067 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001068 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001069 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001070
Michal Vasko428087d2016-01-14 16:04:28 +01001071 assert(session);
1072
1073 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001074 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +02001075 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001076 }
1077
Radek Krejcife0b3472015-10-12 13:43:42 +02001078 arg.session = session;
1079 arg.len = 0;
1080
Michal Vasko131120a2018-05-29 15:44:02 +02001081 /* SESSION IO LOCK */
1082 ret = nc_session_io_lock(session, io_timeout, __func__);
1083 if (ret < 0) {
1084 return NC_MSG_ERROR;
1085 } else if (!ret) {
1086 return NC_MSG_WOULDBLOCK;
1087 }
1088
1089 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +02001090
Radek Krejcife0b3472015-10-12 13:43:42 +02001091 switch (type) {
1092 case NC_MSG_RPC:
1093 content = va_arg(ap, struct lyd_node *);
1094 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001095
Radek Krejcife0b3472015-10-12 13:43:42 +02001096 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001097 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001098 if (count == -1) {
1099 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001100 ret = NC_MSG_ERROR;
1101 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001102 }
Radek Krejci047300e2016-03-08 16:46:58 +01001103 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001104 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001105
Michal Vasko5a91ce72017-10-19 11:30:02 +02001106 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001107 ret = NC_MSG_ERROR;
1108 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001109 }
Radek Krejci047300e2016-03-08 16:46:58 +01001110 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001111
Michal Vasko2e6defd2016-10-07 15:48:15 +02001112 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001113 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001114
Radek Krejcife0b3472015-10-12 13:43:42 +02001115 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001116 rpc_elem = va_arg(ap, struct lyxml_elem *);
1117 reply = va_arg(ap, struct nc_server_reply *);
1118
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001119 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1120 nc_write_clb((void *)&arg, "<", 1, 0);
1121 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1122 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001123 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001124 }
1125 else {
1126 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001127 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001128 }
1129
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001130 /* can be NULL if replying with a malformed-message error */
1131 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001132 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001133 nc_write_clb((void *)&arg, ">", 1, 0);
1134 } else {
1135 /* but put there at least the correct namespace */
Michal Vaskobaaa9f02018-01-18 09:12:58 +01001136 nc_write_clb((void *)&arg, " xmlns=\""NC_NS_BASE"\">", 49, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001137 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001138 switch (reply->type) {
1139 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001140 nc_write_clb((void *)&arg, "<", 1, 0);
1141 if (base_prefix) {
1142 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1143 nc_write_clb((void *)&arg, ":", 1, 0);
1144 }
1145 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001146 break;
1147 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001148 switch(((struct nc_server_reply_data *)reply)->wd) {
1149 case NC_WD_UNKNOWN:
1150 case NC_WD_EXPLICIT:
1151 wd = LYP_WD_EXPLICIT;
1152 break;
1153 case NC_WD_TRIM:
1154 wd = LYP_WD_TRIM;
1155 break;
1156 case NC_WD_ALL:
1157 wd = LYP_WD_ALL;
1158 break;
1159 case NC_WD_ALL_TAG:
1160 wd = LYP_WD_ALL_TAG;
1161 break;
1162 }
Michal Vasko5a91ce72017-10-19 11:30:02 +02001163 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
1164 LYP_WITHSIBLINGS | LYP_NETCONF | wd)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001165 ret = NC_MSG_ERROR;
1166 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001167 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001168 break;
1169 case NC_RPL_ERROR:
1170 error_rpl = (struct nc_server_reply_error *)reply;
1171 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001172 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001173 }
1174 break;
1175 default:
1176 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001177 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001178 ret = NC_MSG_ERROR;
1179 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001180 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001181 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1182 nc_write_clb((void *)&arg, "</", 2, 0);
1183 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1184 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1185 }
1186 else {
1187 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1188 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001189 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001190
Radek Krejcife0b3472015-10-12 13:43:42 +02001191 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001192 notif = va_arg(ap, struct nc_server_notif *);
1193
1194 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1195 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1196 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1197 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001198 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001199 ret = NC_MSG_ERROR;
1200 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001201 }
mohitarora24878b2962016-11-09 18:45:33 -05001202 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001203 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001204
Radek Krejcid116db42016-01-08 15:36:30 +01001205 case NC_MSG_HELLO:
1206 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001207 ret = NC_MSG_ERROR;
1208 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001209 }
1210 capabilities = va_arg(ap, const char **);
1211 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001212
Radek Krejcid116db42016-01-08 15:36:30 +01001213 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001214 if (count == -1) {
1215 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001216 ret = NC_MSG_ERROR;
1217 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001218 }
Radek Krejci047300e2016-03-08 16:46:58 +01001219 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001220 free(buf);
1221 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001222 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1223 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1224 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001225 }
1226 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001227 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001228 if (count == -1) {
1229 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001230 ret = NC_MSG_ERROR;
1231 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001232 }
Radek Krejci047300e2016-03-08 16:46:58 +01001233 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001234 free(buf);
1235 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001236 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001237 }
Radek Krejcid116db42016-01-08 15:36:30 +01001238 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001239
Radek Krejcife0b3472015-10-12 13:43:42 +02001240 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001241 ret = NC_MSG_ERROR;
1242 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001243 }
1244
1245 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001246 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001247
Michal Vasko428087d2016-01-14 16:04:28 +01001248 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1249 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001250 ret = NC_MSG_ERROR;
1251 } else {
1252 /* specific message successfully sent */
1253 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001254 }
1255
Michal Vasko131120a2018-05-29 15:44:02 +02001256cleanup:
1257 va_end(ap);
1258 nc_session_io_unlock(session, __func__);
1259 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001260}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001261
1262void *
1263nc_realloc(void *ptr, size_t size)
1264{
1265 void *ret;
1266
1267 ret = realloc(ptr, size);
1268 if (!ret) {
1269 free(ptr);
1270 }
1271
1272 return ret;
1273}
David Sedlákfedbc792018-07-04 11:07:07 +02001274