1
0

wifi_linux_iwd.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package settings
  2. import (
  3. "context"
  4. "strings"
  5. "time"
  6. "github.com/sagernet/sing-box/adapter"
  7. "github.com/godbus/dbus/v5"
  8. )
  9. type iwdMonitor struct {
  10. conn *dbus.Conn
  11. callback func(adapter.WIFIState)
  12. cancel context.CancelFunc
  13. signalChan chan *dbus.Signal
  14. }
  15. func newIWDMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
  16. conn, err := dbus.ConnectSystemBus()
  17. if err != nil {
  18. return nil, err
  19. }
  20. iwdObj := conn.Object("net.connman.iwd", "/")
  21. ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  22. defer cancel()
  23. call := iwdObj.CallWithContext(ctx, "org.freedesktop.DBus.ObjectManager.GetManagedObjects", 0)
  24. if call.Err != nil {
  25. conn.Close()
  26. return nil, call.Err
  27. }
  28. return &iwdMonitor{conn: conn, callback: callback}, nil
  29. }
  30. func (m *iwdMonitor) ReadWIFIState() adapter.WIFIState {
  31. ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
  32. defer cancel()
  33. iwdObj := m.conn.Object("net.connman.iwd", "/")
  34. var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
  35. err := iwdObj.CallWithContext(ctx, "org.freedesktop.DBus.ObjectManager.GetManagedObjects", 0).Store(&objects)
  36. if err != nil {
  37. return adapter.WIFIState{}
  38. }
  39. for _, interfaces := range objects {
  40. stationProps, hasStation := interfaces["net.connman.iwd.Station"]
  41. if !hasStation {
  42. continue
  43. }
  44. stateVariant, hasState := stationProps["State"]
  45. if !hasState {
  46. continue
  47. }
  48. state, ok := stateVariant.Value().(string)
  49. if !ok || state != "connected" {
  50. continue
  51. }
  52. connectedNetworkVariant, hasNetwork := stationProps["ConnectedNetwork"]
  53. if !hasNetwork {
  54. continue
  55. }
  56. networkPath, ok := connectedNetworkVariant.Value().(dbus.ObjectPath)
  57. if !ok || networkPath == "/" {
  58. continue
  59. }
  60. networkInterfaces, hasNetworkPath := objects[networkPath]
  61. if !hasNetworkPath {
  62. continue
  63. }
  64. networkProps, hasNetworkInterface := networkInterfaces["net.connman.iwd.Network"]
  65. if !hasNetworkInterface {
  66. continue
  67. }
  68. nameVariant, hasName := networkProps["Name"]
  69. if !hasName {
  70. continue
  71. }
  72. ssid, ok := nameVariant.Value().(string)
  73. if !ok {
  74. continue
  75. }
  76. connectedBSSVariant, hasBSS := stationProps["ConnectedAccessPoint"]
  77. if !hasBSS {
  78. return adapter.WIFIState{SSID: ssid}
  79. }
  80. bssPath, ok := connectedBSSVariant.Value().(dbus.ObjectPath)
  81. if !ok || bssPath == "/" {
  82. return adapter.WIFIState{SSID: ssid}
  83. }
  84. bssInterfaces, hasBSSPath := objects[bssPath]
  85. if !hasBSSPath {
  86. return adapter.WIFIState{SSID: ssid}
  87. }
  88. bssProps, hasBSSInterface := bssInterfaces["net.connman.iwd.BasicServiceSet"]
  89. if !hasBSSInterface {
  90. return adapter.WIFIState{SSID: ssid}
  91. }
  92. addressVariant, hasAddress := bssProps["Address"]
  93. if !hasAddress {
  94. return adapter.WIFIState{SSID: ssid}
  95. }
  96. bssid, ok := addressVariant.Value().(string)
  97. if !ok {
  98. return adapter.WIFIState{SSID: ssid}
  99. }
  100. return adapter.WIFIState{
  101. SSID: ssid,
  102. BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
  103. }
  104. }
  105. return adapter.WIFIState{}
  106. }
  107. func (m *iwdMonitor) Start() error {
  108. if m.callback == nil {
  109. return nil
  110. }
  111. ctx, cancel := context.WithCancel(context.Background())
  112. m.cancel = cancel
  113. m.signalChan = make(chan *dbus.Signal, 10)
  114. m.conn.Signal(m.signalChan)
  115. err := m.conn.AddMatchSignal(
  116. dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
  117. dbus.WithMatchSender("net.connman.iwd"),
  118. )
  119. if err != nil {
  120. return err
  121. }
  122. state := m.ReadWIFIState()
  123. go m.monitorSignals(ctx, m.signalChan, state)
  124. m.callback(state)
  125. return nil
  126. }
  127. func (m *iwdMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
  128. for {
  129. select {
  130. case <-ctx.Done():
  131. return
  132. case signal, ok := <-signalChan:
  133. if !ok {
  134. return
  135. }
  136. if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" {
  137. state := m.ReadWIFIState()
  138. if state != lastState {
  139. lastState = state
  140. m.callback(state)
  141. }
  142. }
  143. }
  144. }
  145. }
  146. func (m *iwdMonitor) Close() error {
  147. if m.cancel != nil {
  148. m.cancel()
  149. }
  150. if m.signalChan != nil {
  151. m.conn.RemoveSignal(m.signalChan)
  152. close(m.signalChan)
  153. }
  154. if m.conn != nil {
  155. return m.conn.Close()
  156. }
  157. return nil
  158. }