@@ -4,16 +4,22 @@ import (
44 "context"
55 "encoding/base64"
66 "encoding/hex"
7+ "errors"
8+ "fmt"
79 "net"
810 "net/netip"
911 "os"
12+ "strings"
1013
1114 "github.com/sagernet/sing-box/transport/wireguard"
15+ "github.com/sagernet/sing/common"
1216 E "github.com/sagernet/sing/common/exceptions"
1317 F "github.com/sagernet/sing/common/format"
1418 M "github.com/sagernet/sing/common/metadata"
1519 "github.com/sagernet/sing/common/x/list"
20+ "github.com/sagernet/sing/service"
1621 "github.com/sagernet/sing/service/pause"
22+ "github.com/sagernet/wireguard-go/conn"
1723 "github.com/sagernet/wireguard-go/device"
1824
1925 "go4.org/netipx"
@@ -146,7 +152,104 @@ func NewEndpoint(options EndpointOptions) (*Endpoint, error) {
146152 }, nil
147153}
148154func (e * Endpoint ) Start (resolve bool ) error {
149- return start (e , resolve )
155+ if common .Any (e .peers , func (peer peerConfig ) bool {
156+ return ! peer .endpoint .IsValid () && peer .destination .IsFqdn ()
157+ }) {
158+ if ! resolve {
159+ return nil
160+ }
161+ for peerIndex , peer := range e .peers {
162+ if peer .endpoint .IsValid () || ! peer .destination .IsFqdn () {
163+ continue
164+ }
165+ destinationAddress , err := e .options .ResolvePeer (peer .destination .Fqdn )
166+ if err != nil {
167+ return E .Cause (err , "resolve endpoint domain for peer[" , peerIndex , "]: " , peer .destination )
168+ }
169+ e .peers [peerIndex ].endpoint = netip .AddrPortFrom (destinationAddress , peer .destination .Port )
170+ }
171+ } else if resolve {
172+ return nil
173+ }
174+
175+ var bind conn.Bind
176+ wgListener , isWgListener := e .options .Dialer .(conn.Listener )
177+ if isWgListener {
178+ bind = conn .NewStdNetBind (wgListener )
179+ } else {
180+ var (
181+ isConnect bool
182+ connectAddr netip.AddrPort
183+ reserved [3 ]uint8
184+ )
185+ if len (e .peers ) == 1 {
186+ isConnect = true
187+ connectAddr = e .peers [0 ].endpoint
188+ reserved = e .peers [0 ].reserved
189+ }
190+ bind = wireguard .NewClientBind (e .options .Context , e .options .Logger , e .options .Dialer , isConnect , connectAddr , reserved )
191+ }
192+ if isWgListener || len (e .peers ) > 1 {
193+ for _ , peer := range e .peers {
194+ if peer .reserved != [3 ]uint8 {} {
195+ bind .SetReservedForEndpoint (peer .endpoint , peer .reserved )
196+ }
197+ }
198+ }
199+ err := e .tunDevice .Start ()
200+ if err != nil {
201+ return err
202+ }
203+ logger := & device.Logger {
204+ Verbosef : func (format string , args ... interface {}) {
205+ e .options .Logger .Debug (fmt .Sprintf (strings .ToLower (format ), args ... ))
206+ },
207+ Errorf : func (format string , args ... interface {}) {
208+ e .options .Logger .Error (fmt .Sprintf (strings .ToLower (format ), args ... ))
209+ },
210+ }
211+ wgDevice := device .NewDevice (e .options .Context , e .tunDevice , bind , logger , e .options .Workers )
212+
213+ uapi , err := uapiListen (e .options .Name )
214+ if err != nil {
215+ return err
216+ }
217+
218+ go func () {
219+ for {
220+ select {
221+ case <- e .options .Context .Done ():
222+ uapi .Close ()
223+ return
224+ default :
225+ conn , err := uapi .Accept ()
226+ if err != nil {
227+ if errors .Is (err , net .ErrClosed ) {
228+ return
229+ }
230+ e .options .Logger .Error (E .Cause (err , "uapi accept error" ))
231+ continue // any other accept error, just continue
232+ }
233+ go wgDevice .IpcHandle (conn )
234+ }
235+ }
236+ }()
237+
238+ e .tunDevice .SetDevice (wgDevice )
239+ ipcConf := e .ipcConf
240+ for _ , peer := range e .peers {
241+ ipcConf += peer .GenerateIpcLines ()
242+ }
243+ err = wgDevice .IpcSet (ipcConf )
244+ if err != nil {
245+ return E .Cause (err , "setup wireguard: \n " , ipcConf )
246+ }
247+ e .device = wgDevice
248+ e .pauseManager = service.FromContext [pause.Manager ](e .options .Context )
249+ if e .pauseManager != nil {
250+ e .pauseCallback = e .pauseManager .RegisterCallback (e .onPauseUpdated )
251+ }
252+ return nil
150253}
151254
152255func (e * Endpoint ) DialContext (ctx context.Context , network string , destination M.Socksaddr ) (net.Conn , error ) {
0 commit comments