115 lines
3.1 KiB
Go
115 lines
3.1 KiB
Go
|
package zonecut
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"github.com/miekg/dns"
|
||
|
"io"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"github.com/ANSSI-FR/transdep/errors"
|
||
|
)
|
||
|
|
||
|
// CACHE_DIRNAME is the name of the directory under the cache root directory for storage of zone cut cache files
|
||
|
const CACHE_DIRNAME = "zonecut"
|
||
|
|
||
|
// CreateCacheDir creates the cache dir for storage of zone cut cache files.
|
||
|
// It may return an error if the directory cannot be created. If the directory already exists, this function does
|
||
|
// nothing.
|
||
|
func CreateCacheDir(cacheRootDir string) error {
|
||
|
if err := os.MkdirAll(cacheRootDir+string(os.PathSeparator)+CACHE_DIRNAME, 0700); !os.IsExist(err) {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// CacheFile represents just that.
|
||
|
type CacheFile struct {
|
||
|
name string
|
||
|
}
|
||
|
|
||
|
// NewCacheFile initializes a new CacheFile struct, based on the cache root dir, and the name of the domain that is the
|
||
|
// subject of this cache file.
|
||
|
func NewCacheFile(cacheRootDir string, topic RequestTopic) *CacheFile {
|
||
|
buf := new(bytes.Buffer)
|
||
|
buf.WriteString(cacheRootDir)
|
||
|
buf.WriteRune(os.PathSeparator)
|
||
|
buf.WriteString(CACHE_DIRNAME)
|
||
|
buf.WriteRune(os.PathSeparator)
|
||
|
buf.WriteString("zcf-")
|
||
|
buf.WriteString(strings.ToLower(dns.Fqdn(topic.Domain)))
|
||
|
buf.WriteString("-")
|
||
|
if topic.Exceptions.RFC8020 {
|
||
|
buf.WriteString("1")
|
||
|
} else {
|
||
|
buf.WriteString("0")
|
||
|
}
|
||
|
if topic.Exceptions.AcceptServFailAsNoData {
|
||
|
buf.WriteString("1")
|
||
|
} else {
|
||
|
buf.WriteString("0")
|
||
|
}
|
||
|
fileName := buf.String()
|
||
|
|
||
|
cf := &CacheFile{fileName}
|
||
|
return cf
|
||
|
}
|
||
|
|
||
|
// NewCacheFile initializes a new CacheFile struct and ensures that this file exists or else returns an error.
|
||
|
func NewExistingCacheFile(cacheRootDir string, topic RequestTopic) (*CacheFile, error) {
|
||
|
cf := NewCacheFile(cacheRootDir, topic)
|
||
|
fd, err := os.Open(cf.name)
|
||
|
defer fd.Close()
|
||
|
return cf, err
|
||
|
}
|
||
|
|
||
|
/* Result returns the entry or the error that were stored in the cache file. An error may also be returned, if an
|
||
|
incident happens during retrieval/interpretation of the cache file.
|
||
|
|
||
|
entry is the entry that was stored in the cache file.
|
||
|
|
||
|
resultError is the resolution error that was stored in the cache file.
|
||
|
|
||
|
err is the error that may happen during retrieval of the value in the cache file.
|
||
|
*/
|
||
|
func (cf *CacheFile) Result() (entry *Entry, resultError *errors.ErrorStack, err error) {
|
||
|
fd, err := os.Open(cf.name)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
defer fd.Close()
|
||
|
|
||
|
buffedFd := bufio.NewReader(fd)
|
||
|
jsonbstr, err := buffedFd.ReadBytes('\x00')
|
||
|
if err != nil && err != io.EOF {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
res := new(result)
|
||
|
err = json.Unmarshal(jsonbstr, res)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
return res.Result, res.Err, nil
|
||
|
}
|
||
|
|
||
|
// SetResult writes in the cache file the provided entry or error. An error is returned if an incident happens or else
|
||
|
// nil is returned.
|
||
|
func (cf *CacheFile) SetResult(entry *Entry, resultErr *errors.ErrorStack) error {
|
||
|
var jsonRepr []byte
|
||
|
var err error
|
||
|
var fd *os.File
|
||
|
|
||
|
if jsonRepr, err = json.Marshal(&result{entry, resultErr}); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if fd, err = os.OpenFile(cf.name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
fd.WriteString(string(jsonRepr))
|
||
|
fd.Close()
|
||
|
return nil
|
||
|
}
|