blob: d9b5c54a39adc934bdf451c67accdc4f630d7ef2 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file io.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief libnetconf2 - input/output functions
Radek Krejci206fcd62015-10-07 15:42:48 +02005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
Radek Krejci206fcd62015-10-07 15:42:48 +02007 * Copyright (c) 2015 CESNET, z.s.p.o.
8 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020014 */
15
Miroslav Mareš9563b812017-08-19 17:45:36 +020016#define _GNU_SOURCE /* asprintf, signals */
Michal Vaskoba9f3582023-02-22 10:26:32 +010017
Radek Krejci206fcd62015-10-07 15:42:48 +020018#include <assert.h>
19#include <errno.h>
Michal Vasko3512e402016-01-28 16:22:34 +010020#include <inttypes.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020021#include <poll.h>
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +020022#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020023#include <signal.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020024#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020025#include <stdlib.h>
26#include <string.h>
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +020027#include <sys/types.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010028#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020029#include <unistd.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020030
Michal Vasko964e1732016-09-23 13:39:33 +020031#ifdef NC_ENABLED_TLS
32# include <openssl/err.h>
33#endif
34
Radek Krejci206fcd62015-10-07 15:42:48 +020035#include <libyang/libyang.h>
36
Michal Vasko9e8ac262020-04-07 13:06:45 +020037#include "compat.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020038#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020039
Michal Vasko8fe604c2020-02-10 15:25:04 +010040const char *nc_msgtype2str[] = {
41 "error",
42 "would block",
43 "no message",
44 "hello message",
45 "bad hello message",
46 "RPC message",
47 "rpc-reply message",
48 "rpc-reply message with wrong ID",
49 "notification message",
50};
51
Radek Krejcife0b3472015-10-12 13:43:42 +020052#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020053
Michal Vasko90a87d92018-12-10 15:53:44 +010054#ifdef NC_ENABLED_TLS
55
56static char *
57nc_ssl_error_get_reasons(void)
58{
59 unsigned int e;
60 int reason_size, reason_len;
61 char *reasons = NULL;
62
63 reason_size = 1;
64 reason_len = 0;
65 while ((e = ERR_get_error())) {
66 if (reason_len) {
67 /* add "; " */
68 reason_size += 2;
69 reasons = nc_realloc(reasons, reason_size);
70 if (!reasons) {
71 ERRMEM;
72 return NULL;
73 }
74 reason_len += sprintf(reasons + reason_len, "; ");
75 }
76 reason_size += strlen(ERR_reason_error_string(e));
77 reasons = nc_realloc(reasons, reason_size);
78 if (!reasons) {
79 ERRMEM;
80 return NULL;
81 }
Rosen Peneve38d7ef2019-07-15 18:18:03 -070082 reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e));
Michal Vasko90a87d92018-12-10 15:53:44 +010083 }
84
85 return reasons;
86}
87
88#endif
89
Radek Krejci206fcd62015-10-07 15:42:48 +020090static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010091nc_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 +020092{
Michal Vasko81b33fb2016-09-26 14:57:36 +020093 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010094 ssize_t r = -1;
Robin Jarry7de4b8e2019-10-14 21:46:00 +020095 int fd, interrupted;
roman6ece9c52022-06-22 09:29:17 +020096 struct timespec ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020097
98 assert(session);
99 assert(buf);
100
Michal Vasko428087d2016-01-14 16:04:28 +0100101 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
102 return -1;
103 }
104
Radek Krejci206fcd62015-10-07 15:42:48 +0200105 if (!count) {
106 return 0;
107 }
108
Michal Vaskod8a74192023-02-06 15:51:50 +0100109 nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +0200110 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200111 interrupted = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100112 switch (session->ti_type) {
113 case NC_TI_NONE:
114 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100115
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100116 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200117 case NC_TI_UNIX:
118 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100119 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200120 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200121 if (r < 0) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200122 if (errno == EAGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100123 r = 0;
124 break;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200125 } else if (errno == EINTR) {
126 r = 0;
127 interrupted = 1;
128 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200129 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200130 ERR(session, "Reading from file descriptor (%d) failed (%s).", fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100131 session->status = NC_STATUS_INVALID;
132 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200133 return -1;
134 }
135 } else if (r == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200136 ERR(session, "Communication file descriptor (%d) unexpectedly closed.", fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100137 session->status = NC_STATUS_INVALID;
138 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200139 return -1;
140 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100141 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200142
Radek Krejci53691be2016-02-22 13:58:37 +0100143#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100144 case NC_TI_LIBSSH:
145 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200146 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200147 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100148 r = 0;
149 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200150 } else if (r == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200151 ERR(session, "Reading from the SSH channel failed (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100152 session->status = NC_STATUS_INVALID;
153 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200154 return -1;
155 } else if (r == 0) {
156 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200157 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100158 session->status = NC_STATUS_INVALID;
159 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200160 return -1;
161 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100162 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200163 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100164 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200165#endif
166
Radek Krejci53691be2016-02-22 13:58:37 +0100167#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100168 case NC_TI_OPENSSL:
169 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100170 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200171 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200172 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100173 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100174 char *reasons;
175
Michal Vasko0abba6d2018-12-10 14:09:39 +0100176 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200177 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100178 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100179 r = 0;
180 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200181 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200182 ERR(session, "Communication socket unexpectedly closed (OpenSSL).");
Michal Vasko428087d2016-01-14 16:04:28 +0100183 session->status = NC_STATUS_INVALID;
184 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200185 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100186 case SSL_ERROR_SYSCALL:
Michal Vasko0272dc32021-09-03 13:02:20 +0200187 ERR(session, "SSL socket error (%s).", errno ? strerror(errno) : "unexpected EOF");
Michal Vasko0abba6d2018-12-10 14:09:39 +0100188 session->status = NC_STATUS_INVALID;
189 session->term_reason = NC_SESSION_TERM_OTHER;
190 return -1;
191 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100192 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200193 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100194 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100195 session->status = NC_STATUS_INVALID;
196 session->term_reason = NC_SESSION_TERM_OTHER;
197 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200198 default:
Michal Vaskofdba4a32022-01-05 12:13:53 +0100199 ERR(session, "Unknown SSL error occurred (err code %d).", e);
Michal Vasko428087d2016-01-14 16:04:28 +0100200 session->status = NC_STATUS_INVALID;
201 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200202 return -1;
203 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200204 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100205 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200206#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100207 }
208
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100209 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100210 /* nothing read */
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200211 if (!interrupted) {
212 usleep(NC_TIMEOUT_STEP);
213 }
Michal Vaskod8a74192023-02-06 15:51:50 +0100214 if ((nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) || (nc_timeouttime_cur_diff(ts_act_timeout) < 1)) {
215 if (nc_timeouttime_cur_diff(&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 Vaskod8a74192023-02-06 15:51:50 +0100229 nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100230 }
231
Michal Vasko81b33fb2016-09-26 14:57:36 +0200232 } while (readd < count);
233 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200234
Michal Vasko81b33fb2016-09-26 14:57:36 +0200235 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200236}
237
238static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100239nc_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 +0200240{
241 ssize_t r;
242
243 assert(session);
244 assert(chunk);
245
246 if (!len) {
247 return 0;
248 }
249
Michal Vasko4eb3c312016-03-01 14:09:37 +0100250 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200251 if (!*chunk) {
252 ERRMEM;
253 return -1;
254 }
255
Michal Vasko36c7be82017-02-22 13:37:59 +0100256 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200257 if (r <= 0) {
258 free(*chunk);
259 return -1;
260 }
261
262 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200263 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200264
265 return r;
266}
267
268static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100269nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200270 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200271{
272 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200273 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200274
275 assert(session);
276 assert(endtag);
277
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200278 if (limit && (limit < BUFFERSIZE)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200279 size = limit;
280 } else {
281 size = BUFFERSIZE;
282 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100283 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200284 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200285 ERRMEM;
286 return -1;
287 }
288
289 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100290 while (1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200291 if (limit && (count == limit)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200292 free(chunk);
Michal Vasko05532772021-06-03 12:12:38 +0200293 WRN(session, "Reading limit (%d) reached.", limit);
294 ERR(session, "Invalid input data (missing \"%s\" sequence).", endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200295 return -1;
296 }
297
298 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200299 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200300 /* get more memory */
301 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200302 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100303 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200304 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200305 return -1;
306 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200307 }
308
309 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200310 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
311 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200312 free(chunk);
313 return -1;
314 }
315
David Sedlákfedbc792018-07-04 11:07:07 +0200316 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200317
David Sedlákfedbc792018-07-04 11:07:07 +0200318 for (i = len - matched; i > 0; i--) {
319 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
320 /*part of endtag found */
321 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200322 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200323 } else {
324 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200325 }
326 }
David Sedlákfedbc792018-07-04 11:07:07 +0200327
328 /* whole endtag found */
329 if (matched == len) {
330 break;
331 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200332 }
333
334 /* terminating null byte */
335 chunk[count] = 0;
336
337 if (result) {
338 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200339 } else {
340 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200341 }
342 return count;
343}
344
Michal Vasko77367452021-02-16 16:32:18 +0100345int
346nc_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 +0100347{
Michal Vasko77367452021-02-16 16:32:18 +0100348 int ret = 1, r, io_locked = passing_io_lock;
349 char *data = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100351 /* use timeout in milliseconds instead seconds */
352 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
353 struct timespec ts_act_timeout;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100354
Michal Vasko77367452021-02-16 16:32:18 +0100355 assert(session && msg);
356 *msg = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100357
358 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200359 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100360 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200361 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100362 }
363
Michal Vaskod8a74192023-02-06 15:51:50 +0100364 nc_timeouttime_get(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
Michal Vasko36c7be82017-02-22 13:37:59 +0100365
Michal Vasko131120a2018-05-29 15:44:02 +0200366 if (!io_locked) {
367 /* SESSION IO LOCK */
368 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100369 if (ret < 1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200370 goto cleanup;
371 }
372 io_locked = 1;
373 }
374
Michal Vasko05ba9df2016-01-13 14:40:27 +0100375 /* read the message */
376 switch (session->version) {
377 case NC_VERSION_10:
Michal Vasko77367452021-02-16 16:32:18 +0100378 r = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &data);
379 if (r == -1) {
380 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200381 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100382 }
383
384 /* cut off the end tag */
Michal Vasko77367452021-02-16 16:32:18 +0100385 data[r - NC_VERSION_10_ENDTAG_LEN] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386 break;
387 case NC_VERSION_11:
388 while (1) {
Michal Vasko77367452021-02-16 16:32:18 +0100389 r = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
390 if (r == -1) {
391 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200392 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100393 }
Michal Vasko77367452021-02-16 16:32:18 +0100394 r = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
395 if (r == -1) {
396 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200397 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100398 }
399
400 if (!strcmp(chunk, "#\n")) {
401 /* end of chunked framing message */
402 free(chunk);
Michal Vasko77367452021-02-16 16:32:18 +0100403 if (!data) {
Michal Vasko05532772021-06-03 12:12:38 +0200404 ERR(session, "Invalid frame chunk delimiters.");
Michal Vasko77367452021-02-16 16:32:18 +0100405 ret = -2;
406 goto cleanup;
Michal Vasko79df3262016-07-13 13:42:17 +0200407 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100408 break;
409 }
410
411 /* convert string to the size of the following chunk */
412 chunk_len = strtoul(chunk, (char **)NULL, 10);
413 free(chunk);
414 if (!chunk_len) {
Michal Vasko05532772021-06-03 12:12:38 +0200415 ERR(session, "Invalid frame chunk size detected, fatal error.");
Michal Vasko77367452021-02-16 16:32:18 +0100416 ret = -2;
417 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100418 }
419
420 /* now we have size of next chunk, so read the chunk */
Michal Vasko77367452021-02-16 16:32:18 +0100421 r = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
422 if (r == -1) {
423 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200424 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100425 }
426
427 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko77367452021-02-16 16:32:18 +0100428 data = nc_realloc(data, len + chunk_len + 1);
429 if (!data) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100430 ERRMEM;
Michal Vasko77367452021-02-16 16:32:18 +0100431 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200432 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100433 }
Michal Vasko77367452021-02-16 16:32:18 +0100434 memcpy(data + len, chunk, chunk_len);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100435 len += chunk_len;
Michal Vasko77367452021-02-16 16:32:18 +0100436 data[len] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100437 free(chunk);
438 }
439
440 break;
441 }
Michal Vasko131120a2018-05-29 15:44:02 +0200442
443 /* SESSION IO UNLOCK */
444 assert(io_locked);
445 nc_session_io_unlock(session, __func__);
446 io_locked = 0;
447
Michal Vasko05532772021-06-03 12:12:38 +0200448 DBG(session, "Received message:\n%s\n", data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100449
Michal Vasko77367452021-02-16 16:32:18 +0100450 /* build an input structure, eats data */
451 if (ly_in_new_memory(data, msg)) {
452 ret = -1;
453 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100454 }
Michal Vasko77367452021-02-16 16:32:18 +0100455 data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456
Michal Vasko131120a2018-05-29 15:44:02 +0200457cleanup:
458 if (io_locked) {
Michal Vasko77367452021-02-16 16:32:18 +0100459 /* SESSION IO UNLOCK */
Michal Vasko131120a2018-05-29 15:44:02 +0200460 nc_session_io_unlock(session, __func__);
461 }
Michal Vasko77367452021-02-16 16:32:18 +0100462 free(data);
Michal Vasko131120a2018-05-29 15:44:02 +0200463 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100464}
465
Michal Vasko428087d2016-01-14 16:04:28 +0100466/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
467static int
Michal Vasko131120a2018-05-29 15:44:02 +0200468nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100469{
Radek Krejci5961c702016-07-15 09:15:18 +0200470 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100471 int ret = -2;
472 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100473
474 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200475 ERR(session, "Invalid session to poll.");
Michal Vasko428087d2016-01-14 16:04:28 +0100476 return -1;
477 }
478
479 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100480#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100481 case NC_TI_LIBSSH:
482 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200483 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100484 if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200485 ERR(session, "SSH channel poll error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100486 session->status = NC_STATUS_INVALID;
487 session->term_reason = NC_SESSION_TERM_OTHER;
488 return -1;
489 } else if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200490 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100491 session->status = NC_STATUS_INVALID;
492 session->term_reason = NC_SESSION_TERM_DROPPED;
493 return -1;
494 } else if (ret > 0) {
495 /* fake it */
496 ret = 1;
497 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100498 } else { /* ret == 0 */
499 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100500 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100501 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100502#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100503#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100504 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100505 ret = SSL_pending(session->ti.tls);
506 if (ret) {
507 /* some buffered TLS data available */
508 ret = 1;
509 fds.revents = POLLIN;
510 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100511 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100512
513 fds.fd = SSL_get_fd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100514#endif
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200515 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100516 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200517 case NC_TI_UNIX:
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200518 if (session->ti_type == NC_TI_FD) {
Michal Vasko428087d2016-01-14 16:04:28 +0100519 fds.fd = session->ti.fd.in;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200520 } else if (session->ti_type == NC_TI_UNIX) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200521 fds.fd = session->ti.unixsock.sock;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200522 }
Michal Vasko428087d2016-01-14 16:04:28 +0100523
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100524 fds.events = POLLIN;
525 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100526
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100527 sigfillset(&sigmask);
528 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200529 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100530 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100531
532 break;
533
534 default:
535 ERRINT;
536 return -1;
537 }
538
539 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
540 if (ret < 0) {
541 /* poll failed - something really bad happened, close the session */
Michal Vasko05532772021-06-03 12:12:38 +0200542 ERR(session, "poll error (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100543 session->status = NC_STATUS_INVALID;
544 session->term_reason = NC_SESSION_TERM_OTHER;
545 return -1;
546 } else { /* status > 0 */
547 /* in case of standard (non-libssh) poll, there still can be an error */
Michal Vasko428087d2016-01-14 16:04:28 +0100548 if (fds.revents & POLLERR) {
Michal Vasko05532772021-06-03 12:12:38 +0200549 ERR(session, "Communication channel error.");
Michal Vasko428087d2016-01-14 16:04:28 +0100550 session->status = NC_STATUS_INVALID;
551 session->term_reason = NC_SESSION_TERM_OTHER;
552 return -1;
553 }
Robin Jarryf732adc2020-05-15 11:18:38 +0200554 /* Some poll() implementations may return POLLHUP|POLLIN when the other
555 * side has closed but there is data left to read in the buffer. */
556 if ((fds.revents & POLLHUP) && !(fds.revents & POLLIN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200557 ERR(session, "Communication channel unexpectedly closed.");
Robin Jarryf732adc2020-05-15 11:18:38 +0200558 session->status = NC_STATUS_INVALID;
559 session->term_reason = NC_SESSION_TERM_DROPPED;
560 return -1;
561 }
Michal Vasko428087d2016-01-14 16:04:28 +0100562 }
563
564 return ret;
565}
566
Michal Vasko77367452021-02-16 16:32:18 +0100567int
568nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **msg)
Radek Krejci206fcd62015-10-07 15:42:48 +0200569{
Michal Vasko428087d2016-01-14 16:04:28 +0100570 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200571
Michal Vasko77367452021-02-16 16:32:18 +0100572 assert(msg);
573 *msg = NULL;
Radek Krejci206fcd62015-10-07 15:42:48 +0200574
Michal Vasko428087d2016-01-14 16:04:28 +0100575 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200576 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100577 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200578 }
579
Michal Vasko131120a2018-05-29 15:44:02 +0200580 /* SESSION IO LOCK */
581 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100582 if (ret < 1) {
583 return ret;
Michal Vasko131120a2018-05-29 15:44:02 +0200584 }
585
586 ret = nc_read_poll(session, io_timeout);
Michal Vasko77367452021-02-16 16:32:18 +0100587 if (ret < 1) {
588 /* timed out or error */
Michal Vasko131120a2018-05-29 15:44:02 +0200589
590 /* SESSION IO UNLOCK */
591 nc_session_io_unlock(session, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100592 return ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200593 }
594
Michal Vasko131120a2018-05-29 15:44:02 +0200595 /* SESSION IO LOCK passed down */
Michal Vasko77367452021-02-16 16:32:18 +0100596 return nc_read_msg_io(session, io_timeout, msg, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200597}
Radek Krejcife0b3472015-10-12 13:43:42 +0200598
Michal Vasko428087d2016-01-14 16:04:28 +0100599/* does not really log, only fatal errors */
600int
601nc_session_is_connected(struct nc_session *session)
602{
603 int ret;
604 struct pollfd fds;
605
606 switch (session->ti_type) {
607 case NC_TI_FD:
608 fds.fd = session->ti.fd.in;
609 break;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200610 case NC_TI_UNIX:
611 fds.fd = session->ti.unixsock.sock;
612 break;
Radek Krejci53691be2016-02-22 13:58:37 +0100613#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100614 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100615 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100616#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100617#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100618 case NC_TI_OPENSSL:
619 fds.fd = SSL_get_fd(session->ti.tls);
620 break;
621#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100622 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100623 return 0;
624 }
625
Michal Vasko840a8a62017-02-07 10:56:34 +0100626 if (fds.fd == -1) {
627 return 0;
628 }
629
Michal Vasko428087d2016-01-14 16:04:28 +0100630 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100631 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100632
633 errno = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200634 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR)) {}
Michal Vasko428087d2016-01-14 16:04:28 +0100635
636 if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200637 ERR(session, "poll failed (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100638 return 0;
639 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
640 return 0;
641 }
642
643 return 1;
644}
645
Radek Krejcife0b3472015-10-12 13:43:42 +0200646#define WRITE_BUFSIZE (2 * BUFFERSIZE)
647struct wclb_arg {
648 struct nc_session *session;
649 char buf[WRITE_BUFSIZE];
650 size_t len;
651};
652
Michal Vasko964e1732016-09-23 13:39:33 +0200653static int
Michal Vasko428087d2016-01-14 16:04:28 +0100654nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200655{
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200656 int c, fd, interrupted;
Michal Vasko964e1732016-09-23 13:39:33 +0200657 size_t written = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200658
Michal Vaskoe2357e92016-10-05 14:20:47 +0200659#ifdef NC_ENABLED_TLS
660 unsigned long e;
661#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200662
Michal Vasko428087d2016-01-14 16:04:28 +0100663 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
664 return -1;
665 }
666
667 /* prevent SIGPIPE this way */
668 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +0200669 ERR(session, "Communication socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100670 session->status = NC_STATUS_INVALID;
671 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100672 return -1;
673 }
674
Michal Vasko05532772021-06-03 12:12:38 +0200675 DBG(session, "Sending message:\n%.*s\n", count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200676
Michal Vasko81b33fb2016-09-26 14:57:36 +0200677 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200678 interrupted = 0;
Michal Vasko964e1732016-09-23 13:39:33 +0200679 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200680 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200681 case NC_TI_UNIX:
682 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
683 c = write(fd, (char *)(buf + written), count - written);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200684 if ((c < 0) && (errno == EAGAIN)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200685 c = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200686 } else if ((c < 0) && (errno == EINTR)) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200687 c = 0;
688 interrupted = 1;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200689 } else if (c < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200690 ERR(session, "socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200691 return -1;
692 }
693 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200694
Radek Krejci53691be2016-02-22 13:58:37 +0100695#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200696 case NC_TI_LIBSSH:
697 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
698 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200699 ERR(session, "SSH channel unexpectedly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200700 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200701 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko964e1732016-09-23 13:39:33 +0200702 }
703 session->status = NC_STATUS_INVALID;
704 session->term_reason = NC_SESSION_TERM_DROPPED;
705 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100706 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200707 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200708 if ((c == SSH_ERROR) || (c == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200709 ERR(session, "SSH channel write failed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200710 return -1;
711 }
712 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200713#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100714#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200715 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200716 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200717 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100718 char *reasons;
719
Michal Vasko964e1732016-09-23 13:39:33 +0200720 switch ((e = SSL_get_error(session->ti.tls, c))) {
721 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200722 ERR(session, "SSL connection was properly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200723 return -1;
724 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100725 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200726 c = 0;
727 break;
728 case SSL_ERROR_SYSCALL:
Michal Vasko05532772021-06-03 12:12:38 +0200729 ERR(session, "SSL socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200730 return -1;
731 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100732 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200733 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100734 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200735 return -1;
736 default:
Michal Vaskofdba4a32022-01-05 12:13:53 +0100737 ERR(session, "Unknown SSL error occurred (err code %d).", e);
Michal Vasko964e1732016-09-23 13:39:33 +0200738 return -1;
739 }
740 }
741 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200742#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200743 default:
744 ERRINT;
745 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200746 }
747
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200748 if ((c == 0) && !interrupted) {
Michal Vasko964e1732016-09-23 13:39:33 +0200749 /* we must wait */
750 usleep(NC_TIMEOUT_STEP);
751 }
752
753 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200754 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200755
Michal Vasko964e1732016-09-23 13:39:33 +0200756 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200757}
758
Michal Vasko428087d2016-01-14 16:04:28 +0100759static int
760nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100761{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100762 int ret = 0, c;
Claus Klein22091912020-01-20 13:45:47 +0100763 char chunksize[24];
Michal Vasko086311b2016-01-08 09:53:11 +0100764
Claus Klein22091912020-01-20 13:45:47 +0100765 // warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=]
Michal Vasko086311b2016-01-08 09:53:11 +0100766 if (session->version == NC_VERSION_11) {
767 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100768 ret = nc_write(session, chunksize, strlen(chunksize));
769 if (ret == -1) {
770 return -1;
771 }
Michal Vasko086311b2016-01-08 09:53:11 +0100772 }
Michal Vasko428087d2016-01-14 16:04:28 +0100773
774 c = nc_write(session, buf, count);
775 if (c == -1) {
776 return -1;
777 }
778 ret += c;
779
780 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100781}
782
Radek Krejcife0b3472015-10-12 13:43:42 +0200783static int
Michal Vasko428087d2016-01-14 16:04:28 +0100784nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200785{
Michal Vasko428087d2016-01-14 16:04:28 +0100786 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100787
Michal Vasko428087d2016-01-14 16:04:28 +0100788 if (session->version == NC_VERSION_11) {
789 ret = nc_write(session, "\n##\n", 4);
790 } else {
791 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200792 }
793
Michal Vasko428087d2016-01-14 16:04:28 +0100794 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200795}
796
Michal Vasko428087d2016-01-14 16:04:28 +0100797static int
798nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200799{
Michal Vasko428087d2016-01-14 16:04:28 +0100800 int ret = 0;
801
Radek Krejcife0b3472015-10-12 13:43:42 +0200802 /* flush current buffer */
803 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100804 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200805 warg->len = 0;
806 }
Michal Vasko428087d2016-01-14 16:04:28 +0100807
808 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200809}
810
811static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100812nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200813{
Michal Vasko428087d2016-01-14 16:04:28 +0100814 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100815 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200816 struct wclb_arg *warg = (struct wclb_arg *)arg;
817
818 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100819 c = nc_write_clb_flush(warg);
820 if (c == -1) {
821 return -1;
822 }
823 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200824
825 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100826 c = nc_write_endtag(warg->session);
827 if (c == -1) {
828 return -1;
829 }
830 ret += c;
831
832 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200833 }
834
835 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
836 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100837 c = nc_write_clb_flush(warg);
838 if (c == -1) {
839 return -1;
840 }
841 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200842 }
Michal Vasko428087d2016-01-14 16:04:28 +0100843
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200844 if (!xmlcontent && (count > WRITE_BUFSIZE)) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200845 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100846 c = nc_write_starttag_and_msg(warg->session, buf, count);
847 if (c == -1) {
848 return -1;
849 }
850 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200851 } else {
852 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100853 if (xmlcontent) {
854 for (l = 0; l < count; l++) {
855 if (warg->len + 5 >= WRITE_BUFSIZE) {
856 /* buffer is full */
857 c = nc_write_clb_flush(warg);
858 if (c == -1) {
859 return -1;
860 }
861 }
862
863 switch (((char *)buf)[l]) {
864 case '&':
865 ret += 5;
866 memcpy(&warg->buf[warg->len], "&amp;", 5);
867 warg->len += 5;
868 break;
869 case '<':
870 ret += 4;
871 memcpy(&warg->buf[warg->len], "&lt;", 4);
872 warg->len += 4;
873 break;
874 case '>':
875 /* not needed, just for readability */
876 ret += 4;
877 memcpy(&warg->buf[warg->len], "&gt;", 4);
878 warg->len += 4;
879 break;
880 default:
881 ret++;
882 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
883 warg->len++;
884 }
885 }
886 } else {
887 memcpy(&warg->buf[warg->len], buf, count);
888 warg->len += count; /* is <= WRITE_BUFSIZE */
889 ret += count;
890 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200891 }
892
Michal Vasko428087d2016-01-14 16:04:28 +0100893 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200894}
895
Radek Krejci047300e2016-03-08 16:46:58 +0100896static ssize_t
897nc_write_xmlclb(void *arg, const void *buf, size_t count)
898{
Michal Vasko77367452021-02-16 16:32:18 +0100899 ssize_t r;
Radek Krejci047300e2016-03-08 16:46:58 +0100900
Michal Vasko77367452021-02-16 16:32:18 +0100901 r = nc_write_clb(arg, buf, count, 0);
902 if (r == -1) {
903 return -1;
Michal Vasko08611b32016-12-05 13:30:37 +0100904 }
905
Michal Vasko77367452021-02-16 16:32:18 +0100906 /* always return what libyang expects, simply that all the characters were printed */
907 return count;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100908}
909
Michal Vasko131120a2018-05-29 15:44:02 +0200910/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
911NC_MSG_TYPE
912nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200913{
Radek Krejcid116db42016-01-08 15:36:30 +0100914 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +0200915 int count, ret;
Michal Vasko70bf2222021-10-26 10:45:15 +0200916 const char *attrs, *str;
Michal Vaskoff7286b2021-07-09 13:13:11 +0200917 struct lyd_node *op, *reply_envp, *node, *next;
Michal Vasko77367452021-02-16 16:32:18 +0100918 struct lyd_node_opaq *rpc_envp;
Radek Krejci93e80222016-10-03 13:34:25 +0200919 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100920 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +0100921 char *buf;
Radek Krejcid116db42016-01-08 15:36:30 +0100922 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200923 const char **capabilities;
Michal Vasko77367452021-02-16 16:32:18 +0100924 uint32_t *sid = NULL, i, wd = 0;
925 LY_ERR lyrc;
Radek Krejcife0b3472015-10-12 13:43:42 +0200926
Michal Vasko428087d2016-01-14 16:04:28 +0100927 assert(session);
928
929 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200930 ERR(session, "Invalid session to write to.");
Michal Vasko131120a2018-05-29 15:44:02 +0200931 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100932 }
933
Radek Krejcife0b3472015-10-12 13:43:42 +0200934 arg.session = session;
935 arg.len = 0;
936
Michal Vasko131120a2018-05-29 15:44:02 +0200937 /* SESSION IO LOCK */
938 ret = nc_session_io_lock(session, io_timeout, __func__);
939 if (ret < 0) {
940 return NC_MSG_ERROR;
941 } else if (!ret) {
942 return NC_MSG_WOULDBLOCK;
943 }
944
945 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +0200946
Radek Krejcife0b3472015-10-12 13:43:42 +0200947 switch (type) {
948 case NC_MSG_RPC:
Michal Vasko77367452021-02-16 16:32:18 +0100949 op = va_arg(ap, struct lyd_node *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200950 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100951
Michal Vasko70bf2222021-10-26 10:45:15 +0200952 /* <rpc> open */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200953 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%" PRIu64 "\"%s>",
Michal Vasko77367452021-02-16 16:32:18 +0100954 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +0100955 if (count == -1) {
956 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200957 ret = NC_MSG_ERROR;
958 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100959 }
Radek Krejci047300e2016-03-08 16:46:58 +0100960 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200961 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +0200962
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100963 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200964 /* <action> open */
965 str = "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">";
966 nc_write_clb((void *)&arg, str, strlen(str), 0);
967 }
968
969 /* rpc data */
Michal Vasko6b70b822022-01-21 08:39:16 +0100970 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, op, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_KEEPEMPTYCONT)) {
Michal Vasko131120a2018-05-29 15:44:02 +0200971 ret = NC_MSG_ERROR;
972 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +0200973 }
Michal Vasko70bf2222021-10-26 10:45:15 +0200974
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100975 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200976 /* <action> close */
977 str = "</action>";
978 nc_write_clb((void *)&arg, str, strlen(str), 0);
979 }
980
981 /* <rpc> close */
982 str = "</rpc>";
983 nc_write_clb((void *)&arg, str, strlen(str), 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200984
Michal Vasko2e6defd2016-10-07 15:48:15 +0200985 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +0200986 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100987
Radek Krejcife0b3472015-10-12 13:43:42 +0200988 case NC_MSG_REPLY:
Michal Vasko77367452021-02-16 16:32:18 +0100989 rpc_envp = va_arg(ap, struct lyd_node_opaq *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100990 reply = va_arg(ap, struct nc_server_reply *);
991
Michal Vasko77367452021-02-16 16:32:18 +0100992 if (!rpc_envp) {
993 /* can be NULL if replying with a malformed-message error */
994 nc_write_clb((void *)&arg, "<rpc-reply xmlns=\"" NC_NS_BASE "\">", 18 + strlen(NC_NS_BASE) + 2, 0);
995
996 assert(reply->type == NC_RPL_ERROR);
997 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_server_reply_error *)reply)->err, LYD_XML,
998 LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)) {
999 ret = NC_MSG_ERROR;
1000 goto cleanup;
1001 }
1002
1003 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1004 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001005 }
1006
Michal Vasko77367452021-02-16 16:32:18 +01001007 /* build a rpc-reply opaque node that can be simply printed */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001008 if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns,
1009 &reply_envp)) {
Michal Vasko77367452021-02-16 16:32:18 +01001010 ERRINT;
1011 ret = NC_MSG_ERROR;
1012 goto cleanup;
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001013 }
Michal Vasko77367452021-02-16 16:32:18 +01001014
Michal Vasko05ba9df2016-01-13 14:40:27 +01001015 switch (reply->type) {
1016 case NC_RPL_OK:
Michal Vasko77367452021-02-16 16:32:18 +01001017 if (lyd_new_opaq2(reply_envp, NULL, "ok", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, NULL)) {
1018 lyd_free_tree(reply_envp);
1019
1020 ERRINT;
1021 ret = NC_MSG_ERROR;
1022 goto cleanup;
Michal Vasko08611b32016-12-05 13:30:37 +01001023 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001024 break;
1025 case NC_RPL_DATA:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001026 switch (((struct nc_server_reply_data *)reply)->wd) {
Radek Krejci36dfdb32016-09-01 16:56:35 +02001027 case NC_WD_UNKNOWN:
1028 case NC_WD_EXPLICIT:
Michal Vasko77367452021-02-16 16:32:18 +01001029 wd = LYD_PRINT_WD_EXPLICIT;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001030 break;
1031 case NC_WD_TRIM:
Michal Vasko77367452021-02-16 16:32:18 +01001032 wd = LYD_PRINT_WD_TRIM;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001033 break;
1034 case NC_WD_ALL:
Michal Vasko77367452021-02-16 16:32:18 +01001035 wd = LYD_PRINT_WD_ALL;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001036 break;
1037 case NC_WD_ALL_TAG:
Michal Vasko77367452021-02-16 16:32:18 +01001038 wd = LYD_PRINT_WD_ALL_TAG;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001039 break;
1040 }
Michal Vasko77367452021-02-16 16:32:18 +01001041
1042 node = ((struct nc_server_reply_data *)reply)->data;
1043 assert(node->schema->nodetype & (LYS_RPC | LYS_ACTION));
Michal Vaskoff7286b2021-07-09 13:13:11 +02001044 LY_LIST_FOR_SAFE(lyd_child(node), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001045 /* temporary */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001046 lyd_insert_child(reply_envp, node);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001047 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001048 break;
1049 case NC_RPL_ERROR:
Michal Vasko77367452021-02-16 16:32:18 +01001050 /* temporary */
1051 lyd_insert_child(reply_envp, ((struct nc_server_reply_error *)reply)->err);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001052 break;
1053 default:
1054 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001055 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001056 ret = NC_MSG_ERROR;
1057 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001058 }
Michal Vasko77367452021-02-16 16:32:18 +01001059
1060 /* temporary */
1061 ((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr;
1062
1063 /* print */
1064 lyrc = lyd_print_clb(nc_write_xmlclb, (void *)&arg, reply_envp, LYD_XML, LYD_PRINT_SHRINK | wd);
1065 ((struct lyd_node_opaq *)reply_envp)->attr = NULL;
1066
1067 /* cleanup */
1068 switch (reply->type) {
1069 case NC_RPL_OK:
1070 /* just free everything */
1071 lyd_free_tree(reply_envp);
1072 break;
1073 case NC_RPL_DATA:
Michal Vaskoff7286b2021-07-09 13:13:11 +02001074 LY_LIST_FOR_SAFE(lyd_child(reply_envp), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001075 /* connect back to the reply structure */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001076 lyd_insert_child(((struct nc_server_reply_data *)reply)->data, node);
Michal Vasko77367452021-02-16 16:32:18 +01001077 }
1078 lyd_free_tree(reply_envp);
1079 break;
1080 case NC_RPL_ERROR:
1081 /* unlink from the data reply */
1082 lyd_unlink_tree(lyd_child(reply_envp));
1083 lyd_free_tree(reply_envp);
1084 break;
1085 default:
1086 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001087 }
Michal Vasko77367452021-02-16 16:32:18 +01001088
1089 if (lyrc) {
1090 ret = NC_MSG_ERROR;
1091 goto cleanup;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001092 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001093 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001094
Radek Krejcife0b3472015-10-12 13:43:42 +02001095 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001096 notif = va_arg(ap, struct nc_server_notif *);
1097
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001098 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF "\">", 21 + 47 + 2, 0);
Radek Krejci93e80222016-10-03 13:34:25 +02001099 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1100 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1101 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko77367452021-02-16 16:32:18 +01001102 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->ntf, LYD_XML, LYD_PRINT_SHRINK)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001103 ret = NC_MSG_ERROR;
1104 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001105 }
mohitarora24878b2962016-11-09 18:45:33 -05001106 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001107 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001108
Radek Krejcid116db42016-01-08 15:36:30 +01001109 case NC_MSG_HELLO:
1110 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001111 ret = NC_MSG_ERROR;
1112 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001113 }
1114 capabilities = va_arg(ap, const char **);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001115 sid = va_arg(ap, uint32_t *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001116
Radek Krejcid116db42016-01-08 15:36:30 +01001117 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001118 if (count == -1) {
1119 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001120 ret = NC_MSG_ERROR;
1121 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001122 }
Radek Krejci047300e2016-03-08 16:46:58 +01001123 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001124 free(buf);
1125 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001126 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1127 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1128 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001129 }
1130 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001131 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001132 if (count == -1) {
1133 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001134 ret = NC_MSG_ERROR;
1135 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001136 }
Radek Krejci047300e2016-03-08 16:46:58 +01001137 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001138 free(buf);
1139 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001140 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001141 }
Radek Krejcid116db42016-01-08 15:36:30 +01001142 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001143
Radek Krejcife0b3472015-10-12 13:43:42 +02001144 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001145 ret = NC_MSG_ERROR;
1146 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001147 }
1148
1149 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001150 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001151
Michal Vasko428087d2016-01-14 16:04:28 +01001152 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1153 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001154 ret = NC_MSG_ERROR;
1155 } else {
1156 /* specific message successfully sent */
1157 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001158 }
1159
Michal Vasko131120a2018-05-29 15:44:02 +02001160cleanup:
1161 va_end(ap);
1162 nc_session_io_unlock(session, __func__);
1163 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001164}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001165
1166void *
1167nc_realloc(void *ptr, size_t size)
1168{
1169 void *ret;
1170
1171 ret = realloc(ptr, size);
1172 if (!ret) {
1173 free(ptr);
1174 }
1175
1176 return ret;
1177}
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001178
1179struct passwd *
romanf6e32012023-04-24 15:51:26 +02001180nc_getpw(uid_t uid, const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001181{
1182 struct passwd *pwd = NULL;
Michal Vasko7e06ee52021-11-02 08:53:05 +01001183 long sys_size;
1184 int ret;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001185
1186 do {
Michal Vasko7e06ee52021-11-02 08:53:05 +01001187 if (!*buf_size) {
1188 /* learn suitable buffer size */
1189 sys_size = sysconf(_SC_GETPW_R_SIZE_MAX);
1190 *buf_size = (sys_size == -1) ? 2048 : sys_size;
1191 } else {
1192 /* enlarge buffer */
1193 *buf_size += 2048;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001194 }
1195
Michal Vasko7e06ee52021-11-02 08:53:05 +01001196 /* allocate some buffer */
1197 *buf = nc_realloc(*buf, *buf_size);
1198 if (!*buf) {
1199 ERRMEM;
1200 return NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001201 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001202
romanf6e32012023-04-24 15:51:26 +02001203 if (username) {
1204 ret = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd);
1205 } else {
1206 ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd);
1207 }
Michal Vasko7e06ee52021-11-02 08:53:05 +01001208 } while (ret && (ret == ERANGE));
1209
1210 if (ret) {
romanf6e32012023-04-24 15:51:26 +02001211 if (username) {
1212 ERR(NULL, "Retrieving username \"%s\" passwd entry failed (%s).", username, strerror(ret));
1213 } else {
1214 ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long)uid, strerror(ret));
1215 }
Michal Vasko7e06ee52021-11-02 08:53:05 +01001216 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001217 return pwd;
1218}