250 lines
8.2 KiB
Go
250 lines
8.2 KiB
Go
package api
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io/ioutil"
|
||
"net/http"
|
||
"net/url"
|
||
"strconv"
|
||
"time"
|
||
|
||
"github.com/fatedier/frp/pkg/msg"
|
||
)
|
||
|
||
// Service sakurafrp api servie
|
||
type Service struct {
|
||
Host url.URL
|
||
}
|
||
|
||
// NewService crate sakurafrp api servie
|
||
func NewService(host string) (s *Service, err error) {
|
||
u, err := url.Parse(host)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return &Service{*u}, nil
|
||
}
|
||
|
||
// CheckToken 校验客户端 token
|
||
func (s Service) CheckToken(user string, token string, timestamp int64, stk string) (ok bool, err error) {
|
||
values := url.Values{}
|
||
values.Set("action", "checktoken")
|
||
values.Set("user", user)
|
||
values.Set("token", token)
|
||
values.Set("timestamp", fmt.Sprintf("%d", timestamp))
|
||
values.Set("apitoken", stk)
|
||
s.Host.RawQuery = values.Encode()
|
||
defer func(u *url.URL) {
|
||
u.RawQuery = ""
|
||
}(&s.Host)
|
||
resp, err := http.Get(s.Host.String())
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
if resp.StatusCode != http.StatusOK {
|
||
return false, ErrHTTPStatus{
|
||
Status: resp.StatusCode,
|
||
Text: resp.Status,
|
||
}
|
||
}
|
||
defer resp.Body.Close()
|
||
body, err := ioutil.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
response := ResponseCheckToken{}
|
||
if err = json.Unmarshal(body, &response); err != nil {
|
||
return false, err
|
||
}
|
||
if !response.Success {
|
||
return false, ErrCheckTokenFail{response.Message}
|
||
}
|
||
return true, nil
|
||
}
|
||
|
||
// CheckProxy 校验客户端代理
|
||
func (s Service) CheckProxy(user string, pMsg *msg.NewProxy, timestamp int64, stk string) (ok bool, err error) {
|
||
|
||
domains, err := json.Marshal(pMsg.CustomDomains)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
headers, err := json.Marshal(pMsg.Headers)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
locations, err := json.Marshal(pMsg.Locations)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
values := url.Values{}
|
||
|
||
// API Basic
|
||
values.Set("action", "checkproxy")
|
||
values.Set("user", user)
|
||
values.Set("timestamp", fmt.Sprintf("%d", timestamp))
|
||
values.Set("apitoken", stk)
|
||
|
||
// Proxies basic info
|
||
values.Set("proxy_name", pMsg.ProxyName)
|
||
values.Set("proxy_type", pMsg.ProxyType)
|
||
values.Set("use_encryption", BoolToString(pMsg.UseEncryption))
|
||
values.Set("use_compression", BoolToString(pMsg.UseCompression))
|
||
|
||
// Http Proxies
|
||
values.Set("domain", string(domains))
|
||
values.Set("subdomain", pMsg.SubDomain)
|
||
|
||
// Headers
|
||
values.Set("locations", string(locations))
|
||
values.Set("http_user", pMsg.HTTPUser)
|
||
values.Set("http_pwd", pMsg.HTTPPwd)
|
||
values.Set("host_header_rewrite", pMsg.HostHeaderRewrite)
|
||
values.Set("headers", string(headers))
|
||
|
||
// Tcp & Udp & Stcp
|
||
values.Set("remote_port", strconv.Itoa(pMsg.RemotePort))
|
||
|
||
// Stcp & Xtcp
|
||
values.Set("sk", pMsg.Sk)
|
||
|
||
// Load balance
|
||
values.Set("group", pMsg.Group)
|
||
values.Set("group_key", pMsg.GroupKey)
|
||
|
||
s.Host.RawQuery = values.Encode()
|
||
defer func(u *url.URL) {
|
||
u.RawQuery = ""
|
||
}(&s.Host)
|
||
resp, err := http.Get(s.Host.String())
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
if resp.StatusCode != http.StatusOK {
|
||
return false, ErrHTTPStatus{
|
||
Status: resp.StatusCode,
|
||
Text: resp.Status,
|
||
}
|
||
}
|
||
defer resp.Body.Close()
|
||
body, err := ioutil.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
response := ResponseCheckProxy{}
|
||
if err = json.Unmarshal(body, &response); err != nil {
|
||
return false, err
|
||
}
|
||
if !response.Success {
|
||
return false, ErrCheckProxyFail{response.Message}
|
||
}
|
||
return true, nil
|
||
|
||
}
|
||
|
||
// GetProxyLimit 获取隧道限速信息
|
||
func (s Service) GetProxyLimit(user string, timestamp int64, stk string) (inLimit, outLimit uint64, err error) {
|
||
// 这部分就照之前的搬过去了,能跑就行x
|
||
values := url.Values{}
|
||
values.Set("action", "getlimit")
|
||
values.Set("user", user)
|
||
values.Set("timestamp", fmt.Sprintf("%d", timestamp))
|
||
values.Set("apitoken", stk)
|
||
s.Host.RawQuery = values.Encode()
|
||
defer func(u *url.URL) {
|
||
u.RawQuery = ""
|
||
}(&s.Host)
|
||
resp, err := http.Get(s.Host.String())
|
||
if err != nil {
|
||
return 0, 0, err
|
||
}
|
||
defer resp.Body.Close()
|
||
body, err := ioutil.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return 0, 0, err
|
||
}
|
||
|
||
er := &ErrHTTPStatus{}
|
||
if err = json.Unmarshal(body, er); err != nil {
|
||
return 0, 0, err
|
||
}
|
||
if er.Status != 200 {
|
||
return 0, 0, er
|
||
}
|
||
|
||
response := &ResponseGetLimit{}
|
||
if err = json.Unmarshal(body, response); err != nil {
|
||
return 0, 0, err
|
||
}
|
||
|
||
// 这里直接返回 uint64 应该问题不大
|
||
return response.MaxIn, response.MaxOut, nil
|
||
|
||
}
|
||
|
||
func BoolToString(val bool) (str string) {
|
||
if val {
|
||
return "true"
|
||
}
|
||
return "false"
|
||
|
||
}
|
||
|
||
type ErrHTTPStatus struct {
|
||
Status int `json:"status"`
|
||
Text string `json:"message"`
|
||
}
|
||
|
||
func (e ErrHTTPStatus) Error() string {
|
||
t := time.Now()
|
||
layout := "2006-01-02 15:04:05"
|
||
str := t.Format(layout)
|
||
switch e.Status {
|
||
case 403:
|
||
return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:465] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:470] [HayFrp] 根据状态码判定,问题为隧道被禁止或者无权启用隧道\n"+str+" [W] [hayfrp.go:475] [HayFrp] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:480] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:485] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:490] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text)
|
||
case 404:
|
||
return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:466] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:471] [HayFrp] 根据状态码判定,问题为没有找到隧道或者API出现故障\n"+str+" [W] [hayfrp.go:476] [HayFrp] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:481] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:486] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:491] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text)
|
||
case 520:
|
||
return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:467] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:472] [HayFrp] 根据状态码判定,问题为API网关出现故障\n"+str+" [W] [hayfrp.go] [HayFrp:477] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:482] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:487] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:492] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text)
|
||
case 503:
|
||
return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:468] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:473] [HayFrp] 根据状态码判定,问题为API请求了过大进行限流\n"+str+" [W] [hayfrp.go:478] [HayFrp] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:483] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:488] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:493] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text)
|
||
default:
|
||
return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:469] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:474] [HayFrp] 目前HayFrp无法状态码判定问题......\n"+str+" [W] [hayfrp.go:479] [HayFrp] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:484] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:489] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:494] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text)
|
||
}
|
||
}
|
||
|
||
type ResponseGetLimit struct {
|
||
MaxIn uint64 `json:"max-in"`
|
||
MaxOut uint64 `json:"max-out"`
|
||
}
|
||
|
||
type ResponseCheckToken struct {
|
||
Success bool `json:"success"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
type ResponseCheckProxy struct {
|
||
Success bool `json:"success"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
type ErrCheckTokenFail struct {
|
||
Message string
|
||
}
|
||
|
||
type ErrCheckProxyFail struct {
|
||
Message string
|
||
}
|
||
|
||
func (e ErrCheckTokenFail) Error() string {
|
||
return e.Message
|
||
}
|
||
|
||
func (e ErrCheckProxyFail) Error() string {
|
||
return e.Message
|
||
}
|