wifi_linux_wpa.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package settings
  2. import (
  3. "bufio"
  4. "context"
  5. "fmt"
  6. "net"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. "time"
  11. "github.com/sagernet/sing-box/adapter"
  12. )
  13. type wpaSupplicantMonitor struct {
  14. socketPath string
  15. callback func(adapter.WIFIState)
  16. cancel context.CancelFunc
  17. }
  18. func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
  19. socketDirs := []string{"/var/run/wpa_supplicant", "/run/wpa_supplicant"}
  20. for _, socketDir := range socketDirs {
  21. entries, err := os.ReadDir(socketDir)
  22. if err != nil {
  23. continue
  24. }
  25. for _, entry := range entries {
  26. if entry.IsDir() || entry.Name() == "." || entry.Name() == ".." {
  27. continue
  28. }
  29. socketPath := filepath.Join(socketDir, entry.Name())
  30. localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d", os.Getpid()), Net: "unixgram"}
  31. remoteAddr := &net.UnixAddr{Name: socketPath, Net: "unixgram"}
  32. conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
  33. if err != nil {
  34. continue
  35. }
  36. conn.Close()
  37. return &wpaSupplicantMonitor{socketPath: socketPath, callback: callback}, nil
  38. }
  39. }
  40. return nil, os.ErrNotExist
  41. }
  42. func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
  43. localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d", os.Getpid()), Net: "unixgram"}
  44. remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
  45. conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
  46. if err != nil {
  47. return adapter.WIFIState{}
  48. }
  49. defer conn.Close()
  50. conn.SetDeadline(time.Now().Add(3 * time.Second))
  51. status, err := m.sendCommand(conn, "STATUS")
  52. if err != nil {
  53. return adapter.WIFIState{}
  54. }
  55. var ssid, bssid string
  56. var connected bool
  57. scanner := bufio.NewScanner(strings.NewReader(status))
  58. for scanner.Scan() {
  59. line := scanner.Text()
  60. if strings.HasPrefix(line, "wpa_state=") {
  61. state := strings.TrimPrefix(line, "wpa_state=")
  62. connected = state == "COMPLETED"
  63. } else if strings.HasPrefix(line, "ssid=") {
  64. ssid = strings.TrimPrefix(line, "ssid=")
  65. } else if strings.HasPrefix(line, "bssid=") {
  66. bssid = strings.TrimPrefix(line, "bssid=")
  67. }
  68. }
  69. if !connected || ssid == "" {
  70. return adapter.WIFIState{}
  71. }
  72. return adapter.WIFIState{
  73. SSID: ssid,
  74. BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
  75. }
  76. }
  77. func (m *wpaSupplicantMonitor) sendCommand(conn *net.UnixConn, command string) (string, error) {
  78. _, err := conn.Write([]byte(command + "\n"))
  79. if err != nil {
  80. return "", err
  81. }
  82. buf := make([]byte, 4096)
  83. n, err := conn.Read(buf)
  84. if err != nil {
  85. return "", err
  86. }
  87. response := string(buf[:n])
  88. if strings.HasPrefix(response, "FAIL") {
  89. return "", os.ErrInvalid
  90. }
  91. return strings.TrimSpace(response), nil
  92. }
  93. func (m *wpaSupplicantMonitor) Start() error {
  94. if m.callback == nil {
  95. return nil
  96. }
  97. ctx, cancel := context.WithCancel(context.Background())
  98. m.cancel = cancel
  99. state := m.ReadWIFIState()
  100. go m.monitorEvents(ctx, state)
  101. m.callback(state)
  102. return nil
  103. }
  104. func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adapter.WIFIState) {
  105. var consecutiveErrors int
  106. localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-mon-%d", os.Getpid()), Net: "unixgram"}
  107. remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
  108. conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
  109. if err != nil {
  110. return
  111. }
  112. defer conn.Close()
  113. _, err = conn.Write([]byte("ATTACH\n"))
  114. if err != nil {
  115. return
  116. }
  117. buf := make([]byte, 4096)
  118. n, err := conn.Read(buf)
  119. if err != nil || !strings.HasPrefix(string(buf[:n]), "OK") {
  120. return
  121. }
  122. for {
  123. select {
  124. case <-ctx.Done():
  125. return
  126. default:
  127. }
  128. conn.SetReadDeadline(time.Now().Add(30 * time.Second))
  129. n, err := conn.Read(buf)
  130. if err != nil {
  131. consecutiveErrors++
  132. if consecutiveErrors > 10 {
  133. return
  134. }
  135. time.Sleep(time.Second)
  136. continue
  137. }
  138. consecutiveErrors = 0
  139. msg := string(buf[:n])
  140. if strings.Contains(msg, "CTRL-EVENT-CONNECTED") || strings.Contains(msg, "CTRL-EVENT-DISCONNECTED") {
  141. state := m.ReadWIFIState()
  142. if state != lastState {
  143. lastState = state
  144. m.callback(state)
  145. }
  146. }
  147. }
  148. }
  149. func (m *wpaSupplicantMonitor) Close() error {
  150. if m.cancel != nil {
  151. m.cancel()
  152. }
  153. return nil
  154. }