blob: f2a6451eeb74243b519053d0c0dc9f2401921e47 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file io.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - input/output functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020013 */
14
Miroslav Mareš9563b812017-08-19 17:45:36 +020015#define _GNU_SOURCE /* asprintf, signals */
Radek Krejci206fcd62015-10-07 15:42:48 +020016#include <assert.h>
17#include <errno.h>
Michal Vasko3512e402016-01-28 16:22:34 +010018#include <inttypes.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020019#include <poll.h>
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +020020#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020021#include <signal.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020022#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020023#include <stdlib.h>
24#include <string.h>
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +020025#include <sys/types.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010026#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020027#include <unistd.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020028
Michal Vasko964e1732016-09-23 13:39:33 +020029#ifdef NC_ENABLED_TLS
30# include <openssl/err.h>
31#endif
32
Radek Krejci206fcd62015-10-07 15:42:48 +020033#include <libyang/libyang.h>
34
Michal Vasko9e8ac262020-04-07 13:06:45 +020035#include "compat.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020036#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020037
Michal Vasko8fe604c2020-02-10 15:25:04 +010038const char *nc_msgtype2str[] = {
39 "error",
40 "would block",
41 "no message",
42 "hello message",
43 "bad hello message",
44 "RPC message",
45 "rpc-reply message",
46 "rpc-reply message with wrong ID",
47 "notification message",
48};
49
Radek Krejcife0b3472015-10-12 13:43:42 +020050#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020051
Michal Vasko90a87d92018-12-10 15:53:44 +010052#ifdef NC_ENABLED_TLS
53
54static char *
55nc_ssl_error_get_reasons(void)
56{
57 unsigned int e;
58 int reason_size, reason_len;
59 char *reasons = NULL;
60
61 reason_size = 1;
62 reason_len = 0;
63 while ((e = ERR_get_error())) {
64 if (reason_len) {
65 /* add "; " */
66 reason_size += 2;
67 reasons = nc_realloc(reasons, reason_size);
68 if (!reasons) {
69 ERRMEM;
70 return NULL;
71 }
72 reason_len += sprintf(reasons + reason_len, "; ");
73 }
74 reason_size += strlen(ERR_reason_error_string(e));
75 reasons = nc_realloc(reasons, reason_size);
76 if (!reasons) {
77 ERRMEM;
78 return NULL;
79 }
Rosen Peneve38d7ef2019-07-15 18:18:03 -070080 reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e));
Michal Vasko90a87d92018-12-10 15:53:44 +010081 }
82
83 return reasons;
84}
85
86#endif
87
Radek Krejci206fcd62015-10-07 15:42:48 +020088static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010089nc_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 +020090{
Michal Vasko81b33fb2016-09-26 14:57:36 +020091 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010092 ssize_t r = -1;
Robin Jarry7de4b8e2019-10-14 21:46:00 +020093 int fd, interrupted;
Michal Vasko36c7be82017-02-22 13:37:59 +010094 struct timespec ts_cur, ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020095
96 assert(session);
97 assert(buf);
98
Michal Vasko428087d2016-01-14 16:04:28 +010099 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
100 return -1;
101 }
102
Radek Krejci206fcd62015-10-07 15:42:48 +0200103 if (!count) {
104 return 0;
105 }
106
Michal Vasko77a6abe2017-10-05 10:02:20 +0200107 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100108 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +0200109 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200110 interrupted = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100111 switch (session->ti_type) {
112 case NC_TI_NONE:
113 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100114
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100115 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200116 case NC_TI_UNIX:
117 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100118 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200119 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200120 if (r < 0) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200121 if (errno == EAGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100122 r = 0;
123 break;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200124 } else if (errno == EINTR) {
125 r = 0;
126 interrupted = 1;
127 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200128 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200129 ERR(session, "Reading from file descriptor (%d) failed (%s).", fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100130 session->status = NC_STATUS_INVALID;
131 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200132 return -1;
133 }
134 } else if (r == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200135 ERR(session, "Communication file descriptor (%d) unexpectedly closed.", fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100136 session->status = NC_STATUS_INVALID;
137 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200138 return -1;
139 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100140 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200141
Radek Krejci53691be2016-02-22 13:58:37 +0100142#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100143 case NC_TI_LIBSSH:
144 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200145 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200146 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100147 r = 0;
148 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200149 } else if (r == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200150 ERR(session, "Reading from the SSH channel failed (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100151 session->status = NC_STATUS_INVALID;
152 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200153 return -1;
154 } else if (r == 0) {
155 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200156 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100157 session->status = NC_STATUS_INVALID;
158 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200159 return -1;
160 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100161 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200162 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100163 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200164#endif
165
Radek Krejci53691be2016-02-22 13:58:37 +0100166#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100167 case NC_TI_OPENSSL:
168 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100169 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200170 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200171 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100172 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100173 char *reasons;
174
Michal Vasko0abba6d2018-12-10 14:09:39 +0100175 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200176 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100177 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100178 r = 0;
179 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200180 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200181 ERR(session, "Communication socket unexpectedly closed (OpenSSL).");
Michal Vasko428087d2016-01-14 16:04:28 +0100182 session->status = NC_STATUS_INVALID;
183 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200184 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100185 case SSL_ERROR_SYSCALL:
Michal Vasko0272dc32021-09-03 13:02:20 +0200186 ERR(session, "SSL socket error (%s).", errno ? strerror(errno) : "unexpected EOF");
Michal Vasko0abba6d2018-12-10 14:09:39 +0100187 session->status = NC_STATUS_INVALID;
188 session->term_reason = NC_SESSION_TERM_OTHER;
189 return -1;
190 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100191 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200192 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100193 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100194 session->status = NC_STATUS_INVALID;
195 session->term_reason = NC_SESSION_TERM_OTHER;
196 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200197 default:
Michal Vasko05532772021-06-03 12:12:38 +0200198 ERR(session, "Unknown SSL error occured (err code %d).", e);
Michal Vasko428087d2016-01-14 16:04:28 +0100199 session->status = NC_STATUS_INVALID;
200 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200201 return -1;
202 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200203 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100204 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200205#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100206 }
207
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100208 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100209 /* nothing read */
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200210 if (!interrupted) {
211 usleep(NC_TIMEOUT_STEP);
212 }
Michal Vasko77a6abe2017-10-05 10:02:20 +0200213 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100214 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
215 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200216 ERR(session, "Inactive read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100217 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200218 ERR(session, "Active read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100219 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100220 session->status = NC_STATUS_INVALID;
221 session->term_reason = NC_SESSION_TERM_OTHER;
222 return -1;
223 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100224 } else {
225 /* something read */
226 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100227
228 /* reset inactive timeout */
Michal Vasko77a6abe2017-10-05 10:02:20 +0200229 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100230 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100231 }
232
Michal Vasko81b33fb2016-09-26 14:57:36 +0200233 } while (readd < count);
234 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200235
Michal Vasko81b33fb2016-09-26 14:57:36 +0200236 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200237}
238
239static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100240nc_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 +0200241{
242 ssize_t r;
243
244 assert(session);
245 assert(chunk);
246
247 if (!len) {
248 return 0;
249 }
250
Michal Vasko4eb3c312016-03-01 14:09:37 +0100251 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200252 if (!*chunk) {
253 ERRMEM;
254 return -1;
255 }
256
Michal Vasko36c7be82017-02-22 13:37:59 +0100257 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200258 if (r <= 0) {
259 free(*chunk);
260 return -1;
261 }
262
263 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200264 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200265
266 return r;
267}
268
269static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100270nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200271 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200272{
273 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200274 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200275
276 assert(session);
277 assert(endtag);
278
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200279 if (limit && (limit < BUFFERSIZE)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200280 size = limit;
281 } else {
282 size = BUFFERSIZE;
283 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100284 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200285 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200286 ERRMEM;
287 return -1;
288 }
289
290 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100291 while (1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200292 if (limit && (count == limit)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200293 free(chunk);
Michal Vasko05532772021-06-03 12:12:38 +0200294 WRN(session, "Reading limit (%d) reached.", limit);
295 ERR(session, "Invalid input data (missing \"%s\" sequence).", endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200296 return -1;
297 }
298
299 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200300 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200301 /* get more memory */
302 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200303 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100304 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200305 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200306 return -1;
307 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200308 }
309
310 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200311 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
312 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200313 free(chunk);
314 return -1;
315 }
316
David Sedlákfedbc792018-07-04 11:07:07 +0200317 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200318
David Sedlákfedbc792018-07-04 11:07:07 +0200319 for (i = len - matched; i > 0; i--) {
320 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
321 /*part of endtag found */
322 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200323 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200324 } else {
325 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200326 }
327 }
David Sedlákfedbc792018-07-04 11:07:07 +0200328
329 /* whole endtag found */
330 if (matched == len) {
331 break;
332 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200333 }
334
335 /* terminating null byte */
336 chunk[count] = 0;
337
338 if (result) {
339 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200340 } else {
341 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200342 }
343 return count;
344}
345
Michal Vasko77367452021-02-16 16:32:18 +0100346int
347nc_read_msg_io(struct nc_session *session, int io_timeout, struct ly_in **msg, int passing_io_lock)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100348{
Michal Vasko77367452021-02-16 16:32:18 +0100349 int ret = 1, r, io_locked = passing_io_lock;
350 char *data = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100351 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100352 /* use timeout in milliseconds instead seconds */
353 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
354 struct timespec ts_act_timeout;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100355
Michal Vasko77367452021-02-16 16:32:18 +0100356 assert(session && msg);
357 *msg = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100358
359 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200360 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100361 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200362 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100363 }
364
Michal Vasko77a6abe2017-10-05 10:02:20 +0200365 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100366 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
367
Michal Vasko131120a2018-05-29 15:44:02 +0200368 if (!io_locked) {
369 /* SESSION IO LOCK */
370 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100371 if (ret < 1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200372 goto cleanup;
373 }
374 io_locked = 1;
375 }
376
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377 /* read the message */
378 switch (session->version) {
379 case NC_VERSION_10:
Michal Vasko77367452021-02-16 16:32:18 +0100380 r = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &data);
381 if (r == -1) {
382 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200383 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 }
385
386 /* cut off the end tag */
Michal Vasko77367452021-02-16 16:32:18 +0100387 data[r - NC_VERSION_10_ENDTAG_LEN] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100388 break;
389 case NC_VERSION_11:
390 while (1) {
Michal Vasko77367452021-02-16 16:32:18 +0100391 r = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
392 if (r == -1) {
393 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200394 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100395 }
Michal Vasko77367452021-02-16 16:32:18 +0100396 r = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
397 if (r == -1) {
398 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200399 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100400 }
401
402 if (!strcmp(chunk, "#\n")) {
403 /* end of chunked framing message */
404 free(chunk);
Michal Vasko77367452021-02-16 16:32:18 +0100405 if (!data) {
Michal Vasko05532772021-06-03 12:12:38 +0200406 ERR(session, "Invalid frame chunk delimiters.");
Michal Vasko77367452021-02-16 16:32:18 +0100407 ret = -2;
408 goto cleanup;
Michal Vasko79df3262016-07-13 13:42:17 +0200409 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100410 break;
411 }
412
413 /* convert string to the size of the following chunk */
414 chunk_len = strtoul(chunk, (char **)NULL, 10);
415 free(chunk);
416 if (!chunk_len) {
Michal Vasko05532772021-06-03 12:12:38 +0200417 ERR(session, "Invalid frame chunk size detected, fatal error.");
Michal Vasko77367452021-02-16 16:32:18 +0100418 ret = -2;
419 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100420 }
421
422 /* now we have size of next chunk, so read the chunk */
Michal Vasko77367452021-02-16 16:32:18 +0100423 r = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
424 if (r == -1) {
425 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200426 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 }
428
429 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko77367452021-02-16 16:32:18 +0100430 data = nc_realloc(data, len + chunk_len + 1);
431 if (!data) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432 ERRMEM;
Michal Vasko77367452021-02-16 16:32:18 +0100433 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200434 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100435 }
Michal Vasko77367452021-02-16 16:32:18 +0100436 memcpy(data + len, chunk, chunk_len);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100437 len += chunk_len;
Michal Vasko77367452021-02-16 16:32:18 +0100438 data[len] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100439 free(chunk);
440 }
441
442 break;
443 }
Michal Vasko131120a2018-05-29 15:44:02 +0200444
445 /* SESSION IO UNLOCK */
446 assert(io_locked);
447 nc_session_io_unlock(session, __func__);
448 io_locked = 0;
449
Michal Vasko05532772021-06-03 12:12:38 +0200450 DBG(session, "Received message:\n%s\n", data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100451
Michal Vasko77367452021-02-16 16:32:18 +0100452 /* build an input structure, eats data */
453 if (ly_in_new_memory(data, msg)) {
454 ret = -1;
455 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456 }
Michal Vasko77367452021-02-16 16:32:18 +0100457 data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100458
Michal Vasko131120a2018-05-29 15:44:02 +0200459cleanup:
460 if (io_locked) {
Michal Vasko77367452021-02-16 16:32:18 +0100461 /* SESSION IO UNLOCK */
Michal Vasko131120a2018-05-29 15:44:02 +0200462 nc_session_io_unlock(session, __func__);
463 }
Michal Vasko77367452021-02-16 16:32:18 +0100464 free(data);
Michal Vasko131120a2018-05-29 15:44:02 +0200465 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100466}
467
Michal Vasko428087d2016-01-14 16:04:28 +0100468/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
469static int
Michal Vasko131120a2018-05-29 15:44:02 +0200470nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100471{
Radek Krejci5961c702016-07-15 09:15:18 +0200472 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100473 int ret = -2;
474 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100475
476 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200477 ERR(session, "Invalid session to poll.");
Michal Vasko428087d2016-01-14 16:04:28 +0100478 return -1;
479 }
480
481 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100482#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100483 case NC_TI_LIBSSH:
484 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200485 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100486 if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200487 ERR(session, "SSH channel poll error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100488 session->status = NC_STATUS_INVALID;
489 session->term_reason = NC_SESSION_TERM_OTHER;
490 return -1;
491 } else if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200492 ERR(session, "SSH channel unexpected EOF.");
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 } else if (ret > 0) {
497 /* fake it */
498 ret = 1;
499 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100500 } else { /* ret == 0 */
501 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100502 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100503 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100504#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100505#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100506 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100507 ret = SSL_pending(session->ti.tls);
508 if (ret) {
509 /* some buffered TLS data available */
510 ret = 1;
511 fds.revents = POLLIN;
512 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100513 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100514
515 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100516#endif
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200517 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100518 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200519 case NC_TI_UNIX:
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200520 if (session->ti_type == NC_TI_FD) {
Michal Vasko428087d2016-01-14 16:04:28 +0100521 fds.fd = session->ti.fd.in;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200522 } else if (session->ti_type == NC_TI_UNIX) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200523 fds.fd = session->ti.unixsock.sock;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200524 }
Michal Vasko428087d2016-01-14 16:04:28 +0100525
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100526 fds.events = POLLIN;
527 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100528
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100529 sigfillset(&sigmask);
530 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200531 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100532 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100533
534 break;
535
536 default:
537 ERRINT;
538 return -1;
539 }
540
541 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
542 if (ret < 0) {
543 /* poll failed - something really bad happened, close the session */
Michal Vasko05532772021-06-03 12:12:38 +0200544 ERR(session, "poll error (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100545 session->status = NC_STATUS_INVALID;
546 session->term_reason = NC_SESSION_TERM_OTHER;
547 return -1;
548 } else { /* status > 0 */
549 /* in case of standard (non-libssh) poll, there still can be an error */
Michal Vasko428087d2016-01-14 16:04:28 +0100550 if (fds.revents & POLLERR) {
Michal Vasko05532772021-06-03 12:12:38 +0200551 ERR(session, "Communication channel error.");
Michal Vasko428087d2016-01-14 16:04:28 +0100552 session->status = NC_STATUS_INVALID;
553 session->term_reason = NC_SESSION_TERM_OTHER;
554 return -1;
555 }
Robin Jarryf732adc2020-05-15 11:18:38 +0200556 /* Some poll() implementations may return POLLHUP|POLLIN when the other
557 * side has closed but there is data left to read in the buffer. */
558 if ((fds.revents & POLLHUP) && !(fds.revents & POLLIN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200559 ERR(session, "Communication channel unexpectedly closed.");
Robin Jarryf732adc2020-05-15 11:18:38 +0200560 session->status = NC_STATUS_INVALID;
561 session->term_reason = NC_SESSION_TERM_DROPPED;
562 return -1;
563 }
Michal Vasko428087d2016-01-14 16:04:28 +0100564 }
565
566 return ret;
567}
568
Michal Vasko77367452021-02-16 16:32:18 +0100569int
570nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **msg)
Radek Krejci206fcd62015-10-07 15:42:48 +0200571{
Michal Vasko428087d2016-01-14 16:04:28 +0100572 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200573
Michal Vasko77367452021-02-16 16:32:18 +0100574 assert(msg);
575 *msg = NULL;
Radek Krejci206fcd62015-10-07 15:42:48 +0200576
Michal Vasko428087d2016-01-14 16:04:28 +0100577 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200578 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100579 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200580 }
581
Michal Vasko131120a2018-05-29 15:44:02 +0200582 /* SESSION IO LOCK */
583 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100584 if (ret < 1) {
585 return ret;
Michal Vasko131120a2018-05-29 15:44:02 +0200586 }
587
588 ret = nc_read_poll(session, io_timeout);
Michal Vasko77367452021-02-16 16:32:18 +0100589 if (ret < 1) {
590 /* timed out or error */
Michal Vasko131120a2018-05-29 15:44:02 +0200591
592 /* SESSION IO UNLOCK */
593 nc_session_io_unlock(session, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100594 return ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200595 }
596
Michal Vasko131120a2018-05-29 15:44:02 +0200597 /* SESSION IO LOCK passed down */
Michal Vasko77367452021-02-16 16:32:18 +0100598 return nc_read_msg_io(session, io_timeout, msg, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200599}
Radek Krejcife0b3472015-10-12 13:43:42 +0200600
Michal Vasko428087d2016-01-14 16:04:28 +0100601/* does not really log, only fatal errors */
602int
603nc_session_is_connected(struct nc_session *session)
604{
605 int ret;
606 struct pollfd fds;
607
608 switch (session->ti_type) {
609 case NC_TI_FD:
610 fds.fd = session->ti.fd.in;
611 break;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200612 case NC_TI_UNIX:
613 fds.fd = session->ti.unixsock.sock;
614 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100615#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100616 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100617 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100618#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100619#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100620 case NC_TI_OPENSSL:
621 fds.fd = SSL_get_fd(session->ti.tls);
622 break;
623#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100624 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100625 return 0;
626 }
627
Michal Vasko840a8a62017-02-07 10:56:34 +0100628 if (fds.fd == -1) {
629 return 0;
630 }
631
Michal Vasko428087d2016-01-14 16:04:28 +0100632 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100633 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100634
635 errno = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200636 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR)) {}
Michal Vasko428087d2016-01-14 16:04:28 +0100637
638 if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200639 ERR(session, "poll failed (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100640 return 0;
641 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
642 return 0;
643 }
644
645 return 1;
646}
647
Radek Krejcife0b3472015-10-12 13:43:42 +0200648#define WRITE_BUFSIZE (2 * BUFFERSIZE)
649struct wclb_arg {
650 struct nc_session *session;
651 char buf[WRITE_BUFSIZE];
652 size_t len;
653};
654
Michal Vasko964e1732016-09-23 13:39:33 +0200655static int
Michal Vasko428087d2016-01-14 16:04:28 +0100656nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200657{
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200658 int c, fd, interrupted;
Michal Vasko964e1732016-09-23 13:39:33 +0200659 size_t written = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200660
Michal Vaskoe2357e92016-10-05 14:20:47 +0200661#ifdef NC_ENABLED_TLS
662 unsigned long e;
663#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200664
Michal Vasko428087d2016-01-14 16:04:28 +0100665 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
666 return -1;
667 }
668
669 /* prevent SIGPIPE this way */
670 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +0200671 ERR(session, "Communication socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100672 session->status = NC_STATUS_INVALID;
673 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100674 return -1;
675 }
676
Michal Vasko05532772021-06-03 12:12:38 +0200677 DBG(session, "Sending message:\n%.*s\n", count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200678
Michal Vasko81b33fb2016-09-26 14:57:36 +0200679 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200680 interrupted = 0;
Michal Vasko964e1732016-09-23 13:39:33 +0200681 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200682 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200683 case NC_TI_UNIX:
684 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
685 c = write(fd, (char *)(buf + written), count - written);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200686 if ((c < 0) && (errno == EAGAIN)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200687 c = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200688 } else if ((c < 0) && (errno == EINTR)) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200689 c = 0;
690 interrupted = 1;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200691 } else if (c < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200692 ERR(session, "socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200693 return -1;
694 }
695 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200696
Radek Krejci53691be2016-02-22 13:58:37 +0100697#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200698 case NC_TI_LIBSSH:
699 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
700 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200701 ERR(session, "SSH channel unexpectedly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200702 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200703 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko964e1732016-09-23 13:39:33 +0200704 }
705 session->status = NC_STATUS_INVALID;
706 session->term_reason = NC_SESSION_TERM_DROPPED;
707 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100708 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200709 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200710 if ((c == SSH_ERROR) || (c == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200711 ERR(session, "SSH channel write failed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200712 return -1;
713 }
714 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200715#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100716#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200717 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200718 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200719 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100720 char *reasons;
721
Michal Vasko964e1732016-09-23 13:39:33 +0200722 switch ((e = SSL_get_error(session->ti.tls, c))) {
723 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200724 ERR(session, "SSL connection was properly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200725 return -1;
726 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100727 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200728 c = 0;
729 break;
730 case SSL_ERROR_SYSCALL:
Michal Vasko05532772021-06-03 12:12:38 +0200731 ERR(session, "SSL socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200732 return -1;
733 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100734 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200735 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100736 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200737 return -1;
738 default:
Michal Vasko05532772021-06-03 12:12:38 +0200739 ERR(session, "Unknown SSL error occured (err code %d).", e);
Michal Vasko964e1732016-09-23 13:39:33 +0200740 return -1;
741 }
742 }
743 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200744#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200745 default:
746 ERRINT;
747 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200748 }
749
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200750 if ((c == 0) && !interrupted) {
Michal Vasko964e1732016-09-23 13:39:33 +0200751 /* we must wait */
752 usleep(NC_TIMEOUT_STEP);
753 }
754
755 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200756 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200757
Michal Vasko964e1732016-09-23 13:39:33 +0200758 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200759}
760
Michal Vasko428087d2016-01-14 16:04:28 +0100761static int
762nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100763{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100764 int ret = 0, c;
Claus Klein22091912020-01-20 13:45:47 +0100765 char chunksize[24];
Michal Vasko086311b2016-01-08 09:53:11 +0100766
Claus Klein22091912020-01-20 13:45:47 +0100767 // warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=]
Michal Vasko086311b2016-01-08 09:53:11 +0100768 if (session->version == NC_VERSION_11) {
769 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100770 ret = nc_write(session, chunksize, strlen(chunksize));
771 if (ret == -1) {
772 return -1;
773 }
Michal Vasko086311b2016-01-08 09:53:11 +0100774 }
Michal Vasko428087d2016-01-14 16:04:28 +0100775
776 c = nc_write(session, buf, count);
777 if (c == -1) {
778 return -1;
779 }
780 ret += c;
781
782 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100783}
784
Radek Krejcife0b3472015-10-12 13:43:42 +0200785static int
Michal Vasko428087d2016-01-14 16:04:28 +0100786nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200787{
Michal Vasko428087d2016-01-14 16:04:28 +0100788 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100789
Michal Vasko428087d2016-01-14 16:04:28 +0100790 if (session->version == NC_VERSION_11) {
791 ret = nc_write(session, "\n##\n", 4);
792 } else {
793 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200794 }
795
Michal Vasko428087d2016-01-14 16:04:28 +0100796 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200797}
798
Michal Vasko428087d2016-01-14 16:04:28 +0100799static int
800nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200801{
Michal Vasko428087d2016-01-14 16:04:28 +0100802 int ret = 0;
803
Radek Krejcife0b3472015-10-12 13:43:42 +0200804 /* flush current buffer */
805 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100806 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200807 warg->len = 0;
808 }
Michal Vasko428087d2016-01-14 16:04:28 +0100809
810 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200811}
812
813static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100814nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200815{
Michal Vasko428087d2016-01-14 16:04:28 +0100816 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100817 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200818 struct wclb_arg *warg = (struct wclb_arg *)arg;
819
820 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100821 c = nc_write_clb_flush(warg);
822 if (c == -1) {
823 return -1;
824 }
825 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200826
827 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100828 c = nc_write_endtag(warg->session);
829 if (c == -1) {
830 return -1;
831 }
832 ret += c;
833
834 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200835 }
836
837 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
838 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100839 c = nc_write_clb_flush(warg);
840 if (c == -1) {
841 return -1;
842 }
843 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200844 }
Michal Vasko428087d2016-01-14 16:04:28 +0100845
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200846 if (!xmlcontent && (count > WRITE_BUFSIZE)) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200847 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100848 c = nc_write_starttag_and_msg(warg->session, buf, count);
849 if (c == -1) {
850 return -1;
851 }
852 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200853 } else {
854 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100855 if (xmlcontent) {
856 for (l = 0; l < count; l++) {
857 if (warg->len + 5 >= WRITE_BUFSIZE) {
858 /* buffer is full */
859 c = nc_write_clb_flush(warg);
860 if (c == -1) {
861 return -1;
862 }
863 }
864
865 switch (((char *)buf)[l]) {
866 case '&':
867 ret += 5;
868 memcpy(&warg->buf[warg->len], "&amp;", 5);
869 warg->len += 5;
870 break;
871 case '<':
872 ret += 4;
873 memcpy(&warg->buf[warg->len], "&lt;", 4);
874 warg->len += 4;
875 break;
876 case '>':
877 /* not needed, just for readability */
878 ret += 4;
879 memcpy(&warg->buf[warg->len], "&gt;", 4);
880 warg->len += 4;
881 break;
882 default:
883 ret++;
884 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
885 warg->len++;
886 }
887 }
888 } else {
889 memcpy(&warg->buf[warg->len], buf, count);
890 warg->len += count; /* is <= WRITE_BUFSIZE */
891 ret += count;
892 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200893 }
894
Michal Vasko428087d2016-01-14 16:04:28 +0100895 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200896}
897
Radek Krejci047300e2016-03-08 16:46:58 +0100898static ssize_t
899nc_write_xmlclb(void *arg, const void *buf, size_t count)
900{
Michal Vasko77367452021-02-16 16:32:18 +0100901 ssize_t r;
Radek Krejci047300e2016-03-08 16:46:58 +0100902
Michal Vasko77367452021-02-16 16:32:18 +0100903 r = nc_write_clb(arg, buf, count, 0);
904 if (r == -1) {
905 return -1;
Michal Vasko08611b32016-12-05 13:30:37 +0100906 }
907
Michal Vasko77367452021-02-16 16:32:18 +0100908 /* always return what libyang expects, simply that all the characters were printed */
909 return count;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100910}
911
Michal Vasko131120a2018-05-29 15:44:02 +0200912/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
913NC_MSG_TYPE
914nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200915{
Radek Krejcid116db42016-01-08 15:36:30 +0100916 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +0200917 int count, ret;
Michal Vasko70bf2222021-10-26 10:45:15 +0200918 const char *attrs, *str;
Michal Vaskoff7286b2021-07-09 13:13:11 +0200919 struct lyd_node *op, *reply_envp, *node, *next;
Michal Vasko77367452021-02-16 16:32:18 +0100920 struct lyd_node_opaq *rpc_envp;
Radek Krejci93e80222016-10-03 13:34:25 +0200921 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100922 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +0100923 char *buf;
Radek Krejcid116db42016-01-08 15:36:30 +0100924 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200925 const char **capabilities;
Michal Vasko77367452021-02-16 16:32:18 +0100926 uint32_t *sid = NULL, i, wd = 0;
927 LY_ERR lyrc;
Radek Krejcife0b3472015-10-12 13:43:42 +0200928
Michal Vasko428087d2016-01-14 16:04:28 +0100929 assert(session);
930
931 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200932 ERR(session, "Invalid session to write to.");
Michal Vasko131120a2018-05-29 15:44:02 +0200933 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100934 }
935
Radek Krejcife0b3472015-10-12 13:43:42 +0200936 arg.session = session;
937 arg.len = 0;
938
Michal Vasko131120a2018-05-29 15:44:02 +0200939 /* SESSION IO LOCK */
940 ret = nc_session_io_lock(session, io_timeout, __func__);
941 if (ret < 0) {
942 return NC_MSG_ERROR;
943 } else if (!ret) {
944 return NC_MSG_WOULDBLOCK;
945 }
946
947 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +0200948
Radek Krejcife0b3472015-10-12 13:43:42 +0200949 switch (type) {
950 case NC_MSG_RPC:
Michal Vasko77367452021-02-16 16:32:18 +0100951 op = va_arg(ap, struct lyd_node *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200952 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100953
Michal Vasko70bf2222021-10-26 10:45:15 +0200954 /* <rpc> open */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200955 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%" PRIu64 "\"%s>",
Michal Vasko77367452021-02-16 16:32:18 +0100956 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +0100957 if (count == -1) {
958 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200959 ret = NC_MSG_ERROR;
960 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100961 }
Radek Krejci047300e2016-03-08 16:46:58 +0100962 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200963 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +0200964
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100965 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200966 /* <action> open */
967 str = "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">";
968 nc_write_clb((void *)&arg, str, strlen(str), 0);
969 }
970
971 /* rpc data */
Michal Vasko77367452021-02-16 16:32:18 +0100972 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, op, LYD_XML, LYD_PRINT_SHRINK)) {
Michal Vasko131120a2018-05-29 15:44:02 +0200973 ret = NC_MSG_ERROR;
974 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +0200975 }
Michal Vasko70bf2222021-10-26 10:45:15 +0200976
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100977 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200978 /* <action> close */
979 str = "</action>";
980 nc_write_clb((void *)&arg, str, strlen(str), 0);
981 }
982
983 /* <rpc> close */
984 str = "</rpc>";
985 nc_write_clb((void *)&arg, str, strlen(str), 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200986
Michal Vasko2e6defd2016-10-07 15:48:15 +0200987 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +0200988 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100989
Radek Krejcife0b3472015-10-12 13:43:42 +0200990 case NC_MSG_REPLY:
Michal Vasko77367452021-02-16 16:32:18 +0100991 rpc_envp = va_arg(ap, struct lyd_node_opaq *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100992 reply = va_arg(ap, struct nc_server_reply *);
993
Michal Vasko77367452021-02-16 16:32:18 +0100994 if (!rpc_envp) {
995 /* can be NULL if replying with a malformed-message error */
996 nc_write_clb((void *)&arg, "<rpc-reply xmlns=\"" NC_NS_BASE "\">", 18 + strlen(NC_NS_BASE) + 2, 0);
997
998 assert(reply->type == NC_RPL_ERROR);
999 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_server_reply_error *)reply)->err, LYD_XML,
1000 LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)) {
1001 ret = NC_MSG_ERROR;
1002 goto cleanup;
1003 }
1004
1005 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1006 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001007 }
1008
Michal Vasko77367452021-02-16 16:32:18 +01001009 /* build a rpc-reply opaque node that can be simply printed */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001010 if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns,
1011 &reply_envp)) {
Michal Vasko77367452021-02-16 16:32:18 +01001012 ERRINT;
1013 ret = NC_MSG_ERROR;
1014 goto cleanup;
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001015 }
Michal Vasko77367452021-02-16 16:32:18 +01001016
Michal Vasko05ba9df2016-01-13 14:40:27 +01001017 switch (reply->type) {
1018 case NC_RPL_OK:
Michal Vasko77367452021-02-16 16:32:18 +01001019 if (lyd_new_opaq2(reply_envp, NULL, "ok", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, NULL)) {
1020 lyd_free_tree(reply_envp);
1021
1022 ERRINT;
1023 ret = NC_MSG_ERROR;
1024 goto cleanup;
Michal Vasko08611b32016-12-05 13:30:37 +01001025 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001026 break;
1027 case NC_RPL_DATA:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001028 switch (((struct nc_server_reply_data *)reply)->wd) {
Radek Krejci36dfdb32016-09-01 16:56:35 +02001029 case NC_WD_UNKNOWN:
1030 case NC_WD_EXPLICIT:
Michal Vasko77367452021-02-16 16:32:18 +01001031 wd = LYD_PRINT_WD_EXPLICIT;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001032 break;
1033 case NC_WD_TRIM:
Michal Vasko77367452021-02-16 16:32:18 +01001034 wd = LYD_PRINT_WD_TRIM;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001035 break;
1036 case NC_WD_ALL:
Michal Vasko77367452021-02-16 16:32:18 +01001037 wd = LYD_PRINT_WD_ALL;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001038 break;
1039 case NC_WD_ALL_TAG:
Michal Vasko77367452021-02-16 16:32:18 +01001040 wd = LYD_PRINT_WD_ALL_TAG;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001041 break;
1042 }
Michal Vasko77367452021-02-16 16:32:18 +01001043
1044 node = ((struct nc_server_reply_data *)reply)->data;
1045 assert(node->schema->nodetype & (LYS_RPC | LYS_ACTION));
Michal Vaskoff7286b2021-07-09 13:13:11 +02001046 LY_LIST_FOR_SAFE(lyd_child(node), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001047 /* temporary */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001048 lyd_insert_child(reply_envp, node);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001049 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001050 break;
1051 case NC_RPL_ERROR:
Michal Vasko77367452021-02-16 16:32:18 +01001052 /* temporary */
1053 lyd_insert_child(reply_envp, ((struct nc_server_reply_error *)reply)->err);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001054 break;
1055 default:
1056 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001057 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001058 ret = NC_MSG_ERROR;
1059 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001060 }
Michal Vasko77367452021-02-16 16:32:18 +01001061
1062 /* temporary */
1063 ((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr;
1064
1065 /* print */
1066 lyrc = lyd_print_clb(nc_write_xmlclb, (void *)&arg, reply_envp, LYD_XML, LYD_PRINT_SHRINK | wd);
1067 ((struct lyd_node_opaq *)reply_envp)->attr = NULL;
1068
1069 /* cleanup */
1070 switch (reply->type) {
1071 case NC_RPL_OK:
1072 /* just free everything */
1073 lyd_free_tree(reply_envp);
1074 break;
1075 case NC_RPL_DATA:
Michal Vaskoff7286b2021-07-09 13:13:11 +02001076 LY_LIST_FOR_SAFE(lyd_child(reply_envp), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001077 /* connect back to the reply structure */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001078 lyd_insert_child(((struct nc_server_reply_data *)reply)->data, node);
Michal Vasko77367452021-02-16 16:32:18 +01001079 }
1080 lyd_free_tree(reply_envp);
1081 break;
1082 case NC_RPL_ERROR:
1083 /* unlink from the data reply */
1084 lyd_unlink_tree(lyd_child(reply_envp));
1085 lyd_free_tree(reply_envp);
1086 break;
1087 default:
1088 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001089 }
Michal Vasko77367452021-02-16 16:32:18 +01001090
1091 if (lyrc) {
1092 ret = NC_MSG_ERROR;
1093 goto cleanup;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001094 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001095 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001096
Radek Krejcife0b3472015-10-12 13:43:42 +02001097 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001098 notif = va_arg(ap, struct nc_server_notif *);
1099
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001100 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF "\">", 21 + 47 + 2, 0);
Radek Krejci93e80222016-10-03 13:34:25 +02001101 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1102 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1103 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko77367452021-02-16 16:32:18 +01001104 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->ntf, LYD_XML, LYD_PRINT_SHRINK)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001105 ret = NC_MSG_ERROR;
1106 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001107 }
mohitarora24878b2962016-11-09 18:45:33 -05001108 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001109 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001110
Radek Krejcid116db42016-01-08 15:36:30 +01001111 case NC_MSG_HELLO:
1112 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001113 ret = NC_MSG_ERROR;
1114 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001115 }
1116 capabilities = va_arg(ap, const char **);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001117 sid = va_arg(ap, uint32_t *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001118
Radek Krejcid116db42016-01-08 15:36:30 +01001119 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001120 if (count == -1) {
1121 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001122 ret = NC_MSG_ERROR;
1123 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001124 }
Radek Krejci047300e2016-03-08 16:46:58 +01001125 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001126 free(buf);
1127 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001128 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1129 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1130 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001131 }
1132 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001133 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001134 if (count == -1) {
1135 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001136 ret = NC_MSG_ERROR;
1137 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001138 }
Radek Krejci047300e2016-03-08 16:46:58 +01001139 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001140 free(buf);
1141 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001142 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001143 }
Radek Krejcid116db42016-01-08 15:36:30 +01001144 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001145
Radek Krejcife0b3472015-10-12 13:43:42 +02001146 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001147 ret = NC_MSG_ERROR;
1148 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001149 }
1150
1151 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001152 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001153
Michal Vasko428087d2016-01-14 16:04:28 +01001154 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1155 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001156 ret = NC_MSG_ERROR;
1157 } else {
1158 /* specific message successfully sent */
1159 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001160 }
1161
Michal Vasko131120a2018-05-29 15:44:02 +02001162cleanup:
1163 va_end(ap);
1164 nc_session_io_unlock(session, __func__);
1165 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001166}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001167
1168void *
1169nc_realloc(void *ptr, size_t size)
1170{
1171 void *ret;
1172
1173 ret = realloc(ptr, size);
1174 if (!ret) {
1175 free(ptr);
1176 }
1177
1178 return ret;
1179}
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001180
1181struct passwd *
1182nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size)
1183{
1184 struct passwd *pwd = NULL;
Michal Vasko7e06ee52021-11-02 08:53:05 +01001185 long sys_size;
1186 int ret;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001187
1188 do {
Michal Vasko7e06ee52021-11-02 08:53:05 +01001189 if (!*buf_size) {
1190 /* learn suitable buffer size */
1191 sys_size = sysconf(_SC_GETPW_R_SIZE_MAX);
1192 *buf_size = (sys_size == -1) ? 2048 : sys_size;
1193 } else {
1194 /* enlarge buffer */
1195 *buf_size += 2048;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001196 }
1197
Michal Vasko7e06ee52021-11-02 08:53:05 +01001198 /* allocate some buffer */
1199 *buf = nc_realloc(*buf, *buf_size);
1200 if (!*buf) {
1201 ERRMEM;
1202 return NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001203 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001204
Michal Vasko7e06ee52021-11-02 08:53:05 +01001205 ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd);
1206 } while (ret && (ret == ERANGE));
1207
1208 if (ret) {
1209 ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long int)uid, strerror(ret));
1210 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001211 return pwd;
1212}