package gns import ( "encoding/hex" "fmt" "gnunet/enums" "gnunet/message" "github.com/bfix/gospel/crypto/ed25519" "github.com/bfix/gospel/logger" ) // HdlrInst is the type for functions that instanciate custom block handlers. type HdlrInst func(*message.GNSResourceRecord, []string) (BlockHandler, error) // Error codes var ( ErrInvalidRecordMix = fmt.Errorf("Invalid mix of RR types in block") ErrBlockHandler = fmt.Errorf("Internal block handler failure") ) // Mapping of RR types to BlockHandler instanciation functions var ( customHandler = map[int]HdlrInst{ enums.GNS_TYPE_PKEY: NewPkeyHandler, enums.GNS_TYPE_GNS2DNS: NewGns2DnsHandler, enums.GNS_TYPE_BOX: NewBoxHandler, enums.GNS_TYPE_LEHO: NewLehoHandler, } ) //====================================================================== // GNS blocks that contain special records (PKEY, GNS2DNS, BOX, LEHO...) // require special treatment with respect to other resource records with // different types in the same block. Usually only certain other types // (or none at all) are allowed. //====================================================================== // BlockHandler interface. type BlockHandler interface { // AddRecord inserts a RR into the BlockHandler for (later) processing. // The handler can inspect the remaining labels in a path if required. // It returns an error if a record is not accepted by the block handler. AddRecord(rr *message.GNSResourceRecord, labels []string) error // TypeAction returns a flag indicating how a resource record of a // given type is to be treated by a custom block handler: // = -1: Record is not allowed // = 0: Record is allowed but will be ignored // = 1: Record is allowed and will be processed TypeAction(t int) int // Records returns a list of RR of the given types associated with // the custom handler Records(kind RRTypeList) *GNSRecordSet } //---------------------------------------------------------------------- // Manage list of block handlers // Under normal circumstances there is only one (or none) block handler // per block, but future constructs may allow multiple block handlers // to be present. The block handler list implements the BlockHandler // interface. // The BlockHandlerList maintains a map of actually instantiated handlers // (indexed by record type) and a list of record types (with occurrence // count) in the block. //---------------------------------------------------------------------- // BlockHandlerList is a list of block handlers instantiated. type BlockHandlerList struct { list map[int]BlockHandler // list of handler instances } // NewBlockHandlerList instantiates an a list of active block handlers // for a given set of records (GNS block). func NewBlockHandlerList(records []*message.GNSResourceRecord, labels []string) (*BlockHandlerList, error) { // initialize block handler list hl := &BlockHandlerList{ list: make(map[int]BlockHandler), } // build a list of record types that are handled by a custom handler. rrList := NewRRTypeList( enums.GNS_TYPE_PKEY, enums.GNS_TYPE_GNS2DNS, enums.GNS_TYPE_BOX, enums.GNS_TYPE_LEHO) // Traverse record list and build list of handler instances for _, rec := range records { // check for custom handler type rrType := int(rec.Type) if rrList.HasType(rrType) { // check if a handler for given type already exists var ( hdlr BlockHandler ok bool err error ) if hdlr, ok = hl.list[rrType]; ok { // add record to existing handler if err = hdlr.AddRecord(rec, labels); err != nil { return nil, err } continue } // create a new handler instance switch rrType { case enums.GNS_TYPE_PKEY: hdlr, err = NewPkeyHandler(rec, labels) case enums.GNS_TYPE_GNS2DNS: hdlr, err = NewGns2DnsHandler(rec, labels) case enums.GNS_TYPE_BOX: hdlr, err = NewBoxHandler(rec, labels) case enums.GNS_TYPE_LEHO: hdlr, err = NewLehoHandler(rec, labels) } if err != nil { return nil, err } // store handler in list hl.list[rrType] = hdlr } } return hl, nil } // GetHandler returns a BlockHandler for the given key. If no block handler exists // under the given name, a new one is created and stored in the list. The type of // the new block handler is derived from the key value. func (hl *BlockHandlerList) GetHandler(t int) BlockHandler { // return handler for given key if it exists if hdlr, ok := hl.list[t]; ok { return hdlr } return nil } //---------------------------------------------------------------------- // PKEY handler: Only one PKEY as sole record in a block //---------------------------------------------------------------------- // PkeyHandler implementing the BlockHandler interface type PkeyHandler struct { pkey *ed25519.PublicKey // Zone key rec *message.GNSResourceRecord // associated recource record } // NewPkeyHandler returns a new BlockHandler instance func NewPkeyHandler(rec *message.GNSResourceRecord, labels []string) (BlockHandler, error) { if int(rec.Type) != enums.GNS_TYPE_PKEY { return nil, ErrInvalidRecordType } h := &PkeyHandler{ pkey: nil, } if err := h.AddRecord(rec, labels); err != nil { return nil, err } return h, nil } // AddRecord inserts a PKEY record into the handler. func (h *PkeyHandler) AddRecord(rec *message.GNSResourceRecord, labels []string) error { if int(rec.Type) != enums.GNS_TYPE_PKEY { return ErrInvalidRecordType } // check for sole PKEY record in block if h.pkey != nil { return ErrInvalidPKEY } // check for sane key data if len(rec.Data) != 32 { return ErrInvalidPKEY } // set a PKEY handler h.pkey = ed25519.NewPublicKeyFromBytes(rec.Data) h.rec = rec return nil } // TypeAction return a flag indicating how a resource record of a given type // is to be treated (see BlockHandler interface) func (h *PkeyHandler) TypeAction(t int) int { // no other resource record type is not allowed if t == enums.GNS_TYPE_PKEY { return 1 } return -1 } // Records returns a list of RR of the given type associated with this handler func (h *PkeyHandler) Records(kind RRTypeList) *GNSRecordSet { rs := NewGNSRecordSet() if kind.HasType(enums.GNS_TYPE_PKEY) { rs.AddRecord(h.rec) } return rs } //---------------------------------------------------------------------- // GNS2DNS handler //---------------------------------------------------------------------- // Gns2DnsHandler implementing the BlockHandler interface type Gns2DnsHandler struct { Name string // DNS query name Servers []string // DNS servers to ask recs []*message.GNSResourceRecord // list of rersource records } // NewGns2DnsHandler returns a new BlockHandler instance func NewGns2DnsHandler(rec *message.GNSResourceRecord, labels []string) (BlockHandler, error) { if int(rec.Type) != enums.GNS_TYPE_GNS2DNS { return nil, ErrInvalidRecordType } h := &Gns2DnsHandler{ Name: "", Servers: make([]string, 0), recs: make([]*message.GNSResourceRecord, 0), } if err := h.AddRecord(rec, labels); err != nil { return nil, err } return h, nil } // AddRecord inserts a GNS2DNS record into the handler. func (h *Gns2DnsHandler) AddRecord(rec *message.GNSResourceRecord, labels []string) error { if int(rec.Type) != enums.GNS_TYPE_GNS2DNS { return ErrInvalidRecordType } logger.Printf(logger.DBG, "[gns] GNS2DNS data: %s\n", hex.EncodeToString(rec.Data)) // extract list of names in DATA block: next, dnsQuery := DNSNameFromBytes(rec.Data, 0) dnsServer := string(rec.Data[next : len(rec.Data)-1]) logger.Printf(logger.DBG, "[gns] GNS2DNS query '%s'@'%s'\n", dnsQuery, dnsServer) if len(dnsServer) == 0 || len(dnsQuery) == 0 { return ErrInvalidRecordBody } // check if all GNS2DNS records refer to the same query name if len(h.Servers) == 0 { h.Name = dnsQuery } if dnsQuery != h.Name { return ErrInvalidRecordBody } h.Servers = append(h.Servers, dnsServer) h.recs = append(h.recs, rec) return nil } // TypeAction return a flag indicating how a resource record of a given type // is to be treated (see BlockHandler interface) func (h *Gns2DnsHandler) TypeAction(t int) int { // anything goes... return 1 } // Records returns a list of RR of the given type associated with this handler func (h *Gns2DnsHandler) Records(kind RRTypeList) *GNSRecordSet { rs := NewGNSRecordSet() if kind.HasType(enums.GNS_TYPE_GNS2DNS) { for _, rec := range h.recs { rs.AddRecord(rec) } } return rs } //---------------------------------------------------------------------- // BOX handler //---------------------------------------------------------------------- // BoxHandler implementing the BlockHandler interface type BoxHandler struct { boxes map[string]*Box // map of found boxes } // NewBoxHandler returns a new BlockHandler instance func NewBoxHandler(rec *message.GNSResourceRecord, labels []string) (BlockHandler, error) { if int(rec.Type) != enums.GNS_TYPE_BOX { return nil, ErrInvalidRecordType } h := &BoxHandler{ boxes: make(map[string]*Box), } if err := h.AddRecord(rec, labels); err != nil { return nil, err } return h, nil } // AddRecord inserts a BOX record into the handler. func (h *BoxHandler) AddRecord(rec *message.GNSResourceRecord, labels []string) error { if int(rec.Type) != enums.GNS_TYPE_BOX { return ErrInvalidRecordType } logger.Printf(logger.DBG, "[box-rr] for labels %v\n", labels) // check if we need to process the BOX record: // (1) only two remaining labels if len(labels) != 2 { return nil } // (2) remaining labels must start with '_' if labels[0][0] != '_' || labels[1][0] != '_' { return nil } // (3) check of "svc" and "proto" match values in the BOX box := NewBox(rec) if box.Matches(labels) { logger.Println(logger.DBG, "[box-rr] MATCH -- adding record") h.boxes[box.key] = box } return nil } // TypeAction return a flag indicating how a resource record of a given type // is to be treated (see BlockHandler interface) func (h *BoxHandler) TypeAction(t int) int { // anything goes... return 1 } // Records returns a list of RR of the given type associated with this handler func (h *BoxHandler) Records(kind RRTypeList) *GNSRecordSet { rs := NewGNSRecordSet() for _, box := range h.boxes { if kind.HasType(int(box.Type)) { // valid box found: assemble new resource record. rr := new(message.GNSResourceRecord) rr.Expires = box.rec.Expires rr.Flags = box.rec.Flags rr.Type = box.Type rr.Size = uint32(len(box.RR)) rr.Data = box.RR rs.AddRecord(rr) } } return rs } //---------------------------------------------------------------------- // LEHO handler //---------------------------------------------------------------------- // LehoHandler implementing the BlockHandler interface type LehoHandler struct { name string rec *message.GNSResourceRecord } // NewLehoHandler returns a new BlockHandler instance func NewLehoHandler(rec *message.GNSResourceRecord, labels []string) (BlockHandler, error) { if int(rec.Type) != enums.GNS_TYPE_LEHO { return nil, ErrInvalidRecordType } h := &LehoHandler{ name: "", } if err := h.AddRecord(rec, labels); err != nil { return nil, err } return h, nil } // AddRecord inserts a LEHO record into the handler. func (h *LehoHandler) AddRecord(rec *message.GNSResourceRecord, labels []string) error { if int(rec.Type) != enums.GNS_TYPE_LEHO { return ErrInvalidRecordType } h.name = string(rec.Data) h.rec = rec return nil } // TypeAction return a flag indicating how a resource record of a given type // is to be treated (see BlockHandler interface) func (h *LehoHandler) TypeAction(t int) int { // only A and AAAA records allowed beside LEHO switch t { case enums.GNS_TYPE_LEHO, enums.GNS_TYPE_DNS_A, enums.GNS_TYPE_DNS_AAAA: return 1 default: return -1 } } // Records returns a list of RR of the given type associated with this handler func (h *LehoHandler) Records(kind RRTypeList) *GNSRecordSet { rs := NewGNSRecordSet() if kind.HasType(enums.GNS_TYPE_LEHO) { rs.AddRecord(h.rec) } return rs }