Skip to content

Commit e96d6cd

Browse files
Added http colorization in transparent mode
1 parent 2bcac0a commit e96d6cd

File tree

2 files changed

+176
-77
lines changed

2 files changed

+176
-77
lines changed

gohpts.go

Lines changed: 163 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -367,33 +367,28 @@ func (p *proxyapp) doReq(w http.ResponseWriter, r *http.Request, sock *http.Clie
367367
}
368368

369369
func (p *proxyapp) colorizeStatus(code int, status string, bg bool) string {
370-
if !p.nocolor {
371-
if bg {
372-
if code < 300 {
373-
status = colors.GreenBg(status).String()
374-
} else if code < 400 {
375-
status = colors.YellowBg(status).String()
376-
} else {
377-
status = colors.RedBgDark(status).String()
378-
}
370+
if bg {
371+
if code < 300 {
372+
status = colors.GreenBg(status).String()
373+
} else if code < 400 {
374+
status = colors.YellowBg(status).String()
379375
} else {
380-
if code < 300 {
381-
status = colors.Green(status).String()
382-
} else if code < 400 {
383-
status = colors.Yellow(status).String()
384-
} else {
385-
status = colors.Red(status).String()
386-
}
376+
status = colors.RedBgDark(status).String()
377+
}
378+
} else {
379+
if code < 300 {
380+
status = colors.Green(status).String()
381+
} else if code < 400 {
382+
status = colors.Yellow(status).String()
383+
} else {
384+
status = colors.Red(status).String()
387385
}
388386
}
389387
return status
390388
}
391389

392-
func (p *proxyapp) colorizeHTTP(req *http.Request, resp *http.Response, reqBodySaved, respBodySaved *[]byte) {
393-
*respBodySaved = bytes.Trim(*respBodySaved, "\r\n\t ")
394-
*reqBodySaved = bytes.Trim(*reqBodySaved, "\r\n\t ")
390+
func (p *proxyapp) colorizeHTTP(req *http.Request, resp *http.Response, reqBodySaved, respBodySaved *[]byte, id uuid.UUID) string {
395391
var sb strings.Builder
396-
id := uuid.New()
397392
if p.sniffnocolor {
398393
sb.WriteString(fmt.Sprintf("%s ", colors.WrapBrackets(id.String())))
399394
sb.WriteString(fmt.Sprintf("%s %s %s ", req.Method, req.URL, req.Proto))
@@ -464,12 +459,17 @@ func (p *proxyapp) colorizeHTTP(req *http.Request, resp *http.Response, reqBodyS
464459
}
465460
}
466461
}
467-
p.snifflogger.Log().Msg(sb.String())
462+
return sb.String()
463+
}
464+
465+
func (p *proxyapp) colorizeTLS(req *layers.TLSClientHello, resp *layers.TLSServerHello, id uuid.UUID) string {
466+
return "TODO:"
468467
}
469468

470469
func (p *proxyapp) highlightPatterns(line string) (string, bool) {
471470
matched := false
472471

472+
// TODO: make this configurable
473473
// line, matched = p.replace(line, ipPortPattern, colors.YellowBg, matched)
474474
// line, matched = p.replace(line, domainPattern, colors.YellowBg, matched)
475475
line, matched = p.replace(line, jwtPattern, colors.Magenta, matched)
@@ -566,26 +566,29 @@ func (p *proxyapp) handleForward(w http.ResponseWriter, r *http.Request) {
566566
respBodySaved, _ = io.ReadAll(gzr)
567567
}
568568
}
569+
reqBodySaved = bytes.Trim(reqBodySaved, "\r\n\t ")
570+
respBodySaved = bytes.Trim(respBodySaved, "\r\n\t ")
569571
}
570572
if p.json {
571573
sniffheader := make([]string, 0, 4)
572574
j, err := json.Marshal(&layers.HTTPMessage{Request: r})
573575
if err == nil {
574576
sniffheader = append(sniffheader, string(j))
575-
if p.body && len(reqBodySaved) > 0 {
576-
sniffheader = append(sniffheader, fmt.Sprintf("{\"req_body\":%q}", reqBodySaved))
577-
}
578577
}
579578
j, err = json.Marshal(&layers.HTTPMessage{Response: resp})
580579
if err == nil {
581580
sniffheader = append(sniffheader, string(j))
582-
if p.body && len(respBodySaved) > 0 {
583-
sniffheader = append(sniffheader, fmt.Sprintf("{\"resp_body\":%q}", respBodySaved))
584-
}
581+
}
582+
if p.body && len(reqBodySaved) > 0 {
583+
sniffheader = append(sniffheader, fmt.Sprintf("{\"req_body\":%s}", reqBodySaved))
584+
}
585+
if p.body && len(respBodySaved) > 0 {
586+
sniffheader = append(sniffheader, fmt.Sprintf("{\"resp_body\":%s}", respBodySaved))
585587
}
586588
p.snifflogger.Log().Msg(fmt.Sprintf("[%s]", strings.Join(sniffheader, ",")))
587589
} else {
588-
p.colorizeHTTP(req, resp, &reqBodySaved, &respBodySaved)
590+
id := uuid.New()
591+
p.snifflogger.Log().Msg(p.colorizeHTTP(req, resp, &reqBodySaved, &respBodySaved, id))
589592
}
590593
}
591594
defer resp.Body.Close()
@@ -644,7 +647,11 @@ func (p *proxyapp) handleForward(w http.ResponseWriter, r *http.Request) {
644647
if chunked {
645648
written = fmt.Sprintf("%s - chunked", written)
646649
}
647-
p.logger.Debug().Msgf("%s - %s - %s - %s - %s", r.Proto, r.Method, r.Host, p.colorizeStatus(resp.StatusCode, resp.Status, false), written)
650+
status := resp.Status
651+
if !p.nocolor {
652+
status = p.colorizeStatus(resp.StatusCode, status, false)
653+
}
654+
p.logger.Debug().Msgf("%s - %s - %s - %s - %s", r.Proto, r.Method, r.Host, status, written)
648655
if len(resp.Trailer) == announcedTrailers {
649656
copyHeader(w.Header(), resp.Trailer)
650657
}
@@ -705,77 +712,162 @@ func (p *proxyapp) handleTunnel(w http.ResponseWriter, r *http.Request) {
705712

706713
p.logger.Debug().Msgf("%s - %s - %s", r.Proto, r.Method, r.Host)
707714
p.logger.Debug().Msgf("src: %s - dst: %s", srcConnStr, dstConnStr)
708-
reqChan := make(chan []byte)
709-
respChan := make(chan []byte)
715+
reqChan := make(chan layers.Layer)
716+
respChan := make(chan layers.Layer)
710717
var wg sync.WaitGroup
711718
wg.Add(2)
712719
go p.transfer(&wg, dstConn, srcConn, dstConnStr, srcConnStr, reqChan)
713720
go p.transfer(&wg, srcConn, dstConn, srcConnStr, dstConnStr, respChan)
714721
if p.sniff {
715722
wg.Add(1)
716-
sniffheader := make([]string, 0, 4)
717-
if p.sniffnocolor {
718-
sniffheader = append(sniffheader, fmt.Sprintf("{\"connection\":{\"src_local\":%q,\"src_remote\":%q,\"dst_local\":%q,\"dst_remote\":%q}}",
723+
sniffheader := make([]string, 0, 6)
724+
id := uuid.New()
725+
if p.json {
726+
sniffheader = append(sniffheader, fmt.Sprintf("{\"connection\":{\"src_local\":%s,\"src_remote\":%s,\"dst_local\":%s,\"dst_remote\":%s}}",
719727
srcConn.LocalAddr(), srcConn.RemoteAddr(), dstConn.LocalAddr(), dstConn.RemoteAddr()))
720728
j, err := json.Marshal(&layers.HTTPMessage{Request: r})
721729
if err == nil {
722730
sniffheader = append(sniffheader, string(j))
723731
}
732+
} else {
733+
// TODO:
724734
}
725-
go p.sniffreporter(&wg, &sniffheader, reqChan, respChan)
735+
go p.sniffreporter(&wg, &sniffheader, reqChan, respChan, id)
726736
}
727737
wg.Wait()
728738
}
729739

730-
func (p *proxyapp) sniffreporter(wg *sync.WaitGroup, sniffheader *[]string, reqChan <-chan []byte, respChan <-chan []byte) {
740+
func (p *proxyapp) colorizeReqResp(req, resp layers.Layer, sniffheader *[]string, id uuid.UUID) error {
741+
switch reqt := req.(type) {
742+
case *layers.HTTPMessage:
743+
var reqBodySaved, respBodySaved []byte
744+
rest := resp.(*layers.HTTPMessage)
745+
if p.body {
746+
reqBodySaved, _ = io.ReadAll(reqt.Request.Body)
747+
respBodySaved, _ = io.ReadAll(rest.Response.Body)
748+
reqBodySaved = bytes.Trim(reqBodySaved, "\r\n\t ")
749+
respBodySaved = bytes.Trim(respBodySaved, "\r\n\t ")
750+
}
751+
if p.json {
752+
j1, err := json.Marshal(reqt)
753+
if err != nil {
754+
return err
755+
}
756+
j2, err := json.Marshal(rest)
757+
if err != nil {
758+
return err
759+
}
760+
*sniffheader = append(*sniffheader, string(j1), string(j2))
761+
if p.body && len(reqBodySaved) > 0 {
762+
*sniffheader = append(*sniffheader, fmt.Sprintf("{\"req_body\":%s}", reqBodySaved))
763+
}
764+
if p.body && len(respBodySaved) > 0 {
765+
*sniffheader = append(*sniffheader, fmt.Sprintf("{\"resp_body\":%s}", respBodySaved))
766+
}
767+
} else {
768+
*sniffheader = append(*sniffheader, p.colorizeHTTP(reqt.Request, rest.Response, &reqBodySaved, &respBodySaved, id))
769+
}
770+
case *layers.TLSMessage:
771+
var chs *layers.TLSClientHello
772+
var shs *layers.TLSServerHello
773+
if len(reqt.Records) > 0 {
774+
hsrec := reqt.Records[0]
775+
if hsrec.ContentType == layers.HandshakeTLSVal { // TODO: add more cases, parse all records
776+
switch parser := layers.HSTLSParserByType(hsrec.Data[0]).(type) {
777+
case *layers.TLSClientHello:
778+
err := parser.ParseHS(hsrec.Data)
779+
if err != nil {
780+
return err
781+
}
782+
chs = parser
783+
}
784+
}
785+
}
786+
rest := resp.(*layers.TLSMessage)
787+
if len(rest.Records) > 0 {
788+
hsrec := rest.Records[0]
789+
if hsrec.ContentType == layers.HandshakeTLSVal {
790+
switch parser := layers.HSTLSParserByType(hsrec.Data[0]).(type) {
791+
case *layers.TLSServerHello:
792+
err := parser.ParseHS(hsrec.Data)
793+
if err != nil {
794+
return err
795+
}
796+
shs = parser
797+
}
798+
}
799+
}
800+
if chs != nil && shs != nil {
801+
if p.json {
802+
j1, err := json.Marshal(chs)
803+
if err != nil {
804+
return err
805+
}
806+
j2, err := json.Marshal(shs)
807+
if err != nil {
808+
return err
809+
}
810+
*sniffheader = append(*sniffheader, string(j1), string(j2))
811+
} else {
812+
*sniffheader = append(*sniffheader, p.colorizeTLS(chs, shs, id))
813+
}
814+
}
815+
}
816+
return nil
817+
}
818+
819+
func (p *proxyapp) sniffreporter(wg *sync.WaitGroup, sniffheader *[]string, reqChan, respChan <-chan layers.Layer, id uuid.UUID) {
731820
defer wg.Done()
732821
sniffheaderlen := len(*sniffheader)
822+
var reqQueue, respQueue []layers.Layer
733823
for {
734-
req, okreq := <-reqChan // FIX: if resp comes first it blocks
735-
resp, okresp := <-respChan
736-
if !okreq || !okresp {
737-
return
824+
select {
825+
case req, ok := <-reqChan:
826+
if !ok {
827+
return
828+
} else {
829+
reqQueue = append(reqQueue, req)
830+
}
831+
case resp, ok := <-respChan:
832+
if !ok {
833+
return
834+
} else if len(reqQueue) > 0 { // HACK: is this right?
835+
respQueue = append(respQueue, resp)
836+
}
837+
}
838+
if len(reqQueue) > 0 && len(respQueue) > 0 {
839+
req := reqQueue[0]
840+
resp := respQueue[0]
841+
reqQueue = reqQueue[1:]
842+
respQueue = respQueue[1:]
843+
844+
err := p.colorizeReqResp(req, resp, sniffheader, id)
845+
if err == nil {
846+
if p.json {
847+
p.snifflogger.Log().Msg(fmt.Sprintf("[%s]", strings.Join(*sniffheader, ",")))
848+
} else {
849+
p.snifflogger.Log().Msg(fmt.Sprintf("%s", strings.Join(*sniffheader, "\n")))
850+
}
851+
}
852+
*sniffheader = (*sniffheader)[:sniffheaderlen]
738853
}
739-
*sniffheader = append(*sniffheader, string(req), string(resp))
740-
p.snifflogger.Debug().Msg(fmt.Sprintf("[%s]", strings.Join(*sniffheader, ",")))
741-
*sniffheader = (*sniffheader)[:sniffheaderlen]
742854
}
743855
}
744856

745-
func sniff(data []byte, nocolor bool) ([]byte, error) {
857+
func dispatch(data []byte) (layers.Layer, error) {
746858
// TODO: check if it is http or tls beforehand
747859
h := &layers.HTTPMessage{}
748860
if err := h.Parse(data); err == nil && !h.IsEmpty() {
749-
j, err := json.Marshal(h)
750-
if err == nil {
751-
return j, nil
752-
}
861+
return h, nil
753862
}
754863
m := &layers.TLSMessage{}
755-
if err := m.Parse(data); err != nil {
756-
return nil, err
757-
}
758-
if len(m.Records) > 0 {
759-
hsrec := m.Records[0]
760-
if hsrec.ContentType == layers.HandshakeTLSVal { // TODO: add more cases, parse all records
761-
switch parser := layers.HSTLSParserByType(hsrec.Data[0]).(type) {
762-
case *layers.TLSClientHello, *layers.TLSServerHello:
763-
err := parser.ParseHS(hsrec.Data)
764-
if err != nil {
765-
return nil, err
766-
}
767-
j, err := json.Marshal(parser)
768-
if err != nil {
769-
return nil, err
770-
}
771-
return j, nil
772-
}
773-
}
864+
if err := m.Parse(data); err == nil {
865+
return m, nil
774866
}
775867
return nil, fmt.Errorf("failed sniffing traffic")
776868
}
777869

778-
func (p *proxyapp) copyWithTimeout(dst net.Conn, src net.Conn, msgChan chan<- []byte) (written int64, err error) {
870+
func (p *proxyapp) copyWithTimeout(dst net.Conn, src net.Conn, msgChan chan<- layers.Layer) (written int64, err error) {
779871
buf := make([]byte, 32*1024)
780872
for {
781873
er := src.SetReadDeadline(time.Now().Add(readTimeout))
@@ -791,9 +883,9 @@ func (p *proxyapp) copyWithTimeout(dst net.Conn, src net.Conn, msgChan chan<- []
791883
break
792884
}
793885
if p.sniff {
794-
s, err := sniff(buf[0:nr], p.sniffnocolor)
886+
l, err := dispatch(buf[0:nr])
795887
if err == nil {
796-
msgChan <- s
888+
msgChan <- l
797889
}
798890
}
799891
nw, ew := dst.Write(buf[0:nr])
@@ -828,7 +920,7 @@ func (p *proxyapp) copyWithTimeout(dst net.Conn, src net.Conn, msgChan chan<- []
828920
return written, err
829921
}
830922

831-
func (p *proxyapp) transfer(wg *sync.WaitGroup, dst net.Conn, src net.Conn, destName, srcName string, msgChan chan<- []byte) {
923+
func (p *proxyapp) transfer(wg *sync.WaitGroup, dst net.Conn, src net.Conn, destName, srcName string, msgChan chan<- layers.Layer) {
832924
defer func() {
833925
wg.Done()
834926
close(msgChan)

tproxy_linux.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"time"
1515
"unsafe"
1616

17+
"github.com/google/uuid"
18+
"github.com/shadowy-pycoder/mshark/layers"
1719
"golang.org/x/net/proxy"
1820
"golang.org/x/sys/unix"
1921
)
@@ -169,18 +171,23 @@ func (ts *tproxyServer) handleConnection(srcConn net.Conn) {
169171

170172
ts.pa.logger.Debug().Msgf("[tproxy] src: %s - dst: %s", srcConnStr, dstConnStr)
171173

172-
reqChan := make(chan []byte)
173-
respChan := make(chan []byte)
174+
reqChan := make(chan layers.Layer)
175+
respChan := make(chan layers.Layer)
174176
var wg sync.WaitGroup
175177
wg.Add(2)
176178
go ts.pa.transfer(&wg, dstConn, srcConn, dstConnStr, srcConnStr, reqChan)
177179
go ts.pa.transfer(&wg, srcConn, dstConn, srcConnStr, dstConnStr, respChan)
178180
if ts.pa.sniff {
179181
wg.Add(1)
180-
sniffheader := make([]string, 0, 3)
181-
sniffheader = append(sniffheader, fmt.Sprintf("{\"connection\":{\"tproxy_mode\":%q,\"src_local\":%q,\"src_remote\":%q,\"dst_local\":%q,\"dst_remote\":%q,\"original_dst\":%q}}",
182-
ts.pa.tproxyMode, srcConn.LocalAddr(), srcConn.RemoteAddr(), dstConn.LocalAddr(), dstConn.RemoteAddr(), dst))
183-
go ts.pa.sniffreporter(&wg, &sniffheader, reqChan, respChan)
182+
sniffheader := make([]string, 0, 6)
183+
id := uuid.New()
184+
if ts.pa.json {
185+
sniffheader = append(sniffheader, fmt.Sprintf("{\"connection\":{\"tproxy_mode\":%s,\"src_local\":%s,\"src_remote\":%s,\"dst_local\":%s,\"dst_remote\":%s,\"original_dst\":%s}}",
186+
ts.pa.tproxyMode, srcConn.LocalAddr(), srcConn.RemoteAddr(), dstConn.LocalAddr(), dstConn.RemoteAddr(), dst))
187+
} else {
188+
// TODO:
189+
}
190+
go ts.pa.sniffreporter(&wg, &sniffheader, reqChan, respChan, id)
184191
}
185192
wg.Wait()
186193
}

0 commit comments

Comments
 (0)