forked from Mxmilu666/frp
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
655dc3cb2a
|
|||
|
9894342f46
|
|||
|
e7cc706c86
|
|||
|
92ac2b9153
|
|||
|
1ed369e962
|
|||
|
b74a8d0232
|
|||
|
d2180081a0
|
|||
|
51f4e065b5
|
|||
|
e58f774086
|
|||
|
178e381a26
|
|||
|
26b93ae3a3
|
|||
|
a2aeee28e4
|
2
.github/workflows/build-all.yaml
vendored
2
.github/workflows/build-all.yaml
vendored
@@ -4,8 +4,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '**'
|
- '**'
|
||||||
tags:
|
|
||||||
- '**'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
|
|||||||
38
.github/workflows/release.yaml
vendored
38
.github/workflows/release.yaml
vendored
@@ -17,7 +17,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
# 调用 build-all workflow
|
# 调用 build-all workflow
|
||||||
build:
|
build:
|
||||||
uses: ./.github/workflows/build-all.yml
|
uses: ./.github/workflows/build-all.yaml
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -47,17 +47,49 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
|
|
||||||
|
- name: Display artifact structure
|
||||||
|
run: |
|
||||||
|
echo "Artifact structure:"
|
||||||
|
ls -R artifacts/
|
||||||
|
|
||||||
- name: Organize release files
|
- name: Organize release files
|
||||||
run: |
|
run: |
|
||||||
mkdir -p release_files
|
mkdir -p release_files
|
||||||
|
|
||||||
|
# 查找并复制所有压缩包
|
||||||
find artifacts -type f \( -name "*.zip" -o -name "*.tar.gz" \) -exec cp {} release_files/ \;
|
find artifacts -type f \( -name "*.zip" -o -name "*.tar.gz" \) -exec cp {} release_files/ \;
|
||||||
|
|
||||||
|
# 如果没有压缩包,尝试查找二进制文件并打包
|
||||||
|
if [ -z "$(ls -A release_files/)" ]; then
|
||||||
|
echo "No archives found, looking for directories to package..."
|
||||||
|
for dir in artifacts/*/; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
artifact_name=$(basename "$dir")
|
||||||
|
echo "Packaging $artifact_name"
|
||||||
|
|
||||||
|
# 检查是否是 Windows 构建
|
||||||
|
if echo "$artifact_name" | grep -q "windows"; then
|
||||||
|
(cd "$dir" && zip -r "../../release_files/${artifact_name}.zip" .)
|
||||||
|
else
|
||||||
|
tar -czf "release_files/${artifact_name}.tar.gz" -C "$dir" .
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Files in release_files:"
|
||||||
ls -lh release_files/
|
ls -lh release_files/
|
||||||
|
|
||||||
- name: Generate checksums
|
- name: Generate checksums
|
||||||
run: |
|
run: |
|
||||||
cd release_files
|
cd release_files
|
||||||
sha256sum * > sha256sum.txt
|
if [ -n "$(ls -A .)" ]; then
|
||||||
cat sha256sum.txt
|
sha256sum * > sha256sum.txt
|
||||||
|
cat sha256sum.txt
|
||||||
|
else
|
||||||
|
echo "No files to generate checksums for!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Save checksums for changelog
|
- name: Save checksums for changelog
|
||||||
id: checksums
|
id: checksums
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ func (pxy *UDPProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *UDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
|
func (pxy *UDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
|
||||||
xl := pxy.xl
|
xl := pxy.xl
|
||||||
xl.Infof("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String())
|
xl.Infof("收到一条新的 UDP 代理工作连接, %s", conn.RemoteAddr().String())
|
||||||
// close resources related with old workConn
|
// close resources related with old workConn
|
||||||
pxy.Close()
|
pxy.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ var (
|
|||||||
showVersion bool
|
showVersion bool
|
||||||
strictConfigMode bool
|
strictConfigMode bool
|
||||||
allowUnsafe []string
|
allowUnsafe []string
|
||||||
authToken string
|
authTokens []string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -56,7 +56,7 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory")
|
rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
|
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&strictConfigMode, "strict_config", "", true, "strict config parsing mode, unknown fields will cause an errors")
|
rootCmd.PersistentFlags().BoolVarP(&strictConfigMode, "strict_config", "", true, "strict config parsing mode, unknown fields will cause an errors")
|
||||||
rootCmd.PersistentFlags().StringVarP(&authToken, "token", "t", "", "authentication token of frpc (LoliaFRP only)")
|
rootCmd.PersistentFlags().StringSliceVarP(&authTokens, "token", "t", []string{}, "authentication tokens in format 'id:token' (LoliaFRP only)")
|
||||||
rootCmd.PersistentFlags().StringSliceVarP(&allowUnsafe, "allow-unsafe", "", []string{},
|
rootCmd.PersistentFlags().StringSliceVarP(&allowUnsafe, "allow-unsafe", "", []string{},
|
||||||
fmt.Sprintf("allowed unsafe features, one or more of: %s", strings.Join(security.ClientUnsafeFeatures, ", ")))
|
fmt.Sprintf("allowed unsafe features, one or more of: %s", strings.Join(security.ClientUnsafeFeatures, ", ")))
|
||||||
}
|
}
|
||||||
@@ -72,9 +72,9 @@ var rootCmd = &cobra.Command{
|
|||||||
|
|
||||||
unsafeFeatures := security.NewUnsafeFeatures(allowUnsafe)
|
unsafeFeatures := security.NewUnsafeFeatures(allowUnsafe)
|
||||||
|
|
||||||
// If authToken is provided, fetch config from API
|
// If authTokens is provided, fetch config from API
|
||||||
if authToken != "" {
|
if len(authTokens) > 0 {
|
||||||
err := runClientWithToken(authToken, unsafeFeatures)
|
err := runClientWithTokens(authTokens, unsafeFeatures)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -216,15 +216,58 @@ type APIResponse struct {
|
|||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func runClientWithToken(token string, unsafeFeatures *security.UnsafeFeatures) error {
|
// TokenInfo stores parsed id and token from the -t parameter
|
||||||
|
type TokenInfo struct {
|
||||||
|
ID string
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func runClientWithTokens(tokens []string, unsafeFeatures *security.UnsafeFeatures) error {
|
||||||
|
// Parse all tokens (format: id:token)
|
||||||
|
tokenInfos := make([]TokenInfo, 0, len(tokens))
|
||||||
|
for _, t := range tokens {
|
||||||
|
parts := strings.SplitN(t, ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return fmt.Errorf("invalid token format '%s', expected 'id:token'", t)
|
||||||
|
}
|
||||||
|
tokenInfos = append(tokenInfos, TokenInfo{
|
||||||
|
ID: strings.TrimSpace(parts[0]),
|
||||||
|
Token: strings.TrimSpace(parts[1]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group tokens by token value (same token can have multiple IDs)
|
||||||
|
tokenToIDs := make(map[string][]string)
|
||||||
|
for _, ti := range tokenInfos {
|
||||||
|
tokenToIDs[ti.Token] = append(tokenToIDs[ti.Token], ti.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all tokens use the same token value
|
||||||
|
if len(tokenToIDs) > 1 {
|
||||||
|
return fmt.Errorf("different tokens are not supported, all ids must use the same token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the single token and all its IDs
|
||||||
|
var token string
|
||||||
|
var ids []string
|
||||||
|
for t, idList := range tokenToIDs {
|
||||||
|
token = t
|
||||||
|
ids = idList
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return runClientWithTokenAndIDs(token, ids, unsafeFeatures)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runClientWithTokenAndIDs(token string, ids []string, unsafeFeatures *security.UnsafeFeatures) error {
|
||||||
// Get API server address from environment variable
|
// Get API server address from environment variable
|
||||||
apiServer := os.Getenv("LOLIA_API")
|
apiServer := os.Getenv("LOLIA_API")
|
||||||
if apiServer == "" {
|
if apiServer == "" {
|
||||||
apiServer = "https://api.lolia.link"
|
apiServer = "https://api.lolia.link"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch config from API
|
// Build URL with query parameters
|
||||||
url := fmt.Sprintf("%s/api/v1/tunnel/frpc/config/%s", apiServer, token)
|
url := fmt.Sprintf("%s/api/v1/tunnel/frpc/config?token=%s&id=%s", apiServer, token, strings.Join(ids, ","))
|
||||||
// #nosec G107 -- URL is constructed from trusted source (environment variable or hardcoded)
|
// #nosec G107 -- URL is constructed from trusted source (environment variable or hardcoded)
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
var version = "LoliaFRP-CLI 0.66.0"
|
var version = "LoliaFRP-CLI 0.66.2"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
|||||||
@@ -28,23 +28,70 @@ var NotFoundPagePath = ""
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
NotFound = `<!DOCTYPE html>
|
NotFound = `<!DOCTYPE html>
|
||||||
<html>
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<title>Not Found</title>
|
<meta charset="UTF-8">
|
||||||
<style>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
body {
|
<title>404 - 未绑定域名</title>
|
||||||
width: 35em;
|
<style>
|
||||||
margin: 0 auto;
|
body {
|
||||||
font-family: Tahoma, Verdana, Arial, sans-serif;
|
font-family: -apple-system, sans-serif;
|
||||||
}
|
display: flex;
|
||||||
</style>
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background: #fff;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
padding: 40px 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #666;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
text-align: left;
|
||||||
|
margin: 20px auto;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin: 8px 0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #0066cc;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:hover { text-decoration: underline; }
|
||||||
|
.footer {
|
||||||
|
margin-top: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>The page you requested was not found.</h1>
|
<div class="container">
|
||||||
<p>Sorry, the page you are looking for is currently unavailable.<br/>
|
<h1>域名未绑定</h1>
|
||||||
Please try again later.</p>
|
<p>这个域名还没有绑定到任何隧道哦 (;д;)</p>
|
||||||
<p>The server is powered by <a href="https://github.com/fatedier/frp">frp</a>.</p>
|
<p><strong>可能是这些原因:</strong></p>
|
||||||
<p><em>Faithfully yours, frp.</em></p>
|
<ul>
|
||||||
|
<li>域名配置不对,或者没有正确解析</li>
|
||||||
|
<li>隧道可能还没启动,或者已经停止</li>
|
||||||
|
<li>自定义域名忘记在服务端配置了</li>
|
||||||
|
</ul>
|
||||||
|
<div class="footer">由 <a href="https://lolia.link/">LoliaFRP</a> 与捐赠者们用爱发电</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`
|
`
|
||||||
@@ -69,7 +116,7 @@ func getNotFoundPageContent() []byte {
|
|||||||
|
|
||||||
func NotFoundResponse() *http.Response {
|
func NotFoundResponse() *http.Response {
|
||||||
header := make(http.Header)
|
header := make(http.Header)
|
||||||
header.Set("server", "frp/"+version.Full())
|
header.Set("server", version.Full())
|
||||||
header.Set("Content-Type", "text/html")
|
header.Set("Content-Type", "text/html")
|
||||||
|
|
||||||
content := getNotFoundPageContent()
|
content := getNotFoundPageContent()
|
||||||
|
|||||||
Reference in New Issue
Block a user