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,
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) {
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,
})
}
}

View file

@ -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
}

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
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)

View file

@ -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
}