diff options
author | kali kaneko (leap communications) <kali@leap.se> | 2021-11-29 01:46:27 +0100 |
---|---|---|
committer | kali kaneko (leap communications) <kali@leap.se> | 2021-11-29 18:14:16 +0100 |
commit | 18f52af5be3a9a0c73811706108f790d65ee9c67 (patch) | |
tree | e13cbacb47d56919caa9c44a2b45dec1497a7860 /vendor/github.com/cretz/bine/control/cmd_event.go | |
parent | ebcef0d57b6ecb5a40c6579f6be07182dd3033ba (diff) |
[pkg] update vendor
Diffstat (limited to 'vendor/github.com/cretz/bine/control/cmd_event.go')
-rw-r--r-- | vendor/github.com/cretz/bine/control/cmd_event.go | 1218 |
1 files changed, 1218 insertions, 0 deletions
diff --git a/vendor/github.com/cretz/bine/control/cmd_event.go b/vendor/github.com/cretz/bine/control/cmd_event.go new file mode 100644 index 0000000..578a800 --- /dev/null +++ b/vendor/github.com/cretz/bine/control/cmd_event.go @@ -0,0 +1,1218 @@ +package control + +import ( + "context" + "errors" + "strconv" + "strings" + "time" + + "github.com/cretz/bine/torutil" +) + +// EventCode represents an asynchronous event code (ref control spec 4.1). +type EventCode string + +// Event codes +const ( + EventCodeAddrMap EventCode = "ADDRMAP" + EventCodeBandwidth EventCode = "BW" + EventCodeBuildTimeoutSet EventCode = "BUILDTIMEOUT_SET" + EventCodeCellStats EventCode = "CELL_STATS" + EventCodeCircuit EventCode = "CIRC" + EventCodeCircuitBandwidth EventCode = "CIRC_BW" + EventCodeCircuitMinor EventCode = "CIRC_MINOR" + EventCodeClientsSeen EventCode = "CLIENTS_SEEN" + EventCodeConfChanged EventCode = "CONF_CHANGED" + EventCodeConnBandwidth EventCode = "CONN_BW" + EventCodeDescChanged EventCode = "DESCCHANGED" + EventCodeGuard EventCode = "GUARD" + EventCodeHSDesc EventCode = "HS_DESC" + EventCodeHSDescContent EventCode = "HS_DESC_CONTENT" + EventCodeLogDebug EventCode = "DEBUG" + EventCodeLogErr EventCode = "ERR" + EventCodeLogInfo EventCode = "INFO" + EventCodeLogNotice EventCode = "NOTICE" + EventCodeLogWarn EventCode = "WARN" + EventCodeNetworkLiveness EventCode = "NETWORK_LIVENESS" + EventCodeNetworkStatus EventCode = "NS" + EventCodeNewConsensus EventCode = "NEWCONSENSUS" + EventCodeNewDesc EventCode = "NEWDESC" + EventCodeORConn EventCode = "ORCONN" + EventCodeSignal EventCode = "SIGNAL" + EventCodeStatusClient EventCode = "STATUS_CLIENT" + EventCodeStatusGeneral EventCode = "STATUS_GENERAL" + EventCodeStatusServer EventCode = "STATUS_SERVER" + EventCodeStream EventCode = "STREAM" + EventCodeStreamBandwidth EventCode = "STREAM_BW" + EventCodeTokenBucketEmpty EventCode = "TB_EMPTY" + EventCodeTransportLaunched EventCode = "TRANSPORT_LAUNCHED" +) + +// EventCodeUnrecognized is a special event code that is only used with +// AddEventListener and RemoveEventListener to listen for events that are +// unrecognized by this library (i.e. UnrecognizedEvent). +var EventCodeUnrecognized EventCode = "<unrecognized>" + +var recognizedEventCodes = []EventCode{ + EventCodeAddrMap, + EventCodeBandwidth, + EventCodeBuildTimeoutSet, + EventCodeCellStats, + EventCodeCircuit, + EventCodeCircuitBandwidth, + EventCodeCircuitMinor, + EventCodeClientsSeen, + EventCodeConfChanged, + EventCodeConnBandwidth, + EventCodeDescChanged, + EventCodeGuard, + EventCodeHSDesc, + EventCodeHSDescContent, + EventCodeLogDebug, + EventCodeLogErr, + EventCodeLogInfo, + EventCodeLogNotice, + EventCodeLogWarn, + EventCodeNetworkLiveness, + EventCodeNetworkStatus, + EventCodeNewConsensus, + EventCodeNewDesc, + EventCodeORConn, + EventCodeSignal, + EventCodeStatusClient, + EventCodeStatusGeneral, + EventCodeStatusServer, + EventCodeStream, + EventCodeStreamBandwidth, + EventCodeTokenBucketEmpty, + EventCodeTransportLaunched, +} + +var recognizedEventCodesByCode = mapEventCodes() + +func mapEventCodes() map[EventCode]struct{} { + ret := make(map[EventCode]struct{}, len(recognizedEventCodes)) + for _, eventCode := range recognizedEventCodes { + ret[eventCode] = struct{}{} + } + return ret +} + +// EventCodes returns a new slice of all event codes that are recognized (i.e. +// does not include EventCodeUnrecognized). +func EventCodes() []EventCode { + ret := make([]EventCode, len(recognizedEventCodes)) + copy(ret, recognizedEventCodes) + return ret +} + +// ErrEventWaitSynchronousResponseOccurred is returned from EventWait (see docs) +var ErrEventWaitSynchronousResponseOccurred = errors.New("Synchronous event occurred during EventWait") + +// EventWait waits for the predicate to be satisified or a non-event message to +// come through. If a non-event comes through, the error +// ErrEventWaitSynchronousResponseOccurred is returned. If there is an error in +// the predicate or if the context completes or there is an error internally +// handling the event, the error is returned. Otherwise, the event that true was +// returned from the predicate for is returned. +func (c *Conn) EventWait( + ctx context.Context, events []EventCode, predicate func(Event) (bool, error), +) (Event, error) { + eventCh := make(chan Event, 10) + defer close(eventCh) + if err := c.AddEventListener(eventCh, events...); err != nil { + return nil, err + } + defer c.RemoveEventListener(eventCh, events...) + eventCtx, eventCancel := context.WithCancel(ctx) + defer eventCancel() + errCh := make(chan error, 1) + go func() { errCh <- c.HandleEvents(eventCtx) }() + for { + select { + case <-eventCtx.Done(): + return nil, eventCtx.Err() + case err := <-errCh: + return nil, err + case event := <-eventCh: + if ok, err := predicate(event); err != nil { + return nil, err + } else if ok { + return event, nil + } + } + } +} + +// HandleEvents loops until the context is closed dispatching async events. Can +// dispatch events even after context is done and of course during synchronous +// request. This will always end with an error, either from ctx.Done() or from +// an error reading/handling the event. +func (c *Conn) HandleEvents(ctx context.Context) error { + errCh := make(chan error, 1) + go func() { + for ctx.Err() == nil { + if err := c.HandleNextEvent(); err != nil { + errCh <- err + break + } + } + }() + select { + case err := <-errCh: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +// HandleNextEvent attempts to read and handle the next event. It will return on +// first message seen, event or not. Otherwise it will wait until there is a +// message read. +func (c *Conn) HandleNextEvent() error { + c.readLock.Lock() + defer c.readLock.Unlock() + // We'll just peek for the next 3 bytes and see if they are async + byts, err := c.conn.R.Peek(3) + if err != nil { + return err + } + statusCode, err := strconv.Atoi(string(byts)) + if err != nil || statusCode != StatusAsyncEvent { + return err + } + // Read the entire thing and handle it + resp, err := c.ReadResponse() + if err != nil { + return err + } + c.relayAsyncEvents(resp) + return nil +} + +// AddEventListener adds the given channel as an event listener for the given +// events. Then Tor is notified about which events should be listened to. +// Callers are expected to call RemoveEventListener for the channel and all +// event codes used here before closing the channel. If no events are provided, +// this is essentially a no-op. The EventCodeUnrecognized event code can be used +// to listen for unrecognized events. +func (c *Conn) AddEventListener(ch chan<- Event, events ...EventCode) error { + c.addEventListenerToMap(ch, events...) + // If there is an error updating the events, remove what we just added + err := c.sendSetEvents() + if err != nil { + c.removeEventListenerFromMap(ch, events...) + } + return err +} + +// RemoveEventListener removes the given channel from being sent to by the given +// event codes. It is not an error to remove a channel from events +// AddEventListener was not called for. Tor is notified about events which may +// no longer be listened to. If no events are provided, this is essentially a +// no-op. +func (c *Conn) RemoveEventListener(ch chan<- Event, events ...EventCode) error { + c.removeEventListenerFromMap(ch, events...) + return c.sendSetEvents() +} + +func (c *Conn) addEventListenerToMap(ch chan<- Event, events ...EventCode) { + c.eventListenersLock.Lock() + defer c.eventListenersLock.Unlock() + 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 + } +} + +func (c *Conn) removeEventListenerFromMap(ch chan<- Event, events ...EventCode) { + c.eventListenersLock.Lock() + defer c.eventListenersLock.Unlock() + 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 + } + } + } +} + +func (c *Conn) sendSetEvents() error { + c.eventListenersLock.RLock() + cmd := "SETEVENTS" + for event := range c.eventListeners { + cmd += " " + string(event) + } + c.eventListenersLock.RUnlock() + return c.sendRequestIgnoreResponse(cmd) +} + +func (c *Conn) relayAsyncEvents(resp *Response) { + var code, data string + var dataArray []string + if len(resp.Data) == 1 { + // On single line, part up to space, newline, or EOL is the code, rest is data + if index := strings.Index(resp.Data[0], " "); index != -1 { + code, data = resp.Data[0][:index], resp.Data[0][index+1:] + } else if index := strings.Index(resp.Data[0], "\r\n"); index != -1 { + code, data = resp.Data[0][:index], resp.Data[0][index+2:] + } else { + code, data = resp.Data[0], "" + } + } else if len(resp.Data) > 0 { + // If there are multiple lines, the entire first line is the code + code, dataArray = resp.Data[0], resp.Data[1:] + } else { + // Otherwise, the reply line has the data + code, data, _ = torutil.PartitionString(resp.Reply, ' ') + } + // Only relay if there are chans + eventCode := EventCode(code) + c.eventListenersLock.RLock() + chans := c.eventListeners[eventCode] + if _, ok := recognizedEventCodesByCode[eventCode]; !ok { + chans = append(chans, c.eventListeners[EventCodeUnrecognized]...) + } + c.eventListenersLock.RUnlock() + if len(chans) == 0 { + return + } + // Parse the event and only send if known event + if event := ParseEvent(eventCode, data, dataArray); event != nil { + for _, ch := range chans { + // Just send, if closed or blocking, that's not our problem + ch <- event + } + } +} + +// Zero on fail +func parseISOTime(str string) time.Time { + // Essentially time.RFC3339 but without 'T' or TZ info + const layout = "2006-01-02 15:04:05" + ret, err := time.Parse(layout, str) + if err != nil { + ret = time.Time{} + } + return ret +} + +// 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 +} + +// Event is the base interface for all known asynchronous events. +type Event interface { + Code() EventCode +} + +// ParseEvent returns an Event for the given code and data info. Raw is the raw +// single line if it is a single-line event (even if it has newlines), dataArray +// is the array of lines for multi-line events. Only one of the two needs to be +// set. The response is never nil, but may be UnrecognizedEvent. Format errors +// are ignored per the Tor spec. +func ParseEvent(code EventCode, raw string, dataArray []string) Event { + switch code { + case EventCodeAddrMap: + return ParseAddrMapEvent(raw) + case EventCodeBandwidth: + return ParseBandwidthEvent(raw) + case EventCodeBuildTimeoutSet: + return ParseBuildTimeoutSetEvent(raw) + case EventCodeCellStats: + return ParseCellStatsEvent(raw) + case EventCodeCircuit: + return ParseCircuitEvent(raw) + case EventCodeCircuitBandwidth: + return ParseCircuitBandwidthEvent(raw) + case EventCodeCircuitMinor: + return ParseCircuitMinorEvent(raw) + case EventCodeClientsSeen: + return ParseClientsSeenEvent(raw) + case EventCodeConfChanged: + return ParseConfChangedEvent(dataArray) + case EventCodeConnBandwidth: + return ParseConnBandwidthEvent(raw) + case EventCodeDescChanged: + return ParseDescChangedEvent(raw) + case EventCodeGuard: + return ParseGuardEvent(raw) + case EventCodeHSDesc: + return ParseHSDescEvent(raw) + case EventCodeHSDescContent: + return ParseHSDescContentEvent(raw) + case EventCodeLogDebug, EventCodeLogErr, EventCodeLogInfo, EventCodeLogNotice, EventCodeLogWarn: + return ParseLogEvent(code, raw) + case EventCodeNetworkLiveness: + return ParseNetworkLivenessEvent(raw) + case EventCodeNetworkStatus: + return ParseNetworkStatusEvent(raw) + case EventCodeNewConsensus: + return ParseNewConsensusEvent(raw) + case EventCodeNewDesc: + return ParseNewDescEvent(raw) + case EventCodeORConn: + return ParseORConnEvent(raw) + case EventCodeSignal: + return ParseSignalEvent(raw) + case EventCodeStatusClient, EventCodeStatusGeneral, EventCodeStatusServer: + return ParseStatusEvent(code, raw) + case EventCodeStream: + return ParseStreamEvent(raw) + case EventCodeStreamBandwidth: + return ParseStreamBandwidthEvent(raw) + case EventCodeTokenBucketEmpty: + return ParseTokenBucketEmptyEvent(raw) + case EventCodeTransportLaunched: + return ParseTransportLaunchedEvent(raw) + default: + return ParseUnrecognizedEvent(code, raw, dataArray) + } +} + +// CircuitEvent is CIRC in spec. +type CircuitEvent struct { + Raw string + 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 +} + +// ParseCircuitEvent parses the event. +func ParseCircuitEvent(raw string) *CircuitEvent { + event := &CircuitEvent{Raw: raw} + event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ') + var ok bool + event.Status, raw, ok = torutil.PartitionString(raw, ' ') + var attr string + first := true + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.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 +} + +// Code implements Event.Code +func (*CircuitEvent) Code() EventCode { return EventCodeCircuit } + +// StreamEvent is STREAM in spec. +type StreamEvent struct { + Raw string + StreamID string + Status string + CircuitID string + TargetAddress string + TargetPort int + Reason string + RemoteReason string + Source string + SourceAddress string + SourcePort int + Purpose string +} + +// ParseStreamEvent parses the event. +func ParseStreamEvent(raw string) *StreamEvent { + event := &StreamEvent{Raw: raw} + event.StreamID, raw, _ = torutil.PartitionString(raw, ' ') + event.Status, raw, _ = torutil.PartitionString(raw, ' ') + event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ') + var ok bool + event.TargetAddress, raw, ok = torutil.PartitionString(raw, ' ') + if target, port, hasPort := torutil.PartitionStringFromEnd(event.TargetAddress, ':'); hasPort { + event.TargetAddress = target + event.TargetPort, _ = strconv.Atoi(port) + } + var attr string + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "REASON": + event.Reason = val + case "REMOTE_REASON": + event.RemoteReason = val + case "SOURCE": + event.Source = val + case "SOURCE_ADDR": + event.SourceAddress = val + if source, port, hasPort := torutil.PartitionStringFromEnd(event.SourceAddress, ':'); hasPort { + event.SourceAddress = source + event.SourcePort, _ = strconv.Atoi(port) + } + case "PURPOSE": + event.Purpose = val + } + } + return event +} + +// Code implements Event.Code +func (*StreamEvent) Code() EventCode { return EventCodeStream } + +// ORConnEvent is ORCONN in spec. +type ORConnEvent struct { + Raw string + Target string + Status string + Reason string + NumCircuits int + ConnID string +} + +// ParseORConnEvent parses the event. +func ParseORConnEvent(raw string) *ORConnEvent { + event := &ORConnEvent{Raw: raw} + event.Target, raw, _ = torutil.PartitionString(raw, ' ') + var ok bool + event.Status, raw, ok = torutil.PartitionString(raw, ' ') + var attr string + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "REASON": + event.Reason = val + case "NCIRCS": + event.NumCircuits, _ = strconv.Atoi(val) + case "ID": + event.ConnID = val + } + } + return event +} + +// Code implements Event.Code +func (*ORConnEvent) Code() EventCode { return EventCodeORConn } + +// BandwidthEvent is BW in spec. +type BandwidthEvent struct { + Raw string + BytesRead int64 + BytesWritten int64 +} + +// ParseBandwidthEvent parses the event. +func ParseBandwidthEvent(raw string) *BandwidthEvent { + event := &BandwidthEvent{Raw: raw} + var temp string + temp, raw, _ = torutil.PartitionString(raw, ' ') + event.BytesRead, _ = strconv.ParseInt(temp, 10, 64) + temp, raw, _ = torutil.PartitionString(raw, ' ') + event.BytesWritten, _ = strconv.ParseInt(temp, 10, 64) + return event +} + +// Code implements Event.Code +func (*BandwidthEvent) Code() EventCode { return EventCodeBandwidth } + +// LogEvent is DEBUG, ERR, INFO, NOTICE, and WARN in spec. +type LogEvent struct { + Severity EventCode + Raw string +} + +// ParseLogEvent parses the event. +func ParseLogEvent(severity EventCode, raw string) *LogEvent { + return &LogEvent{Severity: severity, Raw: raw} +} + +// Code implements Event.Code +func (l *LogEvent) Code() EventCode { return l.Severity } + +// NewDescEvent is NEWDESC in spec. +type NewDescEvent struct { + Raw string + Descs []string +} + +// ParseNewDescEvent parses the event. +func ParseNewDescEvent(raw string) *NewDescEvent { + return &NewDescEvent{Raw: raw, Descs: strings.Split(raw, " ")} +} + +// Code implements Event.Code +func (*NewDescEvent) Code() EventCode { return EventCodeNewDesc } + +// AddrMapEvent is ADDRMAP in spec. +type AddrMapEvent struct { + Raw string + Address string + NewAddress string + ErrorCode string + // Zero if no expire + Expires time.Time + // Sans double quotes + Cached string +} + +// ParseAddrMapEvent parses the event. +func ParseAddrMapEvent(raw string) *AddrMapEvent { + event := &AddrMapEvent{Raw: raw} + event.Address, raw, _ = torutil.PartitionString(raw, ' ') + event.NewAddress, raw, _ = torutil.PartitionString(raw, ' ') + var ok bool + // Skip local expiration, use UTC one later + _, raw, ok = torutil.PartitionString(raw, ' ') + var attr string + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "error": + event.ErrorCode = val + case "EXPIRES": + val, _ = torutil.UnescapeSimpleQuotedString(val) + event.Expires = parseISOTime(val) + case "CACHED": + event.Cached, _ = torutil.UnescapeSimpleQuotedStringIfNeeded(val) + } + } + return event +} + +// Code implements Event.Code +func (*AddrMapEvent) Code() EventCode { return EventCodeAddrMap } + +// DescChangedEvent is DESCCHANGED in spec. +type DescChangedEvent struct { + Raw string +} + +// ParseDescChangedEvent parses the event. +func ParseDescChangedEvent(raw string) *DescChangedEvent { + return &DescChangedEvent{Raw: raw} +} + +// Code implements Event.Code +func (*DescChangedEvent) Code() EventCode { return EventCodeDescChanged } + +// StatusEvent is STATUS_CLIENT, STATUS_GENERAL, and STATUS_SERVER in spec. +type StatusEvent struct { + Raw string + Type EventCode + Severity string + Action string + Arguments map[string]string +} + +// ParseStatusEvent parses the event. +func ParseStatusEvent(typ EventCode, raw string) *StatusEvent { + event := &StatusEvent{Raw: raw, Type: typ, Arguments: map[string]string{}} + event.Severity, raw, _ = torutil.PartitionString(raw, ' ') + var ok bool + event.Action, raw, ok = torutil.PartitionString(raw, ' ') + var attr string + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + event.Arguments[key], _ = torutil.UnescapeSimpleQuotedStringIfNeeded(val) + } + return event +} + +// Code implements Event.Code +func (s *StatusEvent) Code() EventCode { return s.Type } + +// GuardEvent is GUARD in spec. +type GuardEvent struct { + Raw string + Type string + Name string + Status string +} + +// ParseGuardEvent parses the event. +func ParseGuardEvent(raw string) *GuardEvent { + event := &GuardEvent{Raw: raw} + event.Type, raw, _ = torutil.PartitionString(raw, ' ') + event.Name, raw, _ = torutil.PartitionString(raw, ' ') + event.Status, raw, _ = torutil.PartitionString(raw, ' ') + return event +} + +// Code implements Event.Code +func (*GuardEvent) Code() EventCode { return EventCodeGuard } + +// NetworkStatusEvent is NS in spec. +type NetworkStatusEvent struct { + Raw string +} + +// ParseNetworkStatusEvent parses the event. +func ParseNetworkStatusEvent(raw string) *NetworkStatusEvent { + return &NetworkStatusEvent{Raw: raw} +} + +// Code implements Event.Code +func (*NetworkStatusEvent) Code() EventCode { return EventCodeNetworkStatus } + +// StreamBandwidthEvent is STREAM_BW in spec. +type StreamBandwidthEvent struct { + Raw string + BytesRead int64 + BytesWritten int64 + Time time.Time +} + +// ParseStreamBandwidthEvent parses the event. +func ParseStreamBandwidthEvent(raw string) *StreamBandwidthEvent { + event := &StreamBandwidthEvent{Raw: raw} + var temp string + temp, raw, _ = torutil.PartitionString(raw, ' ') + event.BytesRead, _ = strconv.ParseInt(temp, 10, 64) + temp, raw, _ = torutil.PartitionString(raw, ' ') + event.BytesWritten, _ = strconv.ParseInt(temp, 10, 64) + temp, raw, _ = torutil.PartitionString(raw, ' ') + temp, _ = torutil.UnescapeSimpleQuotedString(temp) + event.Time = parseISOTime2Frac(temp) + return event +} + +// Code implements Event.Code +func (*StreamBandwidthEvent) Code() EventCode { return EventCodeStreamBandwidth } + +// ClientsSeenEvent is CLIENTS_SEEN in spec. +type ClientsSeenEvent struct { + Raw string + TimeStarted time.Time + CountrySummary map[string]int + IPVersions map[string]int +} + +// ParseClientsSeenEvent parses the event. +func ParseClientsSeenEvent(raw string) *ClientsSeenEvent { + event := &ClientsSeenEvent{Raw: raw} + var temp string + var ok bool + temp, raw, ok = torutil.PartitionString(raw, ' ') + temp, _ = torutil.UnescapeSimpleQuotedString(temp) + event.TimeStarted = parseISOTime(temp) + strToMap := func(str string) map[string]int { + ret := map[string]int{} + for _, keyVal := range strings.Split(str, ",") { + key, val, _ := torutil.PartitionString(keyVal, '=') + ret[key], _ = strconv.Atoi(val) + } + return ret + } + var attr string + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "CountrySummary": + event.CountrySummary = strToMap(val) + case "IPVersions": + event.IPVersions = strToMap(val) + } + } + return event +} + +// Code implements Event.Code +func (*ClientsSeenEvent) Code() EventCode { return EventCodeClientsSeen } + +// NewConsensusEvent is NEWCONSENSUS in spec. +type NewConsensusEvent struct { + Raw string +} + +// ParseNewConsensusEvent parses the event. +func ParseNewConsensusEvent(raw string) *NewConsensusEvent { + return &NewConsensusEvent{Raw: raw} +} + +// Code implements Event.Code +func (*NewConsensusEvent) Code() EventCode { return EventCodeNewConsensus } + +// BuildTimeoutSetEvent is BUILDTIMEOUT_SET in spec. +type BuildTimeoutSetEvent struct { + Raw string + Type string + TotalTimes int + Timeout time.Duration + Xm int + Alpha float32 + Quantile float32 + TimeoutRate float32 + CloseTimeout time.Duration + CloseRate float32 +} + +// ParseBuildTimeoutSetEvent parses the event. +func ParseBuildTimeoutSetEvent(raw string) *BuildTimeoutSetEvent { + event := &BuildTimeoutSetEvent{Raw: raw} + var ok bool + event.Type, raw, ok = torutil.PartitionString(raw, ' ') + _, raw, ok = torutil.PartitionString(raw, ' ') + var attr string + parseFloat := func(val string) float32 { + f, _ := strconv.ParseFloat(val, 32) + return float32(f) + } + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "TOTAL_TIMES": + event.TotalTimes, _ = strconv.Atoi(val) + case "TIMEOUT_MS": + if ms, err := strconv.ParseInt(val, 10, 64); err == nil { + event.Timeout = time.Duration(ms) * time.Millisecond + } + case "XM": + event.Xm, _ = strconv.Atoi(val) + case "ALPHA": + event.Alpha = parseFloat(val) + case "CUTOFF_QUANTILE": + event.Quantile = parseFloat(val) + case "TIMEOUT_RATE": + event.TimeoutRate = parseFloat(val) + case "CLOSE_MS": + if ms, err := strconv.ParseInt(val, 10, 64); err == nil { + event.CloseTimeout = time.Duration(ms) * time.Millisecond + } + case "CLOSE_RATE": + event.CloseRate = parseFloat(val) + } + } + return event +} + +// Code implements Event.Code +func (*BuildTimeoutSetEvent) Code() EventCode { return EventCodeBuildTimeoutSet } + +// SignalEvent is SIGNAL in spec. +type SignalEvent struct { + Raw string +} + +// ParseSignalEvent parses the event. +func ParseSignalEvent(raw string) *SignalEvent { + return &SignalEvent{Raw: raw} +} + +// Code implements Event.Code +func (*SignalEvent) Code() EventCode { return EventCodeSignal } + +// ConfChangedEvent is CONF_CHANGED in spec. +type ConfChangedEvent struct { + Raw []string +} + +// ParseConfChangedEvent parses the event. +func ParseConfChangedEvent(raw []string) *ConfChangedEvent { + // TODO: break into KeyVal and unescape strings + return &ConfChangedEvent{Raw: raw} +} + +// Code implements Event.Code +func (*ConfChangedEvent) Code() EventCode { return EventCodeConfChanged } + +// CircuitMinorEvent is CIRC_MINOR in spec. +type CircuitMinorEvent struct { + Raw string + CircuitID string + Event string + Path []string + BuildFlags []string + Purpose string + HSState string + RendQuery string + TimeCreated time.Time + OldPurpose string + OldHSState string +} + +// ParseCircuitMinorEvent parses the event. +func ParseCircuitMinorEvent(raw string) *CircuitMinorEvent { + event := &CircuitMinorEvent{Raw: raw} + event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ') + var ok bool + event.Event, raw, ok = torutil.PartitionString(raw, ' ') + var attr string + first := true + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.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 "OLD_PURPOSE": + event.OldPurpose = val + case "OLD_HS_STATE": + event.OldHSState = val + default: + if first { + event.Path = strings.Split(val, ",") + } + } + first = false + } + return event +} + +// Code implements Event.Code +func (*CircuitMinorEvent) Code() EventCode { return EventCodeCircuitMinor } + +// TransportLaunchedEvent is TRANSPORT_LAUNCHED in spec. +type TransportLaunchedEvent struct { + Raw string + Type string + Name string + Address string + Port int +} + +// ParseTransportLaunchedEvent parses the event. +func ParseTransportLaunchedEvent(raw string) *TransportLaunchedEvent { + event := &TransportLaunchedEvent{Raw: raw} + event.Type, raw, _ = torutil.PartitionString(raw, ' ') + event.Name, raw, _ = torutil.PartitionString(raw, ' ') + event.Address, raw, _ = torutil.PartitionString(raw, ' ') + var temp string + temp, raw, _ = torutil.PartitionString(raw, ' ') + event.Port, _ = strconv.Atoi(temp) + return event +} + +// Code implements Event.Code +func (*TransportLaunchedEvent) Code() EventCode { return EventCodeTransportLaunched } + +// ConnBandwidthEvent is CONN_BW in spec. +type ConnBandwidthEvent struct { + Raw string + ConnID string + ConnType string + BytesRead int64 + BytesWritten int64 +} + +// ParseConnBandwidthEvent parses the event. +func ParseConnBandwidthEvent(raw string) *ConnBandwidthEvent { + event := &ConnBandwidthEvent{Raw: raw} + ok := true + var attr string + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "ID": + event.ConnID = val + case "TYPE": + event.ConnType = val + case "READ": + event.BytesRead, _ = strconv.ParseInt(val, 10, 64) + case "WRITTEN": + event.BytesWritten, _ = strconv.ParseInt(val, 10, 64) + } + } + return event +} + +// Code implements Event.Code +func (*ConnBandwidthEvent) Code() EventCode { return EventCodeConnBandwidth } + +// CircuitBandwidthEvent is CIRC_BW in spec. +type CircuitBandwidthEvent struct { + Raw string + CircuitID string + BytesRead int64 + BytesWritten int64 + Time time.Time +} + +// ParseCircuitBandwidthEvent parses the event. +func ParseCircuitBandwidthEvent(raw string) *CircuitBandwidthEvent { + event := &CircuitBandwidthEvent{Raw: raw} + ok := true + var attr string + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "ID": + event.CircuitID = val + case "READ": + event.BytesRead, _ = strconv.ParseInt(val, 10, 64) + case "WRITTEN": + event.BytesWritten, _ = strconv.ParseInt(val, 10, 64) + case "TIME": + event.Time = parseISOTime2Frac(val) + } + } + return event +} + +// Code implements Event.Code +func (*CircuitBandwidthEvent) Code() EventCode { return EventCodeCircuitBandwidth } + +// CellStatsEvent is CELL_STATS in spec. +type CellStatsEvent struct { + Raw string + CircuitID string + InboundQueueID string + InboundConnID string + InboundAdded map[string]int + InboundRemoved map[string]int + InboundTime map[string]int + OutboundQueueID string + OutboundConnID string + OutboundAdded map[string]int + OutboundRemoved map[string]int + OutboundTime map[string]int +} + +// ParseCellStatsEvent parses the event. +func ParseCellStatsEvent(raw string) *CellStatsEvent { + event := &CellStatsEvent{Raw: raw} + ok := true + var attr string + toIntMap := func(val string) map[string]int { + ret := map[string]int{} + for _, v := range strings.Split(val, ",") { + key, val, _ := torutil.PartitionString(v, ':') + ret[key], _ = strconv.Atoi(val) + } + return ret + } + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "ID": + event.CircuitID = val + case "InboundQueue": + event.InboundQueueID = val + case "InboundConn": + event.InboundConnID = val + case "InboundAdded": + event.InboundAdded = toIntMap(val) + case "InboundRemoved": + event.InboundRemoved = toIntMap(val) + case "InboundTime": + event.OutboundTime = toIntMap(val) + case "OutboundQueue": + event.OutboundQueueID = val + case "OutboundConn": + event.OutboundConnID = val + case "OutboundAdded": + event.OutboundAdded = toIntMap(val) + case "OutboundRemoved": + event.OutboundRemoved = toIntMap(val) + case "OutboundTime": + event.OutboundTime = toIntMap(val) + } + } + return event +} + +// Code implements Event.Code +func (*CellStatsEvent) Code() EventCode { return EventCodeCellStats } + +// TokenBucketEmptyEvent is TB_EMPTY in spec. +type TokenBucketEmptyEvent struct { + Raw string + BucketName string + ConnID string + ReadBucketEmpty time.Duration + WriteBucketEmpty time.Duration + LastRefil time.Duration +} + +// ParseTokenBucketEmptyEvent parses the event. +func ParseTokenBucketEmptyEvent(raw string) *TokenBucketEmptyEvent { + event := &TokenBucketEmptyEvent{Raw: raw} + var ok bool + event.BucketName, raw, ok = torutil.PartitionString(raw, ' ') + var attr string + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, _ := torutil.PartitionString(attr, '=') + switch key { + case "ID": + event.ConnID = val + case "READ": + i, _ := strconv.ParseInt(val, 10, 64) + event.ReadBucketEmpty = time.Duration(i) * time.Millisecond + case "WRITTEN": + i, _ := strconv.ParseInt(val, 10, 64) + event.WriteBucketEmpty = time.Duration(i) * time.Millisecond + case "LAST": + i, _ := strconv.ParseInt(val, 10, 64) + event.LastRefil = time.Duration(i) * time.Millisecond + } + } + return event +} + +// Code implements Event.Code +func (*TokenBucketEmptyEvent) Code() EventCode { return EventCodeTokenBucketEmpty } + +// HSDescEvent is HS_DESC in spec. +type HSDescEvent struct { + Raw string + Action string + Address string + AuthType string + HSDir string + DescID string + Reason string + Replica int + HSDirIndex string +} + +// ParseHSDescEvent parses the event. +func ParseHSDescEvent(raw string) *HSDescEvent { + event := &HSDescEvent{Raw: raw} + event.Action, raw, _ = torutil.PartitionString(raw, ' ') + event.Address, raw, _ = torutil.PartitionString(raw, ' ') + event.AuthType, raw, _ = torutil.PartitionString(raw, ' ') + var ok bool + event.HSDir, raw, ok = torutil.PartitionString(raw, ' ') + var attr string + first := true + for ok { + attr, raw, ok = torutil.PartitionString(raw, ' ') + key, val, valOk := torutil.PartitionString(attr, '=') + switch key { + case "REASON": + event.Reason = val + case "REPLICA": + event.Replica, _ = strconv.Atoi(val) + case "HSDIR_INDEX": + event.HSDirIndex = val + default: + if first && !valOk { + event.DescID = attr + } + } + first = false + } + return event +} + +// Code implements Event.Code +func (*HSDescEvent) Code() EventCode { return EventCodeHSDesc } + +// HSDescContentEvent is HS_DESC_CONTENT in spec. +type HSDescContentEvent struct { + Raw string + Address string + DescID string + HSDir string + Descriptor string +} + +// ParseHSDescContentEvent parses the event. +func ParseHSDescContentEvent(raw string) *HSDescContentEvent { + event := &HSDescContentEvent{Raw: raw} + event.Address, raw, _ = torutil.PartitionString(raw, ' ') + event.DescID, raw, _ = torutil.PartitionString(raw, ' ') + newlineIndex := strings.Index(raw, "\r\n") + if newlineIndex != -1 { + event.HSDir, event.Descriptor = raw[:newlineIndex], raw[newlineIndex+2:] + } + return event +} + +// Code implements Event.Code +func (*HSDescContentEvent) Code() EventCode { return EventCodeHSDescContent } + +// NetworkLivenessEvent is NETWORK_LIVENESS in spec. +type NetworkLivenessEvent struct { + Raw string +} + +// ParseNetworkLivenessEvent parses the event. +func ParseNetworkLivenessEvent(raw string) *NetworkLivenessEvent { + return &NetworkLivenessEvent{Raw: raw} +} + +// Code implements Event.Code +func (*NetworkLivenessEvent) Code() EventCode { return EventCodeNetworkLiveness } + +// UnrecognizedEvent is any unrecognized event code. +type UnrecognizedEvent struct { + EventCode EventCode + RawSingleLine string + RawMultiLine []string +} + +// ParseUnrecognizedEvent creates an unrecognized event with the given values. +func ParseUnrecognizedEvent(eventCode EventCode, rawSingleLine string, rawMultiLine []string) *UnrecognizedEvent { + return &UnrecognizedEvent{EventCode: eventCode, RawSingleLine: rawSingleLine, RawMultiLine: rawMultiLine} +} + +// Code implements Event.Code +func (u *UnrecognizedEvent) Code() EventCode { return u.EventCode } |