From a7e9eb5082d1538002912b5de80d8d2da154e3e1 Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Tue, 3 Dec 2024 23:55:13 -0500 Subject: [PATCH] BLE --- wap/pkg/server/server.go | 34 +++++- wap/pkg/wifi/init.go | 2 +- wap/pkg/wifi/scan.go | 246 ++++++++++----------------------------- 3 files changed, 95 insertions(+), 187 deletions(-) diff --git a/wap/pkg/server/server.go b/wap/pkg/server/server.go index 6517a62..03d8d6c 100644 --- a/wap/pkg/server/server.go +++ b/wap/pkg/server/server.go @@ -45,6 +45,21 @@ type Config struct { IpniPublisherIdentity string `yaml:"ipniPublisherIdentity"` } +type multiCloser struct { + listeners []io.Closer +} + +// Implement Close method for multiCloser +func (mc *multiCloser) Close() error { + var err error + for _, l := range mc.listeners { + if cerr := l.Close(); cerr != nil { + err = cerr + } + } + return err +} + func checkPathExistAndFileNotExist(path string) string { dir := filepath.Dir(path) @@ -796,5 +811,22 @@ func Serve(peerFn func(clientPeerId string, bloxSeed string) (string, error), ip log.Errorw("Serve could not initialize", "err", err) } }() - return ln + + mc := &multiCloser{ + listeners: []io.Closer{ln}, + } + + ln1, err1 := net.Listen("tcp", "127.0.0.1:"+port) + if err1 != nil { + log.Errorw("Failed to use 127.0.0.1 for serve", "err", err1) + } else { + mc.listeners = append(mc.listeners, ln1) + go func() { + if err := http.Serve(ln1, mux); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { + log.Errorw("Serve could not initialize on 127.0.0.1", "err", err) + } + }() + } + + return mc } diff --git a/wap/pkg/wifi/init.go b/wap/pkg/wifi/init.go index b8f9d9a..b223a5c 100644 --- a/wap/pkg/wifi/init.go +++ b/wap/pkg/wifi/init.go @@ -15,7 +15,7 @@ var log = logging.Logger("fula/wap/wifi") const maxRetries = 4 -var TimeLimit = 10 * time.Second +var TimeLimit = 25 * time.Second func init() { // Working Directory diff --git a/wap/pkg/wifi/scan.go b/wap/pkg/wifi/scan.go index 214e079..e4928ae 100644 --- a/wap/pkg/wifi/scan.go +++ b/wap/pkg/wifi/scan.go @@ -4,8 +4,7 @@ import ( "bufio" "context" "fmt" - "os/exec" - "runtime" + "sort" "strconv" "strings" ) @@ -89,122 +88,75 @@ func parseDarwin(output string) (wifis []Wifi, err error) { } func parseLinux(output string) (wifis []Wifi, err error) { - // Check which command is available - useIw := false - if _, err := exec.LookPath("iwlist"); err != nil { - if _, err := exec.LookPath("iw"); err != nil { - return nil, fmt.Errorf("neither iwlist nor iw commands found") - } - useIw = true - } + scanner := bufio.NewScanner(strings.NewReader(output)) - if output == "" { - return nil, fmt.Errorf("empty output received") + // Skip header line + if !scanner.Scan() { + return nil, fmt.Errorf("empty output") } - scanner := bufio.NewScanner(strings.NewReader(output)) - w := Wifi{} - wifis = []Wifi{} + // Use map to track strongest signal for each SSID + uniqueWifi := make(map[string]Wifi) - if useIw { - // Parse iw output - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" { - continue - } + for scanner.Scan() { + line := scanner.Text() + fields := strings.Fields(line) - if strings.Contains(line, "BSS") { - // If we have a complete wifi entry, append it - if w.SSID != "" && w.RSSI != 0 { - // Copy SSID to ESSID if ESSID is empty - if w.ESSID == "" { - w.ESSID = w.SSID - } - wifis = append(wifis, w) - w = Wifi{} // Reset for next entry - } - } else if strings.Contains(line, "SSID:") { - fs := strings.Fields(line) - if len(fs) > 1 { - ssid := strings.Join(fs[1:], " ") - if ssid != "" { - w.SSID = ssid - w.ESSID = ssid // In iw, SSID is what we want for both fields - } - } - } else if strings.Contains(line, "signal:") { - fs := strings.Fields(line) - if len(fs) > 1 { - levelStr := strings.TrimSpace(strings.TrimSuffix(fs[1], " dBm")) - level, errParse := strconv.ParseFloat(levelStr, 64) - if errParse == nil && level < 0 { // Signal strength should be negative - w.RSSI = int(level) // Already in dBm - } - } - } + if len(fields) < 8 { + continue } - // Don't forget the last entry - if w.SSID != "" && w.RSSI != 0 { - if w.ESSID == "" { - w.ESSID = w.SSID - } - wifis = append(wifis, w) + + var startIndex int + if fields[0] == "*" { + startIndex = 1 } - } else { - // Original iwlist parsing logic - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" { - continue - } - if w.SSID == "" { - if strings.Contains(line, "Address") { - fs := strings.Fields(line) - if len(fs) >= 5 { // Changed from == 5 to >= 5 for more flexibility - w.SSID = strings.ToLower(fs[4]) - } - } else { - continue - } - } else { - if strings.Contains(line, "ESSID") { - parts := strings.Split(line, ":") - if len(parts) > 1 { - essid := strings.Trim(parts[1], "\"") - if essid != "" { - w.ESSID = essid - } - } - } else if strings.Contains(line, "Signal level=") { - parts := strings.Split(line, "level=") - if len(parts) > 1 { - levelParts := strings.Split(parts[1], "/") - if len(levelParts) > 0 { - levelStr := strings.Split(levelParts[0], " dB")[0] - level, errParse := strconv.Atoi(strings.TrimSpace(levelStr)) - if errParse == nil { - if level > 0 { - level = (level / 2) - 100 - } - w.RSSI = level - } - } - } + // Skip networks with "--" as SSID + if fields[startIndex+1] == "--" { + continue + } + + ssid := fields[startIndex+1] + signal, err := strconv.Atoi(fields[startIndex+6]) + if err != nil { + continue + } + + rssi := (signal / 2) - 100 + + // If SSID exists, only update if new signal is stronger + if existing, exists := uniqueWifi[ssid]; exists { + if rssi > existing.RSSI { + uniqueWifi[ssid] = Wifi{ + SSID: ssid, + ESSID: ssid, + RSSI: rssi, } } - if w.SSID != "" && w.RSSI != 0 && w.ESSID != "" { - wifis = append(wifis, w) - w = Wifi{} + } else { + uniqueWifi[ssid] = Wifi{ + SSID: ssid, + ESSID: ssid, + RSSI: rssi, } } } if err := scanner.Err(); err != nil { - return wifis, fmt.Errorf("error scanning output: %v", err) + return nil, fmt.Errorf("error scanning output: %v", err) + } + + // Convert map to slice + wifis = make([]Wifi, 0, len(uniqueWifi)) + for _, wifi := range uniqueWifi { + wifis = append(wifis, wifi) } + // Sort by signal strength (RSSI), strongest first + sort.Slice(wifis, func(i, j int) bool { + return wifis[i].RSSI > wifis[j].RSSI + }) + return wifis, nil } @@ -212,90 +164,14 @@ func parseLinux(output string) (wifis []Wifi, err error) { // If forceReload is set to true it resets the network adapter to make sure it fetches the latest list, otherwise it reads from cache // wifiInterface is the name of interface that it should look for in Linux. func Scan(forceReload bool, wifiInterface ...string) (wifilist []Wifi, err error) { - var command, stdout, stderr string - switch runtime.GOOS { - case "windows": - // Your windows related code - case "darwin": - // Your darwin related code - default: - // Get all available wireless network interfaces - ctx, cl := context.WithTimeout(context.Background(), TimeLimit) - defer cl() + ctx, cl := context.WithTimeout(context.Background(), TimeLimit) + defer cl() - // Check which command is available - useIw := false - if _, err := exec.LookPath("iwlist"); err != nil { - if _, err := exec.LookPath("iw"); err != nil { - return nil, fmt.Errorf("neither iwlist nor iw commands found") - } - useIw = true - } - - var interfaces []string - if useIw { - // Get interfaces using iw - stdout, stderr, err = runCommand(ctx, "iw dev") - if err != nil { - log.Errorw("failed to run iw dev", "err", err, "stderr", stderr) - return nil, err - } - - // Parse iw output for interfaces - scanner := bufio.NewScanner(strings.NewReader(stdout)) - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(line, "Interface") { - fields := strings.Fields(line) - if len(fields) > 1 { - interfaces = append(interfaces, fields[1]) - } - } - } - } else { - // Use existing logic for iwconfig/iwlist - stdout, stderr, err = runCommand(ctx, "iwconfig") - if err != nil { - log.Errorw("failed to run iwconfig", "err", err, "stderr", stderr) - return nil, err - } - - // Filter output with grep-like functionality - var filteredLines []string - scanner := bufio.NewScanner(strings.NewReader(stdout)) - for scanner.Scan() { - line := scanner.Text() - if len(line) > 0 && (line[0] >= 'a' && line[0] <= 'z' || line[0] >= 'A' && line[0] <= 'Z') { - filteredLines = append(filteredLines, line) - } - } - - // Run awk-like functionality to print the first field of each line - for _, line := range filteredLines { - fields := strings.Fields(line) - if len(fields) > 0 { - interfaces = append(interfaces, fields[0]) - } - } - } - - // Loop over interfaces and perform scan - for _, iface := range interfaces { - if useIw { - command = fmt.Sprintf("iw dev %s scan", iface) - } else { - command = fmt.Sprintf("iwlist %s scan", iface) - } - - stdout, _, err = runCommand(ctx, command) - if err == nil { - // Break the loop when the scan command is successful - wifilist, err = parse(stdout, "linux") - if err == nil { - break - } - } - } + stdout, stderr, err := runCommand(ctx, "nmcli dev wifi list --rescan yes") + if err != nil { + log.Errorw("failed to run nmcli scan", "err", err, "stderr", stderr) + return nil, err } - return + + return parse(stdout, "linux") }