/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
)
const (
- MaxSPSize = 2<<16 - 256
+ MaxSPSize = 1<<16 - 256
PartSuffix = ".part"
DefaultDeadline = 10
)
noise.CipherChaChaPoly,
noise.HashBLAKE2b,
)
+
+ spWorkersGroup sync.WaitGroup
)
type SPType uint8
type SPState struct {
ctx *Ctx
Node *Node
- onlineDeadline int
+ onlineDeadline uint
+ maxOnlineTime uint
nice uint8
hs *noise.HandshakeState
csOur *noise.CipherState
TxSpeed int64
rxLock *os.File
txLock *os.File
- xxOnly *TRxTx
+ xxOnly TRxTx
isDead bool
sync.RWMutex
}
func (state *SPState) NotAlive() bool {
+ if state.isDead {
+ return true
+ }
now := time.Now()
- return state.isDead || (int(now.Sub(state.RxLastSeen).Seconds()) >= state.onlineDeadline && int(now.Sub(state.TxLastSeen).Seconds()) >= state.onlineDeadline)
+ if state.maxOnlineTime > 0 && state.started.Add(time.Duration(state.maxOnlineTime)*time.Second).Before(now) {
+ return true
+ }
+ return uint(now.Sub(state.RxLastSeen).Seconds()) >= state.onlineDeadline && uint(now.Sub(state.TxLastSeen).Seconds()) >= state.onlineDeadline
}
func (state *SPState) dirUnlock() {
func (state *SPState) ReadSP(src io.Reader) ([]byte, error) {
var sp SPRaw
- n, err := xdr.UnmarshalLimited(src, &sp, 2<<17)
+ n, err := xdr.UnmarshalLimited(src, &sp, 1<<17)
if err != nil {
return nil, err
}
return payloadsSplit(payloads)
}
-func (ctx *Ctx) StartI(conn net.Conn, nodeId *NodeId, nice uint8, xxOnly *TRxTx, onlineDeadline int) (*SPState, error) {
+func (ctx *Ctx) StartI(conn net.Conn, nodeId *NodeId, nice uint8, xxOnly TRxTx, onlineDeadline, maxOnlineTime uint) (*SPState, error) {
err := ctx.ensureRxDir(nodeId)
if err != nil {
return nil, err
}
var rxLock *os.File
- if xxOnly != nil && *xxOnly == TRx {
+ if xxOnly == "" || xxOnly == TRx {
rxLock, err = ctx.LockDir(nodeId, TRx)
if err != nil {
return nil, err
}
}
var txLock *os.File
- if xxOnly != nil && *xxOnly == TTx {
+ if xxOnly == "" || xxOnly == TTx {
txLock, err = ctx.LockDir(nodeId, TTx)
if err != nil {
return nil, err
},
PeerStatic: node.NoisePub[:],
}
+ hs, err := noise.NewHandshakeState(conf)
+ if err != nil {
+ return nil, err
+ }
state := SPState{
ctx: ctx,
- hs: noise.NewHandshakeState(conf),
+ hs: hs,
Node: node,
onlineDeadline: onlineDeadline,
+ maxOnlineTime: maxOnlineTime,
nice: nice,
payloads: make(chan []byte),
infosTheir: make(map[[32]byte]*SPInfo),
}
var infosPayloads [][]byte
- if xxOnly == nil || *xxOnly != TTx {
+ if xxOnly == "" || xxOnly == TTx {
infosPayloads = ctx.infosOur(nodeId, nice, &state.infosOurSeen)
}
var firstPayload []byte
var buf []byte
var payload []byte
- buf, _, _ = state.hs.WriteMessage(nil, firstPayload)
+ buf, _, _, err = state.hs.WriteMessage(nil, firstPayload)
+ if err != nil {
+ state.dirUnlock()
+ return nil, err
+ }
sds := SDS{"node": nodeId, "nice": strconv.Itoa(int(nice))}
ctx.LogD("sp-start", sds, "sending first message")
conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
return &state, err
}
-func (ctx *Ctx) StartR(conn net.Conn, nice uint8, xxOnly *TRxTx) (*SPState, error) {
+func (ctx *Ctx) StartR(conn net.Conn, nice uint8, xxOnly TRxTx) (*SPState, error) {
started := time.Now()
conf := noise.Config{
CipherSuite: NoiseCipherSuite,
Public: ctx.Self.NoisePub[:],
},
}
+ hs, err := noise.NewHandshakeState(conf)
+ if err != nil {
+ return nil, err
+ }
state := SPState{
ctx: ctx,
- hs: noise.NewHandshakeState(conf),
+ hs: hs,
nice: nice,
payloads: make(chan []byte),
infosOurSeen: make(map[[32]byte]struct{}),
}
var buf []byte
var payload []byte
- var err error
ctx.LogD(
"sp-start",
SDS{"nice": strconv.Itoa(int(nice))},
}
state.Node = node
state.onlineDeadline = node.OnlineDeadline
+ state.maxOnlineTime = node.MaxOnlineTime
sds := SDS{"node": node.Id, "nice": strconv.Itoa(int(nice))}
if ctx.ensureRxDir(node.Id); err != nil {
return nil, err
}
var rxLock *os.File
- if xxOnly != nil && *xxOnly == TRx {
+ if xxOnly == "" || xxOnly == TRx {
rxLock, err = ctx.LockDir(node.Id, TRx)
if err != nil {
return nil, err
}
state.rxLock = rxLock
var txLock *os.File
- if xxOnly != nil && *xxOnly == TTx {
+ if xxOnly == "" || xxOnly == TTx {
txLock, err = ctx.LockDir(node.Id, TTx)
if err != nil {
return nil, err
state.txLock = txLock
var infosPayloads [][]byte
- if xxOnly == nil || *xxOnly != TTx {
+ if xxOnly == "" || xxOnly == TTx {
infosPayloads = ctx.infosOur(node.Id, nice, &state.infosOurSeen)
}
var firstPayload []byte
}
ctx.LogD("sp-start", sds, "sending first message")
- buf, state.csTheir, state.csOur = state.hs.WriteMessage(nil, firstPayload)
+ buf, state.csTheir, state.csOur, err = state.hs.WriteMessage(nil, firstPayload)
+ if err != nil {
+ state.dirUnlock()
+ return nil, err
+ }
conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
if err = state.WriteSP(conn, buf); err != nil {
ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
}
}()
- go func() {
- for range time.Tick(time.Second) {
- for _, payload := range state.ctx.infosOur(
- state.Node.Id,
- state.nice,
- &state.infosOurSeen,
- ) {
- state.ctx.LogD(
- "sp-work",
- SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
- "queuing new info",
- )
- state.payloads <- payload
+ if state.xxOnly == "" || state.xxOnly == TTx {
+ go func() {
+ for range time.Tick(time.Second) {
+ for _, payload := range state.ctx.infosOur(
+ state.Node.Id,
+ state.nice,
+ &state.infosOurSeen,
+ ) {
+ state.ctx.LogD(
+ "sp-work",
+ SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
+ "queuing new info",
+ )
+ state.payloads <- payload
+ }
}
- }
- }()
+ }()
+ }
state.wg.Add(1)
go func() {
var buf []byte
if freq.Offset < fullSize {
state.ctx.LogD("sp-file", sdsp, "seeking")
- if _, err = fd.Seek(int64(freq.Offset), io.SeekStart); err != nil {
+ if _, err = fd.Seek(int64(freq.Offset), 0); err != nil {
state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
break
}
continue
}
state.ctx.LogD("sp-process", sdsp, "received")
- if state.xxOnly != nil && *state.xxOnly == TTx {
+ if state.xxOnly == TTx {
continue
}
state.Lock()
SdsAdd(sdsp, SDS{"offset": strconv.FormatInt(int64(file.Offset), 10)}),
"seeking",
)
- if _, err = fd.Seek(int64(file.Offset), io.SeekStart); err != nil {
+ if _, err = fd.Seek(int64(file.Offset), 0); err != nil {
state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
fd.Close()
return nil, err
return nil, err
}
ourSize := uint64(file.Offset) + uint64(len(file.Payload))
+ state.RLock()
sdsp["fullsize"] = strconv.FormatInt(int64(state.infosTheir[*file.Hash].Size), 10)
sdsp["size"] = strconv.FormatInt(int64(ourSize), 10)
state.ctx.LogP("sp-file", sdsp, "")
if state.infosTheir[*file.Hash].Size != ourSize {
+ state.RUnlock()
fd.Close()
continue
}
+ state.RUnlock()
+ spWorkersGroup.Wait()
+ spWorkersGroup.Add(1)
go func() {
if err := fd.Sync(); err != nil {
state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "sync")
}
state.wg.Add(1)
defer state.wg.Done()
- fd.Seek(0, io.SeekStart)
+ fd.Seek(0, 0)
state.ctx.LogD("sp-file", sdsp, "checking")
gut, err := Check(fd, file.Hash[:])
fd.Close()
}
state.ctx.LogI("sp-done", SdsAdd(sdsp, SDS{"xx": string(TRx)}), "")
os.Rename(filePath+PartSuffix, filePath)
+ state.Lock()
+ delete(state.infosTheir, *file.Hash)
+ state.Unlock()
+ spWorkersGroup.Done()
go func() {
state.payloads <- MarshalSP(SPTypeDone, SPDone{file.Hash})
}()
if infosGot {
var pkts int
var size uint64
+ state.RLock()
for _, info := range state.infosTheir {
pkts++
size += info.Size
}
+ state.RUnlock()
state.ctx.LogI("sp-infos", SDS{
"xx": string(TRx),
"node": state.Node.Id,