misc: small fix or general refactoring i did not bother commenting

This commit is contained in:
Simone Margaritelli 2024-09-20 12:06:32 +02:00
commit 17ba1be16c
12 changed files with 239 additions and 498 deletions

3
.gitignore vendored
View file

@ -15,4 +15,5 @@ stage/
/snap /snap
.idea .idea
/cover.out /cover.out
/can-data /can-data
/test*.yml

View file

@ -1,26 +0,0 @@
- name: TEST_NAME
service: _ipps._tcp
domain: local.
port: 6631
records:
- txtvers=1
- ty=EPSON XP-630 Series
- usb_MFG=EPSON
- usb_MDL=XP-630 Series
- product=(EPSON XP-630 Series)
- pdl=application/octet-stream,image/pwg-raster,image/urf,image/jpeg
- rp=ipp/print
- qtotal=1
- Color=T
- Duplex=T
- Scan=T
- Fax=F
- kind=document,envelope,label,photo
- PaperMax=legal-A4
- URF=CP1,MT1-3-5-8-10-11-12,PQ4-5,OB9,OFU0,RS360,SRGB24,W8,DM3,IS1-7,V1.4
- mopria-certified=1.2
- priority=30
- adminurl=http://EPSON59F5BA.local.:80/PRESENTATION/BONJOUR
- note=
- UUID=cfe92100-67c4-11d4-a45f-44d24459f5ba
- TLS=1.2

View file

@ -1,27 +0,0 @@
- name: TEST_NAME
service: _ipps._tcp
domain: local.
port: 6631
responder: 134.122.95.96
records:
- txtvers=1
- ty=EPSON XP-630 Series
- usb_MFG=EPSON
- usb_MDL=XP-630 Series
- product=(EPSON XP-630 Series)
- pdl=application/octet-stream,image/pwg-raster,image/urf,image/jpeg
- rp=ipp/print
- qtotal=1
- Color=T
- Duplex=T
- Scan=T
- Fax=F
- kind=document,envelope,label,photo
- PaperMax=legal-A4
- URF=CP1,MT1-3-5-8-10-11-12,PQ4-5,OB9,OFU0,RS360,SRGB24,W8,DM3,IS1-7,V1.4
- mopria-certified=1.2
- priority=30
- adminurl=http://EPSON59F5BA.local.:80/PRESENTATION/BONJOUR
- note=
- UUID=cfe92100-67c4-11d4-a45f-44d24459f5ba
- TLS=1.2

View file

@ -12,10 +12,10 @@ import (
type Handler struct { type Handler struct {
TLS bool TLS bool
Handle func(mod *ZeroGod, client net.Conn, srvHost string, srvPort int, srvTLS bool) Handle func(ctx *HandlerContext)
} }
// TODO: add more and possibly autodetect from peeking at the first bytes sent by the client // TODO: possibly autodetect from peeking at the first bytes sent by the client
var TCP_HANDLERS = map[string]Handler{ var TCP_HANDLERS = map[string]Handler{
"_ipp": { "_ipp": {
Handle: ippClientHandler, Handle: ippClientHandler,
@ -28,27 +28,38 @@ var TCP_HANDLERS = map[string]Handler{
} }
type Acceptor struct { type Acceptor struct {
mod *ZeroGod mod *ZeroGod
srvHost string srvHost string
port uint16 port uint16
service string service string
tlsConfig *tls.Config tlsConfig *tls.Config
listener net.Listener listener net.Listener
running bool running bool
context context.Context context context.Context
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
handler Handler handler Handler
ippAttributes map[string]string
} }
func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsConfig *tls.Config) *Acceptor { type HandlerContext struct {
mod *ZeroGod
client net.Conn
srvHost string
srvPort int
srvTLS bool
ippAttributes map[string]string
}
func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsConfig *tls.Config, ippAttributes map[string]string) *Acceptor {
context, ctcCancel := context.WithCancel(context.Background()) context, ctcCancel := context.WithCancel(context.Background())
acceptor := Acceptor{ acceptor := Acceptor{
mod: mod, mod: mod,
port: port, port: port,
service: service, service: service,
context: context, context: context,
ctxCancel: ctcCancel, ctxCancel: ctcCancel,
srvHost: srvHost, srvHost: srvHost,
ippAttributes: ippAttributes,
} }
for svcName, svcHandler := range TCP_HANDLERS { for svcName, svcHandler := range TCP_HANDLERS {
@ -90,7 +101,14 @@ func (a *Acceptor) Start() (err error) {
} }
} else { } else {
a.mod.Info("accepted connection for service %s (port %d): %v", tui.Green(a.service), a.port, conn.RemoteAddr()) a.mod.Info("accepted connection for service %s (port %d): %v", tui.Green(a.service), a.port, conn.RemoteAddr())
go a.handler.Handle(a.mod, conn, a.srvHost, int(a.port), a.tlsConfig != nil) go a.handler.Handle(&HandlerContext{
mod: a.mod,
client: conn,
srvHost: a.srvHost,
srvPort: int(a.port),
srvTLS: a.tlsConfig != nil,
ippAttributes: a.ippAttributes,
})
} }
} }
a.mod.Debug("tcp listener for port %d (%s) stopped", a.port, tui.Green(a.service)) a.mod.Debug("tcp listener for port %d (%s) stopped", a.port, tui.Green(a.service))

View file

@ -5,7 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"os" "os"
"strings" "strings"
"time" "time"
@ -13,15 +12,13 @@ import (
tls_utils "github.com/bettercap/bettercap/v2/tls" tls_utils "github.com/bettercap/bettercap/v2/tls"
"github.com/bettercap/bettercap/v2/zeroconf" "github.com/bettercap/bettercap/v2/zeroconf"
"github.com/evilsocket/islazy/fs" "github.com/evilsocket/islazy/fs"
"github.com/evilsocket/islazy/tui"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
type Advertiser struct { type Advertiser struct {
Filename string Filename string
Services []ServiceData Services []*ServiceData
Servers []*zeroconf.Server
Acceptors []*Acceptor Acceptors []*Acceptor
} }
@ -99,7 +96,7 @@ func (mod *ZeroGod) startAdvertiser(fileName string) error {
return fmt.Errorf("could not read %s: %v", fileName, err) return fmt.Errorf("could not read %s: %v", fileName, err)
} }
var services []ServiceData var services []*ServiceData
if err = yaml.Unmarshal(data, &services); err != nil { if err = yaml.Unmarshal(data, &services); err != nil {
return fmt.Errorf("could not deserialize %s: %v", fileName, err) return fmt.Errorf("could not deserialize %s: %v", fileName, err)
} }
@ -109,101 +106,47 @@ func (mod *ZeroGod) startAdvertiser(fileName string) error {
advertiser := &Advertiser{ advertiser := &Advertiser{
Filename: fileName, Filename: fileName,
Services: services, Services: services,
Servers: make([]*zeroconf.Server, 0),
Acceptors: make([]*Acceptor, 0), Acceptors: make([]*Acceptor, 0),
} }
svcChan := make(chan setupResult)
// paralleize initialization // paralleize initialization
for _, svc := range services { svcChan := make(chan error)
go func(svc ServiceData) { for _, svc := range advertiser.Services {
mod.Info("unregistering instance %s ...", tui.Yellow(svc.FullName())) go func(svc *ServiceData) {
// deregister the service from the network first // deregister the service from the network first
if err := svc.Unregister(); err != nil { if err := svc.Unregister(mod); err != nil {
svcChan <- setupResult{err: fmt.Errorf("could not unregister service %s: %v", svc.FullName(), err)} svcChan <- fmt.Errorf("could not unregister service %s: %v", svc.FullName(), err)
return return
} }
// give some time to the network to adjust // give some time to the network to adjust
time.Sleep(time.Duration(1) * time.Second) time.Sleep(time.Duration(1) * time.Second)
var server *zeroconf.Server // register it
if err := svc.Register(mod); err != nil {
// now create it again to actually advertise svcChan <- err
if svc.Responder == "" {
// use our own IP
if server, err = zeroconf.Register(
svc.Name,
svc.Service,
svc.Domain,
svc.Port,
svc.Records,
nil); err != nil {
svcChan <- setupResult{err: fmt.Errorf("could not create service %s: %v", svc.FullName(), err)}
return
}
mod.Info("advertising %s with responder=%s port=%d",
tui.Yellow(svc.FullName()),
tui.Red(svc.Responder),
svc.Port)
} else { } else {
responderHostName := "" svcChan <- nil
// try first to do a reverse DNS of the ip
if addr, err := net.LookupAddr(svc.Responder); err == nil && len(addr) > 0 {
responderHostName = addr[0]
} else {
mod.Debug("could not get responder %s reverse dns entry: %v", svc.Responder, err)
}
// if we don't have a host, create a .nip.io representation
if responderHostName == "" {
responderHostName = fmt.Sprintf("%s.nip.io.", strings.ReplaceAll(svc.Responder, ".", "-"))
}
// use external responder
if server, err = zeroconf.RegisterExternalResponder(
svc.Name,
svc.Service,
svc.Domain,
svc.Port,
responderHostName,
[]string{svc.Responder},
svc.Records,
nil); err != nil {
svcChan <- setupResult{err: fmt.Errorf("could not create service %s: %v", svc.FullName(), err)}
return
}
mod.Info("advertising %s with responder=%s hostname=%s port=%d",
tui.Yellow(svc.FullName()),
tui.Red(svc.Responder),
tui.Yellow(responderHostName),
svc.Port)
}
svcChan <- setupResult{
server: server,
} }
}(svc) }(svc)
} }
for res := range svcChan { got := 0
if res.err != nil { for err := range svcChan {
return res.err if err != nil {
return err
} }
advertiser.Servers = append(advertiser.Servers, res.server) got++
if len(advertiser.Servers) == len(advertiser.Services) { if got == len(advertiser.Services) {
break break
} }
} }
// now create the tcp acceptors for entries without an explicit responder address // now create the tcp acceptors for entries without an explicit responder address
for _, svc := range advertiser.Services { for _, svc := range advertiser.Services {
// if no external responder has been specified
if svc.Responder == "" { if svc.Responder == "" {
acceptor := NewAcceptor(mod, svc.FullName(), hostName, uint16(svc.Port), tlsConfig) acceptor := NewAcceptor(mod, svc.FullName(), hostName, uint16(svc.Port), tlsConfig, svc.IPP)
if err := acceptor.Start(); err != nil { if err := acceptor.Start(); err != nil {
return err return err
} }
@ -225,9 +168,8 @@ func (mod *ZeroGod) stopAdvertiser() error {
mod.Info("stopping %d services ...", len(mod.advertiser.Services)) mod.Info("stopping %d services ...", len(mod.advertiser.Services))
for key, server := range mod.advertiser.Servers { for _, service := range mod.advertiser.Services {
mod.Info("stopping %s ...", key) service.Unregister(mod)
server.Shutdown()
} }
mod.Info("all services stopped") mod.Info("all services stopped")

View file

@ -42,7 +42,6 @@ func NewZeroGod(s *session.Session) *ZeroGod {
return mod.Stop() return mod.Stop()
})) }))
// TODO: add autocomplete
mod.AddHandler(session.NewModuleHandler("zerogod.show", "", mod.AddHandler(session.NewModuleHandler("zerogod.show", "",
"Show discovered services.", "Show discovered services.",
func(args []string) error { func(args []string) error {
@ -55,6 +54,7 @@ func NewZeroGod(s *session.Session) *ZeroGod {
return mod.show("", true) return mod.show("", true)
})) }))
// TODO: add autocomplete
mod.AddHandler(session.NewModuleHandler("zerogod.show ADDRESS", "zerogod.show (.+)", mod.AddHandler(session.NewModuleHandler("zerogod.show ADDRESS", "zerogod.show (.+)",
"Show discovered services given an ip address.", "Show discovered services given an ip address.",
func(args []string) error { func(args []string) error {

View file

@ -23,13 +23,11 @@ func (mod *ZeroGod) updateEndpointMeta(address string, endpoint *network.Endpoin
meta[fmt.Sprintf("mdns:%s:name", svcType)] = svc.ServiceName() meta[fmt.Sprintf("mdns:%s:name", svcType)] = svc.ServiceName()
meta[fmt.Sprintf("mdns:%s:hostname", svcType)] = svc.HostName meta[fmt.Sprintf("mdns:%s:hostname", svcType)] = svc.HostName
// TODO: include all for i, ip := range svc.AddrIPv4 {
if len(svc.AddrIPv4) > 0 { meta[fmt.Sprintf("mdns:%s:ipv4[%d]", svcType, i)] = ip.String()
meta[fmt.Sprintf("mdns:%s:ipv4", svcType)] = svc.AddrIPv4[0].String()
} }
for i, ip := range svc.AddrIPv6 {
if len(svc.AddrIPv6) > 0 { meta[fmt.Sprintf("mdns:%s:ipv6[%d]", svcType, i)] = ip.String()
meta[fmt.Sprintf("mdns:%s:ipv6", svcType)] = svc.AddrIPv6[0].String()
} }
meta[fmt.Sprintf("mdns:%s:port", svcType)] = fmt.Sprintf("%d", svc.Port) meta[fmt.Sprintf("mdns:%s:port", svcType)] = fmt.Sprintf("%d", svc.Port)

View file

@ -2,7 +2,6 @@ package zerogod
import ( import (
"fmt" "fmt"
"net"
) )
func Dump(by []byte) string { func Dump(by []byte) string {
@ -51,19 +50,19 @@ func viewString(b []byte) string {
return string(r) return string(r)
} }
func handleGenericTCP(mod *ZeroGod, client net.Conn, srvHost string, srvPort int, srvTLS bool) { func handleGenericTCP(ctx *HandlerContext) {
defer client.Close() defer ctx.client.Close()
buf := make([]byte, 1024) buf := make([]byte, 1024)
for { for {
if read, err := client.Read(buf); err != nil { if read, err := ctx.client.Read(buf); err != nil {
mod.Error("error while reading from %v: %v", client.RemoteAddr(), err) ctx.mod.Error("error while reading from %v: %v", ctx.client.RemoteAddr(), err)
break break
} else if read == 0 { } else if read == 0 {
mod.Error("error while reading from %v: no data", client.RemoteAddr()) ctx.mod.Error("error while reading from %v: no data", ctx.client.RemoteAddr())
break break
} else { } else {
mod.Info("read %d bytes from %v:\n%s\n", read, client.RemoteAddr(), Dump(buf[0:read])) ctx.mod.Info("read %d bytes from %v:\n%s\n", read, ctx.client.RemoteAddr(), Dump(buf[0:read]))
} }
} }
} }

View file

@ -5,7 +5,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"net"
"net/http" "net/http"
"time" "time"
@ -49,6 +48,15 @@ var IPP_REQUEST_NAMES = map[int16]string{
0x400D: "CUPS-Move-Job", 0x400D: "CUPS-Move-Job",
} }
var IPP_USER_ATTRIBUTES = map[string]string{
"printer-name": "PRINTER_NAME",
"printer-info": "PRINTER_INFO",
"printer-make-and-model": "PRINTER_MAKE PRINTER_MODEL",
"printer-location": "PRINTER_LOCATION",
"printer-privacy-policy-uri": "https://www.bettercap.org/",
"ppd-name": "everywhere",
}
func init() { func init() {
ipp.AttributeTagMapping["printer-uri-supported"] = ipp.TagUri ipp.AttributeTagMapping["printer-uri-supported"] = ipp.TagUri
ipp.AttributeTagMapping["uri-authentication-supported"] = ipp.TagKeyword ipp.AttributeTagMapping["uri-authentication-supported"] = ipp.TagKeyword
@ -74,56 +82,57 @@ func init() {
ipp.AttributeTagMapping["compression-supported"] = ipp.TagKeyword ipp.AttributeTagMapping["compression-supported"] = ipp.TagKeyword
ipp.AttributeTagMapping["printer-privacy-policy-uri"] = ipp.TagUri ipp.AttributeTagMapping["printer-privacy-policy-uri"] = ipp.TagUri
ipp.AttributeTagMapping["printer-location"] = ipp.TagText ipp.AttributeTagMapping["printer-location"] = ipp.TagText
ipp.AttributeTagMapping["ppd-name"] = ipp.TagName
} }
func ippClientHandler(mod *ZeroGod, client net.Conn, srvHost string, srvPort int, srvTLS bool) { func ippClientHandler(ctx *HandlerContext) {
defer client.Close() defer ctx.client.Close()
buf := make([]byte, 4096) buf := make([]byte, 4096)
// read raw request // read raw request
read, err := client.Read(buf) read, err := ctx.client.Read(buf)
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
return return
} }
mod.Error("error while reading from %v: %v", client.RemoteAddr(), err) ctx.mod.Error("error while reading from %v: %v", ctx.client.RemoteAddr(), err)
return return
} else if read == 0 { } else if read == 0 {
mod.Error("error while reading from %v: no data", client.RemoteAddr()) ctx.mod.Error("error while reading from %v: no data", ctx.client.RemoteAddr())
return return
} }
raw_req := buf[0:read] raw_req := buf[0:read]
mod.Debug("read %d bytes from %v:\n%s\n", read, client.RemoteAddr(), Dump(raw_req)) ctx.mod.Debug("read %d bytes from %v:\n%s\n", read, ctx.client.RemoteAddr(), Dump(raw_req))
// parse as http // parse as http
reader := bufio.NewReader(bytes.NewReader(raw_req)) reader := bufio.NewReader(bytes.NewReader(raw_req))
http_req, err := http.ReadRequest(reader) http_req, err := http.ReadRequest(reader)
if err != nil { if err != nil {
mod.Error("error while parsing http request from %v: %v", client.RemoteAddr(), err) ctx.mod.Error("error while parsing http request from %v: %v", ctx.client.RemoteAddr(), err)
return return
} }
mod.Info("%v -> %s", client.RemoteAddr(), tui.Green(http_req.UserAgent())) ctx.mod.Info("%v -> %s", ctx.client.RemoteAddr(), tui.Green(http_req.UserAgent()))
ipp_body := http_req.Body ipp_body := http_req.Body
// check for an Expect 100-continue // check for an Expect 100-continue
if http_req.Header.Get("Expect") == "100-continue" { if http_req.Header.Get("Expect") == "100-continue" {
// inform the client we're ready to read the request body // inform the client we're ready to read the request body
client.Write([]byte("HTTP/1.1 100 Continue\r\n\r\n")) ctx.client.Write([]byte("HTTP/1.1 100 Continue\r\n\r\n"))
// read the body // read the body
read, err := client.Read(buf) read, err := ctx.client.Read(buf)
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
return return
} }
mod.Error("error while reading ipp body from %v: %v", client.RemoteAddr(), err) ctx.mod.Error("error while reading ipp body from %v: %v", ctx.client.RemoteAddr(), err)
return return
} else if read == 0 { } else if read == 0 {
mod.Error("error while reading ipp body from %v: no data", client.RemoteAddr()) ctx.mod.Error("error while reading ipp body from %v: no data", ctx.client.RemoteAddr())
return return
} }
@ -133,7 +142,7 @@ func ippClientHandler(mod *ZeroGod, client net.Conn, srvHost string, srvPort int
// 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 {
mod.Error("error while parsing ip request from %v: %v", client.RemoteAddr(), err) ctx.mod.Error("error while parsing ip request from %v: %v", ctx.client.RemoteAddr(), err)
return return
} }
@ -142,24 +151,24 @@ func ippClientHandler(mod *ZeroGod, client net.Conn, srvHost string, srvPort int
ipp_op_name = name ipp_op_name = name
} }
mod.Info("%v op=%s attributes=%v", client.RemoteAddr(), tui.Bold(ipp_op_name), ipp_req.OperationAttributes) ctx.mod.Info("%v op=%s attributes=%v", ctx.client.RemoteAddr(), tui.Bold(ipp_op_name), ipp_req.OperationAttributes)
switch ipp_req.Operation { switch ipp_req.Operation {
// Get-Printer-Attributes // Get-Printer-Attributes
case 0x000B: case 0x000B:
ippOnGetPrinterAttributes(mod, client, ipp_req, srvHost, srvPort, srvTLS) ippOnGetPrinterAttributes(ctx, ipp_req)
default: default:
ippOnUnhandledRequest(mod, client, ipp_req, ipp_op_name) ippOnUnhandledRequest(ctx, ipp_req, ipp_op_name)
} }
} }
func ippSendResponse(mod *ZeroGod, client net.Conn, response *ipp.Response) { func ippSendResponse(ctx *HandlerContext, response *ipp.Response) {
mod.Debug("SENDING %++v", *response) ctx.mod.Debug("SENDING %++v", *response)
resp_data, err := response.Encode() resp_data, err := response.Encode()
if err != nil { if err != nil {
mod.Error("error while encoding ipp response: %v", err) ctx.mod.Error("error while encoding ipp response: %v", err)
return return
} }
@ -172,29 +181,29 @@ func ippSendResponse(mod *ZeroGod, client net.Conn, response *ipp.Response) {
} }
for _, header := range headers { for _, header := range headers {
if _, err := client.Write(header); err != nil { if _, err := ctx.client.Write(header); err != nil {
mod.Error("error while writing header: %v", err) ctx.mod.Error("error while writing header: %v", err)
return return
} }
} }
if _, err = client.Write(resp_data); err != nil { if _, err = ctx.client.Write(resp_data); err != nil {
mod.Error("error while writing ipp response data: %v", err) ctx.mod.Error("error while writing ipp response data: %v", err)
return return
} }
mod.Debug("sent %d of ipp response to %v", len(resp_data), client.RemoteAddr()) ctx.mod.Debug("sent %d of ipp response to %v", len(resp_data), ctx.client.RemoteAddr())
} }
func ippOnUnhandledRequest(mod *ZeroGod, client net.Conn, ipp_req *ipp.Request, ipp_op_name string) { func ippOnUnhandledRequest(ctx *HandlerContext, ipp_req *ipp.Request, ipp_op_name string) {
ipp_resp := ipp.NewResponse(ipp.StatusErrorOperationNotSupported, ipp_req.RequestId) ctx.mod.Warning("unhandled request from %v: operation=%s", ctx.client.RemoteAddr(), ipp_op_name)
ippSendResponse(mod, client, ipp_resp) ippSendResponse(ctx, ipp.NewResponse(
ipp.StatusErrorOperationNotSupported,
mod.Warning("unhandled request from %v: operation=%s", client.RemoteAddr(), ipp_op_name) ipp_req.RequestId))
} }
func ippOnGetPrinterAttributes(mod *ZeroGod, client net.Conn, ipp_req *ipp.Request, srvHost string, srvPort int, srvTLS bool) { func ippOnGetPrinterAttributes(ctx *HandlerContext, ipp_req *ipp.Request) {
ipp_resp := ipp.NewResponse(ipp.StatusOk, ipp_req.RequestId) ipp_resp := ipp.NewResponse(ipp.StatusOk, ipp_req.RequestId)
// https://tools.ietf.org/html/rfc2911 section 3.1.4.2 Response Operation Attributes // https://tools.ietf.org/html/rfc2911 section 3.1.4.2 Response Operation Attributes
@ -211,52 +220,15 @@ func ippOnGetPrinterAttributes(mod *ZeroGod, client net.Conn, ipp_req *ipp.Reque
}, },
} }
/* // collect user attributes
userProps := make(map[string]string)
""" for name, defaultValue := range IPP_USER_ATTRIBUTES {
marker-names (1setOf nameWithoutLanguage) = Black ink,Cyan ink,Magenta ink,Yellow ink if value, found := ctx.ippAttributes[name]; found {
marker-colors (1setOf nameWithoutLanguage) = #000000,#00FFFF,#FF00FF,#FFFF00 userProps[name] = value
marker-types (1setOf keyword) = ink-cartridge,ink-cartridge,ink-cartridge,ink-cartridge } else {
marker-low-levels (1setOf integer) = 15,15,15,15 userProps[name] = defaultValue
marker-high-levels (1setOf integer) = 100,100,100,100 }
marker-levels (1setOf integer) = 57,94,95,95 }
"""
markers = {
(
SectionEnum.printer,
b'marker-names',
TagEnum.text_without_language
): [b'TestMarker'],
(
SectionEnum.printer,
b'marker-colors',
TagEnum.text_without_language
): [b'#FF0000'],
(
SectionEnum.printer,
b'marker-types',
TagEnum.text_without_language
): [b'ink-cartridge'],
(
SectionEnum.printer,
b'marker-low-levels',
TagEnum.integer
): [Integer(15).bytes()],
(
SectionEnum.printer,
b'marker-high-levels',
TagEnum.integer
): [Integer(100).bytes()],
(
SectionEnum.printer,
b'marker-levels',
TagEnum.integer
): [Integer(66).bytes()],
}
*/
// TODO: allow customization of these attributes.
// rfc2911 section 4.4 // rfc2911 section 4.4
ipp_resp.PrinterAttributes = []ipp.Attributes{ ipp_resp.PrinterAttributes = []ipp.Attributes{
@ -264,61 +236,58 @@ func ippOnGetPrinterAttributes(mod *ZeroGod, client net.Conn, ipp_req *ipp.Reque
// custom // custom
"printer-name": []ipp.Attribute{ "printer-name": []ipp.Attribute{
{ {
Value: "PRINTER_NAME", Value: userProps["printer-name"],
Tag: ipp.TagName, Tag: ipp.TagName,
}, },
}, },
"printer-info": []ipp.Attribute{ "printer-info": []ipp.Attribute{
{ {
Value: "PRINTER_INFO", Value: userProps["printer-info"],
Tag: ipp.TagText, Tag: ipp.TagText,
}, },
}, },
"printer-make-and-model": []ipp.Attribute{ "printer-make-and-model": []ipp.Attribute{
{ {
Value: "PRINTER_MAKE PRINTER_MODEL", Value: userProps["printer-make-and-model"],
Tag: ipp.TagText, Tag: ipp.TagText,
}, },
}, },
"printer-location": []ipp.Attribute{ "printer-location": []ipp.Attribute{
{ {
Value: "PRINTER_LOCATION", Value: userProps["printer-location"],
Tag: ipp.TagText, Tag: ipp.TagText,
}, },
}, },
"printer-privacy-policy-uri": []ipp.Attribute{ "printer-privacy-policy-uri": []ipp.Attribute{
{ {
Value: "https://www.google.com/", Value: userProps["printer-privacy-policy-uri"],
Tag: ipp.TagUri, Tag: ipp.TagUri,
}, },
}, },
// constants "ppd-name": []ipp.Attribute{
/* {
"ppd-name": []ipp.Attribute{ Value: userProps["ppd-name"],
{ Tag: ipp.TagName,
Value: "everywhere",
Tag: ipp.TagName,
},
}, },
*/ },
"printer-uri-supported": []ipp.Attribute{ "printer-uri-supported": []ipp.Attribute{
{ {
Value: fmt.Sprintf("%s://%s:%d/printer", ops.Ternary(srvTLS, "ipps", "ipp"), srvHost, srvPort), Value: fmt.Sprintf("%s://%s:%d/printer", ops.Ternary(ctx.srvTLS, "ipps", "ipp"), ctx.srvHost, ctx.srvPort),
Tag: ipp.TagUri, Tag: ipp.TagUri,
}, },
}, },
"uri-security-supported": []ipp.Attribute{
{
Value: ops.Ternary(ctx.srvTLS, "tls", "none"),
Tag: ipp.TagKeyword,
},
},
"uri-authentication-supported": []ipp.Attribute{ "uri-authentication-supported": []ipp.Attribute{
{ {
Value: "none", Value: "none",
Tag: ipp.TagKeyword, Tag: ipp.TagKeyword,
}, },
}, },
"uri-security-supported": []ipp.Attribute{
{
Value: ops.Ternary(srvTLS, "tls", "none"),
Tag: ipp.TagKeyword,
},
},
"printer-state": []ipp.Attribute{ "printer-state": []ipp.Attribute{
{ {
Value: 3, // idle Value: 3, // idle
@ -424,5 +393,5 @@ func ippOnGetPrinterAttributes(mod *ZeroGod, client net.Conn, ipp_req *ipp.Reque
}, },
} }
ippSendResponse(mod, client, ipp_resp) ippSendResponse(ctx, ipp_resp)
} }

View file

@ -3,38 +3,12 @@ package zerogod
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strings"
"github.com/bettercap/bettercap/v2/zeroconf" "github.com/bettercap/bettercap/v2/zeroconf"
"github.com/evilsocket/islazy/str" "github.com/evilsocket/islazy/str"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
type ServiceData struct {
Name string `yaml:"name"` // Instance name (e.g. "My web page")
Service string `yaml:"service"` // Service name (e.g. _http._tcp.)
Domain string `yaml:"domain"` // If blank, assumes "local"
Port int `yaml:"port"` // Service port
Records []string `yaml:"records,omitempty"` // Service DNS text records
Responder string `yaml:"responder,omitempty"` // Optional IP to use instead of our tcp acceptor
}
func (svc ServiceData) FullName() string {
return fmt.Sprintf("%s.%s.%s",
strings.Trim(svc.Name, "."),
strings.Trim(svc.Service, "."),
strings.Trim(svc.Domain, "."))
}
func (svc ServiceData) Unregister() error {
if server, err := zeroconf.Register(svc.Name, svc.Service, svc.Domain, svc.Port, svc.Records, nil); err != nil {
return err
} else {
server.Shutdown()
}
return nil
}
func svcEntriesToData(services map[string]*zeroconf.ServiceEntry) []ServiceData { func svcEntriesToData(services map[string]*zeroconf.ServiceEntry) []ServiceData {
data := make([]ServiceData, 0) data := make([]ServiceData, 0)
for _, svc := range services { for _, svc := range services {

View file

@ -0,0 +1,100 @@
package zerogod
import (
"fmt"
"net"
"strings"
"github.com/bettercap/bettercap/v2/zeroconf"
"github.com/evilsocket/islazy/tui"
)
type ServiceData struct {
Name string `yaml:"name"` // Instance name (e.g. "My web page")
Service string `yaml:"service"` // Service name (e.g. _http._tcp.)
Domain string `yaml:"domain"` // If blank, assumes "local"
Port int `yaml:"port"` // Service port
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
server *zeroconf.Server
}
func (svc ServiceData) FullName() string {
return fmt.Sprintf("%s.%s.%s",
strings.Trim(svc.Name, "."),
strings.Trim(svc.Service, "."),
strings.Trim(svc.Domain, "."))
}
func (svc *ServiceData) Register(mod *ZeroGod) (err error) {
// now create it again to actually advertise
if svc.Responder == "" {
// use our own IP
if svc.server, err = zeroconf.Register(
svc.Name,
svc.Service,
svc.Domain,
svc.Port,
svc.Records,
nil); err != nil {
return fmt.Errorf("could not create service %s: %v", svc.FullName(), err)
}
mod.Info("advertising %s with responder=%s port=%d",
tui.Yellow(svc.FullName()),
tui.Red(svc.Responder),
svc.Port)
} else {
responderHostName := ""
// try first to do a reverse DNS of the ip
if addr, err := net.LookupAddr(svc.Responder); err == nil && len(addr) > 0 {
responderHostName = addr[0]
} else {
mod.Debug("could not get responder %s reverse dns entry: %v", svc.Responder, err)
}
// if we don't have a host, create a .nip.io representation
if responderHostName == "" {
responderHostName = fmt.Sprintf("%s.nip.io.", strings.ReplaceAll(svc.Responder, ".", "-"))
}
// use external responder
if svc.server, err = zeroconf.RegisterExternalResponder(
svc.Name,
svc.Service,
svc.Domain,
svc.Port,
responderHostName,
[]string{svc.Responder},
svc.Records,
nil); err != nil {
return fmt.Errorf("could not create service %s: %v", svc.FullName(), err)
}
mod.Info("advertising %s with responder=%s hostname=%s port=%d",
tui.Yellow(svc.FullName()),
tui.Red(svc.Responder),
tui.Yellow(responderHostName),
svc.Port)
}
return
}
func (svc *ServiceData) Unregister(mod *ZeroGod) error {
mod.Info("unregistering instance %s ...", tui.Yellow(svc.FullName()))
err := (error)(nil)
if svc.server == nil {
// if we haven't been registered yet, create the server
if svc.server, err = zeroconf.Register(svc.Name, svc.Service, svc.Domain, svc.Port, svc.Records, nil); err != nil {
return err
}
}
svc.server.Shutdown()
return nil
}

207
test.yml
View file

@ -1,207 +0,0 @@
EPSON\ XP-630\ Series-59F5BA-04._http._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _http._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 80
text:
- ""
ttl: 4500
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba
EPSON\ XP-630\ Series-59F5BA-04._ipp._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _ipp._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 6633
text:
- txtvers=1
- ty=EPSON XP-630 Series
- usb_MFG=EPSON
- usb_MDL=XP-630 Series
- product=(EPSON XP-630 Series)
- pdl=application/octet-stream,image/pwg-raster,image/urf,image/jpeg
- rp=ipp/print
- qtotal=1
- Color=T
- Duplex=T
- Scan=T
- Fax=F
- kind=document,envelope,label,photo
- PaperMax=legal-A4
- URF=CP1,MT1-3-5-8-10-11-12,PQ4-5,OB9,OFU0,RS360,SRGB24,W8,DM3,IS1-7,V1.4
- mopria-certified=1.2
- priority=30
- adminurl=http://EPSON59F5BA.local.:80/PRESENTATION/BONJOUR
- note=
- UUID=cfe92100-67c4-11d4-a45f-44d24459f5ba
- TLS=1.2
ttl: 4500
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba
EPSON\ XP-630\ Series-59F5BA-04._ipps._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _ipps._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 6631
text:
- txtvers=1
- ty=EPSON XP-630 Series
- usb_MFG=EPSON
- usb_MDL=XP-630 Series
- product=(EPSON XP-630 Series)
- pdl=application/octet-stream,image/pwg-raster,image/urf,image/jpeg
- rp=ipp/print
- qtotal=1
- Color=T
- Duplex=T
- Scan=T
- Fax=F
- kind=document,envelope,label,photo
- PaperMax=legal-A4
- URF=CP1,MT1-3-5-8-10-11-12,PQ4-5,OB9,OFU0,RS360,SRGB24,W8,DM3,IS1-7,V1.4
- mopria-certified=1.2
- priority=30
- adminurl=http://EPSON59F5BA.local.:80/PRESENTATION/BONJOUR
- note=
- UUID=cfe92100-67c4-11d4-a45f-44d24459f5ba
- TLS=1.2
ttl: 4500
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba
EPSON\ XP-630\ Series-59F5BA-04._pdl-datastream._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _pdl-datastream._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 9100
text:
- txtvers=1
- priority=40
- ty=EPSON XP-630 Series
- usb_MFG=EPSON
- usb_MDL=XP-630 Series
- product=(EPSON XP-630 Series)
- pdl=raw
- qtotal=1
- adminurl=http://EPSON59F5BA.local.:80/PRESENTATION/BONJOUR
- note=
ttl: 4500
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba
EPSON\ XP-630\ Series-59F5BA-04._printer._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _printer._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 1515
text:
- txtvers=1
- priority=50
- ty=EPSON XP-630 Series
- usb_MFG=EPSON
- usb_MDL=XP-630 Series
- product=(EPSON XP-630 Series)
- pdl=raw
- rp=auto
- qtotal=1
- adminurl=http://EPSON59F5BA.local.:80/PRESENTATION/BONJOUR
- note=
ttl: 4500
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba
EPSON\ XP-630\ Series-59F5BA-04._privet._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _privet._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 8081
text:
- txtvers=1
- ty=EPSON XP-630 Series (EPSON59F5BA)
- url=https://www.google.com/cloudprint
- type=printer
- id=0936a89f-33d7-80f5-c1bc-7421d40a78b5
- cs=offline
ttl: 4500
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba
EPSON\ XP-630\ Series-59F5BA-04._scanner._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _scanner._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 1865
text:
- txtvers=1
- ty=EPSON XP-630 Series
- adminurl=http://EPSON59F5BA.local.:80/PRESENTATION/BONJOUR
- mfg=EPSON
- mdl=XP-630 Series
- UUID=cfe92100-67c4-11d4-a45f-44d24459f5ba
- scannerAvailable=0
- note=
ttl: 4500
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba
EPSON\ XP-630\ Series-59F5BA-04._smb._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _smb._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 1445
text: []
ttl: 120
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba
EPSON\ XP-630\ Series-59F5BA-04._uscan._tcp.local.:
servicerecord:
instance: EPSON\ XP-630\ Series-59F5BA-04
service: _uscan._tcp
domain: local.
hostname: EPSON59F5BA.local.
port: 8443
text:
- txtvers=1
- vers=2.5
- representation=/PRESENTATION/AIRPRINT/PRINTER_128.PNG
- rs=eSCL
- ty=EPSON XP-630 Series
- pdl=application/pdf,image/jpeg
- cs=color,grayscale,binary
- is=platen
- duplex=F
- adminurl=http://EPSON59F5BA.local.:80/PRESENTATION/BONJOUR
- UUID=cfe92100-67c4-11d4-a45f-44d24459f5ba
- note=
ttl: 4500
addripv4:
- 192.168.50.21
addripv6:
- fe80::46d2:44ff:fe59:f5ba