blob: 252a8b4a8a2ea4cb77ec76a9bd8d9624b4ebab5e [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
Michal Vasko9e8ac262020-04-07 13:06:45 +020033#include "compat.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020034#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020035
Michal Vasko8fe604c2020-02-10 15:25:04 +010036const char *nc_msgtype2str[] = {
37 "error",
38 "would block",
39 "no message",
40 "hello message",
41 "bad hello message",
42 "RPC message",
43 "rpc-reply message",
44 "rpc-reply message with wrong ID",
45 "notification message",
46};
47
Radek Krejcife0b3472015-10-12 13:43:42 +020048#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020049
Michal Vasko90a87d92018-12-10 15:53:44 +010050#ifdef NC_ENABLED_TLS
51
52static char *
53nc_ssl_error_get_reasons(void)
54{
55 unsigned int e;
56 int reason_size, reason_len;
57 char *reasons = NULL;
58
59 reason_size = 1;
60 reason_len = 0;
61 while ((e = ERR_get_error())) {
62 if (reason_len) {
63 /* add "; " */
64 reason_size += 2;
65 reasons = nc_realloc(reasons, reason_size);
66 if (!reasons) {
67 ERRMEM;
68 return NULL;
69 }
70 reason_len += sprintf(reasons + reason_len, "; ");
71 }
72 reason_size += strlen(ERR_reason_error_string(e));
73 reasons = nc_realloc(reasons, reason_size);
74 if (!reasons) {
75 ERRMEM;
76 return NULL;
77 }
Rosen Peneve38d7ef2019-07-15 18:18:03 -070078 reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e));
Michal Vasko90a87d92018-12-10 15:53:44 +010079 }
80
81 return reasons;
82}
83
84#endif
85
Radek Krejci206fcd62015-10-07 15:42:48 +020086static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010087nc_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 +020088{
Michal Vasko81b33fb2016-09-26 14:57:36 +020089 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010090 ssize_t r = -1;
Robin Jarry7de4b8e2019-10-14 21:46:00 +020091 int fd, interrupted;
Michal Vasko36c7be82017-02-22 13:37:59 +010092 struct timespec ts_cur, ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020093
94 assert(session);
95 assert(buf);
96
Michal Vasko428087d2016-01-14 16:04:28 +010097 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
98 return -1;
99 }
100
Radek Krejci206fcd62015-10-07 15:42:48 +0200101 if (!count) {
102 return 0;
103 }
104
Michal Vasko77a6abe2017-10-05 10:02:20 +0200105 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100106 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +0200107 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200108 interrupted = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100109 switch (session->ti_type) {
110 case NC_TI_NONE:
111 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100112
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100113 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200114 case NC_TI_UNIX:
115 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100116 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200117 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200118 if (r < 0) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200119 if (errno == EAGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100120 r = 0;
121 break;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200122 } else if (errno == EINTR) {
123 r = 0;
124 interrupted = 1;
125 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200126 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100127 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200128 session->id, fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100129 session->status = NC_STATUS_INVALID;
130 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200131 return -1;
132 }
133 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100134 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200135 session->id, fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100136 session->status = NC_STATUS_INVALID;
137 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200138 return -1;
139 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100140 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200141
Radek Krejci53691be2016-02-22 13:58:37 +0100142#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100143 case NC_TI_LIBSSH:
144 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200145 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200146 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100147 r = 0;
148 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200149 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100150 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
151 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100152 session->status = NC_STATUS_INVALID;
153 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200154 return -1;
155 } else if (r == 0) {
156 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +0100157 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100158 session->status = NC_STATUS_INVALID;
159 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200160 return -1;
161 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100162 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200163 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100164 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200165#endif
166
Radek Krejci53691be2016-02-22 13:58:37 +0100167#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100168 case NC_TI_OPENSSL:
169 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100170 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200171 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200172 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100173 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100174 char *reasons;
175
Michal Vasko0abba6d2018-12-10 14:09:39 +0100176 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200177 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100178 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100179 r = 0;
180 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200181 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100182 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100183 session->status = NC_STATUS_INVALID;
184 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200185 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100186 case SSL_ERROR_SYSCALL:
187 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
188 session->status = NC_STATUS_INVALID;
189 session->term_reason = NC_SESSION_TERM_OTHER;
190 return -1;
191 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100192 reasons = nc_ssl_error_get_reasons();
193 ERR("Session %u: SSL error (%s).", session->id, reasons);
194 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100195 session->status = NC_STATUS_INVALID;
196 session->term_reason = NC_SESSION_TERM_OTHER;
197 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200198 default:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100199 ERR("Session %u: unknown SSL error occured (err code %d).", session->id, e);
Michal Vasko428087d2016-01-14 16:04:28 +0100200 session->status = NC_STATUS_INVALID;
201 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200202 return -1;
203 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200204 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100205 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200206#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100207 }
208
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100209 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100210 /* nothing read */
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200211 if (!interrupted) {
212 usleep(NC_TIMEOUT_STEP);
213 }
Michal Vasko77a6abe2017-10-05 10:02:20 +0200214 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100215 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
216 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100217 ERR("Session %u: inactive read timeout elapsed.", session->id);
218 } else {
219 ERR("Session %u: active read timeout elapsed.", session->id);
220 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100221 session->status = NC_STATUS_INVALID;
222 session->term_reason = NC_SESSION_TERM_OTHER;
223 return -1;
224 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100225 } else {
226 /* something read */
227 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100228
229 /* reset inactive timeout */
Michal Vasko77a6abe2017-10-05 10:02:20 +0200230 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100231 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100232 }
233
Michal Vasko81b33fb2016-09-26 14:57:36 +0200234 } while (readd < count);
235 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200236
Michal Vasko81b33fb2016-09-26 14:57:36 +0200237 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200238}
239
240static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100241nc_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 +0200242{
243 ssize_t r;
244
245 assert(session);
246 assert(chunk);
247
248 if (!len) {
249 return 0;
250 }
251
Michal Vasko4eb3c312016-03-01 14:09:37 +0100252 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200253 if (!*chunk) {
254 ERRMEM;
255 return -1;
256 }
257
Michal Vasko36c7be82017-02-22 13:37:59 +0100258 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200259 if (r <= 0) {
260 free(*chunk);
261 return -1;
262 }
263
264 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200265 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200266
267 return r;
268}
269
270static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100271nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
272 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200273{
274 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200275 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200276
277 assert(session);
278 assert(endtag);
279
280 if (limit && limit < BUFFERSIZE) {
281 size = limit;
282 } else {
283 size = BUFFERSIZE;
284 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100285 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200286 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200287 ERRMEM;
288 return -1;
289 }
290
291 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100292 while (1) {
David Sedlák15dad862018-07-04 11:25:55 +0200293 if (limit && count == limit) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200294 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100295 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
296 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200297 return -1;
298 }
299
300 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200301 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200302 /* get more memory */
303 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200304 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100305 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200306 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200307 return -1;
308 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200309 }
310
311 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200312 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
313 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200314 free(chunk);
315 return -1;
316 }
317
David Sedlákfedbc792018-07-04 11:07:07 +0200318 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200319
David Sedlákfedbc792018-07-04 11:07:07 +0200320 for (i = len - matched; i > 0; i--) {
321 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
322 /*part of endtag found */
323 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200324 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200325 } else {
326 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200327 }
328 }
David Sedlákfedbc792018-07-04 11:07:07 +0200329
330 /* whole endtag found */
331 if (matched == len) {
332 break;
333 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200334 }
335
336 /* terminating null byte */
337 chunk[count] = 0;
338
339 if (result) {
340 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200341 } else {
342 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200343 }
344 return count;
345}
346
Michal Vasko131120a2018-05-29 15:44:02 +0200347/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Radek Krejci206fcd62015-10-07 15:42:48 +0200348NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200349nc_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 +0100350{
Michal Vasko131120a2018-05-29 15:44:02 +0200351 int ret, io_locked = passing_io_lock;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100352 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100353 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100354 /* use timeout in milliseconds instead seconds */
355 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
356 struct timespec ts_act_timeout;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100357 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100358
Michal Vasko428087d2016-01-14 16:04:28 +0100359 assert(session && data);
360 *data = NULL;
361
362 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100363 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +0200364 ret = NC_MSG_ERROR;
365 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100366 }
367
Michal Vasko77a6abe2017-10-05 10:02:20 +0200368 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100369 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
370
Michal Vasko131120a2018-05-29 15:44:02 +0200371 if (!io_locked) {
372 /* SESSION IO LOCK */
373 ret = nc_session_io_lock(session, io_timeout, __func__);
374 if (ret < 0) {
375 ret = NC_MSG_ERROR;
376 goto cleanup;
377 } else if (!ret) {
378 ret = NC_MSG_WOULDBLOCK;
379 goto cleanup;
380 }
381 io_locked = 1;
382 }
383
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 /* read the message */
385 switch (session->version) {
386 case NC_VERSION_10:
Michal Vasko36c7be82017-02-22 13:37:59 +0100387 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100388 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200389 ret = NC_MSG_ERROR;
390 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100391 }
392
393 /* cut off the end tag */
394 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
395 break;
396 case NC_VERSION_11:
397 while (1) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100398 ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100399 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200400 ret = NC_MSG_ERROR;
401 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100402 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100403 ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100404 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200405 ret = NC_MSG_ERROR;
406 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100407 }
408
409 if (!strcmp(chunk, "#\n")) {
410 /* end of chunked framing message */
411 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200412 if (!msg) {
413 ERR("Session %u: invalid frame chunk delimiters.", session->id);
414 goto malformed_msg;
415 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100416 break;
417 }
418
419 /* convert string to the size of the following chunk */
420 chunk_len = strtoul(chunk, (char **)NULL, 10);
421 free(chunk);
422 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100423 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100424 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100425 }
426
427 /* now we have size of next chunk, so read the chunk */
Michal Vasko36c7be82017-02-22 13:37:59 +0100428 ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100429 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200430 ret = NC_MSG_ERROR;
431 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432 }
433
434 /* realloc message buffer, remember to count terminating null byte */
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200435 msg = nc_realloc(msg, len + chunk_len + 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100436 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100437 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200438 ret = NC_MSG_ERROR;
439 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100440 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100441 memcpy(msg + len, chunk, chunk_len);
442 len += chunk_len;
443 msg[len] = '\0';
444 free(chunk);
445 }
446
447 break;
448 }
Michal Vasko131120a2018-05-29 15:44:02 +0200449
450 /* SESSION IO UNLOCK */
451 assert(io_locked);
452 nc_session_io_unlock(session, __func__);
453 io_locked = 0;
454
Michal Vasko81b33fb2016-09-26 14:57:36 +0200455 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456
457 /* build XML tree */
Michal Vaskod484a392019-11-11 09:48:17 +0100458 *data = lyxml_parse_mem(session->ctx, msg, LYXML_PARSE_NOMIXEDCONTENT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100459 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100460 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100461 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100462 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100463 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100464 }
465 free(msg);
466 msg = NULL;
467
468 /* get and return message type */
469 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
470 if (!strcmp((*data)->name, "rpc")) {
471 return NC_MSG_RPC;
472 } else if (!strcmp((*data)->name, "rpc-reply")) {
473 return NC_MSG_REPLY;
474 } else if (!strcmp((*data)->name, "hello")) {
475 return NC_MSG_HELLO;
476 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100477 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100478 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100479 }
480 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
481 if (!strcmp((*data)->name, "notification")) {
482 return NC_MSG_NOTIF;
483 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100484 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100485 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100486 }
487 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100488 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100489 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100490 }
491
Michal Vasko428087d2016-01-14 16:04:28 +0100492malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100493 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100494 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
495 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100496 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100497
Michal Vaskofc5d07e2018-08-17 10:23:48 +0200498 if (io_locked) {
499 /* nc_write_msg_io locks and unlocks the lock by itself */
500 nc_session_io_unlock(session, __func__);
501 io_locked = 0;
502 }
503
Michal Vasko131120a2018-05-29 15:44:02 +0200504 if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) {
Michal Vaskod083db62016-01-19 10:31:29 +0100505 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100506 if (session->status != NC_STATUS_INVALID) {
507 session->status = NC_STATUS_INVALID;
508 session->term_reason = NC_SESSION_TERM_OTHER;
509 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100510 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100511 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100512 }
Michal Vasko131120a2018-05-29 15:44:02 +0200513 ret = NC_MSG_ERROR;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100514
Michal Vasko131120a2018-05-29 15:44:02 +0200515cleanup:
516 if (io_locked) {
517 nc_session_io_unlock(session, __func__);
518 }
Michal Vasko428087d2016-01-14 16:04:28 +0100519 free(msg);
Michal Vaskoad356f62020-08-27 08:32:13 +0200520 lyxml_free(session->ctx, *data);
Michal Vasko428087d2016-01-14 16:04:28 +0100521 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100522
Michal Vasko131120a2018-05-29 15:44:02 +0200523 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100524}
525
Michal Vasko428087d2016-01-14 16:04:28 +0100526/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
527static int
Michal Vasko131120a2018-05-29 15:44:02 +0200528nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100529{
Radek Krejci5961c702016-07-15 09:15:18 +0200530 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100531 int ret = -2;
532 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100533
534 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100535 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100536 return -1;
537 }
538
539 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100540#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100541 case NC_TI_LIBSSH:
542 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200543 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100544 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100545 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100546 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100547 session->status = NC_STATUS_INVALID;
548 session->term_reason = NC_SESSION_TERM_OTHER;
549 return -1;
550 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100551 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100552 session->status = NC_STATUS_INVALID;
553 session->term_reason = NC_SESSION_TERM_DROPPED;
554 return -1;
555 } else if (ret > 0) {
556 /* fake it */
557 ret = 1;
558 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100559 } else { /* ret == 0 */
560 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100561 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100562 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100563#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100564#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100565 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100566 ret = SSL_pending(session->ti.tls);
567 if (ret) {
568 /* some buffered TLS data available */
569 ret = 1;
570 fds.revents = POLLIN;
571 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100572 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100573
574 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100575#endif
Michal Vaskob983c002017-11-02 13:10:57 +0100576 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100577 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200578 case NC_TI_UNIX:
579 if (session->ti_type == NC_TI_FD)
Michal Vasko428087d2016-01-14 16:04:28 +0100580 fds.fd = session->ti.fd.in;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200581 else if (session->ti_type == NC_TI_UNIX)
582 fds.fd = session->ti.unixsock.sock;
Michal Vasko428087d2016-01-14 16:04:28 +0100583
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100584 fds.events = POLLIN;
585 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100586
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100587 sigfillset(&sigmask);
588 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200589 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100590 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100591
592 break;
593
594 default:
595 ERRINT;
596 return -1;
597 }
598
599 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
600 if (ret < 0) {
601 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200602 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
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 } else { /* status > 0 */
607 /* in case of standard (non-libssh) poll, there still can be an error */
Michal Vasko428087d2016-01-14 16:04:28 +0100608 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100609 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100610 session->status = NC_STATUS_INVALID;
611 session->term_reason = NC_SESSION_TERM_OTHER;
612 return -1;
613 }
Robin Jarryf732adc2020-05-15 11:18:38 +0200614 /* Some poll() implementations may return POLLHUP|POLLIN when the other
615 * side has closed but there is data left to read in the buffer. */
616 if ((fds.revents & POLLHUP) && !(fds.revents & POLLIN)) {
617 ERR("Session %u: communication channel unexpectedly closed.", session->id);
618 session->status = NC_STATUS_INVALID;
619 session->term_reason = NC_SESSION_TERM_DROPPED;
620 return -1;
621 }
Michal Vasko428087d2016-01-14 16:04:28 +0100622 }
623
624 return ret;
625}
626
Michal Vasko131120a2018-05-29 15:44:02 +0200627/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100628NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200629nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200630{
Michal Vasko428087d2016-01-14 16:04:28 +0100631 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200632
633 assert(data);
634 *data = NULL;
635
Michal Vasko428087d2016-01-14 16:04:28 +0100636 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100637 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100638 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200639 }
640
Michal Vasko131120a2018-05-29 15:44:02 +0200641 /* SESSION IO LOCK */
642 ret = nc_session_io_lock(session, io_timeout, __func__);
643 if (ret < 0) {
644 return NC_MSG_ERROR;
645 } else if (!ret) {
646 return NC_MSG_WOULDBLOCK;
647 }
648
649 ret = nc_read_poll(session, io_timeout);
Michal Vasko428087d2016-01-14 16:04:28 +0100650 if (ret == 0) {
651 /* timed out */
Michal Vasko131120a2018-05-29 15:44:02 +0200652
653 /* SESSION IO UNLOCK */
654 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100655 return NC_MSG_WOULDBLOCK;
656 } else if (ret < 0) {
657 /* poll error, error written */
Michal Vasko131120a2018-05-29 15:44:02 +0200658
659 /* SESSION IO UNLOCK */
660 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100661 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200662 }
663
Michal Vasko131120a2018-05-29 15:44:02 +0200664 /* SESSION IO LOCK passed down */
665 return nc_read_msg_io(session, io_timeout, data, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200666}
Radek Krejcife0b3472015-10-12 13:43:42 +0200667
Michal Vasko428087d2016-01-14 16:04:28 +0100668/* does not really log, only fatal errors */
669int
670nc_session_is_connected(struct nc_session *session)
671{
672 int ret;
673 struct pollfd fds;
674
675 switch (session->ti_type) {
676 case NC_TI_FD:
677 fds.fd = session->ti.fd.in;
678 break;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200679 case NC_TI_UNIX:
680 fds.fd = session->ti.unixsock.sock;
681 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100682#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100683 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100684 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100685#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100686#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100687 case NC_TI_OPENSSL:
688 fds.fd = SSL_get_fd(session->ti.tls);
689 break;
690#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100691 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100692 return 0;
693 }
694
Michal Vasko840a8a62017-02-07 10:56:34 +0100695 if (fds.fd == -1) {
696 return 0;
697 }
698
Michal Vasko428087d2016-01-14 16:04:28 +0100699 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100700 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100701
702 errno = 0;
703 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
704
705 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100706 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100707 return 0;
708 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
709 return 0;
710 }
711
712 return 1;
713}
714
Radek Krejcife0b3472015-10-12 13:43:42 +0200715#define WRITE_BUFSIZE (2 * BUFFERSIZE)
716struct wclb_arg {
717 struct nc_session *session;
718 char buf[WRITE_BUFSIZE];
719 size_t len;
720};
721
Michal Vasko964e1732016-09-23 13:39:33 +0200722static int
Michal Vasko428087d2016-01-14 16:04:28 +0100723nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200724{
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200725 int c, fd, interrupted;
Michal Vasko964e1732016-09-23 13:39:33 +0200726 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200727#ifdef NC_ENABLED_TLS
728 unsigned long e;
729#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200730
Michal Vasko428087d2016-01-14 16:04:28 +0100731 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
732 return -1;
733 }
734
735 /* prevent SIGPIPE this way */
736 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100737 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100738 session->status = NC_STATUS_INVALID;
739 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100740 return -1;
741 }
742
Michal Vasko81b33fb2016-09-26 14:57:36 +0200743 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200744
Michal Vasko81b33fb2016-09-26 14:57:36 +0200745 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200746 interrupted = 0;
Michal Vasko964e1732016-09-23 13:39:33 +0200747 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200748 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200749 case NC_TI_UNIX:
750 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
751 c = write(fd, (char *)(buf + written), count - written);
752 if (c < 0 && errno == EAGAIN) {
753 c = 0;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200754 } else if (c < 0 && errno == EINTR) {
755 c = 0;
756 interrupted = 1;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200757 } else if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200758 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200759 return -1;
760 }
761 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200762
Radek Krejci53691be2016-02-22 13:58:37 +0100763#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200764 case NC_TI_LIBSSH:
765 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
766 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
767 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
768 } else {
769 ERR("Session %u: SSH channel unexpected EOF.", session->id);
770 }
771 session->status = NC_STATUS_INVALID;
772 session->term_reason = NC_SESSION_TERM_DROPPED;
773 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100774 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200775 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200776 if ((c == SSH_ERROR) || (c == -1)) {
777 ERR("Session %u: SSH channel write failed.", session->id);
778 return -1;
779 }
780 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200781#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100782#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200783 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200784 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200785 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100786 char *reasons;
787
Michal Vasko964e1732016-09-23 13:39:33 +0200788 switch ((e = SSL_get_error(session->ti.tls, c))) {
789 case SSL_ERROR_ZERO_RETURN:
790 ERR("Session %u: SSL connection was properly closed.", session->id);
791 return -1;
792 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100793 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200794 c = 0;
795 break;
796 case SSL_ERROR_SYSCALL:
797 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
798 return -1;
799 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100800 reasons = nc_ssl_error_get_reasons();
801 ERR("Session %u: SSL error (%s).", session->id, reasons);
802 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200803 return -1;
804 default:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100805 ERR("Session %u: unknown SSL error occured (err code %d).", session->id, e);
Michal Vasko964e1732016-09-23 13:39:33 +0200806 return -1;
807 }
808 }
809 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200810#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200811 default:
812 ERRINT;
813 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200814 }
815
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200816 if (c == 0 && !interrupted) {
Michal Vasko964e1732016-09-23 13:39:33 +0200817 /* we must wait */
818 usleep(NC_TIMEOUT_STEP);
819 }
820
821 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200822 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200823
Michal Vasko964e1732016-09-23 13:39:33 +0200824 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200825}
826
Michal Vasko428087d2016-01-14 16:04:28 +0100827static int
828nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100829{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100830 int ret = 0, c;
Claus Klein22091912020-01-20 13:45:47 +0100831 char chunksize[24];
Michal Vasko086311b2016-01-08 09:53:11 +0100832
Claus Klein22091912020-01-20 13:45:47 +0100833 // warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=]
Michal Vasko086311b2016-01-08 09:53:11 +0100834 if (session->version == NC_VERSION_11) {
835 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100836 ret = nc_write(session, chunksize, strlen(chunksize));
837 if (ret == -1) {
838 return -1;
839 }
Michal Vasko086311b2016-01-08 09:53:11 +0100840 }
Michal Vasko428087d2016-01-14 16:04:28 +0100841
842 c = nc_write(session, buf, count);
843 if (c == -1) {
844 return -1;
845 }
846 ret += c;
847
848 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100849}
850
Radek Krejcife0b3472015-10-12 13:43:42 +0200851static int
Michal Vasko428087d2016-01-14 16:04:28 +0100852nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200853{
Michal Vasko428087d2016-01-14 16:04:28 +0100854 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100855
Michal Vasko428087d2016-01-14 16:04:28 +0100856 if (session->version == NC_VERSION_11) {
857 ret = nc_write(session, "\n##\n", 4);
858 } else {
859 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200860 }
861
Michal Vasko428087d2016-01-14 16:04:28 +0100862 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200863}
864
Michal Vasko428087d2016-01-14 16:04:28 +0100865static int
866nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200867{
Michal Vasko428087d2016-01-14 16:04:28 +0100868 int ret = 0;
869
Radek Krejcife0b3472015-10-12 13:43:42 +0200870 /* flush current buffer */
871 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100872 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200873 warg->len = 0;
874 }
Michal Vasko428087d2016-01-14 16:04:28 +0100875
876 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200877}
878
879static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100880nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200881{
Michal Vasko428087d2016-01-14 16:04:28 +0100882 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100883 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200884 struct wclb_arg *warg = (struct wclb_arg *)arg;
885
886 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100887 c = nc_write_clb_flush(warg);
888 if (c == -1) {
889 return -1;
890 }
891 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200892
893 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100894 c = nc_write_endtag(warg->session);
895 if (c == -1) {
896 return -1;
897 }
898 ret += c;
899
900 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200901 }
902
903 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
904 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100905 c = nc_write_clb_flush(warg);
906 if (c == -1) {
907 return -1;
908 }
909 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200910 }
Michal Vasko428087d2016-01-14 16:04:28 +0100911
Radek Krejci047300e2016-03-08 16:46:58 +0100912 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200913 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100914 c = nc_write_starttag_and_msg(warg->session, buf, count);
915 if (c == -1) {
916 return -1;
917 }
918 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200919 } else {
920 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100921 if (xmlcontent) {
922 for (l = 0; l < count; l++) {
923 if (warg->len + 5 >= WRITE_BUFSIZE) {
924 /* buffer is full */
925 c = nc_write_clb_flush(warg);
926 if (c == -1) {
927 return -1;
928 }
929 }
930
931 switch (((char *)buf)[l]) {
932 case '&':
933 ret += 5;
934 memcpy(&warg->buf[warg->len], "&amp;", 5);
935 warg->len += 5;
936 break;
937 case '<':
938 ret += 4;
939 memcpy(&warg->buf[warg->len], "&lt;", 4);
940 warg->len += 4;
941 break;
942 case '>':
943 /* not needed, just for readability */
944 ret += 4;
945 memcpy(&warg->buf[warg->len], "&gt;", 4);
946 warg->len += 4;
947 break;
948 default:
949 ret++;
950 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
951 warg->len++;
952 }
953 }
954 } else {
955 memcpy(&warg->buf[warg->len], buf, count);
956 warg->len += count; /* is <= WRITE_BUFSIZE */
957 ret += count;
958 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200959 }
960
Michal Vasko428087d2016-01-14 16:04:28 +0100961 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200962}
963
Radek Krejci047300e2016-03-08 16:46:58 +0100964static ssize_t
965nc_write_xmlclb(void *arg, const void *buf, size_t count)
966{
967 return nc_write_clb(arg, buf, count, 0);
968}
969
Michal Vasko05ba9df2016-01-13 14:40:27 +0100970static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100971nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
972 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100973{
Michal Vasko08611b32016-12-05 13:30:37 +0100974 if (open) {
975 nc_write_clb((void *)arg, "<", 1, 0);
976 } else {
977 nc_write_clb((void *)arg, "</", 2, 0);
978 }
979
980 if (prefix) {
981 nc_write_clb((void *)arg, prefix, pref_len, 0);
982 nc_write_clb((void *)arg, ":", 1, 0);
983 }
984
985 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100986 if (!open || !no_attr) {
987 nc_write_clb((void *)arg, ">", 1, 0);
988 }
Michal Vasko08611b32016-12-05 13:30:37 +0100989}
990
991static void
992nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
993{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100994 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100995 char str_sid[11];
996
Michal Vasko08611b32016-12-05 13:30:37 +0100997 if (prefix) {
998 pref_len = strlen(prefix);
999 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001000
Michal Vasko52bd9492016-12-08 09:37:43 +01001001 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001002
Michal Vasko52bd9492016-12-08 09:37:43 +01001003 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001004 switch (err->type) {
1005 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +01001006 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001007 break;
1008 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +01001009 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001010 break;
1011 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +01001012 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001013 break;
1014 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +01001015 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001016 break;
1017 default:
1018 ERRINT;
1019 return;
1020 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001021
Michal Vasko52bd9492016-12-08 09:37:43 +01001022 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001023
Michal Vasko52bd9492016-12-08 09:37:43 +01001024 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001025 switch (err->tag) {
1026 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +01001027 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001028 break;
1029 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +01001030 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001031 break;
1032 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +01001033 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001034 break;
1035 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001036 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001037 break;
1038 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001039 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001040 break;
1041 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001042 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001043 break;
1044 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001045 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001046 break;
1047 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001048 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001049 break;
1050 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001051 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001052 break;
1053 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +01001054 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001055 break;
1056 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001057 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001058 break;
1059 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001060 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001061 break;
1062 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001063 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001064 break;
1065 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +01001066 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001067 break;
1068 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +01001069 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001070 break;
1071 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +01001072 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001073 break;
1074 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +01001075 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001076 break;
1077 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +01001078 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001079 break;
1080 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +01001081 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001082 break;
1083 default:
1084 ERRINT;
1085 return;
1086 }
Michal Vasko52bd9492016-12-08 09:37:43 +01001087 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001088
Michal Vasko52bd9492016-12-08 09:37:43 +01001089 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001090 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001091 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001092
1093 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001094 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001095 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001096 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001097 }
1098
1099 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001100 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001101 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001102 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001103 }
1104
1105 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001106 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001107 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +01001108 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
1109 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
1110 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001111 }
Radek Krejci047300e2016-03-08 16:46:58 +01001112 nc_write_clb((void *)arg, ">", 1, 0);
1113 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001114 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001115 }
1116
Michal Vasko90920b02016-05-20 14:07:00 +02001117 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001118 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001119
Michal Vasko90920b02016-05-20 14:07:00 +02001120 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001121 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +02001122 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +01001123 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001124 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001125 }
1126
1127 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001128 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001129 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001130 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001131 }
1132
1133 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001134 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001135 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001136 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001137 }
1138
1139 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001140 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001141 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001142 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001143 }
1144
1145 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +01001146 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001147 }
1148
Michal Vasko52bd9492016-12-08 09:37:43 +01001149 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001150 }
1151
Michal Vasko52bd9492016-12-08 09:37:43 +01001152 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001153}
1154
Michal Vasko131120a2018-05-29 15:44:02 +02001155/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
1156NC_MSG_TYPE
1157nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001158{
Radek Krejcid116db42016-01-08 15:36:30 +01001159 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +02001160 int count, ret;
Michal Vasko08611b32016-12-05 13:30:37 +01001161 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001162 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001163 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001164 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001165 struct nc_server_reply *reply;
1166 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001167 char *buf = NULL;
1168 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001169 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001170 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001171 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001172
Michal Vasko428087d2016-01-14 16:04:28 +01001173 assert(session);
1174
1175 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001176 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +02001177 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001178 }
1179
Radek Krejcife0b3472015-10-12 13:43:42 +02001180 arg.session = session;
1181 arg.len = 0;
1182
Michal Vasko131120a2018-05-29 15:44:02 +02001183 /* SESSION IO LOCK */
1184 ret = nc_session_io_lock(session, io_timeout, __func__);
1185 if (ret < 0) {
1186 return NC_MSG_ERROR;
1187 } else if (!ret) {
1188 return NC_MSG_WOULDBLOCK;
1189 }
1190
1191 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +02001192
Radek Krejcife0b3472015-10-12 13:43:42 +02001193 switch (type) {
1194 case NC_MSG_RPC:
1195 content = va_arg(ap, struct lyd_node *);
1196 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001197
Radek Krejcife0b3472015-10-12 13:43:42 +02001198 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001199 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001200 if (count == -1) {
1201 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001202 ret = NC_MSG_ERROR;
1203 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001204 }
Radek Krejci047300e2016-03-08 16:46:58 +01001205 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001206 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001207
Michal Vasko5a91ce72017-10-19 11:30:02 +02001208 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001209 ret = NC_MSG_ERROR;
1210 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001211 }
Radek Krejci047300e2016-03-08 16:46:58 +01001212 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001213
Michal Vasko2e6defd2016-10-07 15:48:15 +02001214 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001215 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001216
Radek Krejcife0b3472015-10-12 13:43:42 +02001217 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001218 rpc_elem = va_arg(ap, struct lyxml_elem *);
1219 reply = va_arg(ap, struct nc_server_reply *);
1220
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001221 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1222 nc_write_clb((void *)&arg, "<", 1, 0);
1223 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1224 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001225 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001226 }
1227 else {
1228 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001229 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001230 }
1231
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001232 /* can be NULL if replying with a malformed-message error */
1233 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001234 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001235 nc_write_clb((void *)&arg, ">", 1, 0);
1236 } else {
1237 /* but put there at least the correct namespace */
Michal Vaskobaaa9f02018-01-18 09:12:58 +01001238 nc_write_clb((void *)&arg, " xmlns=\""NC_NS_BASE"\">", 49, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001239 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001240 switch (reply->type) {
1241 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001242 nc_write_clb((void *)&arg, "<", 1, 0);
1243 if (base_prefix) {
1244 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1245 nc_write_clb((void *)&arg, ":", 1, 0);
1246 }
1247 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001248 break;
1249 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001250 switch(((struct nc_server_reply_data *)reply)->wd) {
1251 case NC_WD_UNKNOWN:
1252 case NC_WD_EXPLICIT:
1253 wd = LYP_WD_EXPLICIT;
1254 break;
1255 case NC_WD_TRIM:
1256 wd = LYP_WD_TRIM;
1257 break;
1258 case NC_WD_ALL:
1259 wd = LYP_WD_ALL;
1260 break;
1261 case NC_WD_ALL_TAG:
1262 wd = LYP_WD_ALL_TAG;
1263 break;
1264 }
Michal Vasko5a91ce72017-10-19 11:30:02 +02001265 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
1266 LYP_WITHSIBLINGS | LYP_NETCONF | wd)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001267 ret = NC_MSG_ERROR;
1268 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001269 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001270 break;
1271 case NC_RPL_ERROR:
1272 error_rpl = (struct nc_server_reply_error *)reply;
1273 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001274 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001275 }
1276 break;
1277 default:
1278 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001279 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001280 ret = NC_MSG_ERROR;
1281 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001282 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001283 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1284 nc_write_clb((void *)&arg, "</", 2, 0);
1285 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1286 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1287 }
1288 else {
1289 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1290 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001291 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001292
Radek Krejcife0b3472015-10-12 13:43:42 +02001293 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001294 notif = va_arg(ap, struct nc_server_notif *);
1295
1296 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1297 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1298 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1299 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001300 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001301 ret = NC_MSG_ERROR;
1302 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001303 }
mohitarora24878b2962016-11-09 18:45:33 -05001304 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001305 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001306
Radek Krejcid116db42016-01-08 15:36:30 +01001307 case NC_MSG_HELLO:
1308 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001309 ret = NC_MSG_ERROR;
1310 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001311 }
1312 capabilities = va_arg(ap, const char **);
1313 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001314
Radek Krejcid116db42016-01-08 15:36:30 +01001315 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001316 if (count == -1) {
1317 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001318 ret = NC_MSG_ERROR;
1319 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001320 }
Radek Krejci047300e2016-03-08 16:46:58 +01001321 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001322 free(buf);
1323 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001324 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1325 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1326 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001327 }
1328 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001329 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001330 if (count == -1) {
1331 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001332 ret = NC_MSG_ERROR;
1333 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001334 }
Radek Krejci047300e2016-03-08 16:46:58 +01001335 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001336 free(buf);
1337 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001338 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001339 }
Radek Krejcid116db42016-01-08 15:36:30 +01001340 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001341
Radek Krejcife0b3472015-10-12 13:43:42 +02001342 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001343 ret = NC_MSG_ERROR;
1344 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001345 }
1346
1347 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001348 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001349
Michal Vasko428087d2016-01-14 16:04:28 +01001350 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1351 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001352 ret = NC_MSG_ERROR;
1353 } else {
1354 /* specific message successfully sent */
1355 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001356 }
1357
Michal Vasko131120a2018-05-29 15:44:02 +02001358cleanup:
1359 va_end(ap);
1360 nc_session_io_unlock(session, __func__);
1361 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001362}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001363
1364void *
1365nc_realloc(void *ptr, size_t size)
1366{
1367 void *ret;
1368
1369 ret = realloc(ptr, size);
1370 if (!ret) {
1371 free(ptr);
1372 }
1373
1374 return ret;
1375}
David Sedlákfedbc792018-07-04 11:07:07 +02001376