Skip to content

Commit

Permalink
option warp endpoint #345
Browse files Browse the repository at this point in the history
  • Loading branch information
alireza0 committed Jan 19, 2025
1 parent 4dc02f7 commit a0dc165
Show file tree
Hide file tree
Showing 8 changed files with 425 additions and 6 deletions.
12 changes: 10 additions & 2 deletions backend/database/model/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Endpoint struct {
Type string `json:"type" form:"type"`
Tag string `json:"tag" form:"tag" gorm:"unique"`
Options json.RawMessage `json:"-" form:"-"`
Ext json.RawMessage `json:"ext" form:"ext"`
}

func (o *Endpoint) UnmarshalJSON(data []byte) error {
Expand All @@ -27,17 +28,24 @@ func (o *Endpoint) UnmarshalJSON(data []byte) error {
delete(raw, "type")
o.Tag = raw["tag"].(string)
delete(raw, "tag")
o.Ext, _ = json.MarshalIndent(raw["ext"], "", " ")
delete(raw, "ext")

// Remaining fields
o.Options, err = json.Marshal(raw)
o.Options, err = json.MarshalIndent(raw, "", " ")
return err
}

// MarshalJSON customizes marshalling
func (o Endpoint) MarshalJSON() ([]byte, error) {
// Combine fixed fields and dynamic fields into one map
combined := make(map[string]interface{})
combined["type"] = o.Type
switch o.Type {
case "warp":
combined["type"] = "wireguard"
default:
combined["type"] = o.Type
}
combined["tag"] = o.Tag

if o.Options != nil {
Expand Down
24 changes: 23 additions & 1 deletion backend/service/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"gorm.io/gorm"
)

type EndpointService struct{}
type EndpointService struct {
WarpService
}

func (o *EndpointService) GetAll() (*[]map[string]interface{}, error) {
db := database.GetDB()
Expand All @@ -25,6 +27,7 @@ func (o *EndpointService) GetAll() (*[]map[string]interface{}, error) {
"id": endpoint.Id,
"type": endpoint.Type,
"tag": endpoint.Tag,
"ext": endpoint.Ext,
}
if endpoint.Options != nil {
var restFields map[string]json.RawMessage
Expand Down Expand Up @@ -68,6 +71,25 @@ func (s *EndpointService) Save(tx *gorm.DB, act string, data json.RawMessage) er
return err
}

if endpoint.Type == "warp" {
if act == "new" {
err = s.WarpService.RegisterWarp(&endpoint)
if err != nil {
return err
}
} else {
var old_license string
err = tx.Model(model.Endpoint{}).Select("json_extract(ext, '$.license_key')").Where("id = ?", endpoint.Id).Find(&old_license).Error
if err != nil {
return err
}
err = s.WarpService.SetWarpLicense(old_license, &endpoint)
if err != nil {
return err
}
}
}

if corePtr.IsRunning() {
configData, err := endpoint.MarshalJSON()
if err != nil {
Expand Down
229 changes: 229 additions & 0 deletions backend/service/warp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package service

import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"s-ui/database/model"
"s-ui/logger"
"s-ui/util/common"
"strconv"
"time"

"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

type WarpService struct{}

func (s *WarpService) getWarpInfo(ep *model.Endpoint) ([]byte, error) {
var warpData map[string]string
err := json.Unmarshal(ep.Ext, &warpData)
if err != nil {
return nil, err
}

url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s", warpData["device_id"])

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+warpData["access_token"])

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
buffer := bytes.NewBuffer(make([]byte, 8192))
buffer.Reset()
_, err = buffer.ReadFrom(resp.Body)
if err != nil {
return nil, err
}

return buffer.Bytes(), nil
}

func (s *WarpService) RegisterWarp(ep *model.Endpoint) error {
tos := time.Now().UTC().Format("2006-01-02T15:04:05.000Z")
privateKey, _ := wgtypes.GenerateKey()
publicKey := privateKey.PublicKey().String()
hostName, _ := os.Hostname()

data := fmt.Sprintf(`{"key":"%s","tos":"%s","type": "PC","model": "s-ui", "name": "%s"}`, publicKey, tos, hostName)
url := "https://api.cloudflareclient.com/v0a2158/reg"

req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data)))
if err != nil {
return err
}

req.Header.Add("CF-Client-Version", "a-7.21-0721")
req.Header.Add("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
buffer := bytes.NewBuffer(make([]byte, 8192))
buffer.Reset()
_, err = buffer.ReadFrom(resp.Body)
if err != nil {
return err
}

var rspData map[string]interface{}
err = json.Unmarshal(buffer.Bytes(), &rspData)
if err != nil {
return err
}

deviceId := rspData["id"].(string)
token := rspData["token"].(string)
license, ok := rspData["account"].(map[string]interface{})["license"].(string)
if !ok {
logger.Debug("Error accessing license value.")
return err
}

warpData := map[string]string{
"access_token": token,
"device_id": deviceId,
"license_key": license,
}

ep.Ext, err = json.MarshalIndent(warpData, "", " ")
if err != nil {
return err
}

warpInfo, err := s.getWarpInfo(ep)
if err != nil {
return err
}

var warpDetails map[string]interface{}
err = json.Unmarshal(warpInfo, &warpDetails)
if err != nil {
return err
}

warpConfig, _ := warpDetails["config"].(map[string]interface{})
clientId, _ := warpConfig["client_id"].(string)
reserved := s.getReserved(clientId)
interfaceConfig, _ := warpConfig["interface"].(map[string]interface{})
addresses, _ := interfaceConfig["addresses"].(map[string]interface{})
v4, _ := addresses["v4"].(string)
v6, _ := addresses["v6"].(string)
peer, _ := warpConfig["peers"].([]interface{})[0].(map[string]interface{})
peerEndpoint, _ := peer["endpoint"].(map[string]interface{})["host"].(string)
peerEpAddress, peerEpPort, err := net.SplitHostPort(peerEndpoint)
if err != nil {
return err
}
peerPublicKey, _ := peer["public_key"].(string)
peerPort, _ := strconv.Atoi(peerEpPort)

peers := []map[string]interface{}{
{
"address": peerEpAddress,
"port": peerPort,
"public_key": peerPublicKey,
"allowed_ips": []string{"0.0.0.0/0", "::/0"},
"reserved": reserved,
},
}

var epOptions map[string]interface{}
err = json.Unmarshal(ep.Options, &epOptions)
if err != nil {
return err
}
epOptions["private_key"] = privateKey.String()
epOptions["address"] = []string{fmt.Sprintf("%s/32", v4), fmt.Sprintf("%s/128", v6)}
epOptions["listen_port"] = 0
epOptions["peers"] = peers

ep.Options, err = json.MarshalIndent(epOptions, "", " ")
return err
}

func (s *WarpService) getReserved(clientID string) []int {
var reserved []int
decoded, err := base64.StdEncoding.DecodeString(clientID)
if err != nil {
return nil
}

hexString := ""
for _, char := range decoded {
hex := fmt.Sprintf("%02x", char)
hexString += hex
}

for i := 0; i < len(hexString); i += 2 {
hexByte := hexString[i : i+2]
decValue, err := strconv.ParseInt(hexByte, 16, 32)
if err != nil {
return nil
}
reserved = append(reserved, int(decValue))
}

return reserved
}

func (s *WarpService) SetWarpLicense(old_license string, ep *model.Endpoint) error {
var warpData map[string]string
err := json.Unmarshal(ep.Ext, &warpData)
if err != nil {
return err
}

if warpData["license_key"] == old_license {
return nil
}

url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s/account", warpData["device_id"])
data := fmt.Sprintf(`{"license": "%s"}`, warpData["license_key"])

req, err := http.NewRequest("PUT", url, bytes.NewBuffer([]byte(data)))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+warpData["access_token"])

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
buffer := bytes.NewBuffer(make([]byte, 8192))
buffer.Reset()
_, err = buffer.ReadFrom(resp.Body)
if err != nil {
return err
}
var response map[string]interface{}
err = json.Unmarshal(buffer.Bytes(), &response)
if err != nil {
return err
}

if success, ok := response["success"].(bool); ok && success == false {
errorArr, _ := response["errors"].([]interface{})
errorObj := errorArr[0].(map[string]interface{})
return common.NewError(errorObj["code"], errorObj["message"])
}

return nil
}
Loading

0 comments on commit a0dc165

Please sign in to comment.