new: _http and _https zeroconf acceptors

This commit is contained in:
evilsocket 2024-09-30 19:06:10 +02:00
commit 02871b0ae6
5 changed files with 130 additions and 41 deletions

View file

@ -25,7 +25,13 @@ var TCP_HANDLERS = map[string]Handler{
TLS: true, TLS: true,
Handle: ippClientHandler, Handle: ippClientHandler,
}, },
// TODO: _http at least "_http": {
Handle: httpClientHandler,
},
"_https": {
TLS: true,
Handle: httpClientHandler,
},
} }
type Acceptor struct { type Acceptor struct {
@ -42,6 +48,7 @@ type Acceptor struct {
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
handler Handler handler Handler
ippAttributes map[string]string ippAttributes map[string]string
httpPaths map[string]string
} }
type HandlerContext struct { type HandlerContext struct {
@ -52,9 +59,10 @@ type HandlerContext struct {
srvPort int srvPort int
srvTLS bool srvTLS bool
ippAttributes map[string]string 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()) context, ctcCancel := context.WithCancel(context.Background())
proto := ops.Ternary(strings.Contains(service, "_tcp"), "tcp", "udp").(string) 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, ctxCancel: ctcCancel,
srvHost: srvHost, srvHost: srvHost,
ippAttributes: ippAttributes, ippAttributes: ippAttributes,
httpPaths: httpPaths,
} }
for svcName, svcHandler := range TCP_HANDLERS { for svcName, svcHandler := range TCP_HANDLERS {
if strings.Contains(service, svcName) { if strings.Contains(service, svcName) {
acceptor.tlsConfig = tlsConfig if svcHandler.TLS {
acceptor.tlsConfig = tlsConfig
}
acceptor.handler = svcHandler acceptor.handler = svcHandler
break 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) mod.Warning("no protocol handler found for service %s, using generic %s dump handler", tui.Yellow(service), proto)
acceptor.handler.Handle = handleGenericTCP acceptor.handler.Handle = handleGenericTCP
} else { } 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 return &acceptor
@ -114,6 +125,7 @@ func (a *Acceptor) startTCP() (err error) {
srvPort: int(a.port), srvPort: int(a.port),
srvTLS: a.tlsConfig != nil, srvTLS: a.tlsConfig != nil,
ippAttributes: a.ippAttributes, ippAttributes: a.ippAttributes,
httpPaths: a.httpPaths,
}) })
} }
} }

View file

@ -172,7 +172,7 @@ func (mod *ZeroGod) startAdvertiser(fileName string) error {
for _, svc := range advertiser.Services { for _, svc := range advertiser.Services {
// if no external responder has been specified // if no external responder has been specified
if svc.Responder == "" { 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 { if err := acceptor.Start(); err != nil {
return err return err
} }

View file

@ -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 := `<html>
<head><title>Not Found</title></head>
<body>
<center><h1>Not Found</h1></center>
</body>
</html>`
// 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())
}
}

View file

@ -1,12 +1,7 @@
package zerogod package zerogod
import ( import (
"bufio"
"bytes"
"fmt" "fmt"
"io"
"net/http"
"strings"
"time" "time"
"github.com/evilsocket/islazy/tui" "github.com/evilsocket/islazy/tui"
@ -42,40 +37,17 @@ type PrintData struct {
func ippClientHandler(ctx *HandlerContext) { func ippClientHandler(ctx *HandlerContext) {
defer ctx.client.Close() defer ctx.client.Close()
clientIP := strings.SplitN(ctx.client.RemoteAddr().String(), ":", 2)[0] shouldQuit, clientIP, httpRequest, err := httpGenericHandler(ctx)
if shouldQuit {
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)
return return
} else if err != nil {
ctx.mod.Error("%v", err)
} }
raw_req := buf[0:read] clientUA := httpRequest.UserAgent()
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()
ctx.mod.Info("%v -> %s", clientIP, tui.Green(clientUA)) 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 { if err != nil {
ctx.mod.Error("%v", err) ctx.mod.Error("%v", err)
return return
@ -84,7 +56,7 @@ func ippClientHandler(ctx *HandlerContext) {
// parse as IPP // parse as IPP
ipp_req, err := ipp.NewRequestDecoder(ipp_body).Decode(nil) ipp_req, err := ipp.NewRequestDecoder(ipp_body).Decode(nil)
if err != 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 return
} }
@ -118,7 +90,7 @@ func ippClientHandler(ctx *HandlerContext) {
ippOnGetJobs(ctx, ipp_req) ippOnGetJobs(ctx, ipp_req)
// Print-Job // Print-Job
case 0x0002: case 0x0002:
ippOnPrintJob(ctx, http_req, ipp_req) ippOnPrintJob(ctx, httpRequest, ipp_req)
// Get-Job-Attributes // Get-Job-Attributes
case 0x0009: case 0x0009:
ippOnGetJobAttributes(ctx, ipp_req) ippOnGetJobAttributes(ctx, ipp_req)

View file

@ -17,6 +17,7 @@ type ServiceData struct {
Records []string `yaml:"records,omitempty"` // Service DNS text records Records []string `yaml:"records,omitempty"` // Service DNS text records
Responder string `yaml:"responder,omitempty"` // Optional IP to use instead of our tcp acceptor 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 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 server *zeroconf.Server
} }