 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			156 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package multicast
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"net"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/koron/go-ssdp/internal/ssdplog"
 | |
| 	"golang.org/x/net/ipv4"
 | |
| )
 | |
| 
 | |
| // Conn is multicast connection.
 | |
| type Conn struct {
 | |
| 	laddr  *net.UDPAddr
 | |
| 	conn   *net.UDPConn
 | |
| 	pconn  *ipv4.PacketConn
 | |
| 	iflist []net.Interface
 | |
| }
 | |
| 
 | |
| // Listen starts to receiving multicast messages.
 | |
| func Listen(r *AddrResolver) (*Conn, error) {
 | |
| 	// prepare parameters.
 | |
| 	laddr, err := r.resolve()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	// connect.
 | |
| 	conn, err := net.ListenUDP("udp4", laddr)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	// configure socket to use with multicast.
 | |
| 	pconn, iflist, err := newIPv4MulticastConn(conn)
 | |
| 	if err != nil {
 | |
| 		conn.Close()
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return &Conn{
 | |
| 		laddr:  laddr,
 | |
| 		conn:   conn,
 | |
| 		pconn:  pconn,
 | |
| 		iflist: iflist,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func newIPv4MulticastConn(conn *net.UDPConn) (*ipv4.PacketConn, []net.Interface, error) {
 | |
| 	iflist, err := interfaces()
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	addr, err := SendAddr()
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	pconn, err := joinGroupIPv4(conn, iflist, addr)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	return pconn, iflist, nil
 | |
| }
 | |
| 
 | |
| // joinGroupIPv4 makes the connection join to a group on interfaces.
 | |
| func joinGroupIPv4(conn *net.UDPConn, iflist []net.Interface, gaddr net.Addr) (*ipv4.PacketConn, error) {
 | |
| 	wrap := ipv4.NewPacketConn(conn)
 | |
| 	wrap.SetMulticastLoopback(true)
 | |
| 	// add interfaces to multicast group.
 | |
| 	joined := 0
 | |
| 	for _, ifi := range iflist {
 | |
| 		if err := wrap.JoinGroup(&ifi, gaddr); err != nil {
 | |
| 			ssdplog.Printf("failed to join group %s on %s: %s", gaddr.String(), ifi.Name, err)
 | |
| 			continue
 | |
| 		}
 | |
| 		joined++
 | |
| 		ssdplog.Printf("joined group %s on %s (#%d)", gaddr.String(), ifi.Name, ifi.Index)
 | |
| 	}
 | |
| 	if joined == 0 {
 | |
| 		return nil, errors.New("no interfaces had joined to group")
 | |
| 	}
 | |
| 	return wrap, nil
 | |
| }
 | |
| 
 | |
| // Close closes a multicast connection.
 | |
| func (mc *Conn) Close() error {
 | |
| 	if err := mc.pconn.Close(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	// mc.conn is closed by mc.pconn.Close()
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // DataProvider provides a body of multicast message to send.
 | |
| type DataProvider interface {
 | |
| 	Bytes(*net.Interface) []byte
 | |
| }
 | |
| 
 | |
| //type multicastDataProviderFunc func(*net.Interface) []byte
 | |
| //
 | |
| //func (f multicastDataProviderFunc) Bytes(ifi *net.Interface) []byte {
 | |
| //	return f(ifi)
 | |
| //}
 | |
| 
 | |
| type BytesDataProvider []byte
 | |
| 
 | |
| func (b BytesDataProvider) Bytes(ifi *net.Interface) []byte {
 | |
| 	return []byte(b)
 | |
| }
 | |
| 
 | |
| // WriteTo sends a multicast message to interfaces.
 | |
| func (mc *Conn) WriteTo(dataProv DataProvider, to net.Addr) (int, error) {
 | |
| 	if uaddr, ok := to.(*net.UDPAddr); ok && !uaddr.IP.IsMulticast() {
 | |
| 		return mc.conn.WriteTo(dataProv.Bytes(nil), to)
 | |
| 	}
 | |
| 	sum := 0
 | |
| 	for _, ifi := range mc.iflist {
 | |
| 		if err := mc.pconn.SetMulticastInterface(&ifi); err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 		n, err := mc.pconn.WriteTo(dataProv.Bytes(&ifi), nil, to)
 | |
| 		if err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 		sum += n
 | |
| 	}
 | |
| 	return sum, nil
 | |
| }
 | |
| 
 | |
| // LocalAddr returns local address to listen multicast packets.
 | |
| func (mc *Conn) LocalAddr() net.Addr {
 | |
| 	return mc.laddr
 | |
| }
 | |
| 
 | |
| // ReadPackets reads multicast packets.
 | |
| func (mc *Conn) ReadPackets(timeout time.Duration, h PacketHandler) error {
 | |
| 	buf := make([]byte, 65535)
 | |
| 	if timeout > 0 {
 | |
| 		mc.pconn.SetReadDeadline(time.Now().Add(timeout))
 | |
| 	}
 | |
| 	for {
 | |
| 		n, _, addr, err := mc.pconn.ReadFrom(buf)
 | |
| 		if err != nil {
 | |
| 			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
 | |
| 				return nil
 | |
| 			}
 | |
| 			if strings.Contains(err.Error(), "use of closed network connection") {
 | |
| 				return io.EOF
 | |
| 			}
 | |
| 			return err
 | |
| 		}
 | |
| 		if err := h(addr, buf[:n]); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| }
 |