blob: 507d5e504134b6e3ee8d271daa886c7179c28c22 [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
Radek Krejci5961c702016-07-15 09:15:18 +020015#define _GNU_SOURCE /* asprintf */
Michal Vasko11d142a2016-01-19 15:58:24 +010016#define _POSIX_SOUCE /* signals */
Radek Krejci206fcd62015-10-07 15:42:48 +020017#include <assert.h>
18#include <errno.h>
19#include <poll.h>
Michal Vasko3512e402016-01-28 16:22:34 +010020#include <inttypes.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020021#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020022#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010025#include <signal.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010026#include <time.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020027
Michal Vasko964e1732016-09-23 13:39:33 +020028#ifdef NC_ENABLED_TLS
29# include <openssl/err.h>
30#endif
31
Radek Krejci206fcd62015-10-07 15:42:48 +020032#include <libyang/libyang.h>
33
Radek Krejci206fcd62015-10-07 15:42:48 +020034#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020035
Radek Krejcife0b3472015-10-12 13:43:42 +020036#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020037
38static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010039nc_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 +020040{
Michal Vasko81b33fb2016-09-26 14:57:36 +020041 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010042 ssize_t r = -1;
Michal Vasko36c7be82017-02-22 13:37:59 +010043 struct timespec ts_cur, ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020044
45 assert(session);
46 assert(buf);
47
Michal Vasko428087d2016-01-14 16:04:28 +010048 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
49 return -1;
50 }
51
Radek Krejci206fcd62015-10-07 15:42:48 +020052 if (!count) {
53 return 0;
54 }
55
Michal Vasko36c7be82017-02-22 13:37:59 +010056 nc_gettimespec(&ts_inact_timeout);
57 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +020058 do {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010059 switch (session->ti_type) {
60 case NC_TI_NONE:
61 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010062
Michal Vasko6b7c42e2016-03-02 15:46:41 +010063 case NC_TI_FD:
64 /* read via standard file descriptor */
Michal Vasko81b33fb2016-09-26 14:57:36 +020065 r = read(session->ti.fd.in, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +020066 if (r < 0) {
Michal Vasko36c7be82017-02-22 13:37:59 +010067 if ((errno == EAGAIN) || (errno == EINTR)) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010068 r = 0;
69 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020070 } else {
Michal Vaskod083db62016-01-19 10:31:29 +010071 ERR("Session %u: reading from file descriptor (%d) failed (%s).",
72 session->id, session->ti.fd.in, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +010073 session->status = NC_STATUS_INVALID;
74 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020075 return -1;
76 }
77 } else if (r == 0) {
Michal Vaskod083db62016-01-19 10:31:29 +010078 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
79 session->id, session->ti.fd.in);
Michal Vasko428087d2016-01-14 16:04:28 +010080 session->status = NC_STATUS_INVALID;
81 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +020082 return -1;
83 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +010084 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020085
Radek Krejci53691be2016-02-22 13:58:37 +010086#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +010087 case NC_TI_LIBSSH:
88 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +020089 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +020090 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010091 r = 0;
92 break;
Radek Krejci206fcd62015-10-07 15:42:48 +020093 } else if (r == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +010094 ERR("Session %u: reading from the SSH channel failed (%s).", session->id,
95 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +010096 session->status = NC_STATUS_INVALID;
97 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +020098 return -1;
99 } else if (r == 0) {
100 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko454e22b2016-01-21 15:34:08 +0100101 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100102 session->status = NC_STATUS_INVALID;
103 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200104 return -1;
105 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100106 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200107 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100108 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200109#endif
110
Radek Krejci53691be2016-02-22 13:58:37 +0100111#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100112 case NC_TI_OPENSSL:
113 /* read via OpenSSL */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200114 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200115 if (r <= 0) {
116 int x;
117 switch (x = SSL_get_error(session->ti.tls, r)) {
118 case SSL_ERROR_WANT_READ:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100119 r = 0;
120 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200121 case SSL_ERROR_ZERO_RETURN:
Michal Vaskod083db62016-01-19 10:31:29 +0100122 ERR("Session %u: communication socket unexpectedly closed (OpenSSL).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100123 session->status = NC_STATUS_INVALID;
124 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200125 return -1;
126 default:
Michal Vaskod083db62016-01-19 10:31:29 +0100127 ERR("Session %u: reading from the TLS session failed (SSL code %d).", session->id, x);
Michal Vasko428087d2016-01-14 16:04:28 +0100128 session->status = NC_STATUS_INVALID;
129 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200130 return -1;
131 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200132 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100133 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200134#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100135 }
136
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100137 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100138 /* nothing read */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100139 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +0100140 nc_gettimespec(&ts_cur);
141 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
142 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100143 ERR("Session %u: inactive read timeout elapsed.", session->id);
144 } else {
145 ERR("Session %u: active read timeout elapsed.", session->id);
146 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100147 session->status = NC_STATUS_INVALID;
148 session->term_reason = NC_SESSION_TERM_OTHER;
149 return -1;
150 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100151 } else {
152 /* something read */
153 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100154
155 /* reset inactive timeout */
156 nc_gettimespec(&ts_inact_timeout);
157 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100158 }
159
Michal Vasko81b33fb2016-09-26 14:57:36 +0200160 } while (readd < count);
161 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200162
Michal Vasko81b33fb2016-09-26 14:57:36 +0200163 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200164}
165
166static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100167nc_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 +0200168{
169 ssize_t r;
170
171 assert(session);
172 assert(chunk);
173
174 if (!len) {
175 return 0;
176 }
177
Michal Vasko4eb3c312016-03-01 14:09:37 +0100178 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200179 if (!*chunk) {
180 ERRMEM;
181 return -1;
182 }
183
Michal Vasko36c7be82017-02-22 13:37:59 +0100184 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200185 if (r <= 0) {
186 free(*chunk);
187 return -1;
188 }
189
190 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200191 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200192
193 return r;
194}
195
196static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100197nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
198 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200199{
200 char *chunk = NULL;
201 size_t size, count = 0, r, len;
202
203 assert(session);
204 assert(endtag);
205
206 if (limit && limit < BUFFERSIZE) {
207 size = limit;
208 } else {
209 size = BUFFERSIZE;
210 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100211 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200212 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200213 ERRMEM;
214 return -1;
215 }
216
217 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100218 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200219 if (limit && count == limit) {
220 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100221 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
222 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200223 return -1;
224 }
225
226 /* resize buffer if needed */
227 if (count == size) {
228 /* get more memory */
229 size = size + BUFFERSIZE;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100230 chunk = realloc(chunk, (size + 1) * sizeof *chunk);
231 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200232 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200233 return -1;
234 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200235 }
236
237 /* get another character */
Michal Vasko36c7be82017-02-22 13:37:59 +0100238 r = nc_read(session, &(chunk[count]), 1, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200239 if (r != 1) {
240 free(chunk);
241 return -1;
242 }
243
244 count++;
245
246 /* check endtag */
247 if (count >= len) {
248 if (!strncmp(endtag, &(chunk[count - len]), len)) {
249 /* endtag found */
250 break;
251 }
252 }
253 }
254
255 /* terminating null byte */
256 chunk[count] = 0;
257
258 if (result) {
259 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200260 } else {
261 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200262 }
263 return count;
264}
265
Michal Vasko428087d2016-01-14 16:04:28 +0100266/* return NC_MSG_ERROR can change session status */
Radek Krejci206fcd62015-10-07 15:42:48 +0200267NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100268nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
269{
270 int ret;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100271 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100272 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100273 /* use timeout in milliseconds instead seconds */
274 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
275 struct timespec ts_act_timeout;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100276 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100277
Michal Vasko428087d2016-01-14 16:04:28 +0100278 assert(session && data);
279 *data = NULL;
280
281 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100282 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100283 return NC_MSG_ERROR;
284 }
285
Michal Vasko36c7be82017-02-22 13:37:59 +0100286 nc_gettimespec(&ts_act_timeout);
287 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
288
Michal Vasko05ba9df2016-01-13 14:40:27 +0100289 /* read the message */
290 switch (session->version) {
291 case NC_VERSION_10:
Michal Vasko36c7be82017-02-22 13:37:59 +0100292 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100293 if (ret == -1) {
294 goto error;
295 }
296
297 /* cut off the end tag */
298 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
299 break;
300 case NC_VERSION_11:
301 while (1) {
Michal Vasko36c7be82017-02-22 13:37:59 +0100302 ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100303 if (ret == -1) {
304 goto error;
305 }
Michal Vasko36c7be82017-02-22 13:37:59 +0100306 ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100307 if (ret == -1) {
308 goto error;
309 }
310
311 if (!strcmp(chunk, "#\n")) {
312 /* end of chunked framing message */
313 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200314 if (!msg) {
315 ERR("Session %u: invalid frame chunk delimiters.", session->id);
316 goto malformed_msg;
317 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100318 break;
319 }
320
321 /* convert string to the size of the following chunk */
322 chunk_len = strtoul(chunk, (char **)NULL, 10);
323 free(chunk);
324 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100325 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100326 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327 }
328
329 /* now we have size of next chunk, so read the chunk */
Michal Vasko36c7be82017-02-22 13:37:59 +0100330 ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100331 if (ret == -1) {
332 goto error;
333 }
334
335 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko4eb3c312016-03-01 14:09:37 +0100336 msg = realloc(msg, len + chunk_len + 1);
337 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100338 ERRMEM;
339 goto error;
340 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100341 memcpy(msg + len, chunk, chunk_len);
342 len += chunk_len;
343 msg[len] = '\0';
344 free(chunk);
345 }
346
347 break;
348 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200349 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350
351 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100352 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100353 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100354 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100355 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100356 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100357 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100358 }
359 free(msg);
360 msg = NULL;
361
362 /* get and return message type */
363 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
364 if (!strcmp((*data)->name, "rpc")) {
365 return NC_MSG_RPC;
366 } else if (!strcmp((*data)->name, "rpc-reply")) {
367 return NC_MSG_REPLY;
368 } else if (!strcmp((*data)->name, "hello")) {
369 return NC_MSG_HELLO;
370 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100371 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100372 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100373 }
374 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
375 if (!strcmp((*data)->name, "notification")) {
376 return NC_MSG_NOTIF;
377 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100378 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100379 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100380 }
381 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100382 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100383 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 }
385
Michal Vasko428087d2016-01-14 16:04:28 +0100386malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100387 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100388 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
389 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100390 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100391
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100392 if (nc_write_msg(session, NC_MSG_REPLY, NULL, reply) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100393 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100394 if (session->status != NC_STATUS_INVALID) {
395 session->status = NC_STATUS_INVALID;
396 session->term_reason = NC_SESSION_TERM_OTHER;
397 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100398 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100399 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100400 }
401
Michal Vasko428087d2016-01-14 16:04:28 +0100402error:
403 /* cleanup */
404 free(msg);
405 free(*data);
406 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100407
408 return NC_MSG_ERROR;
409}
410
Michal Vasko428087d2016-01-14 16:04:28 +0100411/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
412static int
413nc_read_poll(struct nc_session *session, int timeout)
414{
Radek Krejci5961c702016-07-15 09:15:18 +0200415 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100416 int ret = -2;
417 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100418
419 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100420 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100421 return -1;
422 }
423
424 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100425#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100426 case NC_TI_LIBSSH:
427 /* EINTR is handled, it resumes waiting */
428 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
429 if (ret == SSH_ERROR) {
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100430 ERR("Session %u: SSH channel poll error (%s).", session->id,
Michal Vasko051d35b2016-02-03 15:28:37 +0100431 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100432 session->status = NC_STATUS_INVALID;
433 session->term_reason = NC_SESSION_TERM_OTHER;
434 return -1;
435 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100436 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100437 session->status = NC_STATUS_INVALID;
438 session->term_reason = NC_SESSION_TERM_DROPPED;
439 return -1;
440 } else if (ret > 0) {
441 /* fake it */
442 ret = 1;
443 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100444 } else { /* ret == 0 */
445 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100446 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100447 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100448#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100449#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100450 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100451 ret = SSL_pending(session->ti.tls);
452 if (ret) {
453 /* some buffered TLS data available */
454 ret = 1;
455 fds.revents = POLLIN;
456 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100457 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100458
459 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100460 /* fallthrough */
461#endif
462 case NC_TI_FD:
463 if (session->ti_type == NC_TI_FD) {
464 fds.fd = session->ti.fd.in;
465 }
466
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100467 fds.events = POLLIN;
468 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100469
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100470 sigfillset(&sigmask);
471 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
472 ret = poll(&fds, 1, timeout);
473 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100474
475 break;
476
477 default:
478 ERRINT;
479 return -1;
480 }
481
482 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
483 if (ret < 0) {
484 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200485 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100486 session->status = NC_STATUS_INVALID;
487 session->term_reason = NC_SESSION_TERM_OTHER;
488 return -1;
489 } else { /* status > 0 */
490 /* in case of standard (non-libssh) poll, there still can be an error */
491 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100492 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100493 session->status = NC_STATUS_INVALID;
494 session->term_reason = NC_SESSION_TERM_DROPPED;
495 return -1;
496 }
497 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100498 ERR("Session %u: communication channel error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100499 session->status = NC_STATUS_INVALID;
500 session->term_reason = NC_SESSION_TERM_OTHER;
501 return -1;
502 }
503 }
504
505 return ret;
506}
507
508/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100509NC_MSG_TYPE
510nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200511{
Michal Vasko428087d2016-01-14 16:04:28 +0100512 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200513
514 assert(data);
515 *data = NULL;
516
Michal Vasko428087d2016-01-14 16:04:28 +0100517 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100518 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100519 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200520 }
521
Michal Vasko428087d2016-01-14 16:04:28 +0100522 ret = nc_read_poll(session, timeout);
523 if (ret == 0) {
524 /* timed out */
525 return NC_MSG_WOULDBLOCK;
526 } else if (ret < 0) {
527 /* poll error, error written */
528 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200529 }
530
Michal Vasko05ba9df2016-01-13 14:40:27 +0100531 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200532}
Radek Krejcife0b3472015-10-12 13:43:42 +0200533
Michal Vasko428087d2016-01-14 16:04:28 +0100534/* does not really log, only fatal errors */
535int
536nc_session_is_connected(struct nc_session *session)
537{
538 int ret;
539 struct pollfd fds;
540
541 switch (session->ti_type) {
542 case NC_TI_FD:
543 fds.fd = session->ti.fd.in;
544 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100545#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100546 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100547 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100548#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100549#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100550 case NC_TI_OPENSSL:
551 fds.fd = SSL_get_fd(session->ti.tls);
552 break;
553#endif
554 case NC_TI_NONE:
555 ERRINT;
556 return 0;
557 }
558
Michal Vasko840a8a62017-02-07 10:56:34 +0100559 if (fds.fd == -1) {
560 return 0;
561 }
562
Michal Vasko428087d2016-01-14 16:04:28 +0100563 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100564 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100565
566 errno = 0;
567 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
568
569 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100570 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100571 return 0;
572 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
573 return 0;
574 }
575
576 return 1;
577}
578
Radek Krejcife0b3472015-10-12 13:43:42 +0200579#define WRITE_BUFSIZE (2 * BUFFERSIZE)
580struct wclb_arg {
581 struct nc_session *session;
582 char buf[WRITE_BUFSIZE];
583 size_t len;
584};
585
Michal Vasko964e1732016-09-23 13:39:33 +0200586static int
Michal Vasko428087d2016-01-14 16:04:28 +0100587nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200588{
Michal Vasko964e1732016-09-23 13:39:33 +0200589 int c;
590 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200591#ifdef NC_ENABLED_TLS
592 unsigned long e;
593#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200594
Michal Vasko428087d2016-01-14 16:04:28 +0100595 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
596 return -1;
597 }
598
599 /* prevent SIGPIPE this way */
600 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100601 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100602 session->status = NC_STATUS_INVALID;
603 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100604 return -1;
605 }
606
Michal Vasko81b33fb2016-09-26 14:57:36 +0200607 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200608
Michal Vasko81b33fb2016-09-26 14:57:36 +0200609 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200610 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200611 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200612 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200613 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200614 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200615 return -1;
616 }
617 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200618
Radek Krejci53691be2016-02-22 13:58:37 +0100619#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200620 case NC_TI_LIBSSH:
621 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
622 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
623 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
624 } else {
625 ERR("Session %u: SSH channel unexpected EOF.", session->id);
626 }
627 session->status = NC_STATUS_INVALID;
628 session->term_reason = NC_SESSION_TERM_DROPPED;
629 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100630 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200631 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200632 if ((c == SSH_ERROR) || (c == -1)) {
633 ERR("Session %u: SSH channel write failed.", session->id);
634 return -1;
635 }
636 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200637#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100638#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200639 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200640 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200641 if (c < 1) {
642 switch ((e = SSL_get_error(session->ti.tls, c))) {
643 case SSL_ERROR_ZERO_RETURN:
644 ERR("Session %u: SSL connection was properly closed.", session->id);
645 return -1;
646 case SSL_ERROR_WANT_WRITE:
647 c = 0;
648 break;
649 case SSL_ERROR_SYSCALL:
650 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
651 return -1;
652 case SSL_ERROR_SSL:
653 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
654 return -1;
655 default:
656 ERR("Session %u: unknown SSL error occured.", session->id);
657 return -1;
658 }
659 }
660 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200661#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200662 default:
663 ERRINT;
664 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200665 }
666
667 if (c == 0) {
668 /* we must wait */
669 usleep(NC_TIMEOUT_STEP);
670 }
671
672 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200673 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200674
Michal Vasko964e1732016-09-23 13:39:33 +0200675 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200676}
677
Michal Vasko428087d2016-01-14 16:04:28 +0100678static int
679nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100680{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100681 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100682 char chunksize[20];
683
684 if (session->version == NC_VERSION_11) {
685 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100686 ret = nc_write(session, chunksize, strlen(chunksize));
687 if (ret == -1) {
688 return -1;
689 }
Michal Vasko086311b2016-01-08 09:53:11 +0100690 }
Michal Vasko428087d2016-01-14 16:04:28 +0100691
692 c = nc_write(session, buf, count);
693 if (c == -1) {
694 return -1;
695 }
696 ret += c;
697
698 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100699}
700
Radek Krejcife0b3472015-10-12 13:43:42 +0200701static int
Michal Vasko428087d2016-01-14 16:04:28 +0100702nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200703{
Michal Vasko428087d2016-01-14 16:04:28 +0100704 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100705
Michal Vasko428087d2016-01-14 16:04:28 +0100706 if (session->version == NC_VERSION_11) {
707 ret = nc_write(session, "\n##\n", 4);
708 } else {
709 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200710 }
711
Michal Vasko428087d2016-01-14 16:04:28 +0100712 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200713}
714
Michal Vasko428087d2016-01-14 16:04:28 +0100715static int
716nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200717{
Michal Vasko428087d2016-01-14 16:04:28 +0100718 int ret = 0;
719
Radek Krejcife0b3472015-10-12 13:43:42 +0200720 /* flush current buffer */
721 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100722 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200723 warg->len = 0;
724 }
Michal Vasko428087d2016-01-14 16:04:28 +0100725
726 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200727}
728
729static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100730nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200731{
Michal Vasko428087d2016-01-14 16:04:28 +0100732 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100733 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200734 struct wclb_arg *warg = (struct wclb_arg *)arg;
735
736 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100737 c = nc_write_clb_flush(warg);
738 if (c == -1) {
739 return -1;
740 }
741 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200742
743 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100744 c = nc_write_endtag(warg->session);
745 if (c == -1) {
746 return -1;
747 }
748 ret += c;
749
750 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200751 }
752
753 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
754 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100755 c = nc_write_clb_flush(warg);
756 if (c == -1) {
757 return -1;
758 }
759 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200760 }
Michal Vasko428087d2016-01-14 16:04:28 +0100761
Radek Krejci047300e2016-03-08 16:46:58 +0100762 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200763 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100764 c = nc_write_starttag_and_msg(warg->session, buf, count);
765 if (c == -1) {
766 return -1;
767 }
768 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200769 } else {
770 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100771 if (xmlcontent) {
772 for (l = 0; l < count; l++) {
773 if (warg->len + 5 >= WRITE_BUFSIZE) {
774 /* buffer is full */
775 c = nc_write_clb_flush(warg);
776 if (c == -1) {
777 return -1;
778 }
779 }
780
781 switch (((char *)buf)[l]) {
782 case '&':
783 ret += 5;
784 memcpy(&warg->buf[warg->len], "&amp;", 5);
785 warg->len += 5;
786 break;
787 case '<':
788 ret += 4;
789 memcpy(&warg->buf[warg->len], "&lt;", 4);
790 warg->len += 4;
791 break;
792 case '>':
793 /* not needed, just for readability */
794 ret += 4;
795 memcpy(&warg->buf[warg->len], "&gt;", 4);
796 warg->len += 4;
797 break;
798 default:
799 ret++;
800 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
801 warg->len++;
802 }
803 }
804 } else {
805 memcpy(&warg->buf[warg->len], buf, count);
806 warg->len += count; /* is <= WRITE_BUFSIZE */
807 ret += count;
808 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200809 }
810
Michal Vasko428087d2016-01-14 16:04:28 +0100811 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200812}
813
Radek Krejci047300e2016-03-08 16:46:58 +0100814static ssize_t
815nc_write_xmlclb(void *arg, const void *buf, size_t count)
816{
817 return nc_write_clb(arg, buf, count, 0);
818}
819
Michal Vasko05ba9df2016-01-13 14:40:27 +0100820static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100821nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
822 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100823{
Michal Vasko08611b32016-12-05 13:30:37 +0100824 if (open) {
825 nc_write_clb((void *)arg, "<", 1, 0);
826 } else {
827 nc_write_clb((void *)arg, "</", 2, 0);
828 }
829
830 if (prefix) {
831 nc_write_clb((void *)arg, prefix, pref_len, 0);
832 nc_write_clb((void *)arg, ":", 1, 0);
833 }
834
835 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100836 if (!open || !no_attr) {
837 nc_write_clb((void *)arg, ">", 1, 0);
838 }
Michal Vasko08611b32016-12-05 13:30:37 +0100839}
840
841static void
842nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
843{
Michal Vasko3e9d1682017-02-24 09:50:15 +0100844 uint16_t i, pref_len = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100845 char str_sid[11];
846
Michal Vasko08611b32016-12-05 13:30:37 +0100847 if (prefix) {
848 pref_len = strlen(prefix);
849 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100850
Michal Vasko52bd9492016-12-08 09:37:43 +0100851 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100852
Michal Vasko52bd9492016-12-08 09:37:43 +0100853 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100854 switch (err->type) {
855 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100856 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100857 break;
858 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100859 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100860 break;
861 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100862 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100863 break;
864 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100865 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100866 break;
867 default:
868 ERRINT;
869 return;
870 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100871
Michal Vasko52bd9492016-12-08 09:37:43 +0100872 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100873
Michal Vasko52bd9492016-12-08 09:37:43 +0100874 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100875 switch (err->tag) {
876 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100877 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100878 break;
879 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100880 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100881 break;
882 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100883 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100884 break;
885 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100886 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100887 break;
888 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100889 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100890 break;
891 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100892 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100893 break;
894 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100895 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100896 break;
897 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100898 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100899 break;
900 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100901 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100902 break;
903 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100904 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100905 break;
906 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100907 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908 break;
909 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100910 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 break;
912 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100913 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100914 break;
915 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100916 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100917 break;
918 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100919 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100920 break;
921 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100922 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100923 break;
924 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100925 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100926 break;
927 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100928 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100929 break;
930 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100931 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100932 break;
933 default:
934 ERRINT;
935 return;
936 }
Michal Vasko52bd9492016-12-08 09:37:43 +0100937 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100938
Michal Vasko52bd9492016-12-08 09:37:43 +0100939 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100940 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100941 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100942
943 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100944 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100945 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100946 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100947 }
948
949 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100950 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100951 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100952 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100953 }
954
955 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100956 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100957 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +0100958 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
959 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
960 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100961 }
Radek Krejci047300e2016-03-08 16:46:58 +0100962 nc_write_clb((void *)arg, ">", 1, 0);
963 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100964 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100965 }
966
Michal Vasko90920b02016-05-20 14:07:00 +0200967 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100968 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100969
Michal Vasko90920b02016-05-20 14:07:00 +0200970 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100971 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +0200972 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +0100973 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100974 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100975 }
976
977 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100978 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100979 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100980 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100981 }
982
983 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100984 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100985 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100986 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100987 }
988
989 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100990 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100991 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100992 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100993 }
994
995 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100996 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100997 }
998
Michal Vasko52bd9492016-12-08 09:37:43 +0100999 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001000 }
1001
Michal Vasko52bd9492016-12-08 09:37:43 +01001002 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001003}
1004
Michal Vasko428087d2016-01-14 16:04:28 +01001005/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +01001006int
Radek Krejci127f8952016-10-12 14:57:16 +02001007nc_write_msg(struct nc_session *session, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +02001008{
Radek Krejcid116db42016-01-08 15:36:30 +01001009 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001010 int count;
Michal Vasko08611b32016-12-05 13:30:37 +01001011 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +02001012 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001013 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +02001014 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001015 struct nc_server_reply *reply;
1016 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +01001017 char *buf = NULL;
1018 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001019 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001020 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001021 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001022
Michal Vasko428087d2016-01-14 16:04:28 +01001023 assert(session);
1024
1025 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001026 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +01001027 return -1;
1028 }
1029
Radek Krejcid116db42016-01-08 15:36:30 +01001030 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +02001031
1032 arg.session = session;
1033 arg.len = 0;
1034
Radek Krejci127f8952016-10-12 14:57:16 +02001035
Radek Krejcife0b3472015-10-12 13:43:42 +02001036 switch (type) {
1037 case NC_MSG_RPC:
1038 content = va_arg(ap, struct lyd_node *);
1039 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001040
Radek Krejcife0b3472015-10-12 13:43:42 +02001041 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001042 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001043 if (count == -1) {
1044 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001045 va_end(ap);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001046 return -1;
1047 }
Radek Krejci047300e2016-03-08 16:46:58 +01001048 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001049 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001050
Michal Vaskoe6e8b852016-10-20 14:07:29 +02001051 lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF);
Radek Krejci047300e2016-03-08 16:46:58 +01001052 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001053
Michal Vasko2e6defd2016-10-07 15:48:15 +02001054 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001055 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001056
Radek Krejcife0b3472015-10-12 13:43:42 +02001057 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001058 rpc_elem = va_arg(ap, struct lyxml_elem *);
1059 reply = va_arg(ap, struct nc_server_reply *);
1060
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001061 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1062 nc_write_clb((void *)&arg, "<", 1, 0);
1063 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1064 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001065 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001066 }
1067 else {
1068 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001069 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001070 }
1071
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001072 /* can be NULL if replying with a malformed-message error */
1073 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001074 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001075 nc_write_clb((void *)&arg, ">", 1, 0);
1076 } else {
1077 /* but put there at least the correct namespace */
1078 nc_write_clb((void *)&arg, "xmlns=\""NC_NS_BASE"\">", 48, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001079 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001080 switch (reply->type) {
1081 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001082 nc_write_clb((void *)&arg, "<", 1, 0);
1083 if (base_prefix) {
1084 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1085 nc_write_clb((void *)&arg, ":", 1, 0);
1086 }
1087 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001088 break;
1089 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001090 assert(((struct nc_server_reply_data *)reply)->data->schema->nodetype == LYS_RPC);
1091 switch(((struct nc_server_reply_data *)reply)->wd) {
1092 case NC_WD_UNKNOWN:
1093 case NC_WD_EXPLICIT:
1094 wd = LYP_WD_EXPLICIT;
1095 break;
1096 case NC_WD_TRIM:
1097 wd = LYP_WD_TRIM;
1098 break;
1099 case NC_WD_ALL:
1100 wd = LYP_WD_ALL;
1101 break;
1102 case NC_WD_ALL_TAG:
1103 wd = LYP_WD_ALL_TAG;
1104 break;
1105 }
Michal Vaskoe1708602016-10-18 12:17:22 +02001106 lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
Michal Vaskoe6e8b852016-10-20 14:07:29 +02001107 LYP_WITHSIBLINGS | LYP_NETCONF | wd);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001108 break;
1109 case NC_RPL_ERROR:
1110 error_rpl = (struct nc_server_reply_error *)reply;
1111 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001112 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001113 }
1114 break;
1115 default:
1116 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001117 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001118 va_end(ap);
1119 return -1;
1120 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001121 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1122 nc_write_clb((void *)&arg, "</", 2, 0);
1123 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1124 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1125 }
1126 else {
1127 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1128 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001129 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001130
Radek Krejcife0b3472015-10-12 13:43:42 +02001131 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001132 notif = va_arg(ap, struct nc_server_notif *);
1133
1134 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1135 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1136 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1137 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
1138 lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0);
mohitarora24878b2962016-11-09 18:45:33 -05001139 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001140 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001141
Radek Krejcid116db42016-01-08 15:36:30 +01001142 case NC_MSG_HELLO:
1143 if (session->version != NC_VERSION_10) {
1144 va_end(ap);
1145 return -1;
1146 }
1147 capabilities = va_arg(ap, const char **);
1148 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001149
Radek Krejcid116db42016-01-08 15:36:30 +01001150 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001151 if (count == -1) {
1152 ERRMEM;
1153 va_end(ap);
1154 return -1;
1155 }
Radek Krejci047300e2016-03-08 16:46:58 +01001156 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001157 free(buf);
1158 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001159 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1160 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1161 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001162 }
1163 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001164 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001165 if (count == -1) {
1166 ERRMEM;
1167 va_end(ap);
1168 return -1;
1169 }
Radek Krejci047300e2016-03-08 16:46:58 +01001170 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001171 free(buf);
1172 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001173 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001174 }
Radek Krejcid116db42016-01-08 15:36:30 +01001175 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001176
Radek Krejcife0b3472015-10-12 13:43:42 +02001177 default:
Radek Krejcid116db42016-01-08 15:36:30 +01001178 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +02001179 return -1;
1180 }
1181
1182 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001183 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001184
1185 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +01001186 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1187 /* error was already written */
1188 return -1;
1189 }
1190
Radek Krejcid116db42016-01-08 15:36:30 +01001191 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001192}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001193
1194void *
1195nc_realloc(void *ptr, size_t size)
1196{
1197 void *ret;
1198
1199 ret = realloc(ptr, size);
1200 if (!ret) {
1201 free(ptr);
1202 }
1203
1204 return ret;
1205}