Browse Source

move ccapi to trunk

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@109 e5f2f494-b856-4b98-b285-d166d9295462
Michael Graff 15 years ago
parent
commit
da2390e7ec

+ 1 - 0
src/bin/msgq/.cvsignore

@@ -0,0 +1 @@
+msgq

+ 35 - 0
src/bin/msgq/Makefile

@@ -0,0 +1,35 @@
+# Copyright (C) 2002  Internet Software Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+OBJS=cc.o msgq.o msgbuf.o
+TARGETS=msgq ${OBJS}
+
+
+
+CFLAGS += ${ISC_CFLAGS} -g
+CFLAGS += -Wall -Wstrict-prototypes
+CFLAGS += `isc-config.sh --cflags isc`
+
+ISC_LIBS += `isc-config.sh --libs isc`
+
+msgq: ${OBJS}
+	${CC} -o msgq ${OBJS} ${ISC_LIBS}
+
+clean:
+	rm -f *.o *~ *.core core msgq
+
+msgq.o: msgq.c msgbuf.h cc.h
+msgbuf.o: msgbuf.c msgbuf.h
+cc.o: cc.c cc.h

+ 237 - 0
src/bin/msgq/cc.c

@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2002  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: cc.c,v 1.6 2002/12/06 21:06:26 lidl Exp $ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/util.h>
+
+#include "cc.h"
+
+#define MSG_MAGIC 0x536b616e
+
+int
+cc_matchdata(ccsearch_t *c, u_int32_t type, void *data, u_int32_t dlen)
+{
+	if (c->type == type
+	    && c->dlen == dlen
+	    && memcmp(c->data, data, dlen) == 0)
+		return 1;
+
+	return 0;
+}
+
+ccsearch_t *
+cc_findtag(ccsearch_t *cc, char *tag, u_int32_t tlen)
+{
+	ccsearch_t *c;
+
+	c = cc;
+	while (c->tag != NULL) {
+		if (c->tlen == tlen
+		    && memcmp(c->tag, tag, tlen) == 0)
+			return c;
+		c++;
+	}
+
+	return NULL;
+}
+
+int
+cc_search(logfunc_t lf, void *lfarg, ccsearch_t *cc,
+	  void *msg, unsigned int msglen)
+{
+	isc_buffer_t b;
+	u_int32_t magic;
+	ccsearch_t *c;
+	u_int32_t item_type, item_code, item_lencode;
+	u_int32_t item_len, tag_len;
+	char *tag_name, *item_ptr;
+	int total, n;
+
+	n = 0;
+	c = cc;
+	while (c->tag != NULL) {
+		c->type = ITEM_UNDEFINED;
+		c->dlen = 0;
+		c->data = 0;
+
+		c++;
+		n++;
+	}
+
+	if (n == 0)
+		return 0;
+	total = n;
+
+	isc_buffer_init(&b, msg, msglen);
+	isc_buffer_add(&b, msglen);
+
+	if (isc_buffer_remaininglength(&b) < 4)
+		return -1;
+
+	magic = isc_buffer_getuint32(&b);
+	if (magic != MSG_MAGIC) {
+		if (lf != NULL)
+			lf(lfarg, "BAD PACKET: MAGIC == %x", magic);
+		return -1;
+	}
+
+	/*
+	 * Walk the buffer, remembering when we find important tags.
+	 * We'll set pointers and remember the lengths of:
+	 *    to from type group instance
+	 *
+	 * We assume all of these are at the top level (it's our protocol,
+	 * so this is pretty safe) and we only decode the bits we need.
+	 * This is by no means a general purpose decoder.
+	 */
+	while (n != 0 && isc_buffer_remaininglength(&b) > 0) {
+		if (isc_buffer_remaininglength(&b) < 1) {
+			if (lf != NULL)
+				lf(lfarg, "PKTFMT tag len");
+			return -1;
+		}
+		tag_len = isc_buffer_getuint8(&b);
+
+		if (isc_buffer_remaininglength(&b) < tag_len) {
+			if (lf != NULL)
+				lf(lfarg, "PKTFMT tag (len %d)");
+			return -1;
+		}
+		tag_name = isc_buffer_current(&b);
+		isc_buffer_forward(&b, tag_len);
+
+		if (isc_buffer_remaininglength(&b) < 1) {
+			if (lf != NULL)
+				lf(lfarg, "PKTFMT len/type");
+			return -1;
+		}
+		item_type = isc_buffer_getuint8(&b);
+
+		item_code = item_type & ITEM_MASK;
+		item_lencode = item_type & LENGTH_MASK;
+
+		/*
+		 * Crack out the message length.
+		 */
+		item_len = 0;
+		item_ptr = NULL;
+		if (item_code != ITEM_NULL) {
+			switch (item_lencode) {
+			case LENGTH_8:
+				if (isc_buffer_remaininglength(&b) < 1) {
+					if (lf != NULL)
+						lf(lfarg, "PKTFMT (len8)");
+					return -1;
+				}
+				item_len = isc_buffer_getuint8(&b);
+				break;
+			case LENGTH_16:
+				if (isc_buffer_remaininglength(&b) < 2) {
+					if (lf != NULL)
+						lf(lfarg, "PKTFMT (len16)");
+					return -1;
+				}
+				item_len = isc_buffer_getuint16(&b);
+				break;
+			case LENGTH_32:
+				if (isc_buffer_remaininglength(&b) < 4) {
+					if (lf != NULL)
+						lf(lfarg, "PKTFMT (len32)");
+					return -1;
+				}
+				item_len = isc_buffer_getuint32(&b);
+				break;
+			default:
+				if (lf != NULL)
+					lf(lfarg, "PKTFMT (len code 0x%02x)",
+				   item_lencode);
+				return -1;
+			}
+
+			if (isc_buffer_remaininglength(&b) < item_len) {
+				if (lf != NULL)
+					lf(lfarg, "PKTFMT (item length %d, remaining %d)",
+					   item_len,
+					   isc_buffer_remaininglength(&b));
+				return -1;
+			}
+			item_ptr = isc_buffer_current(&b);
+			isc_buffer_forward(&b, item_len);
+		}
+
+		c = cc;
+		while (c->tag != NULL) {
+			if (c->tlen == tag_len
+			    && memcmp(c->tag, tag_name, tag_len) == 0) {
+				    if (c->type != ITEM_UNDEFINED)
+					    break;
+				    c->type = item_code;
+				    c->data = item_ptr;
+				    c->dlen = item_len;
+				    n--;
+				    break;
+			}
+			c++;
+		}
+	}
+
+	return (total - n);
+}
+
+void
+ccmsg_init(ccmsg_t *msg, void *buf, unsigned int buflen)
+{
+	isc_buffer_init(&msg->b, buf, buflen);
+
+	isc_buffer_putuint32(&msg->b, MSG_MAGIC);
+}
+
+void
+ccmsg_add(ccmsg_t *msg, u_int32_t type, char *tag, u_int32_t tlen,
+	  char *data, u_int32_t dlen)
+{
+	isc_buffer_putuint8(&msg->b, tlen);
+	isc_buffer_putmem(&msg->b, tag, tlen);
+
+	if (dlen < 0x000000ff) {
+		type |= LENGTH_8;
+		isc_buffer_putuint8(&msg->b, type);
+		isc_buffer_putuint8(&msg->b, dlen);
+		isc_buffer_putmem(&msg->b, data, dlen);
+	} else if (dlen < 0x0000ffff) {
+		type |= LENGTH_16;
+		isc_buffer_putuint8(&msg->b, type);
+		isc_buffer_putuint16(&msg->b, dlen);
+		isc_buffer_putmem(&msg->b, data, dlen);
+	} else {
+		type |= LENGTH_32;
+		isc_buffer_putuint8(&msg->b, type);
+		isc_buffer_putuint32(&msg->b, dlen);
+		isc_buffer_putmem(&msg->b, data, dlen);
+	}
+}
+
+u_int32_t
+ccmsg_render(ccmsg_t *msg)
+{
+	return isc_buffer_usedlength(&msg->b);
+}

+ 90 - 0
src/bin/msgq/cc.h

@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2002  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: cc.h,v 1.5 2002/12/06 21:06:27 lidl Exp $ */
+
+#ifndef MSGQ_CC_H
+#define MSGQ_CC_H
+
+#include <isc/buffer.h>
+#include <isc/types.h>
+
+/*
+ * Item types and data lengths.
+ */
+#define ITEM_UNDEFINED	0x00
+#define ITEM_DATA	0x01
+#define ITEM_HASH	0x02
+#define ITEM_LIST	0x03
+#define ITEM_NULL	0x04
+#define ITEM_MASK	0x0f
+
+#define LENGTH_32	0x00
+#define LENGTH_16	0x10
+#define LENGTH_8	0x20
+#define LENGTH_MASK	0x30
+
+typedef struct {
+	/* Set by caller */
+	char *tag;	/* the target of our search */
+	u_int8_t tlen;	/* length of tag name */
+
+	/* Set by function */
+	u_int8_t type;	/* the item type found */
+	u_int32_t dlen;  /* the length of the item */
+	void *data;	/* a pointer to the data for this item */
+} ccsearch_t;
+
+typedef struct {
+	isc_buffer_t b;
+} ccmsg_t;
+
+typedef int (*logfunc_t)(void *, const char *, ...);
+
+/*
+ * Given a NULL-terminated array of ccsearch_t, searches the message
+ * and fills it in.  If logging is needed, it will use the logging function
+ * to log, or now logging if NULL.
+ */
+int cc_search(logfunc_t, void *, ccsearch_t *, void *, unsigned int);
+
+/*
+ * Returns 1 if the type, dlen, and data matches.
+ */
+int cc_matchdata(ccsearch_t *, u_int32_t, void *, u_int32_t);
+
+/*
+ * Find the tag in the ccsearch structure, or return NULL if not found.
+ */
+ccsearch_t *cc_findtag(ccsearch_t *, char *, u_int32_t);
+
+/*
+ * Initialize a ccmsg_t using the buffer provided.
+ */
+void ccmsg_init(ccmsg_t *, void *, unsigned int);
+
+/*
+ * Add a data item to the buffer.
+ */
+void ccmsg_add(ccmsg_t *, u_int32_t, char *, u_int32_t, char *, u_int32_t);
+
+/*
+ * "render" the message.  This really just returns its length.
+ */
+u_int32_t ccmsg_render(ccmsg_t *);
+
+#endif /* MSGQ_CC_H */

+ 73 - 0
src/bin/msgq/msgbuf.c

@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2002  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: msgbuf.c,v 1.2 2002/12/06 21:06:27 lidl Exp $ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include "msgbuf.h"
+
+msgbuf_t *
+msgbuf_create(isc_mem_t *mctx, unsigned int length)
+{
+	msgbuf_t *mb;
+	unsigned int len;
+
+	len = sizeof(msgbuf_t);
+	len += length;
+
+	mb = isc_mem_get(mctx, len);
+	RUNTIME_CHECK(mb != NULL);
+
+	mb->ref = 1;
+	mb->r.base = (unsigned char *)(mb + 1);
+	mb->r.length = length;
+
+	return (mb);
+}
+
+void
+msgbuf_detach(isc_mem_t *mctx, msgbuf_t **mbp)
+{
+	msgbuf_t *mb = *mbp;
+	unsigned int len;
+
+	*mbp = NULL;
+
+	INSIST(mb->ref > 0);
+	mb->ref--;
+	if (mb->ref == 0) {
+		len = sizeof(msgbuf_t);
+		len += mb->r.length;
+
+		isc_mem_put(mctx, mb, len);
+	}
+}
+
+msgbuf_t *
+msgbuf_attach(msgbuf_t *mb)
+{
+	INSIST(mb->ref > 0);
+	mb->ref++;
+
+	return (mb);
+}

+ 39 - 0
src/bin/msgq/msgbuf.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2002  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: msgbuf.h,v 1.2 2002/12/06 21:06:27 lidl Exp $ */
+
+#ifndef MSGQ_MSGBUF_H
+#define MSGQ_MSGBUF_H
+
+#include <isc/region.h>
+
+typedef struct _msgbuf msgbuf_t;
+
+/*
+ * A buffer.
+ */
+struct _msgbuf {
+	int ref;
+	isc_region_t r;
+};
+
+msgbuf_t *msgbuf_create(isc_mem_t *mctx, unsigned int);
+void msgbuf_detach(isc_mem_t *mctx, msgbuf_t **);
+msgbuf_t *msgbuf_attach(msgbuf_t *);
+
+#endif /* MSGQ_MSGBUF_H */

File diff suppressed because it is too large
+ 1331 - 0
src/bin/msgq/msgq.c


+ 193 - 0
src/lib/cc/python/ISC/CC/Message.py

@@ -0,0 +1,193 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import sys
+import struct
+
+class DecodeError(Exception): pass
+
+PROTOCOL_VERSION = 0x536b616e
+
+_ITEM_DATA = 0x01
+_ITEM_HASH = 0x02
+_ITEM_LIST = 0x03
+_ITEM_NULL = 0x04
+_ITEM_MASK = 0x0f
+
+_ITEM_LENGTH_32   = 0x00
+_ITEM_LENGTH_16   = 0x10
+_ITEM_LENGTH_8    = 0x20
+_ITEM_LENGTH_MASK = 0x30
+
+def to_wire(items):
+    """Encode a dict into wire format.
+    >>> wire_format = Message.to_wire({"a": "b"})
+    """
+    ret = []
+    ret.append(struct.pack(">I", PROTOCOL_VERSION))
+    ret.append(_encode_hash(items))
+    return (''.join(ret))
+
+def _encode_tag(tag):
+    """Encode a single UTF-8 tag.
+    ... wire_partial = Message._encode_tag('this')
+    """
+    return(struct.pack(">B", len(str(tag))) + str(tag))
+
+def _encode_length_and_type(data, datatype):
+    """Helper method to handle the length encoding in one place."""
+    if data == None:
+        return(struct.pack(">B", _ITEM_NULL))
+    length = len(data)
+    if length < 0x0000100:
+        return(struct.pack(">B B", datatype | _ITEM_LENGTH_8, length)
+               + data)
+    elif length < 0x00010000:
+        return(struct.pack(">B H", datatype | _ITEM_LENGTH_16, length)
+               + data)
+    else:
+        return(struct.pack(">B I", datatype, length) + data)
+
+def _pack_string(item):
+    """Pack a string (data) and its type/length prefix."""
+    return (_encode_length_and_type(item, _ITEM_DATA))
+
+def _pack_array(item):
+    """Pack a list (array) and its type/length prefix."""
+    return (_encode_length_and_type(_encode_array(item),
+                                         _ITEM_LIST))
+
+def _pack_hash(item):
+    """Pack a dict (hash) and its type/length prefix."""
+    data = _encode_hash(item)
+    return (_encode_length_and_type(data, _ITEM_HASH))
+
+def _encode_string(item):
+    """Encode a string.  More or less identity."""
+    return (item)
+
+def _pack_nil():
+    """Encode a nil (NULL, None) item."""
+    return _encode_length_and_type(None, None)
+
+def _encode_item(item):
+    """Encode each item depending on its type"""
+    if item == None:
+        return (_pack_nil())
+    elif type(item) == dict:
+        return (_pack_hash(item))
+    elif type(item) == list:
+        return (_pack_array(item))
+    else:
+        return (_pack_string(str(item)))
+
+def _encode_array(item):
+    """Encode an array, where each value is encoded recursively"""
+    ret = []
+    for i in item:
+        ret.append(_encode_item(i))
+    return (''.join(ret))
+
+def _encode_hash(item):
+    """Encode a hash, where each value is encoded recursively"""
+
+    ret = []
+    for key, value in item.items():
+        ret.append(_encode_tag(key))
+        ret.append(_encode_item(value))
+    return (''.join(ret))
+
+#
+# decode methods
+#
+
+def from_wire(data):
+    if len(data) < 5:
+        raise DecodeError("Data is too short to decode")
+    wire_version, data = data[0:4], data[4:]
+    wire_version = struct.unpack(">I", wire_version)[0]
+    if wire_version != PROTOCOL_VERSION:
+        raise DecodeError("Incorrect protocol version")
+    return _decode_hash(data)
+
+def _decode_tag(data):
+    if len(data) < 1:
+        raise DecodeError("Data underrun while decoding")
+    length = struct.unpack(">B", data[0])[0]
+    if len(data) - 1 < length:
+        raise DecodeError("Data underrun while decoding")
+    return [data[1:length + 1], data[length + 1:]]
+
+def _decode_item(data):
+    if len(data) < 1:
+        raise DecodeError("Data underrun while decoding")
+    type_and_length_format = struct.unpack(">B", data[0])[0]
+    item_type = type_and_length_format & _ITEM_MASK
+    length_format = type_and_length_format & _ITEM_LENGTH_MASK
+
+    if item_type == _ITEM_NULL:
+        data = data[1:]
+    else:
+        if length_format == _ITEM_LENGTH_8:
+            if len(data) - 1 < 1:
+                raise DecodeError("Data underrun while decoding")
+            length = struct.unpack(">B", data[1])[0]
+            data = data[2:]
+        elif length_format == _ITEM_LENGTH_16:
+            if len(data) - 1 < 2:
+                raise DecodeError("Data underrun while decoding")
+            length = struct.unpack(">H", data[1:3])[0]
+            data = data[3:]
+        elif length_format == _ITEM_LENGTH_32:
+            if len(data) - 1 < 4:
+                raise DecodeError("Data underrun while decoding")
+            length = struct.unpack(">I", data[1:5])[0]
+            data = data[5:]
+        if len(data) < length:
+            raise DecodeError("Data underrun while decoding")
+        item = data[0:length]
+        data = data[length:]
+
+    if item_type == _ITEM_DATA:
+        value = item
+    elif item_type == _ITEM_HASH:
+        value = _decode_hash(item)
+    elif item_type == _ITEM_LIST:
+        value = _decode_array(item)
+    elif item_type == _ITEM_NULL:
+        value = None
+    else:
+        raise DecodeError("Unknown item type in decode: %02x" % item_type)
+
+    return (value, data)
+
+def _decode_hash(data):
+    ret = {}
+    while len(data) > 0:
+        tag, data = _decode_tag(data)
+        value, data = _decode_item(data)
+        ret[tag] = value
+    return ret
+
+def _decode_array(data):
+    ret = []
+    while len(data) > 0:
+        value, data = _decode_item(data)
+        ret.append(value)
+    return ret
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()

+ 2 - 0
src/lib/cc/python/ISC/CC/__init__.py

@@ -0,0 +1,2 @@
+import Message
+from session import *

+ 130 - 0
src/lib/cc/python/ISC/CC/session.py

@@ -0,0 +1,130 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import sys
+import socket
+import struct
+
+import Message
+
+class ProtocolError(Exception): pass
+
+class Session:
+    def __init__(self):
+        self._socket = None
+        self._lname = None
+        self._recvbuffer = ""
+        self._recvlength = None
+        self._sendbuffer = ""
+        self._sequence = 1
+
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(tuple(['127.0.0.1', 9912]))
+
+        self.sendmsg({ "type": "getlname" })
+        msg = self.recvmsg(False)
+        self._lname = msg["lname"]
+        if not self._lname:
+            raise ProtocolError("Could not get local name")
+
+    @property
+    def lname(self):
+        return self._lname
+
+    def sendmsg(self, msg):
+        if type(msg) == dict:
+            msg = Message.to_wire(msg)
+        self._socket.setblocking(1)
+        self._socket.send(struct.pack("!I", len(msg)))
+        self._socket.send(msg)
+
+    def recvmsg(self, nonblock = True):
+        data = self._receive_full_buffer(nonblock)
+        if data:
+            return Message.from_wire(data)
+        return None
+
+    def _receive_full_buffer(self, nonblock):
+        if nonblock:
+            self._socket.setblocking(0)
+        else:
+            self._socket.setblocking(1)
+
+        if self._recvlength == None:
+            length = 4
+            length -= len(self._recvbuffer)
+            try:
+                data = self._socket.recv(length)
+            except:
+                return None
+            if not data: # server closed connection
+                return None
+
+            self._recvbuffer += data
+            if len(self._recvbuffer) < 4:
+                return None
+            self._recvlength = struct.unpack('>I', self._recvbuffer)[0]
+            self._recvbuffer = ""
+
+        length = self._recvlength - len(self._recvbuffer)
+        while (length > 0):
+            data = self._socket.recv(length)
+            self._recvbuffer += data
+            length -= len(data)
+        data = self._recvbuffer
+        self._recvbuffer = ""
+        self._recvlength = None
+        return (data)
+
+    def _next_sequence(self):
+        self._sequence += 1
+        return self._sequence
+
+    def group_subscribe(self, group, instance = "*", subtype = "normal"):
+        self.sendmsg({
+            "type": "subscribe",
+            "group": group,
+            "instance": instance,
+            "subtype": subtype,
+        })
+
+    def group_unsubscribe(self, group, instance = "*"):
+        self.sendmsg({
+            "type": "unsubscribe",
+            "group": group,
+            "instance": instance,
+        })
+
+    def group_sendmsg(self, msg, group, instance = "*", to = "*"):
+        self.sendmsg({
+            "type": "send",
+            "from": self._lname,
+            "to": to,
+            "group": group,
+            "instance": instance,
+            "seq": self._next_sequence(),
+            "msg": Message.to_wire(msg),
+        })
+
+    def group_recvmsg(self, nonblock = True):
+        msg = self.recvmsg(nonblock)
+        if msg == None:
+            return None
+        data = Message.from_wire(msg["msg"])
+        return (data, msg)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()

+ 1 - 0
src/lib/cc/python/ISC/Util/__init__.py

@@ -0,0 +1 @@
+from hexdump import *

+ 13 - 0
src/lib/cc/python/ISC/Util/hexdump.py

@@ -0,0 +1,13 @@
+import sys
+
+_FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])
+
+def hexdump(src, length=16):
+    result=[]
+    for i in xrange(0, len(src), length):
+        s = src[i:i+length]
+        hexa = ' '.join(["%02X" % ord(x) for x in s])
+        printable = s.translate(_FILTER)
+        item = "%08X   %-*s   %s\n" % (i, length * 3, hexa, printable)
+        result.append(item[0:34] + ' ' + item[34:])
+    print ''.join(result)

+ 2 - 0
src/lib/cc/python/ISC/__init__.py

@@ -0,0 +1,2 @@
+import CC
+import Util

+ 22 - 0
src/lib/cc/python/test.py

@@ -0,0 +1,22 @@
+import ISC
+
+ss = { "list": [ 1, 2, 3 ],
+       "hash": { "hash1": 1, "hash2": 2 },
+       "none": None,
+       "string": "samplestring" }
+
+s = ISC.CC.Message.to_wire(ss)
+ISC.Util.hexdump(s)
+
+print ISC.CC.Message.from_wire(s)
+
+tcp = ISC.CC.Session()
+print tcp.lname
+
+tcp.group_subscribe("test")
+
+counter = 0
+while counter < 10000:
+    tcp.group_sendmsg({ "counter": counter }, "test", "foo")
+    routing, data = tcp.group_recvmsg(False)
+    counter += 1

+ 60 - 0
src/lib/cc/ruby/lib/cc.rb

@@ -0,0 +1,60 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+unless respond_to?('relative_feature') # nodoc
+  def require_relative(relative_feature)
+    c = caller.first
+    fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/)
+    file = $`
+    if /\A\((.*)\)/ =~ file # eval, etc.
+      raise LoadError, "require_relative is called in #{$1}"
+    end
+    absolute = File.expand_path(relative_feature, File.dirname(file))
+    require absolute
+  end
+end
+
+class CC
+  def self.set_utf8(str) #nodoc
+    if str.respond_to?('force_encoding')
+      str.force_encoding(Encoding::UTF_8)
+    end
+  end
+
+  def self.set_binary(str) #nodoc
+    if str.respond_to?('force_encoding')
+      str.force_encoding(Encoding::BINARY)
+    end
+  end
+end
+
+require_relative 'cc/message'
+require_relative 'cc/session'
+
+if $0 == __FILE__
+  cc = CC::Session.new
+
+  puts "Our local name: #{cc.lname}"
+
+  cc.group_subscribe("test")
+
+  counter = 0
+
+  while counter < 10000 do
+    cc.group_sendmsg({ :counter => counter }, "test", "foo")
+    routing, data = cc.group_recvmsg(false)
+    counter += 1
+  end
+end

+ 265 - 0
src/lib/cc/ruby/lib/cc/message.rb

@@ -0,0 +1,265 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+class CC
+  class DecodeError < Exception ; end
+end
+
+class CC
+class Message
+  PROTOCOL_VERSION = 0x536b616e
+
+  ITEM_DATA = 0x01
+  ITEM_HASH = 0x02
+  ITEM_LIST = 0x03
+  ITEM_NULL = 0x04
+  ITEM_MASK = 0x0f
+
+  ITEM_LENGTH_32   = 0x00
+  ITEM_LENGTH_16   = 0x10
+  ITEM_LENGTH_8    = 0x20
+  ITEM_LENGTH_MASK = 0x30
+
+  def initialize(msg = nil)
+    @data = [PROTOCOL_VERSION].pack("N")
+    if msg.is_a?(Hash)
+      @data += CC::Message::encode_hash(msg)
+    elsif msg.is_a?(String)
+      @data = msg
+    else
+      raise ArgumentError, "initializer is not a Hash or String"
+    end
+  end
+
+  def to_wire
+    CC::set_binary(@data)
+    @data
+  end
+
+  #
+  # Encode a message.  The item passed in should be a hash, and can contain
+  # any number of items of any type afterwards.  All keys in the hash must
+  # be of type String or Symbol, and the values may be of any type.  If
+  # the value is a Hash or Array, it will be encoded as a message type
+  # HASH or LIST.  If it is nil, it will be encoded as NULL, and if it is
+  # any other type, its to_s method will be called on it and it will be
+  # encoded as a DATA item.
+  #
+  def self.to_wire(msg)
+    encoded = [PROTOCOL_VERSION].pack("N")
+    encoded += encode_hash(msg)
+    CC::set_binary(encoded)
+    encoded
+  end
+
+  #
+  # Decode a wire format message.
+  #
+  def self.from_wire(msg)
+    if msg.length < 4
+      raise CC::DecodeError, "Data is too short to decode"
+    end
+    CC::set_binary(msg)
+    version, msg = msg.unpack("N a*")
+    unless version == PROTOCOL_VERSION
+      raise CC::DecodeError, "Incorrect protocol version"
+    end
+    decode_hash(msg)
+  end
+
+  private
+  # encode a simple string with a length prefix
+  def self.encode_tag(tag)
+    tag = tag.to_s
+    [tag.length, tag].pack("C/a*")
+  end
+
+  def self.encode_length_and_type(data, type)
+    if data.nil?
+      [ITEM_NULL].pack("C")
+    else
+      len = data.length
+      if len < 0x00000100
+        [type | ITEM_LENGTH_8, len, data].pack("C C/a*")
+      elsif len < 0x00010000
+        [type | ITEM_LENGTH_16, len, data].pack("C n/a*")
+      else
+        [type | ITEM_LENGTH_32, len, data].pack("C N/a*")
+      end
+    end
+  end
+
+  # pack a string, including its type and length.
+  def self.pack_string(str)
+    encode_length_and_type(str.to_s, ITEM_DATA)
+  end
+
+  def self.pack_array(arr)
+    encode_length_and_type(encode_array(arr), ITEM_LIST)
+  end
+
+  def self.pack_hash(hash)
+    encode_length_and_type(encode_hash(hash), ITEM_HASH)
+  end
+
+  def self.encode_string(str)
+    str
+  end
+
+  def self.pack_nil
+    encode_length_and_type(nil, ITEM_NULL)
+  end
+
+  def self.encode_item(item)
+    case item
+      when nil
+      ret = pack_nil
+      when Hash
+      ret = pack_hash(item)
+      when Array
+      ret = pack_array(item)
+    else
+      ret = pack_string(item.to_s)
+    end
+
+    ret
+  end
+
+  def self.encode_hash(msg)
+    unless msg.is_a?Hash
+      raise ArgumentError, "Should be a hash"
+    end
+    buffer = ""
+    msg.each do |key, value|
+      buffer += encode_tag(key)
+      buffer += encode_item(value)
+    end
+    buffer
+  end
+
+  def self.encode_array(msg)
+    unless msg.is_a?Array
+      raise ArgumentError, "Should be an array"
+    end
+    buffer = ""
+    msg.each do |value|
+      buffer += encode_item(value)
+    end
+    buffer
+  end
+
+  def self.decode_tag(str)
+    if str.length < 1
+      raise CC::DecodeError, "Data underrun while decoding"
+    end
+    length = str.unpack("C")[0]
+    if str.length - 1 < length
+      raise CC::DecodeError, "Data underrun while decoding"
+    end
+    tag, remainder = str.unpack("x a#{length} a*")
+    CC::set_utf8(tag)
+    [ tag, remainder ]
+  end
+
+  def self.decode_item(msg)
+    if msg.length < 1
+      raise CC::DecodeError, "Data underrun while decoding"
+    end
+    type_and_length_format = msg.unpack("C")[0]
+    type = type_and_length_format & ITEM_MASK
+    length_format = type_and_length_format & ITEM_LENGTH_MASK
+
+    if type == ITEM_NULL
+      msg = msg.unpack("x a*")[0]
+    else
+      if length_format == ITEM_LENGTH_8
+        if msg.length - 1 < 1
+          raise CC::DecodeError, "Data underrun while decoding"
+        end
+        length, msg = msg.unpack("x C a*")
+      elsif length_format == ITEM_LENGTH_16
+        if msg.length - 1 < 2
+          raise CC::DecodeError, "Data underrun while decoding"
+        end
+        length, msg = msg.unpack("x n a*")
+      elsif length_format == ITEM_LENGTH_32
+        if msg.length - 1 < 4
+          raise CC::DecodeError, "Data underrun while decoding"
+        end
+        length, msg = msg.unpack("x N a*")
+      end
+      if msg.length < length
+        raise CC::DecodeError, "Data underrun while decoding"
+      end
+      item, msg = msg.unpack("a#{length} a*")
+    end
+
+    # unpack item based on type
+    case type
+      when ITEM_DATA
+      value = item
+      when ITEM_HASH
+      value = decode_hash(item)
+      when ITEM_LIST
+      value = decode_array(item)
+      when ITEM_NULL
+      value = nil
+    else
+      raise CC::DecodeError, "Unknown item type in decode: #{type}"
+    end
+
+    [value, msg]
+  end
+
+  def self.decode_hash(msg)
+    ret = {}
+    while msg.length > 0
+      tag, msg = decode_tag(msg)
+      value, msg = decode_item(msg)
+      ret[tag] = value
+    end
+
+    ret
+  end
+
+  def self.decode_array(msg)
+    ret = []
+    while msg.length > 0
+      value, msg = decode_item(msg)
+      ret << value
+    end
+
+    ret
+  end
+
+end # class Message
+end # class CC
+
+if $0 == __FILE__
+  target = {
+    "from" => "sender@host",
+    "to" => "recipient@host",
+    "seq" => 1234,
+    "data" => {
+      "list" => [ 1, 2, nil, "this" ],
+      "description" => "Fun for all",
+    },
+  }
+
+  wire = CC::Message.to_wire(target)
+  puts wire.inspect
+
+  puts CC::Message.from_wire(wire).inspect
+end

+ 198 - 0
src/lib/cc/ruby/lib/cc/session.rb

@@ -0,0 +1,198 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+require 'socket'
+
+class CC
+class ProtocolError < Exception ; end
+end
+
+class CC
+class Session
+  attr_reader :socket
+  attr_reader :lname
+
+  #
+  # :host => host to connect to (defaults to "127.0.0.1")
+  # :port => port to connect to (defaults to 9913)
+  #
+  def initialize(args = {})
+    @socket = nil      # TCP socket.
+    @lname = nil       # local name, or nil if not connected.
+    @recvbuffer = ""   # data buffer for partial reads.
+    @recvlength = nil  # if non-nil, we have a length to fill buffer to.
+    @sendbuffer = ""   # pending output data.
+    @sequence = "a"    # per message sequence id, always unique
+
+    options = {
+      :host => "127.0.0.1",
+      :port => 9912
+    }.merge(args)
+
+    @socket = TCPSocket.new(options[:host], options[:port])
+
+    #
+    # Get our local name.
+    #
+    sendmsg({ :type => :getlname })
+    msg = recvmsg(false)
+    @lname = msg["lname"]
+    if @lname.nil?
+      raise CC::ProtocolError, "Could not get local name"
+    end
+    CC::set_utf8(@lname)
+  end
+
+  #
+  # Send a message to the controller.  The item to send can either be a
+  # CC::Message object, or a Hash.  If a Hash, it will be internally
+  # converted to a CC::Message before transmitting.
+  #
+  # A return value of true means the entire message was not
+  # transmitted, and a call to send_pending will have to be
+  # made to send remaining data.  This should only happen when
+  # the socket is in non-blocking mode.
+  #
+  def sendmsg(msg)
+    if msg.is_a?(Hash)
+      msg = CC::Message.new(msg)
+    end
+
+    unless msg.is_a?(CC::Message)
+      raise ArgumentError, "msg is not a CC::Message or a Hash"
+    end
+
+    wire = msg.to_wire
+    @sendbuffer << [wire.length].pack("N")
+    @sendbuffer << wire
+
+    send_pending
+  end
+
+  #
+  # Send as much data as we can.  
+  def send_pending
+    return false if @sendbuffer.length == 0
+    sent = @socket.send(@sendbuffer, 0)
+    @sendbuffer = @sendbuffer[sent .. -1]
+    @sendbuffer.length == 0 ? true : false
+  end
+
+  def recvmsg(nonblock = true)
+    data = receive_full_buffer(nonblock)
+    if data
+      CC::Message::from_wire(data)
+    else
+      nil
+    end
+  end
+
+  def group_subscribe(group, instance = "*", subtype = "normal")
+    sendmsg({ :type => "subscribe",
+              :group => group,
+              :instance => instance,
+              :subtype => subtype,
+            })
+  end
+
+  def group_unsubscribe(group, instance = "*")
+    sendmsg({ :type => "unsubscribe",
+              :group => group,
+              :instance => instance,
+            })
+  end
+
+  def group_sendmsg(msg, group, instance = "*", to = "*")
+    sendmsg({ :type => "send",
+              :from => @lname,
+              :to => to,
+              :group => group,
+              :instance => instance,
+              :seq => next_sequence,
+              :msg => CC::Message.to_wire(msg),
+            })
+  end
+
+  def group_recvmsg(nonblock = true)
+    msg = recvmsg(nonblock)
+    return nil if msg.nil?
+    data = CC::Message::from_wire(msg["msg"])
+    msg.delete("msg")
+    return [data, msg]
+  end
+
+  private
+
+  def next_sequence
+    @sequence.next!
+  end
+
+  #
+  # A rather tricky function.  If we have something waiting in our buffer,
+  # and it will satisfy the current request, we will read it all in.  If
+  # not, we will read only what we need to satisfy a single message.
+  #
+  def receive_full_buffer(nonblock)
+    # read the length prefix if we need it still.
+    if @recvlength.nil?
+      length = 4
+      length -= @recvbuffer.length
+      data = nil
+      begin
+        if nonblock
+          data = @socket.recv_nonblock(length)
+        else
+          data = @socket.recv(length)
+        end
+        rescue Errno::EINPROGRESS
+        rescue Errno::EAGAIN
+      end
+      return nil if data == nil
+      @recvbuffer += data
+      return nil if @recvbuffer.length < 4
+      @recvlength = @recvbuffer.unpack('N')[0]
+      @recvbuffer = ""
+      CC::set_binary(@recvbuffer)
+    end
+
+    #
+    # we have a length target.  Loop reading data until we get enough to
+    # fill our buffer.
+    #
+    length = @recvlength - @recvbuffer.length
+    while (length > 0) do
+      data = nil
+      begin
+        if nonblock
+          data = @socket.recv_nonblock(length)
+        else
+          data = @socket.recv(length)
+        end
+        rescue Errno::EINPROGRESS
+        rescue Errno::EAGAIN
+      end
+      return nil if data == 0 # blocking I/O
+      @recvbuffer += data
+      length -= data.length
+    end
+
+    data = @recvbuffer
+    @recvbuffer = ""
+    @recvlength = nil
+    data
+  end
+
+end # class Session
+end # class CC