blob: 95572aa59cf3aad57c03e0c2a379964d42d0655b [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file io.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - input/output functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020013 */
14
Miroslav Mareš9563b812017-08-19 17:45:36 +020015#define _GNU_SOURCE /* asprintf, signals */
Radek Krejci206fcd62015-10-07 15:42:48 +020016#include <assert.h>
17#include <errno.h>
18#include <poll.h>
Michal Vasko3512e402016-01-28 16:22:34 +010019#include <inttypes.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020020#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020021#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010024#include <signal.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010025#include <time.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020026
Michal Vasko964e1732016-09-23 13:39:33 +020027#ifdef NC_ENABLED_TLS
28# include <openssl/err.h>
29#endif
30
Radek Krejci206fcd62015-10-07 15:42:48 +020031#include <libyang/libyang.h>
32
Radek Krejci206fcd62015-10-07 15:42:48 +020033#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020034
Radek Krejcife0b3472015-10-12 13:43:42 +020035#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020036
37static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010038nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +020039{
Michal Vasko81b33fb2016-09-26 14:57:36 +020040 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010041 ssize_t r = -1;
Michal Vasko36c7be82017-02-22 13:37:59 +010042 struct timespec ts_cur, ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020043
44 assert(session);
45 assert(buf);
46
Michal Vasko428087d2016-01-14 16:04:28 +010047 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
48 return -1;
49 }
50
Radek Krejci206fcd62015-10-07 15:42:48 +020051 if (!count) {
52 return 0;
53 }
54
Michal Vasko77a6abe2017-10-05 10:02:20 +020055 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +010056 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +020057 do {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010058 switch (session->ti_type) {
59 case NC_TI_NONE:
60 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010061
Michal Vasko6b7c42e2016-03-02 15:46:41 +010062 case NC_TI_FD:
63 /* read via standard file descriptor */
Michal Vasko81b33fb2016-09-26 14:57:36 +020064 r = read(session->ti.fd.in, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +020065 if (r < 0) {
Michal Vasko36c7be82017-02-22 13:37:59 +010066 if ((errno == EAGAIN) || (errno == EINTR)) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010067 r = 0;
68 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020069 } else {
Michal Vaskod083db62016-01-19 10:31:29 +010070 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
71 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +010072 session->status = NC_STATUS_INVALID;
73 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020074 return -1;
75 }
76 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +010077 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
78 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +010079 session->status = NC_STATUS_INVALID;
80 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +020081 return -1;
82 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +010083 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020084
Radek Krejci53691be2016-02-22 13:58:37 +010085#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +010086 case NC_TI_LIBSSH:
87 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +020088 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +020089 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010090 r = 0;
91 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020092 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +010093 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
94 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +010095 session->status = NC_STATUS_INVALID;
96 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020097 return -1;
98 } else if (r == 0) {
99 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +0100100 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100101 session->status = NC_STATUS_INVALID;
102 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200103 return -1;
104 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100105 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200106 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100107 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200108#endif
109
Radek Krejci53691be2016-02-22 13:58:37 +0100110#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100111 case NC_TI_OPENSSL:
112 /* read via OpenSSL */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200113 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200114 if (r <= 0) {
115 int x;
116 switch (x = SSL_get_error(session->ti.tls, r)) {
117 case SSL_ERROR_WANT_READ:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100118 r = 0;
119 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200120 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100121 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100122 session->status = NC_STATUS_INVALID;
123 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200124 return -1;
125 default:
Michal Vaskod083db62016-01-19 10:31:29 +0100126 ERR("Session %u: reading from the TLS session failed (SSL code %d).", session->id, x);
Michal Vasko428087d2016-01-14 16:04:28 +0100127 session->status = NC_STATUS_INVALID;
128 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200129 return -1;
130 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200131 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100132 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200133#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100134 }
135
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100136 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100137 /* nothing read */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100138 usleep(NC_TIMEOUT_STEP);
Michal Vasko77a6abe2017-10-05 10:02:20 +0200139 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100140 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
141 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100142 ERR("Session %u: inactive read timeout elapsed.", session->id);
143 } else {
144 ERR("Session %u: active read timeout elapsed.", session->id);
145 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100146 session->status = NC_STATUS_INVALID;
147 session->term_reason = NC_SESSION_TERM_OTHER;
148 return -1;
149 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100150 } else {
151 /* something read */
152 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100153
154 /* reset inactive timeout */
Michal Vasko77a6abe2017-10-05 10:02:20 +0200155 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100156 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100157 }
158
Michal Vasko81b33fb2016-09-26 14:57:36 +0200159 } while (readd < count);
160 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200161
Michal Vasko81b33fb2016-09-26 14:57:36 +0200162 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200163}
164
165static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100166nc_read_chunk(struct nc_session *session, size_t len, uint32_t inact_timeout, struct timespec *ts_act_timeout, char **chunk)
Radek Krejci206fcd62015-10-07 15:42:48 +0200167{
168 ssize_t r;
169
170 assert(session);
171 assert(chunk);
172
173 if (!len) {
174 return 0;
175 }
176
Michal Vasko4eb3c312016-03-01 14:09:37 +0100177 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200178 if (!*chunk) {
179 ERRMEM;
180 return -1;
181 }
182
Michal Vasko36c7be82017-02-22 13:37:59 +0100183 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200184 if (r <= 0) {
185 free(*chunk);
186 return -1;
187 }
188
189 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200190 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200191
192 return r;
193}
194
195static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100196nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
197 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200198{
199 char *chunk = NULL;
200 size_t size, count = 0, r, len;
201
202 assert(session);
203 assert(endtag);
204
205 if (limit && limit < BUFFERSIZE) {
206 size = limit;
207 } else {
208 size = BUFFERSIZE;
209 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100210 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200211 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200212 ERRMEM;
213 return -1;
214 }
215
216 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100217 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200218 if (limit && count == limit) {
219 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100220 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
221 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200222 return -1;
223 }
224
225 /* resize buffer if needed */
226 if (count == size) {
227 /* get more memory */
228 size = size + BUFFERSIZE;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100229 chunk = realloc(chunk, (size + 1) * sizeof *chunk);
230 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200231 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200232 return -1;
233 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200234 }
235
236 /* get another character */
Michal Vasko36c7be82017-02-22 13:37:59 +0100237 r = nc_read(session, &(chunk[count]), 1, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200238 if (r != 1) {
239 free(chunk);
240 return -1;
241 }
242
243 count++;
244
245 /* check endtag */
246 if (count >= len) {
247 if (!strncmp(endtag, &(chunk[count - len]), len)) {
248 /* endtag found */
249 break;
250 }
251 }
252 }
253
254 /* terminating null byte */
255 chunk[count] = 0;
256
257 if (result) {
258 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200259 } else {
260 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200261 }
262 return count;
263}
264
Michal Vasko428087d2016-01-14 16:04:28 +0100265/* return NC_MSG_ERROR can change session status */
Radek Krejci206fcd62015-10-07 15:42:48 +0200266NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100267nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
268{
269 int ret;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100270 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100271 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100272 /* use timeout in milliseconds instead seconds */
273 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
274 struct timespec ts_act_timeout;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100275 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100276
Michal Vasko428087d2016-01-14 16:04:28 +0100277 assert(session && data);
278 *data = NULL;
279
280 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100281 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100282 return NC_MSG_ERROR;
283 }
284
Michal Vasko77a6abe2017-10-05 10:02:20 +0200285 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100286 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
287
Michal Vasko05ba9df2016-01-13 14:40:27 +0100288 /* read the message */
289 switch (session->version) {
290 case NC_VERSION_10:
Michal Vasko36c7be82017-02-22 13:37:59 +0100291 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100292 if (ret == -1) {
293 goto error;
294 }
295
296 /* cut off the end tag */
297 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
298 break;
299 case NC_VERSION_11:
300 while (1) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100301 ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100302 if (ret == -1) {
303 goto error;
304 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100305 ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100306 if (ret == -1) {
307 goto error;
308 }
309
310 if (!strcmp(chunk, "#\n")) {
311 /* end of chunked framing message */
312 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200313 if (!msg) {
314 ERR("Session %u: invalid frame chunk delimiters.", session->id);
315 goto malformed_msg;
316 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100317 break;
318 }
319
320 /* convert string to the size of the following chunk */
321 chunk_len = strtoul(chunk, (char **)NULL, 10);
322 free(chunk);
323 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100324 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100325 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100326 }
327
328 /* now we have size of next chunk, so read the chunk */
Michal Vasko36c7be82017-02-22 13:37:59 +0100329 ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100330 if (ret == -1) {
331 goto error;
332 }
333
334 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko4eb3c312016-03-01 14:09:37 +0100335 msg = realloc(msg, len + chunk_len + 1);
336 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100337 ERRMEM;
338 goto error;
339 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100340 memcpy(msg + len, chunk, chunk_len);
341 len += chunk_len;
342 msg[len] = '\0';
343 free(chunk);
344 }
345
346 break;
347 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200348 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100349
350 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100351 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100352 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100353 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100354 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100355 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100356 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100357 }
358 free(msg);
359 msg = NULL;
360
361 /* get and return message type */
362 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
363 if (!strcmp((*data)->name, "rpc")) {
364 return NC_MSG_RPC;
365 } else if (!strcmp((*data)->name, "rpc-reply")) {
366 return NC_MSG_REPLY;
367 } else if (!strcmp((*data)->name, "hello")) {
368 return NC_MSG_HELLO;
369 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100370 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100371 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100372 }
373 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
374 if (!strcmp((*data)->name, "notification")) {
375 return NC_MSG_NOTIF;
376 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100377 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100378 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379 }
380 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100381 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100382 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100383 }
384
Michal Vasko428087d2016-01-14 16:04:28 +0100385malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100386 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100387 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
388 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100389 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100390
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100391 if (nc_write_msg(session, NC_MSG_REPLY, NULL, reply) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100392 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100393 if (session->status != NC_STATUS_INVALID) {
394 session->status = NC_STATUS_INVALID;
395 session->term_reason = NC_SESSION_TERM_OTHER;
396 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100397 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100398 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100399 }
400
Michal Vasko428087d2016-01-14 16:04:28 +0100401error:
402 /* cleanup */
403 free(msg);
404 free(*data);
405 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100406
407 return NC_MSG_ERROR;
408}
409
Michal Vasko428087d2016-01-14 16:04:28 +0100410/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
411static int
412nc_read_poll(struct nc_session *session, int timeout)
413{
Radek Krejci5961c702016-07-15 09:15:18 +0200414 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100415 int ret = -2;
416 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100417
418 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100419 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100420 return -1;
421 }
422
423 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100424#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100425 case NC_TI_LIBSSH:
426 /* EINTR is handled, it resumes waiting */
427 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
428 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100429 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100430 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100431 session->status = NC_STATUS_INVALID;
432 session->term_reason = NC_SESSION_TERM_OTHER;
433 return -1;
434 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100435 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100436 session->status = NC_STATUS_INVALID;
437 session->term_reason = NC_SESSION_TERM_DROPPED;
438 return -1;
439 } else if (ret > 0) {
440 /* fake it */
441 ret = 1;
442 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100443 } else { /* ret == 0 */
444 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100445 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100446 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100447#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100448#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100449 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100450 ret = SSL_pending(session->ti.tls);
451 if (ret) {
452 /* some buffered TLS data available */
453 ret = 1;
454 fds.revents = POLLIN;
455 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100456 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100457
458 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100459#endif
Michal Vaskob983c002017-11-02 13:10:57 +0100460 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100461 case NC_TI_FD:
462 if (session->ti_type == NC_TI_FD) {
463 fds.fd = session->ti.fd.in;
464 }
465
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100466 fds.events = POLLIN;
467 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100468
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100469 sigfillset(&sigmask);
470 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
471 ret = poll(&fds, 1, timeout);
472 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100473
474 break;
475
476 default:
477 ERRINT;
478 return -1;
479 }
480
481 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
482 if (ret < 0) {
483 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200484 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100485 session->status = NC_STATUS_INVALID;
486 session->term_reason = NC_SESSION_TERM_OTHER;
487 return -1;
488 } else { /* status > 0 */
489 /* in case of standard (non-libssh) poll, there still can be an error */
490 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100491 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100492 session->status = NC_STATUS_INVALID;
493 session->term_reason = NC_SESSION_TERM_DROPPED;
494 return -1;
495 }
496 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100497 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100498 session->status = NC_STATUS_INVALID;
499 session->term_reason = NC_SESSION_TERM_OTHER;
500 return -1;
501 }
502 }
503
504 return ret;
505}
506
507/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100508NC_MSG_TYPE
509nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200510{
Michal Vasko428087d2016-01-14 16:04:28 +0100511 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200512
513 assert(data);
514 *data = NULL;
515
Michal Vasko428087d2016-01-14 16:04:28 +0100516 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100517 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100518 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200519 }
520
Michal Vasko428087d2016-01-14 16:04:28 +0100521 ret = nc_read_poll(session, timeout);
522 if (ret == 0) {
523 /* timed out */
524 return NC_MSG_WOULDBLOCK;
525 } else if (ret < 0) {
526 /* poll error, error written */
527 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200528 }
529
Michal Vasko05ba9df2016-01-13 14:40:27 +0100530 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200531}
Radek Krejcife0b3472015-10-12 13:43:42 +0200532
Michal Vasko428087d2016-01-14 16:04:28 +0100533/* does not really log, only fatal errors */
534int
535nc_session_is_connected(struct nc_session *session)
536{
537 int ret;
538 struct pollfd fds;
539
540 switch (session->ti_type) {
541 case NC_TI_FD:
542 fds.fd = session->ti.fd.in;
543 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100544#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100545 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100546 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100547#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100548#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100549 case NC_TI_OPENSSL:
550 fds.fd = SSL_get_fd(session->ti.tls);
551 break;
552#endif
553 case NC_TI_NONE:
554 ERRINT;
555 return 0;
556 }
557
Michal Vasko840a8a62017-02-07 10:56:34 +0100558 if (fds.fd == -1) {
559 return 0;
560 }
561
Michal Vasko428087d2016-01-14 16:04:28 +0100562 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100563 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100564
565 errno = 0;
566 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
567
568 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100569 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100570 return 0;
571 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
572 return 0;
573 }
574
575 return 1;
576}
577
Radek Krejcife0b3472015-10-12 13:43:42 +0200578#define WRITE_BUFSIZE (2 * BUFFERSIZE)
579struct wclb_arg {
580 struct nc_session *session;
581 char buf[WRITE_BUFSIZE];
582 size_t len;
583};
584
Michal Vasko964e1732016-09-23 13:39:33 +0200585static int
Michal Vasko428087d2016-01-14 16:04:28 +0100586nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200587{
Michal Vasko964e1732016-09-23 13:39:33 +0200588 int c;
589 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200590#ifdef NC_ENABLED_TLS
591 unsigned long e;
592#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200593
Michal Vasko428087d2016-01-14 16:04:28 +0100594 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
595 return -1;
596 }
597
598 /* prevent SIGPIPE this way */
599 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100600 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100601 session->status = NC_STATUS_INVALID;
602 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100603 return -1;
604 }
605
Michal Vasko81b33fb2016-09-26 14:57:36 +0200606 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200607
Michal Vasko81b33fb2016-09-26 14:57:36 +0200608 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200609 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200610 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200611 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200612 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200613 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200614 return -1;
615 }
616 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200617
Radek Krejci53691be2016-02-22 13:58:37 +0100618#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200619 case NC_TI_LIBSSH:
620 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
621 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
622 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
623 } else {
624 ERR("Session %u: SSH channel unexpected EOF.", session->id);
625 }
626 session->status = NC_STATUS_INVALID;
627 session->term_reason = NC_SESSION_TERM_DROPPED;
628 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100629 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200630 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200631 if ((c == SSH_ERROR) || (c == -1)) {
632 ERR("Session %u: SSH channel write failed.", session->id);
633 return -1;
634 }
635 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200636#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100637#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200638 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200639 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200640 if (c < 1) {
641 switch ((e = SSL_get_error(session->ti.tls, c))) {
642 case SSL_ERROR_ZERO_RETURN:
643 ERR("Session %u: SSL connection was properly closed.", session->id);
644 return -1;
645 case SSL_ERROR_WANT_WRITE:
646 c = 0;
647 break;
648 case SSL_ERROR_SYSCALL:
649 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
650 return -1;
651 case SSL_ERROR_SSL:
652 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
653 return -1;
654 default:
655 ERR("Session %u: unknown SSL error occured.", session->id);
656 return -1;
657 }
658 }
659 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200660#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200661 default:
662 ERRINT;
663 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200664 }
665
666 if (c == 0) {
667 /* we must wait */
668 usleep(NC_TIMEOUT_STEP);
669 }
670
671 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200672 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200673
Michal Vasko964e1732016-09-23 13:39:33 +0200674 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200675}
676
Michal Vasko428087d2016-01-14 16:04:28 +0100677static int
678nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100679{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100680 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100681 char chunksize[20];
682
683 if (session->version == NC_VERSION_11) {
684 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100685 ret = nc_write(session, chunksize, strlen(chunksize));
686 if (ret == -1) {
687 return -1;
688 }
Michal Vasko086311b2016-01-08 09:53:11 +0100689 }
Michal Vasko428087d2016-01-14 16:04:28 +0100690
691 c = nc_write(session, buf, count);
692 if (c == -1) {
693 return -1;
694 }
695 ret += c;
696
697 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100698}
699
Radek Krejcife0b3472015-10-12 13:43:42 +0200700static int
Michal Vasko428087d2016-01-14 16:04:28 +0100701nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200702{
Michal Vasko428087d2016-01-14 16:04:28 +0100703 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100704
Michal Vasko428087d2016-01-14 16:04:28 +0100705 if (session->version == NC_VERSION_11) {
706 ret = nc_write(session, "\n##\n", 4);
707 } else {
708 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200709 }
710
Michal Vasko428087d2016-01-14 16:04:28 +0100711 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200712}
713
Michal Vasko428087d2016-01-14 16:04:28 +0100714static int
715nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200716{
Michal Vasko428087d2016-01-14 16:04:28 +0100717 int ret = 0;
718
Radek Krejcife0b3472015-10-12 13:43:42 +0200719 /* flush current buffer */
720 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100721 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200722 warg->len = 0;
723 }
Michal Vasko428087d2016-01-14 16:04:28 +0100724
725 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200726}
727
728static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100729nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200730{
Michal Vasko428087d2016-01-14 16:04:28 +0100731 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100732 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200733 struct wclb_arg *warg = (struct wclb_arg *)arg;
734
735 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100736 c = nc_write_clb_flush(warg);
737 if (c == -1) {
738 return -1;
739 }
740 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200741
742 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100743 c = nc_write_endtag(warg->session);
744 if (c == -1) {
745 return -1;
746 }
747 ret += c;
748
749 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200750 }
751
752 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
753 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100754 c = nc_write_clb_flush(warg);
755 if (c == -1) {
756 return -1;
757 }
758 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200759 }
Michal Vasko428087d2016-01-14 16:04:28 +0100760
Radek Krejci047300e2016-03-08 16:46:58 +0100761 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200762 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100763 c = nc_write_starttag_and_msg(warg->session, buf, count);
764 if (c == -1) {
765 return -1;
766 }
767 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200768 } else {
769 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100770 if (xmlcontent) {
771 for (l = 0; l < count; l++) {
772 if (warg->len + 5 >= WRITE_BUFSIZE) {
773 /* buffer is full */
774 c = nc_write_clb_flush(warg);
775 if (c == -1) {
776 return -1;
777 }
778 }
779
780 switch (((char *)buf)[l]) {
781 case '&':
782 ret += 5;
783 memcpy(&warg->buf[warg->len], "&amp;", 5);
784 warg->len += 5;
785 break;
786 case '<':
787 ret += 4;
788 memcpy(&warg->buf[warg->len], "&lt;", 4);
789 warg->len += 4;
790 break;
791 case '>':
792 /* not needed, just for readability */
793 ret += 4;
794 memcpy(&warg->buf[warg->len], "&gt;", 4);
795 warg->len += 4;
796 break;
797 default:
798 ret++;
799 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
800 warg->len++;
801 }
802 }
803 } else {
804 memcpy(&warg->buf[warg->len], buf, count);
805 warg->len += count; /* is <= WRITE_BUFSIZE */
806 ret += count;
807 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200808 }
809
Michal Vasko428087d2016-01-14 16:04:28 +0100810 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200811}
812
Radek Krejci047300e2016-03-08 16:46:58 +0100813static ssize_t
814nc_write_xmlclb(void *arg, const void *buf, size_t count)
815{
816 return nc_write_clb(arg, buf, count, 0);
817}
818
Michal Vasko05ba9df2016-01-13 14:40:27 +0100819static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100820nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
821 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100822{
Michal Vasko08611b32016-12-05 13:30:37 +0100823 if (open) {
824 nc_write_clb((void *)arg, "<", 1, 0);
825 } else {
826 nc_write_clb((void *)arg, "</", 2, 0);
827 }
828
829 if (prefix) {
830 nc_write_clb((void *)arg, prefix, pref_len, 0);
831 nc_write_clb((void *)arg, ":", 1, 0);
832 }
833
834 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100835 if (!open || !no_attr) {
836 nc_write_clb((void *)arg, ">", 1, 0);
837 }
Michal Vasko08611b32016-12-05 13:30:37 +0100838}
839
840static void
841nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
842{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100843 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100844 char str_sid[11];
845
Michal Vasko08611b32016-12-05 13:30:37 +0100846 if (prefix) {
847 pref_len = strlen(prefix);
848 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100849
Michal Vasko52bd9492016-12-08 09:37:43 +0100850 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100851
Michal Vasko52bd9492016-12-08 09:37:43 +0100852 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100853 switch (err->type) {
854 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100855 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100856 break;
857 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100858 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100859 break;
860 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100861 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100862 break;
863 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100864 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100865 break;
866 default:
867 ERRINT;
868 return;
869 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100870
Michal Vasko52bd9492016-12-08 09:37:43 +0100871 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100872
Michal Vasko52bd9492016-12-08 09:37:43 +0100873 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100874 switch (err->tag) {
875 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100876 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100877 break;
878 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100879 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100880 break;
881 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100882 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100883 break;
884 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100885 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100886 break;
887 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100888 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100889 break;
890 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100891 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100892 break;
893 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100894 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100895 break;
896 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100897 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100898 break;
899 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100900 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100901 break;
902 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100903 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100904 break;
905 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100906 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100907 break;
908 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100909 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100910 break;
911 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100912 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100913 break;
914 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100915 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100916 break;
917 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100918 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100919 break;
920 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100921 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100922 break;
923 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100924 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100925 break;
926 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100927 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100928 break;
929 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100930 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100931 break;
932 default:
933 ERRINT;
934 return;
935 }
Michal Vasko52bd9492016-12-08 09:37:43 +0100936 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100937
Michal Vasko52bd9492016-12-08 09:37:43 +0100938 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100939 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100940 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100941
942 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100943 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100944 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100945 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100946 }
947
948 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100949 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100950 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100951 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100952 }
953
954 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100955 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100956 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +0100957 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
958 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
959 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100960 }
Radek Krejci047300e2016-03-08 16:46:58 +0100961 nc_write_clb((void *)arg, ">", 1, 0);
962 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100963 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100964 }
965
Michal Vasko90920b02016-05-20 14:07:00 +0200966 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100967 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100968
Michal Vasko90920b02016-05-20 14:07:00 +0200969 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100970 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +0200971 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +0100972 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100973 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100974 }
975
976 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100977 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100978 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100979 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100980 }
981
982 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100983 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100984 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100985 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100986 }
987
988 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100989 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100990 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100991 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100992 }
993
994 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100995 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100996 }
997
Michal Vasko52bd9492016-12-08 09:37:43 +0100998 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100999 }
1000
Michal Vasko52bd9492016-12-08 09:37:43 +01001001 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001002}
1003
Michal Vasko428087d2016-01-14 16:04:28 +01001004/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +01001005int
Radek Krejci127f8952016-10-12 14:57:16 +02001006nc_write_msg(struct nc_session *session, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001007{
Radek Krejcid116db42016-01-08 15:36:30 +01001008 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001009 int count;
Michal Vasko08611b32016-12-05 13:30:37 +01001010 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001011 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001012 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001013 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001014 struct nc_server_reply *reply;
1015 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001016 char *buf = NULL;
1017 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001018 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001019 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001020 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001021
Michal Vasko428087d2016-01-14 16:04:28 +01001022 assert(session);
1023
1024 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001025 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +01001026 return -1;
1027 }
1028
Radek Krejcid116db42016-01-08 15:36:30 +01001029 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +02001030
1031 arg.session = session;
1032 arg.len = 0;
1033
Radek Krejci127f8952016-10-12 14:57:16 +02001034
Radek Krejcife0b3472015-10-12 13:43:42 +02001035 switch (type) {
1036 case NC_MSG_RPC:
1037 content = va_arg(ap, struct lyd_node *);
1038 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001039
Radek Krejcife0b3472015-10-12 13:43:42 +02001040 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001041 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001042 if (count == -1) {
1043 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001044 va_end(ap);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001045 return -1;
1046 }
Radek Krejci047300e2016-03-08 16:46:58 +01001047 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001048 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001049
Michal Vasko5a91ce72017-10-19 11:30:02 +02001050 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF)) {
1051 va_end(ap);
1052 return -1;
1053 }
Radek Krejci047300e2016-03-08 16:46:58 +01001054 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001055
Michal Vasko2e6defd2016-10-07 15:48:15 +02001056 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001057 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001058
Radek Krejcife0b3472015-10-12 13:43:42 +02001059 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001060 rpc_elem = va_arg(ap, struct lyxml_elem *);
1061 reply = va_arg(ap, struct nc_server_reply *);
1062
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001063 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1064 nc_write_clb((void *)&arg, "<", 1, 0);
1065 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1066 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001067 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001068 }
1069 else {
1070 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001071 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001072 }
1073
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001074 /* can be NULL if replying with a malformed-message error */
1075 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001076 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001077 nc_write_clb((void *)&arg, ">", 1, 0);
1078 } else {
1079 /* but put there at least the correct namespace */
1080 nc_write_clb((void *)&arg, "xmlns=\""NC_NS_BASE"\">", 48, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001081 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001082 switch (reply->type) {
1083 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001084 nc_write_clb((void *)&arg, "<", 1, 0);
1085 if (base_prefix) {
1086 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1087 nc_write_clb((void *)&arg, ":", 1, 0);
1088 }
1089 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001090 break;
1091 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001092 switch(((struct nc_server_reply_data *)reply)->wd) {
1093 case NC_WD_UNKNOWN:
1094 case NC_WD_EXPLICIT:
1095 wd = LYP_WD_EXPLICIT;
1096 break;
1097 case NC_WD_TRIM:
1098 wd = LYP_WD_TRIM;
1099 break;
1100 case NC_WD_ALL:
1101 wd = LYP_WD_ALL;
1102 break;
1103 case NC_WD_ALL_TAG:
1104 wd = LYP_WD_ALL_TAG;
1105 break;
1106 }
Michal Vasko5a91ce72017-10-19 11:30:02 +02001107 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
1108 LYP_WITHSIBLINGS | LYP_NETCONF | wd)) {
1109 va_end(ap);
1110 return -1;
1111 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001112 break;
1113 case NC_RPL_ERROR:
1114 error_rpl = (struct nc_server_reply_error *)reply;
1115 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001116 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001117 }
1118 break;
1119 default:
1120 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001121 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001122 va_end(ap);
1123 return -1;
1124 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001125 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1126 nc_write_clb((void *)&arg, "</", 2, 0);
1127 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1128 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1129 }
1130 else {
1131 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1132 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001133 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001134
Radek Krejcife0b3472015-10-12 13:43:42 +02001135 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001136 notif = va_arg(ap, struct nc_server_notif *);
1137
1138 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1139 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1140 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1141 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001142 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0)) {
1143 va_end(ap);
1144 return -1;
1145 }
mohitarora24878b2962016-11-09 18:45:33 -05001146 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001147 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001148
Radek Krejcid116db42016-01-08 15:36:30 +01001149 case NC_MSG_HELLO:
1150 if (session->version != NC_VERSION_10) {
1151 va_end(ap);
1152 return -1;
1153 }
1154 capabilities = va_arg(ap, const char **);
1155 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001156
Radek Krejcid116db42016-01-08 15:36:30 +01001157 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001158 if (count == -1) {
1159 ERRMEM;
1160 va_end(ap);
1161 return -1;
1162 }
Radek Krejci047300e2016-03-08 16:46:58 +01001163 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001164 free(buf);
1165 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001166 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1167 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1168 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001169 }
1170 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001171 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001172 if (count == -1) {
1173 ERRMEM;
1174 va_end(ap);
1175 return -1;
1176 }
Radek Krejci047300e2016-03-08 16:46:58 +01001177 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001178 free(buf);
1179 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001180 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001181 }
Radek Krejcid116db42016-01-08 15:36:30 +01001182 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001183
Radek Krejcife0b3472015-10-12 13:43:42 +02001184 default:
Radek Krejcid116db42016-01-08 15:36:30 +01001185 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +02001186 return -1;
1187 }
1188
1189 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001190 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001191
1192 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +01001193 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1194 /* error was already written */
1195 return -1;
1196 }
1197
Radek Krejcid116db42016-01-08 15:36:30 +01001198 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001199}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001200
1201void *
1202nc_realloc(void *ptr, size_t size)
1203{
1204 void *ret;
1205
1206 ret = realloc(ptr, size);
1207 if (!ret) {
1208 free(ptr);
1209 }
1210
1211 return ret;
1212}