|
|
@ -2,12 +2,14 @@ package lifx |
|
|
|
|
|
|
|
import ( |
|
|
|
"context" |
|
|
|
"encoding/binary" |
|
|
|
"errors" |
|
|
|
"git.aiterp.net/lucifer/new-server/models" |
|
|
|
"log" |
|
|
|
"math/rand" |
|
|
|
"net" |
|
|
|
"sync" |
|
|
|
"sync/atomic" |
|
|
|
"time" |
|
|
|
) |
|
|
|
|
|
|
@ -21,6 +23,8 @@ type Client struct { |
|
|
|
source uint32 |
|
|
|
debug bool |
|
|
|
|
|
|
|
isHorrible uint32 |
|
|
|
|
|
|
|
addrMap map[string]*net.UDPAddr |
|
|
|
} |
|
|
|
|
|
|
@ -74,6 +78,107 @@ func (c *Client) Send(addr string, payload Payload) (seq uint8, err error) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// HorribleBroadcast "broadcasts" by blasting every IP address in the space with a tagged packet, because LIFX is LIFX.
|
|
|
|
func (c *Client) HorribleBroadcast(ctx context.Context, payload Payload) (seq uint8, err error) { |
|
|
|
c.mu.Lock() |
|
|
|
seq = c.seq |
|
|
|
c.seq += 1 |
|
|
|
c.mu.Unlock() |
|
|
|
|
|
|
|
defer atomic.StoreUint32(&c.isHorrible, 0) |
|
|
|
|
|
|
|
packet := createPacket() |
|
|
|
packet.SetSource(c.source) |
|
|
|
packet.SetSequence(seq) |
|
|
|
packet.SetPayload(payload) |
|
|
|
packet.SetTagged(true) |
|
|
|
|
|
|
|
ifaces, err := net.Interfaces() |
|
|
|
if err != nil { |
|
|
|
return 0, err |
|
|
|
} |
|
|
|
|
|
|
|
connIP := c.conn.LocalAddr().(*net.UDPAddr).IP |
|
|
|
|
|
|
|
var minIPNum uint32 |
|
|
|
var maxIPNum uint32 |
|
|
|
for _, iface := range ifaces { |
|
|
|
addrs, err := iface.Addrs() |
|
|
|
if err != nil { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
for _, addr := range addrs { |
|
|
|
naddr, ok := addr.(*net.IPNet) |
|
|
|
if !ok || !naddr.Contains(connIP) { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
networkAddress := naddr.IP.Mask(naddr.Mask) |
|
|
|
cidr, bits := naddr.Mask.Size() |
|
|
|
minIPNum = binary.BigEndian.Uint32(networkAddress) + 1 |
|
|
|
maxIPNum = minIPNum + (1 << (bits - cidr)) - 2 |
|
|
|
|
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
if minIPNum == maxIPNum { |
|
|
|
return |
|
|
|
} |
|
|
|
myIPNum := binary.BigEndian.Uint32(connIP) |
|
|
|
|
|
|
|
if c.debug { |
|
|
|
ipBuf := make(net.IP, 8) |
|
|
|
binary.BigEndian.PutUint32(ipBuf, minIPNum) |
|
|
|
binary.BigEndian.PutUint32(ipBuf[4:], maxIPNum-1) |
|
|
|
|
|
|
|
log.Println("Sending tagged", payload, "to range", ipBuf[:4], "-", ipBuf[4:]) |
|
|
|
|
|
|
|
defer func() { |
|
|
|
log.Println("Completed sending tagged", payload, "to range", ipBuf[:4], "-", ipBuf[4:]) |
|
|
|
}() |
|
|
|
} |
|
|
|
|
|
|
|
for ipNum := minIPNum; ipNum < maxIPNum; ipNum++ { |
|
|
|
if ipNum == myIPNum { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
atomic.StoreUint32(&c.isHorrible, 1) |
|
|
|
|
|
|
|
ipBuf := make(net.IP, 4) |
|
|
|
binary.BigEndian.PutUint32(ipBuf, ipNum) |
|
|
|
addr := &net.UDPAddr{IP: ipBuf, Port: 56700} |
|
|
|
|
|
|
|
_, err = c.conn.WriteToUDP(packet, addr) |
|
|
|
if err != nil { |
|
|
|
if c.debug { |
|
|
|
log.Println("HorribleBroadcast™ aborted by error.") |
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if ipNum%256 == 0 { |
|
|
|
if c.debug { |
|
|
|
log.Println("Progress:", (ipNum-minIPNum)+1, "of", maxIPNum-minIPNum) |
|
|
|
} |
|
|
|
|
|
|
|
time.Sleep(time.Millisecond * 100) |
|
|
|
} |
|
|
|
|
|
|
|
if ctx.Err() != nil { |
|
|
|
if c.debug { |
|
|
|
log.Println("HorribleBroadcast™ aborted at call site.") |
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// LastPacket gets the last read packet. This data is valid until the next call to Recv. The Packet may
|
|
|
|
// not be valid, however!
|
|
|
|
func (c *Client) LastPacket() Packet { |
|
|
@ -169,6 +274,8 @@ func createClient(ctx context.Context, bindAddr string, debug bool) (*Client, er |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
_ = conn.SetWriteBuffer(2048 * 1024) |
|
|
|
|
|
|
|
go func() { |
|
|
|
<-ctx.Done() |
|
|
|
_ = conn.Close() |
|
|
|