Known hosts knowladge sharing algorithm; nicks and ports
This commit is contained in:
parent
67c688d772
commit
c49f7ade65
2
server/.gitignore
vendored
Normal file
2
server/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
test*.sh
|
||||||
|
server
|
35
server/README.md
Normal file
35
server/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Server
|
||||||
|
|
||||||
|
## Development notes
|
||||||
|
|
||||||
|
|
||||||
|
### Testing server list synchronisation (2022-12-14)
|
||||||
|
|
||||||
|
For ease of testing you can launch N instances of server in N tmux panes.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ while true; do sleep {n}; echo "======="; ./server -nick {nick} -port {port}; done
|
||||||
|
```
|
||||||
|
|
||||||
|
where `n` is increasing for each server to ensure that initial scan would not cover entire task of network scanning.
|
||||||
|
|
||||||
|
Next you can use this script to test:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go build
|
||||||
|
killall server
|
||||||
|
sleep 4
|
||||||
|
|
||||||
|
# Repeat line below for all N servers
|
||||||
|
echo '{"version":1, "type":"hosts"}' | nc localhost {port} | jq
|
||||||
|
|
||||||
|
# Choose one or few that will request synchronization with their remotes
|
||||||
|
echo '{"version":1, "type":"synchronize-hosts-with-remotes"}' | nc localhost {port} | jq
|
||||||
|
|
||||||
|
# Ensure that all synchronisation propagated with enough sleep time
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Repeat line below for all N servers
|
||||||
|
echo '{"version":1, "type":"hosts"}' | nc localhost {port} | jq
|
||||||
|
|
||||||
|
```
|
3
server/go.mod
Normal file
3
server/go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module musique/server
|
||||||
|
|
||||||
|
go 1.19
|
248
server/main.go
248
server/main.go
@ -2,8 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"musique/server/proto"
|
||||||
|
"musique/server/scan"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -53,7 +58,7 @@ func (e *timeExchange) estimateFor(host string) bool {
|
|||||||
e.after = time.Now().UnixMilli()
|
e.after = time.Now().UnixMilli()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("estimateFor: %v", err)
|
log.Printf("estimateFor: %v\n", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if parsedCount != 1 {
|
if parsedCount != 1 {
|
||||||
@ -95,12 +100,27 @@ func timesync(hosts []string) []client {
|
|||||||
|
|
||||||
const maxReactionTime = 300
|
const maxReactionTime = 300
|
||||||
|
|
||||||
|
func isThisMyAddress(address string) bool {
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ip, _, err := net.ParseCIDR(addr.String())
|
||||||
|
if err == nil && ip.To4() != nil && fmt.Sprintf("%s:%d", ip, port) == address {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func notifyAll(clients []client) <-chan time.Time {
|
func notifyAll(clients []client) <-chan time.Time {
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(len(clients))
|
wg.Add(len(clients))
|
||||||
startDeadline := time.After(maxReactionTime * time.Millisecond)
|
startDeadline := time.After(maxReactionTime * time.Millisecond)
|
||||||
|
|
||||||
|
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
client := client
|
client := client
|
||||||
go func() {
|
go func() {
|
||||||
@ -116,7 +136,229 @@ func notifyAll(clients []client) <-chan time.Time {
|
|||||||
return startDeadline
|
return startDeadline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleIncoming(incoming net.Conn) {
|
||||||
|
defer incoming.Close()
|
||||||
|
|
||||||
|
request := proto.Request{}
|
||||||
|
json.NewDecoder(incoming).Decode(&request)
|
||||||
|
log.Printf("%s: %+v\n", incoming.RemoteAddr(), request)
|
||||||
|
|
||||||
|
if request.Type == "handshake" {
|
||||||
|
var response proto.HandshakeResponse
|
||||||
|
response.Version = proto.Version
|
||||||
|
response.Nick = nick
|
||||||
|
json.NewEncoder(incoming).Encode(response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Type == "hosts" {
|
||||||
|
var response proto.HostsResponse
|
||||||
|
for _, remote := range remotes {
|
||||||
|
response.Hosts = append(response.Hosts, proto.HostsResponseEntry{
|
||||||
|
Nick: remote.Nick,
|
||||||
|
Version: remote.Version,
|
||||||
|
Address: remote.Address,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
json.NewEncoder(incoming).Encode(response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Type == "synchronize-hosts" {
|
||||||
|
response := synchronizeHosts(request.HostsResponse)
|
||||||
|
json.NewEncoder(incoming).Encode(response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Type == "synchronize-hosts-with-remotes" {
|
||||||
|
synchronizeHostsWithRemotes()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCommandServer(port uint16) <-chan struct{} {
|
||||||
|
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", baseIP, port))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exit := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer listener.Close()
|
||||||
|
defer close(exit)
|
||||||
|
for {
|
||||||
|
incoming, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
go handleIncoming(incoming)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return exit
|
||||||
|
}
|
||||||
|
|
||||||
|
type Remote struct {
|
||||||
|
Address string
|
||||||
|
Nick string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseIP string
|
||||||
|
nick string
|
||||||
|
port int
|
||||||
|
remotes map[string]Remote
|
||||||
|
)
|
||||||
|
|
||||||
|
func synchronizeHosts(incoming proto.HostsResponse) (response proto.HostsResponse) {
|
||||||
|
visitedHosts := make(map[string]struct{})
|
||||||
|
|
||||||
|
// Add all hosts that are in incoming to our list of remotes
|
||||||
|
// Additionaly build set of all hosts that remote knows
|
||||||
|
for _, incomingHost := range incoming.Hosts {
|
||||||
|
if _, ok := remotes[incomingHost.Address]; !ok && !isThisMyAddress(incomingHost.Address) {
|
||||||
|
remotes[incomingHost.Address] = Remote{
|
||||||
|
Address: incomingHost.Address,
|
||||||
|
Nick: incomingHost.Nick,
|
||||||
|
Version: incomingHost.Version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitedHosts[incomingHost.Address] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build list of hosts that incoming doesn't know
|
||||||
|
for _, remote := range remotes {
|
||||||
|
if _, ok := visitedHosts[remote.Address]; !ok {
|
||||||
|
response.Hosts = append(response.Hosts, proto.HostsResponseEntry{
|
||||||
|
Address: remote.Address,
|
||||||
|
Version: remote.Version,
|
||||||
|
Nick: remote.Nick,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func myAddressInTheSameNetwork(remote string) (string, error) {
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("myAddressInTheSameNetwork: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteInParts := strings.Split(remote, ":")
|
||||||
|
if len(remoteInParts) == 2 {
|
||||||
|
remote = remoteInParts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteIP := net.ParseIP(remote)
|
||||||
|
if remoteIP == nil {
|
||||||
|
// TODO Hoist error to global variable
|
||||||
|
return "", errors.New("Cannot parse remote IP")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ip, ipNet, err := net.ParseCIDR(addr.String())
|
||||||
|
if err == nil && ipNet.Contains(remoteIP) {
|
||||||
|
return ip.String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Hoist error to global variable
|
||||||
|
return "", errors.New("Cannot find matching IP addr")
|
||||||
|
}
|
||||||
|
|
||||||
|
func synchronizeHostsWithRemotes() {
|
||||||
|
previousResponseLength := -1
|
||||||
|
var response proto.HostsResponse
|
||||||
|
|
||||||
|
for previousResponseLength != len(response.Hosts) {
|
||||||
|
response = proto.HostsResponse{}
|
||||||
|
|
||||||
|
// Add all known remotes
|
||||||
|
for _, remote := range remotes {
|
||||||
|
response.Hosts = append(response.Hosts, proto.HostsResponseEntry{
|
||||||
|
Address: remote.Address,
|
||||||
|
Nick: remote.Nick,
|
||||||
|
Version: remote.Version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send constructed list to each remote
|
||||||
|
previousResponseLength = len(response.Hosts)
|
||||||
|
for _, remote := range response.Hosts {
|
||||||
|
var localResponse proto.HostsResponse
|
||||||
|
localResponse.Hosts = make([]proto.HostsResponseEntry, len(response.Hosts))
|
||||||
|
copy(localResponse.Hosts, response.Hosts)
|
||||||
|
|
||||||
|
myAddress, err := myAddressInTheSameNetwork(remote.Address)
|
||||||
|
// TODO Report when err != nil
|
||||||
|
if err == nil {
|
||||||
|
localResponse.Hosts = append(localResponse.Hosts, proto.HostsResponseEntry{
|
||||||
|
Address: myAddress,
|
||||||
|
Nick: nick,
|
||||||
|
Version: proto.Version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var remoteResponse proto.HostsResponse
|
||||||
|
proto.Command(remote.Address, proto.SynchronizeHosts(localResponse), &remoteResponse)
|
||||||
|
synchronizeHosts(remoteResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var (
|
||||||
|
logsPath string
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.StringVar(&baseIP, "ip", "", "IP where server will listen")
|
||||||
|
flag.StringVar(&nick, "nick", "", "Name that is going to be used to recognize this server")
|
||||||
|
flag.IntVar(&port, "port", 8081, "TCP port where server receives connections")
|
||||||
|
flag.StringVar(&logsPath, "logs", "", "Target file for logs from server. By default stdout")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if len(logsPath) != 0 {
|
||||||
|
// TODO Is defer logFile.Close() needed here? Dunno
|
||||||
|
logFile, err := os.OpenFile(logsPath, os.O_WRONLY|os.O_APPEND, 0o640)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Cannot open log file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
log.SetOutput(logFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nick) == 0 {
|
||||||
|
log.Fatalln("Please provide nick via --nick flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
exit := runCommandServer(uint16(port))
|
||||||
|
|
||||||
|
networks, err := scan.AvailableNetworks()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts := scan.TCPHosts(networks, []uint16{8081, 8082, 8083, 8084})
|
||||||
|
|
||||||
|
remotes = make(map[string]Remote)
|
||||||
|
for host := range hosts {
|
||||||
|
if !isThisMyAddress(host.Address) {
|
||||||
|
remotes[host.Address] = Remote{
|
||||||
|
Address: host.Address,
|
||||||
|
Nick: host.Nick,
|
||||||
|
Version: host.Version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for range exit {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main2() {
|
||||||
l, err := net.Listen("tcp", ":8081")
|
l, err := net.Listen("tcp", ":8081")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@ -139,7 +381,7 @@ func main() {
|
|||||||
log.Println(resp)
|
log.Println(resp)
|
||||||
if resp == "scan" {
|
if resp == "scan" {
|
||||||
conn.Write([]byte("Scanning...\n"))
|
conn.Write([]byte("Scanning...\n"))
|
||||||
scanResult = scan()
|
scanResult = nil // scan()
|
||||||
conn.Write([]byte("Scanning done!\n"))
|
conn.Write([]byte("Scanning done!\n"))
|
||||||
fmt.Println(len(scanResult))
|
fmt.Println(len(scanResult))
|
||||||
continue
|
continue
|
||||||
|
@ -2,12 +2,12 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"C"
|
"C"
|
||||||
"net"
|
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var clients []client
|
var clients []client
|
||||||
@ -56,7 +56,7 @@ func ServerInit() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
waitForConnection.Wait()
|
waitForConnection.Wait()
|
||||||
scanResult := scan()
|
scanResult := []string{} // scan()
|
||||||
clients = timesync(scanResult)
|
clients = timesync(scanResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ func ServerInit() {
|
|||||||
func ServerBeginProtocol() {
|
func ServerBeginProtocol() {
|
||||||
self := notifyAll(clients)
|
self := notifyAll(clients)
|
||||||
select {
|
select {
|
||||||
case <- self:
|
case <-self:
|
||||||
case <- pinger:
|
case <-pinger:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
server/proto/basic.go
Normal file
9
server/proto/basic.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
const Version = "1"
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Version string
|
||||||
|
Type string
|
||||||
|
HostsResponse
|
||||||
|
}
|
12
server/proto/handshake.go
Normal file
12
server/proto/handshake.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
type HandshakeResponse struct {
|
||||||
|
Nick string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Handshake() (req Request) {
|
||||||
|
req.Type = "handshake"
|
||||||
|
req.Version = Version
|
||||||
|
return
|
||||||
|
}
|
24
server/proto/hosts.go
Normal file
24
server/proto/hosts.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
type HostsResponseEntry struct {
|
||||||
|
Nick string
|
||||||
|
Address string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HostsResponse struct {
|
||||||
|
Hosts []HostsResponseEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hosts() (req Request) {
|
||||||
|
req.Version = Version
|
||||||
|
req.Type = "hosts"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SynchronizeHosts(response HostsResponse) (req Request) {
|
||||||
|
req.HostsResponse = response
|
||||||
|
req.Version = Version
|
||||||
|
req.Type = "synchronize-hosts"
|
||||||
|
return
|
||||||
|
}
|
35
server/proto/net.go
Normal file
35
server/proto/net.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Command(target string, request interface{}, response interface{}) error {
|
||||||
|
conn, err := net.Dial("tcp", target)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
if err = json.NewEncoder(conn).Encode(request); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.NewDecoder(conn).Decode(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandTimeout(target string, request interface{}, response interface{}, timeout time.Duration) error {
|
||||||
|
conn, err := net.DialTimeout("tcp", target, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
if err = json.NewEncoder(conn).Encode(request); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.NewDecoder(conn).Decode(response)
|
||||||
|
}
|
88
server/scan/scanner.go
Normal file
88
server/scan/scanner.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package scan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"musique/server/proto"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Network struct {
|
||||||
|
FirstAddress string
|
||||||
|
MaxHostsCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeoutTCPHosts = time.Duration(1) * time.Second
|
||||||
|
|
||||||
|
func nextIP(ip net.IP) net.IP {
|
||||||
|
// FIXME Proper next IP address in network calculation
|
||||||
|
next := make([]byte, 4)
|
||||||
|
copy(next, ip)
|
||||||
|
next[3]++
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// AvailableNetworks returns all IPv4 networks that are available to the host
|
||||||
|
func AvailableNetworks() ([]Network, error) {
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting interfaces info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
networks := []Network{}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
_, ipNet, err := net.ParseCIDR(addr.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipNet.IP.IsGlobalUnicast() && ipNet.IP.To4() != nil {
|
||||||
|
// FIXME We assume mask /24. This is a reasonable assumption performance wise
|
||||||
|
// but may lead to inability to recognize some of the host in network
|
||||||
|
networks = append(networks, Network{nextIP(ipNet.IP).String(), 254})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return networks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
proto.HandshakeResponse
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPHosts returns all TCP hosts that are in given networks on one of given ports
|
||||||
|
func TCPHosts(networks []Network, ports []uint16) <-chan Response {
|
||||||
|
ips := make(chan Response, 32)
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
for _, network := range networks {
|
||||||
|
ip := net.ParseIP(network.FirstAddress)
|
||||||
|
for i := 0; i < network.MaxHostsCount; i++ {
|
||||||
|
for _, port := range ports {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(ip net.IP, port uint16) {
|
||||||
|
defer wg.Done()
|
||||||
|
target := fmt.Sprintf("%s:%d", ip, port)
|
||||||
|
var hs proto.HandshakeResponse
|
||||||
|
|
||||||
|
err := proto.CommandTimeout(target, proto.Handshake(), &hs, timeoutTCPHosts)
|
||||||
|
if err == nil {
|
||||||
|
ips <- Response{hs, target}
|
||||||
|
}
|
||||||
|
}(ip, port)
|
||||||
|
}
|
||||||
|
ip = nextIP(ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(ips)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ips
|
||||||
|
}
|
@ -1,53 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func scan() []string {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
ips := make(chan string, 256)
|
|
||||||
|
|
||||||
ifaces, _ := net.Interfaces()
|
|
||||||
for _, iface := range ifaces {
|
|
||||||
addrs, _ := iface.Addrs()
|
|
||||||
for _, addr := range addrs {
|
|
||||||
ipv4, _, _ := net.ParseCIDR(addr.String())
|
|
||||||
|
|
||||||
if ipv4.IsGlobalUnicast() && ipv4.To4() != nil {
|
|
||||||
ipv4 = ipv4.To4()
|
|
||||||
ipv4 = ipv4.Mask(ipv4.DefaultMask())
|
|
||||||
|
|
||||||
for i := 1; i < 255; i++ {
|
|
||||||
localIP := make([]byte, 4)
|
|
||||||
copy(localIP, ipv4)
|
|
||||||
wg.Add(1)
|
|
||||||
go func(ip net.IP) {
|
|
||||||
conn, dialErr := net.DialTimeout("tcp", ip.String()+":8081", time.Duration(1)*time.Second)
|
|
||||||
if dialErr == nil {
|
|
||||||
ips <- ip.String() + ":8081"
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}(localIP)
|
|
||||||
ipv4[3]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(ips)
|
|
||||||
}()
|
|
||||||
|
|
||||||
information := []string{}
|
|
||||||
for ip := range ips {
|
|
||||||
fmt.Println("Response from " + ip)
|
|
||||||
information = append(information, ip)
|
|
||||||
}
|
|
||||||
return information
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user