summaryrefslogtreecommitdiffstats
path: root/src/main/channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/channel.c')
-rw-r--r--src/main/channel.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/src/main/channel.c b/src/main/channel.c
new file mode 100644
index 0000000..757ccd2
--- /dev/null
+++ b/src/main/channel.c
@@ -0,0 +1,231 @@
+/*
+ * radmin.c RADIUS Administration tool.
+ *
+ * Version: $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2015 The FreeRADIUS server project
+ * Copyright 2015 Alan DeKok <aland@deployingradius.com>
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/channel.h>
+
+typedef struct rchannel_t {
+ uint32_t channel;
+ uint32_t length;
+} rchannel_t;
+
+
+static ssize_t lo_read(int fd, void *inbuf, size_t buflen)
+{
+ size_t total;
+ ssize_t r;
+ uint8_t *p = inbuf;
+
+ for (total = 0; total < buflen; total += r) {
+ r = read(fd, p + total, buflen - total);
+
+ if (r == 0) return 0;
+
+ if (r < 0) {
+ if (errno == EINTR) continue;
+
+ return -1;
+
+ }
+ }
+
+ return total;
+}
+
+
+/*
+ * A non-blocking copy of fr_channel_read().
+ */
+ssize_t fr_channel_drain(int fd, fr_channel_type_t *pchannel, void *inbuf, size_t buflen, uint8_t **outbuf, size_t have_read)
+{
+ ssize_t r;
+ size_t data_len;
+ uint8_t *buffer = inbuf;
+ rchannel_t hdr;
+
+ /*
+ * If we can't even read a header, die.
+ */
+ if (buflen <= sizeof(hdr)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Ensure that we read the header first.
+ */
+ if (have_read < sizeof(hdr)) {
+ *pchannel = FR_CHANNEL_WANT_MORE;
+
+ r = lo_read(fd, buffer + have_read, sizeof(hdr) - have_read);
+ if (r <= 0) return r;
+
+ have_read += r;
+
+ if (have_read < sizeof(hdr)) return have_read;
+ }
+
+ /*
+ * We've read the header. Figure out how much more data
+ * we need to read.
+ */
+ memcpy(&hdr, buffer, sizeof(hdr));
+ data_len = ntohl(hdr.length);
+
+ /*
+ * The data will overflow the buffer. Die.
+ */
+ if ((sizeof(hdr) + data_len) > buflen) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * This is how much we really want.
+ */
+ buflen = sizeof(hdr) + data_len;
+
+ r = lo_read(fd, buffer + have_read, buflen - have_read);
+ if (r <= 0) return r;
+
+ have_read += r;
+
+ if (have_read == buflen) {
+ *pchannel = ntohl(hdr.channel);
+ *outbuf = buffer + sizeof(hdr);
+ return data_len;
+ }
+
+ *pchannel = FR_CHANNEL_WANT_MORE;
+ return have_read;
+}
+
+ssize_t fr_channel_read(int fd, fr_channel_type_t *pchannel, void *inbuf, size_t buflen)
+{
+ ssize_t r;
+ size_t data_len;
+ uint8_t *buffer = inbuf;
+ rchannel_t hdr;
+
+ /*
+ * Read the header
+ */
+ r = lo_read(fd, &hdr, sizeof(hdr));
+ if (r <= 0) return r;
+
+ /*
+ * Read the data into the buffer.
+ */
+ *pchannel = ntohl(hdr.channel);
+ data_len = ntohl(hdr.length);
+
+#if 0
+ fprintf(stderr, "CHANNEL R %zu length %zu\n", *pchannel, data_len);
+#endif
+
+ /*
+ * Shrink the output buffer to the size of the data we
+ * have.
+ */
+ if (buflen > data_len) buflen = data_len;
+
+ r = lo_read(fd, buffer, buflen);
+ if (r <= 0) return r;
+
+ /*
+ * Read and discard any extra data sent to us. Sorry,
+ * caller, you should have used a larger buffer!
+ */
+ while (data_len > buflen) {
+ size_t discard;
+ uint8_t junk[64];
+
+ discard = data_len - buflen;
+ if (discard > sizeof(junk)) discard = sizeof(junk);
+
+ r = lo_read(fd, junk, discard);
+ if (r <= 0) break;
+
+ data_len -= r;
+ }
+
+ return buflen;
+}
+
+static ssize_t lo_write(int fd, void const *inbuf, size_t buflen)
+{
+ size_t total;
+ ssize_t r;
+ uint8_t const *buffer = inbuf;
+
+ total = buflen;
+
+ while (total > 0) {
+ r = write(fd, buffer, total);
+ if (r == 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ if (r < 0) {
+ if (errno == EINTR) continue;
+
+ return -1;
+ }
+
+ buffer += r;
+ total -= r;
+ }
+
+ return buflen;
+}
+
+ssize_t fr_channel_write(int fd, fr_channel_type_t channel, void const *inbuf, size_t buflen)
+{
+ ssize_t r;
+ rchannel_t hdr;
+ uint8_t const *buffer = inbuf;
+
+ hdr.channel = htonl(channel);
+ hdr.length = htonl(buflen);
+
+#if 0
+ fprintf(stderr, "CHANNEL W %zu length %zu\n", channel, buflen);
+#endif
+
+ /*
+ * write the header
+ */
+ r = lo_write(fd, &hdr, sizeof(hdr));
+ if (r <= 0) return r;
+
+ /*
+ * write the data directly from the buffer
+ */
+ r = lo_write(fd, buffer, buflen);
+ if (r <= 0) return r;
+
+ return buflen;
+}