blob: efcb1ab2e9ae1cc88f61dcf835f93088ad30d45a [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
Michal Vasko4848ac82021-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 Vasko4848ac82021-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 */
Radek Krejci206fcd62015-10-07 15:42:48 +020017#include <assert.h>
18#include <errno.h>
Michal Vasko3512e402016-01-28 16:22:34 +010019#include <inttypes.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020020#include <poll.h>
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +020021#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020022#include <signal.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020023#include <stdarg.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
Michal Vasko964e1732016-09-23 13:39:33 +020030#ifdef NC_ENABLED_TLS
31# include <openssl/err.h>
32#endif
33
Radek Krejci206fcd62015-10-07 15:42:48 +020034#include <libyang/libyang.h>
35
Michal Vasko9e8ac262020-04-07 13:06:45 +020036#include "compat.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020037#include "libnetconf.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020038
Michal Vasko8fe604c2020-02-10 15:25:04 +010039const char *nc_msgtype2str[] = {
40 "error",
41 "would block",
42 "no message",
43 "hello message",
44 "bad hello message",
45 "RPC message",
46 "rpc-reply message",
47 "rpc-reply message with wrong ID",
48 "notification message",
49};
50
Radek Krejcife0b3472015-10-12 13:43:42 +020051#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020052
Michal Vasko90a87d92018-12-10 15:53:44 +010053#ifdef NC_ENABLED_TLS
54
55static char *
56nc_ssl_error_get_reasons(void)
57{
58 unsigned int e;
59 int reason_size, reason_len;
60 char *reasons = NULL;
61
62 reason_size = 1;
63 reason_len = 0;
64 while ((e = ERR_get_error())) {
65 if (reason_len) {
66 /* add "; " */
67 reason_size += 2;
68 reasons = nc_realloc(reasons, reason_size);
69 if (!reasons) {
70 ERRMEM;
71 return NULL;
72 }
73 reason_len += sprintf(reasons + reason_len, "; ");
74 }
75 reason_size += strlen(ERR_reason_error_string(e));
76 reasons = nc_realloc(reasons, reason_size);
77 if (!reasons) {
78 ERRMEM;
79 return NULL;
80 }
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
87#endif
88
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;
Michal Vasko36c7be82017-02-22 13:37:59 +010095 struct timespec ts_cur, 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 Vasko77a6abe2017-10-05 10:02:20 +0200108 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100109 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko81b33fb2016-09-26 14:57:36 +0200110 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200111 interrupted = 0;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100112 switch (session->ti_type) {
113 case NC_TI_NONE:
114 return 0;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100115
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100116 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200117 case NC_TI_UNIX:
118 fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100119 /* read via standard file descriptor */
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200120 r = read(fd, buf + readd, count - readd);
Radek Krejci206fcd62015-10-07 15:42:48 +0200121 if (r < 0) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200122 if (errno == EAGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100123 r = 0;
124 break;
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200125 } else if (errno == EINTR) {
126 r = 0;
127 interrupted = 1;
128 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200129 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200130 ERR(session, "Reading from file descriptor (%d) failed (%s).", fd, strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100131 session->status = NC_STATUS_INVALID;
132 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200133 return -1;
134 }
135 } else if (r == 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200136 ERR(session, "Communication file descriptor (%d) unexpectedly closed.", fd);
Michal Vasko428087d2016-01-14 16:04:28 +0100137 session->status = NC_STATUS_INVALID;
138 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200139 return -1;
140 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100141 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200142
Radek Krejci53691be2016-02-22 13:58:37 +0100143#ifdef NC_ENABLED_SSH
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100144 case NC_TI_LIBSSH:
145 /* read via libssh */
Michal Vasko81b33fb2016-09-26 14:57:36 +0200146 r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200147 if (r == SSH_AGAIN) {
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100148 r = 0;
149 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200150 } else if (r == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200151 ERR(session, "Reading from the SSH channel failed (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100152 session->status = NC_STATUS_INVALID;
153 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejci206fcd62015-10-07 15:42:48 +0200154 return -1;
155 } else if (r == 0) {
156 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200157 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100158 session->status = NC_STATUS_INVALID;
159 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejci206fcd62015-10-07 15:42:48 +0200160 return -1;
161 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100162 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200163 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100164 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200165#endif
166
Radek Krejci53691be2016-02-22 13:58:37 +0100167#ifdef NC_ENABLED_TLS
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100168 case NC_TI_OPENSSL:
169 /* read via OpenSSL */
Michal Vasko90a87d92018-12-10 15:53:44 +0100170 ERR_clear_error();
Michal Vasko81b33fb2016-09-26 14:57:36 +0200171 r = SSL_read(session->ti.tls, buf + readd, count - readd);
Radek Krejcid0046592015-10-08 12:52:02 +0200172 if (r <= 0) {
Michal Vasko0abba6d2018-12-10 14:09:39 +0100173 int e;
Michal Vasko90a87d92018-12-10 15:53:44 +0100174 char *reasons;
175
Michal Vasko0abba6d2018-12-10 14:09:39 +0100176 switch (e = SSL_get_error(session->ti.tls, r)) {
Radek Krejcid0046592015-10-08 12:52:02 +0200177 case SSL_ERROR_WANT_READ:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100178 case SSL_ERROR_WANT_WRITE:
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100179 r = 0;
180 break;
Radek Krejcid0046592015-10-08 12:52:02 +0200181 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200182 ERR(session, "Communication socket unexpectedly closed (OpenSSL).");
Michal Vasko428087d2016-01-14 16:04:28 +0100183 session->status = NC_STATUS_INVALID;
184 session->term_reason = NC_SESSION_TERM_DROPPED;
Radek Krejcid0046592015-10-08 12:52:02 +0200185 return -1;
Michal Vasko0abba6d2018-12-10 14:09:39 +0100186 case SSL_ERROR_SYSCALL:
Michal Vasko0272dc32021-09-03 13:02:20 +0200187 ERR(session, "SSL socket error (%s).", errno ? strerror(errno) : "unexpected EOF");
Michal Vasko0abba6d2018-12-10 14:09:39 +0100188 session->status = NC_STATUS_INVALID;
189 session->term_reason = NC_SESSION_TERM_OTHER;
190 return -1;
191 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100192 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200193 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100194 free(reasons);
Michal Vasko0abba6d2018-12-10 14:09:39 +0100195 session->status = NC_STATUS_INVALID;
196 session->term_reason = NC_SESSION_TERM_OTHER;
197 return -1;
Radek Krejcid0046592015-10-08 12:52:02 +0200198 default:
Michal Vasko05532772021-06-03 12:12:38 +0200199 ERR(session, "Unknown SSL error occured (err code %d).", e);
Michal Vasko428087d2016-01-14 16:04:28 +0100200 session->status = NC_STATUS_INVALID;
201 session->term_reason = NC_SESSION_TERM_OTHER;
Radek Krejcid0046592015-10-08 12:52:02 +0200202 return -1;
203 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200204 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100205 break;
Radek Krejci206fcd62015-10-07 15:42:48 +0200206#endif
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100207 }
208
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100209 if (r == 0) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100210 /* nothing read */
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200211 if (!interrupted) {
212 usleep(NC_TIMEOUT_STEP);
213 }
Michal Vasko77a6abe2017-10-05 10:02:20 +0200214 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +0100215 if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) {
216 if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200217 ERR(session, "Inactive read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100218 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200219 ERR(session, "Active read timeout elapsed.");
Michal Vaskof471fa02017-02-15 10:48:12 +0100220 }
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100221 session->status = NC_STATUS_INVALID;
222 session->term_reason = NC_SESSION_TERM_OTHER;
223 return -1;
224 }
Michal Vaskof471fa02017-02-15 10:48:12 +0100225 } else {
226 /* something read */
227 readd += r;
Michal Vasko36c7be82017-02-22 13:37:59 +0100228
229 /* reset inactive timeout */
Michal Vasko77a6abe2017-10-05 10:02:20 +0200230 nc_gettimespec_mono(&ts_inact_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100231 nc_addtimespec(&ts_inact_timeout, inact_timeout);
Michal Vasko6b7c42e2016-03-02 15:46:41 +0100232 }
233
Michal Vasko81b33fb2016-09-26 14:57:36 +0200234 } while (readd < count);
235 buf[count] = '\0';
Radek Krejci206fcd62015-10-07 15:42:48 +0200236
Michal Vasko81b33fb2016-09-26 14:57:36 +0200237 return (ssize_t)readd;
Radek Krejci206fcd62015-10-07 15:42:48 +0200238}
239
240static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100241nc_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 +0200242{
243 ssize_t r;
244
245 assert(session);
246 assert(chunk);
247
248 if (!len) {
249 return 0;
250 }
251
Michal Vasko4eb3c312016-03-01 14:09:37 +0100252 *chunk = malloc((len + 1) * sizeof **chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200253 if (!*chunk) {
254 ERRMEM;
255 return -1;
256 }
257
Michal Vasko36c7be82017-02-22 13:37:59 +0100258 r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout);
Radek Krejci206fcd62015-10-07 15:42:48 +0200259 if (r <= 0) {
260 free(*chunk);
261 return -1;
262 }
263
264 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200265 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200266
267 return r;
268}
269
270static ssize_t
Michal Vasko36c7be82017-02-22 13:37:59 +0100271nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200272 struct timespec *ts_act_timeout, char **result)
Radek Krejci206fcd62015-10-07 15:42:48 +0200273{
274 char *chunk = NULL;
David Sedlákfedbc792018-07-04 11:07:07 +0200275 size_t size, count = 0, r, len, i, matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200276
277 assert(session);
278 assert(endtag);
279
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200280 if (limit && (limit < BUFFERSIZE)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200281 size = limit;
282 } else {
283 size = BUFFERSIZE;
284 }
Michal Vasko4eb3c312016-03-01 14:09:37 +0100285 chunk = malloc((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200286 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200287 ERRMEM;
288 return -1;
289 }
290
291 len = strlen(endtag);
Michal Vasko428087d2016-01-14 16:04:28 +0100292 while (1) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200293 if (limit && (count == limit)) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200294 free(chunk);
Michal Vasko05532772021-06-03 12:12:38 +0200295 WRN(session, "Reading limit (%d) reached.", limit);
296 ERR(session, "Invalid input data (missing \"%s\" sequence).", endtag);
Radek Krejci206fcd62015-10-07 15:42:48 +0200297 return -1;
298 }
299
300 /* resize buffer if needed */
David Sedlákfedbc792018-07-04 11:07:07 +0200301 if ((count + (len - matched)) >= size) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200302 /* get more memory */
303 size = size + BUFFERSIZE;
Radek Krejcif6d9aef2018-08-17 11:50:53 +0200304 chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100305 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200306 ERRMEM;
Radek Krejci206fcd62015-10-07 15:42:48 +0200307 return -1;
308 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200309 }
310
311 /* get another character */
David Sedlákfedbc792018-07-04 11:07:07 +0200312 r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout);
313 if (r != len - matched) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200314 free(chunk);
315 return -1;
316 }
317
David Sedlákfedbc792018-07-04 11:07:07 +0200318 count += len - matched;
Radek Krejci206fcd62015-10-07 15:42:48 +0200319
David Sedlákfedbc792018-07-04 11:07:07 +0200320 for (i = len - matched; i > 0; i--) {
321 if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) {
322 /*part of endtag found */
323 matched += i;
Radek Krejci206fcd62015-10-07 15:42:48 +0200324 break;
David Sedlákfedbc792018-07-04 11:07:07 +0200325 } else {
326 matched = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200327 }
328 }
David Sedlákfedbc792018-07-04 11:07:07 +0200329
330 /* whole endtag found */
331 if (matched == len) {
332 break;
333 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200334 }
335
336 /* terminating null byte */
337 chunk[count] = 0;
338
339 if (result) {
340 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200341 } else {
342 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200343 }
344 return count;
345}
346
Michal Vasko77367452021-02-16 16:32:18 +0100347int
348nc_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 +0100349{
Michal Vasko77367452021-02-16 16:32:18 +0100350 int ret = 1, r, io_locked = passing_io_lock;
351 char *data = NULL, *chunk;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100352 uint64_t chunk_len, len = 0;
Michal Vasko36c7be82017-02-22 13:37:59 +0100353 /* use timeout in milliseconds instead seconds */
354 uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
355 struct timespec ts_act_timeout;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100356
Michal Vasko77367452021-02-16 16:32:18 +0100357 assert(session && msg);
358 *msg = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100359
360 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200361 ERR(session, "Invalid session to read from.");
Michal Vasko77367452021-02-16 16:32:18 +0100362 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200363 goto cleanup;
Michal Vasko428087d2016-01-14 16:04:28 +0100364 }
365
Michal Vasko77a6abe2017-10-05 10:02:20 +0200366 nc_gettimespec_mono(&ts_act_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100367 nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);
368
Michal Vasko131120a2018-05-29 15:44:02 +0200369 if (!io_locked) {
370 /* SESSION IO LOCK */
371 ret = nc_session_io_lock(session, io_timeout, __func__);
Michal Vasko77367452021-02-16 16:32:18 +0100372 if (ret < 1) {
Michal Vasko131120a2018-05-29 15:44:02 +0200373 goto cleanup;
374 }
375 io_locked = 1;
376 }
377
Michal Vasko05ba9df2016-01-13 14:40:27 +0100378 /* read the message */
379 switch (session->version) {
380 case NC_VERSION_10:
Michal Vasko77367452021-02-16 16:32:18 +0100381 r = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &data);
382 if (r == -1) {
383 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200384 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100385 }
386
387 /* cut off the end tag */
Michal Vasko77367452021-02-16 16:32:18 +0100388 data[r - NC_VERSION_10_ENDTAG_LEN] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389 break;
390 case NC_VERSION_11:
391 while (1) {
Michal Vasko77367452021-02-16 16:32:18 +0100392 r = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
393 if (r == -1) {
394 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200395 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100396 }
Michal Vasko77367452021-02-16 16:32:18 +0100397 r = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
398 if (r == -1) {
399 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200400 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100401 }
402
403 if (!strcmp(chunk, "#\n")) {
404 /* end of chunked framing message */
405 free(chunk);
Michal Vasko77367452021-02-16 16:32:18 +0100406 if (!data) {
Michal Vasko05532772021-06-03 12:12:38 +0200407 ERR(session, "Invalid frame chunk delimiters.");
Michal Vasko77367452021-02-16 16:32:18 +0100408 ret = -2;
409 goto cleanup;
Michal Vasko79df3262016-07-13 13:42:17 +0200410 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100411 break;
412 }
413
414 /* convert string to the size of the following chunk */
415 chunk_len = strtoul(chunk, (char **)NULL, 10);
416 free(chunk);
417 if (!chunk_len) {
Michal Vasko05532772021-06-03 12:12:38 +0200418 ERR(session, "Invalid frame chunk size detected, fatal error.");
Michal Vasko77367452021-02-16 16:32:18 +0100419 ret = -2;
420 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100421 }
422
423 /* now we have size of next chunk, so read the chunk */
Michal Vasko77367452021-02-16 16:32:18 +0100424 r = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
425 if (r == -1) {
426 ret = r;
Michal Vasko131120a2018-05-29 15:44:02 +0200427 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100428 }
429
430 /* realloc message buffer, remember to count terminating null byte */
Michal Vasko77367452021-02-16 16:32:18 +0100431 data = nc_realloc(data, len + chunk_len + 1);
432 if (!data) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100433 ERRMEM;
Michal Vasko77367452021-02-16 16:32:18 +0100434 ret = -1;
Michal Vasko131120a2018-05-29 15:44:02 +0200435 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100436 }
Michal Vasko77367452021-02-16 16:32:18 +0100437 memcpy(data + len, chunk, chunk_len);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100438 len += chunk_len;
Michal Vasko77367452021-02-16 16:32:18 +0100439 data[len] = '\0';
Michal Vasko05ba9df2016-01-13 14:40:27 +0100440 free(chunk);
441 }
442
443 break;
444 }
Michal Vasko131120a2018-05-29 15:44:02 +0200445
446 /* SESSION IO UNLOCK */
447 assert(io_locked);
448 nc_session_io_unlock(session, __func__);
449 io_locked = 0;
450
Michal Vasko05532772021-06-03 12:12:38 +0200451 DBG(session, "Received message:\n%s\n", data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100452
Michal Vasko77367452021-02-16 16:32:18 +0100453 /* build an input structure, eats data */
454 if (ly_in_new_memory(data, msg)) {
455 ret = -1;
456 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100457 }
Michal Vasko77367452021-02-16 16:32:18 +0100458 data = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100459
Michal Vasko131120a2018-05-29 15:44:02 +0200460cleanup:
461 if (io_locked) {
Michal Vasko77367452021-02-16 16:32:18 +0100462 /* SESSION IO UNLOCK */
Michal Vasko131120a2018-05-29 15:44:02 +0200463 nc_session_io_unlock(session, __func__);
464 }
Michal Vasko77367452021-02-16 16:32:18 +0100465 free(data);
Michal Vasko131120a2018-05-29 15:44:02 +0200466 return ret;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100467}
468
Michal Vasko428087d2016-01-14 16:04:28 +0100469/* return -1 means either poll error or that session was invalidated (socket error), EINTR is handled inside */
470static int
Michal Vasko131120a2018-05-29 15:44:02 +0200471nc_read_poll(struct nc_session *session, int io_timeout)
Michal Vasko428087d2016-01-14 16:04:28 +0100472{
Radek Krejci5961c702016-07-15 09:15:18 +0200473 sigset_t sigmask, origmask;
Michal Vasko428087d2016-01-14 16:04:28 +0100474 int ret = -2;
475 struct pollfd fds;
Michal Vasko428087d2016-01-14 16:04:28 +0100476
477 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200478 ERR(session, "Invalid session to poll.");
Michal Vasko428087d2016-01-14 16:04:28 +0100479 return -1;
480 }
481
482 switch (session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +0100483#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100484 case NC_TI_LIBSSH:
485 /* EINTR is handled, it resumes waiting */
Michal Vasko131120a2018-05-29 15:44:02 +0200486 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0);
Michal Vasko428087d2016-01-14 16:04:28 +0100487 if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200488 ERR(session, "SSH channel poll error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko428087d2016-01-14 16:04:28 +0100489 session->status = NC_STATUS_INVALID;
490 session->term_reason = NC_SESSION_TERM_OTHER;
491 return -1;
492 } else if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200493 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko428087d2016-01-14 16:04:28 +0100494 session->status = NC_STATUS_INVALID;
495 session->term_reason = NC_SESSION_TERM_DROPPED;
496 return -1;
497 } else if (ret > 0) {
498 /* fake it */
499 ret = 1;
500 fds.revents = POLLIN;
Michal Vasko5550cda2016-02-03 15:28:57 +0100501 } else { /* ret == 0 */
502 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100503 }
Michal Vaskob5a58fa2017-01-31 09:47:50 +0100504 break;
Michal Vasko428087d2016-01-14 16:04:28 +0100505#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100506#ifdef NC_ENABLED_TLS
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);
Michal Vasko428087d2016-01-14 16:04:28 +0100517#endif
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;
Radek Krejci53691be2016-02-22 13:58:37 +0100616#ifdef NC_ENABLED_SSH
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#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100620#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100621 case NC_TI_OPENSSL:
622 fds.fd = SSL_get_fd(session->ti.tls);
623 break;
624#endif
Michal Vaskof945da52018-02-15 08:45:13 +0100625 default:
Michal Vasko428087d2016-01-14 16:04:28 +0100626 return 0;
627 }
628
Michal Vasko840a8a62017-02-07 10:56:34 +0100629 if (fds.fd == -1) {
630 return 0;
631 }
632
Michal Vasko428087d2016-01-14 16:04:28 +0100633 fds.events = POLLIN;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100634 fds.revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100635
636 errno = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200637 while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR)) {}
Michal Vasko428087d2016-01-14 16:04:28 +0100638
639 if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200640 ERR(session, "poll failed (%s).", strerror(errno));
Michal Vasko428087d2016-01-14 16:04:28 +0100641 return 0;
642 } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) {
643 return 0;
644 }
645
646 return 1;
647}
648
Radek Krejcife0b3472015-10-12 13:43:42 +0200649#define WRITE_BUFSIZE (2 * BUFFERSIZE)
650struct wclb_arg {
651 struct nc_session *session;
652 char buf[WRITE_BUFSIZE];
653 size_t len;
654};
655
Michal Vasko964e1732016-09-23 13:39:33 +0200656static int
Michal Vasko428087d2016-01-14 16:04:28 +0100657nc_write(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200658{
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200659 int c, fd, interrupted;
Michal Vasko964e1732016-09-23 13:39:33 +0200660 size_t written = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200661
Michal Vaskoe2357e92016-10-05 14:20:47 +0200662#ifdef NC_ENABLED_TLS
663 unsigned long e;
664#endif
Michal Vasko964e1732016-09-23 13:39:33 +0200665
Michal Vasko428087d2016-01-14 16:04:28 +0100666 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
667 return -1;
668 }
669
670 /* prevent SIGPIPE this way */
671 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +0200672 ERR(session, "Communication socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100673 session->status = NC_STATUS_INVALID;
674 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko428087d2016-01-14 16:04:28 +0100675 return -1;
676 }
677
Michal Vasko05532772021-06-03 12:12:38 +0200678 DBG(session, "Sending message:\n%.*s\n", count, buf);
Michal Vasko160b7912016-06-20 10:00:53 +0200679
Michal Vasko81b33fb2016-09-26 14:57:36 +0200680 do {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200681 interrupted = 0;
Michal Vasko964e1732016-09-23 13:39:33 +0200682 switch (session->ti_type) {
Michal Vasko964e1732016-09-23 13:39:33 +0200683 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200684 case NC_TI_UNIX:
685 fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
686 c = write(fd, (char *)(buf + written), count - written);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200687 if ((c < 0) && (errno == EAGAIN)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200688 c = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200689 } else if ((c < 0) && (errno == EINTR)) {
Robin Jarry7de4b8e2019-10-14 21:46:00 +0200690 c = 0;
691 interrupted = 1;
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200692 } else if (c < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200693 ERR(session, "socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200694 return -1;
695 }
696 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200697
Radek Krejci53691be2016-02-22 13:58:37 +0100698#ifdef NC_ENABLED_SSH
Michal Vasko964e1732016-09-23 13:39:33 +0200699 case NC_TI_LIBSSH:
700 if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) {
701 if (ssh_channel_is_closed(session->ti.libssh.channel)) {
Michal Vasko05532772021-06-03 12:12:38 +0200702 ERR(session, "SSH channel unexpectedly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200703 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200704 ERR(session, "SSH channel unexpected EOF.");
Michal Vasko964e1732016-09-23 13:39:33 +0200705 }
706 session->status = NC_STATUS_INVALID;
707 session->term_reason = NC_SESSION_TERM_DROPPED;
708 return -1;
Michal Vasko454e22b2016-01-21 15:34:08 +0100709 }
Michal Vasko81b33fb2016-09-26 14:57:36 +0200710 c = ssh_channel_write(session->ti.libssh.channel, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200711 if ((c == SSH_ERROR) || (c == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200712 ERR(session, "SSH channel write failed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200713 return -1;
714 }
715 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200716#endif
Radek Krejci53691be2016-02-22 13:58:37 +0100717#ifdef NC_ENABLED_TLS
Michal Vasko964e1732016-09-23 13:39:33 +0200718 case NC_TI_OPENSSL:
Michal Vasko81b33fb2016-09-26 14:57:36 +0200719 c = SSL_write(session->ti.tls, (char *)(buf + written), count - written);
Michal Vasko964e1732016-09-23 13:39:33 +0200720 if (c < 1) {
Michal Vasko90a87d92018-12-10 15:53:44 +0100721 char *reasons;
722
Michal Vasko964e1732016-09-23 13:39:33 +0200723 switch ((e = SSL_get_error(session->ti.tls, c))) {
724 case SSL_ERROR_ZERO_RETURN:
Michal Vasko05532772021-06-03 12:12:38 +0200725 ERR(session, "SSL connection was properly closed.");
Michal Vasko964e1732016-09-23 13:39:33 +0200726 return -1;
727 case SSL_ERROR_WANT_WRITE:
Michal Vasko0abba6d2018-12-10 14:09:39 +0100728 case SSL_ERROR_WANT_READ:
Michal Vasko964e1732016-09-23 13:39:33 +0200729 c = 0;
730 break;
731 case SSL_ERROR_SYSCALL:
Michal Vasko05532772021-06-03 12:12:38 +0200732 ERR(session, "SSL socket error (%s).", strerror(errno));
Michal Vasko964e1732016-09-23 13:39:33 +0200733 return -1;
734 case SSL_ERROR_SSL:
Michal Vasko90a87d92018-12-10 15:53:44 +0100735 reasons = nc_ssl_error_get_reasons();
Michal Vasko05532772021-06-03 12:12:38 +0200736 ERR(session, "SSL error (%s).", reasons);
Michal Vasko90a87d92018-12-10 15:53:44 +0100737 free(reasons);
Michal Vasko964e1732016-09-23 13:39:33 +0200738 return -1;
739 default:
Michal Vasko05532772021-06-03 12:12:38 +0200740 ERR(session, "Unknown SSL error occured (err code %d).", e);
Michal Vasko964e1732016-09-23 13:39:33 +0200741 return -1;
742 }
743 }
744 break;
Radek Krejcife0b3472015-10-12 13:43:42 +0200745#endif
Michal Vasko339eea82016-09-29 11:42:36 +0200746 default:
747 ERRINT;
748 return -1;
Michal Vasko964e1732016-09-23 13:39:33 +0200749 }
750
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200751 if ((c == 0) && !interrupted) {
Michal Vasko964e1732016-09-23 13:39:33 +0200752 /* we must wait */
753 usleep(NC_TIMEOUT_STEP);
754 }
755
756 written += c;
Michal Vasko81b33fb2016-09-26 14:57:36 +0200757 } while (written < count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200758
Michal Vasko964e1732016-09-23 13:39:33 +0200759 return written;
Radek Krejcife0b3472015-10-12 13:43:42 +0200760}
761
Michal Vasko428087d2016-01-14 16:04:28 +0100762static int
763nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
Michal Vasko086311b2016-01-08 09:53:11 +0100764{
Michal Vaskofd2db9b2016-01-14 16:15:16 +0100765 int ret = 0, c;
Claus Klein22091912020-01-20 13:45:47 +0100766 char chunksize[24];
Michal Vasko086311b2016-01-08 09:53:11 +0100767
Claus Klein22091912020-01-20 13:45:47 +0100768 // warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=]
Michal Vasko086311b2016-01-08 09:53:11 +0100769 if (session->version == NC_VERSION_11) {
770 sprintf(chunksize, "\n#%zu\n", count);
Michal Vasko428087d2016-01-14 16:04:28 +0100771 ret = nc_write(session, chunksize, strlen(chunksize));
772 if (ret == -1) {
773 return -1;
774 }
Michal Vasko086311b2016-01-08 09:53:11 +0100775 }
Michal Vasko428087d2016-01-14 16:04:28 +0100776
777 c = nc_write(session, buf, count);
778 if (c == -1) {
779 return -1;
780 }
781 ret += c;
782
783 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100784}
785
Radek Krejcife0b3472015-10-12 13:43:42 +0200786static int
Michal Vasko428087d2016-01-14 16:04:28 +0100787nc_write_endtag(struct nc_session *session)
Radek Krejcife0b3472015-10-12 13:43:42 +0200788{
Michal Vasko428087d2016-01-14 16:04:28 +0100789 int ret;
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100790
Michal Vasko428087d2016-01-14 16:04:28 +0100791 if (session->version == NC_VERSION_11) {
792 ret = nc_write(session, "\n##\n", 4);
793 } else {
794 ret = nc_write(session, "]]>]]>", 6);
Radek Krejcife0b3472015-10-12 13:43:42 +0200795 }
796
Michal Vasko428087d2016-01-14 16:04:28 +0100797 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200798}
799
Michal Vasko428087d2016-01-14 16:04:28 +0100800static int
801nc_write_clb_flush(struct wclb_arg *warg)
Radek Krejcife0b3472015-10-12 13:43:42 +0200802{
Michal Vasko428087d2016-01-14 16:04:28 +0100803 int ret = 0;
804
Radek Krejcife0b3472015-10-12 13:43:42 +0200805 /* flush current buffer */
806 if (warg->len) {
Michal Vasko428087d2016-01-14 16:04:28 +0100807 ret = nc_write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200808 warg->len = 0;
809 }
Michal Vasko428087d2016-01-14 16:04:28 +0100810
811 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200812}
813
814static ssize_t
Radek Krejci047300e2016-03-08 16:46:58 +0100815nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent)
Radek Krejcife0b3472015-10-12 13:43:42 +0200816{
Michal Vasko428087d2016-01-14 16:04:28 +0100817 int ret = 0, c;
Radek Krejci047300e2016-03-08 16:46:58 +0100818 size_t l;
Radek Krejcife0b3472015-10-12 13:43:42 +0200819 struct wclb_arg *warg = (struct wclb_arg *)arg;
820
821 if (!buf) {
Michal Vasko428087d2016-01-14 16:04:28 +0100822 c = nc_write_clb_flush(warg);
823 if (c == -1) {
824 return -1;
825 }
826 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200827
828 /* endtag */
Michal Vasko428087d2016-01-14 16:04:28 +0100829 c = nc_write_endtag(warg->session);
830 if (c == -1) {
831 return -1;
832 }
833 ret += c;
834
835 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200836 }
837
838 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
839 /* dump current buffer */
Michal Vasko428087d2016-01-14 16:04:28 +0100840 c = nc_write_clb_flush(warg);
841 if (c == -1) {
842 return -1;
843 }
844 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200845 }
Michal Vasko428087d2016-01-14 16:04:28 +0100846
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200847 if (!xmlcontent && (count > WRITE_BUFSIZE)) {
Radek Krejcife0b3472015-10-12 13:43:42 +0200848 /* write directly */
Michal Vasko428087d2016-01-14 16:04:28 +0100849 c = nc_write_starttag_and_msg(warg->session, buf, count);
850 if (c == -1) {
851 return -1;
852 }
853 ret += c;
Radek Krejcife0b3472015-10-12 13:43:42 +0200854 } else {
855 /* keep in buffer and write later */
Radek Krejci047300e2016-03-08 16:46:58 +0100856 if (xmlcontent) {
857 for (l = 0; l < count; l++) {
858 if (warg->len + 5 >= WRITE_BUFSIZE) {
859 /* buffer is full */
860 c = nc_write_clb_flush(warg);
861 if (c == -1) {
862 return -1;
863 }
864 }
865
866 switch (((char *)buf)[l]) {
867 case '&':
868 ret += 5;
869 memcpy(&warg->buf[warg->len], "&amp;", 5);
870 warg->len += 5;
871 break;
872 case '<':
873 ret += 4;
874 memcpy(&warg->buf[warg->len], "&lt;", 4);
875 warg->len += 4;
876 break;
877 case '>':
878 /* not needed, just for readability */
879 ret += 4;
880 memcpy(&warg->buf[warg->len], "&gt;", 4);
881 warg->len += 4;
882 break;
883 default:
884 ret++;
885 memcpy(&warg->buf[warg->len], &((char *)buf)[l], 1);
886 warg->len++;
887 }
888 }
889 } else {
890 memcpy(&warg->buf[warg->len], buf, count);
891 warg->len += count; /* is <= WRITE_BUFSIZE */
892 ret += count;
893 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200894 }
895
Michal Vasko428087d2016-01-14 16:04:28 +0100896 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +0200897}
898
Radek Krejci047300e2016-03-08 16:46:58 +0100899static ssize_t
900nc_write_xmlclb(void *arg, const void *buf, size_t count)
901{
Michal Vasko77367452021-02-16 16:32:18 +0100902 ssize_t r;
Radek Krejci047300e2016-03-08 16:46:58 +0100903
Michal Vasko77367452021-02-16 16:32:18 +0100904 r = nc_write_clb(arg, buf, count, 0);
905 if (r == -1) {
906 return -1;
Michal Vasko08611b32016-12-05 13:30:37 +0100907 }
908
Michal Vasko77367452021-02-16 16:32:18 +0100909 /* always return what libyang expects, simply that all the characters were printed */
910 return count;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911}
912
Michal Vasko131120a2018-05-29 15:44:02 +0200913/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
914NC_MSG_TYPE
915nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200916{
Radek Krejcid116db42016-01-08 15:36:30 +0100917 va_list ap;
Michal Vasko131120a2018-05-29 15:44:02 +0200918 int count, ret;
Michal Vasko70bf2222021-10-26 10:45:15 +0200919 const char *attrs, *str;
Michal Vaskoff7286b2021-07-09 13:13:11 +0200920 struct lyd_node *op, *reply_envp, *node, *next;
Michal Vasko77367452021-02-16 16:32:18 +0100921 struct lyd_node_opaq *rpc_envp;
Radek Krejci93e80222016-10-03 13:34:25 +0200922 struct nc_server_notif *notif;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100923 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +0100924 char *buf;
Radek Krejcid116db42016-01-08 15:36:30 +0100925 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200926 const char **capabilities;
Michal Vasko77367452021-02-16 16:32:18 +0100927 uint32_t *sid = NULL, i, wd = 0;
928 LY_ERR lyrc;
Radek Krejcife0b3472015-10-12 13:43:42 +0200929
Michal Vasko428087d2016-01-14 16:04:28 +0100930 assert(session);
931
932 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
Michal Vasko05532772021-06-03 12:12:38 +0200933 ERR(session, "Invalid session to write to.");
Michal Vasko131120a2018-05-29 15:44:02 +0200934 return NC_MSG_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100935 }
936
Radek Krejcife0b3472015-10-12 13:43:42 +0200937 arg.session = session;
938 arg.len = 0;
939
Michal Vasko131120a2018-05-29 15:44:02 +0200940 /* SESSION IO LOCK */
941 ret = nc_session_io_lock(session, io_timeout, __func__);
942 if (ret < 0) {
943 return NC_MSG_ERROR;
944 } else if (!ret) {
945 return NC_MSG_WOULDBLOCK;
946 }
947
948 va_start(ap, type);
Radek Krejci127f8952016-10-12 14:57:16 +0200949
Radek Krejcife0b3472015-10-12 13:43:42 +0200950 switch (type) {
951 case NC_MSG_RPC:
Michal Vasko77367452021-02-16 16:32:18 +0100952 op = va_arg(ap, struct lyd_node *);
Radek Krejcife0b3472015-10-12 13:43:42 +0200953 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100954
Michal Vasko70bf2222021-10-26 10:45:15 +0200955 /* <rpc> open */
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200956 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%" PRIu64 "\"%s>",
Michal Vasko77367452021-02-16 16:32:18 +0100957 NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : "");
Michal Vasko4eb3c312016-03-01 14:09:37 +0100958 if (count == -1) {
959 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +0200960 ret = NC_MSG_ERROR;
961 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100962 }
Radek Krejci047300e2016-03-08 16:46:58 +0100963 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200964 free(buf);
Michal Vaskoe1708602016-10-18 12:17:22 +0200965
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100966 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200967 /* <action> open */
968 str = "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">";
969 nc_write_clb((void *)&arg, str, strlen(str), 0);
970 }
971
972 /* rpc data */
Michal Vasko77367452021-02-16 16:32:18 +0100973 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, op, LYD_XML, LYD_PRINT_SHRINK)) {
Michal Vasko131120a2018-05-29 15:44:02 +0200974 ret = NC_MSG_ERROR;
975 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +0200976 }
Michal Vasko70bf2222021-10-26 10:45:15 +0200977
Michal Vasko7e1f5fb2021-11-10 10:14:45 +0100978 if (op->schema && (op->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) {
Michal Vasko70bf2222021-10-26 10:45:15 +0200979 /* <action> close */
980 str = "</action>";
981 nc_write_clb((void *)&arg, str, strlen(str), 0);
982 }
983
984 /* <rpc> close */
985 str = "</rpc>";
986 nc_write_clb((void *)&arg, str, strlen(str), 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200987
Michal Vasko2e6defd2016-10-07 15:48:15 +0200988 session->opts.client.msgid++;
Radek Krejcife0b3472015-10-12 13:43:42 +0200989 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100990
Radek Krejcife0b3472015-10-12 13:43:42 +0200991 case NC_MSG_REPLY:
Michal Vasko77367452021-02-16 16:32:18 +0100992 rpc_envp = va_arg(ap, struct lyd_node_opaq *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100993 reply = va_arg(ap, struct nc_server_reply *);
994
Michal Vasko77367452021-02-16 16:32:18 +0100995 if (!rpc_envp) {
996 /* can be NULL if replying with a malformed-message error */
997 nc_write_clb((void *)&arg, "<rpc-reply xmlns=\"" NC_NS_BASE "\">", 18 + strlen(NC_NS_BASE) + 2, 0);
998
999 assert(reply->type == NC_RPL_ERROR);
1000 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_server_reply_error *)reply)->err, LYD_XML,
1001 LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)) {
1002 ret = NC_MSG_ERROR;
1003 goto cleanup;
1004 }
1005
1006 nc_write_clb((void *)&arg, "</rpc-reply>", 12, 0);
1007 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001008 }
1009
Michal Vasko77367452021-02-16 16:32:18 +01001010 /* build a rpc-reply opaque node that can be simply printed */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001011 if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns,
1012 &reply_envp)) {
Michal Vasko77367452021-02-16 16:32:18 +01001013 ERRINT;
1014 ret = NC_MSG_ERROR;
1015 goto cleanup;
Michal Vaskoe7e534f2016-01-15 09:51:09 +01001016 }
Michal Vasko77367452021-02-16 16:32:18 +01001017
Michal Vasko05ba9df2016-01-13 14:40:27 +01001018 switch (reply->type) {
1019 case NC_RPL_OK:
Michal Vasko77367452021-02-16 16:32:18 +01001020 if (lyd_new_opaq2(reply_envp, NULL, "ok", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, NULL)) {
1021 lyd_free_tree(reply_envp);
1022
1023 ERRINT;
1024 ret = NC_MSG_ERROR;
1025 goto cleanup;
Michal Vasko08611b32016-12-05 13:30:37 +01001026 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001027 break;
1028 case NC_RPL_DATA:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001029 switch (((struct nc_server_reply_data *)reply)->wd) {
Radek Krejci36dfdb32016-09-01 16:56:35 +02001030 case NC_WD_UNKNOWN:
1031 case NC_WD_EXPLICIT:
Michal Vasko77367452021-02-16 16:32:18 +01001032 wd = LYD_PRINT_WD_EXPLICIT;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001033 break;
1034 case NC_WD_TRIM:
Michal Vasko77367452021-02-16 16:32:18 +01001035 wd = LYD_PRINT_WD_TRIM;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001036 break;
1037 case NC_WD_ALL:
Michal Vasko77367452021-02-16 16:32:18 +01001038 wd = LYD_PRINT_WD_ALL;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001039 break;
1040 case NC_WD_ALL_TAG:
Michal Vasko77367452021-02-16 16:32:18 +01001041 wd = LYD_PRINT_WD_ALL_TAG;
Radek Krejci36dfdb32016-09-01 16:56:35 +02001042 break;
1043 }
Michal Vasko77367452021-02-16 16:32:18 +01001044
1045 node = ((struct nc_server_reply_data *)reply)->data;
1046 assert(node->schema->nodetype & (LYS_RPC | LYS_ACTION));
Michal Vaskoff7286b2021-07-09 13:13:11 +02001047 LY_LIST_FOR_SAFE(lyd_child(node), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001048 /* temporary */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001049 lyd_insert_child(reply_envp, node);
Michal Vasko5a91ce72017-10-19 11:30:02 +02001050 }
Michal Vasko05ba9df2016-01-13 14:40:27 +01001051 break;
1052 case NC_RPL_ERROR:
Michal Vasko77367452021-02-16 16:32:18 +01001053 /* temporary */
1054 lyd_insert_child(reply_envp, ((struct nc_server_reply_error *)reply)->err);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001055 break;
1056 default:
1057 ERRINT;
Radek Krejci047300e2016-03-08 16:46:58 +01001058 nc_write_clb((void *)&arg, NULL, 0, 0);
Michal Vasko131120a2018-05-29 15:44:02 +02001059 ret = NC_MSG_ERROR;
1060 goto cleanup;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001061 }
Michal Vasko77367452021-02-16 16:32:18 +01001062
1063 /* temporary */
1064 ((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr;
1065
1066 /* print */
1067 lyrc = lyd_print_clb(nc_write_xmlclb, (void *)&arg, reply_envp, LYD_XML, LYD_PRINT_SHRINK | wd);
1068 ((struct lyd_node_opaq *)reply_envp)->attr = NULL;
1069
1070 /* cleanup */
1071 switch (reply->type) {
1072 case NC_RPL_OK:
1073 /* just free everything */
1074 lyd_free_tree(reply_envp);
1075 break;
1076 case NC_RPL_DATA:
Michal Vaskoff7286b2021-07-09 13:13:11 +02001077 LY_LIST_FOR_SAFE(lyd_child(reply_envp), next, node) {
Michal Vasko77367452021-02-16 16:32:18 +01001078 /* connect back to the reply structure */
Michal Vaskoff7286b2021-07-09 13:13:11 +02001079 lyd_insert_child(((struct nc_server_reply_data *)reply)->data, node);
Michal Vasko77367452021-02-16 16:32:18 +01001080 }
1081 lyd_free_tree(reply_envp);
1082 break;
1083 case NC_RPL_ERROR:
1084 /* unlink from the data reply */
1085 lyd_unlink_tree(lyd_child(reply_envp));
1086 lyd_free_tree(reply_envp);
1087 break;
1088 default:
1089 break;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001090 }
Michal Vasko77367452021-02-16 16:32:18 +01001091
1092 if (lyrc) {
1093 ret = NC_MSG_ERROR;
1094 goto cleanup;
Michal Vasko7f0b0ff2016-11-15 11:02:28 +01001095 }
Radek Krejcife0b3472015-10-12 13:43:42 +02001096 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001097
Radek Krejcife0b3472015-10-12 13:43:42 +02001098 case NC_MSG_NOTIF:
Radek Krejci93e80222016-10-03 13:34:25 +02001099 notif = va_arg(ap, struct nc_server_notif *);
1100
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001101 nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF "\">", 21 + 47 + 2, 0);
Radek Krejci93e80222016-10-03 13:34:25 +02001102 nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
1103 nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
1104 nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
Michal Vasko77367452021-02-16 16:32:18 +01001105 if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->ntf, LYD_XML, LYD_PRINT_SHRINK)) {
Michal Vasko131120a2018-05-29 15:44:02 +02001106 ret = NC_MSG_ERROR;
1107 goto cleanup;
Michal Vasko5a91ce72017-10-19 11:30:02 +02001108 }
mohitarora24878b2962016-11-09 18:45:33 -05001109 nc_write_clb((void *)&arg, "</notification>", 15, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001110 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +01001111
Radek Krejcid116db42016-01-08 15:36:30 +01001112 case NC_MSG_HELLO:
1113 if (session->version != NC_VERSION_10) {
Michal Vasko131120a2018-05-29 15:44:02 +02001114 ret = NC_MSG_ERROR;
1115 goto cleanup;
Radek Krejcid116db42016-01-08 15:36:30 +01001116 }
1117 capabilities = va_arg(ap, const char **);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001118 sid = va_arg(ap, uint32_t *);
Michal Vasko05ba9df2016-01-13 14:40:27 +01001119
Radek Krejcid116db42016-01-08 15:36:30 +01001120 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001121 if (count == -1) {
1122 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001123 ret = NC_MSG_ERROR;
1124 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001125 }
Radek Krejci047300e2016-03-08 16:46:58 +01001126 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001127 free(buf);
1128 for (i = 0; capabilities[i]; i++) {
Radek Krejci047300e2016-03-08 16:46:58 +01001129 nc_write_clb((void *)&arg, "<capability>", 12, 0);
1130 nc_write_clb((void *)&arg, capabilities[i], strlen(capabilities[i]), 1);
1131 nc_write_clb((void *)&arg, "</capability>", 13, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001132 }
1133 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +01001134 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001135 if (count == -1) {
1136 ERRMEM;
Michal Vasko131120a2018-05-29 15:44:02 +02001137 ret = NC_MSG_ERROR;
1138 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001139 }
Radek Krejci047300e2016-03-08 16:46:58 +01001140 nc_write_clb((void *)&arg, buf, count, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001141 free(buf);
1142 } else {
Radek Krejci047300e2016-03-08 16:46:58 +01001143 nc_write_clb((void *)&arg, "</capabilities></hello>", 23, 0);
Radek Krejcid116db42016-01-08 15:36:30 +01001144 }
Radek Krejcid116db42016-01-08 15:36:30 +01001145 break;
Michal Vaskoed462342016-01-12 12:33:48 +01001146
Radek Krejcife0b3472015-10-12 13:43:42 +02001147 default:
Michal Vasko131120a2018-05-29 15:44:02 +02001148 ret = NC_MSG_ERROR;
1149 goto cleanup;
Radek Krejcife0b3472015-10-12 13:43:42 +02001150 }
1151
1152 /* flush message */
Radek Krejci047300e2016-03-08 16:46:58 +01001153 nc_write_clb((void *)&arg, NULL, 0, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +02001154
Michal Vasko428087d2016-01-14 16:04:28 +01001155 if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
1156 /* error was already written */
Michal Vasko131120a2018-05-29 15:44:02 +02001157 ret = NC_MSG_ERROR;
1158 } else {
1159 /* specific message successfully sent */
1160 ret = type;
Michal Vasko428087d2016-01-14 16:04:28 +01001161 }
1162
Michal Vasko131120a2018-05-29 15:44:02 +02001163cleanup:
1164 va_end(ap);
1165 nc_session_io_unlock(session, __func__);
1166 return ret;
Radek Krejcife0b3472015-10-12 13:43:42 +02001167}
Michal Vasko4eb3c312016-03-01 14:09:37 +01001168
1169void *
1170nc_realloc(void *ptr, size_t size)
1171{
1172 void *ret;
1173
1174 ret = realloc(ptr, size);
1175 if (!ret) {
1176 free(ptr);
1177 }
1178
1179 return ret;
1180}
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001181
1182struct passwd *
1183nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size)
1184{
1185 struct passwd *pwd = NULL;
Michal Vasko7e06ee52021-11-02 08:53:05 +01001186 long sys_size;
1187 int ret;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001188
1189 do {
Michal Vasko7e06ee52021-11-02 08:53:05 +01001190 if (!*buf_size) {
1191 /* learn suitable buffer size */
1192 sys_size = sysconf(_SC_GETPW_R_SIZE_MAX);
1193 *buf_size = (sys_size == -1) ? 2048 : sys_size;
1194 } else {
1195 /* enlarge buffer */
1196 *buf_size += 2048;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001197 }
1198
Michal Vasko7e06ee52021-11-02 08:53:05 +01001199 /* allocate some buffer */
1200 *buf = nc_realloc(*buf, *buf_size);
1201 if (!*buf) {
1202 ERRMEM;
1203 return NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001204 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001205
Michal Vasko7e06ee52021-11-02 08:53:05 +01001206 ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd);
1207 } while (ret && (ret == ERANGE));
1208
1209 if (ret) {
1210 ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long int)uid, strerror(ret));
1211 }
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001212 return pwd;
1213}