blob: f44af529561f2ed95380fdbb727243151093e072 [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>
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +020021#include <pwd.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020022#include <stdarg.h>
roman3f9b65c2023-06-05 14:26:58 +020023#include <stdint.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020024#include <stdlib.h>
25#include <string.h>
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +020026#include <sys/types.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010027#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020028#include <unistd.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020029
roman2eab4742023-06-06 10:00:26 +020030#ifdef NC_ENABLED_SSH_TLS
Michal Vasko964e1732016-09-23 13:39:33 +020031# include <openssl/err.h>
roman3f9b65c2023-06-05 14:26:58 +020032# include <openssl/ssl.h>
roman2eab4742023-06-06 10:00:26 +020033#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko964e1732016-09-23 13:39:33 +020034
Radek Krejci206fcd62015-10-07 15:42:48 +020035#include <libyang/libyang.h>
36
Michal Vasko9e8ac262020-04-07 13:06:45 +020037#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020038#include "config.h"
39#include "log_p.h"
40#include "messages_p.h"
41#include "netconf.h"
42#include "session.h"
43#include "session_p.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020044
Michal Vasko8fe604c2020-02-10 15:25:04 +010045const char *nc_msgtype2str[] = {
46 "error",
47 "would block",
48 "no message",
49 "hello message",
50 "bad hello message",
51 "RPC message",
52 "rpc-reply message",
53 "rpc-reply message with wrong ID",
54 "notification message",
55};
56
Radek Krejcife0b3472015-10-12 13:43:42 +020057#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020058
roman2eab4742023-06-06 10:00:26 +020059#ifdef NC_ENABLED_SSH_TLS
Michal Vasko90a87d92018-12-10 15:53:44 +010060
61static char *
62nc_ssl_error_get_reasons(void)
63{
64 unsigned int e;
65 int reason_size, reason_len;
66 char *reasons = NULL;
67
68 reason_size = 1;
69 reason_len = 0;
70 while ((e = ERR_get_error())) {
71 if (reason_len) {
72 /* add "; " */
73 reason_size += 2;
74 reasons = nc_realloc(reasons, reason_size);
roman3a95bb22023-10-26 11:07:17 +020075 NC_CHECK_ERRMEM_RET(!reasons, NULL);
Michal Vasko90a87d92018-12-10 15:53:44 +010076 reason_len += sprintf(reasons + reason_len, "; ");
77 }
78 reason_size += strlen(ERR_reason_error_string(e));
79 reasons = nc_realloc(reasons, reason_size);
roman3a95bb22023-10-26 11:07:17 +020080 NC_CHECK_ERRMEM_RET(!reasons, NULL);
Rosen Peneve38d7ef2019-07-15 18:18:03 -070081 reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e));
Michal Vasko90a87d92018-12-10 15:53:44 +010082 }
83
84 return reasons;
85}
86
roman2eab4742023-06-06 10:00:26 +020087#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko90a87d92018-12-10 15:53:44 +010088
Radek Krejci206fcd62015-10-07 15:42:48 +020089static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010090nc_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 +020091{
Michal Vasko81b33fb2016-09-26 14:57:36 +020092 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +010093 ssize_t r = -1;
Robin Jarry7de4b8e2019-10-14 21:46:00 +020094 int fd, interrupted;
roman6ece9c52022-06-22 09:29:17 +020095 struct timespec ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +020096
97 assert(session);
98 assert(buf);
99
Michal Vasko428087d2016-01-14 16:04:28 +0100100 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
101 return -1;
102 }
103
Radek Krejci206fcd62015-10-07 15:42:48 +0200104 if (!count) {
105 return 0;
106 }
107
Michal Vaskod8a74192023-02-06 15:51:50 +0100108 nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +0200109 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200110 interrupted = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100111 switch (session->ti_type) {
112 case NC_TI_NONE:
113 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100114
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100115 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200116 case NC_TI_UNIX:
117 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100118 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200119 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200120 if (r < 0) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200121 if (errno == EAGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100122 r = 0;
123 break;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200124 } else if (errno == EINTR) {
125 r = 0;
126 interrupted = 1;
127 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200128 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200129 ERR(session, "Reading from file descriptor (%d) failed (%s).", fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100130 session->status = NC_STATUS_INVALID;
131 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200132 return -1;
133 }
134 } else if (r == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200135 ERR(session, "Communication file descriptor (%d) unexpectedly closed.", fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100136 session->status = NC_STATUS_INVALID;
137 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200138 return -1;
139 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100140 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200141
roman2eab4742023-06-06 10:00:26 +0200142#ifdef NC_ENABLED_SSH_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100143 case NC_TI_LIBSSH:
144 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200145 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200146 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100147 r = 0;
148 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200149 } else if (r == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200150 ERR(session, "Reading from the SSH channel failed (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100151 session->status = NC_STATUS_INVALID;
152 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200153 return -1;
154 } else if (r == 0) {
155 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200156 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100157 session->status = NC_STATUS_INVALID;
158 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200159 return -1;
160 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100161 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200162 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100163 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200164
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100165 case NC_TI_OPENSSL:
166 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100167 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200168 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200169 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100170 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100171 char *reasons;
172
Michal Vasko0abba6d2018-12-10 14:09:39 +0100173 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200174 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100175 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100176 r = 0;
177 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200178 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200179 ERR(session, "Communication socket unexpectedly closed (OpenSSL).");
Michal Vasko428087d2016-01-14 16:04:28 +0100180 session->status = NC_STATUS_INVALID;
181 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200182 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100183 case SSL_ERROR_SYSCALL:
Michal Vasko0272dc32021-09-03 13:02:20 +0200184 ERR(session, "SSL socket error (%s).", errno ? strerror(errno) : "unexpected EOF");
Michal Vasko0abba6d2018-12-10 14:09:39 +0100185 session->status = NC_STATUS_INVALID;
186 session->term_reason = NC_SESSION_TERM_OTHER;
187 return -1;
188 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100189 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200190 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100191 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100192 session->status = NC_STATUS_INVALID;
193 session->term_reason = NC_SESSION_TERM_OTHER;
194 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200195 default:
Michal Vaskofdba4a32022-01-05 12:13:53 +0100196 ERR(session, "Unknown SSL error occurred (err code %d).", e);
Michal Vasko428087d2016-01-14 16:04:28 +0100197 session->status = NC_STATUS_INVALID;
198 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200199 return -1;
200 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200201 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100202 break;
roman2eab4742023-06-06 10:00:26 +0200203#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100204 }
205
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100206 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100207 /* nothing read */
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200208 if (!interrupted) {
209 usleep(NC_TIMEOUT_STEP);
210 }
Michal Vaskod8a74192023-02-06 15:51:50 +0100211 if ((nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) || (nc_timeouttime_cur_diff(ts_act_timeout) < 1)) {
212 if (nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200213 ERR(session, "Inactive read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100214 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200215 ERR(session, "Active read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100216 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100217 session->status = NC_STATUS_INVALID;
218 session->term_reason = NC_SESSION_TERM_OTHER;
219 return -1;
220 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100221 } else {
222 /* something read */
223 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100224
225 /* reset inactive timeout */
Michal Vaskod8a74192023-02-06 15:51:50 +0100226 nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100227 }
228
Michal Vasko81b33fb2016-09-26 14:57:36 +0200229 } while (readd < count);
230 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200231
Michal Vasko81b33fb2016-09-26 14:57:36 +0200232 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200233}
234
235static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100236nc_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 +0200237{
238 ssize_t r;
239
240 assert(session);
241 assert(chunk);
242
243 if (!len) {
244 return 0;
245 }
246
Michal Vasko4eb3c312016-03-01 14:09:37 +0100247 *chunk = malloc((len + 1) * sizeof **chunk);
roman3a95bb22023-10-26 11:07:17 +0200248 NC_CHECK_ERRMEM_RET(!*chunk, -1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200249
Michal Vasko36c7be82017-02-22 13:37:59 +0100250 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200251 if (r <= 0) {
252 free(*chunk);
253 return -1;
254 }
255
256 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200257 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200258
259 return r;
260}
261
262static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100263nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200264 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200265{
266 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200267 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200268
269 assert(session);
270 assert(endtag);
271
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200272 if (limit && (limit < BUFFERSIZE)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200273 size = limit;
274 } else {
275 size = BUFFERSIZE;
276 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100277 chunk = malloc((size + 1) * sizeof *chunk);
roman3a95bb22023-10-26 11:07:17 +0200278 NC_CHECK_ERRMEM_RET(!chunk, -1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200279
280 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100281 while (1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200282 if (limit && (count == limit)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200283 free(chunk);
Michal Vasko05532772021-06-03 12:12:38 +0200284 WRN(session, "Reading limit (%d) reached.", limit);
285 ERR(session, "Invalid input data (missing \"%s\" sequence).", endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200286 return -1;
287 }
288
289 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200290 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200291 /* get more memory */
292 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200293 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
roman3a95bb22023-10-26 11:07:17 +0200294 NC_CHECK_ERRMEM_RET(!chunk, -1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200295 }
296
297 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200298 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
299 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200300 free(chunk);
301 return -1;
302 }
303
David Sedlákfedbc792018-07-04 11:07:07 +0200304 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200305
David Sedlákfedbc792018-07-04 11:07:07 +0200306 for (i = len - matched; i > 0; i--) {
307 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
308 /*part of endtag found */
309 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200310 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200311 } else {
312 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200313 }
314 }
David Sedlákfedbc792018-07-04 11:07:07 +0200315
316 /* whole endtag found */
317 if (matched == len) {
318 break;
319 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200320 }
321
322 /* terminating null byte */
323 chunk[count] = 0;
324
325 if (result) {
326 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200327 } else {
328 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200329 }
330 return count;
331}
332
Michal Vasko77367452021-02-16 16:32:18 +0100333int
334nc_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 +0100335{
Michal Vasko77367452021-02-16 16:32:18 +0100336 int ret = 1, r, io_locked = passing_io_lock;
337 char *data = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100338 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100339 /* use timeout in milliseconds instead seconds */
340 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
341 struct timespec ts_act_timeout;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100342
Michal Vasko77367452021-02-16 16:32:18 +0100343 assert(session && msg);
344 *msg = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100345
346 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200347 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100348 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200349 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100350 }
351
Michal Vaskod8a74192023-02-06 15:51:50 +0100352 nc_timeouttime_get(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
Michal Vasko36c7be82017-02-22 13:37:59 +0100353
Michal Vasko131120a2018-05-29 15:44:02 +0200354 if (!io_locked) {
355 /* SESSION IO LOCK */
356 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100357 if (ret < 1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200358 goto cleanup;
359 }
360 io_locked = 1;
361 }
362
Michal Vasko05ba9df2016-01-13 14:40:27 +0100363 /* read the message */
364 switch (session->version) {
365 case NC_VERSION_10:
Michal Vasko77367452021-02-16 16:32:18 +0100366 r = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &data);
367 if (r == -1) {
368 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200369 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100370 }
371
372 /* cut off the end tag */
Michal Vasko77367452021-02-16 16:32:18 +0100373 data[r - NC_VERSION_10_ENDTAG_LEN] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100374 break;
375 case NC_VERSION_11:
376 while (1) {
Michal Vasko77367452021-02-16 16:32:18 +0100377 r = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
378 if (r == -1) {
379 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200380 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100381 }
Michal Vasko77367452021-02-16 16:32:18 +0100382 r = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
383 if (r == -1) {
384 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200385 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386 }
387
388 if (!strcmp(chunk, "#\n")) {
389 /* end of chunked framing message */
390 free(chunk);
Michal Vasko77367452021-02-16 16:32:18 +0100391 if (!data) {
Michal Vasko05532772021-06-03 12:12:38 +0200392 ERR(session, "Invalid frame chunk delimiters.");
Michal Vasko77367452021-02-16 16:32:18 +0100393 ret = -2;
394 goto cleanup;
Michal Vasko79df3262016-07-13 13:42:17 +0200395 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100396 break;
397 }
398
399 /* convert string to the size of the following chunk */
400 chunk_len = strtoul(chunk, (char **)NULL, 10);
401 free(chunk);
402 if (!chunk_len) {
Michal Vasko05532772021-06-03 12:12:38 +0200403 ERR(session, "Invalid frame chunk size detected, fatal error.");
Michal Vasko77367452021-02-16 16:32:18 +0100404 ret = -2;
405 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100406 }
407
408 /* now we have size of next chunk, so read the chunk */
Michal Vasko77367452021-02-16 16:32:18 +0100409 r = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
410 if (r == -1) {
411 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200412 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100413 }
414
415 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko77367452021-02-16 16:32:18 +0100416 data = nc_realloc(data, len + chunk_len + 1);
roman3a95bb22023-10-26 11:07:17 +0200417 NC_CHECK_ERRMEM_GOTO(!data, ret = -1, cleanup);
Michal Vasko77367452021-02-16 16:32:18 +0100418 memcpy(data + len, chunk, chunk_len);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100419 len += chunk_len;
Michal Vasko77367452021-02-16 16:32:18 +0100420 data[len] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100421 free(chunk);
422 }
423
424 break;
425 }
Michal Vasko131120a2018-05-29 15:44:02 +0200426
427 /* SESSION IO UNLOCK */
428 assert(io_locked);
429 nc_session_io_unlock(session, __func__);
430 io_locked = 0;
431
Michal Vasko05532772021-06-03 12:12:38 +0200432 DBG(session, "Received message:\n%s\n", data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100433
Michal Vasko77367452021-02-16 16:32:18 +0100434 /* build an input structure, eats data */
435 if (ly_in_new_memory(data, msg)) {
436 ret = -1;
437 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100438 }
Michal Vasko77367452021-02-16 16:32:18 +0100439 data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100440
Michal Vasko131120a2018-05-29 15:44:02 +0200441cleanup:
442 if (io_locked) {
Michal Vasko77367452021-02-16 16:32:18 +0100443 /* SESSION IO UNLOCK */
Michal Vasko131120a2018-05-29 15:44:02 +0200444 nc_session_io_unlock(session, __func__);
445 }
Michal Vasko77367452021-02-16 16:32:18 +0100446 free(data);
Michal Vasko131120a2018-05-29 15:44:02 +0200447 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100448}
449
Michal Vasko428087d2016-01-14 16:04:28 +0100450/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
451static int
Michal Vasko131120a2018-05-29 15:44:02 +0200452nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100453{
454 int ret = -2;
455 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100456
457 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200458 ERR(session, "Invalid session to poll.");
Michal Vasko428087d2016-01-14 16:04:28 +0100459 return -1;
460 }
461
462 switch (session->ti_type) {
roman2eab4742023-06-06 10:00:26 +0200463#ifdef NC_ENABLED_SSH_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100464 case NC_TI_LIBSSH:
465 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200466 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100467 if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200468 ERR(session, "SSH channel poll error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100469 session->status = NC_STATUS_INVALID;
470 session->term_reason = NC_SESSION_TERM_OTHER;
471 return -1;
472 } else if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200473 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100474 session->status = NC_STATUS_INVALID;
475 session->term_reason = NC_SESSION_TERM_DROPPED;
476 return -1;
477 } else if (ret > 0) {
478 /* fake it */
479 ret = 1;
480 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100481 } else { /* ret == 0 */
482 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100483 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100484 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100485 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100486 ret = SSL_pending(session->ti.tls);
487 if (ret) {
488 /* some buffered TLS data available */
489 ret = 1;
490 fds.revents = POLLIN;
491 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100492 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100493
494 fds.fd = SSL_get_fd(session->ti.tls);
roman2eab4742023-06-06 10:00:26 +0200495#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200496 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100497 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200498 case NC_TI_UNIX:
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200499 if (session->ti_type == NC_TI_FD) {
Michal Vasko428087d2016-01-14 16:04:28 +0100500 fds.fd = session->ti.fd.in;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200501 } else if (session->ti_type == NC_TI_UNIX) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200502 fds.fd = session->ti.unixsock.sock;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200503 }
Michal Vasko428087d2016-01-14 16:04:28 +0100504
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100505 fds.events = POLLIN;
506 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100507
Michal Vasko63b92d62024-04-29 10:04:56 +0200508 ret = nc_poll(&fds, 1, io_timeout);
Michal Vasko428087d2016-01-14 16:04:28 +0100509 break;
510
511 default:
512 ERRINT;
513 return -1;
514 }
515
516 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
517 if (ret < 0) {
518 /* poll failed - something really bad happened, close the session */
Michal Vasko05532772021-06-03 12:12:38 +0200519 ERR(session, "poll error (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100520 session->status = NC_STATUS_INVALID;
521 session->term_reason = NC_SESSION_TERM_OTHER;
522 return -1;
523 } else { /* status > 0 */
524 /* in case of standard (non-libssh) poll, there still can be an error */
Michal Vasko428087d2016-01-14 16:04:28 +0100525 if (fds.revents & POLLERR) {
Michal Vasko05532772021-06-03 12:12:38 +0200526 ERR(session, "Communication channel error.");
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 }
Robin Jarryf732adc2020-05-15 11:18:38 +0200531 /* Some poll() implementations may return POLLHUP|POLLIN when the other
532 * side has closed but there is data left to read in the buffer. */
533 if ((fds.revents & POLLHUP) && !(fds.revents & POLLIN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200534 ERR(session, "Communication channel unexpectedly closed.");
Robin Jarryf732adc2020-05-15 11:18:38 +0200535 session->status = NC_STATUS_INVALID;
536 session->term_reason = NC_SESSION_TERM_DROPPED;
537 return -1;
538 }
Michal Vasko428087d2016-01-14 16:04:28 +0100539 }
540
541 return ret;
542}
543
Michal Vasko77367452021-02-16 16:32:18 +0100544int
545nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **msg)
Radek Krejci206fcd62015-10-07 15:42:48 +0200546{
Michal Vasko428087d2016-01-14 16:04:28 +0100547 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200548
Michal Vasko77367452021-02-16 16:32:18 +0100549 assert(msg);
550 *msg = NULL;
Radek Krejci206fcd62015-10-07 15:42:48 +0200551
Michal Vasko428087d2016-01-14 16:04:28 +0100552 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200553 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100554 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200555 }
556
Michal Vasko131120a2018-05-29 15:44:02 +0200557 /* SESSION IO LOCK */
558 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100559 if (ret < 1) {
560 return ret;
Michal Vasko131120a2018-05-29 15:44:02 +0200561 }
562
563 ret = nc_read_poll(session, io_timeout);
Michal Vasko77367452021-02-16 16:32:18 +0100564 if (ret < 1) {
565 /* timed out or error */
Michal Vasko131120a2018-05-29 15:44:02 +0200566
567 /* SESSION IO UNLOCK */
568 nc_session_io_unlock(session, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100569 return ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200570 }
571
Michal Vasko131120a2018-05-29 15:44:02 +0200572 /* SESSION IO LOCK passed down */
Michal Vasko77367452021-02-16 16:32:18 +0100573 return nc_read_msg_io(session, io_timeout, msg, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200574}
Radek Krejcife0b3472015-10-12 13:43:42 +0200575
Michal Vasko428087d2016-01-14 16:04:28 +0100576/* does not really log, only fatal errors */
577int
romane5675b12024-03-05 14:26:23 +0100578nc_session_is_connected(const struct nc_session *session)
Michal Vasko428087d2016-01-14 16:04:28 +0100579{
580 int ret;
581 struct pollfd fds;
582
583 switch (session->ti_type) {
584 case NC_TI_FD:
585 fds.fd = session->ti.fd.in;
586 break;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200587 case NC_TI_UNIX:
588 fds.fd = session->ti.unixsock.sock;
589 break;
roman2eab4742023-06-06 10:00:26 +0200590#ifdef NC_ENABLED_SSH_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100591 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100592 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100593 case NC_TI_OPENSSL:
594 fds.fd = SSL_get_fd(session->ti.tls);
595 break;
roman2eab4742023-06-06 10:00:26 +0200596#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskof945da52018-02-15 08:45:13 +0100597 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100598 return 0;
599 }
600
Michal Vasko840a8a62017-02-07 10:56:34 +0100601 if (fds.fd == -1) {
602 return 0;
603 }
604
Michal Vasko428087d2016-01-14 16:04:28 +0100605 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100606 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100607
Michal Vasko63b92d62024-04-29 10:04:56 +0200608 ret = nc_poll(&fds, 1, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100609 if (ret == -1) {
Michal Vasko428087d2016-01-14 16:04:28 +0100610 return 0;
611 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
612 return 0;
613 }
614
615 return 1;
616}
617
Radek Krejcife0b3472015-10-12 13:43:42 +0200618#define WRITE_BUFSIZE (2 * BUFFERSIZE)
619struct wclb_arg {
620 struct nc_session *session;
621 char buf[WRITE_BUFSIZE];
622 size_t len;
623};
624
Michal Vasko964e1732016-09-23 13:39:33 +0200625static int
Michal Vasko428087d2016-01-14 16:04:28 +0100626nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200627{
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200628 int c, fd, interrupted;
Michal Vasko964e1732016-09-23 13:39:33 +0200629 size_t written = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200630
roman2eab4742023-06-06 10:00:26 +0200631#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoe2357e92016-10-05 14:20:47 +0200632 unsigned long e;
roman2eab4742023-06-06 10:00:26 +0200633#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko964e1732016-09-23 13:39:33 +0200634
Michal Vasko428087d2016-01-14 16:04:28 +0100635 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
636 return -1;
637 }
638
639 /* prevent SIGPIPE this way */
640 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +0200641 ERR(session, "Communication socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100642 session->status = NC_STATUS_INVALID;
643 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100644 return -1;
645 }
646
Michal Vasko05532772021-06-03 12:12:38 +0200647 DBG(session, "Sending message:\n%.*s\n", count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200648
Michal Vasko81b33fb2016-09-26 14:57:36 +0200649 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200650 interrupted = 0;
Michal Vasko964e1732016-09-23 13:39:33 +0200651 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200652 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200653 case NC_TI_UNIX:
654 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
655 c = write(fd, (char *)(buf + written), count - written);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200656 if ((c < 0) && (errno == EAGAIN)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200657 c = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200658 } else if ((c < 0) && (errno == EINTR)) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200659 c = 0;
660 interrupted = 1;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200661 } else if (c < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200662 ERR(session, "socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200663 return -1;
664 }
665 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200666
roman2eab4742023-06-06 10:00:26 +0200667#ifdef NC_ENABLED_SSH_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200668 case NC_TI_LIBSSH:
669 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
670 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200671 ERR(session, "SSH channel unexpectedly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200672 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200673 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko964e1732016-09-23 13:39:33 +0200674 }
675 session->status = NC_STATUS_INVALID;
676 session->term_reason = NC_SESSION_TERM_DROPPED;
677 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100678 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200679 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200680 if ((c == SSH_ERROR) || (c == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200681 ERR(session, "SSH channel write failed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200682 return -1;
683 }
684 break;
Michal Vasko964e1732016-09-23 13:39:33 +0200685 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200686 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200687 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100688 char *reasons;
689
Michal Vasko964e1732016-09-23 13:39:33 +0200690 switch ((e = SSL_get_error(session->ti.tls, c))) {
691 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200692 ERR(session, "SSL connection was properly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200693 return -1;
694 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100695 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200696 c = 0;
697 break;
698 case SSL_ERROR_SYSCALL:
Michal Vasko05532772021-06-03 12:12:38 +0200699 ERR(session, "SSL socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200700 return -1;
701 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100702 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200703 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100704 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200705 return -1;
706 default:
Michal Vaskofdba4a32022-01-05 12:13:53 +0100707 ERR(session, "Unknown SSL error occurred (err code %d).", e);
Michal Vasko964e1732016-09-23 13:39:33 +0200708 return -1;
709 }
710 }
711 break;
roman2eab4742023-06-06 10:00:26 +0200712#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko339eea82016-09-29 11:42:36 +0200713 default:
714 ERRINT;
715 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200716 }
717
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200718 if ((c == 0) && !interrupted) {
Michal Vasko964e1732016-09-23 13:39:33 +0200719 /* we must wait */
720 usleep(NC_TIMEOUT_STEP);
721 }
722
723 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200724 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200725
Michal Vasko964e1732016-09-23 13:39:33 +0200726 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200727}
728
Michal Vasko428087d2016-01-14 16:04:28 +0100729static int
730nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100731{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100732 int ret = 0, c;
Claus Klein22091912020-01-20 13:45:47 +0100733 char chunksize[24];
Michal Vasko086311b2016-01-08 09:53:11 +0100734
Claus Klein22091912020-01-20 13:45:47 +0100735 // warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=]
Michal Vasko086311b2016-01-08 09:53:11 +0100736 if (session->version == NC_VERSION_11) {
737 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100738 ret = nc_write(session, chunksize, strlen(chunksize));
739 if (ret == -1) {
740 return -1;
741 }
Michal Vasko086311b2016-01-08 09:53:11 +0100742 }
Michal Vasko428087d2016-01-14 16:04:28 +0100743
744 c = nc_write(session, buf, count);
745 if (c == -1) {
746 return -1;
747 }
748 ret += c;
749
750 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100751}
752
Radek Krejcife0b3472015-10-12 13:43:42 +0200753static int
Michal Vasko428087d2016-01-14 16:04:28 +0100754nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200755{
Michal Vasko428087d2016-01-14 16:04:28 +0100756 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100757
Michal Vasko428087d2016-01-14 16:04:28 +0100758 if (session->version == NC_VERSION_11) {
759 ret = nc_write(session, "\n##\n", 4);
760 } else {
761 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200762 }
763
Michal Vasko428087d2016-01-14 16:04:28 +0100764 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200765}
766
Michal Vasko428087d2016-01-14 16:04:28 +0100767static int
768nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200769{
Michal Vasko428087d2016-01-14 16:04:28 +0100770 int ret = 0;
771
Radek Krejcife0b3472015-10-12 13:43:42 +0200772 /* flush current buffer */
773 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100774 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200775 warg->len = 0;
776 }
Michal Vasko428087d2016-01-14 16:04:28 +0100777
778 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200779}
780
781static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100782nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200783{
Michal Vasko428087d2016-01-14 16:04:28 +0100784 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100785 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200786 struct wclb_arg *warg = (struct wclb_arg *)arg;
787
788 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100789 c = nc_write_clb_flush(warg);
790 if (c == -1) {
791 return -1;
792 }
793 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200794
795 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100796 c = nc_write_endtag(warg->session);
797 if (c == -1) {
798 return -1;
799 }
800 ret += c;
801
802 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200803 }
804
805 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
806 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100807 c = nc_write_clb_flush(warg);
808 if (c == -1) {
809 return -1;
810 }
811 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200812 }
Michal Vasko428087d2016-01-14 16:04:28 +0100813
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200814 if (!xmlcontent && (count > WRITE_BUFSIZE)) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200815 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100816 c = nc_write_starttag_and_msg(warg->session, buf, count);
817 if (c == -1) {
818 return -1;
819 }
820 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200821 } else {
822 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100823 if (xmlcontent) {
824 for (l = 0; l < count; l++) {
825 if (warg->len + 5 >= WRITE_BUFSIZE) {
826 /* buffer is full */
827 c = nc_write_clb_flush(warg);
828 if (c == -1) {
829 return -1;
830 }
831 }
832
833 switch (((char *)buf)[l]) {
834 case '&':
835 ret += 5;
836 memcpy(&warg->buf[warg->len], "&amp;", 5);
837 warg->len += 5;
838 break;
839 case '<':
840 ret += 4;
841 memcpy(&warg->buf[warg->len], "&lt;", 4);
842 warg->len += 4;
843 break;
844 case '>':
845 /* not needed, just for readability */
846 ret += 4;
847 memcpy(&warg->buf[warg->len], "&gt;", 4);
848 warg->len += 4;
849 break;
850 default:
851 ret++;
852 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
853 warg->len++;
854 }
855 }
856 } else {
857 memcpy(&warg->buf[warg->len], buf, count);
858 warg->len += count; /* is <= WRITE_BUFSIZE */
859 ret += count;
860 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200861 }
862
Michal Vasko428087d2016-01-14 16:04:28 +0100863 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200864}
865
Radek Krejci047300e2016-03-08 16:46:58 +0100866static ssize_t
867nc_write_xmlclb(void *arg, const void *buf, size_t count)
868{
Michal Vasko77367452021-02-16 16:32:18 +0100869 ssize_t r;
Radek Krejci047300e2016-03-08 16:46:58 +0100870
Michal Vasko77367452021-02-16 16:32:18 +0100871 r = nc_write_clb(arg, buf, count, 0);
872 if (r == -1) {
873 return -1;
Michal Vasko08611b32016-12-05 13:30:37 +0100874 }
875
Michal Vasko77367452021-02-16 16:32:18 +0100876 /* always return what libyang expects, simply that all the characters were printed */
877 return count;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100878}
879
Michal Vasko131120a2018-05-29 15:44:02 +0200880/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
881NC_MSG_TYPE
882nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200883{
Radek Krejcid116db42016-01-08 15:36:30 +0100884 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +0200885 int count, ret;
Michal Vasko70bf2222021-10-26 10:45:15 +0200886 const char *attrs, *str;
Michal Vaskoff7286b2021-07-09 13:13:11 +0200887 struct lyd_node *op, *reply_envp, *node, *next;
Michal Vasko77367452021-02-16 16:32:18 +0100888 struct lyd_node_opaq *rpc_envp;
Radek Krejci93e80222016-10-03 13:34:25 +0200889 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100890 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +0100891 char *buf;
Radek Krejcid116db42016-01-08 15:36:30 +0100892 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200893 const char **capabilities;
Michal Vasko77367452021-02-16 16:32:18 +0100894 uint32_t *sid = NULL, i, wd = 0;
895 LY_ERR lyrc;
Radek Krejcife0b3472015-10-12 13:43:42 +0200896
Michal Vasko428087d2016-01-14 16:04:28 +0100897 assert(session);
898
899 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200900 ERR(session, "Invalid session to write to.");
Michal Vasko131120a2018-05-29 15:44:02 +0200901 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100902 }
903
Radek Krejcife0b3472015-10-12 13:43:42 +0200904 arg.session = session;
905 arg.len = 0;
906
Michal Vasko131120a2018-05-29 15:44:02 +0200907 /* SESSION IO LOCK */
908 ret = nc_session_io_lock(session, io_timeout, __func__);
909 if (ret < 0) {
910 return NC_MSG_ERROR;
911 } else if (!ret) {
912 return NC_MSG_WOULDBLOCK;
913 }
914
915 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +0200916
Radek Krejcife0b3472015-10-12 13:43:42 +0200917 switch (type) {
918 case NC_MSG_RPC:
Michal Vasko77367452021-02-16 16:32:18 +0100919 op = va_arg(ap, struct lyd_node *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200920 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100921
Michal Vasko70bf2222021-10-26 10:45:15 +0200922 /* <rpc> open */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200923 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%" PRIu64 "\"%s>",
Michal Vasko77367452021-02-16 16:32:18 +0100924 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
roman3a95bb22023-10-26 11:07:17 +0200925 NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup);
Radek Krejci047300e2016-03-08 16:46:58 +0100926 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200927 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +0200928
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100929 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200930 /* <action> open */
931 str = "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">";
932 nc_write_clb((void *)&arg, str, strlen(str), 0);
933 }
934
935 /* rpc data */
Michal Vasko6b70b822022-01-21 08:39:16 +0100936 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 +0200937 ret = NC_MSG_ERROR;
938 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +0200939 }
Michal Vasko70bf2222021-10-26 10:45:15 +0200940
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100941 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200942 /* <action> close */
943 str = "</action>";
944 nc_write_clb((void *)&arg, str, strlen(str), 0);
945 }
946
947 /* <rpc> close */
948 str = "</rpc>";
949 nc_write_clb((void *)&arg, str, strlen(str), 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200950
Michal Vasko2e6defd2016-10-07 15:48:15 +0200951 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +0200952 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100953
Radek Krejcife0b3472015-10-12 13:43:42 +0200954 case NC_MSG_REPLY:
Michal Vasko77367452021-02-16 16:32:18 +0100955 rpc_envp = va_arg(ap, struct lyd_node_opaq *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100956 reply = va_arg(ap, struct nc_server_reply *);
957
Michal Vasko77367452021-02-16 16:32:18 +0100958 if (!rpc_envp) {
959 /* can be NULL if replying with a malformed-message error */
960 nc_write_clb((void *)&arg, "<rpc-reply xmlns=\"" NC_NS_BASE "\">", 18 + strlen(NC_NS_BASE) + 2, 0);
961
962 assert(reply->type == NC_RPL_ERROR);
963 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_server_reply_error *)reply)->err, LYD_XML,
964 LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)) {
965 ret = NC_MSG_ERROR;
966 goto cleanup;
967 }
968
969 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
970 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +0100971 }
972
Michal Vasko77367452021-02-16 16:32:18 +0100973 /* build a rpc-reply opaque node that can be simply printed */
Michal Vaskoff7286b2021-07-09 13:13:11 +0200974 if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns,
975 &reply_envp)) {
Michal Vasko77367452021-02-16 16:32:18 +0100976 ERRINT;
977 ret = NC_MSG_ERROR;
978 goto cleanup;
Michal Vaskoe7e534f2016-01-15 09:51:09 +0100979 }
Michal Vasko77367452021-02-16 16:32:18 +0100980
Michal Vasko05ba9df2016-01-13 14:40:27 +0100981 switch (reply->type) {
982 case NC_RPL_OK:
Michal Vasko77367452021-02-16 16:32:18 +0100983 if (lyd_new_opaq2(reply_envp, NULL, "ok", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, NULL)) {
984 lyd_free_tree(reply_envp);
985
986 ERRINT;
987 ret = NC_MSG_ERROR;
988 goto cleanup;
Michal Vasko08611b32016-12-05 13:30:37 +0100989 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100990 break;
991 case NC_RPL_DATA:
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200992 switch (((struct nc_server_reply_data *)reply)->wd) {
Radek Krejci36dfdb32016-09-01 16:56:35 +0200993 case NC_WD_UNKNOWN:
994 case NC_WD_EXPLICIT:
Michal Vasko77367452021-02-16 16:32:18 +0100995 wd = LYD_PRINT_WD_EXPLICIT;
Radek Krejci36dfdb32016-09-01 16:56:35 +0200996 break;
997 case NC_WD_TRIM:
Michal Vasko77367452021-02-16 16:32:18 +0100998 wd = LYD_PRINT_WD_TRIM;
Radek Krejci36dfdb32016-09-01 16:56:35 +0200999 break;
1000 case NC_WD_ALL:
Michal Vasko77367452021-02-16 16:32:18 +01001001 wd = LYD_PRINT_WD_ALL;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001002 break;
1003 case NC_WD_ALL_TAG:
Michal Vasko77367452021-02-16 16:32:18 +01001004 wd = LYD_PRINT_WD_ALL_TAG;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001005 break;
1006 }
Michal Vasko77367452021-02-16 16:32:18 +01001007
1008 node = ((struct nc_server_reply_data *)reply)->data;
1009 assert(node->schema->nodetype & (LYS_RPC | LYS_ACTION));
Michal Vaskoff7286b2021-07-09 13:13:11 +02001010 LY_LIST_FOR_SAFE(lyd_child(node), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001011 /* temporary */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001012 lyd_insert_child(reply_envp, node);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001013 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001014 break;
1015 case NC_RPL_ERROR:
Michal Vasko77367452021-02-16 16:32:18 +01001016 /* temporary */
1017 lyd_insert_child(reply_envp, ((struct nc_server_reply_error *)reply)->err);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001018 break;
1019 default:
1020 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001021 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001022 ret = NC_MSG_ERROR;
1023 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001024 }
Michal Vasko77367452021-02-16 16:32:18 +01001025
1026 /* temporary */
1027 ((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr;
1028
1029 /* print */
1030 lyrc = lyd_print_clb(nc_write_xmlclb, (void *)&arg, reply_envp, LYD_XML, LYD_PRINT_SHRINK | wd);
1031 ((struct lyd_node_opaq *)reply_envp)->attr = NULL;
1032
1033 /* cleanup */
1034 switch (reply->type) {
1035 case NC_RPL_OK:
1036 /* just free everything */
1037 lyd_free_tree(reply_envp);
1038 break;
1039 case NC_RPL_DATA:
Michal Vaskoff7286b2021-07-09 13:13:11 +02001040 LY_LIST_FOR_SAFE(lyd_child(reply_envp), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001041 /* connect back to the reply structure */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001042 lyd_insert_child(((struct nc_server_reply_data *)reply)->data, node);
Michal Vasko77367452021-02-16 16:32:18 +01001043 }
1044 lyd_free_tree(reply_envp);
1045 break;
1046 case NC_RPL_ERROR:
1047 /* unlink from the data reply */
1048 lyd_unlink_tree(lyd_child(reply_envp));
1049 lyd_free_tree(reply_envp);
1050 break;
1051 default:
1052 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001053 }
Michal Vasko77367452021-02-16 16:32:18 +01001054
1055 if (lyrc) {
1056 ret = NC_MSG_ERROR;
1057 goto cleanup;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001058 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001059 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001060
Radek Krejcife0b3472015-10-12 13:43:42 +02001061 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001062 notif = va_arg(ap, struct nc_server_notif *);
1063
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001064 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF "\">", 21 + 47 + 2, 0);
Radek Krejci93e80222016-10-03 13:34:25 +02001065 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1066 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1067 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko77367452021-02-16 16:32:18 +01001068 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->ntf, LYD_XML, LYD_PRINT_SHRINK)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001069 ret = NC_MSG_ERROR;
1070 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001071 }
mohitarora24878b2962016-11-09 18:45:33 -05001072 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001073 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001074
Radek Krejcid116db42016-01-08 15:36:30 +01001075 case NC_MSG_HELLO:
1076 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001077 ret = NC_MSG_ERROR;
1078 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001079 }
1080 capabilities = va_arg(ap, const char **);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001081 sid = va_arg(ap, uint32_t *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001082
Radek Krejcid116db42016-01-08 15:36:30 +01001083 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
roman3a95bb22023-10-26 11:07:17 +02001084 NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup);
Radek Krejci047300e2016-03-08 16:46:58 +01001085 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001086 free(buf);
1087 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001088 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1089 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1090 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001091 }
1092 if (sid) {
Michal Vasko16374712024-04-26 14:13:00 +02001093 count = asprintf(&buf, "</capabilities><session-id>%" PRIu32 "</session-id></hello>", *sid);
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 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001098 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001099 }
Radek Krejcid116db42016-01-08 15:36:30 +01001100 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001101
Radek Krejcife0b3472015-10-12 13:43:42 +02001102 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001103 ret = NC_MSG_ERROR;
1104 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001105 }
1106
1107 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001108 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001109
Michal Vasko428087d2016-01-14 16:04:28 +01001110 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1111 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001112 ret = NC_MSG_ERROR;
1113 } else {
1114 /* specific message successfully sent */
1115 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001116 }
1117
Michal Vasko131120a2018-05-29 15:44:02 +02001118cleanup:
1119 va_end(ap);
1120 nc_session_io_unlock(session, __func__);
1121 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001122}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001123
1124void *
1125nc_realloc(void *ptr, size_t size)
1126{
1127 void *ret;
1128
1129 ret = realloc(ptr, size);
1130 if (!ret) {
1131 free(ptr);
1132 }
1133
1134 return ret;
1135}
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001136
1137struct passwd *
romanf6e32012023-04-24 15:51:26 +02001138nc_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 +02001139{
1140 struct passwd *pwd = NULL;
Michal Vasko7e06ee52021-11-02 08:53:05 +01001141 long sys_size;
1142 int ret;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001143
1144 do {
Michal Vasko7e06ee52021-11-02 08:53:05 +01001145 if (!*buf_size) {
1146 /* learn suitable buffer size */
1147 sys_size = sysconf(_SC_GETPW_R_SIZE_MAX);
1148 *buf_size = (sys_size == -1) ? 2048 : sys_size;
1149 } else {
1150 /* enlarge buffer */
1151 *buf_size += 2048;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001152 }
1153
Michal Vasko7e06ee52021-11-02 08:53:05 +01001154 /* allocate some buffer */
1155 *buf = nc_realloc(*buf, *buf_size);
roman3a95bb22023-10-26 11:07:17 +02001156 NC_CHECK_ERRMEM_RET(!*buf, NULL);
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001157
romanf6e32012023-04-24 15:51:26 +02001158 if (username) {
1159 ret = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd);
1160 } else {
1161 ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd);
1162 }
Michal Vasko7e06ee52021-11-02 08:53:05 +01001163 } while (ret && (ret == ERANGE));
1164
1165 if (ret) {
romanf6e32012023-04-24 15:51:26 +02001166 if (username) {
1167 ERR(NULL, "Retrieving username \"%s\" passwd entry failed (%s).", username, strerror(ret));
1168 } else {
1169 ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long)uid, strerror(ret));
1170 }
Michal Vasko7e06ee52021-11-02 08:53:05 +01001171 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001172 return pwd;
1173}