blob: 47894d41e00807313e7962885ba6da39d57ba0fb [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);
77 if (!reasons) {
78 ERRMEM;
79 return NULL;
80 }
81 reason_len += sprintf(reasons + reason_len, "; ");
82 }
83 reason_size += strlen(ERR_reason_error_string(e));
84 reasons = nc_realloc(reasons, reason_size);
85 if (!reasons) {
86 ERRMEM;
87 return NULL;
88 }
Rosen Peneve38d7ef2019-07-15 18:18:03 -070089 reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e));
Michal Vasko90a87d92018-12-10 15:53:44 +010090 }
91
92 return reasons;
93}
94
roman2eab4742023-06-06 10:00:26 +020095#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko90a87d92018-12-10 15:53:44 +010096
Radek Krejci206fcd62015-10-07 15:42:48 +020097static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +010098nc_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 +020099{
Michal Vasko81b33fb2016-09-26 14:57:36 +0200100 size_t readd = 0;
Michal Vasko9d8bee62016-03-03 10:58:24 +0100101 ssize_t r = -1;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200102 int fd, interrupted;
roman6ece9c52022-06-22 09:29:17 +0200103 struct timespec ts_inact_timeout;
Radek Krejci206fcd62015-10-07 15:42:48 +0200104
105 assert(session);
106 assert(buf);
107
Michal Vasko428087d2016-01-14 16:04:28 +0100108 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
109 return -1;
110 }
111
Radek Krejci206fcd62015-10-07 15:42:48 +0200112 if (!count) {
113 return 0;
114 }
115
Michal Vaskod8a74192023-02-06 15:51:50 +0100116 nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +0200117 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200118 interrupted = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100119 switch (session->ti_type) {
120 case NC_TI_NONE:
121 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100122
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100123 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200124 case NC_TI_UNIX:
125 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100126 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200127 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200128 if (r < 0) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200129 if (errno == EAGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100130 r = 0;
131 break;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200132 } else if (errno == EINTR) {
133 r = 0;
134 interrupted = 1;
135 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200136 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200137 ERR(session, "Reading from file descriptor (%d) failed (%s).", fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100138 session->status = NC_STATUS_INVALID;
139 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200140 return -1;
141 }
142 } else if (r == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200143 ERR(session, "Communication file descriptor (%d) unexpectedly closed.", fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100144 session->status = NC_STATUS_INVALID;
145 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200146 return -1;
147 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100148 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200149
roman2eab4742023-06-06 10:00:26 +0200150#ifdef NC_ENABLED_SSH_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100151 case NC_TI_LIBSSH:
152 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200153 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200154 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100155 r = 0;
156 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200157 } else if (r == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200158 ERR(session, "Reading from the SSH channel failed (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100159 session->status = NC_STATUS_INVALID;
160 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200161 return -1;
162 } else if (r == 0) {
163 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200164 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100165 session->status = NC_STATUS_INVALID;
166 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200167 return -1;
168 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100169 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200170 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100171 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200172
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100173 case NC_TI_OPENSSL:
174 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100175 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200176 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200177 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100178 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100179 char *reasons;
180
Michal Vasko0abba6d2018-12-10 14:09:39 +0100181 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200182 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100183 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100184 r = 0;
185 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200186 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200187 ERR(session, "Communication socket unexpectedly closed (OpenSSL).");
Michal Vasko428087d2016-01-14 16:04:28 +0100188 session->status = NC_STATUS_INVALID;
189 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200190 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100191 case SSL_ERROR_SYSCALL:
Michal Vasko0272dc32021-09-03 13:02:20 +0200192 ERR(session, "SSL socket error (%s).", errno ? strerror(errno) : "unexpected EOF");
Michal Vasko0abba6d2018-12-10 14:09:39 +0100193 session->status = NC_STATUS_INVALID;
194 session->term_reason = NC_SESSION_TERM_OTHER;
195 return -1;
196 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100197 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200198 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100199 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100200 session->status = NC_STATUS_INVALID;
201 session->term_reason = NC_SESSION_TERM_OTHER;
202 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200203 default:
Michal Vaskofdba4a32022-01-05 12:13:53 +0100204 ERR(session, "Unknown SSL error occurred (err code %d).", e);
Michal Vasko428087d2016-01-14 16:04:28 +0100205 session->status = NC_STATUS_INVALID;
206 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200207 return -1;
208 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200209 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100210 break;
roman2eab4742023-06-06 10:00:26 +0200211#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100212 }
213
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100214 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100215 /* nothing read */
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200216 if (!interrupted) {
217 usleep(NC_TIMEOUT_STEP);
218 }
Michal Vaskod8a74192023-02-06 15:51:50 +0100219 if ((nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) || (nc_timeouttime_cur_diff(ts_act_timeout) < 1)) {
220 if (nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200221 ERR(session, "Inactive read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100222 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200223 ERR(session, "Active read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100224 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100225 session->status = NC_STATUS_INVALID;
226 session->term_reason = NC_SESSION_TERM_OTHER;
227 return -1;
228 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100229 } else {
230 /* something read */
231 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100232
233 /* reset inactive timeout */
Michal Vaskod8a74192023-02-06 15:51:50 +0100234 nc_timeouttime_get(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100235 }
236
Michal Vasko81b33fb2016-09-26 14:57:36 +0200237 } while (readd < count);
238 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200239
Michal Vasko81b33fb2016-09-26 14:57:36 +0200240 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200241}
242
243static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100244nc_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 +0200245{
246 ssize_t r;
247
248 assert(session);
249 assert(chunk);
250
251 if (!len) {
252 return 0;
253 }
254
Michal Vasko4eb3c312016-03-01 14:09:37 +0100255 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200256 if (!*chunk) {
257 ERRMEM;
258 return -1;
259 }
260
Michal Vasko36c7be82017-02-22 13:37:59 +0100261 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200262 if (r <= 0) {
263 free(*chunk);
264 return -1;
265 }
266
267 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200268 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200269
270 return r;
271}
272
273static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100274nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200275 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200276{
277 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200278 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200279
280 assert(session);
281 assert(endtag);
282
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200283 if (limit && (limit < BUFFERSIZE)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200284 size = limit;
285 } else {
286 size = BUFFERSIZE;
287 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100288 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200289 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200290 ERRMEM;
291 return -1;
292 }
293
294 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100295 while (1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200296 if (limit && (count == limit)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200297 free(chunk);
Michal Vasko05532772021-06-03 12:12:38 +0200298 WRN(session, "Reading limit (%d) reached.", limit);
299 ERR(session, "Invalid input data (missing \"%s\" sequence).", endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200300 return -1;
301 }
302
303 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200304 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200305 /* get more memory */
306 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200307 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100308 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200309 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200310 return -1;
311 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200312 }
313
314 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200315 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
316 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200317 free(chunk);
318 return -1;
319 }
320
David Sedlákfedbc792018-07-04 11:07:07 +0200321 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200322
David Sedlákfedbc792018-07-04 11:07:07 +0200323 for (i = len - matched; i > 0; i--) {
324 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
325 /*part of endtag found */
326 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200327 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200328 } else {
329 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200330 }
331 }
David Sedlákfedbc792018-07-04 11:07:07 +0200332
333 /* whole endtag found */
334 if (matched == len) {
335 break;
336 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200337 }
338
339 /* terminating null byte */
340 chunk[count] = 0;
341
342 if (result) {
343 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200344 } else {
345 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200346 }
347 return count;
348}
349
Michal Vasko77367452021-02-16 16:32:18 +0100350int
351nc_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 +0100352{
Michal Vasko77367452021-02-16 16:32:18 +0100353 int ret = 1, r, io_locked = passing_io_lock;
354 char *data = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100355 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100356 /* use timeout in milliseconds instead seconds */
357 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
358 struct timespec ts_act_timeout;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100359
Michal Vasko77367452021-02-16 16:32:18 +0100360 assert(session && msg);
361 *msg = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100362
363 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200364 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100365 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200366 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100367 }
368
Michal Vaskod8a74192023-02-06 15:51:50 +0100369 nc_timeouttime_get(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
Michal Vasko36c7be82017-02-22 13:37:59 +0100370
Michal Vasko131120a2018-05-29 15:44:02 +0200371 if (!io_locked) {
372 /* SESSION IO LOCK */
373 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100374 if (ret < 1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200375 goto cleanup;
376 }
377 io_locked = 1;
378 }
379
Michal Vasko05ba9df2016-01-13 14:40:27 +0100380 /* read the message */
381 switch (session->version) {
382 case NC_VERSION_10:
Michal Vasko77367452021-02-16 16:32:18 +0100383 r = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &data);
384 if (r == -1) {
385 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200386 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100387 }
388
389 /* cut off the end tag */
Michal Vasko77367452021-02-16 16:32:18 +0100390 data[r - NC_VERSION_10_ENDTAG_LEN] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100391 break;
392 case NC_VERSION_11:
393 while (1) {
Michal Vasko77367452021-02-16 16:32:18 +0100394 r = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
395 if (r == -1) {
396 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200397 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100398 }
Michal Vasko77367452021-02-16 16:32:18 +0100399 r = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
400 if (r == -1) {
401 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200402 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100403 }
404
405 if (!strcmp(chunk, "#\n")) {
406 /* end of chunked framing message */
407 free(chunk);
Michal Vasko77367452021-02-16 16:32:18 +0100408 if (!data) {
Michal Vasko05532772021-06-03 12:12:38 +0200409 ERR(session, "Invalid frame chunk delimiters.");
Michal Vasko77367452021-02-16 16:32:18 +0100410 ret = -2;
411 goto cleanup;
Michal Vasko79df3262016-07-13 13:42:17 +0200412 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100413 break;
414 }
415
416 /* convert string to the size of the following chunk */
417 chunk_len = strtoul(chunk, (char **)NULL, 10);
418 free(chunk);
419 if (!chunk_len) {
Michal Vasko05532772021-06-03 12:12:38 +0200420 ERR(session, "Invalid frame chunk size detected, fatal error.");
Michal Vasko77367452021-02-16 16:32:18 +0100421 ret = -2;
422 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100423 }
424
425 /* now we have size of next chunk, so read the chunk */
Michal Vasko77367452021-02-16 16:32:18 +0100426 r = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
427 if (r == -1) {
428 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200429 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100430 }
431
432 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko77367452021-02-16 16:32:18 +0100433 data = nc_realloc(data, len + chunk_len + 1);
434 if (!data) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100435 ERRMEM;
Michal Vasko77367452021-02-16 16:32:18 +0100436 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200437 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100438 }
Michal Vasko77367452021-02-16 16:32:18 +0100439 memcpy(data + len, chunk, chunk_len);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100440 len += chunk_len;
Michal Vasko77367452021-02-16 16:32:18 +0100441 data[len] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100442 free(chunk);
443 }
444
445 break;
446 }
Michal Vasko131120a2018-05-29 15:44:02 +0200447
448 /* SESSION IO UNLOCK */
449 assert(io_locked);
450 nc_session_io_unlock(session, __func__);
451 io_locked = 0;
452
Michal Vasko05532772021-06-03 12:12:38 +0200453 DBG(session, "Received message:\n%s\n", data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100454
Michal Vasko77367452021-02-16 16:32:18 +0100455 /* build an input structure, eats data */
456 if (ly_in_new_memory(data, msg)) {
457 ret = -1;
458 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100459 }
Michal Vasko77367452021-02-16 16:32:18 +0100460 data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100461
Michal Vasko131120a2018-05-29 15:44:02 +0200462cleanup:
463 if (io_locked) {
Michal Vasko77367452021-02-16 16:32:18 +0100464 /* SESSION IO UNLOCK */
Michal Vasko131120a2018-05-29 15:44:02 +0200465 nc_session_io_unlock(session, __func__);
466 }
Michal Vasko77367452021-02-16 16:32:18 +0100467 free(data);
Michal Vasko131120a2018-05-29 15:44:02 +0200468 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100469}
470
Michal Vasko428087d2016-01-14 16:04:28 +0100471/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
472static int
Michal Vasko131120a2018-05-29 15:44:02 +0200473nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100474{
Radek Krejci5961c702016-07-15 09:15:18 +0200475 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100476 int ret = -2;
477 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100478
479 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200480 ERR(session, "Invalid session to poll.");
Michal Vasko428087d2016-01-14 16:04:28 +0100481 return -1;
482 }
483
484 switch (session->ti_type) {
roman2eab4742023-06-06 10:00:26 +0200485#ifdef NC_ENABLED_SSH_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100486 case NC_TI_LIBSSH:
487 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200488 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100489 if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200490 ERR(session, "SSH channel poll error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100491 session->status = NC_STATUS_INVALID;
492 session->term_reason = NC_SESSION_TERM_OTHER;
493 return -1;
494 } else if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200495 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100496 session->status = NC_STATUS_INVALID;
497 session->term_reason = NC_SESSION_TERM_DROPPED;
498 return -1;
499 } else if (ret > 0) {
500 /* fake it */
501 ret = 1;
502 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100503 } else { /* ret == 0 */
504 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100505 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100506 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100507 case NC_TI_OPENSSL:
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100508 ret = SSL_pending(session->ti.tls);
509 if (ret) {
510 /* some buffered TLS data available */
511 ret = 1;
512 fds.revents = POLLIN;
513 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100514 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100515
516 fds.fd = SSL_get_fd(session->ti.tls);
roman2eab4742023-06-06 10:00:26 +0200517#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200518 /* fallthrough */
Michal Vasko428087d2016-01-14 16:04:28 +0100519 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200520 case NC_TI_UNIX:
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200521 if (session->ti_type == NC_TI_FD) {
Michal Vasko428087d2016-01-14 16:04:28 +0100522 fds.fd = session->ti.fd.in;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200523 } else if (session->ti_type == NC_TI_UNIX) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200524 fds.fd = session->ti.unixsock.sock;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200525 }
Michal Vasko428087d2016-01-14 16:04:28 +0100526
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100527 fds.events = POLLIN;
528 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100529
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100530 sigfillset(&sigmask);
531 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vasko131120a2018-05-29 15:44:02 +0200532 ret = poll(&fds, 1, io_timeout);
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100533 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100534
535 break;
536
537 default:
538 ERRINT;
539 return -1;
540 }
541
542 /* process the poll result, unified ret meaning for poll and ssh_channel poll */
543 if (ret < 0) {
544 /* poll failed - something really bad happened, close the session */
Michal Vasko05532772021-06-03 12:12:38 +0200545 ERR(session, "poll error (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100546 session->status = NC_STATUS_INVALID;
547 session->term_reason = NC_SESSION_TERM_OTHER;
548 return -1;
549 } else { /* status > 0 */
550 /* in case of standard (non-libssh) poll, there still can be an error */
Michal Vasko428087d2016-01-14 16:04:28 +0100551 if (fds.revents & POLLERR) {
Michal Vasko05532772021-06-03 12:12:38 +0200552 ERR(session, "Communication channel error.");
Michal Vasko428087d2016-01-14 16:04:28 +0100553 session->status = NC_STATUS_INVALID;
554 session->term_reason = NC_SESSION_TERM_OTHER;
555 return -1;
556 }
Robin Jarryf732adc2020-05-15 11:18:38 +0200557 /* Some poll() implementations may return POLLHUP|POLLIN when the other
558 * side has closed but there is data left to read in the buffer. */
559 if ((fds.revents & POLLHUP) && !(fds.revents & POLLIN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200560 ERR(session, "Communication channel unexpectedly closed.");
Robin Jarryf732adc2020-05-15 11:18:38 +0200561 session->status = NC_STATUS_INVALID;
562 session->term_reason = NC_SESSION_TERM_DROPPED;
563 return -1;
564 }
Michal Vasko428087d2016-01-14 16:04:28 +0100565 }
566
567 return ret;
568}
569
Michal Vasko77367452021-02-16 16:32:18 +0100570int
571nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **msg)
Radek Krejci206fcd62015-10-07 15:42:48 +0200572{
Michal Vasko428087d2016-01-14 16:04:28 +0100573 int ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200574
Michal Vasko77367452021-02-16 16:32:18 +0100575 assert(msg);
576 *msg = NULL;
Radek Krejci206fcd62015-10-07 15:42:48 +0200577
Michal Vasko428087d2016-01-14 16:04:28 +0100578 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200579 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100580 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200581 }
582
Michal Vasko131120a2018-05-29 15:44:02 +0200583 /* SESSION IO LOCK */
584 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100585 if (ret < 1) {
586 return ret;
Michal Vasko131120a2018-05-29 15:44:02 +0200587 }
588
589 ret = nc_read_poll(session, io_timeout);
Michal Vasko77367452021-02-16 16:32:18 +0100590 if (ret < 1) {
591 /* timed out or error */
Michal Vasko131120a2018-05-29 15:44:02 +0200592
593 /* SESSION IO UNLOCK */
594 nc_session_io_unlock(session, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100595 return ret;
Radek Krejci206fcd62015-10-07 15:42:48 +0200596 }
597
Michal Vasko131120a2018-05-29 15:44:02 +0200598 /* SESSION IO LOCK passed down */
Michal Vasko77367452021-02-16 16:32:18 +0100599 return nc_read_msg_io(session, io_timeout, msg, 1);
Radek Krejci206fcd62015-10-07 15:42:48 +0200600}
Radek Krejcife0b3472015-10-12 13:43:42 +0200601
Michal Vasko428087d2016-01-14 16:04:28 +0100602/* does not really log, only fatal errors */
603int
604nc_session_is_connected(struct nc_session *session)
605{
606 int ret;
607 struct pollfd fds;
608
609 switch (session->ti_type) {
610 case NC_TI_FD:
611 fds.fd = session->ti.fd.in;
612 break;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200613 case NC_TI_UNIX:
614 fds.fd = session->ti.unixsock.sock;
615 break;
roman2eab4742023-06-06 10:00:26 +0200616#ifdef NC_ENABLED_SSH_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100617 case NC_TI_LIBSSH:
Michal Vasko840a8a62017-02-07 10:56:34 +0100618 return ssh_is_connected(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100619 case NC_TI_OPENSSL:
620 fds.fd = SSL_get_fd(session->ti.tls);
621 break;
roman2eab4742023-06-06 10:00:26 +0200622#endif /* NC_ENABLED_SSH_TLS */
Michal Vaskof945da52018-02-15 08:45:13 +0100623 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100624 return 0;
625 }
626
Michal Vasko840a8a62017-02-07 10:56:34 +0100627 if (fds.fd == -1) {
628 return 0;
629 }
630
Michal Vasko428087d2016-01-14 16:04:28 +0100631 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100632 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100633
634 errno = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200635 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR)) {}
Michal Vasko428087d2016-01-14 16:04:28 +0100636
637 if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200638 ERR(session, "poll failed (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100639 return 0;
640 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
641 return 0;
642 }
643
644 return 1;
645}
646
Radek Krejcife0b3472015-10-12 13:43:42 +0200647#define WRITE_BUFSIZE (2 * BUFFERSIZE)
648struct wclb_arg {
649 struct nc_session *session;
650 char buf[WRITE_BUFSIZE];
651 size_t len;
652};
653
Michal Vasko964e1732016-09-23 13:39:33 +0200654static int
Michal Vasko428087d2016-01-14 16:04:28 +0100655nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200656{
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200657 int c, fd, interrupted;
Michal Vasko964e1732016-09-23 13:39:33 +0200658 size_t written = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200659
roman2eab4742023-06-06 10:00:26 +0200660#ifdef NC_ENABLED_SSH_TLS
Michal Vaskoe2357e92016-10-05 14:20:47 +0200661 unsigned long e;
roman2eab4742023-06-06 10:00:26 +0200662#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko964e1732016-09-23 13:39:33 +0200663
Michal Vasko428087d2016-01-14 16:04:28 +0100664 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
665 return -1;
666 }
667
668 /* prevent SIGPIPE this way */
669 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +0200670 ERR(session, "Communication socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100671 session->status = NC_STATUS_INVALID;
672 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100673 return -1;
674 }
675
Michal Vasko05532772021-06-03 12:12:38 +0200676 DBG(session, "Sending message:\n%.*s\n", count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200677
Michal Vasko81b33fb2016-09-26 14:57:36 +0200678 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200679 interrupted = 0;
Michal Vasko964e1732016-09-23 13:39:33 +0200680 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200681 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200682 case NC_TI_UNIX:
683 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
684 c = write(fd, (char *)(buf + written), count - written);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200685 if ((c < 0) && (errno == EAGAIN)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200686 c = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200687 } else if ((c < 0) && (errno == EINTR)) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200688 c = 0;
689 interrupted = 1;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200690 } else if (c < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200691 ERR(session, "socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200692 return -1;
693 }
694 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200695
roman2eab4742023-06-06 10:00:26 +0200696#ifdef NC_ENABLED_SSH_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200697 case NC_TI_LIBSSH:
698 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
699 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200700 ERR(session, "SSH channel unexpectedly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200701 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200702 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko964e1732016-09-23 13:39:33 +0200703 }
704 session->status = NC_STATUS_INVALID;
705 session->term_reason = NC_SESSION_TERM_DROPPED;
706 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100707 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200708 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200709 if ((c == SSH_ERROR) || (c == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200710 ERR(session, "SSH channel write failed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200711 return -1;
712 }
713 break;
Michal Vasko964e1732016-09-23 13:39:33 +0200714 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200715 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200716 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100717 char *reasons;
718
Michal Vasko964e1732016-09-23 13:39:33 +0200719 switch ((e = SSL_get_error(session->ti.tls, c))) {
720 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200721 ERR(session, "SSL connection was properly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200722 return -1;
723 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100724 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200725 c = 0;
726 break;
727 case SSL_ERROR_SYSCALL:
Michal Vasko05532772021-06-03 12:12:38 +0200728 ERR(session, "SSL socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200729 return -1;
730 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100731 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200732 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100733 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200734 return -1;
735 default:
Michal Vaskofdba4a32022-01-05 12:13:53 +0100736 ERR(session, "Unknown SSL error occurred (err code %d).", e);
Michal Vasko964e1732016-09-23 13:39:33 +0200737 return -1;
738 }
739 }
740 break;
roman2eab4742023-06-06 10:00:26 +0200741#endif /* NC_ENABLED_SSH_TLS */
Michal Vasko339eea82016-09-29 11:42:36 +0200742 default:
743 ERRINT;
744 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200745 }
746
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200747 if ((c == 0) && !interrupted) {
Michal Vasko964e1732016-09-23 13:39:33 +0200748 /* we must wait */
749 usleep(NC_TIMEOUT_STEP);
750 }
751
752 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200753 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200754
Michal Vasko964e1732016-09-23 13:39:33 +0200755 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200756}
757
Michal Vasko428087d2016-01-14 16:04:28 +0100758static int
759nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100760{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100761 int ret = 0, c;
Claus Klein22091912020-01-20 13:45:47 +0100762 char chunksize[24];
Michal Vasko086311b2016-01-08 09:53:11 +0100763
Claus Klein22091912020-01-20 13:45:47 +0100764 // warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=]
Michal Vasko086311b2016-01-08 09:53:11 +0100765 if (session->version == NC_VERSION_11) {
766 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100767 ret = nc_write(session, chunksize, strlen(chunksize));
768 if (ret == -1) {
769 return -1;
770 }
Michal Vasko086311b2016-01-08 09:53:11 +0100771 }
Michal Vasko428087d2016-01-14 16:04:28 +0100772
773 c = nc_write(session, buf, count);
774 if (c == -1) {
775 return -1;
776 }
777 ret += c;
778
779 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100780}
781
Radek Krejcife0b3472015-10-12 13:43:42 +0200782static int
Michal Vasko428087d2016-01-14 16:04:28 +0100783nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200784{
Michal Vasko428087d2016-01-14 16:04:28 +0100785 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100786
Michal Vasko428087d2016-01-14 16:04:28 +0100787 if (session->version == NC_VERSION_11) {
788 ret = nc_write(session, "\n##\n", 4);
789 } else {
790 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200791 }
792
Michal Vasko428087d2016-01-14 16:04:28 +0100793 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200794}
795
Michal Vasko428087d2016-01-14 16:04:28 +0100796static int
797nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200798{
Michal Vasko428087d2016-01-14 16:04:28 +0100799 int ret = 0;
800
Radek Krejcife0b3472015-10-12 13:43:42 +0200801 /* flush current buffer */
802 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100803 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200804 warg->len = 0;
805 }
Michal Vasko428087d2016-01-14 16:04:28 +0100806
807 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200808}
809
810static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100811nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200812{
Michal Vasko428087d2016-01-14 16:04:28 +0100813 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100814 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200815 struct wclb_arg *warg = (struct wclb_arg *)arg;
816
817 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100818 c = nc_write_clb_flush(warg);
819 if (c == -1) {
820 return -1;
821 }
822 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200823
824 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100825 c = nc_write_endtag(warg->session);
826 if (c == -1) {
827 return -1;
828 }
829 ret += c;
830
831 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200832 }
833
834 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
835 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100836 c = nc_write_clb_flush(warg);
837 if (c == -1) {
838 return -1;
839 }
840 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200841 }
Michal Vasko428087d2016-01-14 16:04:28 +0100842
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200843 if (!xmlcontent && (count > WRITE_BUFSIZE)) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200844 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100845 c = nc_write_starttag_and_msg(warg->session, buf, count);
846 if (c == -1) {
847 return -1;
848 }
849 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200850 } else {
851 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100852 if (xmlcontent) {
853 for (l = 0; l < count; l++) {
854 if (warg->len + 5 >= WRITE_BUFSIZE) {
855 /* buffer is full */
856 c = nc_write_clb_flush(warg);
857 if (c == -1) {
858 return -1;
859 }
860 }
861
862 switch (((char *)buf)[l]) {
863 case '&':
864 ret += 5;
865 memcpy(&warg->buf[warg->len], "&amp;", 5);
866 warg->len += 5;
867 break;
868 case '<':
869 ret += 4;
870 memcpy(&warg->buf[warg->len], "&lt;", 4);
871 warg->len += 4;
872 break;
873 case '>':
874 /* not needed, just for readability */
875 ret += 4;
876 memcpy(&warg->buf[warg->len], "&gt;", 4);
877 warg->len += 4;
878 break;
879 default:
880 ret++;
881 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
882 warg->len++;
883 }
884 }
885 } else {
886 memcpy(&warg->buf[warg->len], buf, count);
887 warg->len += count; /* is <= WRITE_BUFSIZE */
888 ret += count;
889 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200890 }
891
Michal Vasko428087d2016-01-14 16:04:28 +0100892 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200893}
894
Radek Krejci047300e2016-03-08 16:46:58 +0100895static ssize_t
896nc_write_xmlclb(void *arg, const void *buf, size_t count)
897{
Michal Vasko77367452021-02-16 16:32:18 +0100898 ssize_t r;
Radek Krejci047300e2016-03-08 16:46:58 +0100899
Michal Vasko77367452021-02-16 16:32:18 +0100900 r = nc_write_clb(arg, buf, count, 0);
901 if (r == -1) {
902 return -1;
Michal Vasko08611b32016-12-05 13:30:37 +0100903 }
904
Michal Vasko77367452021-02-16 16:32:18 +0100905 /* always return what libyang expects, simply that all the characters were printed */
906 return count;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100907}
908
Michal Vasko131120a2018-05-29 15:44:02 +0200909/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
910NC_MSG_TYPE
911nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200912{
Radek Krejcid116db42016-01-08 15:36:30 +0100913 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +0200914 int count, ret;
Michal Vasko70bf2222021-10-26 10:45:15 +0200915 const char *attrs, *str;
Michal Vaskoff7286b2021-07-09 13:13:11 +0200916 struct lyd_node *op, *reply_envp, *node, *next;
Michal Vasko77367452021-02-16 16:32:18 +0100917 struct lyd_node_opaq *rpc_envp;
Radek Krejci93e80222016-10-03 13:34:25 +0200918 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100919 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +0100920 char *buf;
Radek Krejcid116db42016-01-08 15:36:30 +0100921 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200922 const char **capabilities;
Michal Vasko77367452021-02-16 16:32:18 +0100923 uint32_t *sid = NULL, i, wd = 0;
924 LY_ERR lyrc;
Radek Krejcife0b3472015-10-12 13:43:42 +0200925
Michal Vasko428087d2016-01-14 16:04:28 +0100926 assert(session);
927
928 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200929 ERR(session, "Invalid session to write to.");
Michal Vasko131120a2018-05-29 15:44:02 +0200930 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100931 }
932
Radek Krejcife0b3472015-10-12 13:43:42 +0200933 arg.session = session;
934 arg.len = 0;
935
Michal Vasko131120a2018-05-29 15:44:02 +0200936 /* SESSION IO LOCK */
937 ret = nc_session_io_lock(session, io_timeout, __func__);
938 if (ret < 0) {
939 return NC_MSG_ERROR;
940 } else if (!ret) {
941 return NC_MSG_WOULDBLOCK;
942 }
943
944 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +0200945
Radek Krejcife0b3472015-10-12 13:43:42 +0200946 switch (type) {
947 case NC_MSG_RPC:
Michal Vasko77367452021-02-16 16:32:18 +0100948 op = va_arg(ap, struct lyd_node *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200949 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100950
Michal Vasko70bf2222021-10-26 10:45:15 +0200951 /* <rpc> open */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200952 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%" PRIu64 "\"%s>",
Michal Vasko77367452021-02-16 16:32:18 +0100953 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +0100954 if (count == -1) {
955 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200956 ret = NC_MSG_ERROR;
957 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100958 }
Radek Krejci047300e2016-03-08 16:46:58 +0100959 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200960 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +0200961
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100962 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200963 /* <action> open */
964 str = "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">";
965 nc_write_clb((void *)&arg, str, strlen(str), 0);
966 }
967
968 /* rpc data */
Michal Vasko6b70b822022-01-21 08:39:16 +0100969 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 +0200970 ret = NC_MSG_ERROR;
971 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +0200972 }
Michal Vasko70bf2222021-10-26 10:45:15 +0200973
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100974 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200975 /* <action> close */
976 str = "</action>";
977 nc_write_clb((void *)&arg, str, strlen(str), 0);
978 }
979
980 /* <rpc> close */
981 str = "</rpc>";
982 nc_write_clb((void *)&arg, str, strlen(str), 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200983
Michal Vasko2e6defd2016-10-07 15:48:15 +0200984 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +0200985 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100986
Radek Krejcife0b3472015-10-12 13:43:42 +0200987 case NC_MSG_REPLY:
Michal Vasko77367452021-02-16 16:32:18 +0100988 rpc_envp = va_arg(ap, struct lyd_node_opaq *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100989 reply = va_arg(ap, struct nc_server_reply *);
990
Michal Vasko77367452021-02-16 16:32:18 +0100991 if (!rpc_envp) {
992 /* can be NULL if replying with a malformed-message error */
993 nc_write_clb((void *)&arg, "<rpc-reply xmlns=\"" NC_NS_BASE "\">", 18 + strlen(NC_NS_BASE) + 2, 0);
994
995 assert(reply->type == NC_RPL_ERROR);
996 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_server_reply_error *)reply)->err, LYD_XML,
997 LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)) {
998 ret = NC_MSG_ERROR;
999 goto cleanup;
1000 }
1001
1002 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1003 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001004 }
1005
Michal Vasko77367452021-02-16 16:32:18 +01001006 /* build a rpc-reply opaque node that can be simply printed */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001007 if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns,
1008 &reply_envp)) {
Michal Vasko77367452021-02-16 16:32:18 +01001009 ERRINT;
1010 ret = NC_MSG_ERROR;
1011 goto cleanup;
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001012 }
Michal Vasko77367452021-02-16 16:32:18 +01001013
Michal Vasko05ba9df2016-01-13 14:40:27 +01001014 switch (reply->type) {
1015 case NC_RPL_OK:
Michal Vasko77367452021-02-16 16:32:18 +01001016 if (lyd_new_opaq2(reply_envp, NULL, "ok", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, NULL)) {
1017 lyd_free_tree(reply_envp);
1018
1019 ERRINT;
1020 ret = NC_MSG_ERROR;
1021 goto cleanup;
Michal Vasko08611b32016-12-05 13:30:37 +01001022 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001023 break;
1024 case NC_RPL_DATA:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001025 switch (((struct nc_server_reply_data *)reply)->wd) {
Radek Krejci36dfdb32016-09-01 16:56:35 +02001026 case NC_WD_UNKNOWN:
1027 case NC_WD_EXPLICIT:
Michal Vasko77367452021-02-16 16:32:18 +01001028 wd = LYD_PRINT_WD_EXPLICIT;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001029 break;
1030 case NC_WD_TRIM:
Michal Vasko77367452021-02-16 16:32:18 +01001031 wd = LYD_PRINT_WD_TRIM;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001032 break;
1033 case NC_WD_ALL:
Michal Vasko77367452021-02-16 16:32:18 +01001034 wd = LYD_PRINT_WD_ALL;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001035 break;
1036 case NC_WD_ALL_TAG:
Michal Vasko77367452021-02-16 16:32:18 +01001037 wd = LYD_PRINT_WD_ALL_TAG;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001038 break;
1039 }
Michal Vasko77367452021-02-16 16:32:18 +01001040
1041 node = ((struct nc_server_reply_data *)reply)->data;
1042 assert(node->schema->nodetype & (LYS_RPC | LYS_ACTION));
Michal Vaskoff7286b2021-07-09 13:13:11 +02001043 LY_LIST_FOR_SAFE(lyd_child(node), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001044 /* temporary */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001045 lyd_insert_child(reply_envp, node);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001046 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001047 break;
1048 case NC_RPL_ERROR:
Michal Vasko77367452021-02-16 16:32:18 +01001049 /* temporary */
1050 lyd_insert_child(reply_envp, ((struct nc_server_reply_error *)reply)->err);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001051 break;
1052 default:
1053 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001054 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001055 ret = NC_MSG_ERROR;
1056 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001057 }
Michal Vasko77367452021-02-16 16:32:18 +01001058
1059 /* temporary */
1060 ((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr;
1061
1062 /* print */
1063 lyrc = lyd_print_clb(nc_write_xmlclb, (void *)&arg, reply_envp, LYD_XML, LYD_PRINT_SHRINK | wd);
1064 ((struct lyd_node_opaq *)reply_envp)->attr = NULL;
1065
1066 /* cleanup */
1067 switch (reply->type) {
1068 case NC_RPL_OK:
1069 /* just free everything */
1070 lyd_free_tree(reply_envp);
1071 break;
1072 case NC_RPL_DATA:
Michal Vaskoff7286b2021-07-09 13:13:11 +02001073 LY_LIST_FOR_SAFE(lyd_child(reply_envp), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001074 /* connect back to the reply structure */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001075 lyd_insert_child(((struct nc_server_reply_data *)reply)->data, node);
Michal Vasko77367452021-02-16 16:32:18 +01001076 }
1077 lyd_free_tree(reply_envp);
1078 break;
1079 case NC_RPL_ERROR:
1080 /* unlink from the data reply */
1081 lyd_unlink_tree(lyd_child(reply_envp));
1082 lyd_free_tree(reply_envp);
1083 break;
1084 default:
1085 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001086 }
Michal Vasko77367452021-02-16 16:32:18 +01001087
1088 if (lyrc) {
1089 ret = NC_MSG_ERROR;
1090 goto cleanup;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001091 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001092 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001093
Radek Krejcife0b3472015-10-12 13:43:42 +02001094 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001095 notif = va_arg(ap, struct nc_server_notif *);
1096
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001097 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF "\">", 21 + 47 + 2, 0);
Radek Krejci93e80222016-10-03 13:34:25 +02001098 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1099 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1100 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko77367452021-02-16 16:32:18 +01001101 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->ntf, LYD_XML, LYD_PRINT_SHRINK)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001102 ret = NC_MSG_ERROR;
1103 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001104 }
mohitarora24878b2962016-11-09 18:45:33 -05001105 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001106 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001107
Radek Krejcid116db42016-01-08 15:36:30 +01001108 case NC_MSG_HELLO:
1109 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001110 ret = NC_MSG_ERROR;
1111 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001112 }
1113 capabilities = va_arg(ap, const char **);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001114 sid = va_arg(ap, uint32_t *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001115
Radek Krejcid116db42016-01-08 15:36:30 +01001116 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001117 if (count == -1) {
1118 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001119 ret = NC_MSG_ERROR;
1120 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001121 }
Radek Krejci047300e2016-03-08 16:46:58 +01001122 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001123 free(buf);
1124 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001125 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1126 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1127 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001128 }
1129 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001130 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001131 if (count == -1) {
1132 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001133 ret = NC_MSG_ERROR;
1134 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001135 }
Radek Krejci047300e2016-03-08 16:46:58 +01001136 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001137 free(buf);
1138 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001139 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001140 }
Radek Krejcid116db42016-01-08 15:36:30 +01001141 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001142
Radek Krejcife0b3472015-10-12 13:43:42 +02001143 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001144 ret = NC_MSG_ERROR;
1145 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001146 }
1147
1148 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001149 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001150
Michal Vasko428087d2016-01-14 16:04:28 +01001151 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1152 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001153 ret = NC_MSG_ERROR;
1154 } else {
1155 /* specific message successfully sent */
1156 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001157 }
1158
Michal Vasko131120a2018-05-29 15:44:02 +02001159cleanup:
1160 va_end(ap);
1161 nc_session_io_unlock(session, __func__);
1162 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001163}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001164
1165void *
1166nc_realloc(void *ptr, size_t size)
1167{
1168 void *ret;
1169
1170 ret = realloc(ptr, size);
1171 if (!ret) {
1172 free(ptr);
1173 }
1174
1175 return ret;
1176}
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001177
1178struct passwd *
romanf6e32012023-04-24 15:51:26 +02001179nc_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 +02001180{
1181 struct passwd *pwd = NULL;
Michal Vasko7e06ee52021-11-02 08:53:05 +01001182 long sys_size;
1183 int ret;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001184
1185 do {
Michal Vasko7e06ee52021-11-02 08:53:05 +01001186 if (!*buf_size) {
1187 /* learn suitable buffer size */
1188 sys_size = sysconf(_SC_GETPW_R_SIZE_MAX);
1189 *buf_size = (sys_size == -1) ? 2048 : sys_size;
1190 } else {
1191 /* enlarge buffer */
1192 *buf_size += 2048;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001193 }
1194
Michal Vasko7e06ee52021-11-02 08:53:05 +01001195 /* allocate some buffer */
1196 *buf = nc_realloc(*buf, *buf_size);
1197 if (!*buf) {
1198 ERRMEM;
1199 return NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001200 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001201
romanf6e32012023-04-24 15:51:26 +02001202 if (username) {
1203 ret = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd);
1204 } else {
1205 ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd);
1206 }
Michal Vasko7e06ee52021-11-02 08:53:05 +01001207 } while (ret && (ret == ERANGE));
1208
1209 if (ret) {
romanf6e32012023-04-24 15:51:26 +02001210 if (username) {
1211 ERR(NULL, "Retrieving username \"%s\" passwd entry failed (%s).", username, strerror(ret));
1212 } else {
1213 ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long)uid, strerror(ret));
1214 }
Michal Vasko7e06ee52021-11-02 08:53:05 +01001215 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001216 return pwd;
1217}