blob: 74a1eef517113b47475b3aad1ef6606077fb9134 [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>
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 Vasko6b7c42e2016-03-02 15:46:41 +010038nc_read(struct nc_session *session, char *buf, size_t count, uint16_t *read_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 Vasko6b7c42e2016-03-02 15:46:41 +010042 uint16_t sleep_count = 0;
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 Vasko81b33fb2016-09-26 14:57:36 +020055 do {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010056 switch (session->ti_type) {
57 case NC_TI_NONE:
58 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +010059
Michal Vasko6b7c42e2016-03-02 15:46:41 +010060 case NC_TI_FD:
61 /* read via standard file descriptor */
Michal Vasko81b33fb2016-09-26 14:57:36 +020062 r = read(session->ti.fd.in, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +020063 if (r < 0) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +010064 if (errno == EAGAIN) {
65 r = 0;
66 break;
67 } else if (errno == EINTR) {
68 usleep(NC_TIMEOUT_STEP);
Radek Krejci206fcd62015-10-07 15:42:48 +020069 continue;
70 } 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
137 /* nothing read */
138 if (r == 0) {
139 usleep(NC_TIMEOUT_STEP);
140 ++sleep_count;
141 if (1000000 / NC_TIMEOUT_STEP == sleep_count) {
142 /* we slept a full second */
143 --(*read_timeout);
144 sleep_count = 0;
145 }
146 if (!*read_timeout) {
147 ERR("Session %u: reading a full NETCONF message timeout elapsed.", session->id);
148 session->status = NC_STATUS_INVALID;
149 session->term_reason = NC_SESSION_TERM_OTHER;
150 return -1;
151 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100152 }
153
Michal Vasko81b33fb2016-09-26 14:57:36 +0200154 readd += r;
155 } while (readd < count);
156 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200157
Michal Vasko81b33fb2016-09-26 14:57:36 +0200158 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200159}
160
161static ssize_t
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100162nc_read_chunk(struct nc_session *session, size_t len, uint16_t *read_timeout, char **chunk)
Radek Krejci206fcd62015-10-07 15:42:48 +0200163{
164 ssize_t r;
165
166 assert(session);
167 assert(chunk);
168
169 if (!len) {
170 return 0;
171 }
172
Michal Vasko4eb3c312016-03-01 14:09:37 +0100173 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200174 if (!*chunk) {
175 ERRMEM;
176 return -1;
177 }
178
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100179 r = nc_read(session, *chunk, len, read_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200180 if (r <= 0) {
181 free(*chunk);
182 return -1;
183 }
184
185 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200186 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200187
188 return r;
189}
190
191static ssize_t
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100192nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint16_t *read_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200193{
194 char *chunk = NULL;
195 size_t size, count = 0, r, len;
196
197 assert(session);
198 assert(endtag);
199
200 if (limit && limit < BUFFERSIZE) {
201 size = limit;
202 } else {
203 size = BUFFERSIZE;
204 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100205 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200206 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200207 ERRMEM;
208 return -1;
209 }
210
211 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100212 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200213 if (limit && count == limit) {
214 free(chunk);
Michal Vaskod083db62016-01-19 10:31:29 +0100215 WRN("Session %u: reading limit (%d) reached.", session->id, limit);
216 ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200217 return -1;
218 }
219
220 /* resize buffer if needed */
221 if (count == size) {
222 /* get more memory */
223 size = size + BUFFERSIZE;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100224 chunk = realloc(chunk, (size + 1) * sizeof *chunk);
225 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200226 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200227 return -1;
228 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200229 }
230
231 /* get another character */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100232 r = nc_read(session, &(chunk[count]), 1, read_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200233 if (r != 1) {
234 free(chunk);
235 return -1;
236 }
237
238 count++;
239
240 /* check endtag */
241 if (count >= len) {
242 if (!strncmp(endtag, &(chunk[count - len]), len)) {
243 /* endtag found */
244 break;
245 }
246 }
247 }
248
249 /* terminating null byte */
250 chunk[count] = 0;
251
252 if (result) {
253 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200254 } else {
255 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200256 }
257 return count;
258}
259
Michal Vasko428087d2016-01-14 16:04:28 +0100260/* return NC_MSG_ERROR can change session status */
Radek Krejci206fcd62015-10-07 15:42:48 +0200261NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100262nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
263{
264 int ret;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100265 char *msg = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100266 uint64_t chunk_len, len = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100267 uint16_t read_timeout = NC_READ_TIMEOUT;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100268 struct nc_server_reply *reply;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100269
Michal Vasko428087d2016-01-14 16:04:28 +0100270 assert(session && data);
271 *data = NULL;
272
273 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100274 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100275 return NC_MSG_ERROR;
276 }
277
Michal Vasko05ba9df2016-01-13 14:40:27 +0100278 /* read the message */
279 switch (session->version) {
280 case NC_VERSION_10:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100281 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &read_timeout, &msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100282 if (ret == -1) {
283 goto error;
284 }
285
286 /* cut off the end tag */
287 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
288 break;
289 case NC_VERSION_11:
290 while (1) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100291 ret = nc_read_until(session, "\n#", 0, &read_timeout, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100292 if (ret == -1) {
293 goto error;
294 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100295 ret = nc_read_until(session, "\n", 0, &read_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100296 if (ret == -1) {
297 goto error;
298 }
299
300 if (!strcmp(chunk, "#\n")) {
301 /* end of chunked framing message */
302 free(chunk);
Michal Vasko79df3262016-07-13 13:42:17 +0200303 if (!msg) {
304 ERR("Session %u: invalid frame chunk delimiters.", session->id);
305 goto malformed_msg;
306 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100307 break;
308 }
309
310 /* convert string to the size of the following chunk */
311 chunk_len = strtoul(chunk, (char **)NULL, 10);
312 free(chunk);
313 if (!chunk_len) {
Michal Vaskod083db62016-01-19 10:31:29 +0100314 ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100315 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100316 }
317
318 /* now we have size of next chunk, so read the chunk */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100319 ret = nc_read_chunk(session, chunk_len, &read_timeout, &chunk);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100320 if (ret == -1) {
321 goto error;
322 }
323
324 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko4eb3c312016-03-01 14:09:37 +0100325 msg = realloc(msg, len + chunk_len + 1);
326 if (!msg) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327 ERRMEM;
328 goto error;
329 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100330 memcpy(msg + len, chunk, chunk_len);
331 len += chunk_len;
332 msg[len] = '\0';
333 free(chunk);
334 }
335
336 break;
337 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200338 DBG("Session %u: received message:\n%s\n", session->id, msg);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100339
340 /* build XML tree */
Michal Vaskoa4c23d82016-02-03 15:48:09 +0100341 *data = lyxml_parse_mem(session->ctx, msg, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100342 if (!*data) {
Michal Vasko428087d2016-01-14 16:04:28 +0100343 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100344 } else if (!(*data)->ns) {
Michal Vaskod083db62016-01-19 10:31:29 +0100345 ERR("Session %u: invalid message root element (invalid namespace).", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100346 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100347 }
348 free(msg);
349 msg = NULL;
350
351 /* get and return message type */
352 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
353 if (!strcmp((*data)->name, "rpc")) {
354 return NC_MSG_RPC;
355 } else if (!strcmp((*data)->name, "rpc-reply")) {
356 return NC_MSG_REPLY;
357 } else if (!strcmp((*data)->name, "hello")) {
358 return NC_MSG_HELLO;
359 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100360 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100361 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100362 }
363 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
364 if (!strcmp((*data)->name, "notification")) {
365 return NC_MSG_NOTIF;
366 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100367 ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
Michal Vasko428087d2016-01-14 16:04:28 +0100368 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100369 }
370 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100371 ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
Michal Vasko428087d2016-01-14 16:04:28 +0100372 goto malformed_msg;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100373 }
374
Michal Vasko428087d2016-01-14 16:04:28 +0100375malformed_msg:
Michal Vaskod083db62016-01-19 10:31:29 +0100376 ERR("Session %u: malformed message received.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100377 if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
378 /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100379 reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));
Michal Vasko428087d2016-01-14 16:04:28 +0100380
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100381 if (nc_write_msg(session, NC_MSG_REPLY, NULL, reply) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100382 ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100383 if (session->status != NC_STATUS_INVALID) {
384 session->status = NC_STATUS_INVALID;
385 session->term_reason = NC_SESSION_TERM_OTHER;
386 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100387 }
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100388 nc_server_reply_free(reply);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389 }
390
Michal Vasko428087d2016-01-14 16:04:28 +0100391error:
392 /* cleanup */
393 free(msg);
394 free(*data);
395 *data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100396
397 return NC_MSG_ERROR;
398}
399
Michal Vasko428087d2016-01-14 16:04:28 +0100400/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
401static int
402nc_read_poll(struct nc_session *session, int timeout)
403{
Radek Krejci5961c702016-07-15 09:15:18 +0200404 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100405 int ret = -2;
406 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100407
408 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100409 ERR("Session %u: invalid session to poll.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100410 return -1;
411 }
412
413 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100414#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100415 case NC_TI_LIBSSH:
416 /* EINTR is handled, it resumes waiting */
417 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
418 if (ret == SSH_ERROR) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100419 ERR("Session %u: polling on the SSH channel failed (%s).", session->id,
420 ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100421 session->status = NC_STATUS_INVALID;
422 session->term_reason = NC_SESSION_TERM_OTHER;
423 return -1;
424 } else if (ret == SSH_EOF) {
Michal Vasko051d35b2016-02-03 15:28:37 +0100425 ERR("Session %u: SSH channel unexpected EOF.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100426 session->status = NC_STATUS_INVALID;
427 session->term_reason = NC_SESSION_TERM_DROPPED;
428 return -1;
429 } else if (ret > 0) {
430 /* fake it */
431 ret = 1;
432 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100433 } else { /* ret == 0 */
434 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100435 }
436 /* fallthrough */
437#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100438#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100439 case NC_TI_OPENSSL:
440 if (session->ti_type == NC_TI_OPENSSL) {
441 fds.fd = SSL_get_fd(session->ti.tls);
442 }
443 /* fallthrough */
444#endif
445 case NC_TI_FD:
446 if (session->ti_type == NC_TI_FD) {
447 fds.fd = session->ti.fd.in;
448 }
449
450 /* poll only if it is not an SSH session */
451 if (ret == -2) {
452 fds.events = POLLIN;
Michal Vasko11d142a2016-01-19 15:58:24 +0100453 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100454
Michal Vasko11d142a2016-01-19 15:58:24 +0100455 sigfillset(&sigmask);
Radek Krejci5961c702016-07-15 09:15:18 +0200456 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
457 ret = poll(&fds, 1, timeout);
458 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100459 }
460
461 break;
462
463 default:
464 ERRINT;
465 return -1;
466 }
467
468 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
469 if (ret < 0) {
470 /* poll failed - something really bad happened, close the session */
Radek Krejci5961c702016-07-15 09:15:18 +0200471 ERR("Session %u: poll error (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100472 session->status = NC_STATUS_INVALID;
473 session->term_reason = NC_SESSION_TERM_OTHER;
474 return -1;
475 } else { /* status > 0 */
476 /* in case of standard (non-libssh) poll, there still can be an error */
477 if (fds.revents & POLLHUP) {
Michal Vaskod083db62016-01-19 10:31:29 +0100478 ERR("Session %u: communication channel unexpectedly closed.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100479 session->status = NC_STATUS_INVALID;
480 session->term_reason = NC_SESSION_TERM_DROPPED;
481 return -1;
482 }
483 if (fds.revents & POLLERR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100484 ERR("Session %u: communication channel error.", session->id);
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 }
489 }
490
491 return ret;
492}
493
494/* return NC_MSG_ERROR can change session status */
Michal Vasko05ba9df2016-01-13 14:40:27 +0100495NC_MSG_TYPE
496nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200497{
Michal Vasko428087d2016-01-14 16:04:28 +0100498 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200499
500 assert(data);
501 *data = NULL;
502
Michal Vasko428087d2016-01-14 16:04:28 +0100503 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100504 ERR("Session %u: invalid session to read from.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100505 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200506 }
507
Michal Vasko428087d2016-01-14 16:04:28 +0100508 ret = nc_read_poll(session, timeout);
509 if (ret == 0) {
510 /* timed out */
511 return NC_MSG_WOULDBLOCK;
512 } else if (ret < 0) {
513 /* poll error, error written */
514 return NC_MSG_ERROR;
Radek Krejci206fcd62015-10-07 15:42:48 +0200515 }
516
Michal Vasko05ba9df2016-01-13 14:40:27 +0100517 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200518}
Radek Krejcife0b3472015-10-12 13:43:42 +0200519
Michal Vasko428087d2016-01-14 16:04:28 +0100520/* does not really log, only fatal errors */
521int
522nc_session_is_connected(struct nc_session *session)
523{
524 int ret;
525 struct pollfd fds;
526
527 switch (session->ti_type) {
528 case NC_TI_FD:
529 fds.fd = session->ti.fd.in;
530 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100531#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100532 case NC_TI_LIBSSH:
533 fds.fd = ssh_get_fd(session->ti.libssh.session);
534 break;
535#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100536#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100537 case NC_TI_OPENSSL:
538 fds.fd = SSL_get_fd(session->ti.tls);
539 break;
540#endif
541 case NC_TI_NONE:
542 ERRINT;
543 return 0;
544 }
545
546 fds.events = POLLIN;
547
548 errno = 0;
549 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR));
550
551 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100552 ERR("Session %u: poll failed (%s).", session->id, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100553 return 0;
554 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
555 return 0;
556 }
557
558 return 1;
559}
560
Radek Krejcife0b3472015-10-12 13:43:42 +0200561#define WRITE_BUFSIZE (2 * BUFFERSIZE)
562struct wclb_arg {
563 struct nc_session *session;
564 char buf[WRITE_BUFSIZE];
565 size_t len;
566};
567
Michal Vasko964e1732016-09-23 13:39:33 +0200568static int
Michal Vasko428087d2016-01-14 16:04:28 +0100569nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200570{
Michal Vasko964e1732016-09-23 13:39:33 +0200571 int c;
572 size_t written = 0;
Michal Vaskoe2357e92016-10-05 14:20:47 +0200573#ifdef NC_ENABLED_TLS
574 unsigned long e;
575#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200576
Michal Vasko428087d2016-01-14 16:04:28 +0100577 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
578 return -1;
579 }
580
581 /* prevent SIGPIPE this way */
582 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100583 ERR("Session %u: communication socket unexpectedly closed.", session->id);
Michal Vasko2a7d4732016-01-15 09:24:46 +0100584 session->status = NC_STATUS_INVALID;
585 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100586 return -1;
587 }
588
Michal Vasko81b33fb2016-09-26 14:57:36 +0200589 DBG("Session %u: sending message:\n%.*s\n", session->id, count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200590
Michal Vasko81b33fb2016-09-26 14:57:36 +0200591 do {
Michal Vasko964e1732016-09-23 13:39:33 +0200592 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200593 case NC_TI_FD:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200594 c = write(session->ti.fd.out, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200595 if (c < 0) {
Michal Vaskoe2146a32016-09-23 14:20:36 +0200596 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200597 return -1;
598 }
599 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200600
Radek Krejci53691be2016-02-22 13:58:37 +0100601#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200602 case NC_TI_LIBSSH:
603 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
604 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
605 ERR("Session %u: SSH channel unexpectedly closed.", session->id);
606 } else {
607 ERR("Session %u: SSH channel unexpected EOF.", session->id);
608 }
609 session->status = NC_STATUS_INVALID;
610 session->term_reason = NC_SESSION_TERM_DROPPED;
611 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100612 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200613 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200614 if ((c == SSH_ERROR) || (c == -1)) {
615 ERR("Session %u: SSH channel write failed.", session->id);
616 return -1;
617 }
618 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200619#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100620#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200621 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200622 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200623 if (c < 1) {
624 switch ((e = SSL_get_error(session->ti.tls, c))) {
625 case SSL_ERROR_ZERO_RETURN:
626 ERR("Session %u: SSL connection was properly closed.", session->id);
627 return -1;
628 case SSL_ERROR_WANT_WRITE:
629 c = 0;
630 break;
631 case SSL_ERROR_SYSCALL:
632 ERR("Session %u: SSL socket error (%s).", session->id, strerror(errno));
633 return -1;
634 case SSL_ERROR_SSL:
635 ERR("Session %u: SSL error (%s).", session->id, ERR_reason_error_string(e));
636 return -1;
637 default:
638 ERR("Session %u: unknown SSL error occured.", session->id);
639 return -1;
640 }
641 }
642 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200643#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200644 default:
645 ERRINT;
646 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200647 }
648
649 if (c == 0) {
650 /* we must wait */
651 usleep(NC_TIMEOUT_STEP);
652 }
653
654 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200655 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200656
Michal Vasko964e1732016-09-23 13:39:33 +0200657 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200658}
659
Michal Vasko428087d2016-01-14 16:04:28 +0100660static int
661nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100662{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100663 int ret = 0, c;
Michal Vasko086311b2016-01-08 09:53:11 +0100664 char chunksize[20];
665
666 if (session->version == NC_VERSION_11) {
667 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100668 ret = nc_write(session, chunksize, strlen(chunksize));
669 if (ret == -1) {
670 return -1;
671 }
Michal Vasko086311b2016-01-08 09:53:11 +0100672 }
Michal Vasko428087d2016-01-14 16:04:28 +0100673
674 c = nc_write(session, buf, count);
675 if (c == -1) {
676 return -1;
677 }
678 ret += c;
679
680 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100681}
682
Radek Krejcife0b3472015-10-12 13:43:42 +0200683static int
Michal Vasko428087d2016-01-14 16:04:28 +0100684nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200685{
Michal Vasko428087d2016-01-14 16:04:28 +0100686 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100687
Michal Vasko428087d2016-01-14 16:04:28 +0100688 if (session->version == NC_VERSION_11) {
689 ret = nc_write(session, "\n##\n", 4);
690 } else {
691 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200692 }
693
Michal Vasko428087d2016-01-14 16:04:28 +0100694 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200695}
696
Michal Vasko428087d2016-01-14 16:04:28 +0100697static int
698nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200699{
Michal Vasko428087d2016-01-14 16:04:28 +0100700 int ret = 0;
701
Radek Krejcife0b3472015-10-12 13:43:42 +0200702 /* flush current buffer */
703 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100704 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200705 warg->len = 0;
706 }
Michal Vasko428087d2016-01-14 16:04:28 +0100707
708 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200709}
710
711static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100712nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200713{
Michal Vasko428087d2016-01-14 16:04:28 +0100714 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100715 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200716 struct wclb_arg *warg = (struct wclb_arg *)arg;
717
718 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100719 c = nc_write_clb_flush(warg);
720 if (c == -1) {
721 return -1;
722 }
723 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200724
725 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100726 c = nc_write_endtag(warg->session);
727 if (c == -1) {
728 return -1;
729 }
730 ret += c;
731
732 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200733 }
734
735 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
736 /* dump current buffer */
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 }
Michal Vasko428087d2016-01-14 16:04:28 +0100743
Radek Krejci047300e2016-03-08 16:46:58 +0100744 if (!xmlcontent && count > WRITE_BUFSIZE) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200745 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100746 c = nc_write_starttag_and_msg(warg->session, buf, count);
747 if (c == -1) {
748 return -1;
749 }
750 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200751 } else {
752 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100753 if (xmlcontent) {
754 for (l = 0; l < count; l++) {
755 if (warg->len + 5 >= WRITE_BUFSIZE) {
756 /* buffer is full */
757 c = nc_write_clb_flush(warg);
758 if (c == -1) {
759 return -1;
760 }
761 }
762
763 switch (((char *)buf)[l]) {
764 case '&':
765 ret += 5;
766 memcpy(&warg->buf[warg->len], "&amp;", 5);
767 warg->len += 5;
768 break;
769 case '<':
770 ret += 4;
771 memcpy(&warg->buf[warg->len], "&lt;", 4);
772 warg->len += 4;
773 break;
774 case '>':
775 /* not needed, just for readability */
776 ret += 4;
777 memcpy(&warg->buf[warg->len], "&gt;", 4);
778 warg->len += 4;
779 break;
780 default:
781 ret++;
782 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
783 warg->len++;
784 }
785 }
786 } else {
787 memcpy(&warg->buf[warg->len], buf, count);
788 warg->len += count; /* is <= WRITE_BUFSIZE */
789 ret += count;
790 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200791 }
792
Michal Vasko428087d2016-01-14 16:04:28 +0100793 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200794}
795
Radek Krejci047300e2016-03-08 16:46:58 +0100796static ssize_t
797nc_write_xmlclb(void *arg, const void *buf, size_t count)
798{
799 return nc_write_clb(arg, buf, count, 0);
800}
801
Michal Vasko05ba9df2016-01-13 14:40:27 +0100802static void
Michal Vasko52bd9492016-12-08 09:37:43 +0100803nc_write_error_elem(struct wclb_arg *arg, const char *name, uint16_t nam_len, const char *prefix, uint16_t pref_len,
804 int open, int no_attr)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100805{
Michal Vasko08611b32016-12-05 13:30:37 +0100806 if (open) {
807 nc_write_clb((void *)arg, "<", 1, 0);
808 } else {
809 nc_write_clb((void *)arg, "</", 2, 0);
810 }
811
812 if (prefix) {
813 nc_write_clb((void *)arg, prefix, pref_len, 0);
814 nc_write_clb((void *)arg, ":", 1, 0);
815 }
816
817 nc_write_clb((void *)arg, name, nam_len, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100818 if (!open || !no_attr) {
819 nc_write_clb((void *)arg, ">", 1, 0);
820 }
Michal Vasko08611b32016-12-05 13:30:37 +0100821}
822
823static void
824nc_write_error(struct wclb_arg *arg, struct nc_server_error *err, const char *prefix)
825{
826 uint16_t i, pref_len;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100827 char str_sid[11];
828
Michal Vasko08611b32016-12-05 13:30:37 +0100829 if (prefix) {
830 pref_len = strlen(prefix);
831 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100832
Michal Vasko52bd9492016-12-08 09:37:43 +0100833 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100834
Michal Vasko52bd9492016-12-08 09:37:43 +0100835 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100836 switch (err->type) {
837 case NC_ERR_TYPE_TRAN:
Radek Krejci047300e2016-03-08 16:46:58 +0100838 nc_write_clb((void *)arg, "transport", 9, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100839 break;
840 case NC_ERR_TYPE_RPC:
Radek Krejci047300e2016-03-08 16:46:58 +0100841 nc_write_clb((void *)arg, "rpc", 3, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100842 break;
843 case NC_ERR_TYPE_PROT:
Radek Krejci047300e2016-03-08 16:46:58 +0100844 nc_write_clb((void *)arg, "protocol", 8, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100845 break;
846 case NC_ERR_TYPE_APP:
Radek Krejci047300e2016-03-08 16:46:58 +0100847 nc_write_clb((void *)arg, "application", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100848 break;
849 default:
850 ERRINT;
851 return;
852 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100853
Michal Vasko52bd9492016-12-08 09:37:43 +0100854 nc_write_error_elem(arg, "error-type", 10, prefix, pref_len, 0, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100855
Michal Vasko52bd9492016-12-08 09:37:43 +0100856 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100857 switch (err->tag) {
858 case NC_ERR_IN_USE:
Radek Krejci047300e2016-03-08 16:46:58 +0100859 nc_write_clb((void *)arg, "in-use", 6, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100860 break;
861 case NC_ERR_INVALID_VALUE:
Radek Krejci047300e2016-03-08 16:46:58 +0100862 nc_write_clb((void *)arg, "invalid-value", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100863 break;
864 case NC_ERR_TOO_BIG:
Radek Krejci047300e2016-03-08 16:46:58 +0100865 nc_write_clb((void *)arg, "too-big", 7, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100866 break;
867 case NC_ERR_MISSING_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100868 nc_write_clb((void *)arg, "missing-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100869 break;
870 case NC_ERR_BAD_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100871 nc_write_clb((void *)arg, "bad-attribute", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100872 break;
873 case NC_ERR_UNKNOWN_ATTR:
Radek Krejci047300e2016-03-08 16:46:58 +0100874 nc_write_clb((void *)arg, "unknown-attribute", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100875 break;
876 case NC_ERR_MISSING_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100877 nc_write_clb((void *)arg, "missing-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100878 break;
879 case NC_ERR_BAD_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100880 nc_write_clb((void *)arg, "bad-element", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100881 break;
882 case NC_ERR_UNKNOWN_ELEM:
Radek Krejci047300e2016-03-08 16:46:58 +0100883 nc_write_clb((void *)arg, "unknown-element", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100884 break;
885 case NC_ERR_UNKNOWN_NS:
Radek Krejci047300e2016-03-08 16:46:58 +0100886 nc_write_clb((void *)arg, "unknown-namespace", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100887 break;
888 case NC_ERR_ACCESS_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100889 nc_write_clb((void *)arg, "access-denied", 13, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100890 break;
891 case NC_ERR_LOCK_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100892 nc_write_clb((void *)arg, "lock-denied", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100893 break;
894 case NC_ERR_RES_DENIED:
Radek Krejci047300e2016-03-08 16:46:58 +0100895 nc_write_clb((void *)arg, "resource-denied", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100896 break;
897 case NC_ERR_ROLLBACK_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100898 nc_write_clb((void *)arg, "rollback-failed", 15, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100899 break;
900 case NC_ERR_DATA_EXISTS:
Radek Krejci047300e2016-03-08 16:46:58 +0100901 nc_write_clb((void *)arg, "data-exists", 11, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100902 break;
903 case NC_ERR_DATA_MISSING:
Radek Krejci047300e2016-03-08 16:46:58 +0100904 nc_write_clb((void *)arg, "data-missing", 12, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100905 break;
906 case NC_ERR_OP_NOT_SUPPORTED:
Radek Krejci047300e2016-03-08 16:46:58 +0100907 nc_write_clb((void *)arg, "operation-not-supported", 23, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908 break;
909 case NC_ERR_OP_FAILED:
Radek Krejci047300e2016-03-08 16:46:58 +0100910 nc_write_clb((void *)arg, "operation-failed", 16, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 break;
912 case NC_ERR_MALFORMED_MSG:
Radek Krejci047300e2016-03-08 16:46:58 +0100913 nc_write_clb((void *)arg, "malformed-message", 17, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100914 break;
915 default:
916 ERRINT;
917 return;
918 }
Michal Vasko52bd9492016-12-08 09:37:43 +0100919 nc_write_error_elem(arg, "error-tag", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100920
Michal Vasko52bd9492016-12-08 09:37:43 +0100921 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 1, 0);
Michal Vasko08611b32016-12-05 13:30:37 +0100922 nc_write_clb((void *)arg, "error", 5, 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100923 nc_write_error_elem(arg, "error-severity", 14, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100924
925 if (err->apptag) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100926 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100927 nc_write_clb((void *)arg, err->apptag, strlen(err->apptag), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100928 nc_write_error_elem(arg, "error-app-tag", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100929 }
930
931 if (err->path) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100932 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100933 nc_write_clb((void *)arg, err->path, strlen(err->path), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100934 nc_write_error_elem(arg, "error-path", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100935 }
936
937 if (err->message) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100938 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 1, 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100939 if (err->message_lang) {
Radek Krejci047300e2016-03-08 16:46:58 +0100940 nc_write_clb((void *)arg, " xml:lang=\"", 11, 0);
941 nc_write_clb((void *)arg, err->message_lang, strlen(err->message_lang), 1);
942 nc_write_clb((void *)arg, "\"", 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100943 }
Radek Krejci047300e2016-03-08 16:46:58 +0100944 nc_write_clb((void *)arg, ">", 1, 0);
945 nc_write_clb((void *)arg, err->message, strlen(err->message), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100946 nc_write_error_elem(arg, "error-message", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100947 }
948
Michal Vasko90920b02016-05-20 14:07:00 +0200949 if ((err->sid > -1) || err->attr_count || err->elem_count || err->ns_count || err->other_count) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100950 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 1, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100951
Michal Vasko90920b02016-05-20 14:07:00 +0200952 if (err->sid > -1) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100953 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 1, 0);
Michal Vasko90920b02016-05-20 14:07:00 +0200954 sprintf(str_sid, "%u", (uint32_t)err->sid);
Radek Krejci047300e2016-03-08 16:46:58 +0100955 nc_write_clb((void *)arg, str_sid, strlen(str_sid), 0);
Michal Vasko52bd9492016-12-08 09:37:43 +0100956 nc_write_error_elem(arg, "session-id", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100957 }
958
959 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100960 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100961 nc_write_clb((void *)arg, err->attr[i], strlen(err->attr[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100962 nc_write_error_elem(arg, "bad-attribute", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100963 }
964
965 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100966 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100967 nc_write_clb((void *)arg, err->elem[i], strlen(err->elem[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100968 nc_write_error_elem(arg, "bad-element", 11, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100969 }
970
971 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko52bd9492016-12-08 09:37:43 +0100972 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 1, 0);
Radek Krejci047300e2016-03-08 16:46:58 +0100973 nc_write_clb((void *)arg, err->ns[i], strlen(err->ns[i]), 1);
Michal Vasko52bd9492016-12-08 09:37:43 +0100974 nc_write_error_elem(arg, "bad-namespace", 13, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100975 }
976
977 for (i = 0; i < err->other_count; ++i) {
Radek Krejci047300e2016-03-08 16:46:58 +0100978 lyxml_print_clb(nc_write_xmlclb, (void *)arg, err->other[i], 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100979 }
980
Michal Vasko52bd9492016-12-08 09:37:43 +0100981 nc_write_error_elem(arg, "error-info", 10, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100982 }
983
Michal Vasko52bd9492016-12-08 09:37:43 +0100984 nc_write_error_elem(arg, "rpc-error", 9, prefix, pref_len, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100985}
986
Michal Vasko428087d2016-01-14 16:04:28 +0100987/* return -1 can change session status */
Radek Krejcid116db42016-01-08 15:36:30 +0100988int
Radek Krejci127f8952016-10-12 14:57:16 +0200989nc_write_msg(struct nc_session *session, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200990{
Radek Krejcid116db42016-01-08 15:36:30 +0100991 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100992 int count;
Michal Vasko08611b32016-12-05 13:30:37 +0100993 const char *attrs, *base_prefix;
Radek Krejcife0b3472015-10-12 13:43:42 +0200994 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100995 struct lyxml_elem *rpc_elem;
Radek Krejci93e80222016-10-03 13:34:25 +0200996 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100997 struct nc_server_reply *reply;
998 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100999 char *buf = NULL;
1000 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +02001001 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001002 uint32_t *sid = NULL, i;
Radek Krejcif9f93482016-09-21 14:11:15 +02001003 int wd = 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001004
Michal Vasko428087d2016-01-14 16:04:28 +01001005 assert(session);
1006
1007 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001008 ERR("Session %u: invalid session to write to.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +01001009 return -1;
1010 }
1011
Radek Krejcid116db42016-01-08 15:36:30 +01001012 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +02001013
1014 arg.session = session;
1015 arg.len = 0;
1016
Radek Krejci127f8952016-10-12 14:57:16 +02001017
Radek Krejcife0b3472015-10-12 13:43:42 +02001018 switch (type) {
1019 case NC_MSG_RPC:
1020 content = va_arg(ap, struct lyd_node *);
1021 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001022
Radek Krejcife0b3472015-10-12 13:43:42 +02001023 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
Michal Vasko2e6defd2016-10-07 15:48:15 +02001024 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001025 if (count == -1) {
1026 ERRMEM;
Michal Vasko0f74da52016-03-03 08:52:52 +01001027 va_end(ap);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001028 return -1;
1029 }
Radek Krejci047300e2016-03-08 16:46:58 +01001030 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001031 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +02001032
Michal Vaskoe6e8b852016-10-20 14:07:29 +02001033 lyd_print_clb(nc_write_xmlclb, (void *)&arg, content, LYD_XML, LYP_WITHSIBLINGS | LYP_NETCONF);
Radek Krejci047300e2016-03-08 16:46:58 +01001034 nc_write_clb((void *)&arg, "</rpc>", 6, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001035
Michal Vasko2e6defd2016-10-07 15:48:15 +02001036 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +02001037 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001038
Radek Krejcife0b3472015-10-12 13:43:42 +02001039 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +01001040 rpc_elem = va_arg(ap, struct lyxml_elem *);
1041 reply = va_arg(ap, struct nc_server_reply *);
1042
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001043 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1044 nc_write_clb((void *)&arg, "<", 1, 0);
1045 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1046 nc_write_clb((void *)&arg, ":rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001047 base_prefix = rpc_elem->ns->prefix;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001048 }
1049 else {
1050 nc_write_clb((void *)&arg, "<rpc-reply", 10, 0);
Michal Vasko08611b32016-12-05 13:30:37 +01001051 base_prefix = NULL;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001052 }
1053
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001054 /* can be NULL if replying with a malformed-message error */
1055 if (rpc_elem) {
Radek Krejci047300e2016-03-08 16:46:58 +01001056 lyxml_print_clb(nc_write_xmlclb, (void *)&arg, rpc_elem, LYXML_PRINT_ATTRS);
Radek Krejci844662e2016-04-13 16:54:43 +02001057 nc_write_clb((void *)&arg, ">", 1, 0);
1058 } else {
1059 /* but put there at least the correct namespace */
1060 nc_write_clb((void *)&arg, "xmlns=\""NC_NS_BASE"\">", 48, 0);
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001061 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001062 switch (reply->type) {
1063 case NC_RPL_OK:
Michal Vasko08611b32016-12-05 13:30:37 +01001064 nc_write_clb((void *)&arg, "<", 1, 0);
1065 if (base_prefix) {
1066 nc_write_clb((void *)&arg, base_prefix, strlen(base_prefix), 0);
1067 nc_write_clb((void *)&arg, ":", 1, 0);
1068 }
1069 nc_write_clb((void *)&arg, "ok/>", 4, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001070 break;
1071 case NC_RPL_DATA:
Radek Krejci36dfdb32016-09-01 16:56:35 +02001072 assert(((struct nc_server_reply_data *)reply)->data->schema->nodetype == LYS_RPC);
1073 switch(((struct nc_server_reply_data *)reply)->wd) {
1074 case NC_WD_UNKNOWN:
1075 case NC_WD_EXPLICIT:
1076 wd = LYP_WD_EXPLICIT;
1077 break;
1078 case NC_WD_TRIM:
1079 wd = LYP_WD_TRIM;
1080 break;
1081 case NC_WD_ALL:
1082 wd = LYP_WD_ALL;
1083 break;
1084 case NC_WD_ALL_TAG:
1085 wd = LYP_WD_ALL_TAG;
1086 break;
1087 }
Michal Vaskoe1708602016-10-18 12:17:22 +02001088 lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML,
Michal Vaskoe6e8b852016-10-20 14:07:29 +02001089 LYP_WITHSIBLINGS | LYP_NETCONF | wd);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001090 break;
1091 case NC_RPL_ERROR:
1092 error_rpl = (struct nc_server_reply_error *)reply;
1093 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko08611b32016-12-05 13:30:37 +01001094 nc_write_error(&arg, error_rpl->err[i], base_prefix);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001095 }
1096 break;
1097 default:
1098 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001099 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001100 va_end(ap);
1101 return -1;
1102 }
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001103 if (rpc_elem && rpc_elem->ns && rpc_elem->ns->prefix) {
1104 nc_write_clb((void *)&arg, "</", 2, 0);
1105 nc_write_clb((void *)&arg, rpc_elem->ns->prefix, strlen(rpc_elem->ns->prefix), 0);
1106 nc_write_clb((void *)&arg, ":rpc-reply>", 11, 0);
1107 }
1108 else {
1109 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1110 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001111 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001112
Radek Krejcife0b3472015-10-12 13:43:42 +02001113 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001114 notif = va_arg(ap, struct nc_server_notif *);
1115
1116 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
1117 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1118 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1119 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
1120 lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0);
mohitarora24878b2962016-11-09 18:45:33 -05001121 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001122 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001123
Radek Krejcid116db42016-01-08 15:36:30 +01001124 case NC_MSG_HELLO:
1125 if (session->version != NC_VERSION_10) {
1126 va_end(ap);
1127 return -1;
1128 }
1129 capabilities = va_arg(ap, const char **);
1130 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001131
Radek Krejcid116db42016-01-08 15:36:30 +01001132 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001133 if (count == -1) {
1134 ERRMEM;
1135 va_end(ap);
1136 return -1;
1137 }
Radek Krejci047300e2016-03-08 16:46:58 +01001138 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001139 free(buf);
1140 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001141 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1142 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1143 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001144 }
1145 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001146 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001147 if (count == -1) {
1148 ERRMEM;
1149 va_end(ap);
1150 return -1;
1151 }
Radek Krejci047300e2016-03-08 16:46:58 +01001152 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001153 free(buf);
1154 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001155 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001156 }
Radek Krejcid116db42016-01-08 15:36:30 +01001157 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001158
Radek Krejcife0b3472015-10-12 13:43:42 +02001159 default:
Radek Krejcid116db42016-01-08 15:36:30 +01001160 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +02001161 return -1;
1162 }
1163
1164 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001165 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001166
1167 va_end(ap);
Michal Vasko428087d2016-01-14 16:04:28 +01001168 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1169 /* error was already written */
1170 return -1;
1171 }
1172
Radek Krejcid116db42016-01-08 15:36:30 +01001173 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +02001174}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001175
1176void *
1177nc_realloc(void *ptr, size_t size)
1178{
1179 void *ret;
1180
1181 ret = realloc(ptr, size);
1182 if (!ret) {
1183 free(ptr);
1184 }
1185
1186 return ret;
1187}