1 // Written in the D programming language. 2 3 /** 4 Module for decoding raw bytes to messages of the 9P / Styx protocol. 5 6 Copyright: LightHouse Software, 2021 7 License: $(HTTP https://github.com/aquaratixc/ESL-License, Experimental Software License 1.0). 8 Authors: Oleg Bakharev, 9 Ilya Pertsev 10 */ 11 module styx2000.protomsg.decoder; 12 13 private { 14 import styx2000.lowlevel.endianness; 15 16 import styx2000.protomsg.typeconv; 17 } 18 19 public { 20 import styx2000.protoconst.messages; 21 22 import styx2000.protobj; 23 } 24 25 private { 26 /// Decode fields with using only types list 27 auto decodeMessageFields(E...)(ubyte[] bytes...) 28 { 29 E listOfTypes; 30 StyxObject[] msg; 31 32 uint vlsPosition; 33 34 foreach (e; listOfTypes) 35 { 36 auto messageField = new typeof(e); 37 messageField.unpack(bytes[vlsPosition..$]); 38 vlsPosition += cast(uint) (messageField.length); 39 msg ~= cast(StyxObject) messageField; 40 } 41 42 return msg; 43 } 44 45 /// Decode message based on message type 46 auto decodeMessageFields(Type type, ubyte[] bytes...) 47 { 48 StyxObject[] msg; 49 50 with (STYX_MESSAGE_TYPE) 51 { 52 switch(type.getType) 53 { 54 // version 55 case R_VERSION: 56 msg = decodeMessageFields!Rversion(bytes); 57 break; 58 case T_VERSION: 59 msg = decodeMessageFields!Tversion(bytes); 60 break; 61 // auth 62 case R_AUTH: 63 msg = decodeMessageFields!Rauth(bytes); 64 break; 65 case T_AUTH: 66 msg = decodeMessageFields!Tauth(bytes); 67 break; 68 // error 69 case R_ERROR: 70 msg = decodeMessageFields!Rerror(bytes); 71 break; 72 // flush 73 case R_FLUSH: 74 msg = decodeMessageFields!Rflush(bytes); 75 break; 76 case T_FLUSH: 77 msg = decodeMessageFields!Tflush(bytes); 78 break; 79 // attach 80 case R_ATTACH: 81 msg = decodeMessageFields!Rattach(bytes); 82 break; 83 case T_ATTACH: 84 msg = decodeMessageFields!Tattach(bytes); 85 break; 86 // walk 87 case R_WALK: 88 msg = decodeMessageFields!Rwalk(bytes); 89 break; 90 case T_WALK: 91 msg = decodeMessageFields!Twalk(bytes); 92 break; 93 // open 94 case R_OPEN: 95 msg = decodeMessageFields!Ropen(bytes); 96 break; 97 case T_OPEN: 98 msg = decodeMessageFields!Topen(bytes); 99 break; 100 // create 101 case R_CREATE: 102 msg = decodeMessageFields!Rcreate(bytes); 103 break; 104 case T_CREATE: 105 msg = decodeMessageFields!Tcreate(bytes); 106 break; 107 // read 108 case R_READ: 109 msg = decodeMessageFields!Rread(bytes); 110 break; 111 case T_READ: 112 msg = decodeMessageFields!Tread(bytes); 113 break; 114 // write 115 case R_WRITE: 116 msg = decodeMessageFields!Rwrite(bytes); 117 break; 118 case T_WRITE: 119 msg = decodeMessageFields!Twrite(bytes); 120 break; 121 // clunk 122 case R_CLUNK: 123 msg = decodeMessageFields!Rclunk(bytes); 124 break; 125 case T_CLUNK: 126 msg = decodeMessageFields!Tclunk(bytes); 127 break; 128 // remove 129 case R_REMOVE: 130 msg = decodeMessageFields!Rremove(bytes); 131 break; 132 case T_REMOVE: 133 msg = decodeMessageFields!Tremove(bytes); 134 break; 135 // stat 136 case R_STAT: 137 msg = decodeMessageFields!Rstat(bytes); 138 break; 139 case T_STAT: 140 msg = decodeMessageFields!Tstat(bytes); 141 break; 142 // wstat 143 case R_WSTAT: 144 msg = decodeMessageFields!Rwstat(bytes); 145 break; 146 case T_WSTAT: 147 msg = decodeMessageFields!Twstat(bytes); 148 break; 149 default: 150 //throw new Exception("Bad message type"); 151 break; 152 } 153 } 154 return msg; 155 } 156 } 157 158 /** 159 Decodes the received set of bytes into 9P / Styx protocol objects. 160 Params: 161 bytes = Array of unsigned bytes that represents message. 162 163 Typical usage: 164 ---- 165 import std.stdio; 166 auto msg = decode([73, 0, 0, 0, 125, 1, 0, 64, 0, 62, 0, 77, 0, 4, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 253, 1, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 116, 101, 115, 116, 5, 0, 100, 117, 115, 101, 114, 6, 0, 100, 95, 117, 115, 101, 114, 0, 0] 167 ); 168 // prints [Size(size=73), R_STAT, Tag(tag=0x0001), Stat(type=77, dev=4, qid=Qid(type=QTDIR, vers=0, path=0), mode=Permissions(perm=0x800001fd), atime=0, mtime=0, length=0, name="test", uid="duser", gid="d_user", muid="") 169 msg.writeln; 170 ---- 171 */ 172 auto decode(ubyte[] bytes...) 173 { 174 StyxObject[] msg; 175 176 if (bytes.length == 0) 177 { 178 throw new Exception(`Bad Styx message length: empty message`); 179 } 180 else 181 { 182 if (bytes.length < 7) 183 { 184 throw new Exception(`Bad Styx message length: message contains no Styx header`); 185 } 186 else 187 { 188 size_t messageLength = fromLEBytes!size_t(bytes[0..4]); 189 190 if (bytes.length < messageLength) 191 { 192 throw new Exception(`Bad Styx message length: size in header exceeded real message length`); 193 } 194 else 195 { 196 ubyte type = bytes[4]; 197 198 if ((type < 100) || (type > 127)) 199 { 200 throw new Exception(`Bad message content: wrong message type`); 201 } 202 else 203 { 204 auto msgType = new Type(cast(STYX_MESSAGE_TYPE) type); 205 msg = decodeMessageFields(msgType, bytes); 206 } 207 } 208 } 209 } 210 211 return msg; 212 }