Skip to content

Commit

Permalink
warp: rewrite API functions
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Pashmfouroush <[email protected]>
  • Loading branch information
markpash committed May 22, 2024
1 parent cfa41b7 commit bb5871f
Show file tree
Hide file tree
Showing 4 changed files with 540 additions and 284 deletions.
24 changes: 0 additions & 24 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/bepass-org/warp-plus/iputils"
"github.com/bepass-org/warp-plus/psiphon"
"github.com/bepass-org/warp-plus/warp"
"github.com/bepass-org/warp-plus/wiresocks"
"github.com/go-ini/ini"
)
Expand Down Expand Up @@ -58,11 +57,6 @@ func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error {
return errors.New("can't use psiphon and tun at the same time")
}

// create identities
if err := createPrimaryAndSecondaryIdentities(l.With("subsystem", "warp/account"), opts); err != nil {
return err
}

// Decide Working Scenario
endpoints := []string{opts.Endpoint, opts.Endpoint}

Expand Down Expand Up @@ -391,21 +385,3 @@ func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, opts WarpOptions, e
l.Info("serving proxy", "address", opts.Bind)
return nil
}

func createPrimaryAndSecondaryIdentities(l *slog.Logger, opts WarpOptions) error {
// make primary identity
err := warp.LoadOrCreateIdentity(l, path.Join(opts.CacheDir, "primary"), opts.License)
if err != nil {
l.Error("couldn't load primary warp identity")
return err
}

// make secondary
err = warp.LoadOrCreateIdentity(l, path.Join(opts.CacheDir, "secondary"), opts.License)
if err != nil {
l.Error("couldn't load secondary warp identity")
return err
}

return nil
}
23 changes: 23 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ func main() {
opts.Endpoint = addrPort.String()
}

// create identities
if err := createPrimaryAndSecondaryIdentities(l.With("subsystem", "warp/account"), opts); err != nil {
fatal(l, err)
}

ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
go func() {
if err := app.RunWarp(ctx, l, opts); err != nil {
Expand All @@ -193,6 +198,24 @@ func main() {
<-ctx.Done()
}

func createPrimaryAndSecondaryIdentities(l *slog.Logger, opts app.WarpOptions) error {
// make primary identity
err := warp.LoadOrCreateIdentity(l, path.Join(opts.CacheDir, "primary"), opts.License)
if err != nil {
l.Error("couldn't load primary warp identity")
return err
}

// make secondary
err = warp.LoadOrCreateIdentity(l, path.Join(opts.CacheDir, "secondary"), opts.License)
if err != nil {
l.Error("couldn't load secondary warp identity")
return err
}

return nil
}

func fatal(l *slog.Logger, err error) {
l.Error(err.Error())
os.Exit(1)
Expand Down
262 changes: 2 additions & 260 deletions warp/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,182 +2,19 @@ package warp

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"os"
"path/filepath"
"time"
)

const (
apiVersion = "v0a3596"
apiURL = "https://api.cloudflareclient.com"
regURL = apiURL + "/" + apiVersion + "/reg"
)

var (
identityFile = "wgcf-identity.json"
profileFile = "wgcf-profile.ini"
)

var (
defaultHeaders = makeDefaultHeaders()
client = makeClient()
)

type IdentityAccount struct {
Created string `json:"created"`
Updated string `json:"updated"`
License string `json:"license"`
PremiumData int64 `json:"premium_data"`
WarpPlus bool `json:"warp_plus"`
AccountType string `json:"account_type"`
ReferralRenewalCountdown int64 `json:"referral_renewal_countdown"`
Role string `json:"role"`
ID string `json:"id"`
Quota int64 `json:"quota"`
Usage int64 `json:"usage"`
ReferralCount int64 `json:"referral_count"`
TTL string `json:"ttl"`
}

type IdentityConfigPeerEndpoint struct {
V4 string `json:"v4"`
V6 string `json:"v6"`
Host string `json:"host"`
Ports []uint16 `json:"ports"`
}

type IdentityConfigPeer struct {
PublicKey string `json:"public_key"`
Endpoint IdentityConfigPeerEndpoint `json:"endpoint"`
}

type IdentityConfigInterfaceAddresses struct {
V4 string `json:"v4"`
V6 string `json:"v6"`
}

type IdentityConfigInterface struct {
Addresses IdentityConfigInterfaceAddresses `json:"addresses"`
}
type IdentityConfigServices struct {
HTTPProxy string `json:"http_proxy"`
}

type IdentityConfig struct {
Peers []IdentityConfigPeer `json:"peers"`
Interface IdentityConfigInterface `json:"interface"`
Services IdentityConfigServices `json:"services"`
ClientID string `json:"client_id"`
}

type Identity struct {
PrivateKey string `json:"private_key"`
Key string `json:"key"`
Account IdentityAccount `json:"account"`
Place int64 `json:"place"`
FCMToken string `json:"fcm_token"`
Name string `json:"name"`
TOS string `json:"tos"`
Locale string `json:"locale"`
InstallID string `json:"install_id"`
WarpEnabled bool `json:"warp_enabled"`
Type string `json:"type"`
Model string `json:"model"`
Config IdentityConfig `json:"config"`
Token string `json:"token"`
Enabled bool `json:"enabled"`
ID string `json:"id"`
Created string `json:"created"`
Updated string `json:"updated"`
WaitlistEnabled bool `json:"waitlist_enabled"`
}

func makeDefaultHeaders() map[string]string {
return map[string]string{
"Content-Type": "application/json; charset=UTF-8",
"User-Agent": "okhttp/3.12.1",
"CF-Client-Version": "a-6.30-3596",
}
}

func makeClient() *http.Client {
// Create a custom dialer using the TLS config
plainDialer := &net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 5 * time.Second,
}
tlsDialer := Dialer{}
// Create a custom HTTP transport
transport := &http.Transport{
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return tlsDialer.TLSDial(plainDialer, network, addr)
},
}

// Create a custom HTTP client using the transport
return &http.Client{
Transport: transport,
// Other client configurations can be added here
}
}

func doRegister(publicKey string) (Identity, error) {
data := map[string]interface{}{
"install_id": "",
"fcm_token": "",
"tos": time.Now().Format(time.RFC3339Nano),
"key": publicKey,
"type": "Android",
"model": "PC",
"locale": "en_US",
"warp_enabled": true,
}

jsonBody, err := json.Marshal(data)
if err != nil {
return Identity{}, err
}

req, err := http.NewRequest("POST", regURL, bytes.NewBuffer(jsonBody))
if err != nil {
return Identity{}, err
}

// Set headers
for k, v := range defaultHeaders {
req.Header.Set(k, v)
}

// Create HTTP client and execute request
resp, err := client.Do(req)
if err != nil {
return Identity{}, err
}
defer resp.Body.Close()

// convert response to byte array
responseData, err := io.ReadAll(resp.Body)
if err != nil {
return Identity{}, err
}

var rspData = Identity{}
err = json.Unmarshal(responseData, &rspData)
if err != nil {
return Identity{}, err
}

return rspData, nil
}

func saveIdentity(a Identity, path string) error {
file, err := os.Create(filepath.Join(path, identityFile))
if err != nil {
Expand All @@ -194,73 +31,6 @@ func saveIdentity(a Identity, path string) error {
return file.Close()
}

func updateLicenseKey(accountID, accessToken, license string) (IdentityAccount, error) {
jsonData, err := json.Marshal(map[string]string{"license": license})
if err != nil {
return IdentityAccount{}, err
}

url := fmt.Sprintf("%s/%s/account", regURL, accountID)

req, err := http.NewRequest("PATCH", url, bytes.NewBuffer(jsonData))
if err != nil {
return IdentityAccount{}, err
}

headers := defaultHeaders
headers["Authorization"] = "Bearer " + accessToken
for k, v := range headers {
req.Header.Set(k, v)
}

resp, err := client.Do(req)
if err != nil {
return IdentityAccount{}, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
s, err := io.ReadAll(resp.Body)
if err != nil {
return IdentityAccount{}, err
}

return IdentityAccount{}, fmt.Errorf("activation error, status %d %s", resp.StatusCode, string(s))
}

req, err = http.NewRequest("GET", url, nil)
if err != nil {
return IdentityAccount{}, err
}

for k, v := range headers {
req.Header.Set(k, v)
}

resp1, err := client.Do(req)
if err != nil {
return IdentityAccount{}, err
}
defer resp1.Body.Close()

if resp1.StatusCode != http.StatusOK {
s, err := io.ReadAll(resp1.Body)
if err != nil {
return IdentityAccount{}, err
}

return IdentityAccount{}, fmt.Errorf("activation error, status %d %s", resp1.StatusCode, string(s))
}

var activationResp1 = IdentityAccount{}
err = json.NewDecoder(resp1.Body).Decode(&activationResp1)
if err != nil {
return IdentityAccount{}, err
}

return activationResp1, nil
}

func createConf(i Identity, path string) error {
var buffer bytes.Buffer

Expand Down Expand Up @@ -360,14 +130,14 @@ func CreateIdentity(l *slog.Logger, path, license string) (Identity, error) {
privateKey, publicKey := priv.String(), priv.PublicKey().String()

l.Info("creating new identity")
i, err := doRegister(publicKey)
i, err := Register(publicKey)
if err != nil {
return Identity{}, err
}

if license != "" {
l.Info("updating account license key")
ac, err := updateLicenseKey(i.ID, i.Token, license)
ac, err := UpdateAccount(i.Token, i.ID, license)
if err != nil {
return Identity{}, err
}
Expand All @@ -383,31 +153,3 @@ func CreateIdentity(l *slog.Logger, path, license string) (Identity, error) {

return i, nil
}

func RemoveDevice(l *slog.Logger, accountID, accessToken string) error {
url := fmt.Sprintf("%s/%s", regURL, accountID)
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
return err
}

headers := defaultHeaders
headers["Authorization"] = "Bearer " + accessToken
for k, v := range headers {
req.Header.Set(k, v)
}

// Create HTTP client and execute request
resp, err := client.Do(req)
if err != nil {
l.Info("sending request to remote server", err)
return err
}
defer resp.Body.Close()

if resp.StatusCode != 204 {
return fmt.Errorf("error in deleting account %d %s", resp.StatusCode, resp.Status)
}

return nil
}
Loading

0 comments on commit bb5871f

Please sign in to comment.