func isSpecialPgsqlCommand(data []byte) (bool, int, int) { if len(data) < 8 { // 8 bytes required return false, 0, 0 } // read length length := readLength(data[0:]) // read command identifier code := int(common.BytesNtohl(data[4:])) if length == 16 && code == 80877102 { // Cancel Request logp.Debug("pgsqldetailed", "Cancel Request, length=%d", length) return true, length, cancelRequest } else if length == 8 && code == 80877103 { // SSL Request logp.Debug("pgsqldetailed", "SSL Request, length=%d", length) return true, length, sslRequest } else if code == 196608 { // Startup Message logp.Debug("pgsqldetailed", "Startup Message, length=%d", length) return true, length, startupMessage } return false, 0, 0 }
func (thrift *thriftPlugin) readI32(data []byte) (value string, ok bool, complete bool, off int) { if len(data) < 4 { return "", true, false, 0 } i32 := common.BytesNtohl(data[:4]) value = strconv.Itoa(int(i32)) return value, true, true, 4 }
func (thrift *thriftPlugin) readMap(data []byte) (value string, ok bool, complete bool, off int) { if len(data) < 6 { return "", true, false, 0 } typeKey := data[0] typeValue := data[1] funcReaderKey, typeFound := thrift.funcReadersByType(typeKey) if !typeFound { logp.Debug("thrift", "Field type %d not known", typeKey) return "", false, false, 0 } funcReaderValue, typeFound := thrift.funcReadersByType(typeValue) if !typeFound { logp.Debug("thrift", "Field type %d not known", typeValue) return "", false, false, 0 } sz := int(common.BytesNtohl(data[2:6])) if sz < 0 { logp.Debug("thrift", "Map too big: %d", sz) return "", false, false, 0 } fields := []string{} offset := 6 for i := 0; i < sz; i++ { key, ok, complete, bytesRead := funcReaderKey(data[offset:]) if !ok { return "", false, false, 0 } if !complete { return "", true, false, 0 } offset += bytesRead value, ok, complete, bytesRead := funcReaderValue(data[offset:]) if !ok { return "", false, false, 0 } if !complete { return "", true, false, 0 } offset += bytesRead if i < thrift.collectionMaxSize { fields = append(fields, key+": "+value) } else if i == thrift.collectionMaxSize { fields = append(fields, "...") } } return "{" + strings.Join(fields, ", ") + "}", true, true, offset }
// Parse 32bit binary value from the buffer at index. Will not advance the read // buffer func (b *Buffer) ReadNetUint32At(index int) (uint32, error) { if b.Failed() { return 0, b.err } if !b.Avail(4 + index) { return 0, b.bufferEndError() } return common.BytesNtohl(b.data[index+b.mark:]), nil }
// Parse 32bit binary value in network byte order from Buffer // (converted to Host order). func (b *Buffer) ReadNetUint32() (uint32, error) { if b.Failed() { return 0, b.err } tmp := b.data[b.mark:] if err := b.Advance(4); err != nil { return 0, err } value := common.BytesNtohl(tmp) return value, nil }
// Common implementation for lists and sets (they share the same binary repr). func (thrift *thriftPlugin) readListOrSet(data []byte) (value string, ok bool, complete bool, off int) { if len(data) < 5 { return "", true, false, 0 } typ := data[0] funcReader, typeFound := thrift.funcReadersByType(typ) if !typeFound { logp.Debug("thrift", "Field type %d not known", typ) return "", false, false, 0 } sz := int(common.BytesNtohl(data[1:5])) if sz < 0 { logp.Debug("thrift", "List/Set too big: %d", sz) return "", false, false, 0 } fields := []string{} offset := 5 for i := 0; i < sz; i++ { value, ok, complete, bytesRead := funcReader(data[offset:]) if !ok { return "", false, false, 0 } if !complete { return "", true, false, 0 } if i < thrift.collectionMaxSize { fields = append(fields, value) } else if i == thrift.collectionMaxSize { fields = append(fields, "...") } offset += bytesRead } return strings.Join(fields, ", "), true, true, offset }
// thriftReadString caps the returned value to ThriftStringMaxSize but returns the // off to the end of it. func (thrift *thriftPlugin) readString(data []byte) (value string, ok bool, complete bool, off int) { if len(data) < 4 { return "", true, false, 0 // ok, not complete } sz := int(common.BytesNtohl(data[:4])) if int32(sz) < 0 { return "", false, false, 0 // not ok } if len(data[4:]) < sz { return "", true, false, 0 // ok, not complete } if sz > thrift.stringMaxSize { value = string(data[4 : 4+thrift.stringMaxSize]) value += "..." } else { value = string(data[4 : 4+sz]) } off = 4 + sz return value, true, true, off // all good }
func (thrift *thriftPlugin) messageParser(s *thriftStream) (bool, bool) { var ok, complete bool var m = s.message logp.Debug("thriftdetailed", "messageParser called parseState=%v offset=%v", s.parseState, s.parseOffset) for s.parseOffset < len(s.data) { switch s.parseState { case thriftStartState: m.start = s.parseOffset if thrift.TransportType == thriftTFramed { // read I32 if len(s.data) < 4 { return true, false } m.frameSize = common.BytesNtohl(s.data[:4]) s.parseOffset = 4 } ok, complete = thrift.readMessageBegin(s) logp.Debug("thriftdetailed", "readMessageBegin returned: %v %v", ok, complete) if !ok { return false, false } if !complete { return true, false } if !m.isRequest && !thrift.captureReply { // don't actually read the result logp.Debug("thrift", "Don't capture reply") m.returnValue = "" m.exceptions = "" return true, true } s.parseState = thriftFieldState case thriftFieldState: ok, complete, field := thrift.readField(s) logp.Debug("thriftdetailed", "readField returned: %v %v", ok, complete) if !ok { return false, false } if complete { // done var method *thriftIdlMethod if thrift.idl != nil { method = thrift.idl.findMethod(m.method) } if m.isRequest { if method != nil { m.params = thrift.formatStruct(m.fields, true, method.params) m.service = method.service.Name } else { m.params = thrift.formatStruct(m.fields, false, nil) } } else { if len(m.fields) > 1 { logp.Warn("Thrift RPC response with more than field. Ignoring all but first") } if len(m.fields) > 0 { field := m.fields[0] if field.id == 0 { m.returnValue = field.value m.exceptions = "" } else { m.returnValue = "" if method != nil { m.exceptions = thrift.formatStruct(m.fields, true, method.exceptions) } else { m.exceptions = thrift.formatStruct(m.fields, false, nil) } m.hasException = true } } } return true, true } if field == nil { return true, false // ok, not complete } m.fields = append(m.fields, *field) } } return true, false }
func (thrift *thriftPlugin) readMessageBegin(s *thriftStream) (bool, bool) { var ok, complete bool var offset, off int m := s.message if len(s.data[s.parseOffset:]) < 9 { return true, false // ok, not complete } sz := common.BytesNtohl(s.data[s.parseOffset : s.parseOffset+4]) if int32(sz) < 0 { m.version = sz & thriftVersionMask if m.version != thriftVersion1 { logp.Debug("thrift", "Unexpected version: %d", m.version) } logp.Debug("thriftdetailed", "version = %d", m.version) offset = s.parseOffset + 4 logp.Debug("thriftdetailed", "offset = %d", offset) m.Type = sz & ThriftTypeMask m.method, ok, complete, off = thrift.readString(s.data[offset:]) if !ok { return false, false // not ok, not complete } if !complete { logp.Debug("thriftdetailed", "Method name not complete") return true, false // ok, not complete } offset += off logp.Debug("thriftdetailed", "method = %s", m.method) logp.Debug("thriftdetailed", "offset = %d", offset) if len(s.data[offset:]) < 4 { logp.Debug("thriftdetailed", "Less then 4 bytes remaining") return true, false // ok, not complete } m.seqID = common.BytesNtohl(s.data[offset : offset+4]) s.parseOffset = offset + 4 } else { // no version mode offset = s.parseOffset m.method, ok, complete, off = thrift.readString(s.data[offset:]) if !ok { return false, false // not ok, not complete } if !complete { logp.Debug("thriftdetailed", "Method name not complete") return true, false // ok, not complete } offset += off logp.Debug("thriftdetailed", "method = %s", m.method) logp.Debug("thriftdetailed", "offset = %d", offset) if len(s.data[offset:]) < 5 { return true, false // ok, not complete } m.Type = uint32(s.data[offset]) offset++ m.seqID = common.BytesNtohl(s.data[offset : offset+4]) s.parseOffset = offset + 4 } if m.Type == ThriftMsgTypeCall || m.Type == ThriftMsgTypeOneway { m.isRequest = true } else { m.isRequest = false } return true, true }
// length field in pgsql counts total length of length field + payload, not // including the message identifier. => Always check buffer size >= length + 1 func readLength(b []byte) int { return int(common.BytesNtohl(b)) }