diff --git a/modules/zerogod/zerogod_acceptor.go b/modules/zerogod/zerogod_acceptor.go index 09679cca..ccb956ef 100644 --- a/modules/zerogod/zerogod_acceptor.go +++ b/modules/zerogod/zerogod_acceptor.go @@ -25,7 +25,13 @@ var TCP_HANDLERS = map[string]Handler{ TLS: true, Handle: ippClientHandler, }, - // TODO: _http at least + "_http": { + Handle: httpClientHandler, + }, + "_https": { + TLS: true, + Handle: httpClientHandler, + }, } type Acceptor struct { @@ -42,6 +48,7 @@ type Acceptor struct { ctxCancel context.CancelFunc handler Handler ippAttributes map[string]string + httpPaths map[string]string } type HandlerContext struct { @@ -52,9 +59,10 @@ type HandlerContext struct { srvPort int srvTLS bool ippAttributes map[string]string + httpPaths map[string]string } -func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsConfig *tls.Config, ippAttributes map[string]string) *Acceptor { +func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsConfig *tls.Config, ippAttributes map[string]string, httpPaths map[string]string) *Acceptor { context, ctcCancel := context.WithCancel(context.Background()) proto := ops.Ternary(strings.Contains(service, "_tcp"), "tcp", "udp").(string) @@ -67,11 +75,14 @@ func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsC ctxCancel: ctcCancel, srvHost: srvHost, ippAttributes: ippAttributes, + httpPaths: httpPaths, } for svcName, svcHandler := range TCP_HANDLERS { if strings.Contains(service, svcName) { - acceptor.tlsConfig = tlsConfig + if svcHandler.TLS { + acceptor.tlsConfig = tlsConfig + } acceptor.handler = svcHandler break } @@ -81,7 +92,7 @@ func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsC mod.Warning("no protocol handler found for service %s, using generic %s dump handler", tui.Yellow(service), proto) acceptor.handler.Handle = handleGenericTCP } else { - mod.Info("found %s %s protocol handler", proto, tui.Green(service)) + mod.Info("found %s %s protocol handler (tls=%v)", proto, tui.Green(service), acceptor.tlsConfig != nil) } return &acceptor @@ -114,6 +125,7 @@ func (a *Acceptor) startTCP() (err error) { srvPort: int(a.port), srvTLS: a.tlsConfig != nil, ippAttributes: a.ippAttributes, + httpPaths: a.httpPaths, }) } } diff --git a/modules/zerogod/zerogod_advertise.go b/modules/zerogod/zerogod_advertise.go index 82eb9dd3..2b350173 100644 --- a/modules/zerogod/zerogod_advertise.go +++ b/modules/zerogod/zerogod_advertise.go @@ -172,7 +172,7 @@ func (mod *ZeroGod) startAdvertiser(fileName string) error { for _, svc := range advertiser.Services { // if no external responder has been specified if svc.Responder == "" { - acceptor := NewAcceptor(mod, svc.FullName(), hostName, uint16(svc.Port), tlsConfig, svc.IPP) + acceptor := NewAcceptor(mod, svc.FullName(), hostName, uint16(svc.Port), tlsConfig, svc.IPP, svc.HTTP) if err := acceptor.Start(); err != nil { return err } diff --git a/modules/zerogod/zerogod_http_handler.go b/modules/zerogod/zerogod_http_handler.go new file mode 100644 index 00000000..45ad0893 --- /dev/null +++ b/modules/zerogod/zerogod_http_handler.go @@ -0,0 +1,104 @@ +package zerogod + +import ( + "bufio" + "bytes" + "fmt" + "io" + "net/http" + "strings" + + "github.com/evilsocket/islazy/tui" +) + +func httpGenericHandler(ctx *HandlerContext) (shouldQuit bool, clientIP string, req *http.Request, err error) { + clientIP = strings.SplitN(ctx.client.RemoteAddr().String(), ":", 2)[0] + + buf := make([]byte, 4096) + // read raw request + read, err := ctx.client.Read(buf) + if err != nil { + if err == io.EOF { + ctx.mod.Debug("EOF, client %s disconnected", clientIP) + return true, clientIP, nil, nil + } + ctx.mod.Warning("error while reading from %v: %v", clientIP, err) + return true, clientIP, nil, err + } else if read == 0 { + ctx.mod.Warning("error while reading from %v: no data", clientIP) + return true, clientIP, nil, err + } + + raw_req := buf[0:read] + + ctx.mod.Debug("read %d bytes from %v:\n%s\n", read, clientIP, Dump(raw_req)) + + // parse as http + reader := bufio.NewReader(bytes.NewReader(raw_req)) + httpRequest, err := http.ReadRequest(reader) + if err != nil { + ctx.mod.Error("error while parsing http request from %v: %v\n%s", clientIP, err, Dump(raw_req)) + return true, clientIP, nil, err + } + + return false, clientIP, httpRequest, nil +} + +func httpLogRequest(ctx *HandlerContext, clientIP string, httpRequest *http.Request) { + clientUA := httpRequest.UserAgent() + ctx.mod.Info("%v (%s) > %s %s", + clientIP, + tui.Green(clientUA), + tui.Bold(httpRequest.Method), + tui.Yellow(httpRequest.RequestURI)) +} + +func httpClientHandler(ctx *HandlerContext) { + defer ctx.client.Close() + + shouldQuit, clientIP, httpRequest, err := httpGenericHandler(ctx) + if shouldQuit { + return + } else if err != nil { + ctx.mod.Error("%v", err) + } + + httpLogRequest(ctx, clientIP, httpRequest) + + respStatusCode := 404 + respStatus := "Not Found" + respBody := ` +Not Found + +

Not Found

+ +` + + // see if anything in config matches + for path, body := range ctx.httpPaths { + if httpRequest.RequestURI == path { + respStatusCode = 200 + respStatus = "OK" + respBody = body + break + } + } + + response := fmt.Sprintf(`HTTP/1.1 %d %s +Content-Type: text/html; charset=utf-8 +Content-Length: %d +Connection: close + +%s`, + respStatusCode, + respStatus, + len(respBody), + respBody, + ) + + if _, err = ctx.client.Write([]byte(response)); err != nil { + ctx.mod.Error("error while writing http response data: %v", err) + } else { + ctx.mod.Debug("sent %d of http response to %v", len(response), ctx.client.RemoteAddr()) + } +} diff --git a/modules/zerogod/zerogod_ipp_handler.go b/modules/zerogod/zerogod_ipp_handler.go index 9526abea..73ce10b7 100644 --- a/modules/zerogod/zerogod_ipp_handler.go +++ b/modules/zerogod/zerogod_ipp_handler.go @@ -1,12 +1,7 @@ package zerogod import ( - "bufio" - "bytes" "fmt" - "io" - "net/http" - "strings" "time" "github.com/evilsocket/islazy/tui" @@ -42,40 +37,17 @@ type PrintData struct { func ippClientHandler(ctx *HandlerContext) { defer ctx.client.Close() - clientIP := strings.SplitN(ctx.client.RemoteAddr().String(), ":", 2)[0] - - buf := make([]byte, 4096) - - // read raw request - read, err := ctx.client.Read(buf) - if err != nil { - if err == io.EOF { - ctx.mod.Debug("EOF, client %s disconnected", clientIP) - return - } - ctx.mod.Warning("error while reading from %v: %v", clientIP, err) - return - } else if read == 0 { - ctx.mod.Warning("error while reading from %v: no data", clientIP) + shouldQuit, clientIP, httpRequest, err := httpGenericHandler(ctx) + if shouldQuit { return + } else if err != nil { + ctx.mod.Error("%v", err) } - raw_req := buf[0:read] - - ctx.mod.Debug("read %d bytes from %v:\n%s\n", read, clientIP, Dump(raw_req)) - - // parse as http - reader := bufio.NewReader(bytes.NewReader(raw_req)) - http_req, err := http.ReadRequest(reader) - if err != nil { - ctx.mod.Error("error while parsing http request from %v: %v\n%s", clientIP, err, Dump(raw_req)) - return - } - - clientUA := http_req.UserAgent() + clientUA := httpRequest.UserAgent() ctx.mod.Info("%v -> %s", clientIP, tui.Green(clientUA)) - ipp_body, err := ippReadRequestBody(ctx, http_req) + ipp_body, err := ippReadRequestBody(ctx, httpRequest) if err != nil { ctx.mod.Error("%v", err) return @@ -84,7 +56,7 @@ func ippClientHandler(ctx *HandlerContext) { // parse as IPP ipp_req, err := ipp.NewRequestDecoder(ipp_body).Decode(nil) if err != nil { - ctx.mod.Error("error while parsing ipp request from %v: %v -> %++v", clientIP, err, *http_req) + ctx.mod.Error("error while parsing ipp request from %v: %v -> %++v", clientIP, err, *httpRequest) return } @@ -118,7 +90,7 @@ func ippClientHandler(ctx *HandlerContext) { ippOnGetJobs(ctx, ipp_req) // Print-Job case 0x0002: - ippOnPrintJob(ctx, http_req, ipp_req) + ippOnPrintJob(ctx, httpRequest, ipp_req) // Get-Job-Attributes case 0x0009: ippOnGetJobAttributes(ctx, ipp_req) diff --git a/modules/zerogod/zerogod_service.go b/modules/zerogod/zerogod_service.go index 200e22fe..a2429a8c 100644 --- a/modules/zerogod/zerogod_service.go +++ b/modules/zerogod/zerogod_service.go @@ -17,6 +17,7 @@ type ServiceData struct { Records []string `yaml:"records,omitempty"` // Service DNS text records Responder string `yaml:"responder,omitempty"` // Optional IP to use instead of our tcp acceptor IPP map[string]string `yaml:"ipp,omitempty"` // Optional IPP attributes map + HTTP map[string]string `yaml:"http,omitempty"` // Optional HTTP URIs map server *zeroconf.Server }