Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ _testmain.go
*.exe
*.test
*.prof

# Coverage
coverage.out
20 changes: 20 additions & 0 deletions .mockery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
all: false
dir: 'mocks'
filename: 'mock_{{.InterfaceName | snakecase}}.go'
force-file-write: true
formatter: goimports
generate: true
include-auto-generated: false
log-level: info
structname: 'Mock{{.InterfaceName}}'
pkgname: 'mocks'
recursive: false
require-template-schema-exists: true
template: testify
template-schema: '{{.Template}}.schema.json'
packages:
github.com/enbility/zeroconf/v3/api:
interfaces:
PacketConn:
ConnectionFactory:
InterfaceProvider:
63 changes: 55 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,30 @@ Target environments: private LAN/Wifi, small or isolated networks.
## Install
Nothing is as easy as that:
```bash
$ go get -u github.com/enbility/zeroconf/v2
$ go get -u github.com/enbility/zeroconf/v3
```

## Browse for services in your local network

```go
entries := make(chan *zeroconf.ServiceEntry)
go func(results <-chan *zeroconf.ServiceEntry) {
for entry := range results {
log.Println(entry)
removed := make(chan *zeroconf.ServiceEntry)

go func() {
for {
select {
case entry := <-entries:
log.Println("Found:", entry)
case entry := <-removed:
log.Println("Removed:", entry)
}
}
log.Println("No more entries.")
}(entries)
}()

ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
// Discover all services on the network (e.g. _workstation._tcp)
err = zeroconf.Browse(ctx, "_workstation._tcp", "local.", entries)
err := zeroconf.Browse(ctx, "_workstation._tcp", "local.", entries, removed)
if err != nil {
log.Fatalln("Failed to browse:", err.Error())
}
Expand All @@ -53,7 +59,23 @@ See https://github.com/enbility/zeroconf/blob/master/examples/resolv/client.go.
## Lookup a specific service instance

```go
// Example filled soon.
entries := make(chan *zeroconf.ServiceEntry)

go func() {
for entry := range entries {
log.Println("Found:", entry)
}
}()

ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
// Lookup a specific service instance by name
err := zeroconf.Lookup(ctx, "MyService", "_workstation._tcp", "local.", entries)
if err != nil {
log.Fatalln("Failed to lookup:", err.Error())
}

<-ctx.Done()
```

## Register a service
Expand Down Expand Up @@ -81,6 +103,29 @@ Multiple subtypes may be added to service name, separated by commas. E.g `_works

See https://github.com/enbility/zeroconf/blob/master/examples/register/server.go.

## Testing Support (v3)

Version 3 introduces interface-based abstractions for improved testability. You can inject mock connections for unit testing without requiring real network access:

```go
// Create mock connections using the provided interfaces
mockFactory := &MyMockConnectionFactory{}

// Client with mock connections
client, err := zeroconf.NewClient(zeroconf.WithClientConnFactory(mockFactory))

// Server with mock connections
server, err := zeroconf.RegisterProxy(
"MyService", "_http._tcp", "local.", 8080,
"myhost.local.", []string{"192.168.1.100"},
[]string{"txtvers=1"},
nil, // interfaces
zeroconf.WithServerConnFactory(mockFactory),
)
```

See the `api/` package for interface definitions and `mocks/` for mockery-generated mocks.

## Features and ToDo's
This list gives a quick impression about the state of this library.
See what needs to be done and submit a pull request :)
Expand All @@ -89,6 +134,8 @@ See what needs to be done and submit a pull request :)
* [x] Multiple IPv6 / IPv4 addresses support
* [x] Send multiple probes (exp. back-off) if no service answers (*)
* [x] Timestamp entries for TTL checks
* [x] Service removal notifications via `removed` channel
* [x] Interface-based abstractions for testability (v3)
* [ ] Compare new multicasts with already received services

_Notes:_
Expand Down
57 changes: 57 additions & 0 deletions api/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Package api defines the core interfaces for the zeroconf library.
// These interfaces enable dependency injection and testing.
package api

import "net"

//go:generate mockery

// PacketConn abstracts IPv4/IPv6 multicast packet connections.
// This interface unifies ipv4.PacketConn and ipv6.PacketConn by extracting
// only the IfIndex from ControlMessage, which is the only field used.
type PacketConn interface {
// ReadFrom reads a packet from the connection.
// Returns the number of bytes read, the interface index the packet arrived on,
// the source address, and any error.
ReadFrom(b []byte) (n int, ifIndex int, src net.Addr, err error)

// WriteTo writes a packet to the destination address.
// The ifIndex specifies which interface to send from (0 for default/all).
WriteTo(b []byte, ifIndex int, dst net.Addr) (n int, err error)

// Close closes the connection.
Close() error

// JoinGroup joins the multicast group on the specified interface.
JoinGroup(ifi *net.Interface, group net.Addr) error

// LeaveGroup leaves the multicast group on the specified interface.
LeaveGroup(ifi *net.Interface, group net.Addr) error

// SetMulticastTTL sets the TTL for outgoing multicast packets (IPv4).
SetMulticastTTL(ttl int) error

// SetMulticastHopLimit sets the hop limit for outgoing multicast packets (IPv6).
SetMulticastHopLimit(hopLimit int) error

// SetMulticastInterface sets the default interface for outgoing multicast.
// Used as fallback on platforms where ControlMessage is not supported (Windows).
SetMulticastInterface(ifi *net.Interface) error
}

// ConnectionFactory creates multicast connections.
// This abstraction allows injecting mock connections for testing.
type ConnectionFactory interface {
// CreateIPv4Conn creates an IPv4 multicast connection joined to the mDNS group.
CreateIPv4Conn(ifaces []net.Interface) (PacketConn, error)

// CreateIPv6Conn creates an IPv6 multicast connection joined to the mDNS group.
CreateIPv6Conn(ifaces []net.Interface) (PacketConn, error)
}

// InterfaceProvider lists network interfaces.
// This abstraction allows injecting mock interface lists for testing.
type InterfaceProvider interface {
// MulticastInterfaces returns all network interfaces capable of multicast.
MulticastInterfaces() []net.Interface
}
Loading