mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-07-19 21:08:40 +08:00
115 lines
3.2 KiB
Go
115 lines
3.2 KiB
Go
package openvpn
|
|
|
|
import (
|
|
"slices"
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
ovpn "github.com/sagernet/sing-openvpn"
|
|
)
|
|
|
|
var _ adapter.OpenVPNEndpoint = (*ClientEndpoint)(nil)
|
|
|
|
func (c *ClientEndpoint) OpenVPNStatus() adapter.OpenVPNStatus {
|
|
var status adapter.OpenVPNStatus
|
|
challenge := c.client.PendingChallenge()
|
|
state := c.state.Load()
|
|
c.statusAccess.Lock()
|
|
status.Error = c.terminalError
|
|
c.statusAccess.Unlock()
|
|
switch {
|
|
case challenge != nil:
|
|
status.State = adapter.OpenVPNStateAuthPending
|
|
status.Challenge = &adapter.OpenVPNChallenge{
|
|
ID: challenge.ID,
|
|
Kind: string(challenge.Kind),
|
|
Username: challenge.Username,
|
|
Message: challenge.Message,
|
|
URL: challenge.URL,
|
|
SecretMessage: challenge.SecretMessage,
|
|
Echo: challenge.Echo,
|
|
PreviousError: challenge.PreviousError,
|
|
Deadline: challenge.Deadline,
|
|
}
|
|
case status.Error != "":
|
|
status.State = adapter.OpenVPNStateError
|
|
case state.started && state.tunnelConfigured && c.client.Ready():
|
|
status.State = adapter.OpenVPNStateConnected
|
|
tunnelInfo := state.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.OpenVPNStateConnecting
|
|
}
|
|
return status
|
|
}
|
|
|
|
func (c *ClientEndpoint) StatusUpdated() <-chan struct{} {
|
|
c.statusAccess.Lock()
|
|
defer c.statusAccess.Unlock()
|
|
return c.statusUpdated
|
|
}
|
|
|
|
func (c *ClientEndpoint) CompleteChallenge(challengeID string, response adapter.OpenVPNChallengeResponse) error {
|
|
return c.client.CompleteChallenge(challengeID, ovpn.ChallengeResponse{
|
|
Username: response.Username,
|
|
Password: response.Password,
|
|
Secret: response.Secret,
|
|
})
|
|
}
|
|
|
|
func (c *ClientEndpoint) CancelChallenge(challengeID string) error {
|
|
return c.client.CancelChallenge(challengeID)
|
|
}
|
|
|
|
func (c *ClientEndpoint) notifyStatusUpdated() {
|
|
c.statusAccess.Lock()
|
|
c.notifyStatusUpdatedLocked()
|
|
c.statusAccess.Unlock()
|
|
}
|
|
|
|
func (c *ClientEndpoint) notifyStatusUpdatedLocked() {
|
|
close(c.statusUpdated)
|
|
c.statusUpdated = make(chan struct{})
|
|
}
|
|
|
|
func (c *ClientEndpoint) setTerminalError(err error) {
|
|
c.statusAccess.Lock()
|
|
c.terminalError = err.Error()
|
|
c.notifyStatusUpdatedLocked()
|
|
c.statusAccess.Unlock()
|
|
}
|
|
|
|
func (c *ClientEndpoint) watchChallenges() {
|
|
defer close(c.challengeLoopDone)
|
|
var loggedChallengeID string
|
|
for {
|
|
challengeUpdated := c.client.ChallengeUpdated()
|
|
challenge := c.client.PendingChallenge()
|
|
if challenge != nil && challenge.ID != loggedChallengeID {
|
|
loggedChallengeID = challenge.ID
|
|
c.logChallenge(challenge)
|
|
}
|
|
c.notifyStatusUpdated()
|
|
select {
|
|
case <-c.loopContext.Done():
|
|
return
|
|
case <-challengeUpdated:
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *ClientEndpoint) logChallenge(challenge *ovpn.Challenge) {
|
|
switch challenge.Kind {
|
|
case ovpn.ChallengeCredentials:
|
|
c.logger.Info("waiting for credentials")
|
|
case ovpn.ChallengeSecret:
|
|
c.logger.Info("waiting for challenge response: ", challenge.Message)
|
|
case ovpn.ChallengeMessage:
|
|
c.logger.Info("authentication message: ", challenge.Message)
|
|
case ovpn.ChallengeOpenURL:
|
|
c.logger.Info("waiting for authentication: ", challenge.URL)
|
|
}
|
|
}
|