def parse_encrypted(self, part_len, data): if part_len != len(data): raise ProtocolError("Enc pkt size disaggrees with header.") if len(data) <= 38: raise ProtocolError("Truncated encrypted part.") uname_len, data = struct.unpack("!H", data[:2])[0], data[2:] if len(data) <= uname_len + 36: raise ProtocolError("Truncated encrypted part.") uname, data = data[:uname_len].decode(), data[uname_len:] if uname not in self.auth_db: raise ProtocolError("Couldn't decrypt, unknown user '%s'" % uname) iv, data = data[:16], data[16:] password = self.auth_db[uname].encode() key = sha256(password).digest() pad_bytes = 16 - (len(data) % 16) data += b'\0' * pad_bytes cipher = Cipher(algorithms.AES(key), modes.OFB(iv), backend=self.crypto_backend) decryptor = cipher.decryptor() data = decryptor.update(data) data = data[:-pad_bytes] tag, data = data[:20], data[20:] tag2 = sha1(data).digest() if not self._hashes_match(tag, tag2): raise ProtocolError("Bad checksum on enc pkt for '%s'" % uname) return data
def parse_values(self, stype, data): types = {0: "!Q", 1: "<d", 2: "!q", 3: "!Q"} (nvals,) = struct.unpack("!H", data[:2]) data = data[2:] if len(data) != 9 * nvals: raise ProtocolError("Invalid value structure length.") vtypes = self.types.get(stype) if nvals != len(vtypes): raise ProtocolError("Values different than types.db info.") for i in range(nvals): if six.PY3: vtype = data[i] else: (vtype,) = struct.unpack("B", data[i]) if vtype != vtypes[i][1]: if self.counter_eq_derive and \ (vtype, vtypes[i][1]) in ((0, 2), (2, 0)): # if counter vs derive don't break, assume server is right log.debug("Type mismatch (counter/derive) for %s/%s", stype, vtypes[i][0]) else: raise ProtocolError("Type mismatch with types.db") data = data[nvals:] for i in range(nvals): vdata, data = data[:8], data[8:] (val,) = struct.unpack(types[vtypes[i][1]], vdata) yield vtypes[i][0], vtypes[i][1], val
def parse_number(self, data): fmt = self.NUMERIC_TYPES.get(data[0]) if fmt is None: raise ProtocolError("Invalid numeric type") sz = struct.calcsize(fmt) if sz > len(data) - 1: raise ProtocolError("Truncated numeric value") (val, ) = struct.unpack(data[1:1 + sz]) return val, data[1 + sz:]
def parse_string(self, data): (length, ) = struct.unpack("!H", data[:2]) if length > len(data) - 2: raise ProtocolError("Truncated string value") if data[2 + length] != 0x00: raise ProtocolError("String missing null-byte terminator") try: ret = data[2:2 + length - 1].decode("utf-8") return ret, data[2 + length + 1:] except UnicodeDecodeError: raise ProtocolError("String is not value UTF-8")
def parse_signed(self, part_len, data): if part_len <= 32: raise ProtocolError("Truncated signed part.") sig, data = data[:32], data[32:] uname_len = part_len - 32 uname = data[:uname_len].decode() if uname not in self.auth_db: raise ProtocolError("Signed packet, unknown user '%s'" % uname) password = self.auth_db[uname].encode() sig2 = hmac.new(password, msg=data, digestmod=sha256).digest() if not self._hashes_match(sig, sig2): raise ProtocolError("Bad signature from user '%s'" % uname) data = data[uname_len:] return data
def parse_metric(self, hostname, data): cmd, data = data[0], data[1:] mtype = cmd & 0xF0 action = cmd & 0x0F if mtype not in self.METRIC_TYPES: raise ProtocolError("Invalid metric type") if action not in self.METRIC_ACTIONS: raise ProtocolError("Invalid metric action") name, data = self.parse_string(data) if action is MetricsDCommand.UPDATE: value, data = self.parse_number(data) else: value = None stat = names.statname(hostname, name.split(".")) cmd = MetricsDCommand(stat, mtype, action, value) return cmd, data
def parse(self, data): if data[0] != 0xAA: raise ProtocolError("Invalid magic byte") hostname, data = self.parse_string(data) while len(data): mc, data = self.parse_metric(hostname, data) yield mc
def parse_data(self, data): types = set([ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x0100, 0x0101, 0x0200, 0x0210 ]) while len(data) > 0: if len(data) < 4: raise ProtocolError("Truncated header.") (part_type, part_len) = struct.unpack("!HH", data[:4]) data = data[4:] if part_type not in types: raise ProtocolError("Invalid part type: 0x%02x" % part_type) part_len -= 4 # includes four header bytes we just parsed if len(data) < part_len: raise ProtocolError("Truncated value.") part_data, data = data[:part_len], data[part_len:] yield (part_type, part_data)
def parse_values(self, stype, data): types = {0: "!Q", 1: "<d", 2: "!q", 3: "!Q"} (nvals, ) = struct.unpack("!H", data[:2]) data = data[2:] if len(data) != 9 * nvals: raise ProtocolError("Invalid value structure length.") vtypes = self.types.get(stype) if nvals != len(vtypes): raise ProtocolError("Values different than types.db info.") for i in range(nvals): (vtype, ) = struct.unpack("B", data[i]) if vtype != vtypes[i][1]: raise ProtocolError("Type mismatch with types.db") data = data[nvals:] for i in range(nvals): vdata, data = data[:8], data[8:] (val, ) = struct.unpack(types[vtypes[i][1]], vdata) yield vtypes[i][0], vtypes[i][1], val
def parse(self, data): if len(data) < 4: raise ProtocolError("Truncated header.") part_type, part_len = struct.unpack("!HH", data[:4]) sec_level = {0x0200: 1, 0x0210: 2}.get(part_type, 0) if sec_level < self.sec_level: raise ProtocolError("Packet has lower security level than allowed") if not sec_level: return data if sec_level == 1 and not self.sec_level: return data[part_len:] data = data[4:] part_len -= 4 if len(data) < part_len: raise ProtocolError("Truncated part payload.") if self.cfg_mon is not None and self.cfg_mon.modified(): log.info("Collectd authfile modified, reloading") self.load_auth_file() if sec_level == 1: return self.parse_signed(part_len, data) if sec_level == 2: return self.parse_encrypted(part_len, data)
def get(self, name): t = self.types.get(name) if t is None: raise ProtocolError("Invalid type name: %s" % name) return t
def _parser(sample, data): if len(data) != 8: raise ProtocolError("Invalid hires time data length.") (val, ) = struct.unpack("!Q", data) sample[name] = val * (2**-30)
def _parser(sample, data): if len(data) != 8: raise ProtocolError("Invalid time data length.") (val, ) = struct.unpack("!Q", data) sample[name] = float(val)
def _parser(sample, data): if data[-1] != '\0': raise ProtocolError("Invalid string detected.") sample[name] = data[:-1]