package ber
import (
"bytes"
"fmt"
"io"
"os"
"reflect"
)
type Packet struct {
ClassType uint8
TagType uint8
Tag uint8
Value interface{}
Data *bytes.Buffer
Children []*Packet
Description string
}
const (
TagEOC = 0x00
TagBoolean = 0x01
TagInteger = 0x02
TagBitString = 0x03
TagOctetString = 0x04
TagNULL = 0x05
TagObjectIdentifier = 0x06
TagObjectDescriptor = 0x07
TagExternal = 0x08
TagRealFloat = 0x09
TagEnumerated = 0x0a
TagEmbeddedPDV = 0x0b
TagUTF8String = 0x0c
TagRelativeOID = 0x0d
TagSequence = 0x10
TagSet = 0x11
TagNumericString = 0x12
TagPrintableString = 0x13
TagT61String = 0x14
TagVideotexString = 0x15
TagIA5String = 0x16
TagUTCTime = 0x17
TagGeneralizedTime = 0x18
TagGraphicString = 0x19
TagVisibleString = 0x1a
TagGeneralString = 0x1b
TagUniversalString = 0x1c
TagCharacterString = 0x1d
TagBMPString = 0x1e
TagBitmask = 0x1f // xxx11111b
)
var TagMap = map[uint8] string {
TagEOC : "EOC (End-of-Content)",
TagBoolean : "Boolean",
TagInteger : "Integer",
TagBitString : "Bit String",
TagOctetString : "Octet String",
TagNULL : "NULL",
TagObjectIdentifier : "Object Identifier",
TagObjectDescriptor : "Object Descriptor",
TagExternal : "External",
TagRealFloat : "Real (float)",
TagEnumerated : "Enumerated",
TagEmbeddedPDV : "Embedded PDV",
TagUTF8String : "UTF8 String",
TagRelativeOID : "Relative-OID",
TagSequence : "Sequence and Sequence of",
TagSet : "Set and Set OF",
TagNumericString : "Numeric String",
TagPrintableString : "Printable String",
TagT61String : "T61 String",
TagVideotexString : "Videotex String",
TagIA5String : "IA5 String",
TagUTCTime : "UTC Time",
TagGeneralizedTime : "Generalized Time",
TagGraphicString : "Graphic String",
TagVisibleString : "Visible String",
TagGeneralString : "General String",
TagUniversalString : "Universal String",
TagCharacterString : "Character String",
TagBMPString : "BMP String",
}
const (
ClassUniversal = 0 // 00xxxxxxb
ClassApplication = 64 // 01xxxxxxb
ClassContext = 128 // 10xxxxxxb
ClassPrivate = 192 // 11xxxxxxb
ClassBitmask = 192 // 11xxxxxxb
)
var ClassMap = map[uint8] string {
ClassUniversal : "Universal",
ClassApplication : "Application",
ClassContext : "Context",
ClassPrivate : "Private",
}
const (
TypePrimative = 0 // xx0xxxxxb
TypeConstructed = 32 // xx1xxxxxb
TypeBitmask = 32 // xx1xxxxxb
)
var TypeMap = map[uint8] string {
TypePrimative : "Primative",
TypeConstructed : "Constructed",
}
var Debug bool = false
func PrintBytes( buf []byte, indent string ) {
data_lines := make( []string, ( len( buf ) / 30 ) + 1 )
num_lines := make( []string, ( len( buf ) / 30 ) + 1 )
for i, b := range buf {
data_lines[ i / 30 ] += fmt.Sprintf( "%02x ", b )
num_lines[ i / 30 ] += fmt.Sprintf( "%02d ", ( i + 1 ) % 100 )
}
for i := 0; i < len( data_lines ); i++ {
fmt.Print( indent + data_lines[ i ] + "\n" )
fmt.Print( indent + num_lines[ i ] + "\n\n" )
}
}
func PrintPacket( p *Packet ) {
printPacket( p, 0, false )
}
func printPacket( p *Packet, indent int, printBytes bool ) {
indent_str := ""
for len(indent_str) != indent {
indent_str += " "
}
class_str := ClassMap[ p.ClassType ]
tagtype_str := TypeMap[ p.TagType ]
tag_str := fmt.Sprintf( "0x%02X", p.Tag )
if p.ClassType == ClassUniversal {
tag_str = TagMap[ p.Tag ]
}
value := fmt.Sprint( p.Value )
description := ""
if p.Description != "" {
description = p.Description + ": "
}
fmt.Printf( "%s%s(%s, %s, %s) Len=%d %q\n", indent_str, description, class_str, tagtype_str, tag_str, p.Data.Len(), value )
if printBytes {
PrintBytes( p.Bytes(), indent_str )
}
for _, child := range p.Children {
printPacket( child, indent + 1, printBytes )
}
}
func resizeBuffer( in []byte, new_size uint64 ) (out []byte) {
out = make( []byte, new_size )
copy( out, in )
return
}
func readBytes( reader io.Reader, buf []byte ) os.Error {
idx := 0
buflen := len( buf )
for idx < buflen {
n, err := reader.Read( buf[ idx: ] )
if err != nil {
return err
}
idx += n
}
return nil
}
func ReadPacket( reader io.Reader ) ( *Packet, os.Error) {
buf := make([]byte, 2)
err := readBytes( reader, buf )
if err != nil {
return nil, err
}
idx := uint64(2)
datalen := uint64(buf[1])
if Debug {
fmt.Printf( "Read: datalen = %d len(buf) = %d ", datalen, len( buf ) )
for _, b := range buf {
fmt.Printf( "%02X ", b )
}
fmt.Printf( "\n" )
}
if datalen & 128 != 0 {
a := datalen - 128
idx += a
buf = resizeBuffer( buf, 2 + a )
err := readBytes( reader, buf[2:] )
if err != nil {
return nil, err
}
datalen = DecodeInteger( buf[ 2:2+a ] )
if Debug {
fmt.Printf( "Read: a = %d idx = %d datalen = %d len(buf) = %d", a, idx, datalen, len( buf ) )
for _, b := range buf {
fmt.Printf( "%02X ", b )
}
fmt.Printf( "\n" )
}
}
buf = resizeBuffer( buf, idx + datalen )
err = readBytes( reader, buf[idx:] )
if err != nil {
return nil, err
}
if Debug {
fmt.Printf( "Read: len( buf ) = %d idx=%d datalen=%d idx+datalen=%d\n", len( buf ), idx, datalen, idx + datalen )
for _, b := range buf {
fmt.Printf( "%02X ", b )
}
}
p := DecodePacket( buf )
return p, nil
}
func DecodeString( data []byte ) (ret string) {
for _, c := range data {
ret += fmt.Sprintf( "%c", c )
}
return
}
func DecodeInteger( data []byte ) (ret uint64) {
for _, i := range data {
ret = ret * 256
ret = ret + uint64(i)
}
return
}
func EncodeInteger( val uint64 ) []byte {
var out bytes.Buffer
found := false
shift := uint(56)
mask := uint64(0xFF00000000000000)
for mask > 0 {
if !found && ( val & mask != 0 ) {
found = true
}
if found || ( shift == 0 ) {
out.Write( []byte { byte( ( val & mask ) >> shift ) } )
}
shift -= 8
mask = mask >> 8
}
return out.Bytes()
}
func DecodePacket( data []byte ) *Packet {
p, _ := decodePacket( data )
return p
}
func decodePacket( data []byte ) (*Packet, []byte) {
if Debug {
fmt.Printf( "decodePacket: enter %d\n", len( data ) )
}
p := new( Packet )
p.ClassType = data[0] & ClassBitmask
p.TagType = data[0] & TypeBitmask
p.Tag = data[0] & TagBitmask
datalen := DecodeInteger( data[1:2] )
datapos := uint64(2)
if datalen & 128 != 0 {
datalen -= 128
datapos += datalen
datalen = DecodeInteger( data[2:2+datalen] )
}
p.Data = new( bytes.Buffer )
p.Children = make( []*Packet, 0, 2 )
p.Value = nil
value_data := data[datapos:datapos+datalen]
if p.TagType == TypeConstructed {
for len( value_data ) != 0 {
var child *Packet
child, value_data = decodePacket( value_data )
p.AppendChild( child )
}
} else if p.ClassType == ClassUniversal {
p.Data.Write( data[datapos:datapos+datalen] )
switch p.Tag {
case TagEOC:
case TagBoolean:
val := DecodeInteger( value_data )
p.Value = val != 0
case TagInteger:
p.Value = DecodeInteger( value_data )
case TagBitString:
case TagOctetString:
p.Value = DecodeString( value_data )
case TagNULL:
case TagObjectIdentifier:
case TagObjectDescriptor:
case TagExternal:
case TagRealFloat:
case TagEnumerated:
p.Value = DecodeInteger( value_data )
case TagEmbeddedPDV:
case TagUTF8String:
case TagRelativeOID:
case TagSequence:
case TagSet:
case TagNumericString:
case TagPrintableString:
p.Value = DecodeString( value_data )
case TagT61String:
case TagVideotexString:
case TagIA5String:
case TagUTCTime:
case TagGeneralizedTime:
case TagGraphicString:
case TagVisibleString:
case TagGeneralString:
case TagUniversalString:
case TagCharacterString:
case TagBMPString:
}
} else {
p.Data.Write( data[datapos:datapos+datalen] )
}
return p, data[ datapos + datalen: ]
}
func (p *Packet) DataLength() uint64 {
return uint64( p.Data.Len() )
}
func (p *Packet) Bytes() []byte {
var out bytes.Buffer
out.Write( []byte { p.ClassType | p.TagType | p.Tag } )
packet_length := EncodeInteger( p.DataLength() )
if len( packet_length ) > 1 {
out.Write( []byte { byte( len( packet_length ) | 128 ) } )
out.Write( packet_length )
} else {
out.Write( packet_length )
}
out.Write( p.Data.Bytes() )
return out.Bytes()
}
func (p *Packet) AppendChild( child *Packet ) {
p.Data.Write( child.Bytes() )
if len( p.Children ) == cap( p.Children ) {
newChildren := make( []*Packet, cap( p.Children ) * 2 )
copy( newChildren, p.Children )
p.Children = newChildren[0:len(p.Children)]
}
p.Children = p.Children[ 0:len(p.Children) + 1 ]
p.Children[ len( p.Children ) - 1 ] = child
}
func Encode( ClassType, TagType, Tag uint8, Value interface{}, Description string ) *Packet {
p := new( Packet )
p.ClassType = ClassType
p.TagType = TagType
p.Tag = Tag
p.Data = new( bytes.Buffer )
p.Children = make( []*Packet, 0, 2 )
p.Value = Value
p.Description = Description
if Value != nil {
v := reflect.NewValue(Value)
if ( ClassType == ClassUniversal ) {
switch Tag {
case TagOctetString:
sv, ok := v.Interface().(string)
if ok {
p.Data.Write( []byte(sv) )
}
}
}
}
return p
}
func NewSequence( Description string) *Packet {
return Encode( ClassUniversal, TypePrimative, TagSequence, nil, Description )
}
func NewBoolean( ClassType, TagType, Tag uint8, Value bool, Description string ) *Packet {
intValue := 0
if Value {
intValue = 1
}
p := Encode( ClassType, TagType, Tag, nil, Description )
p.Value = Value
p.Data.Write( EncodeInteger( uint64(intValue) ) )
return p
}
func NewInteger( ClassType, TagType, Tag uint8, Value uint64, Description string ) *Packet {
p := Encode( ClassType, TagType, Tag, nil, Description )
p.Value = Value
p.Data.Write( EncodeInteger( Value ) )
return p
}
func NewString( ClassType, TagType, Tag uint8, Value, Description string ) *Packet {
p := Encode( ClassType, TagType, Tag, nil, Description )
p.Value = Value
p.Data.Write( []byte( Value ) )
return p
}