154 lines
2.8 KiB
Go
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
|
|
}
|