Пример #1
0
func isSpecialPgsqlCommand(data []byte) (bool, int) {

	if len(data) < 8 {
		// 8 bytes required
		return false, 0
	}

	// read length
	length := int(common.Bytes_Ntohl(data[0:4]))

	// read command identifier
	code := int(common.Bytes_Ntohl(data[4:8]))

	if length == 16 && code == 80877102 {
		// Cancel Request
		logp.Debug("pgsqldetailed", "Cancel Request, length=%d", length)
		return true, CancelRequest
	} else if length == 8 && code == 80877103 {
		// SSL Request
		logp.Debug("pgsqldetailed", "SSL Request, length=%d", length)
		return true, SSLRequest
	} else if code == 196608 {
		// Startup Message
		logp.Debug("pgsqldetailed", "Startup Message, length=%d", length)
		return true, StartupMessage
	}
	return false, 0
}
Пример #2
0
func (pgsql *Pgsql) pgsqlRowsParser(s *PgsqlStream) error {
	m := s.message

	// read field count (int16)
	field_count := int(common.Bytes_Ntohs(s.data[s.parseOffset : s.parseOffset+2]))
	s.parseOffset += 2
	logp.Debug("pgsqldetailed", "DataRow field count=%d", field_count)

	row := []string{}
	var row_len int

	for i := 0; i < field_count; i++ {

		// read column length (int32)
		column_length := int32(common.Bytes_Ntohl(s.data[s.parseOffset : s.parseOffset+4]))
		s.parseOffset += 4

		if column_length > 0 && int(column_length) > len(s.data[s.parseOffset:]) {
			logp.Err("Pgsql invalid column_length=%v, buffer_length=%v, i=%v",
				column_length, len(s.data[s.parseOffset:]), i)
			return errInvalidLength
		}

		// read column value (byten)
		column_value := []byte{}

		if m.FieldsFormat[i] == 0 {
			// field value in text format
			if column_length > 0 {
				column_value = s.data[s.parseOffset : s.parseOffset+int(column_length)]
			} else if column_length == -1 {
				column_value = nil
			}
		}

		if row_len < pgsql.maxRowLength {
			if row_len+len(column_value) > pgsql.maxRowLength {
				column_value = column_value[:pgsql.maxRowLength-row_len]
			}
			row = append(row, string(column_value))
			row_len += len(column_value)
		}

		if column_length > 0 {
			s.parseOffset += int(column_length)
		}

		logp.Debug("pgsqldetailed", "Value %s, length=%d", string(column_value), column_length)

	}
	m.NumberOfRows += 1
	if len(m.Rows) < pgsql.maxStoreRows {
		m.Rows = append(m.Rows, row)
	}

	return nil
}
Пример #3
0
func (thrift *Thrift) readI32(data []byte) (value string, ok bool, complete bool, off int) {
	if len(data) < 4 {
		return "", true, false, 0
	}
	i32 := common.Bytes_Ntohl(data[:4])
	value = strconv.Itoa(int(i32))

	return value, true, true, 4
}
Пример #4
0
func (thrift *Thrift) readMap(data []byte) (value string, ok bool, complete bool, off int) {
	if len(data) < 6 {
		return "", true, false, 0
	}
	type_key := data[0]
	type_value := data[1]

	funcReaderKey, typeFound := thrift.funcReadersByType(type_key)
	if !typeFound {
		logp.Debug("thrift", "Field type %d not known", type_key)
		return "", false, false, 0
	}

	funcReaderValue, typeFound := thrift.funcReadersByType(type_value)
	if !typeFound {
		logp.Debug("thrift", "Field type %d not known", type_value)
		return "", false, false, 0
	}

	sz := int(common.Bytes_Ntohl(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
}
Пример #5
0
// 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.Bytes_Ntohl(b.data[index+b.mark:]), nil

}
Пример #6
0
// 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.Bytes_Ntohl(tmp)
	return value, nil
}
Пример #7
0
// Common implementation for lists and sets (they share the same binary repr).
func (thrift *Thrift) readListOrSet(data []byte) (value string, ok bool, complete bool, off int) {
	if len(data) < 5 {
		return "", true, false, 0
	}
	type_ := data[0]

	funcReader, typeFound := thrift.funcReadersByType(type_)
	if !typeFound {
		logp.Debug("thrift", "Field type %d not known", type_)
		return "", false, false, 0
	}

	sz := int(common.Bytes_Ntohl(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
}
Пример #8
0
// thriftReadString caps the returned value to ThriftStringMaxSize but returns the
// off to the end of it.
func (thrift *Thrift) 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.Bytes_Ntohl(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
}
Пример #9
0
func (pgsql *Pgsql) pgsqlMessageParser(s *PgsqlStream) (bool, bool) {

	m := s.message
	for s.parseOffset < len(s.data) {
		switch s.parseState {
		case PgsqlStartState:
			if len(s.data[s.parseOffset:]) < 5 {
				logp.Warn("Postgresql Message too short. %X (length=%d). Wait for more.", s.data[s.parseOffset:], len(s.data[s.parseOffset:]))
				return true, false
			}

			is_special, command := isSpecialPgsqlCommand(s.data[s.parseOffset:])

			if is_special {
				// In case of Commands: StartupMessage, SSLRequest, CancelRequest that don't have
				// their type in the first byte

				// read length
				length := int(common.Bytes_Ntohl(s.data[s.parseOffset : s.parseOffset+4]))

				// ignore command
				if len(s.data[s.parseOffset:]) >= length {

					if command == SSLRequest {
						// if SSLRequest is received, expect for one byte reply (S or N)
						m.start = s.parseOffset
						s.parseOffset += length
						m.end = s.parseOffset
						m.isSSLRequest = true
						m.Size = uint64(m.end - m.start)

						return true, true
					}
					s.parseOffset += length
				} else {
					// wait for more
					logp.Debug("pgsqldetailed", "Wait for more data 1")
					return true, false
				}

			} else {
				// In case of Commands that have their type in the first byte

				// read type
				typ := byte(s.data[s.parseOffset])

				if s.expectSSLResponse {
					// SSLRequest was received in the other stream
					if typ == 'N' || typ == 'S' {
						// one byte reply to SSLRequest
						logp.Debug("pgsqldetailed", "Reply for SSLRequest %c", typ)
						m.start = s.parseOffset
						s.parseOffset += 1
						m.end = s.parseOffset
						m.isSSLResponse = true
						m.Size = uint64(m.end - m.start)

						return true, true
					}
				}

				// read length
				length := int(common.Bytes_Ntohl(s.data[s.parseOffset+1 : s.parseOffset+5]))

				if length < 4 {
					// length should include the size of itself (int32)
					logp.Debug("pgsqldetailed", "Invalid pgsql command length.")
					return false, false
				}

				logp.Debug("pgsqldetailed", "Pgsql type %c, length=%d", typ, length)

				if typ == 'Q' {
					// SimpleQuery
					m.start = s.parseOffset
					m.IsRequest = true

					if len(s.data[s.parseOffset:]) >= length+1 {
						s.parseOffset += 1 //type
						s.parseOffset += length
						m.end = s.parseOffset
						m.Size = uint64(m.end - m.start)

						m.Query = string(s.data[m.start+5 : m.end-1]) //without string termination

						m.toExport = true
						logp.Debug("pgsqldetailed", "Simple Query: %s", m.Query)
						return true, true
					} else {
						// wait for more
						logp.Debug("pgsqldetailed", "Wait for more data 2")
						return true, false
					}
				} else if typ == 'T' {
					// RowDescription

					m.start = s.parseOffset
					m.IsRequest = false
					m.IsOK = true
					m.toExport = true

					if len(s.data[s.parseOffset:]) >= length+1 {
						s.parseOffset += 1 //type
						s.parseOffset += 4 //length

						pgsqlFieldsParser(s)
						logp.Debug("pgsqldetailed", "Fields: %s", m.Fields)

						s.parseState = PgsqlGetDataState
					} else {
						// wait for more
						logp.Debug("pgsqldetailed", "Wait for more data 3")
						return true, false
					}

				} else if typ == 'I' {
					// EmptyQueryResponse, appears as a response for empty queries
					// substitutes CommandComplete

					logp.Debug("pgsqldetailed", "EmptyQueryResponse")
					m.start = s.parseOffset
					m.IsOK = true
					m.IsRequest = false
					m.toExport = true
					s.parseOffset += 5 // type + length
					m.end = s.parseOffset
					m.Size = uint64(m.end - m.start)

					return true, true

				} else if typ == 'E' {
					// ErrorResponse

					logp.Debug("pgsqldetailed", "ErrorResponse")
					m.start = s.parseOffset
					m.IsRequest = false
					m.IsError = true
					m.toExport = true

					if len(s.data[s.parseOffset:]) >= length+1 {
						s.parseOffset += 1 //type
						s.parseOffset += 4 //length

						pgsqlErrorParser(s)

						m.end = s.parseOffset
						m.Size = uint64(m.end - m.start)

						return true, true
					} else {
						// wait for more
						logp.Debug("pgsqldetailed", "Wait for more data 4")
						return true, false
					}
				} else if typ == 'C' {
					// CommandComplete -> Successful response

					m.start = s.parseOffset
					m.IsRequest = false
					m.IsOK = true
					m.toExport = true

					if len(s.data[s.parseOffset:]) >= length+1 {
						s.parseOffset += 1 //type

						name := string(s.data[s.parseOffset+4 : s.parseOffset+length-1]) //without \0
						logp.Debug("pgsqldetailed", "CommandComplete length=%d, tag=%s", length, name)

						s.parseOffset += length
						m.end = s.parseOffset
						m.Size = uint64(m.end - m.start)

						return true, true
					} else {
						// wait for more
						logp.Debug("pgsqldetailed", "Wait for more data 5")
						return true, false
					}
				} else if typ == 'Z' {
					// ReadyForQuery -> backend ready for a new query cycle
					if len(s.data[s.parseOffset:]) >= length+1 {
						m.start = s.parseOffset
						s.parseOffset += 1 // type
						s.parseOffset += length
						m.end = s.parseOffset
						m.Size = uint64(m.end - m.start)

						return true, true
					} else {
						// wait for more
						logp.Debug("pgsqldetailed", "Wait for more 5b")
						return true, false
					}
				} else {
					// TODO: add info from NoticeResponse in case there are warning messages for a query
					// ignore command
					if len(s.data[s.parseOffset:]) >= length+1 {
						s.parseOffset += 1 //type
						s.parseOffset += length
						m.end = s.parseOffset
						m.Size = uint64(m.end - m.start)

						// ok and complete, but ignore
						m.toExport = false
						return true, true
					} else {
						// wait for more
						logp.Debug("pgsqldetailed", "Wait for more data 6")
						return true, false
					}
				}
			}

			break

		case PgsqlGetDataState:

			// The response to queries that return row sets contains:
			// RowDescription
			// zero or more DataRow
			// CommandComplete
			// ReadyForQuery

			if len(s.data[s.parseOffset:]) < 5 {
				logp.Warn("Postgresql Message too short (length=%d). Wait for more.", len(s.data[s.parseOffset:]))
				return true, false
			}

			// read type
			typ := byte(s.data[s.parseOffset])

			// read message length
			length := int(common.Bytes_Ntohl(s.data[s.parseOffset+1 : s.parseOffset+5]))

			if typ == 'D' {
				// DataRow

				if len(s.data[s.parseOffset:]) >= length+1 {
					// skip type
					s.parseOffset += 1
					// skip length size
					s.parseOffset += 4

					pgsql.pgsqlRowsParser(s)

				} else {
					// wait for more
					logp.Debug("pgsqldetailed", "Wait for more data 7")
					return true, false
				}

			} else if typ == 'C' {
				// CommandComplete

				if len(s.data[s.parseOffset:]) >= length+1 {

					// skip type
					s.parseOffset += 1

					name := string(s.data[s.parseOffset+4 : s.parseOffset+length-1]) //without \0
					logp.Debug("pgsqldetailed", "CommandComplete length=%d, tag=%s", length, name)

					s.parseOffset += length
					m.end = s.parseOffset
					m.Size = uint64(m.end - m.start)

					s.parseState = PgsqlStartState

					logp.Debug("pgsqldetailed", "Rows: %s", m.Rows)

					return true, true
				} else {
					// wait for more
					logp.Debug("pgsqldetailed", "Wait for more data 8")
					return true, false
				}
			} else {
				// shouldn't happen
				logp.Debug("pgsqldetailed", "Skip command of type %c", typ)
				s.parseState = PgsqlStartState
			}
			break
		}
	}

	return true, false
}
Пример #10
0
// 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.Bytes_Ntohl(b))
}
Пример #11
0
func (thrift *Thrift) 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.Bytes_Ntohl(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 = nil
				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
}
Пример #12
0
func (thrift *Thrift) 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.Bytes_Ntohl(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.Bytes_Ntohl(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 += 1
		m.SeqId = common.Bytes_Ntohl(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
}