blob: 4c5ef551413e8c0b4557ca0d78c0d7d3af816dff [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file io.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - input/output functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020013 */
14
Miroslav Mareš9563b812017-08-19 17:45:36 +020015#define _GNU_SOURCE /* asprintf, signals */
Radek Krejci206fcd62015-10-07 15:42:48 +020016#include <assert.h>
17#include <errno.h>
18#include <poll.h>
Michal Vasko3512e402016-01-28 16:22:34 +010019#include <inttypes.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020020#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020021#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010024#include <signal.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010025#include <time.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020026
Michal Vasko964e1732016-09-23 13:39:33 +020027#ifdef NC_ENABLED_TLS
28# include <openssl/err.h>
29#endif
30
Radek Krejci206fcd62015-10-07 15:42:48 +020031#include <libyang/libyang.h>
32
Radek Krejci206fcd62015-10-07 15:42:48 +020033#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020034
Radek Krejcife0b3472015-10-12 13:43:42 +020035#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020036
Michal Vasko90a87d92018-12-10 15:53:44 +010037#ifdef NC_ENABLED_TLS
38
39static char *
40nc_ssl_error_get_reasons(void)
41{
42 unsigned int e;
43 int reason_size, reason_len;
44 char *reasons = NULL;
45
46 reason_size = 1;
47 reason_len = 0;
48 while ((e = ERR_get_error())) {
49 if (reason_len) {
50 /* add "; " */
51 reason_size += 2;
52 reasons = nc_realloc(reasons, reason_size);
53 if (!reasons) {
54 ERRMEM;
55 return NULL;
56 }
57 reason_len += sprintf(reasons + reason_len, "; ");
58 }
59 reason_size += strlen(ERR_reason_error_string(e));
60 reasons = nc_realloc(reasons, reason_size);
61 if (!reasons) {
62 ERRMEM;
63 return NULL;
64 }
Rosen Peneve38d7ef2019-07-15 18:18:03 -070065 reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e));
Michal Vasko90a87d92018-12-10 15:53:44 +010066 }
67
68 return reasons;
69}
70
71#endif
72
Radek Krejci206fcd62015-10-07 15:42:48 +020073static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010074nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +020075{
Michal Vasko81b33fb2016-09-26 14:57:36 +020076 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010077 ssize_t r = -1;
Robin Jarry7de4b8e2019-10-14 21:46:00 +020078 int fd, interrupted;
Michal Vasko36c7be82017-02-22 13:37:59 +010079 struct timespec ts_cur, ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020080
81 assert(session);
82 assert(buf);
83
Michal Vasko428087d2016-01-14 16:04:28 +010084 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
85 return -1;
86 }
87
Radek Krejci206fcd62015-10-07 15:42:48 +020088 if (!count) {
89 return 0;
90 }
91
Michal Vasko77a6abe2017-10-05 10:02:20 +020092 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +010093 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +020094 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +020095 interrupted = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +010096 switch (session->ti_type) {
97 case NC_TI_NONE:
98 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010099
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100100 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200101 case NC_TI_UNIX:
102 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100103 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200104 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200105 if (r < 0) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200106 if (errno == EAGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100107 r = 0;
108 break;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200109 } else if (errno == EINTR) {
110 r = 0;
111 interrupted = 1;
112 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200113 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100114 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200115 session->id, fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100116 session->status = NC_STATUS_INVALID;
117 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200118 return -1;
119 }
120 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100121 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200122 session->id, fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100123 session->status = NC_STATUS_INVALID;
124 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200125 return -1;
126 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100127 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200128
Radek Krejci53691be2016-02-22 13:58:37 +0100129#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100130 case NC_TI_LIBSSH:
131 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200132 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200133 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100134 r = 0;
135 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200136 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100137 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
138 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100139 session->status = NC_STATUS_INVALID;
140 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200141 return -1;
142 } else if (r == 0) {
143 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +0100144 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100145 session->status = NC_STATUS_INVALID;
146 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200147 return -1;
148 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100149 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200150 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100151 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200152#endif
153
Radek Krejci53691be2016-02-22 13:58:37 +0100154#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100155 case NC_TI_OPENSSL:
156 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100157 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200158 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200159 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100160 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100161 char *reasons;
162
Michal Vasko0abba6d2018-12-10 14:09:39 +0100163 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200164 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100165 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100166 r = 0;
167 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200168 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100169 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100170 session->status = NC_STATUS_INVALID;
171 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200172 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100173 case SSL_ERROR_SYSCALL:
174 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
175 session->status = NC_STATUS_INVALID;
176 session->term_reason = NC_SESSION_TERM_OTHER;
177 return -1;
178 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100179 reasons = nc_ssl_error_get_reasons();
180 ERR("Session %u: SSL error (%s).", session->id, reasons);
181 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100182 session->status = NC_STATUS_INVALID;
183 session->term_reason = NC_SESSION_TERM_OTHER;
184 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200185 default:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100186 ERR("Session %u: unknown SSL error occured (err code %d).", session->id, e);
Michal Vasko428087d2016-01-14 16:04:28 +0100187 session->status = NC_STATUS_INVALID;
188 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200189 return -1;
190 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200191 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100192 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200193#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100194 }
195
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100196 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100197 /* nothing read */
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200198 if (!interrupted) {
199 usleep(NC_TIMEOUT_STEP);
200 }
Michal Vasko77a6abe2017-10-05 10:02:20 +0200201 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100202 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
203 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100204 ERR("Session %u: inactive read timeout elapsed.", session->id);
205 } else {
206 ERR("Session %u: active read timeout elapsed.", session->id);
207 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100208 session->status = NC_STATUS_INVALID;
209 session->term_reason = NC_SESSION_TERM_OTHER;
210 return -1;
211 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100212 } else {
213 /* something read */
214 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100215
216 /* reset inactive timeout */
Michal Vasko77a6abe2017-10-05 10:02:20 +0200217 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100218 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100219 }
220
Michal Vasko81b33fb2016-09-26 14:57:36 +0200221 } while (readd < count);
222 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200223
Michal Vasko81b33fb2016-09-26 14:57:36 +0200224 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200225}
226
227static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100228nc_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 +0200229{
230 ssize_t r;
231
232 assert(session);
233 assert(chunk);
234
235 if (!len) {
236 return 0;
237 }
238
Michal Vasko4eb3c312016-03-01 14:09:37 +0100239 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200240 if (!*chunk) {
241 ERRMEM;
242 return -1;
243 }
244
Michal Vasko36c7be82017-02-22 13:37:59 +0100245 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200246 if (r <= 0) {
247 free(*chunk);
248 return -1;
249 }
250
251 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200252 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200253
254 return r;
255}
256
257static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100258nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
259 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200260{
261 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200262 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200263
264 assert(session);
265 assert(endtag);
266
267 if (limit && limit < BUFFERSIZE) {
268 size = limit;
269 } else {
270 size = BUFFERSIZE;
271 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100272 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200273 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200274 ERRMEM;
275 return -1;
276 }
277
278 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100279 while (1) {
David Sedlák15dad862018-07-04 11:25:55 +0200280 if (limit && count == limit) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200281 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100282 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
283 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200284 return -1;
285 }
286
287 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200288 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200289 /* get more memory */
290 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200291 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100292 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200293 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200294 return -1;
295 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200296 }
297
298 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200299 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
300 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200301 free(chunk);
302 return -1;
303 }
304
David Sedlákfedbc792018-07-04 11:07:07 +0200305 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200306
David Sedlákfedbc792018-07-04 11:07:07 +0200307 for (i = len - matched; i > 0; i--) {
308 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
309 /*part of endtag found */
310 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200311 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200312 } else {
313 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200314 }
315 }
David Sedlákfedbc792018-07-04 11:07:07 +0200316
317 /* whole endtag found */
318 if (matched == len) {
319 break;
320 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200321 }
322
323 /* terminating null byte */
324 chunk[count] = 0;
325
326 if (result) {
327 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200328 } else {
329 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200330 }
331 return count;
332}
333
Michal Vasko131120a2018-05-29 15:44:02 +0200334/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Radek Krejci206fcd62015-10-07 15:42:48 +0200335NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200336nc_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 +0100337{
Michal Vasko131120a2018-05-29 15:44:02 +0200338 int ret, io_locked = passing_io_lock;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100339 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100340 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100341 /* use timeout in milliseconds instead seconds */
342 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
343 struct timespec ts_act_timeout;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100344 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100345
Michal Vasko428087d2016-01-14 16:04:28 +0100346 assert(session && data);
347 *data = NULL;
348
349 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100350 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +0200351 ret = NC_MSG_ERROR;
352 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100353 }
354
Michal Vasko77a6abe2017-10-05 10:02:20 +0200355 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100356 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
357
Michal Vasko131120a2018-05-29 15:44:02 +0200358 if (!io_locked) {
359 /* SESSION IO LOCK */
360 ret = nc_session_io_lock(session, io_timeout, __func__);
361 if (ret < 0) {
362 ret = NC_MSG_ERROR;
363 goto cleanup;
364 } else if (!ret) {
365 ret = NC_MSG_WOULDBLOCK;
366 goto cleanup;
367 }
368 io_locked = 1;
369 }
370
Michal Vasko05ba9df2016-01-13 14:40:27 +0100371 /* read the message */
372 switch (session->version) {
373 case NC_VERSION_10:
Michal Vasko36c7be82017-02-22 13:37:59 +0100374 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100375 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200376 ret = NC_MSG_ERROR;
377 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100378 }
379
380 /* cut off the end tag */
381 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
382 break;
383 case NC_VERSION_11:
384 while (1) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100385 ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200387 ret = NC_MSG_ERROR;
388 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100390 ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100391 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200392 ret = NC_MSG_ERROR;
393 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100394 }
395
396 if (!strcmp(chunk, "#\n")) {
397 /* end of chunked framing message */
398 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200399 if (!msg) {
400 ERR("Session %u: invalid frame chunk delimiters.", session->id);
401 goto malformed_msg;
402 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100403 break;
404 }
405
406 /* convert string to the size of the following chunk */
407 chunk_len = strtoul(chunk, (char **)NULL, 10);
408 free(chunk);
409 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100410 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100411 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100412 }
413
414 /* now we have size of next chunk, so read the chunk */
Michal Vasko36c7be82017-02-22 13:37:59 +0100415 ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100416 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200417 ret = NC_MSG_ERROR;
418 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100419 }
420
421 /* realloc message buffer, remember to count terminating null byte */
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200422 msg = nc_realloc(msg, len + chunk_len + 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100423 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100424 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200425 ret = NC_MSG_ERROR;
426 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100428 memcpy(msg + len, chunk, chunk_len);
429 len += chunk_len;
430 msg[len] = '\0';
431 free(chunk);
432 }
433
434 break;
435 }
Michal Vasko131120a2018-05-29 15:44:02 +0200436
437 /* SESSION IO UNLOCK */
438 assert(io_locked);
439 nc_session_io_unlock(session, __func__);
440 io_locked = 0;
441
Michal Vasko81b33fb2016-09-26 14:57:36 +0200442 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100443
444 /* build XML tree */
Michal Vaskod484a392019-11-11 09:48:17 +0100445 *data = lyxml_parse_mem(session->ctx, msg, LYXML_PARSE_NOMIXEDCONTENT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100446 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100447 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100448 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100449 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100450 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100451 }
452 free(msg);
453 msg = NULL;
454
455 /* get and return message type */
456 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
457 if (!strcmp((*data)->name, "rpc")) {
458 return NC_MSG_RPC;
459 } else if (!strcmp((*data)->name, "rpc-reply")) {
460 return NC_MSG_REPLY;
461 } else if (!strcmp((*data)->name, "hello")) {
462 return NC_MSG_HELLO;
463 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100464 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100465 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100466 }
467 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
468 if (!strcmp((*data)->name, "notification")) {
469 return NC_MSG_NOTIF;
470 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100471 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100472 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100473 }
474 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100475 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100476 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100477 }
478
Michal Vasko428087d2016-01-14 16:04:28 +0100479malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100480 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100481 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
482 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100483 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100484
Michal Vaskofc5d07e2018-08-17 10:23:48 +0200485 if (io_locked) {
486 /* nc_write_msg_io locks and unlocks the lock by itself */
487 nc_session_io_unlock(session, __func__);
488 io_locked = 0;
489 }
490
Michal Vasko131120a2018-05-29 15:44:02 +0200491 if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) {
Michal Vaskod083db62016-01-19 10:31:29 +0100492 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100493 if (session->status != NC_STATUS_INVALID) {
494 session->status = NC_STATUS_INVALID;
495 session->term_reason = NC_SESSION_TERM_OTHER;
496 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100497 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100498 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100499 }
Michal Vasko131120a2018-05-29 15:44:02 +0200500 ret = NC_MSG_ERROR;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100501
Michal Vasko131120a2018-05-29 15:44:02 +0200502cleanup:
503 if (io_locked) {
504 nc_session_io_unlock(session, __func__);
505 }
Michal Vasko428087d2016-01-14 16:04:28 +0100506 free(msg);
507 free(*data);
508 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100509
Michal Vasko131120a2018-05-29 15:44:02 +0200510 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100511}
512
Michal Vasko428087d2016-01-14 16:04:28 +0100513/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
514static int
Michal Vasko131120a2018-05-29 15:44:02 +0200515nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100516{
Radek Krejci5961c702016-07-15 09:15:18 +0200517 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100518 int ret = -2;
519 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100520
521 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100522 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100523 return -1;
524 }
525
526 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100527#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100528 case NC_TI_LIBSSH:
529 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200530 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100531 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100532 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100533 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100534 session->status = NC_STATUS_INVALID;
535 session->term_reason = NC_SESSION_TERM_OTHER;
536 return -1;
537 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100538 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100539 session->status = NC_STATUS_INVALID;
540 session->term_reason = NC_SESSION_TERM_DROPPED;
541 return -1;
542 } else if (ret > 0) {
543 /* fake it */
544 ret = 1;
545 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100546 } else { /* ret == 0 */
547 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100548 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100549 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100550#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100551#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100552 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100553 ret = SSL_pending(session->ti.tls);
554 if (ret) {
555 /* some buffered TLS data available */
556 ret = 1;
557 fds.revents = POLLIN;
558 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100559 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100560
561 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100562#endif
Michal Vaskob983c002017-11-02 13:10:57 +0100563 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100564 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200565 case NC_TI_UNIX:
566 if (session->ti_type == NC_TI_FD)
Michal Vasko428087d2016-01-14 16:04:28 +0100567 fds.fd = session->ti.fd.in;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200568 else if (session->ti_type == NC_TI_UNIX)
569 fds.fd = session->ti.unixsock.sock;
Michal Vasko428087d2016-01-14 16:04:28 +0100570
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100571 fds.events = POLLIN;
572 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100573
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100574 sigfillset(&sigmask);
575 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200576 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100577 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100578
579 break;
580
581 default:
582 ERRINT;
583 return -1;
584 }
585
586 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
587 if (ret < 0) {
588 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200589 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100590 session->status = NC_STATUS_INVALID;
591 session->term_reason = NC_SESSION_TERM_OTHER;
592 return -1;
593 } else { /* status > 0 */
594 /* in case of standard (non-libssh) poll, there still can be an error */
595 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100596 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100597 session->status = NC_STATUS_INVALID;
598 session->term_reason = NC_SESSION_TERM_DROPPED;
599 return -1;
600 }
601 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100602 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100603 session->status = NC_STATUS_INVALID;
604 session->term_reason = NC_SESSION_TERM_OTHER;
605 return -1;
606 }
607 }
608
609 return ret;
610}
611
Michal Vasko131120a2018-05-29 15:44:02 +0200612/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100613NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200614nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200615{
Michal Vasko428087d2016-01-14 16:04:28 +0100616 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200617
618 assert(data);
619 *data = NULL;
620
Michal Vasko428087d2016-01-14 16:04:28 +0100621 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100622 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100623 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200624 }
625
Michal Vasko131120a2018-05-29 15:44:02 +0200626 /* SESSION IO LOCK */
627 ret = nc_session_io_lock(session, io_timeout, __func__);
628 if (ret < 0) {
629 return NC_MSG_ERROR;
630 } else if (!ret) {
631 return NC_MSG_WOULDBLOCK;
632 }
633
634 ret = nc_read_poll(session, io_timeout);
Michal Vasko428087d2016-01-14 16:04:28 +0100635 if (ret == 0) {
636 /* timed out */
Michal Vasko131120a2018-05-29 15:44:02 +0200637
638 /* SESSION IO UNLOCK */
639 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100640 return NC_MSG_WOULDBLOCK;
641 } else if (ret < 0) {
642 /* poll error, error written */
Michal Vasko131120a2018-05-29 15:44:02 +0200643
644 /* SESSION IO UNLOCK */
645 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100646 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200647 }
648
Michal Vasko131120a2018-05-29 15:44:02 +0200649 /* SESSION IO LOCK passed down */
650 return nc_read_msg_io(session, io_timeout, data, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200651}
Radek Krejcife0b3472015-10-12 13:43:42 +0200652
Michal Vasko428087d2016-01-14 16:04:28 +0100653/* does not really log, only fatal errors */
654int
655nc_session_is_connected(struct nc_session *session)
656{
657 int ret;
658 struct pollfd fds;
659
660 switch (session->ti_type) {
661 case NC_TI_FD:
662 fds.fd = session->ti.fd.in;
663 break;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200664 case NC_TI_UNIX:
665 fds.fd = session->ti.unixsock.sock;
666 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100667#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100668 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100669 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100670#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100671#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100672 case NC_TI_OPENSSL:
673 fds.fd = SSL_get_fd(session->ti.tls);
674 break;
675#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100676 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100677 return 0;
678 }
679
Michal Vasko840a8a62017-02-07 10:56:34 +0100680 if (fds.fd == -1) {
681 return 0;
682 }
683
Michal Vasko428087d2016-01-14 16:04:28 +0100684 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100685 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100686
687 errno = 0;
688 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
689
690 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100691 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100692 return 0;
693 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
694 return 0;
695 }
696
697 return 1;
698}
699
Radek Krejcife0b3472015-10-12 13:43:42 +0200700#define WRITE_BUFSIZE (2 * BUFFERSIZE)
701struct wclb_arg {
702 struct nc_session *session;
703 char buf[WRITE_BUFSIZE];
704 size_t len;
705};
706
Michal Vasko964e1732016-09-23 13:39:33 +0200707static int
Michal Vasko428087d2016-01-14 16:04:28 +0100708nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200709{
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200710 int c, fd, interrupted;
Michal Vasko964e1732016-09-23 13:39:33 +0200711 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200712#ifdef NC_ENABLED_TLS
713 unsigned long e;
714#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200715
Michal Vasko428087d2016-01-14 16:04:28 +0100716 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
717 return -1;
718 }
719
720 /* prevent SIGPIPE this way */
721 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100722 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100723 session->status = NC_STATUS_INVALID;
724 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100725 return -1;
726 }
727
Michal Vasko81b33fb2016-09-26 14:57:36 +0200728 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200729
Michal Vasko81b33fb2016-09-26 14:57:36 +0200730 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200731 interrupted = 0;
Michal Vasko964e1732016-09-23 13:39:33 +0200732 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200733 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200734 case NC_TI_UNIX:
735 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
736 c = write(fd, (char *)(buf + written), count - written);
737 if (c < 0 && errno == EAGAIN) {
738 c = 0;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200739 } else if (c < 0 && errno == EINTR) {
740 c = 0;
741 interrupted = 1;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200742 } else if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200743 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200744 return -1;
745 }
746 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200747
Radek Krejci53691be2016-02-22 13:58:37 +0100748#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200749 case NC_TI_LIBSSH:
750 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
751 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
752 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
753 } else {
754 ERR("Session %u: SSH channel unexpected EOF.", session->id);
755 }
756 session->status = NC_STATUS_INVALID;
757 session->term_reason = NC_SESSION_TERM_DROPPED;
758 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100759 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200760 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200761 if ((c == SSH_ERROR) || (c == -1)) {
762 ERR("Session %u: SSH channel write failed.", session->id);
763 return -1;
764 }
765 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200766#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100767#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200768 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200769 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200770 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100771 char *reasons;
772
Michal Vasko964e1732016-09-23 13:39:33 +0200773 switch ((e = SSL_get_error(session->ti.tls, c))) {
774 case SSL_ERROR_ZERO_RETURN:
775 ERR("Session %u: SSL connection was properly closed.", session->id);
776 return -1;
777 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100778 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200779 c = 0;
780 break;
781 case SSL_ERROR_SYSCALL:
782 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
783 return -1;
784 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100785 reasons = nc_ssl_error_get_reasons();
786 ERR("Session %u: SSL error (%s).", session->id, reasons);
787 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200788 return -1;
789 default:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100790 ERR("Session %u: unknown SSL error occured (err code %d).", session->id, e);
Michal Vasko964e1732016-09-23 13:39:33 +0200791 return -1;
792 }
793 }
794 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200795#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200796 default:
797 ERRINT;
798 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200799 }
800
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200801 if (c == 0 && !interrupted) {
Michal Vasko964e1732016-09-23 13:39:33 +0200802 /* we must wait */
803 usleep(NC_TIMEOUT_STEP);
804 }
805
806 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200807 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200808
Michal Vasko964e1732016-09-23 13:39:33 +0200809 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200810}
811
Michal Vasko428087d2016-01-14 16:04:28 +0100812static int
813nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100814{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100815 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100816 char chunksize[20];
817
818 if (session->version == NC_VERSION_11) {
819 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100820 ret = nc_write(session, chunksize, strlen(chunksize));
821 if (ret == -1) {
822 return -1;
823 }
Michal Vasko086311b2016-01-08 09:53:11 +0100824 }
Michal Vasko428087d2016-01-14 16:04:28 +0100825
826 c = nc_write(session, buf, count);
827 if (c == -1) {
828 return -1;
829 }
830 ret += c;
831
832 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100833}
834
Radek Krejcife0b3472015-10-12 13:43:42 +0200835static int
Michal Vasko428087d2016-01-14 16:04:28 +0100836nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200837{
Michal Vasko428087d2016-01-14 16:04:28 +0100838 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100839
Michal Vasko428087d2016-01-14 16:04:28 +0100840 if (session->version == NC_VERSION_11) {
841 ret = nc_write(session, "\n##\n", 4);
842 } else {
843 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200844 }
845
Michal Vasko428087d2016-01-14 16:04:28 +0100846 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200847}
848
Michal Vasko428087d2016-01-14 16:04:28 +0100849static int
850nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200851{
Michal Vasko428087d2016-01-14 16:04:28 +0100852 int ret = 0;
853
Radek Krejcife0b3472015-10-12 13:43:42 +0200854 /* flush current buffer */
855 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100856 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200857 warg->len = 0;
858 }
Michal Vasko428087d2016-01-14 16:04:28 +0100859
860 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200861}
862
863static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100864nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200865{
Michal Vasko428087d2016-01-14 16:04:28 +0100866 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100867 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200868 struct wclb_arg *warg = (struct wclb_arg *)arg;
869
870 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100871 c = nc_write_clb_flush(warg);
872 if (c == -1) {
873 return -1;
874 }
875 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200876
877 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100878 c = nc_write_endtag(warg->session);
879 if (c == -1) {
880 return -1;
881 }
882 ret += c;
883
884 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200885 }
886
887 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
888 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100889 c = nc_write_clb_flush(warg);
890 if (c == -1) {
891 return -1;
892 }
893 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200894 }
Michal Vasko428087d2016-01-14 16:04:28 +0100895
Radek Krejci047300e2016-03-08 16:46:58 +0100896 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200897 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100898 c = nc_write_starttag_and_msg(warg->session, buf, count);
899 if (c == -1) {
900 return -1;
901 }
902 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200903 } else {
904 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100905 if (xmlcontent) {
906 for (l = 0; l < count; l++) {
907 if (warg->len + 5 >= WRITE_BUFSIZE) {
908 /* buffer is full */
909 c = nc_write_clb_flush(warg);
910 if (c == -1) {
911 return -1;
912 }
913 }
914
915 switch (((char *)buf)[l]) {
916 case '&':
917 ret += 5;
918 memcpy(&warg->buf[warg->len], "&amp;", 5);
919 warg->len += 5;
920 break;
921 case '<':
922 ret += 4;
923 memcpy(&warg->buf[warg->len], "&lt;", 4);
924 warg->len += 4;
925 break;
926 case '>':
927 /* not needed, just for readability */
928 ret += 4;
929 memcpy(&warg->buf[warg->len], "&gt;", 4);
930 warg->len += 4;
931 break;
932 default:
933 ret++;
934 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
935 warg->len++;
936 }
937 }
938 } else {
939 memcpy(&warg->buf[warg->len], buf, count);
940 warg->len += count; /* is <= WRITE_BUFSIZE */
941 ret += count;
942 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200943 }
944
Michal Vasko428087d2016-01-14 16:04:28 +0100945 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200946}
947
Radek Krejci047300e2016-03-08 16:46:58 +0100948static ssize_t
949nc_write_xmlclb(void *arg, const void *buf, size_t count)
950{
951 return nc_write_clb(arg, buf, count, 0);
952}
953
Michal Vasko05ba9df2016-01-13 14:40:27 +0100954static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100955nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
956 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100957{
Michal Vasko08611b32016-12-05 13:30:37 +0100958 if (open) {
959 nc_write_clb((void *)arg, "<", 1, 0);
960 } else {
961 nc_write_clb((void *)arg, "</", 2, 0);
962 }
963
964 if (prefix) {
965 nc_write_clb((void *)arg, prefix, pref_len, 0);
966 nc_write_clb((void *)arg, ":", 1, 0);
967 }
968
969 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100970 if (!open || !no_attr) {
971 nc_write_clb((void *)arg, ">", 1, 0);
972 }
Michal Vasko08611b32016-12-05 13:30:37 +0100973}
974
975static void
976nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
977{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100978 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100979 char str_sid[11];
980
Michal Vasko08611b32016-12-05 13:30:37 +0100981 if (prefix) {
982 pref_len = strlen(prefix);
983 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100984
Michal Vasko52bd9492016-12-08 09:37:43 +0100985 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100986
Michal Vasko52bd9492016-12-08 09:37:43 +0100987 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100988 switch (err->type) {
989 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100990 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100991 break;
992 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100993 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100994 break;
995 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100996 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100997 break;
998 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100999 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001000 break;
1001 default:
1002 ERRINT;
1003 return;
1004 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001005
Michal Vasko52bd9492016-12-08 09:37:43 +01001006 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001007
Michal Vasko52bd9492016-12-08 09:37:43 +01001008 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001009 switch (err->tag) {
1010 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +01001011 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001012 break;
1013 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +01001014 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001015 break;
1016 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +01001017 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001018 break;
1019 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001020 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001021 break;
1022 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001023 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001024 break;
1025 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001026 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001027 break;
1028 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001029 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001030 break;
1031 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001032 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001033 break;
1034 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001035 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001036 break;
1037 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +01001038 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001039 break;
1040 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001041 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001042 break;
1043 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001044 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001045 break;
1046 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001047 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001048 break;
1049 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +01001050 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001051 break;
1052 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +01001053 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001054 break;
1055 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +01001056 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001057 break;
1058 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +01001059 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001060 break;
1061 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +01001062 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001063 break;
1064 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +01001065 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001066 break;
1067 default:
1068 ERRINT;
1069 return;
1070 }
Michal Vasko52bd9492016-12-08 09:37:43 +01001071 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001072
Michal Vasko52bd9492016-12-08 09:37:43 +01001073 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001074 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001075 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001076
1077 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001078 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001079 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001080 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001081 }
1082
1083 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001084 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001085 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001086 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001087 }
1088
1089 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001090 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001091 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +01001092 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
1093 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
1094 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001095 }
Radek Krejci047300e2016-03-08 16:46:58 +01001096 nc_write_clb((void *)arg, ">", 1, 0);
1097 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001098 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001099 }
1100
Michal Vasko90920b02016-05-20 14:07:00 +02001101 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001102 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001103
Michal Vasko90920b02016-05-20 14:07:00 +02001104 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001105 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +02001106 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +01001107 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001108 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001109 }
1110
1111 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001112 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001113 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001114 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001115 }
1116
1117 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001118 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001119 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001120 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001121 }
1122
1123 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001124 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001125 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001126 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001127 }
1128
1129 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +01001130 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001131 }
1132
Michal Vasko52bd9492016-12-08 09:37:43 +01001133 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001134 }
1135
Michal Vasko52bd9492016-12-08 09:37:43 +01001136 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001137}
1138
Michal Vasko131120a2018-05-29 15:44:02 +02001139/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
1140NC_MSG_TYPE
1141nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001142{
Radek Krejcid116db42016-01-08 15:36:30 +01001143 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +02001144 int count, ret;
Michal Vasko08611b32016-12-05 13:30:37 +01001145 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001146 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001147 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001148 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001149 struct nc_server_reply *reply;
1150 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001151 char *buf = NULL;
1152 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001153 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001154 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001155 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001156
Michal Vasko428087d2016-01-14 16:04:28 +01001157 assert(session);
1158
1159 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001160 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +02001161 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001162 }
1163
Radek Krejcife0b3472015-10-12 13:43:42 +02001164 arg.session = session;
1165 arg.len = 0;
1166
Michal Vasko131120a2018-05-29 15:44:02 +02001167 /* SESSION IO LOCK */
1168 ret = nc_session_io_lock(session, io_timeout, __func__);
1169 if (ret < 0) {
1170 return NC_MSG_ERROR;
1171 } else if (!ret) {
1172 return NC_MSG_WOULDBLOCK;
1173 }
1174
1175 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +02001176
Radek Krejcife0b3472015-10-12 13:43:42 +02001177 switch (type) {
1178 case NC_MSG_RPC:
1179 content = va_arg(ap, struct lyd_node *);
1180 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001181
Radek Krejcife0b3472015-10-12 13:43:42 +02001182 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001183 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001184 if (count == -1) {
1185 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001186 ret = NC_MSG_ERROR;
1187 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001188 }
Radek Krejci047300e2016-03-08 16:46:58 +01001189 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001190 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001191
Michal Vasko5a91ce72017-10-19 11:30:02 +02001192 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001193 ret = NC_MSG_ERROR;
1194 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001195 }
Radek Krejci047300e2016-03-08 16:46:58 +01001196 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001197
Michal Vasko2e6defd2016-10-07 15:48:15 +02001198 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001199 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001200
Radek Krejcife0b3472015-10-12 13:43:42 +02001201 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001202 rpc_elem = va_arg(ap, struct lyxml_elem *);
1203 reply = va_arg(ap, struct nc_server_reply *);
1204
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001205 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1206 nc_write_clb((void *)&arg, "<", 1, 0);
1207 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1208 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001209 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001210 }
1211 else {
1212 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001213 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001214 }
1215
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001216 /* can be NULL if replying with a malformed-message error */
1217 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001218 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001219 nc_write_clb((void *)&arg, ">", 1, 0);
1220 } else {
1221 /* but put there at least the correct namespace */
Michal Vaskobaaa9f02018-01-18 09:12:58 +01001222 nc_write_clb((void *)&arg, " xmlns=\""NC_NS_BASE"\">", 49, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001223 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001224 switch (reply->type) {
1225 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001226 nc_write_clb((void *)&arg, "<", 1, 0);
1227 if (base_prefix) {
1228 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1229 nc_write_clb((void *)&arg, ":", 1, 0);
1230 }
1231 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001232 break;
1233 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001234 switch(((struct nc_server_reply_data *)reply)->wd) {
1235 case NC_WD_UNKNOWN:
1236 case NC_WD_EXPLICIT:
1237 wd = LYP_WD_EXPLICIT;
1238 break;
1239 case NC_WD_TRIM:
1240 wd = LYP_WD_TRIM;
1241 break;
1242 case NC_WD_ALL:
1243 wd = LYP_WD_ALL;
1244 break;
1245 case NC_WD_ALL_TAG:
1246 wd = LYP_WD_ALL_TAG;
1247 break;
1248 }
Michal Vasko5a91ce72017-10-19 11:30:02 +02001249 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
1250 LYP_WITHSIBLINGS | LYP_NETCONF | wd)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001251 ret = NC_MSG_ERROR;
1252 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001253 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001254 break;
1255 case NC_RPL_ERROR:
1256 error_rpl = (struct nc_server_reply_error *)reply;
1257 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001258 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001259 }
1260 break;
1261 default:
1262 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001263 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001264 ret = NC_MSG_ERROR;
1265 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001266 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001267 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1268 nc_write_clb((void *)&arg, "</", 2, 0);
1269 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1270 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1271 }
1272 else {
1273 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1274 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001275 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001276
Radek Krejcife0b3472015-10-12 13:43:42 +02001277 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001278 notif = va_arg(ap, struct nc_server_notif *);
1279
1280 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1281 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1282 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1283 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001284 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001285 ret = NC_MSG_ERROR;
1286 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001287 }
mohitarora24878b2962016-11-09 18:45:33 -05001288 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001289 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001290
Radek Krejcid116db42016-01-08 15:36:30 +01001291 case NC_MSG_HELLO:
1292 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001293 ret = NC_MSG_ERROR;
1294 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001295 }
1296 capabilities = va_arg(ap, const char **);
1297 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001298
Radek Krejcid116db42016-01-08 15:36:30 +01001299 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001300 if (count == -1) {
1301 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001302 ret = NC_MSG_ERROR;
1303 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001304 }
Radek Krejci047300e2016-03-08 16:46:58 +01001305 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001306 free(buf);
1307 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001308 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1309 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1310 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001311 }
1312 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001313 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001314 if (count == -1) {
1315 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001316 ret = NC_MSG_ERROR;
1317 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001318 }
Radek Krejci047300e2016-03-08 16:46:58 +01001319 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001320 free(buf);
1321 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001322 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001323 }
Radek Krejcid116db42016-01-08 15:36:30 +01001324 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001325
Radek Krejcife0b3472015-10-12 13:43:42 +02001326 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001327 ret = NC_MSG_ERROR;
1328 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001329 }
1330
1331 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001332 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001333
Michal Vasko428087d2016-01-14 16:04:28 +01001334 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1335 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001336 ret = NC_MSG_ERROR;
1337 } else {
1338 /* specific message successfully sent */
1339 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001340 }
1341
Michal Vasko131120a2018-05-29 15:44:02 +02001342cleanup:
1343 va_end(ap);
1344 nc_session_io_unlock(session, __func__);
1345 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001346}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001347
1348void *
1349nc_realloc(void *ptr, size_t size)
1350{
1351 void *ret;
1352
1353 ret = realloc(ptr, size);
1354 if (!ret) {
1355 free(ptr);
1356 }
1357
1358 return ret;
1359}
David Sedlákfedbc792018-07-04 11:07:07 +02001360