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: a17f14f905d184bf05cf64066dff72a11de89e6b $
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: a17f14f905d184bf05cf64066dff72a11de89e6b $")
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 uint16_t conduit = ntohs(hdr.conduit);
144
145 *want_more = false;
146 *pconduit = (fr_conduit_type_t) conduit;
147 return outlen;
148 }
149
150 *leftover = offset;
151 return 0;
152}
153
154ssize_t fr_conduit_read(int fd, fr_conduit_type_t *pconduit, void *out, size_t outlen)
155{
156 ssize_t r;
157 size_t data_len;
158 uint8_t *buffer = out;
160 uint16_t conduit;
161
162 /*
163 * Read the header
164 */
165 r = lo_read(fd, &hdr, sizeof(hdr));
166 if (r <= 0) return r;
167
168 /*
169 * Read the data into the buffer.
170 */
171 conduit = ntohs(hdr.conduit);
172 *pconduit = (fr_conduit_type_t) conduit;
173 data_len = ntohl(hdr.length);
174 if (data_len == 0) return 0;
175 if (data_len > UINT32_MAX) data_len = UINT32_MAX; /* For Coverity */
176
177#if 0
178 fprintf(stderr, "CONDUIT R %zu length %zu\n", *pconduit, data_len);
179#endif
180
181 /*
182 * Shrink the output buffer to the size of the data we
183 * have.
184 */
185 if (outlen > data_len) outlen = data_len;
186
187 r = lo_read(fd, buffer, outlen);
188 if (r <= 0) return r;
189
190 /*
191 * Read and discard any extra data sent to us. Sorry,
192 * caller, you should have used a larger buffer!
193 */
194 while (data_len > outlen) {
195 size_t discard;
196 uint8_t junk[64];
197
198 discard = data_len - outlen;
199 if (discard > sizeof(junk)) discard = sizeof(junk);
200
201 r = lo_read(fd, junk, discard);
202 if (r <= 0) break;
203
204 data_len -= r;
205 }
206
207 return outlen;
208}
209
210static ssize_t lo_write(int fd, void const *out, size_t outlen)
211{
212 size_t total;
213 ssize_t r;
214 uint8_t const *buffer = out;
215
216 total = outlen;
217
218 while (total > 0) {
219 r = write(fd, buffer, total);
220 if (r == 0) {
221 errno = EAGAIN;
222 return -1;
223 }
224
225 if (r < 0) {
226 if (errno == EINTR) continue;
227
228 return -1;
229 }
230
231 buffer += r;
232 total -= r;
233 }
234
235 return outlen;
236}
237
238ssize_t fr_conduit_write(int fd, fr_conduit_type_t conduit, void const *out, size_t outlen)
239{
240 ssize_t r;
242 uint8_t const *buffer = out;
243
244 if (outlen > UINT32_MAX) {
245 fr_strerror_printf("Data to write to conduit (%zu bytes) exceeds maximum length", outlen);
246 return -1;
247 }
248
249 /*
250 * Asked to write nothing, suppress it.
251 */
252 if (!outlen) return 0;
253
254 hdr = (fr_conduit_hdr_t) {
255 .conduit = htons(conduit),
256 .length = htonl(outlen),
257 };
258
259#if 0
260 fprintf(stderr, "CONDUIT W %zu length %zu\n", conduit, outlen);
261#endif
262
263 /*
264 * write the header
265 */
266 r = lo_write(fd, &hdr, sizeof(hdr));
267 if (r <= 0) return r;
268
269 /*
270 * write the data directly from the buffer
271 */
272 r = lo_write(fd, buffer, outlen);
273 if (r <= 0) return r;
274
275 return outlen;
276}
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:210
ssize_t fr_conduit_write(int fd, fr_conduit_type_t conduit, void const *out, size_t outlen)
Definition conduit.c:238
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:154
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 short uint16_t
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