mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-08-05 21:35:15 +03:00
Add OpenVPN, TrustTunnel, Sudoku, inbound managers. Fixes
This commit is contained in:
175
transport/openvpn/config.go
Normal file
175
transport/openvpn/config.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package openvpn
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
ProtoUDP = "udp"
|
||||
ProtoTCP = "tcp"
|
||||
|
||||
CipherAES128GCM = "AES-128-GCM"
|
||||
CipherAES192GCM = "AES-192-GCM"
|
||||
CipherAES256GCM = "AES-256-GCM"
|
||||
CipherAES128CBC = "AES-128-CBC"
|
||||
CipherAES192CBC = "AES-192-CBC"
|
||||
CipherAES256CBC = "AES-256-CBC"
|
||||
CipherCHACHA20POLY = "CHACHA20-POLY1305"
|
||||
|
||||
AuthSHA1 = "SHA1"
|
||||
AuthSHA256 = "SHA256"
|
||||
AuthSHA384 = "SHA384"
|
||||
AuthSHA512 = "SHA512"
|
||||
)
|
||||
|
||||
type ClientConfig struct {
|
||||
Proto string
|
||||
Cipher string
|
||||
Auth string
|
||||
Username string
|
||||
Password string
|
||||
KeyDirection int
|
||||
|
||||
TLSCrypt []byte
|
||||
TLSCryptV2 bool
|
||||
TLSCryptKey []byte
|
||||
TLSCryptV2WKc []byte
|
||||
TLSAuthKey []byte
|
||||
}
|
||||
|
||||
func (c *ClientConfig) Prepare() error {
|
||||
if c == nil {
|
||||
return errors.New("nil openvpn client config")
|
||||
}
|
||||
c.Proto = normalizeProto(c.Proto)
|
||||
c.Cipher = strings.ToUpper(strings.TrimSpace(c.Cipher))
|
||||
if c.Auth == "" {
|
||||
c.Auth = AuthSHA1
|
||||
}
|
||||
c.Auth = strings.ToUpper(strings.TrimSpace(c.Auth))
|
||||
if c.Proto != ProtoUDP && c.Proto != ProtoTCP {
|
||||
return fmt.Errorf("unsupported openvpn proto %q: only udp and tcp are supported", c.Proto)
|
||||
}
|
||||
if c.Cipher != "" && !isValidCipher(c.Cipher) {
|
||||
return fmt.Errorf("unsupported openvpn cipher %q", c.Cipher)
|
||||
}
|
||||
if !isValidAuth(c.Auth) {
|
||||
return fmt.Errorf("unsupported openvpn auth %q", c.Auth)
|
||||
}
|
||||
if c.TLSCryptV2 {
|
||||
kc, wkc, err := decodeTLSCryptV2Key(c.TLSCrypt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse tls-crypt-v2 key: %w", err)
|
||||
}
|
||||
c.TLSCryptKey = kc
|
||||
c.TLSCryptV2WKc = wkc
|
||||
return nil
|
||||
}
|
||||
if len(strings.TrimSpace(string(c.TLSCrypt))) == 0 {
|
||||
return nil
|
||||
}
|
||||
key, err := decodeStaticKey(c.TLSCrypt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse tls key: %w", err)
|
||||
}
|
||||
if c.KeyDirection >= 0 {
|
||||
c.TLSAuthKey = key
|
||||
} else {
|
||||
c.TLSCryptKey = key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeProto(proto string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(proto)) {
|
||||
case "", "udp", "udp4":
|
||||
return ProtoUDP
|
||||
case "tcp", "tcp-client", "tcp4", "tcp4-client":
|
||||
return ProtoTCP
|
||||
default:
|
||||
return strings.ToLower(strings.TrimSpace(proto))
|
||||
}
|
||||
}
|
||||
|
||||
func isValidCipher(cipher string) bool {
|
||||
switch cipher {
|
||||
case CipherAES128GCM, CipherAES192GCM, CipherAES256GCM,
|
||||
CipherAES128CBC, CipherAES192CBC, CipherAES256CBC,
|
||||
CipherCHACHA20POLY:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isValidAuth(auth string) bool {
|
||||
switch auth {
|
||||
case AuthSHA1, AuthSHA256, AuthSHA384, AuthSHA512:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func decodeStaticKey(block []byte) ([]byte, error) {
|
||||
var hexLines []string
|
||||
for _, raw := range strings.Split(string(block), "\n") {
|
||||
line := strings.TrimSpace(raw)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "-----BEGIN OpenVPN Static key") || strings.HasPrefix(line, "-----END OpenVPN Static key") {
|
||||
continue
|
||||
}
|
||||
hexLines = append(hexLines, line)
|
||||
}
|
||||
encoded := strings.Join(hexLines, "")
|
||||
key, err := hex.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(key) != 256 {
|
||||
return nil, fmt.Errorf("invalid static key length %d, expected 256 bytes", len(key))
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func decodeTLSCryptV2Key(block []byte) (kc []byte, wkc []byte, err error) {
|
||||
data, err := decodePEM(block, "OpenVPN tls-crypt-v2 client key")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(data) < 256 {
|
||||
return nil, nil, fmt.Errorf("tls-crypt-v2 key too short: %d bytes", len(data))
|
||||
}
|
||||
return data[:256], data[256:], nil
|
||||
}
|
||||
|
||||
func decodePEM(block []byte, expectedHeader string) ([]byte, error) {
|
||||
lines := strings.Split(string(block), "\n")
|
||||
var b64 strings.Builder
|
||||
inBlock := false
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.Contains(line, "BEGIN") && strings.Contains(line, expectedHeader) {
|
||||
inBlock = true
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, "END") && strings.Contains(line, expectedHeader) {
|
||||
break
|
||||
}
|
||||
if inBlock {
|
||||
b64.WriteString(line)
|
||||
}
|
||||
}
|
||||
if b64.Len() == 0 {
|
||||
return nil, fmt.Errorf("no %s block found", expectedHeader)
|
||||
}
|
||||
return base64Decode(b64.String())
|
||||
}
|
||||
|
||||
func base64Decode(s string) ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString(s)
|
||||
}
|
||||
Reference in New Issue
Block a user