public void TestAnnotations() { var annotations = new Dictionary<string,byte[]>(); annotations["TEST"]=new byte[]{10,20,30,40,50}; var msg = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, this.serializer_id, 0, 0, annotations); byte[] data = msg.to_bytes(); int annotations_size = 4+2+20 + 4+2+5; Assert.AreEqual(Message.HEADER_SIZE + 5 + annotations_size, data.Length); Assert.AreEqual(annotations_size, msg.annotations_size); Assert.AreEqual(2, msg.annotations.Count); Assert.AreEqual(new byte[]{10,20,30,40,50}, msg.annotations["TEST"]); byte[] mac = pyrohmac(new byte[]{1,2,3,4,5}, annotations); Assert.AreEqual(mac, msg.annotations["HMAC"]); }
public void TestMessage() { new Message(99, new byte[0], this.serializer_id, 0, 0, null); // doesn't check msg type here Assert.Throws(typeof(PyroException), ()=>Message.from_header(Encoding.ASCII.GetBytes("FOOBAR"))); var msg = new Message(Message.MSG_CONNECT, Encoding.ASCII.GetBytes("hello"), this.serializer_id, 0, 0, null); Assert.AreEqual(Message.MSG_CONNECT, msg.type); Assert.AreEqual(5, msg.data_size); Assert.AreEqual(new byte[]{(byte)'h',(byte)'e',(byte)'l',(byte)'l',(byte)'o'}, msg.data); Assert.AreEqual(4+2+20, msg.annotations_size); byte[] mac = pyrohmac(Encoding.ASCII.GetBytes("hello"), msg.annotations); var expected = new Dictionary<string, byte[]>(); expected["HMAC"] = mac; CollectionAssert.AreEqual(expected, msg.annotations); byte[] hdr = msg.to_bytes().Take(Message.HEADER_SIZE).ToArray(); msg = Message.from_header(hdr); Assert.AreEqual(Message.MSG_CONNECT, msg.type); Assert.AreEqual(4+2+20, msg.annotations_size); Assert.AreEqual(5, msg.data_size); hdr = new Message(Message.MSG_RESULT, new byte[0], this.serializer_id, 0, 0, null).to_bytes().Take(Message.HEADER_SIZE).ToArray(); msg = Message.from_header(hdr); Assert.AreEqual(Message.MSG_RESULT, msg.type); Assert.AreEqual(4+2+20, msg.annotations_size); Assert.AreEqual(0, msg.data_size); hdr = new Message(Message.MSG_RESULT, Encoding.ASCII.GetBytes("hello"), 12345, 60006, 30003, null).to_bytes().Take(Message.HEADER_SIZE).ToArray(); msg = Message.from_header(hdr); Assert.AreEqual(Message.MSG_RESULT, msg.type); Assert.AreEqual(60006, msg.flags); Assert.AreEqual(5, msg.data_size); Assert.AreEqual(12345, msg.serializer_id); Assert.AreEqual(30003, msg.seq); byte[] data = new Message(255, new byte[0], this.serializer_id, 0, 255, null).to_bytes(); Assert.AreEqual(50, data.Length); data = new Message(1, new byte[0], this.serializer_id, 0, 255, null).to_bytes(); Assert.AreEqual(50, data.Length); data = new Message(1, new byte[0], this.serializer_id, 253, 254, null).to_bytes(); Assert.AreEqual(50, data.Length); // compression is a job of the code supplying the data, so the messagefactory should leave it untouched data = new byte[1000]; byte[] msg_bytes1 = new Message(Message.MSG_INVOKE, data, this.serializer_id, 0, 0, null).to_bytes(); byte[] msg_bytes2 = new Message(Message.MSG_INVOKE, data, this.serializer_id, Message.FLAGS_COMPRESSED, 0, null).to_bytes(); Assert.AreEqual(msg_bytes1.Length, msg_bytes2.Length); }
public void testAnnotationsIdLength4() { try { var anno = new Dictionary<string, byte[]>(); anno["TOOLONG"] = new byte[]{10,20,30}; var msg = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, this.serializer_id, 0, 0, anno); byte[]data = msg.to_bytes(); Assert.Fail("should fail, too long"); } catch(ArgumentException) { //ok } try { var anno = new Dictionary<string, byte[]>(); anno["QQ"] = new byte[]{10,20,30}; var msg = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, this.serializer_id, 0, 0, anno); byte[] data = msg.to_bytes(); Assert.Fail("should fail, too short"); } catch (ArgumentException) { //ok } }
public void TestAnnotations() { byte[] hmac=Encoding.UTF8.GetBytes("secret"); var annotations = new Dictionary<string,byte[]>(); annotations["TES1"]=new byte[]{10,20,30,40,50}; annotations["TES2"]=new byte[]{20,30,40,50,60}; annotations["TES3"]=new byte[]{30,40,50,60,70}; annotations["TES4"]=new byte[]{40,50,60,70,80}; var msg = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, this.serializer_id, 0, 0, annotations, hmac); byte[] data = msg.to_bytes(); int annotations_size = 4+2+20 + (4+2+5)*4; Assert.AreEqual(Message.HEADER_SIZE + 5 + annotations_size, data.Length); Assert.AreEqual(annotations_size, msg.annotations_size); Assert.AreEqual(5, msg.annotations.Count); Assert.AreEqual(new byte[]{10,20,30,40,50}, msg.annotations["TES1"]); Assert.AreEqual(new byte[]{20,30,40,50,60}, msg.annotations["TES2"]); Assert.AreEqual(new byte[]{30,40,50,60,70}, msg.annotations["TES3"]); Assert.AreEqual(new byte[]{40,50,60,70,80}, msg.annotations["TES4"]); byte[] mac = msg.hmac(hmac); Assert.AreEqual(mac, msg.annotations["HMAC"]); annotations = new Dictionary<string,byte[]>(); annotations["TES4"]=new byte[]{40,50,60,70,80}; annotations["TES3"]=new byte[]{30,40,50,60,70}; annotations["TES2"]=new byte[]{20,30,40,50,60}; annotations["TES1"]=new byte[]{10,20,30,40,50}; var msg2 = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, this.serializer_id, 0, 0, annotations, hmac); Assert.AreEqual(msg.hmac(hmac), msg2.hmac(hmac)); annotations = new Dictionary<string,byte[]>(); annotations["TES4"]=new byte[]{40,50,60,70,80}; var msg3 = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, this.serializer_id, 0, 0, annotations, hmac); Assert.AreNotEqual(msg.hmac(hmac), msg3.hmac(hmac)); }
public void testRecvNoAnnotations() { var msg = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, 42, 0, 0, null); var c = new ConnectionMock(); c.send(msg.to_bytes()); msg = Message.recv(c, null); Assert.AreEqual(0, c.RemainingLength); Assert.AreEqual(5, msg.data_size); Assert.AreEqual(new byte[]{1,2,3,4,5}, msg.data); Assert.AreEqual(0, msg.annotations_size); Assert.AreEqual(0, msg.annotations.Count); }
public void testRecvAnnotations() { var annotations = new Dictionary<string, byte[]>(); annotations["TEST"] = new byte[]{10, 20,30,40,50}; var msg = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, this.serializer_id, 0, 0, annotations); var c = new ConnectionMock(); c.send(msg.to_bytes()); msg = Message.recv(c, null); Assert.AreEqual(0, c.RemainingLength); Assert.AreEqual(5, msg.data_size); Assert.AreEqual(new byte[]{1,2,3,4,5}, msg.data); Assert.AreEqual(new byte[]{10,20,30,40,50}, msg.annotations["TEST"]); Assert.IsTrue(msg.annotations.ContainsKey("HMAC")); }
public void testProtocolVersion() { byte[] msg = new Message(Message.MSG_RESULT, new byte[0], this.serializer_id, 0, 1, null).to_bytes().Take(Message.HEADER_SIZE).ToArray(); msg[4] = 99; // screw up protocol version in message header Message.from_header(msg); }
public void testRecvAnnotations() { var annotations = new Dictionary<string, byte[]>(); annotations["TEST"] = new byte[]{10, 20,30,40,50}; var msg = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, this.serializer_id, 0, 0, annotations, Encoding.UTF8.GetBytes("secret")); var ms = new MemoryStream(msg.to_bytes()); msg = Message.recv(ms, null, Encoding.UTF8.GetBytes("secret")); Assert.AreEqual(-1, ms.ReadByte()); Assert.AreEqual(5, msg.data_size); Assert.AreEqual(new byte[]{1,2,3,4,5}, msg.data); Assert.AreEqual(new byte[]{10,20,30,40,50}, msg.annotations["TEST"]); Assert.IsTrue(msg.annotations.ContainsKey("HMAC")); }
/// <summary> /// Decompress the data bytes in the given message (in place). /// </summary> private void _decompressMessageData(Message msg) { if((msg.flags & Message.FLAGS_COMPRESSED) == 0) { throw new ArgumentException("message data is not compressed"); } using(MemoryStream compressed=new MemoryStream(msg.data, 2, msg.data.Length-2, false)) { using(DeflateStream decompresser=new DeflateStream(compressed, CompressionMode.Decompress)) { MemoryStream bos = new MemoryStream(msg.data.Length); byte[] buffer = new byte[4096]; int numRead; while ((numRead = decompresser.Read(buffer, 0, buffer.Length)) != 0) { bos.Write(buffer, 0, numRead); } msg.data=bos.ToArray(); msg.flags ^= Message.FLAGS_COMPRESSED; } } }
/// <summary> /// Perform the Pyro protocol connection handshake with the Pyro daemon. /// </summary> protected void _handshake() { var ser = PyroSerializer.GetFor(Config.SERIALIZER); var handshakedata = new Dictionary<string, Object>(); handshakedata["handshake"] = pyroHandshake; if(Config.METADATA) handshakedata["object"] = objectid; byte[] data = ser.serializeData(handshakedata); ushort flags = Config.METADATA? Message.FLAGS_META_ON_CONNECT : (ushort)0; var msg = new Message(Message.MSG_CONNECT, data, ser.serializer_id, flags, sequenceNr, annotations(), pyroHmacKey); IOUtil.send(sock_stream, msg.to_bytes()); if(Config.MSG_TRACE_DIR!=null) { Message.TraceMessageSend(sequenceNr, msg.get_header_bytes(), msg.get_annotations_bytes(), msg.data); } // process handshake response msg = Message.recv(sock_stream, new ushort[]{Message.MSG_CONNECTOK, Message.MSG_CONNECTFAIL}, pyroHmacKey); responseAnnotations(msg.annotations, msg.type); object handshake_response = "?"; if(msg.data!=null) { if((msg.flags & Message.FLAGS_COMPRESSED) != 0) { _decompressMessageData(msg); } handshake_response = ser.deserializeData(msg.data); } if(msg.type==Message.MSG_CONNECTOK) { if((msg.flags & Message.FLAGS_META_ON_CONNECT) != 0) { var response_dict = (Hashtable)handshake_response; _processMetadata(response_dict["meta"] as Hashtable); handshake_response = response_dict["handshake"]; try { validateHandshake(handshake_response); } catch (Exception) { close(); throw; } } } else if (msg.type==Message.MSG_CONNECTFAIL) { close(); throw new PyroException("connection rejected, reason: "+handshake_response); } else { close(); throw new PyroException(string.Format("connect: invalid msg type {0} received", msg.type)); } }
public void testProtocolVersionsNotSupported2() { byte[] msg = new Message(Message.MSG_RESULT, new byte[0], this.serializer_id, 0, 1, null).to_bytes().Take(Message.HEADER_SIZE).ToArray(); msg[4] = 0; msg[5] = 48; Message.from_header(msg); }
/** * Internal call method to actually perform the Pyro method call and process the result. */ private object call(string method, ushort flags, params object[] parameters) { lock(this) { connect(); unchecked { sequenceNr++; // unchecked so this ushort wraps around 0-65535 instead of raising an OverflowException } } if (parameters == null) parameters = new object[] {}; PyroSerializer ser = PyroSerializer.GetFor(Config.SERIALIZER); byte[] pickle = ser.serializeCall(objectid, method, parameters, new Dictionary<string,object>(0)); var msg = new Message(Message.MSG_INVOKE, pickle, ser.serializer_id, flags, sequenceNr, null); Message resultmsg; lock (this.sock) { IOUtil.send(sock_stream, msg.to_bytes()); if(Config.MSG_TRACE_DIR!=null) { Message.TraceMessageSend(sequenceNr, msg.get_header_bytes(), msg.get_annotations_bytes(), msg.data); } pickle = null; if ((flags & Message.FLAGS_ONEWAY) != 0) return null; resultmsg = Message.recv(sock_stream, new ushort[]{Message.MSG_RESULT}); } if (resultmsg.seq != sequenceNr) { throw new PyroException("result msg out of sync"); } if ((resultmsg.flags & Message.FLAGS_COMPRESSED) != 0) { // we need to skip the first 2 bytes in the buffer due to a tiny mismatch between zlib-written // data and the deflate data bytes that .net expects. // See http://www.chiramattel.com/george/blog/2007/09/09/deflatestream-block-length-does-not-match.html using(MemoryStream compressed=new MemoryStream(resultmsg.data, 2, resultmsg.data.Length-2, false)) { using(DeflateStream decompresser=new DeflateStream(compressed, CompressionMode.Decompress)) { MemoryStream bos = new MemoryStream(resultmsg.data.Length); byte[] buffer = new byte[4096]; int numRead; while ((numRead = decompresser.Read(buffer, 0, buffer.Length)) != 0) { bos.Write(buffer, 0, numRead); } resultmsg.data=bos.ToArray(); } } } if ((resultmsg.flags & Message.FLAGS_EXCEPTION) != 0) { Exception rx = (Exception) ser.deserializeData(resultmsg.data); if (rx is PyroException) { throw (PyroException) rx; } else { PyroException px = new PyroException("remote exception occurred", rx); PropertyInfo remotetbProperty=rx.GetType().GetProperty("_pyroTraceback"); if(remotetbProperty!=null) { string remotetb=(string)remotetbProperty.GetValue(rx,null); px._pyroTraceback=remotetb; } throw px; } } return ser.deserializeData(resultmsg.data); }
public void testChecksum() { var msg = new Message(Message.MSG_RESULT, new byte[]{1,2,3,4}, 42, 0, 1, null, null); byte[] data = msg.to_bytes(); // corrupt the checksum bytes data[Message.HEADER_SIZE-2] = 0; data[Message.HEADER_SIZE-1] = 0; Stream ms = new MemoryStream(data); try { Message.recv(ms, null, null); Assert.Fail("crash expected"); } catch(PyroException x) { Assert.IsTrue(x.Message.Contains("checksum")); } }
public void testRecvNoAnnotations() { var msg = new Message(Message.MSG_CONNECT, new byte[]{1,2,3,4,5}, 42, 0, 0, null, null); byte[] data = msg.to_bytes(); var ms = new MemoryStream(data); msg = Message.recv(ms, null, null); Assert.AreEqual(-1, ms.ReadByte()); Assert.AreEqual(5, msg.data_size); Assert.AreEqual(new byte[]{1,2,3,4,5}, msg.data); Assert.AreEqual(0, msg.annotations_size); Assert.AreEqual(0, msg.annotations.Count); }
public void testChecksum() { var msg = new Message(Message.MSG_RESULT, new byte[]{1,2,3,4}, 42, 0, 1, null); var c = new ConnectionMock(); c.send(msg.to_bytes()); // corrupt the checksum bytes byte[] data = c.ReceivedData; data[Message.HEADER_SIZE-2] = 0; data[Message.HEADER_SIZE-1] = 0; c = new ConnectionMock(data); try { Message.recv(c, null); Assert.Fail("crash expected"); } catch(PyroException x) { Assert.IsTrue(x.Message.Contains("checksum")); } }
/// <summary> /// Parses a message header. Does not yet process the annotations chunks and message data. /// </summary> public static Message from_header(byte[] header) { if(header==null || header.Length!=HEADER_SIZE) throw new PyroException("header data size mismatch"); if(header[0]!='P'||header[1]!='Y'||header[2]!='R'||header[3]!='O') throw new PyroException("invalid message"); int version = (header[4] << 8)|header[5]; if(version!=Config.PROTOCOL_VERSION) throw new PyroException("invalid protocol version: "+version); int msg_type = (header[6] << 8)|header[7]; int flags = (header[8] << 8)|header[9]; int seq = (header[10] << 8)|header[11]; int data_size=(((((header[12] <<8) | header[13]) <<8) | header[14]) <<8) | header[15]; int serializer_id = (header[16] << 8)|header[17]; int annotations_size = (header[18]<<8)|header[19]; // byte 20 and 21 are reserved. int checksum = (header[22]<<8)|header[23]; if(checksum!=((msg_type+version+data_size+annotations_size+flags+serializer_id+seq+CHECKSUM_MAGIC)&0xffff)) throw new PyroException("header checksum mismatch"); var msg = new Message((ushort)msg_type, (ushort)serializer_id, (ushort)flags, (ushort)seq); msg.data_size = data_size; msg.annotations_size = (ushort)annotations_size; return msg; }
public void testHmac() { byte[] hk=Config.HMAC_KEY; Stream c; byte[] data; try { Config.HMAC_KEY = Encoding.ASCII.GetBytes("test key"); data = new Message(Message.MSG_RESULT, new byte[]{1,2,3,4,5}, 42, 0, 1, null).to_bytes(); c = new ConnectionMock(data); } finally { Config.HMAC_KEY = hk; } // test checking of different hmacs try { Message.recv(c, null); Assert.Fail("crash expected"); } catch(PyroException x) { Assert.IsTrue(x.Message.Contains("hmac")); } c = new ConnectionMock(data); // test that it works again when resetting the key try { hk = Config.HMAC_KEY; Config.HMAC_KEY = Encoding.ASCII.GetBytes("test key"); Message.recv(c, null); } finally { Config.HMAC_KEY = hk; } c = new ConnectionMock(data); // test that it doesn't work when no key is set try { hk = Config.HMAC_KEY; Config.HMAC_KEY = null; Message.recv(c, null); Assert.Fail("crash expected"); } catch(PyroException x) { Assert.IsTrue(x.Message.Contains("hmac key config")); } finally { Config.HMAC_KEY = hk; } }
/// <summary> /// Internal call method to actually perform the Pyro method call and process the result. /// </summary> private object internal_call(string method, string actual_objectId, ushort flags, bool checkMethodName, params object[] parameters) { actual_objectId = actual_objectId ?? this.objectid; lock(this) { connect(); unchecked { sequenceNr++; // unchecked so this ushort wraps around 0-65535 instead of raising an OverflowException } } if(pyroAttrs.Contains(method)) { throw new PyroException("cannot call an attribute"); } if(pyroOneway.Contains(method)) { flags |= Message.FLAGS_ONEWAY; } if(checkMethodName && Config.METADATA && !pyroMethods.Contains(method)) { throw new PyroException(string.Format("remote object '{0}' has no exposed attribute or method '{1}'", actual_objectId, method)); } if (parameters == null) parameters = new object[] {}; PyroSerializer ser = PyroSerializer.GetFor(Config.SERIALIZER); byte[] pickle = ser.serializeCall(actual_objectId, method, parameters, new Dictionary<string,object>(0)); var msg = new Message(Message.MSG_INVOKE, pickle, ser.serializer_id, flags, sequenceNr, this.annotations(), pyroHmacKey); Message resultmsg; lock (this.sock) { IOUtil.send(sock_stream, msg.to_bytes()); if(Config.MSG_TRACE_DIR!=null) { Message.TraceMessageSend(sequenceNr, msg.get_header_bytes(), msg.get_annotations_bytes(), msg.data); } pickle = null; if ((flags & Message.FLAGS_ONEWAY) != 0) return null; resultmsg = Message.recv(sock_stream, new ushort[]{Message.MSG_RESULT}, pyroHmacKey); } if (resultmsg.seq != sequenceNr) { throw new PyroException("result msg out of sync"); } responseAnnotations(resultmsg.annotations, resultmsg.type); if ((resultmsg.flags & Message.FLAGS_COMPRESSED) != 0) { _decompressMessageData(resultmsg); } if ((resultmsg.flags & Message.FLAGS_EXCEPTION) != 0) { Exception rx = (Exception) ser.deserializeData(resultmsg.data); if (rx is PyroException) { throw (PyroException) rx; } else { PyroException px = new PyroException("remote exception occurred", rx); PropertyInfo remotetbProperty=rx.GetType().GetProperty("_pyroTraceback"); if(remotetbProperty!=null) { string remotetb=(string)remotetbProperty.GetValue(rx,null); px._pyroTraceback=remotetb; } throw px; } } return ser.deserializeData(resultmsg.data); }
public void testMessageHeaderDatasize() { var msg = new Message(Message.MSG_RESULT, Encoding.ASCII.GetBytes("hello"), 12345, 60006, 30003, null); msg.data_size = 0x12345678; // hack it to a large value to see if it comes back ok byte[] hdr = msg.to_bytes().Take(Message.HEADER_SIZE).ToArray(); msg = Message.from_header(hdr); Assert.AreEqual(Message.MSG_RESULT, msg.type); Assert.AreEqual(60006, msg.flags); Assert.AreEqual(0x12345678, msg.data_size); Assert.AreEqual(12345, msg.serializer_id); Assert.AreEqual(30003, msg.seq); }
/** * Receive a message from the connection. If you set requiredMsgType to the required * message type id instead of zero, it will check the incoming message type and * raise a PyroException if they don't match. */ public static Message getMessage(Stream connection, int requiredMsgType) { byte[] headerdata=IOUtil.recv(connection, HEADER_SIZE); MessageHeader header=parseMessageHeader(headerdata); if(requiredMsgType!=0 && header.type!=requiredMsgType) { throw new PyroException("invalid msg type received: "+header.type); } byte[] data=IOUtil.recv(connection, header.datasize); if(Config.MSG_TRACE_DIR!=null) { TraceMessageRecv(header.sequence, headerdata, data); } if(((header.flags&FLAGS_HMAC) != 0) && (Config.HMAC_KEY!=null)) { if(header.hmac!=makeHMAC(data)) { throw new PyroException("message hmac mismatch"); } } else if(((header.flags&FLAGS_HMAC) != 0) != (Config.HMAC_KEY!=null)) { throw new PyroException("hmac key config not symmetric"); } Message msg=new Message(); msg.type=header.type; msg.flags=header.flags; msg.sequence=header.sequence; msg.data=data; return msg; }
public void testHmac() { Stream c; byte[] data; data = new Message(Message.MSG_RESULT, new byte[]{1,2,3,4,5}, 42, 0, 1, null, Encoding.UTF8.GetBytes("secret")).to_bytes(); c = new MemoryStream(data); // test checking of different hmacs try { Message.recv(c, null, Encoding.UTF8.GetBytes("wrong")); Assert.Fail("crash expected"); } catch(PyroException x) { Assert.IsTrue(x.Message.Contains("hmac")); } c = new MemoryStream(data); // test that it works again when resetting the key Message.recv(c, null, Encoding.UTF8.GetBytes("secret")); c = new MemoryStream(data); // test that it doesn't work when no key is set try { Message.recv(c, null, null); Assert.Fail("crash expected"); } catch(PyroException x) { Assert.IsTrue(x.Message.Contains("hmac key config")); } }