diff options
Diffstat (limited to 'goldap/bytes.go')
-rw-r--r-- | goldap/bytes.go | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/goldap/bytes.go b/goldap/bytes.go new file mode 100644 index 0000000..92c63a2 --- /dev/null +++ b/goldap/bytes.go @@ -0,0 +1,199 @@ +package message + +import ( + "fmt" +) + +type Bytes struct { + offset int + bytes []byte +} + +func (bytes *Bytes) getBytes() []byte { + return bytes.bytes +} +func NewBytes(offset int, bytes []byte) (ret *Bytes) { + return &Bytes{offset: offset, bytes: bytes} +} + +func (bytes Bytes) Debug() { + fmt.Printf("Offset: %d, Bytes: %+v\n", bytes.offset, bytes.bytes) +} + +// Return a string with the hex dump of the bytes around the current offset +// The current offset byte is put in brackets +// Example: 0x01, [0x02], 0x03 +func (bytes *Bytes) DumpCurrentBytes() (ret string) { + var strings [3]string + for i := -1; i <= 1; i++ { + if bytes.offset+i >= 0 && bytes.offset+i < len(bytes.bytes) { + strings[i+1] = fmt.Sprintf("%#x", bytes.bytes[bytes.offset+i]) + } + } + ret = fmt.Sprintf("%s, [%s], %s", strings[0], strings[1], strings[2]) + return +} + +func (bytes *Bytes) HasMoreData() bool { + return bytes.offset < len(bytes.bytes) +} + +func (bytes *Bytes) ParseTagAndLength() (ret TagAndLength, err error) { + var offset int + ret, offset, err = ParseTagAndLength(bytes.bytes, bytes.offset) + if err != nil { + err = LdapError{fmt.Sprintf("ParseTagAndLength: %s", err.Error())} + return + } else { + bytes.offset = offset + } + return +} + +func (bytes *Bytes) ReadSubBytes(class int, tag int, callback func(bytes *Bytes) error) (err error) { + // Check tag + tagAndLength, err := bytes.ParseTagAndLength() + if err != nil { + return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} + } + err = tagAndLength.Expect(class, tag, isCompound) + if err != nil { + return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} + } + + start := bytes.offset + end := bytes.offset + tagAndLength.Length + + // Check we got enough bytes to process + if end > len(bytes.bytes) { + return LdapError{fmt.Sprintf("ReadSubBytes: data truncated: expecting %d bytes at offset %d", tagAndLength.Length, bytes.offset)} + } + // Process sub-bytes + subBytes := Bytes{offset: 0, bytes: bytes.bytes[start:end]} + err = callback(&subBytes) + if err != nil { + bytes.offset += subBytes.offset + err = LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} + return + } + // Check we got no more bytes to process + if subBytes.HasMoreData() { + return LdapError{fmt.Sprintf("ReadSubBytes: data too long: %d more bytes to read at offset %d", end-bytes.offset, bytes.offset)} + } + // Move offset + bytes.offset = end + return +} + +func SizeSubBytes(tag int, callback func() int) (size int) { + size = callback() + size += sizeTagAndLength(tag, size) + return +} + +func (bytes *Bytes) WritePrimitiveSubBytes(class int, tag int, value interface{}) (size int) { + switch value.(type) { + case BOOLEAN: + size = writeBool(bytes, bool(value.(BOOLEAN))) + case INTEGER: + size = writeInt32(bytes, int32(value.(INTEGER))) + case ENUMERATED: + size = writeInt32(bytes, int32(value.(ENUMERATED))) + case OCTETSTRING: + size = writeOctetString(bytes, []byte(string(value.(OCTETSTRING)))) + default: + panic(fmt.Sprintf("WritePrimitiveSubBytes: invalid value type %v", value)) + } + size += bytes.WriteTagAndLength(class, isNotCompound, tag, size) + return +} + +func (bytes *Bytes) WriteTagAndLength(class int, compound bool, tag int, length int) int { + return writeTagAndLength(bytes, TagAndLength{Class: class, IsCompound: compound, Tag: tag, Length: length}) +} + +func (bytes *Bytes) writeString(s string) (size int) { + size = len(s) + start := bytes.offset - size + if start < 0 { + panic("Not enough space for string") + } + copy(bytes.bytes[start:], s) + bytes.offset = start + return +} + +func (bytes *Bytes) writeBytes(b []byte) (size int) { + size = len(b) + start := bytes.offset - size + if start < 0 { + panic("Not enough space for bytes") + } + copy(bytes.bytes[start:], b) + bytes.offset = start + return +} + +// +// Parse tag, length and read the a primitive value +// Supported types are: +// - boolean +// - integer (parsed as int32) +// - enumerated (parsed as int32) +// - UTF8 string +// - Octet string +// +// Parameters: +// - class: the expected class value(classUniversal, classApplication, classContextSpecific) +// - tag: the expected tag value +// - typeTag: the real primitive type to parse (tagBoolean, tagInteger, tagEnym, tagUTF8String, tagOctetString) +// +func (bytes *Bytes) ReadPrimitiveSubBytes(class int, tag int, typeTag int) (value interface{}, err error) { + // Check tag + tagAndLength, err := bytes.ParseTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} + return + } + err = tagAndLength.Expect(class, tag, isNotCompound) + if err != nil { + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} + return + } + + start := bytes.offset + end := bytes.offset + tagAndLength.Length + + // Check we got enough bytes to process + if end > len(bytes.bytes) { + // err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: data truncated: expecting %d bytes at offset %d but only %d bytes are remaining (start: %d, length: %d, end: %d, len(b): %d, bytes: %#+v)", tagAndLength.Length, *b.offset, len(b.bytes)-start, start, tagAndLength.Length, end, len(b.bytes), b.bytes)} + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: data truncated: expecting %d bytes at offset %d but only %d bytes are remaining", tagAndLength.Length, bytes.offset, len(bytes.bytes)-start)} + return + } + // Process sub-bytes + subBytes := bytes.bytes[start:end] + switch typeTag { + case tagBoolean: + value, err = parseBool(subBytes) + case tagInteger: + value, err = parseInt32(subBytes) + case tagEnum: + value, err = parseInt32(subBytes) + case tagOctetString: + value, err = parseOctetString(subBytes) + default: + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: invalid type tag value %d", typeTag)} + return + } + if err != nil { + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} + return + } + // Move offset + bytes.offset = end + return +} + +func (bytes *Bytes) Bytes() []byte { + return bytes.bytes +} |