Files
sing-box/protocol/openconnect/status.go
T
2026-07-17 17:42:02 +08:00

136 lines
3.6 KiB
Go

package openconnect
import (
"slices"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-openconnect"
"github.com/sagernet/sing/common"
)
var _ adapter.OpenConnectEndpoint = (*Endpoint)(nil)
func (e *Endpoint) OpenConnectStatus() adapter.OpenConnectStatus {
var status adapter.OpenConnectStatus
clientState := e.state.Load()
authForm := e.client.PendingAuthForm()
e.statusAccess.Lock()
status.Error = e.terminalError
e.statusAccess.Unlock()
if authForm != nil {
fields := common.Map(authForm.Fields, func(field openconnect.AuthFormField) adapter.OpenConnectAuthFormField {
return adapter.OpenConnectAuthFormField{
SubmissionKey: field.SubmissionKey,
Name: field.Name,
Label: field.Label,
Kind: field.Kind,
Value: field.Value,
Options: common.Map(field.Options, func(choice openconnect.AuthFormChoice) adapter.OpenConnectAuthFormChoice {
return adapter.OpenConnectAuthFormChoice{
Value: choice.Value,
Label: choice.Label,
}
}),
}
})
status.AuthForm = &adapter.OpenConnectAuthForm{
ID: authForm.ID,
Banner: authForm.Banner,
Message: authForm.Message,
Error: authForm.Error,
URL: authForm.URL,
Fields: fields,
}
}
switch {
case status.AuthForm != nil:
status.State = adapter.OpenConnectStateAuthPending
case status.Error != "":
status.State = adapter.OpenConnectStateError
case clientState.started && clientState.tunnelConfigured && e.client.Ready():
status.State = adapter.OpenConnectStateConnected
tunnelInfo := clientState.tunnelInfo
tunnelInfo.IPv4 = slices.Clone(tunnelInfo.IPv4)
tunnelInfo.IPv6 = slices.Clone(tunnelInfo.IPv6)
tunnelInfo.DNS = slices.Clone(tunnelInfo.DNS)
status.TunnelInfo = &tunnelInfo
default:
status.State = adapter.OpenConnectStateConnecting
}
return status
}
func (e *Endpoint) StatusUpdated() <-chan struct{} {
e.statusAccess.Lock()
defer e.statusAccess.Unlock()
return e.statusUpdated
}
func (e *Endpoint) CompleteAuthForm(formID string, values map[string]string) error {
return e.client.CompleteAuthForm(formID, values)
}
func (e *Endpoint) CancelAuthForm(formID string) error {
return e.client.CancelAuthForm(formID)
}
func (e *Endpoint) notifyStatusUpdated() {
e.statusAccess.Lock()
e.notifyStatusUpdatedLocked()
e.statusAccess.Unlock()
}
func (e *Endpoint) notifyStatusUpdatedLocked() {
close(e.statusUpdated)
e.statusUpdated = make(chan struct{})
}
func (e *Endpoint) setTerminalError(err error) {
e.statusAccess.Lock()
e.terminalError = err.Error()
e.notifyStatusUpdatedLocked()
e.statusAccess.Unlock()
}
func (e *Endpoint) watchAuthForms() {
defer close(e.authFormLoopDone)
var loggedAuthFormID string
for {
authFormUpdated := e.client.AuthFormUpdated()
authForm := e.client.PendingAuthForm()
if authForm != nil && authForm.ID != loggedAuthFormID {
loggedAuthFormID = authForm.ID
if authForm.URL != "" {
e.logger.Info("waiting for authentication: ", authForm.URL)
} else {
e.logger.Info("waiting for authentication")
}
}
e.notifyStatusUpdated()
select {
case <-e.loopContext.Done():
return
case <-authFormUpdated:
}
}
}
func (e *Endpoint) watchActiveTransport() {
defer close(e.activeTransportLoopDone)
for {
transportUpdated := e.client.ActiveTransportUpdated()
transport := e.client.ActiveTransport()
e.stateAccess.Lock()
e.updateState(func(state *clientState) {
state.tunnelInfo.Transport = transport
})
e.stateAccess.Unlock()
e.notifyStatusUpdated()
select {
case <-e.loopContext.Done():
return
case <-transportUpdated:
}
}
}