blob: 15123e5f4ddd4b2c096263149c0b04e201d8aea0 [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>
roman3f9b65c2023-06-05 14:26:58 +020025#include <stdint.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020026#include <stdlib.h>
27#include <string.h>
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +020028#include <sys/types.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010029#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020030#include <unistd.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020031
roman2eab4742023-06-06 10:00:26 +020032#ifdef NC_ENABLED_SSH_TLS
Michal Vasko964e1732016-09-23 13:39:33 +020033# include <openssl/err.h>
roman3f9b65c2023-06-05 14:26:58 +020034# include <openssl/ssl.h>
roman2eab4742023-06-06 10:00:26 +020035#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko964e1732016-09-23 13:39:33 +020036
Radek Krejci206fcd62015-10-07 15:42:48 +020037#include <libyang/libyang.h>
38
Michal Vasko9e8ac262020-04-07 13:06:45 +020039#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020040#include "config.h"
41#include "log_p.h"
42#include "messages_p.h"
43#include "netconf.h"
44#include "session.h"
45#include "session_p.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020046
Michal Vasko8fe604c2020-02-10 15:25:04 +010047const char *nc_msgtype2str[] = {
48 "error",
49 "would block",
50 "no message",
51 "hello message",
52 "bad hello message",
53 "RPC message",
54 "rpc-reply message",
55 "rpc-reply message with wrong ID",
56 "notification message",
57};
58
Radek Krejcife0b3472015-10-12 13:43:42 +020059#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020060
roman2eab4742023-06-06 10:00:26 +020061#ifdef NC_ENABLED_SSH_TLS
Michal Vasko90a87d92018-12-10 15:53:44 +010062
63static char *
64nc_ssl_error_get_reasons(void)
65{
66 unsigned int e;
67 int reason_size, reason_len;
68 char *reasons = NULL;
69
70 reason_size = 1;
71 reason_len = 0;
72 while ((e = ERR_get_error())) {
73 if (reason_len) {
74 /* add "; " */
75 reason_size += 2;
76 reasons = nc_realloc(reasons, reason_size);
roman3a95bb22023-10-26 11:07:17 +020077 NC_CHECK_ERRMEM_RET(!reasons, NULL);
Michal Vasko90a87d92018-12-10 15:53:44 +010078 reason_len += sprintf(reasons + reason_len, "; ");
79 }
80 reason_size += strlen(ERR_reason_error_string(e));
81 reasons = nc_realloc(reasons, reason_size);
roman3a95bb22023-10-26 11:07:17 +020082 NC_CHECK_ERRMEM_RET(!reasons, NULL);
Rosen Peneve38d7ef2019-07-15 18:18:03 -070083 reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e));
Michal Vasko90a87d92018-12-10 15:53:44 +010084 }
85
86 return reasons;
87}
88
roman2eab4742023-06-06 10:00:26 +020089#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko90a87d92018-12-10 15:53:44 +010090
Radek Krejci206fcd62015-10-07 15:42:48 +020091static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010092nc_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 +020093{
Michal Vasko81b33fb2016-09-26 14:57:36 +020094 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010095 ssize_t r = -1;
Robin Jarry7de4b8e2019-10-14 21:46:00 +020096 int fd, interrupted;
roman6ece9c52022-06-22 09:29:17 +020097 struct timespec ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020098
99 assert(session);
100 assert(buf);
101
Michal Vasko428087d2016-01-14 16:04:28 +0100102 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
103 return -1;
104 }
105
Radek Krejci206fcd62015-10-07 15:42:48 +0200106 if (!count) {
107 return 0;
108 }
109
Michal Vaskod8a74192023-02-06 15:51:50 +0100110 nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +0200111 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200112 interrupted = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100113 switch (session->ti_type) {
114 case NC_TI_NONE:
115 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100116
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100117 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200118 case NC_TI_UNIX:
119 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100120 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200121 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200122 if (r < 0) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200123 if (errno == EAGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100124 r = 0;
125 break;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200126 } else if (errno == EINTR) {
127 r = 0;
128 interrupted = 1;
129 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200130 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200131 ERR(session, "Reading from file descriptor (%d) failed (%s).", fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100132 session->status = NC_STATUS_INVALID;
133 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200134 return -1;
135 }
136 } else if (r == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200137 ERR(session, "Communication file descriptor (%d) unexpectedly closed.", fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100138 session->status = NC_STATUS_INVALID;
139 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200140 return -1;
141 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100142 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200143
roman2eab4742023-06-06 10:00:26 +0200144#ifdef NC_ENABLED_SSH_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100145 case NC_TI_LIBSSH:
146 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200147 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200148 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100149 r = 0;
150 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200151 } else if (r == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200152 ERR(session, "Reading from the SSH channel failed (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100153 session->status = NC_STATUS_INVALID;
154 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200155 return -1;
156 } else if (r == 0) {
157 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200158 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100159 session->status = NC_STATUS_INVALID;
160 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200161 return -1;
162 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100163 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200164 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100165 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200166
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 Vaskofdba4a32022-01-05 12:13:53 +0100198 ERR(session, "Unknown SSL error occurred (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;
roman2eab4742023-06-06 10:00:26 +0200205#endif /* NC_ENABLED_SSH_TLS */
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 Vaskod8a74192023-02-06 15:51:50 +0100213 if ((nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) || (nc_timeouttime_cur_diff(ts_act_timeout) < 1)) {
214 if (nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200215 ERR(session, "Inactive read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100216 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200217 ERR(session, "Active read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100218 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100219 session->status = NC_STATUS_INVALID;
220 session->term_reason = NC_SESSION_TERM_OTHER;
221 return -1;
222 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100223 } else {
224 /* something read */
225 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100226
227 /* reset inactive timeout */
Michal Vaskod8a74192023-02-06 15:51:50 +0100228 nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100229 }
230
Michal Vasko81b33fb2016-09-26 14:57:36 +0200231 } while (readd < count);
232 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200233
Michal Vasko81b33fb2016-09-26 14:57:36 +0200234 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200235}
236
237static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100238nc_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 +0200239{
240 ssize_t r;
241
242 assert(session);
243 assert(chunk);
244
245 if (!len) {
246 return 0;
247 }
248
Michal Vasko4eb3c312016-03-01 14:09:37 +0100249 *chunk = malloc((len + 1) * sizeof **chunk);
roman3a95bb22023-10-26 11:07:17 +0200250 NC_CHECK_ERRMEM_RET(!*chunk, -1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200251
Michal Vasko36c7be82017-02-22 13:37:59 +0100252 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200253 if (r <= 0) {
254 free(*chunk);
255 return -1;
256 }
257
258 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200259 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200260
261 return r;
262}
263
264static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100265nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200266 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200267{
268 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200269 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200270
271 assert(session);
272 assert(endtag);
273
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200274 if (limit && (limit < BUFFERSIZE)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200275 size = limit;
276 } else {
277 size = BUFFERSIZE;
278 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100279 chunk = malloc((size + 1) * sizeof *chunk);
roman3a95bb22023-10-26 11:07:17 +0200280 NC_CHECK_ERRMEM_RET(!chunk, -1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200281
282 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100283 while (1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200284 if (limit && (count == limit)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200285 free(chunk);
Michal Vasko05532772021-06-03 12:12:38 +0200286 WRN(session, "Reading limit (%d) reached.", limit);
287 ERR(session, "Invalid input data (missing \"%s\" sequence).", endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200288 return -1;
289 }
290
291 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200292 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200293 /* get more memory */
294 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200295 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
roman3a95bb22023-10-26 11:07:17 +0200296 NC_CHECK_ERRMEM_RET(!chunk, -1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200297 }
298
299 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200300 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
301 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200302 free(chunk);
303 return -1;
304 }
305
David Sedlákfedbc792018-07-04 11:07:07 +0200306 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200307
David Sedlákfedbc792018-07-04 11:07:07 +0200308 for (i = len - matched; i > 0; i--) {
309 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
310 /*part of endtag found */
311 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200312 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200313 } else {
314 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200315 }
316 }
David Sedlákfedbc792018-07-04 11:07:07 +0200317
318 /* whole endtag found */
319 if (matched == len) {
320 break;
321 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200322 }
323
324 /* terminating null byte */
325 chunk[count] = 0;
326
327 if (result) {
328 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200329 } else {
330 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200331 }
332 return count;
333}
334
Michal Vasko77367452021-02-16 16:32:18 +0100335int
336nc_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 +0100337{
Michal Vasko77367452021-02-16 16:32:18 +0100338 int ret = 1, r, io_locked = passing_io_lock;
339 char *data = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100340 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100341 /* use timeout in milliseconds instead seconds */
342 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
343 struct timespec ts_act_timeout;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100344
Michal Vasko77367452021-02-16 16:32:18 +0100345 assert(session && msg);
346 *msg = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100347
348 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200349 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100350 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200351 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100352 }
353
Michal Vaskod8a74192023-02-06 15:51:50 +0100354 nc_timeouttime_get(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
Michal Vasko36c7be82017-02-22 13:37:59 +0100355
Michal Vasko131120a2018-05-29 15:44:02 +0200356 if (!io_locked) {
357 /* SESSION IO LOCK */
358 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100359 if (ret < 1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200360 goto cleanup;
361 }
362 io_locked = 1;
363 }
364
Michal Vasko05ba9df2016-01-13 14:40:27 +0100365 /* read the message */
366 switch (session->version) {
367 case NC_VERSION_10:
Michal Vasko77367452021-02-16 16:32:18 +0100368 r = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &data);
369 if (r == -1) {
370 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200371 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100372 }
373
374 /* cut off the end tag */
Michal Vasko77367452021-02-16 16:32:18 +0100375 data[r - NC_VERSION_10_ENDTAG_LEN] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100376 break;
377 case NC_VERSION_11:
378 while (1) {
Michal Vasko77367452021-02-16 16:32:18 +0100379 r = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
380 if (r == -1) {
381 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200382 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100383 }
Michal Vasko77367452021-02-16 16:32:18 +0100384 r = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
385 if (r == -1) {
386 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200387 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100388 }
389
390 if (!strcmp(chunk, "#\n")) {
391 /* end of chunked framing message */
392 free(chunk);
Michal Vasko77367452021-02-16 16:32:18 +0100393 if (!data) {
Michal Vasko05532772021-06-03 12:12:38 +0200394 ERR(session, "Invalid frame chunk delimiters.");
Michal Vasko77367452021-02-16 16:32:18 +0100395 ret = -2;
396 goto cleanup;
Michal Vasko79df3262016-07-13 13:42:17 +0200397 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100398 break;
399 }
400
401 /* convert string to the size of the following chunk */
402 chunk_len = strtoul(chunk, (char **)NULL, 10);
403 free(chunk);
404 if (!chunk_len) {
Michal Vasko05532772021-06-03 12:12:38 +0200405 ERR(session, "Invalid frame chunk size detected, fatal error.");
Michal Vasko77367452021-02-16 16:32:18 +0100406 ret = -2;
407 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100408 }
409
410 /* now we have size of next chunk, so read the chunk */
Michal Vasko77367452021-02-16 16:32:18 +0100411 r = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
412 if (r == -1) {
413 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200414 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100415 }
416
417 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko77367452021-02-16 16:32:18 +0100418 data = nc_realloc(data, len + chunk_len + 1);
roman3a95bb22023-10-26 11:07:17 +0200419 NC_CHECK_ERRMEM_GOTO(!data, ret = -1, cleanup);
Michal Vasko77367452021-02-16 16:32:18 +0100420 memcpy(data + len, chunk, chunk_len);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100421 len += chunk_len;
Michal Vasko77367452021-02-16 16:32:18 +0100422 data[len] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100423 free(chunk);
424 }
425
426 break;
427 }
Michal Vasko131120a2018-05-29 15:44:02 +0200428
429 /* SESSION IO UNLOCK */
430 assert(io_locked);
431 nc_session_io_unlock(session, __func__);
432 io_locked = 0;
433
Michal Vasko05532772021-06-03 12:12:38 +0200434 DBG(session, "Received message:\n%s\n", data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100435
Michal Vasko77367452021-02-16 16:32:18 +0100436 /* build an input structure, eats data */
437 if (ly_in_new_memory(data, msg)) {
438 ret = -1;
439 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100440 }
Michal Vasko77367452021-02-16 16:32:18 +0100441 data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100442
Michal Vasko131120a2018-05-29 15:44:02 +0200443cleanup:
444 if (io_locked) {
Michal Vasko77367452021-02-16 16:32:18 +0100445 /* SESSION IO UNLOCK */
Michal Vasko131120a2018-05-29 15:44:02 +0200446 nc_session_io_unlock(session, __func__);
447 }
Michal Vasko77367452021-02-16 16:32:18 +0100448 free(data);
Michal Vasko131120a2018-05-29 15:44:02 +0200449 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100450}
451
Michal Vasko428087d2016-01-14 16:04:28 +0100452/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
453static int
Michal Vasko131120a2018-05-29 15:44:02 +0200454nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100455{
Radek Krejci5961c702016-07-15 09:15:18 +0200456 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100457 int ret = -2;
458 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100459
460 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200461 ERR(session, "Invalid session to poll.");
Michal Vasko428087d2016-01-14 16:04:28 +0100462 return -1;
463 }
464
465 switch (session->ti_type) {
roman2eab4742023-06-06 10:00:26 +0200466#ifdef NC_ENABLED_SSH_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100467 case NC_TI_LIBSSH:
468 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200469 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100470 if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200471 ERR(session, "SSH channel poll error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100472 session->status = NC_STATUS_INVALID;
473 session->term_reason = NC_SESSION_TERM_OTHER;
474 return -1;
475 } else if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200476 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100477 session->status = NC_STATUS_INVALID;
478 session->term_reason = NC_SESSION_TERM_DROPPED;
479 return -1;
480 } else if (ret > 0) {
481 /* fake it */
482 ret = 1;
483 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100484 } else { /* ret == 0 */
485 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100486 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100487 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100488 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100489 ret = SSL_pending(session->ti.tls);
490 if (ret) {
491 /* some buffered TLS data available */
492 ret = 1;
493 fds.revents = POLLIN;
494 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100495 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100496
497 fds.fd = SSL_get_fd(session->ti.tls);
roman2eab4742023-06-06 10:00:26 +0200498#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200499 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100500 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200501 case NC_TI_UNIX:
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200502 if (session->ti_type == NC_TI_FD) {
Michal Vasko428087d2016-01-14 16:04:28 +0100503 fds.fd = session->ti.fd.in;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200504 } else if (session->ti_type == NC_TI_UNIX) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200505 fds.fd = session->ti.unixsock.sock;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200506 }
Michal Vasko428087d2016-01-14 16:04:28 +0100507
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100508 fds.events = POLLIN;
509 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100510
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100511 sigfillset(&sigmask);
512 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200513 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100514 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100515
516 break;
517
518 default:
519 ERRINT;
520 return -1;
521 }
522
523 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
524 if (ret < 0) {
525 /* poll failed - something really bad happened, close the session */
Michal Vasko05532772021-06-03 12:12:38 +0200526 ERR(session, "poll error (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100527 session->status = NC_STATUS_INVALID;
528 session->term_reason = NC_SESSION_TERM_OTHER;
529 return -1;
530 } else { /* status > 0 */
531 /* in case of standard (non-libssh) poll, there still can be an error */
Michal Vasko428087d2016-01-14 16:04:28 +0100532 if (fds.revents & POLLERR) {
Michal Vasko05532772021-06-03 12:12:38 +0200533 ERR(session, "Communication channel error.");
Michal Vasko428087d2016-01-14 16:04:28 +0100534 session->status = NC_STATUS_INVALID;
535 session->term_reason = NC_SESSION_TERM_OTHER;
536 return -1;
537 }
Robin Jarryf732adc2020-05-15 11:18:38 +0200538 /* Some poll() implementations may return POLLHUP|POLLIN when the other
539 * side has closed but there is data left to read in the buffer. */
540 if ((fds.revents & POLLHUP) && !(fds.revents & POLLIN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200541 ERR(session, "Communication channel unexpectedly closed.");
Robin Jarryf732adc2020-05-15 11:18:38 +0200542 session->status = NC_STATUS_INVALID;
543 session->term_reason = NC_SESSION_TERM_DROPPED;
544 return -1;
545 }
Michal Vasko428087d2016-01-14 16:04:28 +0100546 }
547
548 return ret;
549}
550
Michal Vasko77367452021-02-16 16:32:18 +0100551int
552nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **msg)
Radek Krejci206fcd62015-10-07 15:42:48 +0200553{
Michal Vasko428087d2016-01-14 16:04:28 +0100554 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200555
Michal Vasko77367452021-02-16 16:32:18 +0100556 assert(msg);
557 *msg = NULL;
Radek Krejci206fcd62015-10-07 15:42:48 +0200558
Michal Vasko428087d2016-01-14 16:04:28 +0100559 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200560 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100561 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200562 }
563
Michal Vasko131120a2018-05-29 15:44:02 +0200564 /* SESSION IO LOCK */
565 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100566 if (ret < 1) {
567 return ret;
Michal Vasko131120a2018-05-29 15:44:02 +0200568 }
569
570 ret = nc_read_poll(session, io_timeout);
Michal Vasko77367452021-02-16 16:32:18 +0100571 if (ret < 1) {
572 /* timed out or error */
Michal Vasko131120a2018-05-29 15:44:02 +0200573
574 /* SESSION IO UNLOCK */
575 nc_session_io_unlock(session, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100576 return ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200577 }
578
Michal Vasko131120a2018-05-29 15:44:02 +0200579 /* SESSION IO LOCK passed down */
Michal Vasko77367452021-02-16 16:32:18 +0100580 return nc_read_msg_io(session, io_timeout, msg, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200581}
Radek Krejcife0b3472015-10-12 13:43:42 +0200582
Michal Vasko428087d2016-01-14 16:04:28 +0100583/* does not really log, only fatal errors */
584int
romane5675b12024-03-05 14:26:23 +0100585nc_session_is_connected(const struct nc_session *session)
Michal Vasko428087d2016-01-14 16:04:28 +0100586{
587 int ret;
588 struct pollfd fds;
589
590 switch (session->ti_type) {
591 case NC_TI_FD:
592 fds.fd = session->ti.fd.in;
593 break;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200594 case NC_TI_UNIX:
595 fds.fd = session->ti.unixsock.sock;
596 break;
roman2eab4742023-06-06 10:00:26 +0200597#ifdef NC_ENABLED_SSH_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100598 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100599 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100600 case NC_TI_OPENSSL:
601 fds.fd = SSL_get_fd(session->ti.tls);
602 break;
roman2eab4742023-06-06 10:00:26 +0200603#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskof945da52018-02-15 08:45:13 +0100604 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100605 return 0;
606 }
607
Michal Vasko840a8a62017-02-07 10:56:34 +0100608 if (fds.fd == -1) {
609 return 0;
610 }
611
Michal Vasko428087d2016-01-14 16:04:28 +0100612 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100613 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100614
615 errno = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200616 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR)) {}
Michal Vasko428087d2016-01-14 16:04:28 +0100617
618 if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200619 ERR(session, "poll failed (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100620 return 0;
621 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
622 return 0;
623 }
624
625 return 1;
626}
627
Radek Krejcife0b3472015-10-12 13:43:42 +0200628#define WRITE_BUFSIZE (2 * BUFFERSIZE)
629struct wclb_arg {
630 struct nc_session *session;
631 char buf[WRITE_BUFSIZE];
632 size_t len;
633};
634
Michal Vasko964e1732016-09-23 13:39:33 +0200635static int
Michal Vasko428087d2016-01-14 16:04:28 +0100636nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200637{
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200638 int c, fd, interrupted;
Michal Vasko964e1732016-09-23 13:39:33 +0200639 size_t written = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200640
roman2eab4742023-06-06 10:00:26 +0200641#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoe2357e92016-10-05 14:20:47 +0200642 unsigned long e;
roman2eab4742023-06-06 10:00:26 +0200643#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko964e1732016-09-23 13:39:33 +0200644
Michal Vasko428087d2016-01-14 16:04:28 +0100645 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
646 return -1;
647 }
648
649 /* prevent SIGPIPE this way */
650 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +0200651 ERR(session, "Communication socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100652 session->status = NC_STATUS_INVALID;
653 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100654 return -1;
655 }
656
Michal Vasko05532772021-06-03 12:12:38 +0200657 DBG(session, "Sending message:\n%.*s\n", count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200658
Michal Vasko81b33fb2016-09-26 14:57:36 +0200659 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200660 interrupted = 0;
Michal Vasko964e1732016-09-23 13:39:33 +0200661 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200662 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200663 case NC_TI_UNIX:
664 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
665 c = write(fd, (char *)(buf + written), count - written);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200666 if ((c < 0) && (errno == EAGAIN)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200667 c = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200668 } else if ((c < 0) && (errno == EINTR)) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200669 c = 0;
670 interrupted = 1;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200671 } else if (c < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200672 ERR(session, "socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200673 return -1;
674 }
675 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200676
roman2eab4742023-06-06 10:00:26 +0200677#ifdef NC_ENABLED_SSH_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200678 case NC_TI_LIBSSH:
679 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
680 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200681 ERR(session, "SSH channel unexpectedly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200682 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200683 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko964e1732016-09-23 13:39:33 +0200684 }
685 session->status = NC_STATUS_INVALID;
686 session->term_reason = NC_SESSION_TERM_DROPPED;
687 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100688 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200689 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200690 if ((c == SSH_ERROR) || (c == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200691 ERR(session, "SSH channel write failed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200692 return -1;
693 }
694 break;
Michal Vasko964e1732016-09-23 13:39:33 +0200695 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200696 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200697 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100698 char *reasons;
699
Michal Vasko964e1732016-09-23 13:39:33 +0200700 switch ((e = SSL_get_error(session->ti.tls, c))) {
701 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200702 ERR(session, "SSL connection was properly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200703 return -1;
704 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100705 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200706 c = 0;
707 break;
708 case SSL_ERROR_SYSCALL:
Michal Vasko05532772021-06-03 12:12:38 +0200709 ERR(session, "SSL socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200710 return -1;
711 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100712 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200713 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100714 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200715 return -1;
716 default:
Michal Vaskofdba4a32022-01-05 12:13:53 +0100717 ERR(session, "Unknown SSL error occurred (err code %d).", e);
Michal Vasko964e1732016-09-23 13:39:33 +0200718 return -1;
719 }
720 }
721 break;
roman2eab4742023-06-06 10:00:26 +0200722#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko339eea82016-09-29 11:42:36 +0200723 default:
724 ERRINT;
725 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200726 }
727
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200728 if ((c == 0) && !interrupted) {
Michal Vasko964e1732016-09-23 13:39:33 +0200729 /* we must wait */
730 usleep(NC_TIMEOUT_STEP);
731 }
732
733 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200734 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200735
Michal Vasko964e1732016-09-23 13:39:33 +0200736 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200737}
738
Michal Vasko428087d2016-01-14 16:04:28 +0100739static int
740nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100741{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100742 int ret = 0, c;
Claus Klein22091912020-01-20 13:45:47 +0100743 char chunksize[24];
Michal Vasko086311b2016-01-08 09:53:11 +0100744
Claus Klein22091912020-01-20 13:45:47 +0100745 // warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=]
Michal Vasko086311b2016-01-08 09:53:11 +0100746 if (session->version == NC_VERSION_11) {
747 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100748 ret = nc_write(session, chunksize, strlen(chunksize));
749 if (ret == -1) {
750 return -1;
751 }
Michal Vasko086311b2016-01-08 09:53:11 +0100752 }
Michal Vasko428087d2016-01-14 16:04:28 +0100753
754 c = nc_write(session, buf, count);
755 if (c == -1) {
756 return -1;
757 }
758 ret += c;
759
760 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100761}
762
Radek Krejcife0b3472015-10-12 13:43:42 +0200763static int
Michal Vasko428087d2016-01-14 16:04:28 +0100764nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200765{
Michal Vasko428087d2016-01-14 16:04:28 +0100766 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100767
Michal Vasko428087d2016-01-14 16:04:28 +0100768 if (session->version == NC_VERSION_11) {
769 ret = nc_write(session, "\n##\n", 4);
770 } else {
771 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200772 }
773
Michal Vasko428087d2016-01-14 16:04:28 +0100774 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200775}
776
Michal Vasko428087d2016-01-14 16:04:28 +0100777static int
778nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200779{
Michal Vasko428087d2016-01-14 16:04:28 +0100780 int ret = 0;
781
Radek Krejcife0b3472015-10-12 13:43:42 +0200782 /* flush current buffer */
783 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100784 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200785 warg->len = 0;
786 }
Michal Vasko428087d2016-01-14 16:04:28 +0100787
788 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200789}
790
791static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100792nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200793{
Michal Vasko428087d2016-01-14 16:04:28 +0100794 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100795 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200796 struct wclb_arg *warg = (struct wclb_arg *)arg;
797
798 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100799 c = nc_write_clb_flush(warg);
800 if (c == -1) {
801 return -1;
802 }
803 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200804
805 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100806 c = nc_write_endtag(warg->session);
807 if (c == -1) {
808 return -1;
809 }
810 ret += c;
811
812 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200813 }
814
815 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
816 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100817 c = nc_write_clb_flush(warg);
818 if (c == -1) {
819 return -1;
820 }
821 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200822 }
Michal Vasko428087d2016-01-14 16:04:28 +0100823
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200824 if (!xmlcontent && (count > WRITE_BUFSIZE)) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200825 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100826 c = nc_write_starttag_and_msg(warg->session, buf, count);
827 if (c == -1) {
828 return -1;
829 }
830 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200831 } else {
832 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100833 if (xmlcontent) {
834 for (l = 0; l < count; l++) {
835 if (warg->len + 5 >= WRITE_BUFSIZE) {
836 /* buffer is full */
837 c = nc_write_clb_flush(warg);
838 if (c == -1) {
839 return -1;
840 }
841 }
842
843 switch (((char *)buf)[l]) {
844 case '&':
845 ret += 5;
846 memcpy(&warg->buf[warg->len], "&amp;", 5);
847 warg->len += 5;
848 break;
849 case '<':
850 ret += 4;
851 memcpy(&warg->buf[warg->len], "&lt;", 4);
852 warg->len += 4;
853 break;
854 case '>':
855 /* not needed, just for readability */
856 ret += 4;
857 memcpy(&warg->buf[warg->len], "&gt;", 4);
858 warg->len += 4;
859 break;
860 default:
861 ret++;
862 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
863 warg->len++;
864 }
865 }
866 } else {
867 memcpy(&warg->buf[warg->len], buf, count);
868 warg->len += count; /* is <= WRITE_BUFSIZE */
869 ret += count;
870 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200871 }
872
Michal Vasko428087d2016-01-14 16:04:28 +0100873 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200874}
875
Radek Krejci047300e2016-03-08 16:46:58 +0100876static ssize_t
877nc_write_xmlclb(void *arg, const void *buf, size_t count)
878{
Michal Vasko77367452021-02-16 16:32:18 +0100879 ssize_t r;
Radek Krejci047300e2016-03-08 16:46:58 +0100880
Michal Vasko77367452021-02-16 16:32:18 +0100881 r = nc_write_clb(arg, buf, count, 0);
882 if (r == -1) {
883 return -1;
Michal Vasko08611b32016-12-05 13:30:37 +0100884 }
885
Michal Vasko77367452021-02-16 16:32:18 +0100886 /* always return what libyang expects, simply that all the characters were printed */
887 return count;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100888}
889
Michal Vasko131120a2018-05-29 15:44:02 +0200890/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
891NC_MSG_TYPE
892nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200893{
Radek Krejcid116db42016-01-08 15:36:30 +0100894 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +0200895 int count, ret;
Michal Vasko70bf2222021-10-26 10:45:15 +0200896 const char *attrs, *str;
Michal Vaskoff7286b2021-07-09 13:13:11 +0200897 struct lyd_node *op, *reply_envp, *node, *next;
Michal Vasko77367452021-02-16 16:32:18 +0100898 struct lyd_node_opaq *rpc_envp;
Radek Krejci93e80222016-10-03 13:34:25 +0200899 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100900 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +0100901 char *buf;
Radek Krejcid116db42016-01-08 15:36:30 +0100902 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200903 const char **capabilities;
Michal Vasko77367452021-02-16 16:32:18 +0100904 uint32_t *sid = NULL, i, wd = 0;
905 LY_ERR lyrc;
Radek Krejcife0b3472015-10-12 13:43:42 +0200906
Michal Vasko428087d2016-01-14 16:04:28 +0100907 assert(session);
908
909 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200910 ERR(session, "Invalid session to write to.");
Michal Vasko131120a2018-05-29 15:44:02 +0200911 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100912 }
913
Radek Krejcife0b3472015-10-12 13:43:42 +0200914 arg.session = session;
915 arg.len = 0;
916
Michal Vasko131120a2018-05-29 15:44:02 +0200917 /* SESSION IO LOCK */
918 ret = nc_session_io_lock(session, io_timeout, __func__);
919 if (ret < 0) {
920 return NC_MSG_ERROR;
921 } else if (!ret) {
922 return NC_MSG_WOULDBLOCK;
923 }
924
925 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +0200926
Radek Krejcife0b3472015-10-12 13:43:42 +0200927 switch (type) {
928 case NC_MSG_RPC:
Michal Vasko77367452021-02-16 16:32:18 +0100929 op = va_arg(ap, struct lyd_node *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200930 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100931
Michal Vasko70bf2222021-10-26 10:45:15 +0200932 /* <rpc> open */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200933 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%" PRIu64 "\"%s>",
Michal Vasko77367452021-02-16 16:32:18 +0100934 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
roman3a95bb22023-10-26 11:07:17 +0200935 NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup);
Radek Krejci047300e2016-03-08 16:46:58 +0100936 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200937 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +0200938
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100939 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200940 /* <action> open */
941 str = "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">";
942 nc_write_clb((void *)&arg, str, strlen(str), 0);
943 }
944
945 /* rpc data */
Michal Vasko6b70b822022-01-21 08:39:16 +0100946 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 +0200947 ret = NC_MSG_ERROR;
948 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +0200949 }
Michal Vasko70bf2222021-10-26 10:45:15 +0200950
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100951 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200952 /* <action> close */
953 str = "</action>";
954 nc_write_clb((void *)&arg, str, strlen(str), 0);
955 }
956
957 /* <rpc> close */
958 str = "</rpc>";
959 nc_write_clb((void *)&arg, str, strlen(str), 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200960
Michal Vasko2e6defd2016-10-07 15:48:15 +0200961 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +0200962 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100963
Radek Krejcife0b3472015-10-12 13:43:42 +0200964 case NC_MSG_REPLY:
Michal Vasko77367452021-02-16 16:32:18 +0100965 rpc_envp = va_arg(ap, struct lyd_node_opaq *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100966 reply = va_arg(ap, struct nc_server_reply *);
967
Michal Vasko77367452021-02-16 16:32:18 +0100968 if (!rpc_envp) {
969 /* can be NULL if replying with a malformed-message error */
970 nc_write_clb((void *)&arg, "<rpc-reply xmlns=\"" NC_NS_BASE "\">", 18 + strlen(NC_NS_BASE) + 2, 0);
971
972 assert(reply->type == NC_RPL_ERROR);
973 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_server_reply_error *)reply)->err, LYD_XML,
974 LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)) {
975 ret = NC_MSG_ERROR;
976 goto cleanup;
977 }
978
979 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
980 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +0100981 }
982
Michal Vasko77367452021-02-16 16:32:18 +0100983 /* build a rpc-reply opaque node that can be simply printed */
Michal Vaskoff7286b2021-07-09 13:13:11 +0200984 if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns,
985 &reply_envp)) {
Michal Vasko77367452021-02-16 16:32:18 +0100986 ERRINT;
987 ret = NC_MSG_ERROR;
988 goto cleanup;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100989 }
Michal Vasko77367452021-02-16 16:32:18 +0100990
Michal Vasko05ba9df2016-01-13 14:40:27 +0100991 switch (reply->type) {
992 case NC_RPL_OK:
Michal Vasko77367452021-02-16 16:32:18 +0100993 if (lyd_new_opaq2(reply_envp, NULL, "ok", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, NULL)) {
994 lyd_free_tree(reply_envp);
995
996 ERRINT;
997 ret = NC_MSG_ERROR;
998 goto cleanup;
Michal Vasko08611b32016-12-05 13:30:37 +0100999 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001000 break;
1001 case NC_RPL_DATA:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001002 switch (((struct nc_server_reply_data *)reply)->wd) {
Radek Krejci36dfdb32016-09-01 16:56:35 +02001003 case NC_WD_UNKNOWN:
1004 case NC_WD_EXPLICIT:
Michal Vasko77367452021-02-16 16:32:18 +01001005 wd = LYD_PRINT_WD_EXPLICIT;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001006 break;
1007 case NC_WD_TRIM:
Michal Vasko77367452021-02-16 16:32:18 +01001008 wd = LYD_PRINT_WD_TRIM;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001009 break;
1010 case NC_WD_ALL:
Michal Vasko77367452021-02-16 16:32:18 +01001011 wd = LYD_PRINT_WD_ALL;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001012 break;
1013 case NC_WD_ALL_TAG:
Michal Vasko77367452021-02-16 16:32:18 +01001014 wd = LYD_PRINT_WD_ALL_TAG;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001015 break;
1016 }
Michal Vasko77367452021-02-16 16:32:18 +01001017
1018 node = ((struct nc_server_reply_data *)reply)->data;
1019 assert(node->schema->nodetype & (LYS_RPC | LYS_ACTION));
Michal Vaskoff7286b2021-07-09 13:13:11 +02001020 LY_LIST_FOR_SAFE(lyd_child(node), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001021 /* temporary */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001022 lyd_insert_child(reply_envp, node);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001023 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001024 break;
1025 case NC_RPL_ERROR:
Michal Vasko77367452021-02-16 16:32:18 +01001026 /* temporary */
1027 lyd_insert_child(reply_envp, ((struct nc_server_reply_error *)reply)->err);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001028 break;
1029 default:
1030 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001031 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001032 ret = NC_MSG_ERROR;
1033 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001034 }
Michal Vasko77367452021-02-16 16:32:18 +01001035
1036 /* temporary */
1037 ((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr;
1038
1039 /* print */
1040 lyrc = lyd_print_clb(nc_write_xmlclb, (void *)&arg, reply_envp, LYD_XML, LYD_PRINT_SHRINK | wd);
1041 ((struct lyd_node_opaq *)reply_envp)->attr = NULL;
1042
1043 /* cleanup */
1044 switch (reply->type) {
1045 case NC_RPL_OK:
1046 /* just free everything */
1047 lyd_free_tree(reply_envp);
1048 break;
1049 case NC_RPL_DATA:
Michal Vaskoff7286b2021-07-09 13:13:11 +02001050 LY_LIST_FOR_SAFE(lyd_child(reply_envp), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001051 /* connect back to the reply structure */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001052 lyd_insert_child(((struct nc_server_reply_data *)reply)->data, node);
Michal Vasko77367452021-02-16 16:32:18 +01001053 }
1054 lyd_free_tree(reply_envp);
1055 break;
1056 case NC_RPL_ERROR:
1057 /* unlink from the data reply */
1058 lyd_unlink_tree(lyd_child(reply_envp));
1059 lyd_free_tree(reply_envp);
1060 break;
1061 default:
1062 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001063 }
Michal Vasko77367452021-02-16 16:32:18 +01001064
1065 if (lyrc) {
1066 ret = NC_MSG_ERROR;
1067 goto cleanup;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001068 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001069 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001070
Radek Krejcife0b3472015-10-12 13:43:42 +02001071 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001072 notif = va_arg(ap, struct nc_server_notif *);
1073
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001074 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF "\">", 21 + 47 + 2, 0);
Radek Krejci93e80222016-10-03 13:34:25 +02001075 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1076 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1077 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko77367452021-02-16 16:32:18 +01001078 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->ntf, LYD_XML, LYD_PRINT_SHRINK)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001079 ret = NC_MSG_ERROR;
1080 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001081 }
mohitarora24878b2962016-11-09 18:45:33 -05001082 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001083 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001084
Radek Krejcid116db42016-01-08 15:36:30 +01001085 case NC_MSG_HELLO:
1086 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001087 ret = NC_MSG_ERROR;
1088 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001089 }
1090 capabilities = va_arg(ap, const char **);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001091 sid = va_arg(ap, uint32_t *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001092
Radek Krejcid116db42016-01-08 15:36:30 +01001093 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
roman3a95bb22023-10-26 11:07:17 +02001094 NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup);
Radek Krejci047300e2016-03-08 16:46:58 +01001095 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001096 free(buf);
1097 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001098 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1099 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1100 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001101 }
1102 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001103 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
roman3a95bb22023-10-26 11:07:17 +02001104 NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup);
Radek Krejci047300e2016-03-08 16:46:58 +01001105 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001106 free(buf);
1107 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001108 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001109 }
Radek Krejcid116db42016-01-08 15:36:30 +01001110 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001111
Radek Krejcife0b3472015-10-12 13:43:42 +02001112 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001113 ret = NC_MSG_ERROR;
1114 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001115 }
1116
1117 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001118 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001119
Michal Vasko428087d2016-01-14 16:04:28 +01001120 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1121 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001122 ret = NC_MSG_ERROR;
1123 } else {
1124 /* specific message successfully sent */
1125 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001126 }
1127
Michal Vasko131120a2018-05-29 15:44:02 +02001128cleanup:
1129 va_end(ap);
1130 nc_session_io_unlock(session, __func__);
1131 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001132}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001133
1134void *
1135nc_realloc(void *ptr, size_t size)
1136{
1137 void *ret;
1138
1139 ret = realloc(ptr, size);
1140 if (!ret) {
1141 free(ptr);
1142 }
1143
1144 return ret;
1145}
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001146
1147struct passwd *
romanf6e32012023-04-24 15:51:26 +02001148nc_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 +02001149{
1150 struct passwd *pwd = NULL;
Michal Vasko7e06ee52021-11-02 08:53:05 +01001151 long sys_size;
1152 int ret;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001153
1154 do {
Michal Vasko7e06ee52021-11-02 08:53:05 +01001155 if (!*buf_size) {
1156 /* learn suitable buffer size */
1157 sys_size = sysconf(_SC_GETPW_R_SIZE_MAX);
1158 *buf_size = (sys_size == -1) ? 2048 : sys_size;
1159 } else {
1160 /* enlarge buffer */
1161 *buf_size += 2048;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001162 }
1163
Michal Vasko7e06ee52021-11-02 08:53:05 +01001164 /* allocate some buffer */
1165 *buf = nc_realloc(*buf, *buf_size);
roman3a95bb22023-10-26 11:07:17 +02001166 NC_CHECK_ERRMEM_RET(!*buf, NULL);
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001167
romanf6e32012023-04-24 15:51:26 +02001168 if (username) {
1169 ret = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd);
1170 } else {
1171 ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd);
1172 }
Michal Vasko7e06ee52021-11-02 08:53:05 +01001173 } while (ret && (ret == ERANGE));
1174
1175 if (ret) {
romanf6e32012023-04-24 15:51:26 +02001176 if (username) {
1177 ERR(NULL, "Retrieving username \"%s\" passwd entry failed (%s).", username, strerror(ret));
1178 } else {
1179 ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long)uid, strerror(ret));
1180 }
Michal Vasko7e06ee52021-11-02 08:53:05 +01001181 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001182 return pwd;
1183}