package api import ( "bufio" "bytes" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "plugin" "sync" "time" "github.com/google/martian/har" ) type Protocol struct { Name string `json:"name"` LongName string `json:"longName"` Abbreviation string `json:"abbreviation"` Version string `json:"version"` BackgroundColor string `json:"backgroundColor"` ForegroundColor string `json:"foregroundColor"` FontSize int8 `json:"fontSize"` ReferenceLink string `json:"referenceLink"` Ports []string `json:"ports"` Priority uint8 `json:"priority"` } type Extension struct { Protocol *Protocol Path string Plug *plugin.Plugin Dissector Dissector MatcherMap *sync.Map } type ConnectionInfo struct { ClientIP string ClientPort string ServerIP string ServerPort string IsOutgoing bool } type TcpID struct { SrcIP string DstIP string SrcPort string DstPort string Ident string } type CounterPair struct { Request uint Response uint } type GenericMessage struct { IsRequest bool `json:"isRequest"` CaptureTime time.Time `json:"captureTime"` Payload interface{} `json:"payload"` } type RequestResponsePair struct { Request GenericMessage `json:"request"` Response GenericMessage `json:"response"` } // `Protocol` is modified in the later stages of data propagation. Therefore it's not a pointer. type OutputChannelItem struct { Protocol Protocol Timestamp int64 ConnectionInfo *ConnectionInfo Pair *RequestResponsePair } type SuperTimer struct { CaptureTime time.Time } type SuperIdentifier struct { Protocol *Protocol IsClosedOthers bool } type Dissector interface { Register(*Extension) Ping() Dissect(b *bufio.Reader, isClient bool, tcpID *TcpID, counterPair *CounterPair, superTimer *SuperTimer, superIdentifier *SuperIdentifier, emitter Emitter, options *TrafficFilteringOptions) error Analyze(item *OutputChannelItem, entryId string, resolvedSource string, resolvedDestination string) *MizuEntry Summarize(entry *MizuEntry) *BaseEntryDetails Represent(entry *MizuEntry) (protocol Protocol, object []byte, bodySize int64, err error) } type Emitting struct { AppStats *AppStats OutputChannel chan *OutputChannelItem } type Emitter interface { Emit(item *OutputChannelItem) } func (e *Emitting) Emit(item *OutputChannelItem) { e.OutputChannel <- item e.AppStats.IncMatchedPairs() } type MizuEntry struct { ID uint `gorm:"primarykey"` CreatedAt time.Time UpdatedAt time.Time ProtocolName string `json:"protocolName" gorm:"column:protocolName"` ProtocolLongName string `json:"protocolLongName" gorm:"column:protocolLongName"` ProtocolAbbreviation string `json:"protocolAbbreviation" gorm:"column:protocolAbbreviation"` ProtocolVersion string `json:"protocolVersion" gorm:"column:protocolVersion"` ProtocolBackgroundColor string `json:"protocolBackgroundColor" gorm:"column:protocolBackgroundColor"` ProtocolForegroundColor string `json:"protocolForegroundColor" gorm:"column:protocolForegroundColor"` ProtocolFontSize int8 `json:"protocolFontSize" gorm:"column:protocolFontSize"` ProtocolReferenceLink string `json:"protocolReferenceLink" gorm:"column:protocolReferenceLink"` Entry string `json:"entry,omitempty" gorm:"column:entry"` EntryId string `json:"entryId" gorm:"column:entryId"` Url string `json:"url" gorm:"column:url"` Method string `json:"method" gorm:"column:method"` Status int `json:"status" gorm:"column:status"` RequestSenderIp string `json:"requestSenderIp" gorm:"column:requestSenderIp"` Service string `json:"service" gorm:"column:service"` Timestamp int64 `json:"timestamp" gorm:"column:timestamp"` ElapsedTime int64 `json:"elapsedTime" gorm:"column:elapsedTime"` Path string `json:"path" gorm:"column:path"` ResolvedSource string `json:"resolvedSource,omitempty" gorm:"column:resolvedSource"` ResolvedDestination string `json:"resolvedDestination,omitempty" gorm:"column:resolvedDestination"` SourceIp string `json:"sourceIp,omitempty" gorm:"column:sourceIp"` DestinationIp string `json:"destinationIp,omitempty" gorm:"column:destinationIp"` SourcePort string `json:"sourcePort,omitempty" gorm:"column:sourcePort"` DestinationPort string `json:"destinationPort,omitempty" gorm:"column:destinationPort"` IsOutgoing bool `json:"isOutgoing,omitempty" gorm:"column:isOutgoing"` ContractStatus ContractStatus `json:"contractStatus,omitempty" gorm:"column:contractStatus"` ContractRequestReason string `json:"contractRequestReason,omitempty" gorm:"column:contractRequestReason"` ContractResponseReason string `json:"contractResponseReason,omitempty" gorm:"column:contractResponseReason"` ContractContent string `json:"contractContent,omitempty" gorm:"column:contractContent"` EstimatedSizeBytes int `json:"-" gorm:"column:estimatedSizeBytes"` } type MizuEntryWrapper struct { Protocol Protocol `json:"protocol"` Representation string `json:"representation"` BodySize int64 `json:"bodySize"` Data MizuEntry `json:"data"` Rules []map[string]interface{} `json:"rulesMatched,omitempty"` IsRulesEnabled bool `json:"isRulesEnabled"` } type BaseEntryDetails struct { Id string `json:"id,omitempty"` Protocol Protocol `json:"protocol,omitempty"` Url string `json:"url,omitempty"` RequestSenderIp string `json:"requestSenderIp,omitempty"` Service string `json:"service,omitempty"` Path string `json:"path,omitempty"` Summary string `json:"summary,omitempty"` StatusCode int `json:"statusCode"` Method string `json:"method,omitempty"` Timestamp int64 `json:"timestamp,omitempty"` SourceIp string `json:"sourceIp,omitempty"` DestinationIp string `json:"destinationIp,omitempty"` SourcePort string `json:"sourcePort,omitempty"` DestinationPort string `json:"destinationPort,omitempty"` IsOutgoing bool `json:"isOutgoing,omitempty"` Latency int64 `json:"latency"` Rules ApplicableRules `json:"rules,omitempty"` ContractStatus ContractStatus `json:"contractStatus"` } type ApplicableRules struct { Latency int64 `json:"latency,omitempty"` Status bool `json:"status,omitempty"` NumberOfRules int `json:"numberOfRules,omitempty"` } type ContractStatus int type Contract struct { Status ContractStatus `json:"status"` RequestReason string `json:"requestReason"` ResponseReason string `json:"responseReason"` Content string `json:"content"` } type DataUnmarshaler interface { UnmarshalData(*MizuEntry) error } func (bed *BaseEntryDetails) UnmarshalData(entry *MizuEntry) error { bed.Protocol = Protocol{ Name: entry.ProtocolName, LongName: entry.ProtocolLongName, Abbreviation: entry.ProtocolAbbreviation, Version: entry.ProtocolVersion, BackgroundColor: entry.ProtocolBackgroundColor, ForegroundColor: entry.ProtocolForegroundColor, FontSize: entry.ProtocolFontSize, ReferenceLink: entry.ProtocolReferenceLink, } bed.Id = entry.EntryId bed.Url = entry.Url bed.RequestSenderIp = entry.RequestSenderIp bed.Service = entry.Service bed.Path = entry.Path bed.Summary = entry.Path bed.StatusCode = entry.Status bed.Method = entry.Method bed.Timestamp = entry.Timestamp bed.SourceIp = entry.SourceIp bed.DestinationIp = entry.DestinationIp bed.SourcePort = entry.SourcePort bed.DestinationPort = entry.DestinationPort bed.IsOutgoing = entry.IsOutgoing bed.Latency = entry.ElapsedTime bed.ContractStatus = entry.ContractStatus return nil } const ( TABLE string = "table" BODY string = "body" ) const ( TypeHttpRequest = iota TypeHttpResponse ) type HTTPPayload struct { Type uint8 Data interface{} } type HTTPPayloader interface { MarshalJSON() ([]byte, error) } type HTTPWrapper struct { Method string `json:"method"` Url string `json:"url"` Details interface{} `json:"details"` RawRequest *HTTPRequestWrapper `json:"rawRequest"` RawResponse *HTTPResponseWrapper `json:"rawResponse"` } func (h HTTPPayload) MarshalJSON() ([]byte, error) { switch h.Type { case TypeHttpRequest: harRequest, err := har.NewRequest(h.Data.(*http.Request), true) if err != nil { return nil, errors.New("Failed converting request to HAR") } return json.Marshal(&HTTPWrapper{ Method: harRequest.Method, Url: "", Details: harRequest, RawRequest: &HTTPRequestWrapper{Request: h.Data.(*http.Request)}, }) case TypeHttpResponse: harResponse, err := har.NewResponse(h.Data.(*http.Response), true) if err != nil { return nil, errors.New("Failed converting response to HAR") } return json.Marshal(&HTTPWrapper{ Method: "", Url: "", Details: harResponse, RawResponse: &HTTPResponseWrapper{Response: h.Data.(*http.Response)}, }) default: panic(fmt.Sprintf("HTTP payload cannot be marshaled: %s\n", h.Type)) } } type HTTPWrapperTricky struct { Method string `json:"method"` Url string `json:"url"` Details interface{} `json:"details"` RawRequest *http.Request `json:"rawRequest"` RawResponse *http.Response `json:"rawResponse"` } type HTTPMessage struct { IsRequest bool `json:"isRequest"` CaptureTime time.Time `json:"captureTime"` Payload HTTPWrapperTricky `json:"payload"` } type HTTPRequestResponsePair struct { Request HTTPMessage `json:"request"` Response HTTPMessage `json:"response"` } type HTTPRequestWrapper struct { *http.Request } func (r *HTTPRequestWrapper) MarshalJSON() ([]byte, error) { body, _ := ioutil.ReadAll(r.Request.Body) r.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) return json.Marshal(&struct { Body string `json:"Body,omitempty"` GetBody string `json:"GetBody,omitempty"` Cancel string `json:"Cancel,omitempty"` *http.Request }{ Body: string(body), Request: r.Request, }) } type HTTPResponseWrapper struct { *http.Response } func (r *HTTPResponseWrapper) MarshalJSON() ([]byte, error) { body, _ := ioutil.ReadAll(r.Response.Body) r.Response.Body = ioutil.NopCloser(bytes.NewBuffer(body)) return json.Marshal(&struct { Body string `json:"Body,omitempty"` GetBody string `json:"GetBody,omitempty"` Cancel string `json:"Cancel,omitempty"` *http.Response }{ Body: string(body), Response: r.Response, }) }