// Copyright (C) 2015 Luke Shumaker // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA package nslcd_proto import ( "encoding/binary" "fmt" "io" "net" "reflect" "sync" "syscall" ) type NslcdError string func (o NslcdError) Error() string { return string(o) } type nslcdObject interface { nslcdWrite(fd io.Writer) } type nslcdObjectPtr interface { nslcdRead(fd io.Reader) } type String []byte func (s String) String() string { return string(s) } func (s String) GoString() string { return fmt.Sprintf("nslcd_proto.String(%#v)", string(s)) } func (data String) nslcdWrite(fd io.Writer) { Write(fd, int32(len(data))) Write(fd, []byte(data)) } func (data *String) nslcdRead(fd io.Reader) { var len int32 Read(fd, &len) buf := makeBytes(len) Read(fd, &buf) *data = String(buf) } // Write an object to a stream. In the event of an error, this // function may panic! Handle it! func Write(fd io.Writer, data interface{}) { switch data := data.(type) { // basic data types case nslcdObject: data.nslcdWrite(fd) case []byte: if len(data) > 0 { _, 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))) 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 = makeBytes(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)) } } } // Read an object from a stream. In the event of an error, this // function may panic! Handle it! func Read(fd io.Reader, data interface{}) { switch data := data.(type) { // basic data types case nslcdObjectPtr: data.nslcdRead(fd) case *[]byte: if len(*data) > 0 { _, err := io.ReadFull(fd, *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 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 := makeBytes(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()) } } } // "Free" an object and its members, returning them to various pools. // This keeps the GC sane. func Free(data interface{}) { switch data := data.(type) { case *[]byte: bytePool.Put(*data) case *int32: // do nothing case *String: bytes := []byte(*data) bytePool.Put(bytes) case *[]String: for _, str := range *data { Free(&str) } case *net.IP: bytes := []byte(*data) bytePool.Put(bytes) case *[]net.IP: for _, ip := range *data { Free(&ip) } default: p := reflect.ValueOf(data) v := reflect.Indirect(p) if p == v || v.Kind() != reflect.Struct { panic(fmt.Sprintf("The argument to nslcd_proto.Free() must be a pointer: %T ( %#v )", data, data)) } for i, n := 0, v.NumField(); i < n; i++ { Free(v.Field(i).Addr().Interface()) } } } var bytePool = sync.Pool{ New: func() interface{} { return make([]byte, 0, 256) }, } func makeBytes(l int32) []byte { buf := bytePool.Get().([]byte) if cap(buf) < int(l) { bytePool.Put(buf) buf = make([]byte, l) } return buf[:l] }