package nslcd_proto import ( "encoding/binary" "fmt" "io" "net" "reflect" "syscall" ) type NslcdObject interface { NslcdWrite(fd io.Writer) } type NslcdObjectPtr interface { NslcdRead(fd io.Reader) } func write(fd io.Writer, data interface{}) { switch data := data.(type) { // basic data types case NslcdObject: data.NslcdWrite(fd) case []byte: _, err := fd.Write(data) if err != nil { panic(err) } case int32: err := binary.Write(fd, binary.BigEndian, data) if err != nil { panic(err) } // composite datatypes case string: write(fd, int32(len(data))) write(fd, []byte(data)) case []string: write(fd, int32(len(data))) for _, item := range data { write(fd, item) } case net.IP: var af int32 = -1 switch len(data) { case net.IPv4len: af = syscall.AF_INET case net.IPv6len: af = syscall.AF_INET6 } var bytes []byte if af < 0 { bytes = make([]byte, 0) } else { bytes = data } write(fd, af) write(fd, int32(len(bytes))) write(fd, bytes) case []net.IP: write(fd, int32(len(data))) for _, item := range data { write(fd, item) } default: v := reflect.ValueOf(data) switch v.Kind() { case reflect.Struct: for i, n := 0, v.NumField(); i < n; i++ { write(fd, v.Field(i).Interface()) } default: panic(fmt.Sprintf("Invalid structure to write NSLCD protocol data from: %T ( %#v )", data, data)) } } } func read(fd io.Reader, data interface{}) { switch data := data.(type) { // basic data types case NslcdObjectPtr: data.NslcdRead(fd) case *[]byte: _, err := fd.Read(*data) if err != nil { panic(err) } case *int32: err := binary.Read(fd, binary.BigEndian, data) if err != nil { panic(err) } // composite datatypes case *string: var len int32 read(fd, &len) buf := make([]byte, len) read(fd, &buf) *data = string(buf) case *[]string: var num int32 read(fd, &num) *data = make([]string, num) for i := 0; i < int(num); i++ { read(fd, &((*data)[i])) } case *net.IP: var af int32 read(fd, &af) var _len int32 switch af { case syscall.AF_INET: _len = net.IPv4len case syscall.AF_INET6: _len = net.IPv6len default: panic(NslcdError(fmt.Sprintf("incorrect address family specified: %d", af))) } var len int32 read(fd, &len) if len != _len { panic(NslcdError(fmt.Sprintf("address length incorrect: %d", len))) } buf := make([]byte, len) read(fd, &buf) *data = buf case *[]net.IP: var num int32 read(fd, &num) *data = make([]net.IP, num) for i := 0; i < int(num); i++ { read(fd, &((*data)[i])) } default: p := reflect.ValueOf(data) v := reflect.Indirect(p) if p == v || v.Kind() != reflect.Struct { panic(fmt.Sprintf("The argument to nslcd_proto.read() must be a pointer: %T ( %#v )", data, data)) } for i, n := 0, v.NumField(); i < n; i++ { read(fd, v.Field(i).Addr().Interface()) } } }