badguardhome/internal/storage/logs/logfile.go
2022-06-03 10:31:31 -05:00

154 lines
2.8 KiB
Go

package logfile
import (
"bytes"
"net"
"os"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
"github.com/bits-and-blooms/bloom"
)
type LogStorage struct {
}
type LogBlock struct {
}
type ShapeStorage struct {
blocks []ShapeBlock
shapeCache map[string][8]byte
}
func (s *ShapeStorage) FindOrAdd(k []byte) [8]byte {
if len(k) <= 8 {
o := [8]byte{}
for i, v := range k {
o[i] = v
}
return o
}
return [8]byte{}
}
type ShapeBlock struct {
filter *bloom.BloomFilter
fp *os.File
}
func (b *ShapeBlock) Open(name string) (err error) {
_, err = b.fp.Stat()
if os.IsNotExist(err) {
b.fp, err = os.Create(name)
if err != nil {
return err
}
} else if err == nil {
b.fp, err = os.OpenFile(name, os.O_RDWR, 0o644)
if err != nil {
return err
}
} else {
return err
}
return
}
var magic = [8]byte{0xfe, 0xed, 0xbe, 0xef, 0x69, 0x00, 0x00, 0x00}
const BLOCK_SIZE = 1600
const BLOOM_SIZE_BITS = 500 * 64
const BLOOM_SIZE_BYTES = BLOOM_SIZE_BITS / 8
const BLOOM_SIZE_TOTAL = 8 + 8 + 8 + BLOOM_SIZE_BYTES
func (b *ShapeBlock) WriteTo() (err error) {
if b.filter == nil {
b.filter = bloom.New(BLOOM_SIZE_BITS, 14)
}
_, err = b.fp.WriteAt(magic[:], 0)
bf := new(bytes.Buffer)
b.filter.WriteTo(bf)
n, err := b.fp.WriteAt(bf.Bytes(), 8)
if err != nil {
return err
}
if n != BLOOM_SIZE_TOTAL {
panic("invalid bloom header size")
}
return err
}
func (b *ShapeBlock) ReadHeader() (err error) {
prefix := make([]byte, 8)
_, err = b.fp.ReadAt(prefix, 0)
if err != nil {
return err
}
bloom := make([]byte, BLOOM_SIZE_TOTAL)
_, err = b.fp.ReadAt(bloom, 8)
if err != nil {
return err
}
_, err = b.filter.ReadFrom(bytes.NewBuffer(bloom))
if err != nil {
return err
}
return nil
}
func (b *ShapeBlock) Add(k []byte) int {
if uint(len(b.keys)+1) >= b.filter.Cap() {
return -1
}
if b.Find(k) == -1 {
b.filter.Add(k)
return len(b.keys)
}
return -1
}
func (b *ShapeBlock) Find(k []byte) [8]byte {
if !b.filter.Test(k) {
return -1
}
for idx, v := range b.keys {
if len(k) != len(v) {
continue
}
for i := range v {
if v[i] != k[i] {
continue
}
}
return idx
}
return -1
}
type StorageEntry struct {
Block int64
}
// represents a single log entry in storage
type LogEntry struct {
IP net.IP `json:"IP"` // Client IP
Time time.Time `json:"T"`
QHost string `json:"QH"`
QType string `json:"QT"`
QClass string `json:"QC"`
ClientID string `json:"CID,omitempty"`
ClientProto querylog.ClientProto `json:"CP"`
Answer []byte `json:",omitempty"` // sometimes empty answers happen like binerdunt.top or rev2.globalrootservers.net
OrigAnswer []byte `json:",omitempty"`
Result filtering.Result
Elapsed time.Duration
Upstream string `json:",omitempty"` // if empty, means it was cached
}