blob: f7b9ec2387da59f4bd6068063c27e97c01db39c4 [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;
Olivier Matzac7fa2f2018-10-11 10:02:04 +020078 int fd;
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 {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010095 switch (session->ti_type) {
96 case NC_TI_NONE:
97 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010098
Michal Vasko6b7c42e2016-03-02 15:46:41 +010099 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200100 case NC_TI_UNIX:
101 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100102 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200103 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200104 if (r < 0) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100105 if ((errno == EAGAIN) || (errno == EINTR)) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100106 r = 0;
107 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200108 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100109 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200110 session->id, fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100111 session->status = NC_STATUS_INVALID;
112 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200113 return -1;
114 }
115 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100116 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200117 session->id, fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100118 session->status = NC_STATUS_INVALID;
119 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200120 return -1;
121 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100122 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200123
Radek Krejci53691be2016-02-22 13:58:37 +0100124#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100125 case NC_TI_LIBSSH:
126 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200127 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200128 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100129 r = 0;
130 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200131 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100132 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
133 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100134 session->status = NC_STATUS_INVALID;
135 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200136 return -1;
137 } else if (r == 0) {
138 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +0100139 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100140 session->status = NC_STATUS_INVALID;
141 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200142 return -1;
143 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100144 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200145 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100146 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200147#endif
148
Radek Krejci53691be2016-02-22 13:58:37 +0100149#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100150 case NC_TI_OPENSSL:
151 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100152 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200153 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200154 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100155 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100156 char *reasons;
157
Michal Vasko0abba6d2018-12-10 14:09:39 +0100158 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200159 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100160 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100161 r = 0;
162 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200163 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100164 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100165 session->status = NC_STATUS_INVALID;
166 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200167 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100168 case SSL_ERROR_SYSCALL:
169 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
170 session->status = NC_STATUS_INVALID;
171 session->term_reason = NC_SESSION_TERM_OTHER;
172 return -1;
173 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100174 reasons = nc_ssl_error_get_reasons();
175 ERR("Session %u: SSL error (%s).", session->id, reasons);
176 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100177 session->status = NC_STATUS_INVALID;
178 session->term_reason = NC_SESSION_TERM_OTHER;
179 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200180 default:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100181 ERR("Session %u: unknown SSL error occured (err code %d).", session->id, e);
Michal Vasko428087d2016-01-14 16:04:28 +0100182 session->status = NC_STATUS_INVALID;
183 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200184 return -1;
185 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200186 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100187 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200188#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100189 }
190
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100191 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100192 /* nothing read */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100193 usleep(NC_TIMEOUT_STEP);
Michal Vasko77a6abe2017-10-05 10:02:20 +0200194 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100195 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
196 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100197 ERR("Session %u: inactive read timeout elapsed.", session->id);
198 } else {
199 ERR("Session %u: active read timeout elapsed.", session->id);
200 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100201 session->status = NC_STATUS_INVALID;
202 session->term_reason = NC_SESSION_TERM_OTHER;
203 return -1;
204 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100205 } else {
206 /* something read */
207 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100208
209 /* reset inactive timeout */
Michal Vasko77a6abe2017-10-05 10:02:20 +0200210 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100211 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100212 }
213
Michal Vasko81b33fb2016-09-26 14:57:36 +0200214 } while (readd < count);
215 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200216
Michal Vasko81b33fb2016-09-26 14:57:36 +0200217 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200218}
219
220static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100221nc_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 +0200222{
223 ssize_t r;
224
225 assert(session);
226 assert(chunk);
227
228 if (!len) {
229 return 0;
230 }
231
Michal Vasko4eb3c312016-03-01 14:09:37 +0100232 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200233 if (!*chunk) {
234 ERRMEM;
235 return -1;
236 }
237
Michal Vasko36c7be82017-02-22 13:37:59 +0100238 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200239 if (r <= 0) {
240 free(*chunk);
241 return -1;
242 }
243
244 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200245 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200246
247 return r;
248}
249
250static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100251nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
252 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200253{
254 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200255 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200256
257 assert(session);
258 assert(endtag);
259
260 if (limit && limit < BUFFERSIZE) {
261 size = limit;
262 } else {
263 size = BUFFERSIZE;
264 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100265 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200266 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200267 ERRMEM;
268 return -1;
269 }
270
271 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100272 while (1) {
David Sedlák15dad862018-07-04 11:25:55 +0200273 if (limit && count == limit) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200274 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100275 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
276 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200277 return -1;
278 }
279
280 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200281 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200282 /* get more memory */
283 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200284 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100285 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200286 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200287 return -1;
288 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200289 }
290
291 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200292 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
293 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200294 free(chunk);
295 return -1;
296 }
297
David Sedlákfedbc792018-07-04 11:07:07 +0200298 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200299
David Sedlákfedbc792018-07-04 11:07:07 +0200300 for (i = len - matched; i > 0; i--) {
301 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
302 /*part of endtag found */
303 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200304 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200305 } else {
306 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200307 }
308 }
David Sedlákfedbc792018-07-04 11:07:07 +0200309
310 /* whole endtag found */
311 if (matched == len) {
312 break;
313 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200314 }
315
316 /* terminating null byte */
317 chunk[count] = 0;
318
319 if (result) {
320 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200321 } else {
322 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200323 }
324 return count;
325}
326
Michal Vasko131120a2018-05-29 15:44:02 +0200327/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Radek Krejci206fcd62015-10-07 15:42:48 +0200328NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200329nc_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 +0100330{
Michal Vasko131120a2018-05-29 15:44:02 +0200331 int ret, io_locked = passing_io_lock;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100332 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100333 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100334 /* use timeout in milliseconds instead seconds */
335 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
336 struct timespec ts_act_timeout;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100337 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100338
Michal Vasko428087d2016-01-14 16:04:28 +0100339 assert(session && data);
340 *data = NULL;
341
342 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100343 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +0200344 ret = NC_MSG_ERROR;
345 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100346 }
347
Michal Vasko77a6abe2017-10-05 10:02:20 +0200348 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100349 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
350
Michal Vasko131120a2018-05-29 15:44:02 +0200351 if (!io_locked) {
352 /* SESSION IO LOCK */
353 ret = nc_session_io_lock(session, io_timeout, __func__);
354 if (ret < 0) {
355 ret = NC_MSG_ERROR;
356 goto cleanup;
357 } else if (!ret) {
358 ret = NC_MSG_WOULDBLOCK;
359 goto cleanup;
360 }
361 io_locked = 1;
362 }
363
Michal Vasko05ba9df2016-01-13 14:40:27 +0100364 /* read the message */
365 switch (session->version) {
366 case NC_VERSION_10:
Michal Vasko36c7be82017-02-22 13:37:59 +0100367 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100368 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200369 ret = NC_MSG_ERROR;
370 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100371 }
372
373 /* cut off the end tag */
374 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
375 break;
376 case NC_VERSION_11:
377 while (1) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100378 ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200380 ret = NC_MSG_ERROR;
381 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100382 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100383 ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200385 ret = NC_MSG_ERROR;
386 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100387 }
388
389 if (!strcmp(chunk, "#\n")) {
390 /* end of chunked framing message */
391 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200392 if (!msg) {
393 ERR("Session %u: invalid frame chunk delimiters.", session->id);
394 goto malformed_msg;
395 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100396 break;
397 }
398
399 /* convert string to the size of the following chunk */
400 chunk_len = strtoul(chunk, (char **)NULL, 10);
401 free(chunk);
402 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100403 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100404 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100405 }
406
407 /* now we have size of next chunk, so read the chunk */
Michal Vasko36c7be82017-02-22 13:37:59 +0100408 ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100409 if (ret == -1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200410 ret = NC_MSG_ERROR;
411 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100412 }
413
414 /* realloc message buffer, remember to count terminating null byte */
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200415 msg = nc_realloc(msg, len + chunk_len + 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100416 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100417 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200418 ret = NC_MSG_ERROR;
419 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100420 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100421 memcpy(msg + len, chunk, chunk_len);
422 len += chunk_len;
423 msg[len] = '\0';
424 free(chunk);
425 }
426
427 break;
428 }
Michal Vasko131120a2018-05-29 15:44:02 +0200429
430 /* SESSION IO UNLOCK */
431 assert(io_locked);
432 nc_session_io_unlock(session, __func__);
433 io_locked = 0;
434
Michal Vasko81b33fb2016-09-26 14:57:36 +0200435 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100436
437 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100438 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100439 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100440 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100441 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100442 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100443 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100444 }
445 free(msg);
446 msg = NULL;
447
448 /* get and return message type */
449 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
450 if (!strcmp((*data)->name, "rpc")) {
451 return NC_MSG_RPC;
452 } else if (!strcmp((*data)->name, "rpc-reply")) {
453 return NC_MSG_REPLY;
454 } else if (!strcmp((*data)->name, "hello")) {
455 return NC_MSG_HELLO;
456 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100457 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100458 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100459 }
460 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
461 if (!strcmp((*data)->name, "notification")) {
462 return NC_MSG_NOTIF;
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 {
Michal Vaskod083db62016-01-19 10:31:29 +0100468 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100469 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100470 }
471
Michal Vasko428087d2016-01-14 16:04:28 +0100472malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100473 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100474 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
475 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100476 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100477
Michal Vaskofc5d07e2018-08-17 10:23:48 +0200478 if (io_locked) {
479 /* nc_write_msg_io locks and unlocks the lock by itself */
480 nc_session_io_unlock(session, __func__);
481 io_locked = 0;
482 }
483
Michal Vasko131120a2018-05-29 15:44:02 +0200484 if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) {
Michal Vaskod083db62016-01-19 10:31:29 +0100485 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100486 if (session->status != NC_STATUS_INVALID) {
487 session->status = NC_STATUS_INVALID;
488 session->term_reason = NC_SESSION_TERM_OTHER;
489 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100490 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100491 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100492 }
Michal Vasko131120a2018-05-29 15:44:02 +0200493 ret = NC_MSG_ERROR;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100494
Michal Vasko131120a2018-05-29 15:44:02 +0200495cleanup:
496 if (io_locked) {
497 nc_session_io_unlock(session, __func__);
498 }
Michal Vasko428087d2016-01-14 16:04:28 +0100499 free(msg);
500 free(*data);
501 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100502
Michal Vasko131120a2018-05-29 15:44:02 +0200503 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100504}
505
Michal Vasko428087d2016-01-14 16:04:28 +0100506/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
507static int
Michal Vasko131120a2018-05-29 15:44:02 +0200508nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100509{
Radek Krejci5961c702016-07-15 09:15:18 +0200510 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100511 int ret = -2;
512 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100513
514 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100515 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100516 return -1;
517 }
518
519 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100520#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100521 case NC_TI_LIBSSH:
522 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200523 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100524 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100525 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100526 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100527 session->status = NC_STATUS_INVALID;
528 session->term_reason = NC_SESSION_TERM_OTHER;
529 return -1;
530 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100531 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100532 session->status = NC_STATUS_INVALID;
533 session->term_reason = NC_SESSION_TERM_DROPPED;
534 return -1;
535 } else if (ret > 0) {
536 /* fake it */
537 ret = 1;
538 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100539 } else { /* ret == 0 */
540 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100541 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100542 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100543#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100544#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100545 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100546 ret = SSL_pending(session->ti.tls);
547 if (ret) {
548 /* some buffered TLS data available */
549 ret = 1;
550 fds.revents = POLLIN;
551 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100552 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100553
554 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100555#endif
Michal Vaskob983c002017-11-02 13:10:57 +0100556 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100557 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200558 case NC_TI_UNIX:
559 if (session->ti_type == NC_TI_FD)
Michal Vasko428087d2016-01-14 16:04:28 +0100560 fds.fd = session->ti.fd.in;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200561 else if (session->ti_type == NC_TI_UNIX)
562 fds.fd = session->ti.unixsock.sock;
Michal Vasko428087d2016-01-14 16:04:28 +0100563
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100564 fds.events = POLLIN;
565 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100566
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100567 sigfillset(&sigmask);
568 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200569 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100570 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100571
572 break;
573
574 default:
575 ERRINT;
576 return -1;
577 }
578
579 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
580 if (ret < 0) {
581 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200582 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100583 session->status = NC_STATUS_INVALID;
584 session->term_reason = NC_SESSION_TERM_OTHER;
585 return -1;
586 } else { /* status > 0 */
587 /* in case of standard (non-libssh) poll, there still can be an error */
588 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100589 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100590 session->status = NC_STATUS_INVALID;
591 session->term_reason = NC_SESSION_TERM_DROPPED;
592 return -1;
593 }
594 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100595 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100596 session->status = NC_STATUS_INVALID;
597 session->term_reason = NC_SESSION_TERM_OTHER;
598 return -1;
599 }
600 }
601
602 return ret;
603}
604
Michal Vasko131120a2018-05-29 15:44:02 +0200605/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100606NC_MSG_TYPE
Michal Vasko131120a2018-05-29 15:44:02 +0200607nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200608{
Michal Vasko428087d2016-01-14 16:04:28 +0100609 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200610
611 assert(data);
612 *data = NULL;
613
Michal Vasko428087d2016-01-14 16:04:28 +0100614 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100615 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100616 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200617 }
618
Michal Vasko131120a2018-05-29 15:44:02 +0200619 /* SESSION IO LOCK */
620 ret = nc_session_io_lock(session, io_timeout, __func__);
621 if (ret < 0) {
622 return NC_MSG_ERROR;
623 } else if (!ret) {
624 return NC_MSG_WOULDBLOCK;
625 }
626
627 ret = nc_read_poll(session, io_timeout);
Michal Vasko428087d2016-01-14 16:04:28 +0100628 if (ret == 0) {
629 /* timed out */
Michal Vasko131120a2018-05-29 15:44:02 +0200630
631 /* SESSION IO UNLOCK */
632 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100633 return NC_MSG_WOULDBLOCK;
634 } else if (ret < 0) {
635 /* poll error, error written */
Michal Vasko131120a2018-05-29 15:44:02 +0200636
637 /* SESSION IO UNLOCK */
638 nc_session_io_unlock(session, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100639 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200640 }
641
Michal Vasko131120a2018-05-29 15:44:02 +0200642 /* SESSION IO LOCK passed down */
643 return nc_read_msg_io(session, io_timeout, data, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200644}
Radek Krejcife0b3472015-10-12 13:43:42 +0200645
Michal Vasko428087d2016-01-14 16:04:28 +0100646/* does not really log, only fatal errors */
647int
648nc_session_is_connected(struct nc_session *session)
649{
650 int ret;
651 struct pollfd fds;
652
653 switch (session->ti_type) {
654 case NC_TI_FD:
655 fds.fd = session->ti.fd.in;
656 break;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200657 case NC_TI_UNIX:
658 fds.fd = session->ti.unixsock.sock;
659 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100660#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100661 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100662 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100663#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100664#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100665 case NC_TI_OPENSSL:
666 fds.fd = SSL_get_fd(session->ti.tls);
667 break;
668#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100669 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100670 return 0;
671 }
672
Michal Vasko840a8a62017-02-07 10:56:34 +0100673 if (fds.fd == -1) {
674 return 0;
675 }
676
Michal Vasko428087d2016-01-14 16:04:28 +0100677 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100678 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100679
680 errno = 0;
681 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
682
683 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100684 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100685 return 0;
686 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
687 return 0;
688 }
689
690 return 1;
691}
692
Radek Krejcife0b3472015-10-12 13:43:42 +0200693#define WRITE_BUFSIZE (2 * BUFFERSIZE)
694struct wclb_arg {
695 struct nc_session *session;
696 char buf[WRITE_BUFSIZE];
697 size_t len;
698};
699
Michal Vasko964e1732016-09-23 13:39:33 +0200700static int
Michal Vasko428087d2016-01-14 16:04:28 +0100701nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200702{
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200703 int c, fd;
Michal Vasko964e1732016-09-23 13:39:33 +0200704 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200705#ifdef NC_ENABLED_TLS
706 unsigned long e;
707#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200708
Michal Vasko428087d2016-01-14 16:04:28 +0100709 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
710 return -1;
711 }
712
713 /* prevent SIGPIPE this way */
714 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100715 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100716 session->status = NC_STATUS_INVALID;
717 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100718 return -1;
719 }
720
Michal Vasko81b33fb2016-09-26 14:57:36 +0200721 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200722
Michal Vasko81b33fb2016-09-26 14:57:36 +0200723 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200724 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200725 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200726 case NC_TI_UNIX:
727 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
728 c = write(fd, (char *)(buf + written), count - written);
729 if (c < 0 && errno == EAGAIN) {
730 c = 0;
731 } else if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200732 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200733 return -1;
734 }
735 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200736
Radek Krejci53691be2016-02-22 13:58:37 +0100737#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200738 case NC_TI_LIBSSH:
739 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
740 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
741 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
742 } else {
743 ERR("Session %u: SSH channel unexpected EOF.", session->id);
744 }
745 session->status = NC_STATUS_INVALID;
746 session->term_reason = NC_SESSION_TERM_DROPPED;
747 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100748 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200749 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200750 if ((c == SSH_ERROR) || (c == -1)) {
751 ERR("Session %u: SSH channel write failed.", session->id);
752 return -1;
753 }
754 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200755#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100756#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200757 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200758 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200759 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100760 char *reasons;
761
Michal Vasko964e1732016-09-23 13:39:33 +0200762 switch ((e = SSL_get_error(session->ti.tls, c))) {
763 case SSL_ERROR_ZERO_RETURN:
764 ERR("Session %u: SSL connection was properly closed.", session->id);
765 return -1;
766 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100767 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200768 c = 0;
769 break;
770 case SSL_ERROR_SYSCALL:
771 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
772 return -1;
773 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100774 reasons = nc_ssl_error_get_reasons();
775 ERR("Session %u: SSL error (%s).", session->id, reasons);
776 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200777 return -1;
778 default:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100779 ERR("Session %u: unknown SSL error occured (err code %d).", session->id, e);
Michal Vasko964e1732016-09-23 13:39:33 +0200780 return -1;
781 }
782 }
783 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200784#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200785 default:
786 ERRINT;
787 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200788 }
789
790 if (c == 0) {
791 /* we must wait */
792 usleep(NC_TIMEOUT_STEP);
793 }
794
795 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200796 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200797
Michal Vasko964e1732016-09-23 13:39:33 +0200798 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200799}
800
Michal Vasko428087d2016-01-14 16:04:28 +0100801static int
802nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100803{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100804 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100805 char chunksize[20];
806
807 if (session->version == NC_VERSION_11) {
808 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100809 ret = nc_write(session, chunksize, strlen(chunksize));
810 if (ret == -1) {
811 return -1;
812 }
Michal Vasko086311b2016-01-08 09:53:11 +0100813 }
Michal Vasko428087d2016-01-14 16:04:28 +0100814
815 c = nc_write(session, buf, count);
816 if (c == -1) {
817 return -1;
818 }
819 ret += c;
820
821 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100822}
823
Radek Krejcife0b3472015-10-12 13:43:42 +0200824static int
Michal Vasko428087d2016-01-14 16:04:28 +0100825nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200826{
Michal Vasko428087d2016-01-14 16:04:28 +0100827 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100828
Michal Vasko428087d2016-01-14 16:04:28 +0100829 if (session->version == NC_VERSION_11) {
830 ret = nc_write(session, "\n##\n", 4);
831 } else {
832 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200833 }
834
Michal Vasko428087d2016-01-14 16:04:28 +0100835 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200836}
837
Michal Vasko428087d2016-01-14 16:04:28 +0100838static int
839nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200840{
Michal Vasko428087d2016-01-14 16:04:28 +0100841 int ret = 0;
842
Radek Krejcife0b3472015-10-12 13:43:42 +0200843 /* flush current buffer */
844 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100845 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200846 warg->len = 0;
847 }
Michal Vasko428087d2016-01-14 16:04:28 +0100848
849 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200850}
851
852static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100853nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200854{
Michal Vasko428087d2016-01-14 16:04:28 +0100855 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100856 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200857 struct wclb_arg *warg = (struct wclb_arg *)arg;
858
859 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100860 c = nc_write_clb_flush(warg);
861 if (c == -1) {
862 return -1;
863 }
864 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200865
866 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100867 c = nc_write_endtag(warg->session);
868 if (c == -1) {
869 return -1;
870 }
871 ret += c;
872
873 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200874 }
875
876 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
877 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100878 c = nc_write_clb_flush(warg);
879 if (c == -1) {
880 return -1;
881 }
882 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200883 }
Michal Vasko428087d2016-01-14 16:04:28 +0100884
Radek Krejci047300e2016-03-08 16:46:58 +0100885 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200886 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100887 c = nc_write_starttag_and_msg(warg->session, buf, count);
888 if (c == -1) {
889 return -1;
890 }
891 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200892 } else {
893 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100894 if (xmlcontent) {
895 for (l = 0; l < count; l++) {
896 if (warg->len + 5 >= WRITE_BUFSIZE) {
897 /* buffer is full */
898 c = nc_write_clb_flush(warg);
899 if (c == -1) {
900 return -1;
901 }
902 }
903
904 switch (((char *)buf)[l]) {
905 case '&':
906 ret += 5;
907 memcpy(&warg->buf[warg->len], "&amp;", 5);
908 warg->len += 5;
909 break;
910 case '<':
911 ret += 4;
912 memcpy(&warg->buf[warg->len], "&lt;", 4);
913 warg->len += 4;
914 break;
915 case '>':
916 /* not needed, just for readability */
917 ret += 4;
918 memcpy(&warg->buf[warg->len], "&gt;", 4);
919 warg->len += 4;
920 break;
921 default:
922 ret++;
923 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
924 warg->len++;
925 }
926 }
927 } else {
928 memcpy(&warg->buf[warg->len], buf, count);
929 warg->len += count; /* is <= WRITE_BUFSIZE */
930 ret += count;
931 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200932 }
933
Michal Vasko428087d2016-01-14 16:04:28 +0100934 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200935}
936
Radek Krejci047300e2016-03-08 16:46:58 +0100937static ssize_t
938nc_write_xmlclb(void *arg, const void *buf, size_t count)
939{
940 return nc_write_clb(arg, buf, count, 0);
941}
942
Michal Vasko05ba9df2016-01-13 14:40:27 +0100943static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100944nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
945 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100946{
Michal Vasko08611b32016-12-05 13:30:37 +0100947 if (open) {
948 nc_write_clb((void *)arg, "<", 1, 0);
949 } else {
950 nc_write_clb((void *)arg, "</", 2, 0);
951 }
952
953 if (prefix) {
954 nc_write_clb((void *)arg, prefix, pref_len, 0);
955 nc_write_clb((void *)arg, ":", 1, 0);
956 }
957
958 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100959 if (!open || !no_attr) {
960 nc_write_clb((void *)arg, ">", 1, 0);
961 }
Michal Vasko08611b32016-12-05 13:30:37 +0100962}
963
964static void
965nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
966{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100967 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100968 char str_sid[11];
969
Michal Vasko08611b32016-12-05 13:30:37 +0100970 if (prefix) {
971 pref_len = strlen(prefix);
972 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100973
Michal Vasko52bd9492016-12-08 09:37:43 +0100974 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100975
Michal Vasko52bd9492016-12-08 09:37:43 +0100976 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100977 switch (err->type) {
978 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100979 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100980 break;
981 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100982 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100983 break;
984 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100985 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100986 break;
987 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100988 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100989 break;
990 default:
991 ERRINT;
992 return;
993 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100994
Michal Vasko52bd9492016-12-08 09:37:43 +0100995 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100996
Michal Vasko52bd9492016-12-08 09:37:43 +0100997 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100998 switch (err->tag) {
999 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +01001000 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001001 break;
1002 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +01001003 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001004 break;
1005 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +01001006 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001007 break;
1008 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001009 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001010 break;
1011 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001012 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001013 break;
1014 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +01001015 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001016 break;
1017 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001018 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001019 break;
1020 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001021 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001022 break;
1023 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +01001024 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001025 break;
1026 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +01001027 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001028 break;
1029 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001030 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001031 break;
1032 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001033 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001034 break;
1035 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +01001036 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001037 break;
1038 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +01001039 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001040 break;
1041 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +01001042 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001043 break;
1044 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +01001045 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001046 break;
1047 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +01001048 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001049 break;
1050 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +01001051 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001052 break;
1053 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +01001054 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001055 break;
1056 default:
1057 ERRINT;
1058 return;
1059 }
Michal Vasko52bd9492016-12-08 09:37:43 +01001060 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001061
Michal Vasko52bd9492016-12-08 09:37:43 +01001062 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001063 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001064 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001065
1066 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001067 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001068 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001069 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001070 }
1071
1072 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001073 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001074 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001075 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001076 }
1077
1078 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001079 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001080 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +01001081 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
1082 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
1083 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001084 }
Radek Krejci047300e2016-03-08 16:46:58 +01001085 nc_write_clb((void *)arg, ">", 1, 0);
1086 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001087 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001088 }
1089
Michal Vasko90920b02016-05-20 14:07:00 +02001090 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001091 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001092
Michal Vasko90920b02016-05-20 14:07:00 +02001093 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001094 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +02001095 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +01001096 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +01001097 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001098 }
1099
1100 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001101 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001102 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001103 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001104 }
1105
1106 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001107 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001108 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001109 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001110 }
1111
1112 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +01001113 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +01001114 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +01001115 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001116 }
1117
1118 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +01001119 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001120 }
1121
Michal Vasko52bd9492016-12-08 09:37:43 +01001122 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001123 }
1124
Michal Vasko52bd9492016-12-08 09:37:43 +01001125 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001126}
1127
Michal Vasko131120a2018-05-29 15:44:02 +02001128/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
1129NC_MSG_TYPE
1130nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001131{
Radek Krejcid116db42016-01-08 15:36:30 +01001132 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +02001133 int count, ret;
Michal Vasko08611b32016-12-05 13:30:37 +01001134 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001135 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001136 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001137 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001138 struct nc_server_reply *reply;
1139 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001140 char *buf = NULL;
1141 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001142 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001143 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001144 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001145
Michal Vasko428087d2016-01-14 16:04:28 +01001146 assert(session);
1147
1148 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001149 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko131120a2018-05-29 15:44:02 +02001150 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001151 }
1152
Radek Krejcife0b3472015-10-12 13:43:42 +02001153 arg.session = session;
1154 arg.len = 0;
1155
Michal Vasko131120a2018-05-29 15:44:02 +02001156 /* SESSION IO LOCK */
1157 ret = nc_session_io_lock(session, io_timeout, __func__);
1158 if (ret < 0) {
1159 return NC_MSG_ERROR;
1160 } else if (!ret) {
1161 return NC_MSG_WOULDBLOCK;
1162 }
1163
1164 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +02001165
Radek Krejcife0b3472015-10-12 13:43:42 +02001166 switch (type) {
1167 case NC_MSG_RPC:
1168 content = va_arg(ap, struct lyd_node *);
1169 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001170
Radek Krejcife0b3472015-10-12 13:43:42 +02001171 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001172 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001173 if (count == -1) {
1174 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001175 ret = NC_MSG_ERROR;
1176 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001177 }
Radek Krejci047300e2016-03-08 16:46:58 +01001178 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001179 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001180
Michal Vasko5a91ce72017-10-19 11:30:02 +02001181 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001182 ret = NC_MSG_ERROR;
1183 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001184 }
Radek Krejci047300e2016-03-08 16:46:58 +01001185 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001186
Michal Vasko2e6defd2016-10-07 15:48:15 +02001187 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001188 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001189
Radek Krejcife0b3472015-10-12 13:43:42 +02001190 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001191 rpc_elem = va_arg(ap, struct lyxml_elem *);
1192 reply = va_arg(ap, struct nc_server_reply *);
1193
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001194 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1195 nc_write_clb((void *)&arg, "<", 1, 0);
1196 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1197 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001198 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001199 }
1200 else {
1201 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001202 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001203 }
1204
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001205 /* can be NULL if replying with a malformed-message error */
1206 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001207 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001208 nc_write_clb((void *)&arg, ">", 1, 0);
1209 } else {
1210 /* but put there at least the correct namespace */
Michal Vaskobaaa9f02018-01-18 09:12:58 +01001211 nc_write_clb((void *)&arg, " xmlns=\""NC_NS_BASE"\">", 49, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001212 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001213 switch (reply->type) {
1214 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001215 nc_write_clb((void *)&arg, "<", 1, 0);
1216 if (base_prefix) {
1217 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1218 nc_write_clb((void *)&arg, ":", 1, 0);
1219 }
1220 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001221 break;
1222 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001223 switch(((struct nc_server_reply_data *)reply)->wd) {
1224 case NC_WD_UNKNOWN:
1225 case NC_WD_EXPLICIT:
1226 wd = LYP_WD_EXPLICIT;
1227 break;
1228 case NC_WD_TRIM:
1229 wd = LYP_WD_TRIM;
1230 break;
1231 case NC_WD_ALL:
1232 wd = LYP_WD_ALL;
1233 break;
1234 case NC_WD_ALL_TAG:
1235 wd = LYP_WD_ALL_TAG;
1236 break;
1237 }
Michal Vasko5a91ce72017-10-19 11:30:02 +02001238 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
1239 LYP_WITHSIBLINGS | LYP_NETCONF | wd)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001240 ret = NC_MSG_ERROR;
1241 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001242 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001243 break;
1244 case NC_RPL_ERROR:
1245 error_rpl = (struct nc_server_reply_error *)reply;
1246 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001247 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001248 }
1249 break;
1250 default:
1251 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001252 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001253 ret = NC_MSG_ERROR;
1254 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001255 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001256 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1257 nc_write_clb((void *)&arg, "</", 2, 0);
1258 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1259 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1260 }
1261 else {
1262 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1263 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001264 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001265
Radek Krejcife0b3472015-10-12 13:43:42 +02001266 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001267 notif = va_arg(ap, struct nc_server_notif *);
1268
1269 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1270 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1271 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1272 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001273 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001274 ret = NC_MSG_ERROR;
1275 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001276 }
mohitarora24878b2962016-11-09 18:45:33 -05001277 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001278 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001279
Radek Krejcid116db42016-01-08 15:36:30 +01001280 case NC_MSG_HELLO:
1281 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001282 ret = NC_MSG_ERROR;
1283 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001284 }
1285 capabilities = va_arg(ap, const char **);
1286 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001287
Radek Krejcid116db42016-01-08 15:36:30 +01001288 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001289 if (count == -1) {
1290 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001291 ret = NC_MSG_ERROR;
1292 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001293 }
Radek Krejci047300e2016-03-08 16:46:58 +01001294 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001295 free(buf);
1296 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001297 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1298 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1299 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001300 }
1301 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001302 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001303 if (count == -1) {
1304 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001305 ret = NC_MSG_ERROR;
1306 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001307 }
Radek Krejci047300e2016-03-08 16:46:58 +01001308 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001309 free(buf);
1310 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001311 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001312 }
Radek Krejcid116db42016-01-08 15:36:30 +01001313 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001314
Radek Krejcife0b3472015-10-12 13:43:42 +02001315 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001316 ret = NC_MSG_ERROR;
1317 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001318 }
1319
1320 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001321 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001322
Michal Vasko428087d2016-01-14 16:04:28 +01001323 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1324 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001325 ret = NC_MSG_ERROR;
1326 } else {
1327 /* specific message successfully sent */
1328 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001329 }
1330
Michal Vasko131120a2018-05-29 15:44:02 +02001331cleanup:
1332 va_end(ap);
1333 nc_session_io_unlock(session, __func__);
1334 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001335}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001336
1337void *
1338nc_realloc(void *ptr, size_t size)
1339{
1340 void *ret;
1341
1342 ret = realloc(ptr, size);
1343 if (!ret) {
1344 free(ptr);
1345 }
1346
1347 return ret;
1348}
David Sedlákfedbc792018-07-04 11:07:07 +02001349