The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
conduit.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 48cf66937f7edaf737fcbb778f26405dcc934452 $
19 *
20 * @file conduit.c
21 * @brief Channels for communicating with radmin
22 *
23 * @copyright 2015 The FreeRADIUS server project
24 * @copyright 2015 Alan DeKok (aland@deployingradius.com)
25 */
26RCSID("$Id: 48cf66937f7edaf737fcbb778f26405dcc934452 $")
27
28#include <freeradius-devel/util/debug.h>
29#include <freeradius-devel/util/math.h>
30#include <freeradius-devel/util/strerror.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <errno.h>
34#include "conduit.h"
35
36static ssize_t lo_read(int fd, void *out, size_t outlen)
37{
38 ssize_t total;
39 ssize_t r;
40 uint8_t *p = out;
41
42 for (total = 0; (size_t)total < outlen; ) {
43 r = read(fd, p + total, outlen - total);
44 if (r == 0) return total;
45
46 if (r < 0) {
47 switch (errno) {
48 case EINTR:
49 continue;
50
51#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
52 case EWOULDBLOCK:
53#endif
54 case EAGAIN:
55 return total;
56
57 default:
58 break;
59 }
60
61 return -1;
62 }
63
64 /*
65 * The total never exceeds outlen, which is far below
66 * SSIZE_MAX.
67 */
68 if (!fr_cond_assert(fr_add(&total, total, r))) return -1;
69 }
70
71 return total;
72}
73
74
75/*
76 * A non-blocking copy of fr_conduit_read().
77 */
79 void *out, size_t outlen, size_t *leftover, bool *want_more)
80{
81 ssize_t r;
82 uint32_t data_len;
85 size_t offset = *leftover;
86
87 /*
88 * If we can't even read a header, die.
89 */
90 if (outlen <= sizeof(hdr)) {
91 errno = EINVAL;
92 return -1;
93 }
94
95 *want_more = true;
96
97 /*
98 * Ensure that we read the header first.
99 */
100 if (offset < sizeof(hdr)) {
101 r = lo_read(fd, buffer + offset, sizeof(hdr) - offset);
102 if (r <= 0) return r;
103
104 *leftover += r;
105 offset += r;
106
107 /*
108 * We have leftover data, but no *packet* to
109 * return.
110 */
111 if (offset < sizeof(hdr)) return 0;
112 }
113
114 /*
115 * We've read the header. Figure out how much more data
116 * we need to read.
117 */
118 memcpy(&hdr, buffer, sizeof(hdr));
119 data_len = ntohl(hdr.length);
120
121 /*
122 * The data will overflow the buffer. Die. outlen is larger
123 * than the header, see above, so the subtraction can't wrap,
124 * and data_len from the wire is checked before any arithmetic
125 * uses it.
126 */
127 if (data_len > (outlen - sizeof(hdr))) {
128 errno = EINVAL;
129 return -1;
130 }
131
132 /*
133 * This is how much we really want.
134 */
135 outlen = sizeof(hdr) + data_len;
136
137 r = lo_read(fd, buffer + offset, outlen - offset);
138 if (r <= 0) return r;
139
140 offset += r;
141
142 if (offset == outlen) {
143 *want_more = false;
144 *pconduit = (fr_conduit_type_t) ntohs(hdr.conduit);
145 return outlen;
146 }
147
148 *leftover = offset;
149 return 0;
150}
151
152ssize_t fr_conduit_read(int fd, fr_conduit_type_t *pconduit, void *out, size_t outlen)
153{
154 ssize_t r;
155 size_t data_len;
156 uint8_t *buffer = out;
158
159 /*
160 * Read the header
161 */
162 r = lo_read(fd, &hdr, sizeof(hdr));
163 if (r <= 0) return r;
164
165 /*
166 * Read the data into the buffer.
167 */
168 *pconduit = (fr_conduit_type_t) ntohs(hdr.conduit);
169 data_len = ntohl(hdr.length);
170 if (data_len == 0) return 0;
171 if (data_len > UINT32_MAX) data_len = UINT32_MAX; /* For Coverity */
172
173#if 0
174 fprintf(stderr, "CONDUIT R %zu length %zu\n", *pconduit, data_len);
175#endif
176
177 /*
178 * Shrink the output buffer to the size of the data we
179 * have.
180 */
181 if (outlen > data_len) outlen = data_len;
182
183 r = lo_read(fd, buffer, outlen);
184 if (r <= 0) return r;
185
186 /*
187 * Read and discard any extra data sent to us. Sorry,
188 * caller, you should have used a larger buffer!
189 */
190 while (data_len > outlen) {
191 size_t discard;
192 uint8_t junk[64];
193
194 discard = data_len - outlen;
195 if (discard > sizeof(junk)) discard = sizeof(junk);
196
197 r = lo_read(fd, junk, discard);
198 if (r <= 0) break;
199
200 data_len -= r;
201 }
202
203 return outlen;
204}
205
206static ssize_t lo_write(int fd, void const *out, size_t outlen)
207{
208 size_t total;
209 ssize_t r;
210 uint8_t const *buffer = out;
211
212 total = outlen;
213
214 while (total > 0) {
215 r = write(fd, buffer, total);
216 if (r == 0) {
217 errno = EAGAIN;
218 return -1;
219 }
220
221 if (r < 0) {
222 if (errno == EINTR) continue;
223
224 return -1;
225 }
226
227 buffer += r;
228 total -= r;
229 }
230
231 return outlen;
232}
233
234ssize_t fr_conduit_write(int fd, fr_conduit_type_t conduit, void const *out, size_t outlen)
235{
236 ssize_t r;
238 uint8_t const *buffer = out;
239
240 if (outlen > UINT32_MAX) {
241 fr_strerror_printf("Data to write to conduit (%zu bytes) exceeds maximum length", outlen);
242 return -1;
243 }
244
245 /*
246 * Asked to write nothing, suppress it.
247 */
248 if (!outlen) return 0;
249
250 hdr = (fr_conduit_hdr_t) {
251 .conduit = htons(conduit),
252 .length = htonl(outlen),
253 };
254
255#if 0
256 fprintf(stderr, "CONDUIT W %zu length %zu\n", conduit, outlen);
257#endif
258
259 /*
260 * write the header
261 */
262 r = lo_write(fd, &hdr, sizeof(hdr));
263 if (r <= 0) return r;
264
265 /*
266 * write the data directly from the buffer
267 */
268 r = lo_write(fd, buffer, outlen);
269 if (r <= 0) return r;
270
271 return outlen;
272}
static int const char char buffer[256]
Definition acutest.h:641
#define RCSID(id)
Definition build.h:603
static ssize_t lo_write(int fd, void const *out, size_t outlen)
Definition conduit.c:206
ssize_t fr_conduit_write(int fd, fr_conduit_type_t conduit, void const *out, size_t outlen)
Definition conduit.c:234
static ssize_t lo_read(int fd, void *out, size_t outlen)
Definition conduit.c:36
ssize_t fr_conduit_read_async(int fd, fr_conduit_type_t *pconduit, void *out, size_t outlen, size_t *leftover, bool *want_more)
Definition conduit.c:78
ssize_t fr_conduit_read(int fd, fr_conduit_type_t *pconduit, void *out, size_t outlen)
Definition conduit.c:152
API to provide distinct communication conduits for the radmin protocol.
uint16_t conduit
Definition conduit.h:77
fr_conduit_type_t
Definition conduit.h:34
uint32_t length
Definition conduit.h:78
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define fr_add(_out, _a, _b)
Adds two integers.
Definition math.h:190
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
static fr_sbuff_err_t char ** out
Definition value.h:1061