summaryrefslogtreecommitdiffabout
path: root/src
authorSergey Poznyakoff <gray@gnu.org.ua>2014-11-18 23:49:13 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2014-11-19 00:03:58 (GMT)
commit57a7d63793de517493499e748ce5d5d82def8a57 (patch) (side-by-side diff)
tree35dbead6db811eecc03c0578e8aa3e2ed777b824 /src
parentf0671d1bc19592e5b659959920b51e3da05de79f (diff)
downloadvarnish-mib-57a7d63793de517493499e748ce5d5d82def8a57.tar.gz
varnish-mib-57a7d63793de517493499e748ce5d5d82def8a57.tar.bz2
New rw snmp variable clientBan allows to set bans via snmp
* src/varnish_mib.mib2c: Add support for rw variables. * src/Makefile.am (varnish_mib_la_SOURCES): Add new files. * src/VARNISH-MIB.txt (clientBan): New OID. * src/auth.c: New file. * src/ban.c: New file. * src/sha256.c: New file. * src/sha256.h: New file. * src/varnish_mib.h: New file. * src/vcli.c: New file.
Diffstat (limited to 'src') (more/less context) (ignore whitespace changes)
-rw-r--r--src/Makefile.am8
-rw-r--r--src/VARNISH-MIB.txt9
-rw-r--r--src/auth.c63
-rw-r--r--src/ban.c62
-rw-r--r--src/sha256.c570
-rw-r--r--src/sha256.h91
-rw-r--r--src/varnish_mib.h52
-rw-r--r--src/varnish_mib.mib2c173
-rw-r--r--src/vcli.c499
9 files changed, 1481 insertions, 46 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 4efd22c..439cdb3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,7 +18,13 @@ dlmoddir=@DLMODDIR@
dlmod_LTLIBRARIES = varnish-mib.la
varnish_mib_la_SOURCES = \
- varnish_mib.c
+ auth.c\
+ ban.c\
+ sha256.c\
+ sha256.h\
+ varnish_mib.c\
+ varnish_mib.h\
+ vcli.c
BUILT_SOURCES = \
varnish_mib.c
diff --git a/src/VARNISH-MIB.txt b/src/VARNISH-MIB.txt
index 0ec5dac..01def5c 100644
--- a/src/VARNISH-MIB.txt
+++ b/src/VARNISH-MIB.txt
@@ -70,6 +70,14 @@ clientCacheMisses OBJECT-TYPE
"Cache misses"
::= { client 5 }
+clientBan OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE(0..1024))
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "FIXME"
+ ::= { client 6 }
+
connections OBJECT IDENTIFIER ::= { backend 1 }
backendConnSuccess OBJECT-TYPE
@@ -201,6 +209,7 @@ varnishGroup OBJECT-GROUP
clientCacheHits,
clientCacheHitsPass,
clientCacheMisses,
+ clientBan,
backendConnSuccess,
backendConnNotAttempted,
backendConnToMany,
diff --git a/src/auth.c b/src/auth.c
new file mode 100644
index 0000000..9ef90ac
--- a/dev/null
+++ b/src/auth.c
@@ -0,0 +1,63 @@
+/* This file is part of varnish-mib -*- c -*-
+ Copyright (C) 2014 Sergey Poznyakoff
+
+ Varnish-mib 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 3, or (at your option)
+ any later version.
+
+ Varnish-mib 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 varnish-mib. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "varnish_mib.h"
+#include "sha256.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+
+void
+varnish_auth_response_fd(int fd, const char *challenge,
+ char response[CLI_AUTH_RESPONSE_LEN + 1])
+{
+ struct sha256_ctx ctx;
+ uint8_t buf[BUFSIZ];
+ int i;
+
+ assert(CLI_AUTH_RESPONSE_LEN == (SHA256_DIGEST_SIZE * 2));
+
+ sha256_init_ctx(&ctx);
+ sha256_process_bytes(challenge, 32, &ctx);
+ sha256_process_bytes("\n", 1, &ctx);
+ do {
+ i = read(fd, buf, sizeof buf);
+ if (i > 0)
+ sha256_process_bytes(buf, i, &ctx);
+ } while (i > 0);
+ sha256_process_bytes(challenge, 32, &ctx);
+ sha256_process_bytes("\n", 1, &ctx);
+ sha256_finish_ctx(&ctx, buf);
+ for (i = 0; i < SHA256_DIGEST_SIZE; i++)
+ sprintf(response + 2 * i, "%02x", buf[i]);
+}
+
+int
+varnish_auth_response(const char *file, const char *challenge,
+ char response[CLI_AUTH_RESPONSE_LEN + 1])
+{
+ int fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ snmp_log(LOG_ERR, "can't open secret file %s: %s\n",
+ file, strerror(errno));
+ return -1;
+ }
+ varnish_auth_response_fd(fd, challenge, response);
+ close(fd);
+ return 0;
+}
diff --git a/src/ban.c b/src/ban.c
new file mode 100644
index 0000000..a091e37
--- a/dev/null
+++ b/src/ban.c
@@ -0,0 +1,62 @@
+/* This file is part of varnish-mib -*- c -*-
+ Copyright (C) 2014 Sergey Poznyakoff
+
+ Varnish-mib 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 3, or (at your option)
+ any later version.
+
+ Varnish-mib 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 varnish-mib. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "varnish_mib.h"
+
+static int
+send_ban_cmd(vcli_conn_t *conn, const char *expr)
+{
+ if (vcli_asprintf(conn, "ban %s\n", expr) || vcli_write(conn))
+ return 1;
+
+ if (vcli_read_response(conn))
+ return 1;
+
+ if (conn->resp != CLIS_OK) {
+ snmp_log(LOG_ERR, "command rejected: %u %s\n",
+ conn->resp, conn->base);
+ return 1;
+ }
+ return 0;
+}
+
+int
+varnish_ban(netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests,
+ struct VSM_data *vd)
+{
+ int rc;
+ struct vcli_conn conn;
+ size_t len = requests->requestvb->val_len;
+ char *expr = malloc(len + 1);
+
+ if (!expr) {
+ snmp_log(LOG_ERR, "out of memory\n");
+ return SNMP_ERR_GENERR;
+ }
+ memcpy(expr, requests->requestvb->val.string, len);
+ expr[len] = 0;
+ DEBUGMSGTL(("vcli_mib", "ban %s\n", expr));
+ rc = vcli_connect(vd, &conn);
+ if (rc == SNMP_ERR_NOERROR) {
+ rc = send_ban_cmd(&conn, expr);
+ vcli_disconnect(&conn);
+ }
+ free(expr);
+ return rc ? SNMP_ERR_GENERR : SNMP_ERR_NOERROR;
+}
+
diff --git a/src/sha256.c b/src/sha256.c
new file mode 100644
index 0000000..bcb5f74
--- a/dev/null
+++ b/src/sha256.c
@@ -0,0 +1,570 @@
+/* sha256.c - Functions to compute SHA256 and SHA224 message digest of files or
+ memory blocks according to the NIST specification FIPS-180-2.
+
+ Copyright (C) 2005-2006, 2008-2013 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* Written by David Madore, considerably copypasting from
+ Scott G. Miller's sha1.c
+*/
+
+#include <config.h>
+
+#include "sha256.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) (n)
+#else
+# define SWAP(n) \
+ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#endif
+
+#define BLOCKSIZE 32768
+#if BLOCKSIZE % 64 != 0
+# error "invalid BLOCKSIZE"
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+
+
+/*
+ Takes a pointer to a 256 bit block of data (eight 32 bit ints) and
+ initializes it to the start constants of the SHA256 algorithm. This
+ must be called before using hash in the call to sha256_hash
+*/
+void
+sha256_init_ctx (struct sha256_ctx *ctx)
+{
+ ctx->state[0] = 0x6a09e667UL;
+ ctx->state[1] = 0xbb67ae85UL;
+ ctx->state[2] = 0x3c6ef372UL;
+ ctx->state[3] = 0xa54ff53aUL;
+ ctx->state[4] = 0x510e527fUL;
+ ctx->state[5] = 0x9b05688cUL;
+ ctx->state[6] = 0x1f83d9abUL;
+ ctx->state[7] = 0x5be0cd19UL;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+}
+
+void
+sha224_init_ctx (struct sha256_ctx *ctx)
+{
+ ctx->state[0] = 0xc1059ed8UL;
+ ctx->state[1] = 0x367cd507UL;
+ ctx->state[2] = 0x3070dd17UL;
+ ctx->state[3] = 0xf70e5939UL;
+ ctx->state[4] = 0xffc00b31UL;
+ ctx->state[5] = 0x68581511UL;
+ ctx->state[6] = 0x64f98fa7UL;
+ ctx->state[7] = 0xbefa4fa4UL;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+}
+
+/* Copy the value from v into the memory location pointed to by *cp,
+ If your architecture allows unaligned access this is equivalent to
+ * (uint32_t *) cp = v */
+static void
+set_uint32 (char *cp, uint32_t v)
+{
+ memcpy (cp, &v, sizeof v);
+}
+
+/* Put result from CTX in first 32 bytes following RESBUF. The result
+ must be in little endian byte order. */
+void *
+sha256_read_ctx (const struct sha256_ctx *ctx, void *resbuf)
+{
+ int i;
+ char *r = resbuf;
+
+ for (i = 0; i < 8; i++)
+ set_uint32 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i]));
+
+ return resbuf;
+}
+
+void *
+sha224_read_ctx (const struct sha256_ctx *ctx, void *resbuf)
+{
+ int i;
+ char *r = resbuf;
+
+ for (i = 0; i < 7; i++)
+ set_uint32 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i]));
+
+ return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF. */
+static void
+sha256_conclude_ctx (struct sha256_ctx *ctx)
+{
+ /* Take yet unprocessed bytes into account. */
+ size_t bytes = ctx->buflen;
+ size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
+
+ /* Now count remaining bytes. */
+ ctx->total[0] += bytes;
+ if (ctx->total[0] < bytes)
+ ++ctx->total[1];
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer.
+ Use set_uint32 rather than a simple assignment, to avoid risk of
+ unaligned access. */
+ set_uint32 ((char *) &ctx->buffer[size - 2],
+ SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)));
+ set_uint32 ((char *) &ctx->buffer[size - 1],
+ SWAP (ctx->total[0] << 3));
+
+ memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);
+
+ /* Process last bytes. */
+ sha256_process_block (ctx->buffer, size * 4, ctx);
+}
+
+void *
+sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf)
+{
+ sha256_conclude_ctx (ctx);
+ return sha256_read_ctx (ctx, resbuf);
+}
+
+void *
+sha224_finish_ctx (struct sha256_ctx *ctx, void *resbuf)
+{
+ sha256_conclude_ctx (ctx);
+ return sha224_read_ctx (ctx, resbuf);
+}
+
+/* Compute SHA256 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 32 bytes
+ beginning at RESBLOCK. */
+int
+sha256_stream (FILE *stream, void *resblock)
+{
+ struct sha256_ctx ctx;
+ size_t sum;
+
+ char *buffer = malloc (BLOCKSIZE + 72);
+ if (!buffer)
+ return 1;
+
+ /* Initialize the computation context. */
+ sha256_init_ctx (&ctx);
+
+ /* Iterate over full file contents. */
+ while (1)
+ {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read. */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ while (1)
+ {
+ n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+ sum += n;
+
+ if (sum == BLOCKSIZE)
+ break;
+
+ if (n == 0)
+ {
+ /* Check for the error flag IFF N == 0, so that we don't
+ exit the loop after a partial read due to e.g., EAGAIN
+ or EWOULDBLOCK. */
+ if (ferror (stream))
+ {
+ free (buffer);
+ return 1;
+ }
+ goto process_partial_block;
+ }
+
+ /* We've read at least one byte, so ignore errors. But always
+ check for EOF, since feof may be true even though N > 0.
+ Otherwise, we could end up calling fread after EOF. */
+ if (feof (stream))
+ goto process_partial_block;
+ }
+
+ /* Process buffer with BLOCKSIZE bytes. Note that
+ BLOCKSIZE % 64 == 0
+ */
+ sha256_process_block (buffer, BLOCKSIZE, &ctx);
+ }
+
+ process_partial_block:;
+
+ /* Process any remaining bytes. */
+ if (sum > 0)
+ sha256_process_bytes (buffer, sum, &ctx);
+
+ /* Construct result in desired memory. */
+ sha256_finish_ctx (&ctx, resblock);
+ free (buffer);
+ return 0;
+}
+
+/* FIXME: Avoid code duplication */
+int
+sha224_stream (FILE *stream, void *resblock)
+{
+ struct sha256_ctx ctx;
+ size_t sum;
+
+ char *buffer = malloc (BLOCKSIZE + 72);
+ if (!buffer)
+ return 1;
+
+ /* Initialize the computation context. */
+ sha224_init_ctx (&ctx);
+
+ /* Iterate over full file contents. */
+ while (1)
+ {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read. */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ while (1)
+ {
+ n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+ sum += n;
+
+ if (sum == BLOCKSIZE)
+ break;
+
+ if (n == 0)
+ {
+ /* Check for the error flag IFF N == 0, so that we don't
+ exit the loop after a partial read due to e.g., EAGAIN
+ or EWOULDBLOCK. */
+ if (ferror (stream))
+ {
+ free (buffer);
+ return 1;
+ }
+ goto process_partial_block;
+ }
+
+ /* We've read at least one byte, so ignore errors. But always
+ check for EOF, since feof may be true even though N > 0.
+ Otherwise, we could end up calling fread after EOF. */
+ if (feof (stream))
+ goto process_partial_block;
+ }
+
+ /* Process buffer with BLOCKSIZE bytes. Note that
+ BLOCKSIZE % 64 == 0
+ */
+ sha256_process_block (buffer, BLOCKSIZE, &ctx);
+ }
+
+ process_partial_block:;
+
+ /* Process any remaining bytes. */
+ if (sum > 0)
+ sha256_process_bytes (buffer, sum, &ctx);
+
+ /* Construct result in desired memory. */
+ sha224_finish_ctx (&ctx, resblock);
+ free (buffer);
+ return 0;
+}
+
+/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+void *
+sha256_buffer (const char *buffer, size_t len, void *resblock)
+{
+ struct sha256_ctx ctx;
+
+ /* Initialize the computation context. */
+ sha256_init_ctx (&ctx);
+
+ /* Process whole buffer but last len % 64 bytes. */
+ sha256_process_bytes (buffer, len, &ctx);
+
+ /* Put result in desired memory area. */
+ return sha256_finish_ctx (&ctx, resblock);
+}
+
+void *
+sha224_buffer (const char *buffer, size_t len, void *resblock)
+{
+ struct sha256_ctx ctx;
+
+ /* Initialize the computation context. */
+ sha224_init_ctx (&ctx);
+
+ /* Process whole buffer but last len % 64 bytes. */
+ sha256_process_bytes (buffer, len, &ctx);
+
+ /* Put result in desired memory area. */
+ return sha224_finish_ctx (&ctx, resblock);
+}
+
+void
+sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx)
+{
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+ if (ctx->buflen != 0)
+ {
+ size_t left_over = ctx->buflen;
+ size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+ memcpy (&((char *) ctx->buffer)[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (ctx->buflen > 64)
+ {
+ sha256_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
+
+ ctx->buflen &= 63;
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy (ctx->buffer,
+ &((char *) ctx->buffer)[(left_over + add) & ~63],
+ ctx->buflen);
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len >= 64)
+ {
+#if !_STRING_ARCH_unaligned
+# define alignof(type) offsetof (struct { char c; type x; }, x)
+# define UNALIGNED_P(p) ((uintptr_t) (p) % alignof (uint32_t) != 0)
+ if (UNALIGNED_P (buffer))
+ while (len > 64)
+ {
+ sha256_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
+ buffer = (const char *) buffer + 64;
+ len -= 64;
+ }
+ else
+#endif
+ {
+ sha256_process_block (buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ }
+ }
+
+ /* Move remaining bytes in internal buffer. */
+ if (len > 0)
+ {
+ size_t left_over = ctx->buflen;
+
+ memcpy (&((char *) ctx->buffer)[left_over], buffer, len);
+ left_over += len;
+ if (left_over >= 64)
+ {
+ sha256_process_block (ctx->buffer, 64, ctx);
+ left_over -= 64;
+ memcpy (ctx->buffer, &ctx->buffer[16], left_over);
+ }
+ ctx->buflen = left_over;
+ }
+}
+
+/* --- Code below is the primary difference between sha1.c and sha256.c --- */
+
+/* SHA256 round constants */
+#define K(I) sha256_round_constants[I]
+static const uint32_t sha256_round_constants[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+ 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+ 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+ 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+ 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+ 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+ 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+ 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+ 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+ 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+ 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+ 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+ 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL,
+};
+
+/* Round functions. */
+#define F2(A,B,C) ( ( A & B ) | ( C & ( A | B ) ) )
+#define F1(E,F,G) ( G ^ ( E & ( F ^ G ) ) )
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0.
+ Most of this code comes from GnuPG's cipher/sha1.c. */
+
+void
+sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx)
+{
+ const uint32_t *words = buffer;
+ size_t nwords = len / sizeof (uint32_t);
+ const uint32_t *endp = words + nwords;
+ uint32_t x[16];
+ uint32_t a = ctx->state[0];
+ uint32_t b = ctx->state[1];
+ uint32_t c = ctx->state[2];
+ uint32_t d = ctx->state[3];
+ uint32_t e = ctx->state[4];
+ uint32_t f = ctx->state[5];
+ uint32_t g = ctx->state[6];
+ uint32_t h = ctx->state[7];
+ uint32_t lolen = len;
+
+ /* First increment the byte count. FIPS PUB 180-2 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. Do a double word increment. */
+ ctx->total[0] += lolen;
+ ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen);
+
+#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+#define S0(x) (rol(x,25)^rol(x,14)^(x>>3))
+#define S1(x) (rol(x,15)^rol(x,13)^(x>>10))
+#define SS0(x) (rol(x,30)^rol(x,19)^rol(x,10))
+#define SS1(x) (rol(x,26)^rol(x,21)^rol(x,7))
+
+#define M(I) ( tm = S1(x[(I-2)&0x0f]) + x[(I-7)&0x0f] \
+ + S0(x[(I-15)&0x0f]) + x[I&0x0f] \
+ , x[I&0x0f] = tm )
+
+#define R(A,B,C,D,E,F,G,H,K,M) do { t0 = SS0(A) + F2(A,B,C); \
+ t1 = H + SS1(E) \
+ + F1(E,F,G) \
+ + K \
+ + M; \
+ D += t1; H = t0 + t1; \
+ } while(0)
+
+ while (words < endp)
+ {
+ uint32_t tm;
+ uint32_t t0, t1;
+ int t;
+ /* FIXME: see sha1.c for a better implementation. */
+ for (t = 0; t < 16; t++)
+ {
+ x[t] = SWAP (*words);
+ words++;
+ }
+
+ R( a, b, c, d, e, f, g, h, K( 0), x[ 0] );
+ R( h, a, b, c, d, e, f, g, K( 1), x[ 1] );
+ R( g, h, a, b, c, d, e, f, K( 2), x[ 2] );
+ R( f, g, h, a, b, c, d, e, K( 3), x[ 3] );
+ R( e, f, g, h, a, b, c, d, K( 4), x[ 4] );
+ R( d, e, f, g, h, a, b, c, K( 5), x[ 5] );
+ R( c, d, e, f, g, h, a, b, K( 6), x[ 6] );
+ R( b, c, d, e, f, g, h, a, K( 7), x[ 7] );
+ R( a, b, c, d, e, f, g, h, K( 8), x[ 8] );
+ R( h, a, b, c, d, e, f, g, K( 9), x[ 9] );
+ R( g, h, a, b, c, d, e, f, K(10), x[10] );
+ R( f, g, h, a, b, c, d, e, K(11), x[11] );
+ R( e, f, g, h, a, b, c, d, K(12), x[12] );
+ R( d, e, f, g, h, a, b, c, K(13), x[13] );
+ R( c, d, e, f, g, h, a, b, K(14), x[14] );
+ R( b, c, d, e, f, g, h, a, K(15), x[15] );
+ R( a, b, c, d, e, f, g, h, K(16), M(16) );
+ R( h, a, b, c, d, e, f, g, K(17), M(17) );
+ R( g, h, a, b, c, d, e, f, K(18), M(18) );
+ R( f, g, h, a, b, c, d, e, K(19), M(19) );
+ R( e, f, g, h, a, b, c, d, K(20), M(20) );
+ R( d, e, f, g, h, a, b, c, K(21), M(21) );
+ R( c, d, e, f, g, h, a, b, K(22), M(22) );
+ R( b, c, d, e, f, g, h, a, K(23), M(23) );
+ R( a, b, c, d, e, f, g, h, K(24), M(24) );
+ R( h, a, b, c, d, e, f, g, K(25), M(25) );
+ R( g, h, a, b, c, d, e, f, K(26), M(26) );
+ R( f, g, h, a, b, c, d, e, K(27), M(27) );
+ R( e, f, g, h, a, b, c, d, K(28), M(28) );
+ R( d, e, f, g, h, a, b, c, K(29), M(29) );
+ R( c, d, e, f, g, h, a, b, K(30), M(30) );
+ R( b, c, d, e, f, g, h, a, K(31), M(31) );
+ R( a, b, c, d, e, f, g, h, K(32), M(32) );
+ R( h, a, b, c, d, e, f, g, K(33), M(33) );
+ R( g, h, a, b, c, d, e, f, K(34), M(34) );
+ R( f, g, h, a, b, c, d, e, K(35), M(35) );
+ R( e, f, g, h, a, b, c, d, K(36), M(36) );
+ R( d, e, f, g, h, a, b, c, K(37), M(37) );
+ R( c, d, e, f, g, h, a, b, K(38), M(38) );
+ R( b, c, d, e, f, g, h, a, K(39), M(39) );
+ R( a, b, c, d, e, f, g, h, K(40), M(40) );
+ R( h, a, b, c, d, e, f, g, K(41), M(41) );
+ R( g, h, a, b, c, d, e, f, K(42), M(42) );
+ R( f, g, h, a, b, c, d, e, K(43), M(43) );
+ R( e, f, g, h, a, b, c, d, K(44), M(44) );
+ R( d, e, f, g, h, a, b, c, K(45), M(45) );
+ R( c, d, e, f, g, h, a, b, K(46), M(46) );
+ R( b, c, d, e, f, g, h, a, K(47), M(47) );
+ R( a, b, c, d, e, f, g, h, K(48), M(48) );
+ R( h, a, b, c, d, e, f, g, K(49), M(49) );
+ R( g, h, a, b, c, d, e, f, K(50), M(50) );
+ R( f, g, h, a, b, c, d, e, K(51), M(51) );
+ R( e, f, g, h, a, b, c, d, K(52), M(52) );
+ R( d, e, f, g, h, a, b, c, K(53), M(53) );
+ R( c, d, e, f, g, h, a, b, K(54), M(54) );
+ R( b, c, d, e, f, g, h, a, K(55), M(55) );
+ R( a, b, c, d, e, f, g, h, K(56), M(56) );
+ R( h, a, b, c, d, e, f, g, K(57), M(57) );
+ R( g, h, a, b, c, d, e, f, K(58), M(58) );
+ R( f, g, h, a, b, c, d, e, K(59), M(59) );
+ R( e, f, g, h, a, b, c, d, K(60), M(60) );
+ R( d, e, f, g, h, a, b, c, K(61), M(61) );
+ R( c, d, e, f, g, h, a, b, K(62), M(62) );
+ R( b, c, d, e, f, g, h, a, K(63), M(63) );
+
+ a = ctx->state[0] += a;
+ b = ctx->state[1] += b;
+ c = ctx->state[2] += c;
+ d = ctx->state[3] += d;
+ e = ctx->state[4] += e;
+ f = ctx->state[5] += f;
+ g = ctx->state[6] += g;
+ h = ctx->state[7] += h;
+ }
+}
diff --git a/src/sha256.h b/src/sha256.h
new file mode 100644
index 0000000..44060a7
--- a/dev/null
+++ b/src/sha256.h
@@ -0,0 +1,91 @@
+/* Declarations of functions and data types used for SHA256 and SHA224 sum
+ library functions.
+ Copyright (C) 2005-2006, 2008-2013 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#ifndef ECLAT_SHA256_H
+# define ECLAT_SHA256_H 1
+
+# include <stdio.h>
+# include <stdint.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/* Structure to save state of computation between the single steps. */
+struct sha256_ctx
+{
+ uint32_t state[8];
+
+ uint32_t total[2];
+ size_t buflen;
+ uint32_t buffer[32];
+};
+
+enum { SHA224_DIGEST_SIZE = 224 / 8 };
+enum { SHA256_DIGEST_SIZE = 256 / 8 };
+
+/* Initialize structure containing state of computation. */
+extern void sha256_init_ctx (struct sha256_ctx *ctx);
+extern void sha224_init_ctx (struct sha256_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is necessary that LEN is a multiple of 64!!! */
+extern void sha256_process_block (const void *buffer, size_t len,
+ struct sha256_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is NOT required that LEN is a multiple of 64. */
+extern void sha256_process_bytes (const void *buffer, size_t len,
+ struct sha256_ctx *ctx);
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ in first 32 (28) bytes following RESBUF. The result is always in little
+ endian byte order, so that a byte-wise output yields to the wanted
+ ASCII representation of the message digest. */
+extern void *sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf);
+extern void *sha224_finish_ctx (struct sha256_ctx *ctx, void *resbuf);
+
+
+/* Put result from CTX in first 32 (28) bytes following RESBUF. The result is
+ always in little endian byte order, so that a byte-wise output yields
+ to the wanted ASCII representation of the message digest. */
+extern void *sha256_read_ctx (const struct sha256_ctx *ctx, void *resbuf);
+extern void *sha224_read_ctx (const struct sha256_ctx *ctx, void *resbuf);
+
+
+/* Compute SHA256 (SHA224) message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 32 (28) bytes
+ beginning at RESBLOCK. */
+extern int sha256_stream (FILE *stream, void *resblock);
+extern int sha224_stream (FILE *stream, void *resblock);
+
+/* Compute SHA256 (SHA224) message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+extern void *sha256_buffer (const char *buffer, size_t len, void *resblock);
+extern void *sha224_buffer (const char *buffer, size_t len, void *resblock);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
diff --git a/src/varnish_mib.h b/src/varnish_mib.h
new file mode 100644
index 0000000..31e3de1
--- a/dev/null
+++ b/src/varnish_mib.h
@@ -0,0 +1,52 @@
+/* This file is part of varnish-mib -*- c -*-
+ Copyright (C) 2014 Sergey Poznyakoff
+
+ Varnish-mib 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 3, or (at your option)
+ any later version.
+
+ Varnish-mib 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 varnish-mib. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <config.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <vapi/vsc.h>
+#include <vapi/vsm.h>
+#include <vcli.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+typedef struct vcli_conn {
+ int fd;
+ char *secret;
+ int resp;
+ char *base;
+ size_t bufmax;
+ size_t bufsize;
+} vcli_conn_t;
+
+int vcli_write(vcli_conn_t *conn);
+int vcli_read_response(vcli_conn_t *conn);
+int vcli_vasprintf(vcli_conn_t *conn, const char *fmt, va_list ap);
+int vcli_asprintf(vcli_conn_t *conn, const char *fmt, ...);
+void vcli_disconnect(vcli_conn_t *conn);
+int vcli_connect(struct VSM_data *vd, vcli_conn_t *conn);
+
+
+int varnish_auth_response(const char *file, const char *challenge,
+ char response[CLI_AUTH_RESPONSE_LEN + 1]);
+
+int varnish_ban(netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests,
+ struct VSM_data *vd);
+
diff --git a/src/varnish_mib.mib2c b/src/varnish_mib.mib2c
index af56a0e..8535b0d 100644
--- a/src/varnish_mib.mib2c
+++ b/src/varnish_mib.mib2c
@@ -26,16 +26,7 @@
@open ${name}@
/* THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT EDIT. */
-#include <config.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <vapi/vsc.h>
-#include <vapi/vsm.h>
-
-#include <net-snmp/net-snmp-config.h>
-#include <net-snmp/net-snmp-includes.h>
-#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include "varnish_mib.h"
static struct VSM_data *vd;
@@ -58,25 +49,27 @@ varnish_snmp_deinit(void)
$vars{'varnish_translate'} = sub {
my $name = shift;
my %trans = (
- clientAcceptedConnections => 'sess_conn',
- clientRequestsReceived => 'client_req',
- clientCacheHits => 'cache_hit',
- clientCacheHitsPass => 'cache_hitpass',
- clientCacheMisses => 'cache_miss',
- backendConnSuccess => 'backend_conn',
- backendConnNotAttempted => 'backend_unhealthy',
- backendConnToMany => 'backend_busy ',
- backendConnFailures => 'backend_fail',
- backendConnReuses => 'backend_reuse',
- backendConnRecycled => 'backend_recycle',
- backendConnUnused => 'backend_toolate',
- totalSessions => 's_sess',
- totalRequests => 's_req ',
- totalPipe => 's_pipe',
- totalPass => 's_pass',
- totalFetch => 's_fetch',
- totalHeaderBytes => 's_req_hdrbytes',
- totalBodyBytes => 's_req_bodybytes'
+ clientAcceptedConnections => [ 'MAIN', 'sess_conn' ],
+ clientRequestsReceived => [ 'MAIN', 'client_req' ],
+ clientCacheHits => [ 'MAIN', 'cache_hit' ],
+ clientCacheHitsPass => [ 'MAIN', 'cache_hitpass' ],
+ clientCacheMisses => [ 'MAIN', 'cache_miss' ],
+ clientBan => [ 'STRING', '',
+ { varnish_set_action => 'varnish_ban' } ],
+ backendConnSuccess => [ 'MAIN', 'backend_conn' ],
+ backendConnNotAttempted => [ 'MAIN', 'backend_unhealthy' ],
+ backendConnToMany => [ 'MAIN', 'backend_busy' ],
+ backendConnFailures => [ 'MAIN', 'backend_fail' ],
+ backendConnReuses => [ 'MAIN', 'backend_reuse' ],
+ backendConnRecycled => [ 'MAIN', 'backend_recycle' ],
+ backendConnUnused => [ 'MAIN', 'backend_toolate' ],
+ totalSessions => [ 'MAIN', 's_sess' ],
+ totalRequests => [ 'MAIN', 's_req' ],
+ totalPipe => [ 'MAIN', 's_pipe' ],
+ totalPass => [ 'MAIN', 's_pass' ],
+ totalFetch => [ 'MAIN', 's_fetch' ],
+ totalHeaderBytes => [ 'MAIN', 's_req_hdrbytes' ],
+ totalBodyBytes => [ 'MAIN', 's_req_bodybytes' ]
);
my $r = $trans{$name};
@@ -84,7 +77,16 @@ $vars{'varnish_translate'} = sub {
print STDERR "no translation for $name!\n";
exit(1);
}
- $vars{'varnish_member'} = $r;
+
+ $vars{'varnish_type'} = $r->[0];
+ $vars{'varnish_member'} = $r->[1];
+ my $setkw = qw(varnish_set_reserve2 varnish_set_free varnish_set_action
+ varnish_set_commit varnish_set_undo);
+ if ($#{$r} == 2) {
+ @vars{keys %{$r->[2]}} = values %{$r->[2]};
+ } else {
+ delete $vars{$setkw};
+ }
return 0;
};
@@ -110,31 +112,106 @@ handle_$i(netsnmp_mib_handler *handler,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
- uint32_t val;
+ @if $i.settable@
+ int ret;
+ @end@
@startperl@
&{$vars{'varnish_translate'}}($vars{'i'});
@endperl@
- if (reqinfo->mode == MODE_GET) {
- struct VSC_C_main const *st;
-
- if (VSM_Abandoned(vd)) {
- VSM_Close(vd);
- VSM_Open(vd);
- }
-
- st = VSC_Main(vd, NULL);
+
+ if (VSM_Abandoned(vd)) {
+ DEBUGMSGTL(("$modulename", "reopening vd\n"));
+ VSM_Close(vd);
+ VSM_Open(vd);
+ }
+
+ switch(reqinfo->mode) {
+ case MODE_GET:
+ @if $varnish_type eq 'MAIN'@
+ {
+ struct VSC_C_main const *st = VSC_Main(vd, NULL);
if (!st)
return SNMP_ERR_NOSUCHNAME;
snmp_set_var_typed_value(requests->requestvb, $i.type,
&st->$varnish_member,
sizeof(st->$varnish_member));
- } else {
- snmp_log(LOG_ERR, "unknown mode (%d) in handle_${i}\n",
- reqinfo->mode );
- return SNMP_ERR_GENERR;
}
+ @elsif $varnish_type eq 'STRING'@
+ {
+ const char *s = "$varnish_member";
+ snmp_set_var_typed_value(requests->requestvb, $i.type,
+ s, strlen(s));
+ }
+ @else@
+ @printf "unrecognized type %s for %s" $varnish_type $i@
+ @end@
+ break;
+
+ @if $i.settable@
+ /*
+ * SET REQUEST
+ *
+ * multiple states in the transaction. See:
+ * http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg
+ */
+ case MODE_SET_RESERVE1:
+ /* or you could use netsnmp_check_vb_type_and_size instead */
+ ret = netsnmp_check_vb_type(requests->requestvb, $i.type);
+ if (ret != SNMP_ERR_NOERROR)
+ netsnmp_set_request_error(reqinfo, requests, ret);
+ break;
+
+ case MODE_SET_RESERVE2:
+ @if $varnish_set_reserve2 ne ''@
+ if ($varnish_set_reserve2 (reqinfo, requests, vd)) {
+ netsnmp_set_request_error(reqinfo, requests,
+ SNMP_ERR_RESOURCEUNAVAILABLE);
+ }
+ @end@
+ break;
+
+ case MODE_SET_FREE:
+ @if $varnish_set_free ne ''@
+ /* XXX: free resources allocated in RESERVE1 and/or
+ RESERVE2. Something failed somewhere, and the states
+ below won't be called. */
+ $varnish_set_free(reqinfo, requests, vd);
+ @end@
+ break;
+
+ case MODE_SET_ACTION:
+ @if $varnish_set_action ne ''@
+ /* XXX: perform the value change here */
+ ret = $varnish_set_action(reqinfo, requests, vd);
+ if (ret != SNMP_ERR_NOERROR)
+ netsnmp_set_request_error(reqinfo, requests, ret);
+ @end@
+ break;
+
+ case MODE_SET_COMMIT:
+ @if $varnish_set_commit ne ''@
+ /* XXX: delete temporary storage */
+ if ($varnish_set_commit(reqinfo, requests, vd))
+ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_COMMITFAILED);
+ @end@
+ break;
+
+ case MODE_SET_UNDO:
+ @if $varnish_set_undo ne ''@
+ /* XXX: UNDO and return to previous value for the object */
+ if ($varnish_set_undo(reqinfo, requests, vd))
+ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED);
+ @end@
+ break;
+ @end@
+ default:
+ /* we should never get here, so this is a really bad error */
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_${i}\n", reqinfo->mode );
+ return SNMP_ERR_GENERR;
+ }
+
return SNMP_ERR_NOERROR;
}
@end@
@@ -153,7 +230,13 @@ init_$modulename(void)
netsnmp_register_scalar(
netsnmp_create_handler_registration("$i", handle_$i,
${i}_oid, OID_LENGTH(${i}_oid),
- HANDLER_CAN_RONLY));
+ @if !$i.settable@
+ HANDLER_CAN_RONLY
+ @end@
+ @if $i.settable@
+ HANDLER_CAN_RWRITE
+ @end@
+ ));
@end@
varnish_snmp_init();
}
diff --git a/src/vcli.c b/src/vcli.c
new file mode 100644
index 0000000..aaa0933
--- a/dev/null
+++ b/src/vcli.c
@@ -0,0 +1,499 @@
+/* This file is part of varnish-mib -*- c -*-
+ Copyright (C) 2014 Sergey Poznyakoff
+
+ Varnish-mib 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 3, or (at your option)
+ any later version.
+
+ Varnish-mib 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 varnish-mib. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "varnish_mib.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#define ISSPACE(c) ((c)==' '||(c)=='\t'||(c)=='\n')
+
+unsigned long vcli_timeout = 500;
+
+#define VCLI_INIT_ALLOC 16
+
+static int
+vcli_alloc(struct vcli_conn *conn, size_t size)
+{
+ char *p;
+
+ if (size < conn->bufmax)
+ return 0;
+ p = realloc(conn->base, size);
+ if (!p) {
+ snmp_log(LOG_ERR, "out of memory\n");
+ return -1;
+ }
+ conn->base = p;
+ conn->bufmax = size;
+ return 0;
+}
+
+static int
+vcli_extra_size(struct vcli_conn *conn, size_t incr)
+{
+ if (incr + conn->bufsize > conn->bufmax) {
+ size_t size;
+ if (conn->bufmax == 0)
+ size = incr > VCLI_INIT_ALLOC
+ ? incr
+ : VCLI_INIT_ALLOC;
+ else {
+ for (size = conn->bufmax; size < conn->bufsize + incr;
+ size *= 2)
+ if (size <= conn->bufmax) {
+ snmp_log(LOG_ERR, "out of memory\n");
+ return -1;
+ }
+ }
+ return vcli_alloc(conn, size);
+ }
+ return 0;
+}
+
+static int
+vcli_read(struct vcli_conn *conn, size_t size)
+{
+ fd_set rd;
+ time_t start;
+ struct timeval tv;
+ long ttl;
+ int ret = 0;
+ int rc;
+
+ ++size;
+ rc = vcli_alloc(conn, size + 1);
+ if (rc)
+ return SNMP_ERR_GENERR;
+
+ conn->bufsize = 0;
+ time(&start);
+ while (size) {
+ FD_ZERO(&rd);
+ FD_SET(conn->fd, &rd);
+
+ ttl = vcli_timeout - (time(NULL) - start);
+ if (ttl <= 0) {
+ snmp_log(LOG_ERR, "timed out reading from varnish\n");
+ ret = -1;
+ break;
+ }
+
+ tv.tv_sec = ttl;
+ tv.tv_usec = 0;
+ rc = select(conn->fd + 1, &rd, NULL, NULL, &tv);
+ if (rc < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ snmp_log(LOG_ERR, "select: %s\n", strerror(errno));
+ ret = -1;
+ break;
+ }
+
+ if (FD_ISSET(conn->fd, &rd)) {
+ int n;
+
+ if (ioctl(conn->fd, FIONREAD, &n) < 0) {
+ snmp_log(LOG_ERR, "ioctl: %s\n",
+ strerror(errno));
+ ret = -1;
+ break;
+ }
+ if (n > size)
+ n = size;
+ rc = read(conn->fd, conn->base + conn->bufsize, n);
+ if (rc > 0) {
+ conn->bufsize += rc;
+ size -= rc;
+ } else if (rc == 0
+ || errno == EINTR || errno == EAGAIN) {
+ continue;
+ } else {
+ snmp_log(LOG_ERR, "read: %s\n",
+ strerror(errno));
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ if (ret == 0) {
+ if (conn->bufsize == 0)
+ ret = -1;
+ conn->base[conn->bufsize] = 0;
+ DEBUGMSGTL(("vcli_mib", "<<varnish: %s\n", conn->base));
+ }
+
+ return ret;
+}
+
+static int
+vcli_getline(struct vcli_conn *conn)
+{
+ fd_set rd;
+ time_t start;
+ struct timeval tv;
+ long ttl;
+ int ret = 0;
+ int rc;
+
+ conn->bufsize = 0;
+ time(&start);
+ while (1) {
+ FD_ZERO(&rd);
+ FD_SET(conn->fd, &rd);
+
+ ttl = vcli_timeout - (time(NULL) - start);
+ if (ttl <= 0) {
+ snmp_log(LOG_ERR, "timed out reading from varnish\n");
+ ret = -1;
+ break;
+ }
+
+ tv.tv_sec = ttl;
+ tv.tv_usec = 0;
+ rc = select(conn->fd + 1, &rd, NULL, NULL, &tv);
+ if (rc < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ snmp_log(LOG_ERR, "select: %s\n", strerror(errno));
+ ret = -1;
+ break;
+ }
+
+ if (FD_ISSET(conn->fd, &rd)) {
+ char c;
+
+ rc = read(conn->fd, &c, 1);
+ if (rc == 1) {
+ if (vcli_extra_size(conn, 1)) {
+ ret = -1;
+ break;
+ }
+ conn->base[conn->bufsize++] = c;
+ if (c == '\n')
+ break;
+ } else if (rc == 0
+ || errno == EINTR || errno == EAGAIN) {
+ continue;
+ } else {
+ snmp_log(LOG_ERR, "read: %s\n",
+ strerror(errno));
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ if (ret == 0) {
+ if (conn->bufsize == 0)
+ ret = -1;
+ else if (conn->base[conn->bufsize-1] == '\n') {
+ conn->bufsize--;
+ if (conn->base[conn->bufsize-1] == '\r')
+ conn->bufsize--;
+ }
+ conn->base[conn->bufsize] = 0;
+ }
+
+ return ret;
+}
+
+int
+vcli_write(struct vcli_conn *conn)
+{
+ size_t size;
+
+ DEBUGMSGTL(("vcli_mib", ">>varnish: %s\n", conn->base));
+ for (size = 0; size < conn->bufsize; ) {
+ int n = write(conn->fd, conn->base + size,
+ conn->bufsize - size);
+ if (n < 0) {
+ snmp_log(LOG_ERR, "write error: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ size += n;
+ }
+ return 0;
+}
+
+int
+vcli_read_response(struct vcli_conn *conn)
+{
+ char *p;
+ unsigned long n;
+
+ if (vcli_getline(conn)) {
+ vcli_disconnect(conn);
+ return SNMP_ERR_GENERR;
+ }
+
+ n = strtoul(conn->base, &p, 10);
+ conn->resp = n;
+ if (!ISSPACE(*p)) {
+ snmp_log(LOG_ERR, "unrecognized response from Varnish: %s\n",
+ conn->base);
+ return SNMP_ERR_GENERR;
+ }
+ while (*p && ISSPACE(*p))
+ ++p;
+ n = strtoul(p, &p, 10);
+ if (n > 0)
+ return vcli_read(conn, n);
+ return 0;
+}
+
+int
+vcli_vasprintf(struct vcli_conn *conn, const char *fmt, va_list ap)
+{
+ int rc = 0;
+
+ rc = vcli_alloc(conn, VCLI_INIT_ALLOC);
+ if (rc)
+ return -1;
+
+ for (;;) {
+ va_list aq;
+ ssize_t n;
+
+ va_copy(aq, ap);
+ n = vsnprintf(conn->base, conn->bufmax, fmt, aq);
+ va_end(aq);
+ if (n < 0 || n >= conn->bufmax ||
+ !memchr(conn->base, '\0', n + 1)) {
+ size_t newlen = conn->bufmax * 2;
+ if (newlen < conn->bufmax) {
+ snmp_log(LOG_ERR, "out of memory\n");
+ rc = -1;
+ break;
+ }
+ rc = vcli_alloc(conn, newlen);
+ if (rc)
+ break;
+ } else {
+ conn->bufsize = n;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+int
+vcli_asprintf(struct vcli_conn *conn, const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rc = vcli_vasprintf(conn, fmt, ap);
+ va_end(ap);
+ return rc;
+}
+
+
+static int
+open_socket(struct sockaddr_in *sa, const char *connstr)
+{
+ int fd = socket(sa->sin_family, SOCK_STREAM, 0);
+ struct timeval start, connect_tv;
+
+ if (fd == -1) {
+ snmp_log(LOG_ERR, "socket: %s\n", strerror(errno));
+ return -1;
+ }
+
+ connect_tv.tv_sec = vcli_timeout;
+ connect_tv.tv_usec = 0;
+
+ gettimeofday(&start, NULL);
+ for (;;) {
+ int rc = connect(fd, sa, sizeof(*sa));
+ if (rc == 0)
+ break;
+ else if (errno == ECONNREFUSED) {
+ struct timeval tv, now;
+ fd_set rset, wset, xset;
+
+ gettimeofday(&now, NULL);
+ timersub(&now, &start, &tv);
+
+ if (timercmp(&tv, &connect_tv, < )) {
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+ FD_ZERO(&wset);
+ FD_SET(fd, &wset);
+ FD_ZERO(&xset);
+ FD_SET(fd, &xset);
+ select(fd + 1, &rset, &wset, &xset, &tv);
+ continue;
+ }
+ }
+
+ snmp_log(LOG_ERR, "cannot connect to %s: %s\n",
+ connstr, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+void
+vcli_disconnect(struct vcli_conn *conn)
+{
+ close(conn->fd);
+ free(conn->base);
+ free(conn->secret);
+ memset(conn, 0, sizeof(*conn));
+}
+
+static int
+vcli_handshake(struct vcli_conn *conn)
+{
+ if (vcli_read_response(conn))
+ return 1;
+
+ if (conn->resp == CLIS_AUTH) {
+ char buf[CLI_AUTH_RESPONSE_LEN + 1];
+ char *p = strchr(conn->base, '\n');
+
+ if (!p) {
+ snmp_log(LOG_ERR,
+ "unrecognized response from Varnish: %s\n",
+ conn->base);
+ return SNMP_ERR_GENERR;
+ }
+ *p = 0;
+ if (varnish_auth_response(conn->secret, conn->base, buf))
+ return 1;
+
+ if (vcli_asprintf(conn, "auth %s\n", buf) ||
+ vcli_write(conn))
+ return 1;
+
+ if (vcli_read_response(conn))
+ return 1;
+ }
+
+ if (conn->resp != CLIS_OK) {
+ snmp_log(LOG_ERR, "Varnish connection rejected: %u %s\n",
+ conn->resp, conn->base);
+ return 1;
+ }
+
+ if (vcli_asprintf(conn, "ping\n") || vcli_write(conn))
+ return 1;
+
+ if (vcli_read_response(conn))
+ return 1;
+
+ if (conn->resp != CLIS_OK || strstr(conn->base, "PONG") == NULL) {
+ snmp_log(LOG_ERR, "expected PONG, but got %s\n",
+ conn->base);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+vcli_connect(struct VSM_data *vd, struct vcli_conn *conn)
+{
+ struct VSM_fantom vt;
+ struct sockaddr_in vcli_sa;
+ char *s, *portstr, *p;
+ unsigned long n;
+ short pn;
+ struct hostent *hp;
+
+ memset(conn, 0, sizeof(*conn));
+
+ if (!VSM_Get(vd, &vt, "Arg", "-T", "")) {
+ snmp_log(LOG_ERR, "no -T arg in shared memory\n");
+ return SNMP_ERR_GENERR;
+ }
+ DEBUGMSGTL(("vcli_mib", "-T '%s'\n", vt.b));
+
+ s = strdup(vt.b);
+ if (!s) {
+ snmp_log(LOG_ERR, "out of memory\n");
+ return SNMP_ERR_GENERR;
+ }
+ for (portstr = s; !ISSPACE(*portstr); portstr++)
+ ;
+ if (!*portstr) {
+ snmp_log(LOG_ERR, "unrecognized -T arg: %s\n", s);
+ free(s);
+ return SNMP_ERR_GENERR;
+ }
+ for (*portstr++ = 0; ISSPACE(*portstr); portstr++)
+ ;
+
+ n = pn = strtoul(portstr, &p, 0);
+ if (n != pn || (*p && !ISSPACE(*p))) {
+ snmp_log(LOG_ERR, "unrecognized -T arg: %s\n", s);
+ free(s);
+ return SNMP_ERR_GENERR;
+ }
+
+ hp = gethostbyname(s);
+ if (!hp) {
+ snmp_log(LOG_ERR, "unknown host name %s\n", s);
+ free(s);
+ return SNMP_ERR_GENERR;
+ }
+
+ vcli_sa.sin_family = hp->h_addrtype;
+ if (vcli_sa.sin_family != AF_INET) {
+ snmp_log(LOG_ERR, "unknown host name %s\n", s);
+ free(s);
+ return SNMP_ERR_GENERR;
+ }
+
+ memmove(&vcli_sa.sin_addr, hp->h_addr, 4);
+ vcli_sa.sin_port = htons(pn);
+
+ conn->fd = open_socket(&vcli_sa, s);
+ free(s);
+ if (conn->fd == -1)
+ return SNMP_ERR_GENERR;
+
+ if (!VSM_Get(vd, &vt, "Arg", "-S", "")) {
+ snmp_log(LOG_ERR, "no -S arg in shared memory\n");
+ return SNMP_ERR_GENERR;
+ }
+ DEBUGMSGTL(("vcli_mib", "-S '%s'\n", vt.b));
+ s = strdup(vt.b);
+ if (!s) {
+ snmp_log(LOG_ERR, "out of memory\n");
+ return SNMP_ERR_GENERR;
+ }
+ conn->secret = s;
+
+ if (vcli_handshake(conn)) {
+ vcli_disconnect(conn);
+ return SNMP_ERR_GENERR;
+ }
+ return SNMP_ERR_NOERROR;
+}

Return to:

Send suggestions and report system problems to the System administrator.