mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-14 00:51:12 +03:00
Add MTProxy, MASQUE, VPN, Link parser. Update AmneziaWG. Remove Tunneling
This commit is contained in:
@@ -1,15 +1,12 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
type CloudflareApi struct {
|
||||
@@ -25,50 +22,93 @@ func NewCloudflareApi(opts ...CloudflareApiOption) *CloudflareApi {
|
||||
}
|
||||
|
||||
func (api *CloudflareApi) CreateProfile(ctx context.Context, publicKey string) (*CloudflareProfile, error) {
|
||||
request, err := http.NewRequest("POST", "https://api.cloudflareclient.com/v0i1909051800/reg", strings.NewReader(
|
||||
fmt.Sprintf(
|
||||
"{\"install_id\":\"\",\"tos\":\"%s\",\"key\":\"%s\",\"fcm_token\":\"\",\"type\":\"ios\",\"locale\":\"en_US\"}",
|
||||
time.Now().Format("2006-01-02T15:04:05.000Z"),
|
||||
publicKey,
|
||||
),
|
||||
))
|
||||
serial, err := GenerateRandomAndroidSerial()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate serial: %v", err)
|
||||
}
|
||||
data := Registration{
|
||||
Key: publicKey,
|
||||
InstallID: "",
|
||||
FcmToken: "",
|
||||
Tos: TimeAsCfString(time.Now()),
|
||||
Model: "PC",
|
||||
Serial: serial,
|
||||
OsVersion: "",
|
||||
KeyType: KeyTypeWg,
|
||||
TunType: TunTypeWg,
|
||||
Locale: "en-US",
|
||||
}
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal json: %v", err)
|
||||
}
|
||||
request, err := http.NewRequest("POST", ApiUrl+"/"+ApiVersion+"/reg", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range Headers {
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
response, err := api.client.Do(request.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("status code is not 200")
|
||||
}
|
||||
content, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to register: %v", response.StatusCode)
|
||||
}
|
||||
profile := new(CloudflareProfile)
|
||||
return profile, json.NewDecoder(strings.NewReader(gjson.Get(string(content), "result").Raw)).Decode(profile)
|
||||
return profile, json.NewDecoder(response.Body).Decode(profile)
|
||||
}
|
||||
|
||||
func (api *CloudflareApi) GetProfile(ctx context.Context, authToken string, id string) (*CloudflareProfile, error) {
|
||||
request, err := http.NewRequest("GET", "https://api.cloudflareclient.com/v0i1909051800/reg/"+id, nil)
|
||||
func (api *CloudflareApi) EnrollKey(ctx context.Context, authToken string, id string, keyType, tunType, publicKey string) (*CloudflareProfile, error) {
|
||||
deviceUpdate := DeviceUpdate{
|
||||
Name: "PC",
|
||||
Key: publicKey,
|
||||
KeyType: keyType,
|
||||
TunType: tunType,
|
||||
}
|
||||
jsonData, err := json.Marshal(deviceUpdate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal json: %v", err)
|
||||
}
|
||||
request, err := http.NewRequest("PATCH", ApiUrl+"/"+ApiVersion+"/reg/"+id, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range Headers {
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
request.Header.Set("Authorization", "Bearer "+authToken)
|
||||
response, err := api.client.Do(request.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("status code is not 200")
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to enroll key: %v", response.StatusCode)
|
||||
}
|
||||
content, err := io.ReadAll(response.Body)
|
||||
profile := new(CloudflareProfile)
|
||||
return profile, json.NewDecoder(response.Body).Decode(profile)
|
||||
}
|
||||
|
||||
func (api *CloudflareApi) GetProfile(ctx context.Context, authToken string, id string) (*CloudflareProfile, error) {
|
||||
request, err := http.NewRequest("GET", ApiUrl+"/"+ApiVersion+"/reg/"+id, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range Headers {
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
request.Header.Set("Authorization", "Bearer "+authToken)
|
||||
response, err := api.client.Do(request.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to get profile: %v", response.StatusCode)
|
||||
}
|
||||
profile := new(CloudflareProfile)
|
||||
return profile, json.NewDecoder(strings.NewReader(gjson.Get(string(content), "result").Raw)).Decode(profile)
|
||||
return profile, json.NewDecoder(response.Body).Decode(profile)
|
||||
}
|
||||
|
||||
25
common/cloudflare/constant.go
Normal file
25
common/cloudflare/constant.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package cloudflare
|
||||
|
||||
const (
|
||||
ApiUrl = "https://api.cloudflareclient.com"
|
||||
ApiVersion = "v0a4471"
|
||||
ConnectSNI = "consumer-masque.cloudflareclient.com"
|
||||
// unused for now
|
||||
ZeroTierSNI = "zt-masque.cloudflareclient.com"
|
||||
ConnectURI = "https://cloudflareaccess.com"
|
||||
DefaultModel = "PC"
|
||||
KeyTypeWg = "curve25519"
|
||||
TunTypeWg = "wireguard"
|
||||
KeyTypeMasque = "secp256r1"
|
||||
TunTypeMasque = "masque"
|
||||
DefaultLocale = "en_US"
|
||||
DefaultEndpointH2V4 = "162.159.198.2"
|
||||
DefaultEndpointH2V6 = ""
|
||||
)
|
||||
|
||||
var Headers = map[string]string{
|
||||
"User-Agent": "WARP for Android",
|
||||
"CF-Client-Version": "a-6.35-4471",
|
||||
"Content-Type": "application/json; charset=UTF-8",
|
||||
"Connection": "Keep-Alive",
|
||||
}
|
||||
132
common/cloudflare/models.go
Normal file
132
common/cloudflare/models.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Registration struct {
|
||||
Key string `json:"key"`
|
||||
InstallID string `json:"install_id"`
|
||||
FcmToken string `json:"fcm_token"`
|
||||
Tos string `json:"tos"`
|
||||
Model string `json:"model"`
|
||||
Serial string `json:"serial_number"`
|
||||
OsVersion string `json:"os_version"`
|
||||
KeyType string `json:"key_type"`
|
||||
TunType string `json:"tunnel_type"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
|
||||
type CloudflareProfile struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Model string `json:"model"`
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
KeyType string `json:"key_type"`
|
||||
TunType string `json:"tunnel_type"`
|
||||
Account Account `json:"account"`
|
||||
Config Config `json:"config"`
|
||||
// WarpEnabled not set for ZeroTier
|
||||
WarpEnabled bool `json:"warp_enabled,omitempty"`
|
||||
// Waitlist not set for ZeroTier
|
||||
Waitlist bool `json:"waitlist_enabled,omitempty"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
// Tos not set for ZeroTier
|
||||
Tos string `json:"tos,omitempty"`
|
||||
// Place not set for ZeroTier
|
||||
Place int `json:"place,omitempty"`
|
||||
Locale string `json:"locale"`
|
||||
// Enabled not set for ZeroTier
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
InstallID string `json:"install_id"`
|
||||
// Token only set for /reg call
|
||||
Token string `json:"token,omitempty"`
|
||||
FcmToken string `json:"fcm_token"`
|
||||
// SerialNumber not set for ZeroTier
|
||||
SerialNumber string `json:"serial_number,omitempty"`
|
||||
Policy Policy `json:"policy"`
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
ID string `json:"id"`
|
||||
AccountType string `json:"account_type"`
|
||||
// Created not set for ZeroTier
|
||||
Created string `json:"created,omitempty"`
|
||||
// Updated not set for ZeroTier
|
||||
Updated string `json:"updated,omitempty"`
|
||||
// Managed only set for ZeroTier
|
||||
Managed string `json:"managed,omitempty"`
|
||||
// Organization only set for ZeroTier
|
||||
Organization string `json:"organization,omitempty"`
|
||||
// PremiumData not set for ZeroTier
|
||||
PremiumData int `json:"premium_data,omitempty"`
|
||||
// Quota not set for ZeroTier
|
||||
Quota int `json:"quota,omitempty"`
|
||||
// WarpPlus not set for ZeroTier
|
||||
WarpPlus bool `json:"warp_plus,omitempty"`
|
||||
// ReferralCode not set for ZeroTier
|
||||
ReferralCount int `json:"referral_count,omitempty"`
|
||||
// ReferralRenewalCount not set for ZeroTier
|
||||
ReferralRenewalCount int `json:"referral_renewal_countdown,omitempty"`
|
||||
// Role not set for ZeroTier
|
||||
Role string `json:"role,omitempty"`
|
||||
// License not set for ZeroTier
|
||||
License string `json:"license,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ClientID string `json:"client_id"`
|
||||
Peers []Peer `json:"peers"`
|
||||
Interface struct {
|
||||
Addresses struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
} `json:"addresses"`
|
||||
} `json:"interface"`
|
||||
Services struct {
|
||||
HTTPProxy string `json:"http_proxy"`
|
||||
} `json:"services"`
|
||||
}
|
||||
|
||||
type Peer struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
Endpoint struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
Host string `json:"host"`
|
||||
Ports []int `json:"ports"`
|
||||
} `json:"endpoint"`
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
TunnelProtocol string `json:"tunnel_protocol"`
|
||||
}
|
||||
|
||||
type DeviceUpdate struct {
|
||||
Key string `json:"key"`
|
||||
KeyType string `json:"key_type"`
|
||||
TunType string `json:"tunnel_type"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type APIError struct {
|
||||
Result interface{} `json:"result"`
|
||||
Success bool `json:"success"`
|
||||
Errors []ErrorInfo `json:"errors"`
|
||||
Messages []string `json:"messages"`
|
||||
}
|
||||
|
||||
type ErrorInfo struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *APIError) Error() string {
|
||||
errors := make([]string, len(e.Errors))
|
||||
for i, err := range e.Errors {
|
||||
errors[i] = err.Message
|
||||
}
|
||||
return strings.Join(errors, ",")
|
||||
}
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CloudflareApiOption func(api *CloudflareApi)
|
||||
|
||||
func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) CloudflareApiOption {
|
||||
return func(api *CloudflareApi) {
|
||||
api.client.Timeout = 30 * time.Second
|
||||
api.client.Transport = &http.Transport{
|
||||
DialContext: dialContext,
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package cloudflare
|
||||
|
||||
import "time"
|
||||
|
||||
type CloudflareProfile struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
Account struct {
|
||||
ID string `json:"id"`
|
||||
AccountType string `json:"account_type"`
|
||||
Created time.Time `json:"created"`
|
||||
Updated time.Time `json:"updated"`
|
||||
PremiumData int `json:"premium_data"`
|
||||
Quota int `json:"quota"`
|
||||
Usage int `json:"usage"`
|
||||
WARPPlus bool `json:"warp_plus"`
|
||||
ReferralCount int `json:"referral_count"`
|
||||
ReferralRenewalCountdown int `json:"referral_renewal_countdown"`
|
||||
Role string `json:"role"`
|
||||
License string `json:"license"`
|
||||
TTL time.Time `json:"ttl"`
|
||||
} `json:"account"`
|
||||
Config struct {
|
||||
ClientID string `json:"client_id"`
|
||||
Interface struct {
|
||||
Addresses struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
} `json:"addresses"`
|
||||
} `json:"interface"`
|
||||
Peers []struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
Endpoint struct {
|
||||
V4 string `json:"v4"`
|
||||
V6 string `json:"v6"`
|
||||
Host string `json:"host"`
|
||||
Ports []int `json:"ports"`
|
||||
} `json:"endpoint"`
|
||||
} `json:"peers"`
|
||||
Services struct {
|
||||
HTTPProxy string `json:"http_proxy"`
|
||||
} `json:"services"`
|
||||
Metrics struct {
|
||||
Ping int `json:"ping"`
|
||||
Report int `json:"report"`
|
||||
} `json:"metrics"`
|
||||
} `json:"config"`
|
||||
Token string `json:"token"`
|
||||
WARPEnabled bool `json:"warp_enabled"`
|
||||
WaitlistEnabled bool `json:"waitlist_enabled"`
|
||||
Created time.Time `json:"created"`
|
||||
Updated time.Time `json:"updated"`
|
||||
Tos time.Time `json:"tos"`
|
||||
Place int `json:"place"`
|
||||
Locale string `json:"locale"`
|
||||
Enabled bool `json:"enabled"`
|
||||
InstallID string `json:"install_id"`
|
||||
FcmToken string `json:"fcm_token"`
|
||||
Policy struct {
|
||||
TunnelProtocol string `json:"tunnel_protocol"`
|
||||
} `json:"policy"`
|
||||
}
|
||||
19
common/cloudflare/utils.go
Normal file
19
common/cloudflare/utils.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateRandomAndroidSerial() (string, error) {
|
||||
serial := make([]byte, 8)
|
||||
if _, err := rand.Read(serial); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(serial), nil
|
||||
}
|
||||
|
||||
func TimeAsCfString(t time.Time) string {
|
||||
return t.Format("2006-01-02T15:04:05.000-07:00")
|
||||
}
|
||||
Reference in New Issue
Block a user