The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 421abac07e9966c8398f841ddf649143a271182c $
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  */
26 RCSID("$Id: 421abac07e9966c8398f841ddf649143a271182c $")
27 
28 #include <freeradius-devel/util/strerror.h>
29 #include <stdlib.h>
30 #include <stdbool.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include "conduit.h"
34 
35 static ssize_t lo_read(int fd, void *out, size_t outlen)
36 {
37  size_t total;
38  ssize_t r;
39  uint8_t *p = out;
40 
41  for (total = 0; total < outlen; total += r) {
42  r = read(fd, p + total, outlen - total);
43 
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  return total;
66 }
67 
68 
69 /*
70  * A non-blocking copy of fr_conduit_read().
71  */
73  void *out, size_t outlen, size_t *leftover, bool *want_more)
74 {
75  ssize_t r;
76  size_t data_len;
77  uint8_t *buffer = out;
78  fr_conduit_hdr_t hdr;
79  size_t offset = *leftover;
80 
81  /*
82  * If we can't even read a header, die.
83  */
84  if (outlen <= sizeof(hdr)) {
85  errno = EINVAL;
86  return -1;
87  }
88 
89  *want_more = true;
90 
91  /*
92  * Ensure that we read the header first.
93  */
94  if (offset < sizeof(hdr)) {
95  r = lo_read(fd, buffer + offset, sizeof(hdr) - offset);
96  if (r <= 0) return r;
97 
98  *leftover += r;
99  offset += r;
100 
101  /*
102  * We have leftover data, but no *packet* to
103  * return.
104  */
105  if (offset < sizeof(hdr)) return 0;
106  }
107 
108  /*
109  * We've read the header. Figure out how much more data
110  * we need to read.
111  */
112  memcpy(&hdr, buffer, sizeof(hdr));
113  data_len = ntohl(hdr.length);
114 
115  /*
116  * The data will overflow the buffer. Die.
117  */
118  if ((sizeof(hdr) + data_len) > outlen) {
119  errno = EINVAL;
120  return -1;
121  }
122 
123  /*
124  * This is how much we really want.
125  */
126  outlen = sizeof(hdr) + data_len;
127 
128  r = lo_read(fd, buffer + offset, outlen - offset);
129  if (r <= 0) return r;
130 
131  offset += r;
132 
133  if (offset == outlen) {
134  *want_more = false;
135  *pconduit = ntohs(hdr.conduit);
136  return outlen;
137  }
138 
139  *leftover = offset;
140  return 0;
141 }
142 
143 ssize_t fr_conduit_read(int fd, fr_conduit_type_t *pconduit, void *out, size_t outlen)
144 {
145  ssize_t r;
146  size_t data_len;
147  uint8_t *buffer = out;
148  fr_conduit_hdr_t hdr;
149 
150  /*
151  * Read the header
152  */
153  r = lo_read(fd, &hdr, sizeof(hdr));
154  if (r <= 0) return r;
155 
156  /*
157  * Read the data into the buffer.
158  */
159  *pconduit = ntohs(hdr.conduit);
160  data_len = ntohl(hdr.length);
161  if (data_len == 0) return 0;
162  if (data_len > UINT32_MAX) data_len = UINT32_MAX; /* For Coverity */
163 
164 #if 0
165  fprintf(stderr, "CONDUIT R %zu length %zu\n", *pconduit, data_len);
166 #endif
167 
168  /*
169  * Shrink the output buffer to the size of the data we
170  * have.
171  */
172  if (outlen > data_len) outlen = data_len;
173 
174  r = lo_read(fd, buffer, outlen);
175  if (r <= 0) return r;
176 
177  /*
178  * Read and discard any extra data sent to us. Sorry,
179  * caller, you should have used a larger buffer!
180  */
181  while (data_len > outlen) {
182  size_t discard;
183  uint8_t junk[64];
184 
185  discard = data_len - outlen;
186  if (discard > sizeof(junk)) discard = sizeof(junk);
187 
188  r = lo_read(fd, junk, discard);
189  if (r <= 0) break;
190 
191  data_len -= r;
192  }
193 
194  return outlen;
195 }
196 
197 static ssize_t lo_write(int fd, void const *out, size_t outlen)
198 {
199  size_t total;
200  ssize_t r;
201  uint8_t const *buffer = out;
202 
203  total = outlen;
204 
205  while (total > 0) {
206  r = write(fd, buffer, total);
207  if (r == 0) {
208  errno = EAGAIN;
209  return -1;
210  }
211 
212  if (r < 0) {
213  if (errno == EINTR) continue;
214 
215  return -1;
216  }
217 
218  buffer += r;
219  total -= r;
220  }
221 
222  return outlen;
223 }
224 
225 ssize_t fr_conduit_write(int fd, fr_conduit_type_t conduit, void const *out, size_t outlen)
226 {
227  ssize_t r;
228  fr_conduit_hdr_t hdr;
229  uint8_t const *buffer = out;
230 
231  if (outlen > UINT32_MAX) {
232  fr_strerror_printf("Data to write to conduit (%zu bytes) exceeds maximum length", outlen);
233  return -1;
234  }
235 
236  /*
237  * Asked to write nothing, suppress it.
238  */
239  if (!outlen) return 0;
240 
241  hdr = (fr_conduit_hdr_t) {
242  .conduit = htons(conduit),
243  .length = htonl(outlen),
244  };
245 
246 #if 0
247  fprintf(stderr, "CONDUIT W %zu length %zu\n", conduit, outlen);
248 #endif
249 
250  /*
251  * write the header
252  */
253  r = lo_write(fd, &hdr, sizeof(hdr));
254  if (r <= 0) return r;
255 
256  /*
257  * write the data directly from the buffer
258  */
259  r = lo_write(fd, buffer, outlen);
260  if (r <= 0) return r;
261 
262  return outlen;
263 }
static int const char char buffer[256]
Definition: acutest.h:574
#define RCSID(id)
Definition: build.h:444
static ssize_t lo_write(int fd, void const *out, size_t outlen)
Definition: conduit.c:197
ssize_t fr_conduit_write(int fd, fr_conduit_type_t conduit, void const *out, size_t outlen)
Definition: conduit.c:225
static ssize_t lo_read(int fd, void *out, size_t outlen)
Definition: conduit.c:35
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:72
ssize_t fr_conduit_read(int fd, fr_conduit_type_t *pconduit, void *out, size_t outlen)
Definition: conduit.c:143
API to provide distinct communication conduits for the radmin protocol.
uint16_t conduit
Definition: conduit.h:77
uint32_t length
Definition: conduit.h:78
fr_conduit_type_t
Definition: conduit.h:34
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
static size_t char ** out
Definition: value.h:984