GNUnet P2P protocol specification
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

398 lines
12 KiB

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
}