2018-05-11 04:08:20 +00:00
|
|
|
package control
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/cretz/bine/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
type EventCode string
|
|
|
|
|
|
|
|
const (
|
|
|
|
EventCodeAddrMap EventCode = "ADDRMAP"
|
|
|
|
EventCodeCirc EventCode = "CIRC"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (c *Conn) AddEventListener(events []EventCode, ch chan<- Event) error {
|
|
|
|
// TODO: do we want to set the local map first? Or do we want to lock on the net request too?
|
|
|
|
c.eventListenersLock.Lock()
|
|
|
|
for _, event := range events {
|
|
|
|
// Must completely replace the array, never mutate it
|
|
|
|
prevArr := c.eventListeners[event]
|
|
|
|
newArr := make([]chan<- Event, len(prevArr)+1)
|
|
|
|
copy(newArr, prevArr)
|
|
|
|
newArr[len(newArr)-1] = ch
|
|
|
|
c.eventListeners[event] = newArr
|
|
|
|
}
|
|
|
|
c.eventListenersLock.Unlock()
|
|
|
|
return c.sendSetEvents()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) RemoveEventListener(events []EventCode, ch chan<- Event) error {
|
|
|
|
// TODO: do we want to mutate the local map first?
|
|
|
|
c.eventListenersLock.Lock()
|
|
|
|
for _, event := range events {
|
|
|
|
arr := c.eventListeners[event]
|
|
|
|
index := -1
|
|
|
|
for i, listener := range arr {
|
|
|
|
if listener == ch {
|
|
|
|
index = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if index != -1 {
|
|
|
|
if len(arr) == 1 {
|
|
|
|
delete(c.eventListeners, event)
|
|
|
|
} else {
|
|
|
|
// Must completely replace the array, never mutate it
|
|
|
|
newArr := make([]chan<- Event, len(arr)-1)
|
|
|
|
copy(newArr, arr[:index])
|
|
|
|
copy(newArr[index:], arr[index+1:])
|
|
|
|
c.eventListeners[event] = newArr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.eventListenersLock.Unlock()
|
|
|
|
return c.sendSetEvents()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) sendSetEvents() error {
|
|
|
|
c.eventListenersLock.RLock()
|
|
|
|
cmd := "SETEVENTS"
|
|
|
|
for event := range c.eventListeners {
|
|
|
|
cmd += " " + string(event)
|
|
|
|
}
|
|
|
|
c.eventListenersLock.RUnlock()
|
2018-05-11 06:28:31 +00:00
|
|
|
return c.sendRequestIgnoreResponse(cmd)
|
2018-05-11 04:08:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// zero on fail
|
|
|
|
func parseISOTime2Frac(str string) time.Time {
|
|
|
|
// Essentially time.RFC3339Nano but without TZ info
|
|
|
|
const layout = "2006-01-02T15:04:05.999999999"
|
|
|
|
ret, err := time.Parse(layout, str)
|
|
|
|
if err != nil {
|
|
|
|
ret = time.Time{}
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
type CircuitEvent struct {
|
|
|
|
CircuitID string
|
|
|
|
Status string
|
|
|
|
Path []string
|
|
|
|
BuildFlags []string
|
|
|
|
Purpose string
|
|
|
|
HSState string
|
|
|
|
RendQuery string
|
|
|
|
TimeCreated time.Time
|
|
|
|
Reason string
|
|
|
|
RemoteReason string
|
|
|
|
SocksUsername string
|
|
|
|
SocksPassword string
|
|
|
|
Raw string
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseCircuitEvent(raw string) *CircuitEvent {
|
|
|
|
event := &CircuitEvent{Raw: raw}
|
|
|
|
event.CircuitID, raw, _ = util.PartitionString(raw, ' ')
|
|
|
|
var ok bool
|
|
|
|
event.Status, raw, ok = util.PartitionString(raw, ' ')
|
|
|
|
var attr string
|
|
|
|
first := true
|
|
|
|
for ok {
|
|
|
|
if attr, raw, ok = util.PartitionString(raw, ' '); !ok {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
key, val, _ := util.PartitionString(attr, '=')
|
|
|
|
switch key {
|
|
|
|
case "BUILD_FLAGS":
|
|
|
|
event.BuildFlags = strings.Split(val, ",")
|
|
|
|
case "PURPOSE":
|
|
|
|
event.Purpose = val
|
|
|
|
case "HS_STATE":
|
|
|
|
event.HSState = val
|
|
|
|
case "REND_QUERY":
|
|
|
|
event.RendQuery = val
|
|
|
|
case "TIME_CREATED":
|
|
|
|
event.TimeCreated = parseISOTime2Frac(val)
|
|
|
|
case "REASON":
|
|
|
|
event.Reason = val
|
|
|
|
case "REMOTE_REASON":
|
|
|
|
event.RemoteReason = val
|
|
|
|
case "SOCKS_USERNAME":
|
|
|
|
event.SocksUsername = val
|
|
|
|
case "SOCKS_PASSWORD":
|
|
|
|
event.SocksPassword = val
|
|
|
|
default:
|
|
|
|
if first {
|
|
|
|
event.Path = strings.Split(val, ",")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
first = false
|
|
|
|
}
|
|
|
|
return event
|
|
|
|
}
|
|
|
|
|
|
|
|
type Event interface {
|
|
|
|
Code() EventCode
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*CircuitEvent) Code() EventCode { return EventCodeCirc }
|
|
|
|
|
|
|
|
func (c *Conn) relayAsyncEvents(resp *Response) {
|
|
|
|
code, data, _ := util.PartitionString(resp.Reply, ' ')
|
|
|
|
// Only relay if there are chans
|
|
|
|
c.eventListenersLock.RLock()
|
|
|
|
chans := c.eventListeners[EventCode(code)]
|
|
|
|
c.eventListenersLock.RUnlock()
|
|
|
|
if len(chans) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Parse the event
|
|
|
|
// TODO: more events
|
|
|
|
var event Event
|
|
|
|
switch EventCode(code) {
|
|
|
|
case EventCodeCirc:
|
|
|
|
event = ParseCircuitEvent(data)
|
|
|
|
}
|
|
|
|
if event != nil {
|
|
|
|
for _, ch := range chans {
|
|
|
|
// Just send, if closed or blocking, that's not our problem
|
|
|
|
ch <- event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|