summaryrefslogblamecommitdiffstats
path: root/ber.go
blob: 058fd48a497360d82224d67789695a291a76f0e2 (plain) (tree)
































































































































































































































































































































































                                                                                                                              
                                                             












































































                                                                                             
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 p.DataLength() > 127 || 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
}