Compare commits
225 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6255573e2e | ||
|
|
26bcb3b590 | ||
|
|
fd2ae5d445 | ||
|
|
d51370d443 | ||
|
|
2d711408c6 | ||
|
|
e911039e33 | ||
|
|
e915deffb3 | ||
|
|
3b61871399 | ||
|
|
cb33d58685 | ||
|
|
1135bfafd5 | ||
|
|
ac9716c355 | ||
|
|
f501907264 | ||
|
|
39774542a5 | ||
|
|
783b81bc45 | ||
|
|
af57cb0fed | ||
|
|
6470fbe9f7 | ||
|
|
19f3be2895 | ||
|
|
8404dde243 | ||
|
|
aa52d695e6 | ||
|
|
4c83d2cbc3 | ||
|
|
014973a575 | ||
|
|
84c8cb3450 | ||
|
|
9aba52c276 | ||
|
|
bbfeb5ec16 | ||
|
|
25f2c68738 | ||
|
|
cbd2419f63 | ||
|
|
b61ffef9e8 | ||
|
|
18fcc43326 | ||
|
|
dd63154bf7 | ||
|
|
fd2fce8a8d | ||
|
|
48c90786e9 | ||
|
|
152c679565 | ||
|
|
1e428c6a71 | ||
|
|
2b26064c7d | ||
|
|
c25c713ac1 | ||
|
|
141fb7bc61 | ||
|
|
436ee75709 | ||
|
|
939b925dfa | ||
|
|
9fff1f4d03 | ||
|
|
68d2586120 | ||
|
|
6050df45e8 | ||
|
|
ad664f6d60 | ||
|
|
0bc2894700 | ||
|
|
b0d63c0d5b | ||
|
|
3fa1cf5a2c | ||
|
|
33a5c86beb | ||
|
|
37441b26f1 | ||
|
|
9f123b9a26 | ||
|
|
8b6b6319b5 | ||
|
|
5bce566988 | ||
|
|
ca0c845759 | ||
|
|
a60a7879a7 | ||
|
|
1b131b26ee | ||
|
|
16f4fa6c3f | ||
|
|
fa94426133 | ||
|
|
90484aaa24 | ||
|
|
20fae556d7 | ||
|
|
14e0610af8 | ||
|
|
8ae708a682 | ||
|
|
fc27d210bb | ||
|
|
49da63c146 | ||
|
|
e45361d9d3 | ||
|
|
f7941c2251 | ||
|
|
b72eeb29a9 | ||
|
|
b4dace1253 | ||
|
|
4edec8b20b | ||
|
|
797abbd4e6 | ||
|
|
bce8176866 | ||
|
|
005e25c1b8 | ||
|
|
711e36d0bf | ||
|
|
78b52e7b35 | ||
|
|
89638a5473 | ||
|
|
3755a1c970 | ||
|
|
21963ac738 | ||
|
|
b67ea18471 | ||
|
|
6e27530dec | ||
|
|
c9d5e38db2 | ||
|
|
996adc37ff | ||
|
|
4f6e0f9c65 | ||
|
|
5d5d129604 | ||
|
|
dcf02f4509 | ||
|
|
cbaa344467 | ||
|
|
7da5feada0 | ||
|
|
d901de0063 | ||
|
|
9b730e2a71 | ||
|
|
facd386cf7 | ||
|
|
f72902cd8d | ||
|
|
70e1ef0fdb | ||
|
|
74680a83a9 | ||
|
|
1d9b15ad90 | ||
|
|
9c9132ad72 | ||
|
|
69ec57f1bf | ||
|
|
d11adf3fe6 | ||
|
|
faa34bc216 | ||
|
|
76451c9d27 | ||
|
|
7e5802dcd1 | ||
|
|
2b6d1c70a8 | ||
|
|
faaef3b353 | ||
|
|
6ffa1d4a1d | ||
|
|
10f0e0dcca | ||
|
|
0afad2e192 | ||
|
|
6a1e7266b9 | ||
|
|
6ffc552bd8 | ||
|
|
8d8245971a | ||
|
|
a2f730d57e | ||
|
|
bf282c41fd | ||
|
|
3e3f7b2cc6 | ||
|
|
0580e063be | ||
|
|
2924aad25e | ||
|
|
39907299cd | ||
|
|
77705895d5 | ||
|
|
3fb85edf0b | ||
|
|
0e7a968cac | ||
|
|
195a9dc771 | ||
|
|
ca227a846a | ||
|
|
e1057fc76e | ||
|
|
b5168c3174 | ||
|
|
4ced00de50 | ||
|
|
750c929506 | ||
|
|
8b46a5a5af | ||
|
|
10d606a101 | ||
|
|
45d36dadcb | ||
|
|
61484d5cb8 | ||
|
|
b7775a99ef | ||
|
|
c45ba38ffa | ||
|
|
e97d9fb8a0 | ||
|
|
bb56ec9ec5 | ||
|
|
6d8f659558 | ||
|
|
7e9047c8fe | ||
|
|
b5af560b67 | ||
|
|
5622636870 | ||
|
|
372c276afc | ||
|
|
250b5fdd10 | ||
|
|
d1fb0bf8b7 | ||
|
|
490c273d36 | ||
|
|
0773dbaa28 | ||
|
|
4d04964740 | ||
|
|
537e6c9a5e | ||
|
|
082a6721f3 | ||
|
|
d515a20129 | ||
|
|
a66d3ef61a | ||
|
|
3b05b1a933 | ||
|
|
105479ce4b | ||
|
|
1a950e3aad | ||
|
|
79fa76e039 | ||
|
|
9db9a70a68 | ||
|
|
632862c742 | ||
|
|
76d0783f6f | ||
|
|
fc6a0e9813 | ||
|
|
038e289057 | ||
|
|
4b2a4af1e7 | ||
|
|
0fb2555bea | ||
|
|
bd8a809065 | ||
|
|
fc6fb7dce2 | ||
|
|
bacb94abe6 | ||
|
|
eb04421412 | ||
|
|
2fbd5a785e | ||
|
|
d8ae1525be | ||
|
|
daaa165d25 | ||
|
|
27ea02be5e | ||
|
|
8eac1ef307 | ||
|
|
1181974ea5 | ||
|
|
5f57fa0470 | ||
|
|
c0ad74ab7b | ||
|
|
c27905e1cc | ||
|
|
cadb65a85f | ||
|
|
b3c152685a | ||
|
|
16ce0726f8 | ||
|
|
5be306e087 | ||
|
|
0d21fc27ab | ||
|
|
7deaa678bc | ||
|
|
0d230a9237 | ||
|
|
8453d754f1 | ||
|
|
05f77f7a0d | ||
|
|
4334b6f148 | ||
|
|
72955759d7 | ||
|
|
18dc52996d | ||
|
|
0b16d09330 | ||
|
|
027d81a8cd | ||
|
|
d833158784 | ||
|
|
75024c9364 | ||
|
|
1dd5db3c42 | ||
|
|
faba80e315 | ||
|
|
1b7dc8eb96 | ||
|
|
7c0af45dd0 | ||
|
|
daea270132 | ||
|
|
a42e3ac4a6 | ||
|
|
c94c526b43 | ||
|
|
e531787773 | ||
|
|
0eda84a99a | ||
|
|
89b791448a | ||
|
|
2239328b2e | ||
|
|
e440a46080 | ||
|
|
4a0fec6e35 | ||
|
|
a9b46987bc | ||
|
|
6771afead5 | ||
|
|
553fca2981 | ||
|
|
8ef1e36b8b | ||
|
|
9e6c49b67c | ||
|
|
170632ddbc | ||
|
|
4a74488cac | ||
|
|
5fd6a08988 | ||
|
|
c86b91ead6 | ||
|
|
aeebb763c1 | ||
|
|
b05630e3e0 | ||
|
|
51905a0ee3 | ||
|
|
869ebd59d1 | ||
|
|
8a6aedca16 | ||
|
|
cf98c3453a | ||
|
|
1b6f234f39 | ||
|
|
26c7f0f566 | ||
|
|
617ad9b16e | ||
|
|
04a6792dbf | ||
|
|
f2bf8f2dbf | ||
|
|
3aba0abd24 | ||
|
|
b56ccc7cef | ||
|
|
aeebf55e60 | ||
|
|
9a9e0d25f3 | ||
|
|
6b3033e240 | ||
|
|
2859a59340 | ||
|
|
45367dd579 | ||
|
|
fee3fd743f | ||
|
|
ba4cfea47c | ||
|
|
5c11f07f8e | ||
|
|
ffac080611 |
@@ -53,7 +53,7 @@ function build() {
|
||||
|
||||
# generate files
|
||||
echo "generating files ..."
|
||||
go run -tags $TAG "$ROOT"/../cmd/edge-admin/main.go generate
|
||||
env CGO_ENABLED=0 go run -tags $TAG "$ROOT"/../cmd/edge-admin/main.go generate
|
||||
if [ "$(which uglifyjs)" ]; then
|
||||
echo "compress to component.js ..."
|
||||
uglifyjs --compress --mangle -- "${JS_ROOT}"/components.src.js > "${JS_ROOT}"/components.js
|
||||
@@ -99,9 +99,36 @@ function build() {
|
||||
rm -f "$(basename "$EDGE_API_ZIP_FILE")"
|
||||
cd - || exit
|
||||
|
||||
# find gcc
|
||||
GCC_DIR=""
|
||||
CC_PATH=""
|
||||
CXX_PATH=""
|
||||
if [ "${ARCH}" == "amd64" ]; then
|
||||
GCC_DIR="/usr/local/gcc/x86_64-unknown-linux-gnu/bin"
|
||||
CC_PATH="x86_64-unknown-linux-gnu-gcc"
|
||||
CXX_PATH="x86_64-unknown-linux-gnu-g++"
|
||||
fi
|
||||
if [ "${ARCH}" == "arm64" ]; then
|
||||
GCC_DIR="/usr/local/gcc/aarch64-unknown-linux-gnu/bin"
|
||||
CC_PATH="aarch64-unknown-linux-gnu-gcc"
|
||||
CXX_PATH="aarch64-unknown-linux-gnu-g++"
|
||||
fi
|
||||
|
||||
# build
|
||||
echo "building ${NAME} ..."
|
||||
env GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags $TAG -ldflags="-s -w" -o "$DIST"/bin/${NAME} "$ROOT"/../cmd/edge-admin/main.go
|
||||
if [ -f "${GCC_DIR}/${CC_PATH}" ]; then
|
||||
echo " building ${NAME} with gcc ..."
|
||||
env CC="${GCC_DIR}/${CC_PATH}" \
|
||||
CXX="${GCC_DIR}/${CXX_PATH}" \
|
||||
CGO_ENABLED=1 \
|
||||
GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags "${TAG} gcc" -ldflags="-linkmode external -extldflags -static -s -w" -o "$DIST"/bin/${NAME} "$ROOT"/../cmd/edge-admin/main.go
|
||||
else
|
||||
GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags $TAG -ldflags="-s -w" -o "$DIST"/bin/${NAME} "$ROOT"/../cmd/edge-admin/main.go
|
||||
fi
|
||||
if [ ! -f "${DIST}/bin/${NAME}" ]; then
|
||||
echo "build '${NAME}' failed!"
|
||||
exit
|
||||
fi
|
||||
|
||||
# delete hidden files
|
||||
find "$DIST" -name ".DS_Store" -delete
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
JS_ROOT=../web/public/js
|
||||
|
||||
echo "generating component.src.js ..."
|
||||
go run -tags=community ../cmd/edge-admin/main.go generate
|
||||
env CGO_ENABLED=0 go run -tags=community ../cmd/edge-admin/main.go generate
|
||||
|
||||
if [ "$(which uglifyjs)" ]; then
|
||||
echo "compress to component.js ..."
|
||||
|
||||
@@ -40,7 +40,9 @@ func main() {
|
||||
Option("demo", "switch to demo mode").
|
||||
Option("dev", "switch to 'dev' mode").
|
||||
Option("prod", "switch to 'prod' mode").
|
||||
Option("upgrade [--url=URL]", "upgrade from official site or an url")
|
||||
Option("upgrade [--url=URL]", "upgrade from official site or an url").
|
||||
Option("install-local-node", "install a local node").
|
||||
Option("security.reset", "reset security config")
|
||||
|
||||
app.On("daemon", func() {
|
||||
nodes.NewAdminNode().Daemon()
|
||||
@@ -76,7 +78,7 @@ func main() {
|
||||
fmt.Println("done")
|
||||
})
|
||||
app.On("recover", func() {
|
||||
sock := gosock.NewTmpSock(teaconst.ProcessName)
|
||||
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||
if !sock.IsListening() {
|
||||
fmt.Println("[ERROR]the service not started yet, you should start the service first")
|
||||
return
|
||||
@@ -89,7 +91,7 @@ func main() {
|
||||
fmt.Println("enter recovery mode successfully")
|
||||
})
|
||||
app.On("demo", func() {
|
||||
sock := gosock.NewTmpSock(teaconst.ProcessName)
|
||||
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||
if !sock.IsListening() {
|
||||
fmt.Println("[ERROR]the service not started yet, you should start the service first")
|
||||
return
|
||||
@@ -178,6 +180,17 @@ func main() {
|
||||
log.Println("restarting ...")
|
||||
app.RunRestart()
|
||||
})
|
||||
app.On("security.reset", func() {
|
||||
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||
if !sock.IsListening() {
|
||||
fmt.Println("[ERROR]the service not started yet, you should start the service first")
|
||||
return
|
||||
}
|
||||
_, _ = sock.Send(&gosock.Command{
|
||||
Code: "security.reset",
|
||||
})
|
||||
fmt.Println("ok")
|
||||
})
|
||||
app.Run(func() {
|
||||
var adminNode = nodes.NewAdminNode()
|
||||
adminNode.Run()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM --platform=linux/amd64 alpine:latest
|
||||
LABEL maintainer="goedge.cdn@gmail.com"
|
||||
ENV TZ "Asia/Shanghai"
|
||||
ENV VERSION 1.2.10
|
||||
ENV VERSION 1.3.7
|
||||
ENV ROOT_DIR /usr/local/goedge
|
||||
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
|
||||
|
||||
|
||||
43
go.mod
43
go.mod
@@ -1,24 +1,24 @@
|
||||
module github.com/TeaOSLab/EdgeAdmin
|
||||
|
||||
go 1.18
|
||||
go 1.21
|
||||
|
||||
replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
||||
|
||||
require (
|
||||
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/cespare/xxhash/v2 v2.2.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f
|
||||
github.com/iwind/TeaGo v0.0.0-20240429060313-31a7bc8e9cc9
|
||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/quic-go/quic-go v0.36.0
|
||||
github.com/quic-go/quic-go v0.42.0
|
||||
github.com/shirou/gopsutil/v3 v3.22.5
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/tealeg/xlsx/v3 v3.2.3
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
|
||||
golang.org/x/crypto v0.10.0
|
||||
golang.org/x/sys v0.9.0
|
||||
google.golang.org/grpc v1.45.0
|
||||
github.com/xlzd/gotp v0.1.0
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/sys v0.19.0
|
||||
google.golang.org/grpc v1.63.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -26,30 +26,25 @@ require (
|
||||
github.com/frankban/quicktest v1.11.3 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f // indirect
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.17.1 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/rogpeppe/fastuuid v1.2.0 // indirect
|
||||
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect
|
||||
github.com/tdewolff/minify/v2 v2.12.7 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.6 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/mod v0.11.0 // indirect
|
||||
golang.org/x/net v0.11.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/tools v0.10.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.19.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
|
||||
284
go.sum
284
go.sum
@@ -1,98 +1,39 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
||||
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f h1:xo6XmXLtveKcwcZAXV6VMxkWNzy/2dStfHEnyowsGAE=
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f h1:f00RU+zOX+B3rLAmMMkzHUF2h1z4DeYR9tTCvEq2REY=
|
||||
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/iwind/TeaGo v0.0.0-20240312020455-6f20b5121caf h1:WA9qgiynESu/DDTnLH6npRI5AK6UL9qwJ2YZ5qhJX5E=
|
||||
github.com/iwind/TeaGo v0.0.0-20240312020455-6f20b5121caf/go.mod h1:SfqVbWyIPdVflyA6lMgicZzsoGS8pyeLiTRe8/CIpGI=
|
||||
github.com/iwind/TeaGo v0.0.0-20240429060313-31a7bc8e9cc9 h1:lqSd+OeAMzPlejoVtsmHJEeQ6/MWXCr+Ws2eX9/Rewg=
|
||||
github.com/iwind/TeaGo v0.0.0-20240429060313-31a7bc8e9cc9/go.mod h1:SfqVbWyIPdVflyA6lMgicZzsoGS8pyeLiTRe8/CIpGI=
|
||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
|
||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@@ -101,24 +42,12 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
|
||||
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
|
||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
|
||||
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
|
||||
@@ -127,15 +56,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.36.0 h1:JIrO7p7Ug6hssFcARjWDiqS2RAKJHCiwPxBAA989rbI=
|
||||
github.com/quic-go/quic-go v0.36.0/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ=
|
||||
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
|
||||
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
||||
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk=
|
||||
@@ -144,170 +68,66 @@ github.com/shirou/gopsutil/v3 v3.22.5 h1:atX36I/IXgFiB81687vSiBI5zrMsxcIBkP9cQMJ
|
||||
github.com/shirou/gopsutil/v3 v3.22.5/go.mod h1:so9G9VzeHt/hsd0YwqprnjHnfARAUktauykSbr+y2gA=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tdewolff/minify/v2 v2.12.7 h1:pBzz2tAfz5VghOXiQIsSta6srhmTeinQPjRDHWoumCA=
|
||||
github.com/tdewolff/minify/v2 v2.12.7/go.mod h1:ZRKTheiOGyLSK8hOZWWv+YoJAECzDivNgAlVYDHp/Ws=
|
||||
github.com/tdewolff/parse/v2 v2.6.6 h1:Yld+0CrKUJaCV78DL1G2nk3C9lKrxyRTux5aaK/AkDo=
|
||||
github.com/tdewolff/parse/v2 v2.6.6/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
||||
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
|
||||
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tealeg/xlsx/v3 v3.2.3 h1:MXnVh+9Y8cUglowItTy2HL3Kv6z+q/0aNjeKuTsVqZQ=
|
||||
github.com/tealeg/xlsx/v3 v3.2.3/go.mod h1:0hGmAEoZ48SS1ZAE6eqZJkJVXgOMY+8a33vjXa8S8HA=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4=
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
|
||||
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
|
||||
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg=
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -174,6 +174,17 @@ func FindAdminLang(adminId int64) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// UpdateAdminLang 修改某个管理员选择的语言
|
||||
func UpdateAdminLang(adminId int64, langCode string) {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
|
||||
list, ok := sharedAdminModuleMapping[adminId]
|
||||
if ok {
|
||||
list.Lang = langCode
|
||||
}
|
||||
}
|
||||
|
||||
func FindAdminLangForAction(actionPtr actions.ActionWrapper) (langCode langs.LangCode) {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
|
||||
@@ -93,6 +93,7 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
|
||||
}
|
||||
|
||||
var config = &systemconfigs.AdminUIConfig{}
|
||||
config.DNSResolver.Type = nodeconfigs.DNSResolverTypeDefault // 默认值
|
||||
err = json.Unmarshal(resp.ValueJSON, config)
|
||||
if err != nil {
|
||||
logs.Println("[UI_MANAGER]" + err.Error())
|
||||
@@ -108,7 +109,7 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
|
||||
}
|
||||
|
||||
func defaultAdminUIConfig() *systemconfigs.AdminUIConfig {
|
||||
return &systemconfigs.AdminUIConfig{
|
||||
var config = &systemconfigs.AdminUIConfig{
|
||||
ProductName: langs.DefaultMessage(codes.AdminUI_DefaultProductName),
|
||||
AdminSystemName: langs.DefaultMessage(codes.AdminUI_DefaultSystemName),
|
||||
ShowOpenSourceInfo: true,
|
||||
@@ -117,6 +118,8 @@ func defaultAdminUIConfig() *systemconfigs.AdminUIConfig {
|
||||
DefaultPageSize: 10,
|
||||
TimeZone: nodeconfigs.DefaultTimeZoneLocation,
|
||||
}
|
||||
config.DNSResolver.Type = nodeconfigs.DNSResolverTypeDefault
|
||||
return config
|
||||
}
|
||||
|
||||
// 修改时区
|
||||
|
||||
@@ -79,7 +79,7 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp.ValueJSON) == 0 {
|
||||
sharedSecurityConfig = defaultSecurityConfig()
|
||||
sharedSecurityConfig = NewSecurityConfig()
|
||||
return sharedSecurityConfig, nil
|
||||
}
|
||||
|
||||
@@ -88,11 +88,13 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
||||
AllowLocal: true,
|
||||
CheckClientFingerprint: false,
|
||||
CheckClientRegion: true,
|
||||
DenySearchEngines: true,
|
||||
DenySpiders: true,
|
||||
}
|
||||
err = json.Unmarshal(resp.ValueJSON, config)
|
||||
if err != nil {
|
||||
logs.Println("[SECURITY_MANAGER]" + err.Error())
|
||||
sharedSecurityConfig = defaultSecurityConfig()
|
||||
sharedSecurityConfig = NewSecurityConfig()
|
||||
return sharedSecurityConfig, nil
|
||||
}
|
||||
err = config.Init()
|
||||
@@ -103,11 +105,14 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
||||
return sharedSecurityConfig, nil
|
||||
}
|
||||
|
||||
func defaultSecurityConfig() *systemconfigs.SecurityConfig {
|
||||
// NewSecurityConfig create new security config
|
||||
func NewSecurityConfig() *systemconfigs.SecurityConfig {
|
||||
return &systemconfigs.SecurityConfig{
|
||||
Frame: FrameSameOrigin,
|
||||
AllowLocal: true,
|
||||
CheckClientFingerprint: false,
|
||||
CheckClientRegion: true,
|
||||
DenySearchEngines: true,
|
||||
DenySpiders: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "1.2.10"
|
||||
Version = "1.3.8"
|
||||
|
||||
APINodeVersion = "1.2.10"
|
||||
APINodeVersion = "1.3.8"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
@@ -14,7 +14,7 @@ const (
|
||||
EncryptKey = "8f983f4d69b83aaa0d74b21a212f6967"
|
||||
EncryptMethod = "aes-256-cfb"
|
||||
|
||||
CookieSID = "edgesid"
|
||||
CookieSID = "geadsid"
|
||||
SessionAdminId = "adminId"
|
||||
|
||||
SystemdServiceName = "edge-admin"
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -63,7 +64,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_COND_COMPONENTS = ")
|
||||
buffer.Write(typesJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 条件操作符
|
||||
@@ -73,7 +74,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_COND_OPERATORS = ")
|
||||
buffer.Write(requestOperatorsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 请求变量
|
||||
@@ -83,7 +84,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.REQUEST_VARIABLES = ")
|
||||
buffer.Write(requestVariablesJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// 指标
|
||||
@@ -93,7 +94,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.METRIC_HTTP_KEYS = ")
|
||||
buffer.Write(metricHTTPKeysJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// IP地址阈值项目
|
||||
@@ -103,7 +104,7 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.IP_ADDR_THRESHOLD_ITEMS = ")
|
||||
buffer.Write(ipAddrThresholdItemsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// IP地址阈值动作
|
||||
@@ -113,7 +114,25 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.IP_ADDR_THRESHOLD_ACTIONS = ")
|
||||
buffer.Write(ipAddrThresholdActionsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// WAF checkpoints
|
||||
var wafCheckpointsMaps = []maps.Map{}
|
||||
for _, checkpoint := range firewallconfigs.AllCheckpoints {
|
||||
wafCheckpointsMaps = append(wafCheckpointsMaps, maps.Map{
|
||||
"name": checkpoint.Name,
|
||||
"prefix": checkpoint.Prefix,
|
||||
"description": checkpoint.Description,
|
||||
})
|
||||
}
|
||||
wafCheckpointsJSON, err := json.Marshal(wafCheckpointsMaps)
|
||||
if err != nil {
|
||||
logs.Println("ComponentsAction marshal waf rule checkpoints failed: " + err.Error())
|
||||
} else {
|
||||
buffer.WriteString("window.WAF_RULE_CHECKPOINTS = ")
|
||||
buffer.Write(wafCheckpointsJSON)
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// WAF操作符
|
||||
@@ -123,7 +142,17 @@ func generateComponentsJSFile() error {
|
||||
} else {
|
||||
buffer.WriteString("window.WAF_RULE_OPERATORS = ")
|
||||
buffer.Write(wafOperatorsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
// WAF验证码类型
|
||||
captchaTypesJSON, err := json.Marshal(firewallconfigs.FindAllCaptchaTypes())
|
||||
if err != nil {
|
||||
logs.Println("ComponentsAction marshal captcha types failed: " + err.Error())
|
||||
} else {
|
||||
buffer.WriteString("window.WAF_CAPTCHA_TYPES = ")
|
||||
buffer.Write(captchaTypesJSON)
|
||||
buffer.Write([]byte{';', '\n', '\n'})
|
||||
}
|
||||
|
||||
fp, err := os.OpenFile(filepath.Clean(Tea.PublicFile("/js/components.src.js")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
|
||||
|
||||
@@ -3,10 +3,12 @@ package nodes
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/events"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/iwind/TeaGo"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
@@ -86,6 +88,9 @@ func (this *AdminNode) Run() {
|
||||
// 启动API节点
|
||||
this.startAPINode()
|
||||
|
||||
// 设置DNS相关
|
||||
this.setupDNS()
|
||||
|
||||
// 启动IP库
|
||||
this.startIPLibrary()
|
||||
|
||||
@@ -253,6 +258,33 @@ func (this *AdminNode) addPortsToFirewall() {
|
||||
utils.AddPortsToFirewall(ports)
|
||||
}
|
||||
|
||||
// 设置DNS相关
|
||||
func (this *AdminNode) setupDNS() {
|
||||
config, loadErr := configloaders.LoadAdminUIConfig()
|
||||
if loadErr != nil {
|
||||
// 默认使用go原生
|
||||
err := os.Setenv("GODEBUG", "netdns=go")
|
||||
if err != nil {
|
||||
logs.Println("[DNS_RESOLVER]set env failed: " + err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
switch config.DNSResolver.Type {
|
||||
case nodeconfigs.DNSResolverTypeGoNative:
|
||||
err = os.Setenv("GODEBUG", "netdns=go")
|
||||
case nodeconfigs.DNSResolverTypeCGO:
|
||||
err = os.Setenv("GODEBUG", "netdns=cgo")
|
||||
default:
|
||||
// 默认使用go原生
|
||||
err = os.Setenv("GODEBUG", "netdns=go")
|
||||
}
|
||||
if err != nil {
|
||||
logs.Println("[DNS_RESOLVER]set env failed: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// 启动API节点
|
||||
func (this *AdminNode) startAPINode() {
|
||||
var configPath = Tea.Root + "/edge-api/configs/api.yaml"
|
||||
@@ -406,6 +438,10 @@ func (this *AdminNode) listenSock() error {
|
||||
case "prod": // 切换到prod
|
||||
Tea.Env = Tea.EnvProd
|
||||
_ = cmd.ReplyOk()
|
||||
case "security.reset":
|
||||
var newConfig = configloaders.NewSecurityConfig()
|
||||
_ = configloaders.UpdateSecurityConfig(newConfig)
|
||||
_ = cmd.ReplyOk()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ func (this *SessionManager) Read(sid string) map[string]string {
|
||||
}
|
||||
|
||||
func (this *SessionManager) WriteItem(sid string, key string, value string) bool {
|
||||
// 删除缓存
|
||||
defer ttlcache.DefaultCache.Delete( "SESSION@" + sid)
|
||||
|
||||
// 忽略OTP
|
||||
if strings.HasSuffix(sid, "_otp") {
|
||||
return false
|
||||
@@ -95,6 +98,9 @@ func (this *SessionManager) WriteItem(sid string, key string, value string) bool
|
||||
}
|
||||
|
||||
func (this *SessionManager) Delete(sid string) bool {
|
||||
// 删除缓存
|
||||
defer ttlcache.DefaultCache.Delete( "SESSION@" + sid)
|
||||
|
||||
// 忽略OTP
|
||||
if strings.HasSuffix(sid, "_otp") {
|
||||
return false
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"net"
|
||||
"net/url"
|
||||
@@ -514,12 +515,15 @@ func (this *RPCClient) init() error {
|
||||
grpc.MaxCallSendMsgSize(128<<20),
|
||||
grpc.UseCompressor(gzip.Name),
|
||||
)
|
||||
var keepaliveParams = grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: 30 * time.Second,
|
||||
})
|
||||
if u.Scheme == "http" {
|
||||
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions)
|
||||
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions, keepaliveParams)
|
||||
} else if u.Scheme == "https" {
|
||||
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})), callOptions)
|
||||
})), callOptions, keepaliveParams)
|
||||
} else {
|
||||
return errors.New("parse endpoint failed: invalid scheme '" + u.Scheme + "'")
|
||||
}
|
||||
|
||||
@@ -34,14 +34,9 @@ func TestRPCClient_NodeRPC(t *testing.T) {
|
||||
|
||||
func TestRPC_Dial_HTTP(t *testing.T) {
|
||||
client, err := NewRPCClient(&configs.APIConfig{
|
||||
RPC: struct {
|
||||
Endpoints []string `yaml:"endpoints"`
|
||||
DisableUpdate bool `yaml:"disableUpdate"`
|
||||
}{
|
||||
Endpoints: []string{"http://127.0.0.1:8004"},
|
||||
},
|
||||
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
||||
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
||||
RPCEndpoints: []string{"https://127.0.0.1:8003"},
|
||||
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
||||
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -56,14 +51,9 @@ func TestRPC_Dial_HTTP(t *testing.T) {
|
||||
|
||||
func TestRPC_Dial_HTTP_2(t *testing.T) {
|
||||
client, err := NewRPCClient(&configs.APIConfig{
|
||||
RPC: struct {
|
||||
Endpoints []string `yaml:"endpoints"`
|
||||
DisableUpdate bool `yaml:"disableUpdate"`
|
||||
}{
|
||||
Endpoints: []string{"https://127.0.0.1:8003"},
|
||||
},
|
||||
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
||||
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
||||
RPCEndpoints: []string{"https://127.0.0.1:8003"},
|
||||
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
||||
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -78,14 +68,9 @@ func TestRPC_Dial_HTTP_2(t *testing.T) {
|
||||
|
||||
func TestRPC_Dial_HTTPS(t *testing.T) {
|
||||
client, err := NewRPCClient(&configs.APIConfig{
|
||||
RPC: struct {
|
||||
Endpoints []string `yaml:"endpoints"`
|
||||
DisableUpdate bool `yaml:"disableUpdate"`
|
||||
}{
|
||||
Endpoints: []string{"https://127.0.0.1:8004"},
|
||||
},
|
||||
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
||||
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
||||
RPCEndpoints: []string{"https://127.0.0.1:8004"},
|
||||
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
|
||||
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
|
||||
}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package ttlcache
|
||||
|
||||
import "github.com/cespare/xxhash"
|
||||
import "github.com/cespare/xxhash/v2"
|
||||
|
||||
func HashKey(key []byte) uint64 {
|
||||
return xxhash.Sum64(key)
|
||||
|
||||
162
internal/utils/exec/cmd.go
Normal file
162
internal/utils/exec/cmd.go
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package executils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cmd struct {
|
||||
name string
|
||||
args []string
|
||||
env []string
|
||||
dir string
|
||||
|
||||
ctx context.Context
|
||||
timeout time.Duration
|
||||
cancelFunc func()
|
||||
|
||||
captureStdout bool
|
||||
captureStderr bool
|
||||
|
||||
stdout *bytes.Buffer
|
||||
stderr *bytes.Buffer
|
||||
|
||||
rawCmd *exec.Cmd
|
||||
}
|
||||
|
||||
func NewCmd(name string, args ...string) *Cmd {
|
||||
return &Cmd{
|
||||
name: name,
|
||||
args: args,
|
||||
}
|
||||
}
|
||||
|
||||
func NewTimeoutCmd(timeout time.Duration, name string, args ...string) *Cmd {
|
||||
return (&Cmd{
|
||||
name: name,
|
||||
args: args,
|
||||
}).WithTimeout(timeout)
|
||||
}
|
||||
|
||||
func (this *Cmd) WithTimeout(timeout time.Duration) *Cmd {
|
||||
this.timeout = timeout
|
||||
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
|
||||
this.ctx = ctx
|
||||
this.cancelFunc = cancelFunc
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *Cmd) WithStdout() *Cmd {
|
||||
this.captureStdout = true
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *Cmd) WithStderr() *Cmd {
|
||||
this.captureStderr = true
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *Cmd) WithEnv(env []string) *Cmd {
|
||||
this.env = env
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *Cmd) WithDir(dir string) *Cmd {
|
||||
this.dir = dir
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *Cmd) Start() error {
|
||||
var cmd = this.compose()
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
func (this *Cmd) Wait() error {
|
||||
var cmd = this.compose()
|
||||
return cmd.Wait()
|
||||
}
|
||||
|
||||
func (this *Cmd) Run() error {
|
||||
if this.cancelFunc != nil {
|
||||
defer this.cancelFunc()
|
||||
}
|
||||
|
||||
var cmd = this.compose()
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (this *Cmd) RawStdout() string {
|
||||
if this.stdout != nil {
|
||||
return this.stdout.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (this *Cmd) Stdout() string {
|
||||
return strings.TrimSpace(this.RawStdout())
|
||||
}
|
||||
|
||||
func (this *Cmd) RawStderr() string {
|
||||
if this.stderr != nil {
|
||||
return this.stderr.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (this *Cmd) Stderr() string {
|
||||
return strings.TrimSpace(this.RawStderr())
|
||||
}
|
||||
|
||||
func (this *Cmd) String() string {
|
||||
if this.rawCmd != nil {
|
||||
return this.rawCmd.String()
|
||||
}
|
||||
var newCmd = exec.Command(this.name, this.args...)
|
||||
return newCmd.String()
|
||||
}
|
||||
|
||||
func (this *Cmd) Process() *os.Process {
|
||||
if this.rawCmd != nil {
|
||||
return this.rawCmd.Process
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Cmd) compose() *exec.Cmd {
|
||||
if this.rawCmd != nil {
|
||||
return this.rawCmd
|
||||
}
|
||||
|
||||
if this.ctx != nil {
|
||||
this.rawCmd = exec.CommandContext(this.ctx, this.name, this.args...)
|
||||
} else {
|
||||
this.rawCmd = exec.Command(this.name, this.args...)
|
||||
}
|
||||
|
||||
if this.env != nil {
|
||||
this.rawCmd.Env = this.env
|
||||
}
|
||||
|
||||
if len(this.dir) > 0 {
|
||||
this.rawCmd.Dir = this.dir
|
||||
}
|
||||
|
||||
if this.captureStdout {
|
||||
this.stdout = &bytes.Buffer{}
|
||||
this.rawCmd.Stdout = this.stdout
|
||||
}
|
||||
if this.captureStderr {
|
||||
this.stderr = &bytes.Buffer{}
|
||||
this.rawCmd.Stderr = this.stderr
|
||||
}
|
||||
|
||||
return this.rawCmd
|
||||
}
|
||||
61
internal/utils/exec/cmd_test.go
Normal file
61
internal/utils/exec/cmd_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package executils_test
|
||||
|
||||
import (
|
||||
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewTimeoutCmd_Sleep(t *testing.T) {
|
||||
var cmd = executils.NewTimeoutCmd(1*time.Second, "sleep", "3")
|
||||
cmd.WithStdout()
|
||||
cmd.WithStderr()
|
||||
err := cmd.Run()
|
||||
t.Log("error:", err)
|
||||
t.Log("stdout:", cmd.Stdout())
|
||||
t.Log("stderr:", cmd.Stderr())
|
||||
}
|
||||
|
||||
func TestNewTimeoutCmd_Echo(t *testing.T) {
|
||||
var cmd = executils.NewTimeoutCmd(10*time.Second, "echo", "-n", "hello")
|
||||
cmd.WithStdout()
|
||||
cmd.WithStderr()
|
||||
err := cmd.Run()
|
||||
t.Log("error:", err)
|
||||
t.Log("stdout:", cmd.Stdout())
|
||||
t.Log("stderr:", cmd.Stderr())
|
||||
}
|
||||
|
||||
func TestNewTimeoutCmd_Echo2(t *testing.T) {
|
||||
var cmd = executils.NewCmd("echo", "hello")
|
||||
cmd.WithStdout()
|
||||
cmd.WithStderr()
|
||||
err := cmd.Run()
|
||||
t.Log("error:", err)
|
||||
t.Log("stdout:", cmd.Stdout())
|
||||
t.Log("raw stdout:", cmd.RawStdout())
|
||||
t.Log("stderr:", cmd.Stderr())
|
||||
t.Log("raw stderr:", cmd.RawStderr())
|
||||
}
|
||||
|
||||
func TestNewTimeoutCmd_Echo3(t *testing.T) {
|
||||
var cmd = executils.NewCmd("echo", "-n", "hello")
|
||||
err := cmd.Run()
|
||||
t.Log("error:", err)
|
||||
t.Log("stdout:", cmd.Stdout())
|
||||
t.Log("stderr:", cmd.Stderr())
|
||||
}
|
||||
|
||||
func TestCmd_Process(t *testing.T) {
|
||||
var cmd = executils.NewCmd("echo", "-n", "hello")
|
||||
err := cmd.Run()
|
||||
t.Log("error:", err)
|
||||
t.Log(cmd.Process())
|
||||
}
|
||||
|
||||
func TestNewTimeoutCmd_String(t *testing.T) {
|
||||
var cmd = executils.NewCmd("echo", "-n", "hello")
|
||||
t.Log("stdout:", cmd.String())
|
||||
}
|
||||
58
internal/utils/exec/look_linux.go
Normal file
58
internal/utils/exec/look_linux.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build linux
|
||||
|
||||
package executils
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// LookPath customize our LookPath() function, to work in broken $PATH environment variable
|
||||
func LookPath(file string) (string, error) {
|
||||
result, err := exec.LookPath(file)
|
||||
if err == nil && len(result) > 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// add common dirs contains executable files these may be excluded in $PATH environment variable
|
||||
var binPaths = []string{
|
||||
"/usr/sbin",
|
||||
"/usr/bin",
|
||||
"/usr/local/sbin",
|
||||
"/usr/local/bin",
|
||||
}
|
||||
|
||||
for _, binPath := range binPaths {
|
||||
var fullPath = binPath + string(os.PathSeparator) + file
|
||||
|
||||
stat, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return "", syscall.EISDIR
|
||||
}
|
||||
|
||||
var mode = stat.Mode()
|
||||
if mode.IsDir() {
|
||||
return "", syscall.EISDIR
|
||||
}
|
||||
err = syscall.Faccessat(unix.AT_FDCWD, fullPath, unix.X_OK, unix.AT_EACCESS)
|
||||
if err == nil || (err != syscall.ENOSYS && err != syscall.EPERM) {
|
||||
return fullPath, err
|
||||
}
|
||||
if mode&0111 != 0 {
|
||||
return fullPath, nil
|
||||
}
|
||||
return "", fs.ErrPermission
|
||||
}
|
||||
|
||||
return "", &exec.Error{
|
||||
Name: file,
|
||||
Err: exec.ErrNotFound,
|
||||
}
|
||||
}
|
||||
10
internal/utils/exec/look_others.go
Normal file
10
internal/utils/exec/look_others.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !linux
|
||||
|
||||
package executils
|
||||
|
||||
import "os/exec"
|
||||
|
||||
func LookPath(file string) (string, error) {
|
||||
return exec.LookPath(file)
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"os/exec"
|
||||
@@ -14,7 +15,7 @@ func AddPortsToFirewall(ports []int) {
|
||||
// Linux
|
||||
if runtime.GOOS == "linux" {
|
||||
// firewalld
|
||||
firewallCmd, _ := exec.LookPath("firewall-cmd")
|
||||
firewallCmd, _ := executils.LookPath("firewall-cmd")
|
||||
if len(firewallCmd) > 0 {
|
||||
err := exec.Command(firewallCmd, "--add-port="+types.String(port)+"/tcp").Run()
|
||||
if err == nil {
|
||||
|
||||
@@ -2,47 +2,65 @@ package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"math/big"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IP2Long 将IP转换为整型
|
||||
func IP2Long(ip string) uint64 {
|
||||
s := net.ParseIP(ip)
|
||||
if len(s) != 16 {
|
||||
return 0
|
||||
// ParseIPValue 解析IP值
|
||||
func ParseIPValue(value string) (newValue string, ipFrom string, ipTo string, ok bool) {
|
||||
if len(value) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(ip, ":") { // IPv6
|
||||
bigInt := big.NewInt(0)
|
||||
bigInt.SetBytes(s.To16())
|
||||
return bigInt.Uint64()
|
||||
}
|
||||
return uint64(binary.BigEndian.Uint32(s.To4()))
|
||||
}
|
||||
newValue = value
|
||||
|
||||
// IsIPv4 判断是否为IPv4
|
||||
func IsIPv4(ip string) bool {
|
||||
if !regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(ip) {
|
||||
return false
|
||||
}
|
||||
if IP2Long(ip) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// ip1-ip2
|
||||
if strings.Contains(value, "-") {
|
||||
var pieces = strings.Split(value, "-")
|
||||
if len(pieces) != 2 {
|
||||
return
|
||||
}
|
||||
|
||||
// IsIPv6 判断是否为IPv6
|
||||
func IsIPv6(ip string) bool {
|
||||
if !strings.Contains(ip, ":") {
|
||||
return false
|
||||
ipFrom = strings.TrimSpace(pieces[0])
|
||||
ipTo = strings.TrimSpace(pieces[1])
|
||||
|
||||
if !iputils.IsValid(ipFrom) || !iputils.IsValid(ipTo) {
|
||||
return
|
||||
}
|
||||
|
||||
if !iputils.IsSameVersion(ipFrom, ipTo) {
|
||||
return
|
||||
}
|
||||
|
||||
if iputils.CompareIP(ipFrom, ipTo) > 0 {
|
||||
ipFrom, ipTo = ipTo, ipFrom
|
||||
newValue = ipFrom + "-" + ipTo
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
return len(net.ParseIP(ip)) == net.IPv6len
|
||||
|
||||
// ip/mask
|
||||
if strings.Contains(value, "/") {
|
||||
cidr, err := iputils.ParseCIDR(value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return newValue, cidr.From().String(), cidr.To().String(), true
|
||||
}
|
||||
|
||||
// single value
|
||||
if iputils.IsValid(value) {
|
||||
ipFrom = value
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ExtractIP 分解IP
|
||||
|
||||
@@ -5,86 +5,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIP2Long(t *testing.T) {
|
||||
for _, ip := range []string{
|
||||
"0.0.0.1",
|
||||
"0.0.0.2",
|
||||
"127.0.0.1",
|
||||
"192.0.0.2",
|
||||
"255.255.255.255",
|
||||
"2001:db8:0:1::101",
|
||||
"2001:db8:0:1::102",
|
||||
"2406:8c00:0:3409:133:18:203:0",
|
||||
"2406:8c00:0:3409:133:18:203:158",
|
||||
"2406:8c00:0:3409:133:18:203:159",
|
||||
"2406:8c00:0:3409:133:18:203:160",
|
||||
} {
|
||||
t.Log(ip, " -> ", IP2Long(ip))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv4(t *testing.T) {
|
||||
type testIP struct {
|
||||
ip string
|
||||
ok bool
|
||||
}
|
||||
for _, item := range []testIP{
|
||||
{
|
||||
ip: "1",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
ip: "192.168.0.1",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
ip: "1.1.0.1",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
ip: "255.255.255.255",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
ip: "192.168.0.1233",
|
||||
ok: false,
|
||||
},
|
||||
} {
|
||||
if IsIPv4(item.ip) != item.ok {
|
||||
t.Fatal(item.ip, "should be", item.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv6(t *testing.T) {
|
||||
type testIP struct {
|
||||
ip string
|
||||
ok bool
|
||||
}
|
||||
for _, item := range []testIP{
|
||||
{
|
||||
ip: "1",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
ip: "2406:8c00:0:3409:133:18:203:158",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
ip: "2406::8c00:0:3409:133:18:203:158",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
ip: "2001:db8:0:1::101",
|
||||
ok: true,
|
||||
},
|
||||
} {
|
||||
if IsIPv6(item.ip) != item.ok {
|
||||
t.Fatal(item.ip, "should be", item.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractIP(t *testing.T) {
|
||||
t.Log(ExtractIP("192.168.1.100"))
|
||||
}
|
||||
@@ -107,4 +27,4 @@ func TestNextIP_Copy(t *testing.T) {
|
||||
var ip = net.ParseIP("192.168.1.100")
|
||||
var nextIP = NextIP(ip)
|
||||
t.Log(ip, nextIP)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package utils
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/miekg/dns"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var sharedDNSClient *dns.Client
|
||||
@@ -33,17 +35,47 @@ func LookupCNAME(host string) (string, error) {
|
||||
m.RecursionDesired = true
|
||||
|
||||
var lastErr error
|
||||
for _, serverAddr := range sharedDNSConfig.Servers {
|
||||
r, _, err := sharedDNSClient.Exchange(m, configutils.QuoteIP(serverAddr)+":"+sharedDNSConfig.Port)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
if len(r.Answer) == 0 {
|
||||
continue
|
||||
}
|
||||
var success = false
|
||||
var result = ""
|
||||
|
||||
return r.Answer[0].(*dns.CNAME).Target, nil
|
||||
var serverAddrs = sharedDNSConfig.Servers
|
||||
|
||||
{
|
||||
var publicDNSHosts = []string{"8.8.8.8" /** Google **/, "8.8.4.4" /** Google **/}
|
||||
for _, publicDNSHost := range publicDNSHosts {
|
||||
if !lists.ContainsString(serverAddrs, publicDNSHost) {
|
||||
serverAddrs = append(serverAddrs, publicDNSHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var wg = &sync.WaitGroup{}
|
||||
|
||||
for _, serverAddr := range serverAddrs {
|
||||
wg.Add(1)
|
||||
|
||||
go func(serverAddr string) {
|
||||
defer wg.Done()
|
||||
r, _, err := sharedDNSClient.Exchange(m, configutils.QuoteIP(serverAddr)+":"+sharedDNSConfig.Port)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
return
|
||||
}
|
||||
|
||||
success = true
|
||||
|
||||
if len(r.Answer) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
result = r.Answer[0].(*dns.CNAME).Target
|
||||
}(serverAddr)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if success {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
return "", lastErr
|
||||
}
|
||||
|
||||
@@ -8,5 +8,8 @@ import (
|
||||
)
|
||||
|
||||
func TestLookupCNAME(t *testing.T) {
|
||||
t.Log(utils.LookupCNAME("www.yun4s.cn"))
|
||||
for _, domain := range []string{"www.yun4s.cn", "example.com"} {
|
||||
result, err := utils.LookupCNAME(domain)
|
||||
t.Log(domain, "=>", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,17 +30,17 @@ func FormatBytes(bytes int64) string {
|
||||
if bytes < Pow1024(1) {
|
||||
return FormatInt64(bytes) + "B"
|
||||
} else if bytes < Pow1024(2) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1))))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fKiB", float64(bytes)/float64(Pow1024(1))))
|
||||
} else if bytes < Pow1024(3) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2))))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fMiB", float64(bytes)/float64(Pow1024(2))))
|
||||
} else if bytes < Pow1024(4) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3))))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fGiB", float64(bytes)/float64(Pow1024(3))))
|
||||
} else if bytes < Pow1024(5) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4))))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fTiB", float64(bytes)/float64(Pow1024(4))))
|
||||
} else if bytes < Pow1024(6) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5))))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fPiB", float64(bytes)/float64(Pow1024(5))))
|
||||
} else {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6))))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fEiB", float64(bytes)/float64(Pow1024(6))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
internal/utils/otputils/utils.go
Normal file
31
internal/utils/otputils/utils.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package otputils
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// FixIssuer fix issuer in otp provisioning url
|
||||
func FixIssuer(urlString string) string {
|
||||
u, err := url.Parse(urlString)
|
||||
if err != nil {
|
||||
return urlString
|
||||
}
|
||||
|
||||
var query = u.Query()
|
||||
|
||||
if query != nil {
|
||||
var issuerName = query.Get("issuer")
|
||||
if len(issuerName) > 0 {
|
||||
unescapedIssuerName, unescapeErr := url.QueryUnescape(issuerName)
|
||||
if unescapeErr == nil {
|
||||
query.Set("issuer", unescapedIssuerName)
|
||||
u.RawQuery = query.Encode()
|
||||
}
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
|
||||
return urlString
|
||||
}
|
||||
18
internal/utils/otputils/utils_test.go
Normal file
18
internal/utils/otputils/utils_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package otputils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/otputils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFixIssuer(t *testing.T) {
|
||||
var beforeURL = "otpauth://totp/GoEdge%25E7%25AE%25A1%25E7%2590%2586%25E5%2591%2598%25E7%25B3%25BB%25E7%25BB%259F:admin?issuer=GoEdge%25E7%25AE%25A1%25E7%2590%2586%25E5%2591%2598%25E7%25B3%25BB%25E7%25BB%259F&secret=Q3J4WNOWBRFLP3HI"
|
||||
var afterURL = otputils.FixIssuer(beforeURL)
|
||||
t.Log(afterURL)
|
||||
|
||||
if beforeURL == afterURL {
|
||||
t.Fatal("'afterURL' should not be equal to 'beforeURL'")
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package utils
|
||||
import (
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
"os"
|
||||
@@ -21,7 +22,7 @@ func (this *ServiceManager) Install(exePath string, args []string) error {
|
||||
return errors.New("only root users can install the service")
|
||||
}
|
||||
|
||||
systemd, err := exec.LookPath("systemctl")
|
||||
systemd, err := executils.LookPath("systemctl")
|
||||
if err != nil {
|
||||
return this.installInitService(exePath, args)
|
||||
}
|
||||
@@ -36,7 +37,7 @@ func (this *ServiceManager) Start() error {
|
||||
}
|
||||
|
||||
if files.NewFile(systemdServiceFile).Exists() {
|
||||
systemd, err := exec.LookPath("systemctl")
|
||||
systemd, err := executils.LookPath("systemctl")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -53,7 +54,7 @@ func (this *ServiceManager) Uninstall() error {
|
||||
}
|
||||
|
||||
if files.NewFile(systemdServiceFile).Exists() {
|
||||
systemd, err := exec.LookPath("systemctl")
|
||||
systemd, err := executils.LookPath("systemctl")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,7 +94,7 @@ func (this *ServiceManager) installInitService(exePath string, args []string) er
|
||||
return err
|
||||
}
|
||||
|
||||
chkCmd, err := exec.LookPath("chkconfig")
|
||||
chkCmd, err := executils.LookPath("chkconfig")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,6 +112,12 @@ func (this *ServiceManager) installSystemdService(systemd, exePath string, args
|
||||
shortName := teaconst.SystemdServiceName
|
||||
longName := "GoEdge Admin" // TODO 将来可以修改
|
||||
|
||||
var startCmd = exePath + " daemon"
|
||||
bashPath, _ := executils.LookPath("bash")
|
||||
if len(bashPath) > 0 {
|
||||
startCmd = bashPath + " -c \"" + startCmd + "\""
|
||||
}
|
||||
|
||||
desc := `### BEGIN INIT INFO
|
||||
# Provides: ` + shortName + `
|
||||
# Required-Start: $all
|
||||
@@ -129,7 +136,7 @@ After=network-online.target
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
ExecStart=` + exePath + ` daemon
|
||||
ExecStart=` + startCmd + `
|
||||
ExecStop=` + exePath + ` stop
|
||||
ExecReload=` + exePath + ` reload
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
@@ -87,10 +88,10 @@ func (this *UpgradeManager) Start() error {
|
||||
}()
|
||||
|
||||
// 检查unzip
|
||||
unzipExe, _ := exec.LookPath("unzip")
|
||||
unzipExe, _ := executils.LookPath("unzip")
|
||||
|
||||
// 检查cp
|
||||
cpExe, _ := exec.LookPath("cp")
|
||||
cpExe, _ := executils.LookPath("cp")
|
||||
if len(cpExe) == 0 {
|
||||
return errors.New("can not find 'cp' command")
|
||||
}
|
||||
|
||||
32
internal/waf/injectionutils/libinjection/COPYING
Normal file
32
internal/waf/injectionutils/libinjection/COPYING
Normal file
@@ -0,0 +1,32 @@
|
||||
Copyright (c) 2012-2016, Nick Galbreath
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
https://github.com/client9/libinjection
|
||||
http://opensource.org/licenses/BSD-3-Clause
|
||||
1
internal/waf/injectionutils/libinjection/README.md
Normal file
1
internal/waf/injectionutils/libinjection/README.md
Normal file
@@ -0,0 +1 @@
|
||||
copy from https://github.com/libinjection/libinjection
|
||||
65
internal/waf/injectionutils/libinjection/src/libinjection.h
Normal file
65
internal/waf/injectionutils/libinjection/src/libinjection.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright 2012-2016 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* https://libinjection.client9.com/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LIBINJECTION_H
|
||||
#define LIBINJECTION_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define LIBINJECTION_BEGIN_DECLS extern "C" {
|
||||
# define LIBINJECTION_END_DECLS }
|
||||
#else
|
||||
# define LIBINJECTION_BEGIN_DECLS
|
||||
# define LIBINJECTION_END_DECLS
|
||||
#endif
|
||||
|
||||
LIBINJECTION_BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* Pull in size_t
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Version info.
|
||||
*
|
||||
* This is moved into a function to allow SWIG and other auto-generated
|
||||
* binding to not be modified during minor release changes. We change
|
||||
* change the version number in the c source file, and not regenerated
|
||||
* the binding
|
||||
*
|
||||
* See python's normalized version
|
||||
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
|
||||
*/
|
||||
const char* libinjection_version(void);
|
||||
|
||||
/**
|
||||
* Simple API for SQLi detection - returns a SQLi fingerprint or NULL
|
||||
* is benign input
|
||||
*
|
||||
* \param[in] s input string, may contain nulls, does not need to be null-terminated
|
||||
* \param[in] slen input string length
|
||||
* \param[out] fingerprint buffer of 8+ characters. c-string,
|
||||
* \return 1 if SQLi, 0 if benign. fingerprint will be set or set to empty string.
|
||||
*/
|
||||
int libinjection_sqli(const char* s, size_t slen, char fingerprint[]);
|
||||
|
||||
/** ALPHA version of xss detector.
|
||||
*
|
||||
* NOT DONE.
|
||||
*
|
||||
* \param[in] s input string, may contain nulls, does not need to be null-terminated
|
||||
* \param[in] slen input string length
|
||||
* \return 1 if XSS found, 0 if benign
|
||||
*
|
||||
*/
|
||||
int libinjection_xss(const char* s, size_t slen, int strictMode);
|
||||
|
||||
LIBINJECTION_END_DECLS
|
||||
|
||||
#endif /* LIBINJECTION_H */
|
||||
@@ -0,0 +1,868 @@
|
||||
#include "libinjection_html5.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#define TRACE() printf("%s:%d\n", __FUNCTION__, __LINE__)
|
||||
#else
|
||||
#define TRACE()
|
||||
#endif
|
||||
|
||||
|
||||
#define CHAR_EOF -1
|
||||
#define CHAR_NULL 0
|
||||
#define CHAR_BANG 33
|
||||
#define CHAR_DOUBLE 34
|
||||
#define CHAR_PERCENT 37
|
||||
#define CHAR_SINGLE 39
|
||||
#define CHAR_DASH 45
|
||||
#define CHAR_SLASH 47
|
||||
#define CHAR_LT 60
|
||||
#define CHAR_EQUALS 61
|
||||
#define CHAR_GT 62
|
||||
#define CHAR_QUESTION 63
|
||||
#define CHAR_RIGHTB 93
|
||||
#define CHAR_TICK 96
|
||||
|
||||
/* prototypes */
|
||||
|
||||
static int h5_skip_white(h5_state_t* hs);
|
||||
static int h5_is_white(char ch);
|
||||
static int h5_state_eof(h5_state_t* hs);
|
||||
static int h5_state_data(h5_state_t* hs);
|
||||
static int h5_state_tag_open(h5_state_t* hs);
|
||||
static int h5_state_tag_name(h5_state_t* hs);
|
||||
static int h5_state_tag_name_close(h5_state_t* hs);
|
||||
static int h5_state_end_tag_open(h5_state_t* hs);
|
||||
static int h5_state_self_closing_start_tag(h5_state_t* hs);
|
||||
static int h5_state_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_after_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_before_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_before_attribute_value(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_double_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_single_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_back_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_no_quote(h5_state_t* hs);
|
||||
static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs);
|
||||
static int h5_state_comment(h5_state_t* hs);
|
||||
static int h5_state_cdata(h5_state_t* hs);
|
||||
|
||||
|
||||
/* 12.2.4.44 */
|
||||
static int h5_state_bogus_comment(h5_state_t* hs);
|
||||
static int h5_state_bogus_comment2(h5_state_t* hs);
|
||||
|
||||
/* 12.2.4.45 */
|
||||
static int h5_state_markup_declaration_open(h5_state_t* hs);
|
||||
|
||||
/* 8.2.4.52 */
|
||||
static int h5_state_doctype(h5_state_t* hs);
|
||||
|
||||
/**
|
||||
* public function
|
||||
*/
|
||||
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags flags)
|
||||
{
|
||||
memset(hs, 0, sizeof(h5_state_t));
|
||||
hs->s = s;
|
||||
hs->len = len;
|
||||
|
||||
switch (flags) {
|
||||
case DATA_STATE:
|
||||
hs->state = h5_state_data;
|
||||
break;
|
||||
case VALUE_NO_QUOTE:
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
break;
|
||||
case VALUE_SINGLE_QUOTE:
|
||||
hs->state = h5_state_attribute_value_single_quote;
|
||||
break;
|
||||
case VALUE_DOUBLE_QUOTE:
|
||||
hs->state = h5_state_attribute_value_double_quote;
|
||||
break;
|
||||
case VALUE_BACK_QUOTE:
|
||||
hs->state = h5_state_attribute_value_back_quote;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* public function
|
||||
*/
|
||||
int libinjection_h5_next(h5_state_t* hs)
|
||||
{
|
||||
assert(hs->state != NULL);
|
||||
return (*hs->state)(hs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything below here is private
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
static int h5_is_white(char ch)
|
||||
{
|
||||
/*
|
||||
* \t = horizontal tab = 0x09
|
||||
* \n = newline = 0x0A
|
||||
* \v = vertical tab = 0x0B
|
||||
* \f = form feed = 0x0C
|
||||
* \r = cr = 0x0D
|
||||
*/
|
||||
return strchr(" \t\n\v\f\r", ch) != NULL;
|
||||
}
|
||||
|
||||
static int h5_skip_white(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
while (hs->pos < hs->len) {
|
||||
ch = hs->s[hs->pos];
|
||||
switch (ch) {
|
||||
case 0x00: /* IE only */
|
||||
case 0x20:
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0B: /* IE only */
|
||||
case 0x0C:
|
||||
case 0x0D: /* IE only */
|
||||
hs->pos += 1;
|
||||
break;
|
||||
default:
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
return CHAR_EOF;
|
||||
}
|
||||
|
||||
static int h5_state_eof(h5_state_t* hs)
|
||||
{
|
||||
/* eliminate unused function argument warning */
|
||||
(void)hs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_state_data(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
assert(hs->len >= hs->pos);
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_LT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->state = h5_state_eof;
|
||||
if (hs->token_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
hs->state = h5_state_tag_open;
|
||||
if (hs->token_len == 0) {
|
||||
return h5_state_tag_open(hs);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12 2.4.8
|
||||
*/
|
||||
static int h5_state_tag_open(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_BANG) {
|
||||
hs->pos += 1;
|
||||
return h5_state_markup_declaration_open(hs);
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->pos += 1;
|
||||
hs->is_close = 1;
|
||||
return h5_state_end_tag_open(hs);
|
||||
} else if (ch == CHAR_QUESTION) {
|
||||
hs->pos += 1;
|
||||
return h5_state_bogus_comment(hs);
|
||||
} else if (ch == CHAR_PERCENT) {
|
||||
/* this is not in spec.. alternative comment format used
|
||||
by IE <= 9 and Safari < 4.0.3 */
|
||||
hs->pos += 1;
|
||||
return h5_state_bogus_comment2(hs);
|
||||
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return h5_state_tag_name(hs);
|
||||
} else if (ch == CHAR_NULL) {
|
||||
/* IE-ism NULL characters are ignored */
|
||||
return h5_state_tag_name(hs);
|
||||
} else {
|
||||
/* user input mistake in configuring state */
|
||||
if (hs->pos == 0) {
|
||||
return h5_state_data(hs);
|
||||
}
|
||||
hs->token_start = hs->s + hs->pos - 1;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->state = h5_state_data;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 12.2.4.9
|
||||
*/
|
||||
static int h5_state_end_tag_open(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_GT) {
|
||||
return h5_state_data(hs);
|
||||
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return h5_state_tag_name(hs);
|
||||
}
|
||||
|
||||
hs->is_close = 0;
|
||||
return h5_state_bogus_comment(hs);
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int h5_state_tag_name_close(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
hs->is_close = 0;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
if (hs->pos < hs->len) {
|
||||
hs->state = h5_state_data;
|
||||
} else {
|
||||
hs->state = h5_state_eof;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.10
|
||||
*/
|
||||
static int h5_state_tag_name(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (ch == 0) {
|
||||
/* special non-standard case */
|
||||
/* allow nulls in tag name */
|
||||
/* some old browsers apparently allow and ignore them */
|
||||
pos += 1;
|
||||
} else if (h5_is_white(ch)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
return 1;
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_self_closing_start_tag;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
if (hs->is_close) {
|
||||
hs->pos = pos + 1;
|
||||
hs->is_close = 0;
|
||||
hs->token_type = TAG_CLOSE;
|
||||
hs->state = h5_state_data;
|
||||
} else {
|
||||
hs->pos = pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->state = h5_state_eof;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.34
|
||||
*/
|
||||
static int h5_state_before_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
int ch;
|
||||
|
||||
TRACE();
|
||||
|
||||
/* for manual tail call optimization, see comment below */
|
||||
tail_call:;
|
||||
|
||||
ch = h5_skip_white(hs);
|
||||
switch (ch) {
|
||||
case CHAR_EOF: {
|
||||
return 0;
|
||||
}
|
||||
case CHAR_SLASH: {
|
||||
hs->pos += 1;
|
||||
/* Logically, We want to call h5_state_self_closing_start_tag(hs) here.
|
||||
|
||||
As this function may call us back and the compiler
|
||||
might not implement automatic tail call optimization,
|
||||
this might result in a deep recursion.
|
||||
|
||||
We detect this case here and start over with the current state.
|
||||
*/
|
||||
|
||||
if (hs->pos < hs->len && hs->s[hs->pos] != CHAR_GT) {
|
||||
goto tail_call;
|
||||
}
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
}
|
||||
case CHAR_GT: {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
return h5_state_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int h5_state_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos + 1;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_after_attribute_name;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_self_closing_start_tag;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_EQUALS) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_before_attribute_value;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
hs->pos = pos;
|
||||
return 1;
|
||||
} else {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
/* EOF */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_eof;
|
||||
hs->pos = hs->len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.36
|
||||
*/
|
||||
static int h5_state_after_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
int c;
|
||||
|
||||
TRACE();
|
||||
c = h5_skip_white(hs);
|
||||
switch (c) {
|
||||
case CHAR_EOF: {
|
||||
return 0;
|
||||
}
|
||||
case CHAR_SLASH: {
|
||||
hs->pos += 1;
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
}
|
||||
case CHAR_EQUALS: {
|
||||
hs->pos += 1;
|
||||
return h5_state_before_attribute_value(hs);
|
||||
}
|
||||
case CHAR_GT: {
|
||||
return h5_state_tag_name_close(hs);
|
||||
}
|
||||
default: {
|
||||
return h5_state_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.37
|
||||
*/
|
||||
static int h5_state_before_attribute_value(h5_state_t* hs)
|
||||
{
|
||||
int c;
|
||||
TRACE();
|
||||
|
||||
c = h5_skip_white(hs);
|
||||
|
||||
if (c == CHAR_EOF) {
|
||||
hs->state = h5_state_eof;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c == CHAR_DOUBLE) {
|
||||
return h5_state_attribute_value_double_quote(hs);
|
||||
} else if (c == CHAR_SINGLE) {
|
||||
return h5_state_attribute_value_single_quote(hs);
|
||||
} else if (c == CHAR_TICK) {
|
||||
/* NON STANDARD IE */
|
||||
return h5_state_attribute_value_back_quote(hs);
|
||||
} else {
|
||||
return h5_state_attribute_value_no_quote(hs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int h5_state_attribute_value_quote(h5_state_t* hs, char qchar)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
|
||||
/* skip initial quote in normal case.
|
||||
* don't do this "if (pos == 0)" since it means we have started
|
||||
* in a non-data state. given an input of '><foo
|
||||
* we want to make 0-length attribute name
|
||||
*/
|
||||
if (hs->pos > 0) {
|
||||
hs->pos += 1;
|
||||
}
|
||||
|
||||
|
||||
idx = (const char*) memchr(hs->s + hs->pos, qchar, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->state = h5_state_eof;
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->state = h5_state_after_attribute_value_quoted_state;
|
||||
hs->pos += hs->token_len + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_double_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_DOUBLE);
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_single_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_SINGLE);
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_back_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_TICK);
|
||||
}
|
||||
|
||||
static int h5_state_attribute_value_no_quote(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->pos = pos;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
return 1;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
TRACE();
|
||||
/* EOF */
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.41
|
||||
*/
|
||||
static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->pos += 1;
|
||||
return h5_state_before_attribute_name(hs);
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->pos += 1;
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
hs->state = h5_state_data;
|
||||
return 1;
|
||||
} else {
|
||||
return h5_state_before_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.43
|
||||
*
|
||||
* WARNING: This function is partially inlined into h5_state_before_attribute_name()
|
||||
*/
|
||||
static int h5_state_self_closing_start_tag(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_GT) {
|
||||
assert(hs->pos > 0);
|
||||
hs->token_start = hs->s + hs->pos -1;
|
||||
hs->token_len = 2;
|
||||
hs->token_type = TAG_NAME_SELFCLOSE;
|
||||
hs->state = h5_state_data;
|
||||
hs->pos += 1;
|
||||
return 1;
|
||||
} else {
|
||||
return h5_state_before_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.44
|
||||
*/
|
||||
static int h5_state_bogus_comment(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->pos = hs->len;
|
||||
hs->state = h5_state_eof;
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
hs->state = h5_state_data;
|
||||
}
|
||||
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.44 ALT
|
||||
*/
|
||||
static int h5_state_bogus_comment2(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_PERCENT, hs->len - pos);
|
||||
if (idx == NULL || (idx + 1 >= hs->s + hs->len)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->pos = hs->len;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
hs->state = h5_state_eof;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*(idx +1) != CHAR_GT) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ends in %> */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 2;
|
||||
hs->state = h5_state_data;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 8.2.4.45
|
||||
*/
|
||||
static int h5_state_markup_declaration_open(h5_state_t* hs)
|
||||
{
|
||||
size_t remaining;
|
||||
|
||||
TRACE();
|
||||
remaining = hs->len - hs->pos;
|
||||
if (remaining >= 7 &&
|
||||
/* case insensitive */
|
||||
(hs->s[hs->pos + 0] == 'D' || hs->s[hs->pos + 0] == 'd') &&
|
||||
(hs->s[hs->pos + 1] == 'O' || hs->s[hs->pos + 1] == 'o') &&
|
||||
(hs->s[hs->pos + 2] == 'C' || hs->s[hs->pos + 2] == 'c') &&
|
||||
(hs->s[hs->pos + 3] == 'T' || hs->s[hs->pos + 3] == 't') &&
|
||||
(hs->s[hs->pos + 4] == 'Y' || hs->s[hs->pos + 4] == 'y') &&
|
||||
(hs->s[hs->pos + 5] == 'P' || hs->s[hs->pos + 5] == 'p') &&
|
||||
(hs->s[hs->pos + 6] == 'E' || hs->s[hs->pos + 6] == 'e')
|
||||
) {
|
||||
return h5_state_doctype(hs);
|
||||
} else if (remaining >= 7 &&
|
||||
/* upper case required */
|
||||
hs->s[hs->pos + 0] == '[' &&
|
||||
hs->s[hs->pos + 1] == 'C' &&
|
||||
hs->s[hs->pos + 2] == 'D' &&
|
||||
hs->s[hs->pos + 3] == 'A' &&
|
||||
hs->s[hs->pos + 4] == 'T' &&
|
||||
hs->s[hs->pos + 5] == 'A' &&
|
||||
hs->s[hs->pos + 6] == '['
|
||||
) {
|
||||
hs->pos += 7;
|
||||
return h5_state_cdata(hs);
|
||||
} else if (remaining >= 2 &&
|
||||
hs->s[hs->pos + 0] == '-' &&
|
||||
hs->s[hs->pos + 1] == '-') {
|
||||
hs->pos += 2;
|
||||
return h5_state_comment(hs);
|
||||
}
|
||||
|
||||
return h5_state_bogus_comment(hs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.48
|
||||
* 12.2.4.49
|
||||
* 12.2.4.50
|
||||
* 12.2.4.51
|
||||
* state machine spec is confusing since it can only look
|
||||
* at one character at a time but simply it's comments end by:
|
||||
* 1) EOF
|
||||
* 2) ending in -->
|
||||
* 3) ending in -!>
|
||||
*/
|
||||
static int h5_state_comment(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
size_t offset;
|
||||
const char* end = hs->s + hs->len;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_DASH, hs->len - pos);
|
||||
|
||||
/* did not find anything or has less than 3 chars left */
|
||||
if (idx == NULL || idx > hs->s + hs->len - 3) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
offset = 1;
|
||||
|
||||
/* skip all nulls */
|
||||
while (idx + offset < end && *(idx + offset) == 0) {
|
||||
offset += 1;
|
||||
}
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch = *(idx + offset);
|
||||
if (ch != CHAR_DASH && ch != CHAR_BANG) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* need to test */
|
||||
#if 0
|
||||
/* skip all nulls */
|
||||
while (idx + offset < end && *(idx + offset) == 0) {
|
||||
offset += 1;
|
||||
}
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
offset += 1;
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ch = *(idx + offset);
|
||||
if (ch != CHAR_GT) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
/* ends in --> or -!> */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx + offset - hs->s);
|
||||
hs->state = h5_state_data;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int h5_state_cdata(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_RIGHTB, hs->len - pos);
|
||||
|
||||
/* did not find anything or has less than 3 chars left */
|
||||
if (idx == NULL || idx > hs->s + hs->len - 3) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
return 1;
|
||||
} else if ( *(idx+1) == CHAR_RIGHTB && *(idx+2) == CHAR_GT) {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 3;
|
||||
hs->token_type = DATA_TEXT;
|
||||
return 1;
|
||||
} else {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 8.2.4.52
|
||||
* http://www.w3.org/html/wg/drafts/html/master/syntax.html#doctype-state
|
||||
*/
|
||||
static int h5_state_doctype(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_type = DOCTYPE;
|
||||
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
} else {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef LIBINJECTION_HTML5
|
||||
#define LIBINJECTION_HTML5
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* pull in size_t */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
enum html5_type {
|
||||
DATA_TEXT
|
||||
, TAG_NAME_OPEN
|
||||
, TAG_NAME_CLOSE
|
||||
, TAG_NAME_SELFCLOSE
|
||||
, TAG_DATA
|
||||
, TAG_CLOSE
|
||||
, ATTR_NAME
|
||||
, ATTR_VALUE
|
||||
, TAG_COMMENT
|
||||
, DOCTYPE
|
||||
};
|
||||
|
||||
enum html5_flags {
|
||||
DATA_STATE
|
||||
, VALUE_NO_QUOTE
|
||||
, VALUE_SINGLE_QUOTE
|
||||
, VALUE_DOUBLE_QUOTE
|
||||
, VALUE_BACK_QUOTE
|
||||
};
|
||||
|
||||
struct h5_state;
|
||||
typedef int (*ptr_html5_state)(struct h5_state*);
|
||||
|
||||
typedef struct h5_state {
|
||||
const char* s;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
int is_close;
|
||||
ptr_html5_state state;
|
||||
const char* token_start;
|
||||
size_t token_len;
|
||||
enum html5_type token_type;
|
||||
} h5_state_t;
|
||||
|
||||
|
||||
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags);
|
||||
int libinjection_h5_next(h5_state_t* hs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
2336
internal/waf/injectionutils/libinjection/src/libinjection_sqli.c
Normal file
2336
internal/waf/injectionutils/libinjection/src/libinjection_sqli.c
Normal file
File diff suppressed because it is too large
Load Diff
294
internal/waf/injectionutils/libinjection/src/libinjection_sqli.h
Normal file
294
internal/waf/injectionutils/libinjection/src/libinjection_sqli.h
Normal file
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* Copyright 2012-2016 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see `COPYING.txt` for details
|
||||
*
|
||||
* https://libinjection.client9.com/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LIBINJECTION_SQLI_H
|
||||
#define LIBINJECTION_SQLI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pull in size_t
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
enum sqli_flags {
|
||||
FLAG_NONE = 0
|
||||
, FLAG_QUOTE_NONE = 1 /* 1 << 0 */
|
||||
, FLAG_QUOTE_SINGLE = 2 /* 1 << 1 */
|
||||
, FLAG_QUOTE_DOUBLE = 4 /* 1 << 2 */
|
||||
|
||||
, FLAG_SQL_ANSI = 8 /* 1 << 3 */
|
||||
, FLAG_SQL_MYSQL = 16 /* 1 << 4 */
|
||||
};
|
||||
|
||||
enum lookup_type {
|
||||
LOOKUP_WORD = 1
|
||||
, LOOKUP_TYPE = 2
|
||||
, LOOKUP_OPERATOR = 3
|
||||
, LOOKUP_FINGERPRINT = 4
|
||||
};
|
||||
|
||||
struct libinjection_sqli_token {
|
||||
#ifdef SWIG
|
||||
%immutable;
|
||||
#endif
|
||||
/*
|
||||
* position and length of token
|
||||
* in original string
|
||||
*/
|
||||
size_t pos;
|
||||
size_t len;
|
||||
|
||||
/* count:
|
||||
* in type 'v', used for number of opening '@'
|
||||
* but maybe used in other contexts
|
||||
*/
|
||||
int count;
|
||||
|
||||
char type;
|
||||
char str_open;
|
||||
char str_close;
|
||||
char val[32];
|
||||
};
|
||||
|
||||
typedef struct libinjection_sqli_token stoken_t;
|
||||
|
||||
/**
|
||||
* Pointer to function, takes c-string input,
|
||||
* returns '\0' for no match, else a char
|
||||
*/
|
||||
struct libinjection_sqli_state;
|
||||
typedef char (*ptr_lookup_fn)(struct libinjection_sqli_state*, int lookuptype, const char* word, size_t len);
|
||||
|
||||
struct libinjection_sqli_state {
|
||||
#ifdef SWIG
|
||||
%immutable;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* input, does not need to be null terminated.
|
||||
* it is also not modified.
|
||||
*/
|
||||
const char *s;
|
||||
|
||||
/*
|
||||
* input length
|
||||
*/
|
||||
size_t slen;
|
||||
|
||||
/*
|
||||
* How to lookup a word or fingerprint
|
||||
*/
|
||||
ptr_lookup_fn lookup;
|
||||
void* userdata;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int flags;
|
||||
|
||||
/*
|
||||
* pos is the index in the string during tokenization
|
||||
*/
|
||||
size_t pos;
|
||||
|
||||
#ifndef SWIG
|
||||
/* for SWIG.. don't use this.. use functional API instead */
|
||||
|
||||
/* MAX TOKENS + 1 since we use one extra token
|
||||
* to determine the type of the previous token
|
||||
*/
|
||||
struct libinjection_sqli_token tokenvec[8];
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pointer to token position in tokenvec, above
|
||||
*/
|
||||
struct libinjection_sqli_token *current;
|
||||
|
||||
/*
|
||||
* fingerprint pattern c-string
|
||||
* +1 for ending null
|
||||
* Minimum of 8 bytes to add gcc's -fstack-protector to work
|
||||
*/
|
||||
char fingerprint[8];
|
||||
|
||||
/*
|
||||
* Line number of code that said decided if the input was SQLi or
|
||||
* not. Most of the time it's line that said "it's not a matching
|
||||
* fingerprint" but there is other logic that sometimes approves
|
||||
* an input. This is only useful for debugging.
|
||||
*
|
||||
*/
|
||||
int reason;
|
||||
|
||||
/* Number of ddw (dash-dash-white) comments
|
||||
* These comments are in the form of
|
||||
* '--[whitespace]' or '--[EOF]'
|
||||
*
|
||||
* All databases treat this as a comment.
|
||||
*/
|
||||
int stats_comment_ddw;
|
||||
|
||||
/* Number of ddx (dash-dash-[notwhite]) comments
|
||||
*
|
||||
* ANSI SQL treats these are comments, MySQL treats this as
|
||||
* two unary operators '-' '-'
|
||||
*
|
||||
* If you are parsing result returns FALSE and
|
||||
* stats_comment_dd > 0, you should reparse with
|
||||
* COMMENT_MYSQL
|
||||
*
|
||||
*/
|
||||
int stats_comment_ddx;
|
||||
|
||||
/*
|
||||
* c-style comments found /x .. x/
|
||||
*/
|
||||
int stats_comment_c;
|
||||
|
||||
/* '#' operators or MySQL EOL comments found
|
||||
*
|
||||
*/
|
||||
int stats_comment_hash;
|
||||
|
||||
/*
|
||||
* number of tokens folded away
|
||||
*/
|
||||
int stats_folds;
|
||||
|
||||
/*
|
||||
* total tokens processed
|
||||
*/
|
||||
int stats_tokens;
|
||||
|
||||
};
|
||||
|
||||
typedef struct libinjection_sqli_state sfilter;
|
||||
|
||||
struct libinjection_sqli_token* libinjection_sqli_get_token(
|
||||
struct libinjection_sqli_state* sql_state, int i);
|
||||
|
||||
/*
|
||||
* Version info.
|
||||
*
|
||||
* This is moved into a function to allow SWIG and other auto-generated
|
||||
* binding to not be modified during minor release changes. We change
|
||||
* change the version number in the c source file, and not regenerated
|
||||
* the binding
|
||||
*
|
||||
* See python's normalized version
|
||||
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
|
||||
*/
|
||||
const char* libinjection_version(void);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void libinjection_sqli_init(struct libinjection_sqli_state *sf,
|
||||
const char* s, size_t len,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
* Main API: tests for SQLi in three possible contexts, no quotes,
|
||||
* single quote and double quote
|
||||
*
|
||||
* \param sql_state core data structure
|
||||
*
|
||||
* \return 1 (true) if SQLi, 0 (false) if benign
|
||||
*/
|
||||
int libinjection_is_sqli(struct libinjection_sqli_state* sql_state);
|
||||
|
||||
/* FOR HACKERS ONLY
|
||||
* provides deep hooks into the decision making process
|
||||
*/
|
||||
void libinjection_sqli_callback(struct libinjection_sqli_state *sf,
|
||||
ptr_lookup_fn fn,
|
||||
void* userdata);
|
||||
|
||||
|
||||
/*
|
||||
* Resets state, but keeps initial string and callbacks
|
||||
*/
|
||||
void libinjection_sqli_reset(struct libinjection_sqli_state *sf,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This detects SQLi in a single context, mostly useful for custom
|
||||
* logic and debugging.
|
||||
*
|
||||
* \param sql_state Main data structure
|
||||
* \param flags flags to adjust parsing
|
||||
*
|
||||
* \returns a pointer to sfilter.fingerprint as convenience
|
||||
* do not free!
|
||||
*
|
||||
*/
|
||||
const char* libinjection_sqli_fingerprint(struct libinjection_sqli_state *sql_state,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
* The default "word" to token-type or fingerprint function. This
|
||||
* uses a ASCII case-insensitive binary tree.
|
||||
*/
|
||||
char libinjection_sqli_lookup_word(struct libinjection_sqli_state *sql_state,
|
||||
int lookup_type,
|
||||
const char* str,
|
||||
size_t len);
|
||||
|
||||
/* Streaming tokenization interface.
|
||||
*
|
||||
* sql_state->current is updated with the current token.
|
||||
*
|
||||
* \returns 1, has a token, keep going, or 0 no tokens
|
||||
*
|
||||
*/
|
||||
int libinjection_sqli_tokenize(struct libinjection_sqli_state *sf);
|
||||
|
||||
/**
|
||||
* parses and folds input, up to 5 tokens
|
||||
*
|
||||
*/
|
||||
int libinjection_sqli_fold(struct libinjection_sqli_state *sf);
|
||||
|
||||
/** The built-in default function to match fingerprints
|
||||
* and do false negative/positive analysis. This calls the following
|
||||
* two functions. With this, you over-ride one part or the other.
|
||||
*
|
||||
* return libinjection_sqli_blacklist(sql_state) &&
|
||||
* libinjection_sqli_not_whitelist(sql_state);
|
||||
*
|
||||
* \param sql_state should be filled out after libinjection_sqli_fingerprint is called
|
||||
*/
|
||||
int libinjection_sqli_check_fingerprint(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
/* Given a pattern determine if it's a SQLi pattern.
|
||||
*
|
||||
* \return TRUE if sqli, false otherwise
|
||||
*/
|
||||
int libinjection_sqli_blacklist(struct libinjection_sqli_state* sql_state);
|
||||
|
||||
/* Given a positive match for a pattern (i.e. pattern is SQLi), this function
|
||||
* does additional analysis to reduce false positives.
|
||||
*
|
||||
* \return TRUE if SQLi, false otherwise
|
||||
*/
|
||||
int libinjection_sqli_not_whitelist(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LIBINJECTION_SQLI_H */
|
||||
File diff suppressed because it is too large
Load Diff
918
internal/waf/injectionutils/libinjection/src/libinjection_xss.c
Normal file
918
internal/waf/injectionutils/libinjection/src/libinjection_xss.c
Normal file
@@ -0,0 +1,918 @@
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_xss.h"
|
||||
#include "libinjection_html5.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum attribute {
|
||||
TYPE_NONE
|
||||
, TYPE_BLACK /* ban always */
|
||||
, TYPE_ATTR_URL /* attribute value takes a URL-like object */
|
||||
, TYPE_STYLE
|
||||
, TYPE_ATTR_INDIRECT /* attribute *name* is given in *value* */
|
||||
} attribute_t;
|
||||
|
||||
|
||||
static attribute_t is_black_attr(const char* s, size_t len, int strictMode);
|
||||
static int is_black_tag(const char* s, size_t len, int strictMode);
|
||||
static int is_black_url(const char* s, size_t len);
|
||||
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n);
|
||||
static int html_decode_char_at(const char* src, size_t len, size_t* consumed);
|
||||
static int htmlencode_startswith(const char *a/* prefix */, const char *b /* src */, size_t n);
|
||||
|
||||
|
||||
typedef struct stringtype {
|
||||
const char* name;
|
||||
attribute_t atype;
|
||||
} stringtype_t;
|
||||
|
||||
|
||||
static const int gsHexDecodeMap[256] = {
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 256,
|
||||
256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 10, 11, 12, 13, 14, 15, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256
|
||||
};
|
||||
|
||||
static int html_decode_char_at(const char* src, size_t len, size_t* consumed)
|
||||
{
|
||||
int val = 0;
|
||||
size_t i;
|
||||
int ch;
|
||||
|
||||
if (len == 0 || src == NULL) {
|
||||
*consumed = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*consumed = 1;
|
||||
if (*src != '&' || len < 2) {
|
||||
return (unsigned char)(*src);
|
||||
}
|
||||
|
||||
|
||||
if (*(src+1) != '#') {
|
||||
/* normally this would be for named entities
|
||||
* but for this case we don't actually care
|
||||
*/
|
||||
return '&';
|
||||
}
|
||||
|
||||
if (*(src+2) == 'x' || *(src+2) == 'X') {
|
||||
ch = (unsigned char) (*(src+3));
|
||||
ch = gsHexDecodeMap[ch];
|
||||
if (ch == 256) {
|
||||
/* degenerate case '&#[?]' */
|
||||
return '&';
|
||||
}
|
||||
val = ch;
|
||||
i = 4;
|
||||
while (i < len) {
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch == ';') {
|
||||
*consumed = i + 1;
|
||||
return val;
|
||||
}
|
||||
ch = gsHexDecodeMap[ch];
|
||||
if (ch == 256) {
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
val = (val * 16) + ch;
|
||||
if (val > 0x1000FF) {
|
||||
return '&';
|
||||
}
|
||||
++i;
|
||||
}
|
||||
*consumed = i;
|
||||
return val;
|
||||
} else {
|
||||
i = 2;
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch < '0' || ch > '9') {
|
||||
return '&';
|
||||
}
|
||||
val = ch - '0';
|
||||
i += 1;
|
||||
while (i < len) {
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch == ';') {
|
||||
*consumed = i + 1;
|
||||
return val;
|
||||
}
|
||||
if (ch < '0' || ch > '9') {
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
val = (val * 10) + (ch - '0');
|
||||
if (val > 0x1000FF) {
|
||||
return '&';
|
||||
}
|
||||
++i;
|
||||
}
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* These were mostly extracted from: https://raw.githubusercontent.com/WebKit/WebKit/main/Source/WebCore/dom/EventNames.h
|
||||
*
|
||||
* view-source:
|
||||
* data:
|
||||
* javascript:
|
||||
* events:
|
||||
*/
|
||||
static stringtype_t BLACKATTREVENT[] = {
|
||||
{ "ABORT", TYPE_BLACK }
|
||||
, { "ACTIVATE", TYPE_BLACK }
|
||||
, { "ACTIVE", TYPE_BLACK }
|
||||
, { "ADDSOURCEBUFFER", TYPE_BLACK }
|
||||
, { "ADDSTREAM", TYPE_BLACK }
|
||||
, { "ADDTRACK", TYPE_BLACK }
|
||||
, { "AFTERPRINT", TYPE_BLACK }
|
||||
, { "ANIMATIONCANCEL", TYPE_BLACK }
|
||||
, { "ANIMATIONEND", TYPE_BLACK }
|
||||
, { "ANIMATIONITERATION", TYPE_BLACK }
|
||||
, { "ANIMATIONSTART", TYPE_BLACK }
|
||||
, { "AUDIOEND", TYPE_BLACK }
|
||||
, { "AUDIOPROCESS", TYPE_BLACK }
|
||||
, { "AUDIOSTART", TYPE_BLACK }
|
||||
, { "AUTOCOMPLETEERROR", TYPE_BLACK }
|
||||
, { "AUTOCOMPLETE", TYPE_BLACK }
|
||||
, { "BEFOREACTIVATE", TYPE_BLACK }
|
||||
, { "BEFORECOPY", TYPE_BLACK }
|
||||
, { "BEFORECUT", TYPE_BLACK }
|
||||
, { "BEFOREINPUT", TYPE_BLACK }
|
||||
, { "BEFORELOAD", TYPE_BLACK }
|
||||
, { "BEFOREPASTE", TYPE_BLACK }
|
||||
, { "BEFOREPRINT", TYPE_BLACK }
|
||||
, { "BEFOREUNLOAD", TYPE_BLACK }
|
||||
, { "BEGINEVENT", TYPE_BLACK }
|
||||
, { "BLOCKED", TYPE_BLACK }
|
||||
, { "BLUR", TYPE_BLACK }
|
||||
, { "BOUNDARY", TYPE_BLACK }
|
||||
, { "BUFFEREDAMOUNTLOW", TYPE_BLACK }
|
||||
, { "CACHED", TYPE_BLACK }
|
||||
, { "CANCEL", TYPE_BLACK }
|
||||
, { "CANPLAYTHROUGH", TYPE_BLACK }
|
||||
, { "CANPLAY", TYPE_BLACK }
|
||||
, { "CHANGE", TYPE_BLACK }
|
||||
, { "CHARGINGCHANGE", TYPE_BLACK }
|
||||
, { "CHARGINGTIMECHANGE", TYPE_BLACK }
|
||||
, { "CHECKING", TYPE_BLACK }
|
||||
, { "CLICK", TYPE_BLACK }
|
||||
, { "CLOSE", TYPE_BLACK }
|
||||
, { "COMPLETE", TYPE_BLACK }
|
||||
, { "COMPOSITIONEND", TYPE_BLACK }
|
||||
, { "COMPOSITIONSTART", TYPE_BLACK }
|
||||
, { "COMPOSITIONUPDATE", TYPE_BLACK }
|
||||
, { "CONNECTING", TYPE_BLACK }
|
||||
, { "CONNECTIONSTATECHANGE", TYPE_BLACK }
|
||||
, { "CONNECT", TYPE_BLACK }
|
||||
, { "CONTEXTMENU", TYPE_BLACK }
|
||||
, { "CONTROLLERCHANGE", TYPE_BLACK }
|
||||
, { "COPY", TYPE_BLACK }
|
||||
, { "CUECHANGE", TYPE_BLACK }
|
||||
, { "CUT", TYPE_BLACK }
|
||||
, { "DATAAVAILABLE", TYPE_BLACK }
|
||||
, { "DATACHANNEL", TYPE_BLACK }
|
||||
, { "DBLCLICK", TYPE_BLACK }
|
||||
, { "DEVICECHANGE", TYPE_BLACK }
|
||||
, { "DEVICEMOTION", TYPE_BLACK }
|
||||
, { "DEVICEORIENTATION", TYPE_BLACK }
|
||||
, { "DISCHARGINGTIMECHANGE", TYPE_BLACK }
|
||||
, { "DISCONNECT", TYPE_BLACK }
|
||||
, { "DOMACTIVATE", TYPE_BLACK }
|
||||
, { "DOMCHARACTERDATAMODIFIED", TYPE_BLACK }
|
||||
, { "DOMCONTENTLOADED", TYPE_BLACK }
|
||||
, { "DOMFOCUSIN", TYPE_BLACK }
|
||||
, { "DOMFOCUSOUT", TYPE_BLACK }
|
||||
, { "DOMNODEINSERTEDINTODOCUMENT", TYPE_BLACK }
|
||||
, { "DOMNODEINSERTED", TYPE_BLACK }
|
||||
, { "DOMNODEREMOVEDFROMDOCUMENT", TYPE_BLACK }
|
||||
, { "DOMNODEREMOVED", TYPE_BLACK }
|
||||
, { "DOMSUBTREEMODIFIED", TYPE_BLACK }
|
||||
, { "DOWNLOADING", TYPE_BLACK }
|
||||
, { "DRAGEND", TYPE_BLACK }
|
||||
, { "DRAGENTER", TYPE_BLACK }
|
||||
, { "DRAGLEAVE", TYPE_BLACK }
|
||||
, { "DRAGOVER", TYPE_BLACK }
|
||||
, { "DRAGSTART", TYPE_BLACK }
|
||||
, { "DRAG", TYPE_BLACK }
|
||||
, { "DROP", TYPE_BLACK }
|
||||
, { "DURATIONCHANGE", TYPE_BLACK }
|
||||
, { "EMPTIED", TYPE_BLACK }
|
||||
, { "ENCRYPTED", TYPE_BLACK }
|
||||
, { "ENDED", TYPE_BLACK }
|
||||
, { "ENDEVENT", TYPE_BLACK }
|
||||
, { "END", TYPE_BLACK }
|
||||
, { "ENTERPICTUREINPICTURE", TYPE_BLACK }
|
||||
, { "ENTER", TYPE_BLACK }
|
||||
, { "ERROR", TYPE_BLACK }
|
||||
, { "EXIT", TYPE_BLACK }
|
||||
, { "FETCH", TYPE_BLACK }
|
||||
, { "FINISH", TYPE_BLACK }
|
||||
, { "FOCUSIN", TYPE_BLACK }
|
||||
, { "FOCUSOUT", TYPE_BLACK }
|
||||
, { "FOCUS", TYPE_BLACK }
|
||||
, { "FORMCHANGE", TYPE_BLACK }
|
||||
, { "FORMINPUT", TYPE_BLACK }
|
||||
, { "GAMEPADCONNECTED", TYPE_BLACK }
|
||||
, { "GAMEPADDISCONNECTED", TYPE_BLACK }
|
||||
, { "GESTURECHANGE", TYPE_BLACK }
|
||||
, { "GESTUREEND", TYPE_BLACK }
|
||||
, { "GESTURESCROLLEND", TYPE_BLACK }
|
||||
, { "GESTURESCROLLSTART", TYPE_BLACK }
|
||||
, { "GESTURESCROLLUPDATE", TYPE_BLACK }
|
||||
, { "GESTURESTART", TYPE_BLACK }
|
||||
, { "GESTURETAPDOWN", TYPE_BLACK }
|
||||
, { "GESTURETAP", TYPE_BLACK }
|
||||
, { "GOTPOINTERCAPTURE", TYPE_BLACK }
|
||||
, { "HASHCHANGE", TYPE_BLACK }
|
||||
, { "ICECANDIDATEERROR", TYPE_BLACK }
|
||||
, { "ICECANDIDATE", TYPE_BLACK }
|
||||
, { "ICECONNECTIONSTATECHANGE", TYPE_BLACK }
|
||||
, { "ICEGATHERINGSTATECHANGE", TYPE_BLACK }
|
||||
, { "INACTIVE", TYPE_BLACK }
|
||||
, { "INPUTSOURCESCHANGE", TYPE_BLACK }
|
||||
, { "INPUT", TYPE_BLACK }
|
||||
, { "INSTALL", TYPE_BLACK }
|
||||
, { "INVALID", TYPE_BLACK }
|
||||
, { "KEYDOWN", TYPE_BLACK }
|
||||
, { "KEYPRESS", TYPE_BLACK }
|
||||
, { "KEYSTATUSESCHANGE", TYPE_BLACK }
|
||||
, { "KEYUP", TYPE_BLACK }
|
||||
, { "LANGUAGECHANGE", TYPE_BLACK }
|
||||
, { "LEAVEPICTUREINPICTURE", TYPE_BLACK }
|
||||
, { "LEVELCHANGE", TYPE_BLACK }
|
||||
, { "LOADEDDATA", TYPE_BLACK }
|
||||
, { "LOADEDMETADATA", TYPE_BLACK }
|
||||
, { "LOADEND", TYPE_BLACK }
|
||||
, { "LOADINGDONE", TYPE_BLACK }
|
||||
, { "LOADINGERROR", TYPE_BLACK }
|
||||
, { "LOADING", TYPE_BLACK }
|
||||
, { "LOADSTART", TYPE_BLACK }
|
||||
, { "LOAD", TYPE_BLACK }
|
||||
, { "LOSTPOINTERCAPTURE", TYPE_BLACK }
|
||||
, { "MARK", TYPE_BLACK }
|
||||
, { "MERCHANTVALIDATION", TYPE_BLACK }
|
||||
, { "MESSAGEERROR", TYPE_BLACK }
|
||||
, { "MESSAGE", TYPE_BLACK }
|
||||
, { "MOUSEDOWN", TYPE_BLACK }
|
||||
, { "MOUSEENTER", TYPE_BLACK }
|
||||
, { "MOUSELEAVE", TYPE_BLACK }
|
||||
, { "MOUSEMOVE", TYPE_BLACK }
|
||||
, { "MOUSEOUT", TYPE_BLACK }
|
||||
, { "MOUSEOVER", TYPE_BLACK }
|
||||
, { "MOUSEUP", TYPE_BLACK }
|
||||
, { "MOUSEWHEEL", TYPE_BLACK }
|
||||
, { "MUTE", TYPE_BLACK }
|
||||
, { "NEGOTIATIONNEEDED", TYPE_BLACK }
|
||||
, { "NEXTTRACK", TYPE_BLACK }
|
||||
, { "NOMATCH", TYPE_BLACK }
|
||||
, { "NOUPDATE", TYPE_BLACK }
|
||||
, { "OBSOLETE", TYPE_BLACK }
|
||||
, { "OFFLINE", TYPE_BLACK }
|
||||
, { "ONLINE", TYPE_BLACK }
|
||||
, { "OPEN", TYPE_BLACK }
|
||||
, { "ORIENTATIONCHANGE", TYPE_BLACK }
|
||||
, { "OVERCONSTRAINED", TYPE_BLACK }
|
||||
, { "OVERFLOWCHANGED", TYPE_BLACK }
|
||||
, { "PAGEHIDE", TYPE_BLACK }
|
||||
, { "PAGESHOW", TYPE_BLACK }
|
||||
, { "PASTE", TYPE_BLACK }
|
||||
, { "PAUSE", TYPE_BLACK }
|
||||
, { "PAYERDETAILCHANGE", TYPE_BLACK }
|
||||
, { "PAYMENTAUTHORIZED", TYPE_BLACK }
|
||||
, { "PAYMENTMETHODCHANGE", TYPE_BLACK }
|
||||
, { "PAYMENTMETHODSELECTED", TYPE_BLACK }
|
||||
, { "PLAYING", TYPE_BLACK }
|
||||
, { "PLAY", TYPE_BLACK }
|
||||
, { "POINTERCANCEL", TYPE_BLACK }
|
||||
, { "POINTERDOWN", TYPE_BLACK }
|
||||
, { "POINTERENTER", TYPE_BLACK }
|
||||
, { "POINTERLEAVE", TYPE_BLACK }
|
||||
, { "POINTERLOCKCHANGE", TYPE_BLACK }
|
||||
, { "POINTERLOCKERROR", TYPE_BLACK }
|
||||
, { "POINTERMOVE", TYPE_BLACK }
|
||||
, { "POINTEROUT", TYPE_BLACK }
|
||||
, { "POINTEROVER", TYPE_BLACK }
|
||||
, { "POINTERUP", TYPE_BLACK }
|
||||
, { "POPSTATE", TYPE_BLACK }
|
||||
, { "PREVIOUSTRACK", TYPE_BLACK }
|
||||
, { "PROCESSORERROR", TYPE_BLACK }
|
||||
, { "PROGRESS", TYPE_BLACK }
|
||||
, { "PROPERTYCHANGE", TYPE_BLACK }
|
||||
, { "RATECHANGE", TYPE_BLACK }
|
||||
, { "READYSTATECHANGE", TYPE_BLACK }
|
||||
, { "REJECTIONHANDLED", TYPE_BLACK }
|
||||
, { "REMOVESOURCEBUFFER", TYPE_BLACK }
|
||||
, { "REMOVESTREAM", TYPE_BLACK }
|
||||
, { "REMOVETRACK", TYPE_BLACK }
|
||||
, { "REMOVE", TYPE_BLACK }
|
||||
, { "RESET", TYPE_BLACK }
|
||||
, { "RESIZE", TYPE_BLACK }
|
||||
, { "RESOURCETIMINGBUFFERFULL", TYPE_BLACK }
|
||||
, { "RESULT", TYPE_BLACK }
|
||||
, { "RESUME", TYPE_BLACK }
|
||||
, { "SCROLL", TYPE_BLACK }
|
||||
, { "SEARCH", TYPE_BLACK }
|
||||
, { "SECURITYPOLICYVIOLATION", TYPE_BLACK }
|
||||
, { "SEEKED", TYPE_BLACK }
|
||||
, { "SEEKING", TYPE_BLACK }
|
||||
, { "SELECTEND", TYPE_BLACK }
|
||||
, { "SELECTIONCHANGE", TYPE_BLACK }
|
||||
, { "SELECTSTART", TYPE_BLACK }
|
||||
, { "SELECT", TYPE_BLACK }
|
||||
, { "SHIPPINGADDRESSCHANGE", TYPE_BLACK }
|
||||
, { "SHIPPINGCONTACTSELECTED", TYPE_BLACK }
|
||||
, { "SHIPPINGMETHODSELECTED", TYPE_BLACK }
|
||||
, { "SHIPPINGOPTIONCHANGE", TYPE_BLACK }
|
||||
, { "SHOW", TYPE_BLACK }
|
||||
, { "SIGNALINGSTATECHANGE", TYPE_BLACK }
|
||||
, { "SLOTCHANGE", TYPE_BLACK }
|
||||
, { "SOUNDEND", TYPE_BLACK }
|
||||
, { "SOUNDSTART", TYPE_BLACK }
|
||||
, { "SOURCECLOSE", TYPE_BLACK }
|
||||
, { "SOURCEENDED", TYPE_BLACK }
|
||||
, { "SOURCEOPEN", TYPE_BLACK }
|
||||
, { "SPEECHEND", TYPE_BLACK }
|
||||
, { "SPEECHSTART", TYPE_BLACK }
|
||||
, { "SQUEEZEEND", TYPE_BLACK }
|
||||
, { "SQUEEZESTART", TYPE_BLACK }
|
||||
, { "SQUEEZE", TYPE_BLACK }
|
||||
, { "STALLED", TYPE_BLACK }
|
||||
, { "STARTED", TYPE_BLACK }
|
||||
, { "START", TYPE_BLACK }
|
||||
, { "STATECHANGE", TYPE_BLACK }
|
||||
, { "STOP", TYPE_BLACK }
|
||||
, { "STORAGE", TYPE_BLACK }
|
||||
, { "SUBMIT", TYPE_BLACK }
|
||||
, { "SUCCESS", TYPE_BLACK }
|
||||
, { "SUSPEND", TYPE_BLACK }
|
||||
, { "TEXTINPUT", TYPE_BLACK }
|
||||
, { "TIMEOUT", TYPE_BLACK }
|
||||
, { "TIMEUPDATE", TYPE_BLACK }
|
||||
, { "TOGGLE", TYPE_BLACK }
|
||||
, { "TOGGLE", TYPE_BLACK }
|
||||
, { "TONECHANGE", TYPE_BLACK }
|
||||
, { "TOUCHCANCEL", TYPE_BLACK }
|
||||
, { "TOUCHEND", TYPE_BLACK }
|
||||
, { "TOUCHFORCECHANGE", TYPE_BLACK }
|
||||
, { "TOUCHMOVE", TYPE_BLACK }
|
||||
, { "TOUCHSTART", TYPE_BLACK }
|
||||
, { "TRACK", TYPE_BLACK }
|
||||
, { "TRANSITIONCANCEL", TYPE_BLACK }
|
||||
, { "TRANSITIONEND", TYPE_BLACK }
|
||||
, { "TRANSITIONRUN", TYPE_BLACK }
|
||||
, { "TRANSITIONSTART", TYPE_BLACK }
|
||||
, { "UNCAPTUREDERROR", TYPE_BLACK }
|
||||
, { "UNHANDLEDREJECTION", TYPE_BLACK }
|
||||
, { "UNLOAD", TYPE_BLACK }
|
||||
, { "UNMUTE", TYPE_BLACK }
|
||||
, { "UPDATEEND", TYPE_BLACK }
|
||||
, { "UPDATEFOUND", TYPE_BLACK }
|
||||
, { "UPDATEREADY", TYPE_BLACK }
|
||||
, { "UPDATESTART", TYPE_BLACK }
|
||||
, { "UPDATE", TYPE_BLACK }
|
||||
, { "UPGRADENEEDED", TYPE_BLACK }
|
||||
, { "VALIDATEMERCHANT", TYPE_BLACK }
|
||||
, { "VERSIONCHANGE", TYPE_BLACK }
|
||||
, { "VISIBILITYCHANGE", TYPE_BLACK }
|
||||
, { "VOLUMECHANGE", TYPE_BLACK }
|
||||
, { "WAITINGFORKEY", TYPE_BLACK }
|
||||
, { "WAITING", TYPE_BLACK }
|
||||
, { "WEBGLCONTEXTCHANGED", TYPE_BLACK }
|
||||
, { "WEBGLCONTEXTCREATIONERROR", TYPE_BLACK }
|
||||
, { "WEBGLCONTEXTLOST", TYPE_BLACK }
|
||||
, { "WEBGLCONTEXTRESTORED", TYPE_BLACK }
|
||||
, { "WEBKITANIMATIONEND", TYPE_BLACK }
|
||||
, { "WEBKITANIMATIONITERATION", TYPE_BLACK }
|
||||
, { "WEBKITANIMATIONSTART", TYPE_BLACK }
|
||||
, { "WEBKITBEFORETEXTINSERTED", TYPE_BLACK }
|
||||
, { "WEBKITBEGINFULLSCREEN", TYPE_BLACK }
|
||||
, { "WEBKITCURRENTPLAYBACKTARGETISWIRELESSCHANGED", TYPE_BLACK }
|
||||
, { "WEBKITENDFULLSCREEN", TYPE_BLACK }
|
||||
, { "WEBKITFULLSCREENCHANGE", TYPE_BLACK }
|
||||
, { "WEBKITFULLSCREENERROR", TYPE_BLACK }
|
||||
, { "WEBKITKEYADDED", TYPE_BLACK }
|
||||
, { "WEBKITKEYERROR", TYPE_BLACK }
|
||||
, { "WEBKITKEYMESSAGE", TYPE_BLACK }
|
||||
, { "WEBKITMOUSEFORCECHANGED", TYPE_BLACK }
|
||||
, { "WEBKITMOUSEFORCEDOWN", TYPE_BLACK }
|
||||
, { "WEBKITMOUSEFORCEUP", TYPE_BLACK }
|
||||
, { "WEBKITMOUSEFORCEWILLBEGIN", TYPE_BLACK }
|
||||
, { "WEBKITNEEDKEY", TYPE_BLACK }
|
||||
, { "WEBKITNETWORKINFOCHANGE", TYPE_BLACK }
|
||||
, { "WEBKITPLAYBACKTARGETAVAILABILITYCHANGED", TYPE_BLACK }
|
||||
, { "WEBKITPRESENTATIONMODECHANGED", TYPE_BLACK }
|
||||
, { "WEBKITREGIONOVERSETCHANGE", TYPE_BLACK }
|
||||
, { "WEBKITREMOVESOURCEBUFFER", TYPE_BLACK }
|
||||
, { "WEBKITSOURCECLOSE", TYPE_BLACK }
|
||||
, { "WEBKITSOURCEENDED", TYPE_BLACK }
|
||||
, { "WEBKITSOURCEOPEN", TYPE_BLACK }
|
||||
, { "WEBKITSPEECHCHANGE", TYPE_BLACK }
|
||||
, { "WEBKITTRANSITIONEND", TYPE_BLACK }
|
||||
, { "WEBKITWILLREVEALBOTTOM", TYPE_BLACK }
|
||||
, { "WEBKITWILLREVEALLEFT", TYPE_BLACK }
|
||||
, { "WEBKITWILLREVEALRIGHT", TYPE_BLACK }
|
||||
, { "WEBKITWILLREVEALTOP", TYPE_BLACK }
|
||||
, { "WHEEL", TYPE_BLACK }
|
||||
, { "WRITEEND", TYPE_BLACK }
|
||||
, { "WRITESTART", TYPE_BLACK }
|
||||
, { "WRITE", TYPE_BLACK }
|
||||
, { "ZOOM", TYPE_BLACK }
|
||||
, { NULL, TYPE_NONE }
|
||||
};
|
||||
|
||||
/*
|
||||
* view-source:
|
||||
* data:
|
||||
* javascript:
|
||||
*/
|
||||
static stringtype_t STRICT_BLACKATTR[] = {
|
||||
{ "ACTION", TYPE_ATTR_URL } /* form */
|
||||
, { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */
|
||||
, { "BY", TYPE_ATTR_URL } /* SVG */
|
||||
, { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */
|
||||
, { "DATAFORMATAS", TYPE_BLACK } /* IE */
|
||||
, { "DATASRC", TYPE_BLACK } /* IE */
|
||||
, { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||
, { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */
|
||||
, { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */
|
||||
, { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */
|
||||
, { "FROM", TYPE_ATTR_URL } /* SVG */
|
||||
, { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */
|
||||
, { "HREF", TYPE_ATTR_URL }
|
||||
, { "LOWSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||
, { "POSTER", TYPE_ATTR_URL } /* Opera 10,11 */
|
||||
, { "SRC", TYPE_ATTR_URL }
|
||||
, { "STYLE", TYPE_STYLE }
|
||||
, { "TO", TYPE_ATTR_URL } /* SVG */
|
||||
, { "VALUES", TYPE_ATTR_URL } /* SVG */
|
||||
, { "XLINK:HREF", TYPE_ATTR_URL }
|
||||
, { NULL, TYPE_NONE }
|
||||
};
|
||||
|
||||
static stringtype_t BLACKATTR[] = {
|
||||
{ "ACTION", TYPE_ATTR_URL } /* form */
|
||||
, { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */
|
||||
, { "BY", TYPE_ATTR_URL } /* SVG */
|
||||
, { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */
|
||||
, { "DATAFORMATAS", TYPE_BLACK } /* IE */
|
||||
, { "DATASRC", TYPE_BLACK } /* IE */
|
||||
, { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||
, { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */
|
||||
, { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */
|
||||
, { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */
|
||||
, { "FROM", TYPE_ATTR_URL } /* SVG */
|
||||
, { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */
|
||||
, { "HREF", TYPE_ATTR_URL }
|
||||
, { "LOWSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||
, { "POSTER", TYPE_ATTR_URL } /* Opera 10,11 */
|
||||
, { "SRC", TYPE_ATTR_URL }
|
||||
, { "TO", TYPE_ATTR_URL } /* SVG */
|
||||
, { "VALUES", TYPE_ATTR_URL } /* SVG */
|
||||
, { "XLINK:HREF", TYPE_ATTR_URL }
|
||||
, { NULL, TYPE_NONE }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* xmlns */
|
||||
/* `xml-stylesheet` > <eval>, <if expr=> */
|
||||
|
||||
/*
|
||||
static const char* BLACKATTR[] = {
|
||||
"ATTRIBUTENAME",
|
||||
"BACKGROUND",
|
||||
"DATAFORMATAS",
|
||||
"HREF",
|
||||
"SCROLL",
|
||||
"SRC",
|
||||
"STYLE",
|
||||
"SRCDOC",
|
||||
NULL
|
||||
};
|
||||
*/
|
||||
|
||||
// GoEdge: change BLACKTAG to STRICT_BLACKTAG
|
||||
static const char* STRICT_BLACKTAG[] = {
|
||||
"APPLET"
|
||||
, "AUDIO"
|
||||
, "BASE"
|
||||
, "COMMENT" /* IE http://html5sec.org/#38 */
|
||||
, "EMBED"
|
||||
, "FORM"
|
||||
, "FRAME"
|
||||
, "FRAMESET"
|
||||
, "HANDLER" /* Opera SVG, effectively a script tag */
|
||||
, "IFRAME"
|
||||
, "IMPORT"
|
||||
, "ISINDEX"
|
||||
, "LINK"
|
||||
, "LISTENER"
|
||||
/* , "MARQUEE" */
|
||||
, "META"
|
||||
, "NOSCRIPT"
|
||||
, "OBJECT"
|
||||
, "SCRIPT"
|
||||
, "STYLE"
|
||||
, "VIDEO"
|
||||
, "VMLFRAME"
|
||||
, "XML"
|
||||
, "XSS"
|
||||
, NULL
|
||||
};
|
||||
|
||||
static const char* BLACKTAG[] = {
|
||||
"APPLET"
|
||||
/* , "AUDIO" */
|
||||
, "BASE"
|
||||
, "COMMENT" /* IE http://html5sec.org/#38 */
|
||||
, "EMBED"
|
||||
/* , "FORM" */
|
||||
, "FRAME"
|
||||
, "FRAMESET"
|
||||
, "HANDLER" /* Opera SVG, effectively a script tag */
|
||||
, "IFRAME"
|
||||
, "IMPORT"
|
||||
, "ISINDEX"
|
||||
, "LINK"
|
||||
, "LISTENER"
|
||||
/* , "MARQUEE" */
|
||||
, "META"
|
||||
, "NOSCRIPT"
|
||||
, "OBJECT"
|
||||
, "SCRIPT"
|
||||
, "STYLE"
|
||||
/* , "VIDEO" */
|
||||
, "VMLFRAME"
|
||||
, "XSS"
|
||||
, NULL
|
||||
};
|
||||
|
||||
|
||||
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n)
|
||||
{
|
||||
char ca;
|
||||
char cb;
|
||||
/* printf("Comparing to %s %.*s\n", a, (int)n, b); */
|
||||
while (n-- > 0) {
|
||||
cb = *b++;
|
||||
if (cb == '\0') continue;
|
||||
|
||||
ca = *a++;
|
||||
|
||||
if (cb >= 'a' && cb <= 'z') {
|
||||
cb -= 0x20;
|
||||
}
|
||||
/* printf("Comparing %c vs %c with %d left\n", ca, cb, (int)n); */
|
||||
if (ca != cb) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*a == 0) {
|
||||
/* printf(" MATCH \n"); */
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does an HTML encoded binary string (const char*, length) start with
|
||||
* a all uppercase c-string (null terminated), case insensitive!
|
||||
*
|
||||
* also ignore any embedded nulls in the HTML string!
|
||||
*
|
||||
* return 1 if match / starts with
|
||||
* return 0 if not
|
||||
*/
|
||||
static int htmlencode_startswith(const char *a, const char *b, size_t n)
|
||||
{
|
||||
size_t consumed;
|
||||
int cb;
|
||||
int first = 1;
|
||||
/* printf("Comparing %s with %.*s\n", a,(int)n,b); */
|
||||
while (n > 0) {
|
||||
if (*a == 0) {
|
||||
/* printf("Match EOL!\n"); */
|
||||
return 1;
|
||||
}
|
||||
cb = html_decode_char_at(b, n, &consumed);
|
||||
b += consumed;
|
||||
n -= consumed;
|
||||
|
||||
if (first && cb <= 32) {
|
||||
/* ignore all leading whitespace and control characters */
|
||||
continue;
|
||||
}
|
||||
first = 0;
|
||||
|
||||
if (cb == 0) {
|
||||
/* always ignore null characters in user input */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb == 10) {
|
||||
/* always ignore vertical tab characters in user input */
|
||||
/* who allows this?? */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb >= 'a' && cb <= 'z') {
|
||||
/* upcase */
|
||||
cb -= 0x20;
|
||||
}
|
||||
|
||||
if (*a != (char) cb) {
|
||||
/* printf(" %c != %c\n", *a, cb); */
|
||||
/* mismatch */
|
||||
return 0;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
|
||||
return (*a == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int is_black_tag(const char* s, size_t len, int strictMode)
|
||||
{
|
||||
const char** black;
|
||||
|
||||
if (len < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strictMode == 1) {
|
||||
black = STRICT_BLACKTAG;
|
||||
} else {
|
||||
black = BLACKTAG;
|
||||
}
|
||||
while (*black != NULL) {
|
||||
if (cstrcasecmp_with_null(*black, s, len) == 0) {
|
||||
/* printf("Got black tag %s\n", *black); */
|
||||
return 1;
|
||||
}
|
||||
black += 1;
|
||||
}
|
||||
|
||||
/* anything SVG related */
|
||||
if ((s[0] == 's' || s[0] == 'S') &&
|
||||
(s[1] == 'v' || s[1] == 'V') &&
|
||||
(s[2] == 'g' || s[2] == 'G')) {
|
||||
/* printf("Got SVG tag \n"); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Anything XSL(t) related */
|
||||
if ((s[0] == 'x' || s[0] == 'X') &&
|
||||
(s[1] == 's' || s[1] == 'S') &&
|
||||
(s[2] == 'l' || s[2] == 'L')) {
|
||||
/* printf("Got XSL tag\n"); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static attribute_t is_black_attr(const char* s, size_t len, int strictMode)
|
||||
{
|
||||
stringtype_t* black;
|
||||
|
||||
if (len < 2) {
|
||||
return TYPE_NONE;
|
||||
}
|
||||
|
||||
if (len >= 5) {
|
||||
|
||||
/* JavaScript on.* event handlers */
|
||||
if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) {
|
||||
black = BLACKATTREVENT;
|
||||
const char *s_without_on = &s[2]; // start comparing from the third char
|
||||
while (black->name != NULL) {
|
||||
if (cstrcasecmp_with_null(black->name, s_without_on, strlen(black->name)) == 0) {
|
||||
/* printf("Got banned attribute name %s\n", black->name); */
|
||||
return black->atype;
|
||||
}
|
||||
black += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* XMLNS can be used to create arbitrary tags */
|
||||
// goedge: commented for photo uploading
|
||||
//if (cstrcasecmp_with_null("XMLNS", s, 5) == 0 || cstrcasecmp_with_null("XLINK", s, 5) == 0) {
|
||||
/* printf("Got XMLNS and XLINK tags\n"); */
|
||||
// return TYPE_BLACK;
|
||||
//}
|
||||
}
|
||||
|
||||
if (strictMode == 1) {
|
||||
black = STRICT_BLACKATTR;
|
||||
} else {
|
||||
black = BLACKATTR;
|
||||
}
|
||||
while (black->name != NULL) {
|
||||
if (cstrcasecmp_with_null(black->name, s, len) == 0) {
|
||||
/* printf("Got banned attribute name %s\n", black->name); */
|
||||
return black->atype;
|
||||
}
|
||||
black += 1;
|
||||
}
|
||||
|
||||
return TYPE_NONE;
|
||||
}
|
||||
|
||||
static int is_black_url(const char* s, size_t len)
|
||||
{
|
||||
|
||||
static const char* data_url = "DATA";
|
||||
static const char* viewsource_url = "VIEW-SOURCE";
|
||||
|
||||
/* obsolete but interesting signal */
|
||||
static const char* vbscript_url = "VBSCRIPT";
|
||||
|
||||
/* covers JAVA, JAVASCRIPT, + colon */
|
||||
static const char* javascript_url = "JAVA";
|
||||
|
||||
/* skip whitespace */
|
||||
while (len > 0 && (*s <= 32 || *s >= 127)) {
|
||||
/*
|
||||
* HEY: this is a signed character.
|
||||
* We are intentionally skipping high-bit characters too
|
||||
* since they are not ASCII, and Opera sometimes uses UTF-8 whitespace.
|
||||
*
|
||||
* Also in EUC-JP some of the high bytes are just ignored.
|
||||
*/
|
||||
++s;
|
||||
--len;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(data_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(viewsource_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(javascript_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(vbscript_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode)
|
||||
{
|
||||
h5_state_t h5;
|
||||
attribute_t attr = TYPE_NONE;
|
||||
|
||||
libinjection_h5_init(&h5, s, len, (enum html5_flags) flags);
|
||||
while (libinjection_h5_next(&h5)) {
|
||||
if (h5.token_type != ATTR_VALUE) {
|
||||
attr = TYPE_NONE;
|
||||
}
|
||||
|
||||
if (h5.token_type == DOCTYPE) {
|
||||
return 1;
|
||||
} else if (h5.token_type == TAG_NAME_OPEN) {
|
||||
if (is_black_tag(h5.token_start, h5.token_len, strictMode)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (h5.token_type == ATTR_NAME) {
|
||||
attr = is_black_attr(h5.token_start, h5.token_len, strictMode);
|
||||
} else if (h5.token_type == ATTR_VALUE) {
|
||||
/*
|
||||
* IE6,7,8 parsing works a bit differently so
|
||||
* a whole <script> or other black tag might be hiding
|
||||
* inside an attribute value under HTML 5 parsing
|
||||
* See http://html5sec.org/#102
|
||||
* to avoid doing a full reparse of the value, just
|
||||
* look for "<". This probably need adjusting to
|
||||
* handle escaped characters
|
||||
*/
|
||||
/*
|
||||
if (memchr(h5.token_start, '<', h5.token_len) != NULL) {
|
||||
return 1;
|
||||
}
|
||||
*/
|
||||
|
||||
switch (attr) {
|
||||
case TYPE_NONE:
|
||||
break;
|
||||
case TYPE_BLACK:
|
||||
return 1;
|
||||
case TYPE_ATTR_URL:
|
||||
if (is_black_url(h5.token_start, h5.token_len)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case TYPE_STYLE:
|
||||
return 1;
|
||||
case TYPE_ATTR_INDIRECT:
|
||||
/* an attribute name is specified in a _value_ */
|
||||
if (is_black_attr(h5.token_start, h5.token_len, strictMode)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
default:
|
||||
assert(0);
|
||||
*/
|
||||
}
|
||||
attr = TYPE_NONE;
|
||||
} else if (h5.token_type == TAG_COMMENT) {
|
||||
/* IE uses a "`" as a tag ending char */
|
||||
// goedge: commented for photo uploading
|
||||
/**if (memchr(h5.token_start, '`', h5.token_len) != NULL) {
|
||||
return 1;
|
||||
}**/
|
||||
|
||||
/* IE conditional comment */
|
||||
if (h5.token_len > 3) {
|
||||
if (h5.token_start[0] == '[' &&
|
||||
(h5.token_start[1] == 'i' || h5.token_start[1] == 'I') &&
|
||||
(h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) {
|
||||
return 1;
|
||||
}
|
||||
if ((h5.token_start[0] == 'x' || h5.token_start[0] == 'X') &&
|
||||
(h5.token_start[1] == 'm' || h5.token_start[1] == 'M') &&
|
||||
(h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (h5.token_len > 5) {
|
||||
/* IE <?import pseudo-tag */
|
||||
if (cstrcasecmp_with_null("IMPORT", h5.token_start, 6) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* XML Entity definition */
|
||||
if (cstrcasecmp_with_null("ENTITY", h5.token_start, 6) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* wrapper
|
||||
*
|
||||
*
|
||||
* const char* s: input string, may contain nulls, does not need to be null-terminated.
|
||||
* size_t len: input string length.
|
||||
*
|
||||
*
|
||||
*/
|
||||
int libinjection_xss(const char* s, size_t slen, int strictMode)
|
||||
{
|
||||
if (libinjection_is_xss(s, slen, DATA_STATE, strictMode)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, slen, VALUE_NO_QUOTE, strictMode)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, slen, VALUE_SINGLE_QUOTE, strictMode)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, slen, VALUE_DOUBLE_QUOTE, strictMode)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, slen, VALUE_BACK_QUOTE, strictMode)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef LIBINJECTION_XSS
|
||||
#define LIBINJECTION_XSS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* HEY THIS ISN'T DONE
|
||||
*/
|
||||
|
||||
/* pull in size_t */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
313
internal/waf/injectionutils/libinjection/src/reader.c
Normal file
313
internal/waf/injectionutils/libinjection/src/reader.c
Normal file
@@ -0,0 +1,313 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
#include "libinjection_xss.h"
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
static int g_test_ok = 0;
|
||||
static int g_test_fail = 0;
|
||||
|
||||
typedef enum {
|
||||
MODE_SQLI,
|
||||
MODE_XSS
|
||||
} detect_mode_t;
|
||||
|
||||
static void usage(const char* program_name);
|
||||
size_t modp_rtrim(char* str, size_t len);
|
||||
void modp_toprint(char* str, size_t len);
|
||||
void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
|
||||
int flag_invert, int flag_true, int flag_quiet);
|
||||
|
||||
int urlcharmap(char ch);
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len);
|
||||
|
||||
int urlcharmap(char ch) {
|
||||
switch (ch) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a': case 'A': return 10;
|
||||
case 'b': case 'B': return 11;
|
||||
case 'c': case 'C': return 12;
|
||||
case 'd': case 'D': return 13;
|
||||
case 'e': case 'E': return 14;
|
||||
case 'f': case 'F': return 15;
|
||||
default:
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len)
|
||||
{
|
||||
const char* deststart = dest;
|
||||
|
||||
size_t i = 0;
|
||||
int d = 0;
|
||||
while (i < len) {
|
||||
switch (s[i]) {
|
||||
case '+':
|
||||
*dest++ = ' ';
|
||||
i += 1;
|
||||
break;
|
||||
case '%':
|
||||
if (i+2 < len) {
|
||||
d = (urlcharmap(s[i+1]) << 4) | urlcharmap(s[i+2]);
|
||||
if ( d < 256) {
|
||||
*dest = (char) d;
|
||||
dest++;
|
||||
i += 3; /* loop will increment one time */
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*dest++ = s[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
*dest = '\0';
|
||||
return (size_t)(dest - deststart); /* compute "strlen" of dest */
|
||||
}
|
||||
|
||||
void modp_toprint(char* str, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (str[i] < 32 || str[i] > 126) {
|
||||
str[i] = '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t modp_rtrim(char* str, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
char c = str[len -1];
|
||||
if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
|
||||
str[len -1] = '\0';
|
||||
len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
|
||||
int flag_invert, int flag_true, int flag_quiet)
|
||||
{
|
||||
char linebuf[8192];
|
||||
int issqli = 0;
|
||||
int linenum = 0;
|
||||
size_t len;
|
||||
sfilter sf;
|
||||
|
||||
while (fgets(linebuf, sizeof(linebuf), fd)) {
|
||||
linenum += 1;
|
||||
len = modp_rtrim(linebuf, strlen(linebuf));
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (linebuf[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
len = modp_url_decode(linebuf, linebuf, len);
|
||||
switch (mode) {
|
||||
case MODE_SQLI: {
|
||||
libinjection_sqli_init(&sf, linebuf, len, 0);
|
||||
issqli = libinjection_is_sqli(&sf);
|
||||
break;
|
||||
}
|
||||
case MODE_XSS: {
|
||||
issqli = libinjection_xss(linebuf, len);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (issqli) {
|
||||
g_test_ok += 1;
|
||||
} else {
|
||||
g_test_fail += 1;
|
||||
}
|
||||
|
||||
if (!flag_quiet) {
|
||||
if ((issqli && flag_true && ! flag_invert) ||
|
||||
(!issqli && flag_true && flag_invert) ||
|
||||
!flag_true) {
|
||||
|
||||
modp_toprint(linebuf, len);
|
||||
|
||||
switch (mode) {
|
||||
case MODE_SQLI: {
|
||||
/*
|
||||
* if we didn't find a SQLi and fingerprint from
|
||||
* sqlstats is is 'sns' or 'snsns' then redo using
|
||||
* plain context
|
||||
*/
|
||||
if (!issqli && (strcmp(sf.fingerprint, "sns") == 0 ||
|
||||
strcmp(sf.fingerprint, "snsns") == 0)) {
|
||||
libinjection_sqli_fingerprint(&sf, 0);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s\t%d\t%s\t%s\t%s\n",
|
||||
fname, linenum,
|
||||
(issqli ? "True" : "False"), sf.fingerprint, linebuf);
|
||||
break;
|
||||
}
|
||||
case MODE_XSS: {
|
||||
fprintf(stdout, "%s\t%d\t%s\t%s\n",
|
||||
fname, linenum,
|
||||
(issqli ? "True" : "False"), linebuf);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(const char* program_name)
|
||||
{
|
||||
fprintf(stdout, "usage: %s [flags] [files...]\n", program_name);
|
||||
fprintf(stdout, "%s\n", "");
|
||||
fprintf(stdout, "%s\n", "-q --quiet : quiet mode");
|
||||
fprintf(stdout, "%s\n", "-m --max-fails : number of failed cases need to fail entire test");
|
||||
fprintf(stdout, "%s\n", "-s INTEGER : repeat each test N time "
|
||||
"(for performance testing)");
|
||||
fprintf(stdout, "%s\n", "-t : only print positive matches");
|
||||
fprintf(stdout, "%s\n", "-x --mode-xss : test input for XSS");
|
||||
fprintf(stdout, "%s\n", "-i --invert : invert test logic "
|
||||
"(input is tested for being safe)");
|
||||
|
||||
fprintf(stdout, "%s\n", "");
|
||||
fprintf(stdout, "%s\n", "-? -h -help --help : this page");
|
||||
fprintf(stdout, "%s\n", "");
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
/*
|
||||
* invert output, by
|
||||
*/
|
||||
int flag_invert = FALSE;
|
||||
|
||||
/*
|
||||
* don't print anything.. useful for
|
||||
* performance monitors, gprof.
|
||||
*/
|
||||
int flag_quiet = FALSE;
|
||||
|
||||
/*
|
||||
* only print positive results
|
||||
* with invert, only print negative results
|
||||
*/
|
||||
int flag_true = FALSE;
|
||||
detect_mode_t mode = MODE_SQLI;
|
||||
|
||||
int flag_slow = 1;
|
||||
int count = 0;
|
||||
int max = -1;
|
||||
|
||||
int i, j;
|
||||
int offset = 1;
|
||||
|
||||
while (offset < argc) {
|
||||
if (strcmp(argv[offset], "-?") == 0 ||
|
||||
strcmp(argv[offset], "-h") == 0 ||
|
||||
strcmp(argv[offset], "-help") == 0 ||
|
||||
strcmp(argv[offset], "--help") == 0) {
|
||||
usage(argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (strcmp(argv[offset], "-i") == 0) {
|
||||
offset += 1;
|
||||
flag_invert = TRUE;
|
||||
} else if (strcmp(argv[offset], "-q") == 0 ||
|
||||
strcmp(argv[offset], "--quiet") == 0) {
|
||||
offset += 1;
|
||||
flag_quiet = TRUE;
|
||||
} else if (strcmp(argv[offset], "-t") == 0) {
|
||||
offset += 1;
|
||||
flag_true = TRUE;
|
||||
} else if (strcmp(argv[offset], "-s") == 0) {
|
||||
offset += 1;
|
||||
flag_slow = 100;
|
||||
} else if (strcmp(argv[offset], "-m") == 0 ||
|
||||
strcmp(argv[offset], "--max-fails") == 0) {
|
||||
offset += 1;
|
||||
max = atoi(argv[offset]);
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-x") == 0 ||
|
||||
strcmp(argv[offset], "--mode-xss") == 0) {
|
||||
mode = MODE_XSS;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset == argc) {
|
||||
test_positive(stdin, "stdin", mode, flag_invert, flag_true, flag_quiet);
|
||||
} else {
|
||||
for (j = 0; j < flag_slow; ++j) {
|
||||
for (i = offset; i < argc; ++i) {
|
||||
FILE* fd = fopen(argv[i], "r");
|
||||
if (fd) {
|
||||
test_positive(fd, argv[i], mode, flag_invert, flag_true, flag_quiet);
|
||||
fclose(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag_quiet) {
|
||||
fprintf(stdout, "%s", "\n");
|
||||
fprintf(stdout, "SQLI : %d\n", g_test_ok);
|
||||
fprintf(stdout, "SAFE : %d\n", g_test_fail);
|
||||
fprintf(stdout, "TOTAL : %d\n", g_test_ok + g_test_fail);
|
||||
}
|
||||
|
||||
if (max == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = g_test_ok;
|
||||
if (flag_invert) {
|
||||
count = g_test_fail;
|
||||
}
|
||||
|
||||
if (count > max) {
|
||||
printf("\nThreshold is %d, got %d, failing.\n", max, count);
|
||||
return 1;
|
||||
} else {
|
||||
printf("\nThreshold is %d, got %d, passing.\n", max, count);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
165
internal/waf/injectionutils/libinjection/src/sqli_cli.c
Normal file
165
internal/waf/injectionutils/libinjection/src/sqli_cli.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* This is for testing against files in ../data/ *.txt
|
||||
* Reads from stdin or a list of files, and emits if a line
|
||||
* is a SQLi attack or not, and does basic statistics
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
|
||||
void print_string(stoken_t* t);
|
||||
void print_var(stoken_t* t);
|
||||
void print_token(stoken_t *t);
|
||||
void usage(void);
|
||||
|
||||
void print_string(stoken_t* t)
|
||||
{
|
||||
/* print opening quote */
|
||||
if (t->str_open != '\0') {
|
||||
printf("%c", t->str_open);
|
||||
}
|
||||
|
||||
/* print content */
|
||||
printf("%s", t->val);
|
||||
|
||||
/* print closing quote */
|
||||
if (t->str_close != '\0') {
|
||||
printf("%c", t->str_close);
|
||||
}
|
||||
}
|
||||
|
||||
void print_var(stoken_t* t)
|
||||
{
|
||||
if (t->count >= 1) {
|
||||
printf("%c", '@');
|
||||
}
|
||||
if (t->count == 2) {
|
||||
printf("%c", '@');
|
||||
}
|
||||
print_string(t);
|
||||
}
|
||||
|
||||
void print_token(stoken_t *t) {
|
||||
printf("%c ", t->type);
|
||||
switch (t->type) {
|
||||
case 's':
|
||||
print_string(t);
|
||||
break;
|
||||
case 'v':
|
||||
print_var(t);
|
||||
break;
|
||||
default:
|
||||
printf("%s", t->val);
|
||||
}
|
||||
printf("%s", "\n");
|
||||
}
|
||||
|
||||
void usage(void) {
|
||||
printf("\n");
|
||||
printf("libinjection sqli tester\n");
|
||||
printf("\n");
|
||||
printf(" -ca parse as ANSI SQL\n");
|
||||
printf(" -cm parse as MySQL SQL\n");
|
||||
printf(" -q0 parse as is\n");
|
||||
printf(" -q1 parse in single-quote mode\n");
|
||||
printf(" -q2 parse in doiuble-quote mode\n");
|
||||
printf("\n");
|
||||
printf(" -f --fold fold results\n");
|
||||
printf("\n");
|
||||
printf(" -d --detect detect SQLI. empty reply = not detected\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
size_t slen;
|
||||
char* copy;
|
||||
|
||||
int flags = 0;
|
||||
int fold = 0;
|
||||
int detect = 0;
|
||||
|
||||
int i;
|
||||
int count;
|
||||
int offset = 1;
|
||||
int issqli;
|
||||
|
||||
sfilter sf;
|
||||
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
while (1) {
|
||||
if (strcmp(argv[offset], "-h") == 0 || strcmp(argv[offset], "-?") == 0 || strcmp(argv[offset], "--help") == 0) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(argv[offset], "-m") == 0) {
|
||||
flags |= FLAG_SQL_MYSQL;
|
||||
offset += 1;
|
||||
}
|
||||
else if (strcmp(argv[offset], "-f") == 0 || strcmp(argv[offset], "--fold") == 0) {
|
||||
fold = 1;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-d") == 0 || strcmp(argv[offset], "--detect") == 0) {
|
||||
detect = 1;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-ca") == 0) {
|
||||
flags |= FLAG_SQL_ANSI;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-cm") == 0) {
|
||||
flags |= FLAG_SQL_MYSQL;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q0") == 0) {
|
||||
flags |= FLAG_QUOTE_NONE;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q1") == 0) {
|
||||
flags |= FLAG_QUOTE_SINGLE;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q2") == 0) {
|
||||
flags |= FLAG_QUOTE_DOUBLE;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ATTENTION: argv is a C-string, null terminated. We copy this
|
||||
* to it's own location, WITHOUT null byte. This way, valgrind
|
||||
* can see if we run past the buffer.
|
||||
*/
|
||||
|
||||
slen = strlen(argv[offset]);
|
||||
copy = (char* ) malloc(slen);
|
||||
memcpy(copy, argv[offset], slen);
|
||||
libinjection_sqli_init(&sf, copy, slen, flags);
|
||||
|
||||
if (detect == 1) {
|
||||
issqli = libinjection_is_sqli(&sf);
|
||||
if (issqli) {
|
||||
printf("%s\n", sf.fingerprint);
|
||||
}
|
||||
} else if (fold == 1) {
|
||||
count = libinjection_sqli_fold(&sf);
|
||||
for (i = 0; i < count; ++i) {
|
||||
print_token(&(sf.tokenvec[i]));
|
||||
}
|
||||
} else {
|
||||
while (libinjection_sqli_tokenize(&sf)) {
|
||||
print_token(sf.current);
|
||||
}
|
||||
}
|
||||
|
||||
free(copy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
132
internal/waf/injectionutils/libinjection/src/sqlparse2c.py
Executable file
132
internal/waf/injectionutils/libinjection/src/sqlparse2c.py
Executable file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright 2012, 2013 Nick Galbreath
|
||||
# nickg@client9.com
|
||||
# BSD License -- see COPYING.txt for details
|
||||
#
|
||||
|
||||
"""
|
||||
Converts a libinjection JSON data file to a C header (.h) file
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
def toc(obj):
|
||||
""" main routine """
|
||||
|
||||
print("""
|
||||
#ifndef LIBINJECTION_SQLI_DATA_H
|
||||
#define LIBINJECTION_SQLI_DATA_H
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
|
||||
typedef struct {
|
||||
const char *word;
|
||||
char type;
|
||||
} keyword_t;
|
||||
|
||||
static size_t parse_money(sfilter * sf);
|
||||
static size_t parse_other(sfilter * sf);
|
||||
static size_t parse_white(sfilter * sf);
|
||||
static size_t parse_operator1(sfilter *sf);
|
||||
static size_t parse_char(sfilter *sf);
|
||||
static size_t parse_hash(sfilter *sf);
|
||||
static size_t parse_dash(sfilter *sf);
|
||||
static size_t parse_slash(sfilter *sf);
|
||||
static size_t parse_backslash(sfilter * sf);
|
||||
static size_t parse_operator2(sfilter *sf);
|
||||
static size_t parse_string(sfilter *sf);
|
||||
static size_t parse_word(sfilter * sf);
|
||||
static size_t parse_var(sfilter * sf);
|
||||
static size_t parse_number(sfilter * sf);
|
||||
static size_t parse_tick(sfilter * sf);
|
||||
static size_t parse_ustring(sfilter * sf);
|
||||
static size_t parse_qstring(sfilter * sf);
|
||||
static size_t parse_nqstring(sfilter * sf);
|
||||
static size_t parse_xstring(sfilter * sf);
|
||||
static size_t parse_bstring(sfilter * sf);
|
||||
static size_t parse_estring(sfilter * sf);
|
||||
static size_t parse_bword(sfilter * sf);
|
||||
""")
|
||||
|
||||
#
|
||||
# Mapping of character to function
|
||||
#
|
||||
fnmap = {
|
||||
'CHAR_WORD' : 'parse_word',
|
||||
'CHAR_WHITE': 'parse_white',
|
||||
'CHAR_OP1' : 'parse_operator1',
|
||||
'CHAR_UNARY': 'parse_operator1',
|
||||
'CHAR_OP2' : 'parse_operator2',
|
||||
'CHAR_BANG' : 'parse_operator2',
|
||||
'CHAR_BACK' : 'parse_backslash',
|
||||
'CHAR_DASH' : 'parse_dash',
|
||||
'CHAR_STR' : 'parse_string',
|
||||
'CHAR_HASH' : 'parse_hash',
|
||||
'CHAR_NUM' : 'parse_number',
|
||||
'CHAR_SLASH': 'parse_slash',
|
||||
'CHAR_SEMICOLON' : 'parse_char',
|
||||
'CHAR_COMMA': 'parse_char',
|
||||
'CHAR_LEFTPARENS': 'parse_char',
|
||||
'CHAR_RIGHTPARENS': 'parse_char',
|
||||
'CHAR_LEFTBRACE': 'parse_char',
|
||||
'CHAR_RIGHTBRACE': 'parse_char',
|
||||
'CHAR_VAR' : 'parse_var',
|
||||
'CHAR_OTHER': 'parse_other',
|
||||
'CHAR_MONEY': 'parse_money',
|
||||
'CHAR_TICK' : 'parse_tick',
|
||||
'CHAR_UNDERSCORE': 'parse_underscore',
|
||||
'CHAR_USTRING' : 'parse_ustring',
|
||||
'CHAR_QSTRING' : 'parse_qstring',
|
||||
'CHAR_NQSTRING' : 'parse_nqstring',
|
||||
'CHAR_XSTRING' : 'parse_xstring',
|
||||
'CHAR_BSTRING' : 'parse_bstring',
|
||||
'CHAR_ESTRING' : 'parse_estring',
|
||||
'CHAR_BWORD' : 'parse_bword'
|
||||
}
|
||||
print()
|
||||
print("typedef size_t (*pt2Function)(sfilter *sf);")
|
||||
print("static const pt2Function char_parse_map[] = {")
|
||||
pos = 0
|
||||
for character in obj['charmap']:
|
||||
print(" &%s, /* %d */" % (fnmap[character], pos))
|
||||
pos += 1
|
||||
print("};")
|
||||
print()
|
||||
|
||||
# keywords
|
||||
# load them
|
||||
keywords = obj['keywords']
|
||||
|
||||
for fingerprint in list(obj['fingerprints']):
|
||||
fingerprint = '0' + fingerprint.upper()
|
||||
keywords[fingerprint] = 'F'
|
||||
|
||||
needhelp = []
|
||||
for key in keywords.keys():
|
||||
if key != key.upper():
|
||||
needhelp.append(key)
|
||||
|
||||
for key in needhelp:
|
||||
tmpv = keywords[key]
|
||||
del keywords[key]
|
||||
keywords[key.upper()] = tmpv
|
||||
|
||||
print("static const keyword_t sql_keywords[] = {")
|
||||
for k in sorted(keywords.keys()):
|
||||
if len(k) > 31:
|
||||
sys.stderr.write("ERROR: keyword greater than 32 chars\n")
|
||||
sys.exit(1)
|
||||
|
||||
print(" {\"%s\", '%s'}," % (k, keywords[k]))
|
||||
print("};")
|
||||
print("static const size_t sql_keywords_sz = %d;" % (len(keywords), ))
|
||||
|
||||
print("#endif")
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
import json
|
||||
sys.exit(toc(json.load(sys.stdin)))
|
||||
|
||||
3
internal/waf/injectionutils/libinjection_sqli.c
Normal file
3
internal/waf/injectionutils/libinjection_sqli.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#define LIBINJECTION_VERSION "3.9.1"
|
||||
|
||||
#include "libinjection/src/libinjection_sqli.c"
|
||||
6
internal/waf/injectionutils/libinjection_xss.c
Normal file
6
internal/waf/injectionutils/libinjection_xss.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#define LIBINJECTION_VERSION "3.9.1"
|
||||
|
||||
#include "libinjection/src/libinjection_xss.c"
|
||||
#include "libinjection/src/libinjection_html5.c"
|
||||
|
||||
#define GOEDGE_VERSION "25" // last version is for GoEdge change
|
||||
91
internal/waf/injectionutils/utils_sqli.go
Normal file
91
internal/waf/injectionutils/utils_sqli.go
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build gcc
|
||||
|
||||
package injectionutils
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -O2 -I./libinjection/src
|
||||
|
||||
#include <libinjection.h>
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DetectSQLInjectionCache detect sql injection in string with cache
|
||||
func DetectSQLInjectionCache(input string, isStrict bool, cacheLife int) bool {
|
||||
var l = len(input)
|
||||
|
||||
if l == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if cacheLife <= 0 || l < 128 || l > MaxCacheDataSize {
|
||||
return DetectSQLInjection(input, isStrict)
|
||||
}
|
||||
|
||||
var result = DetectSQLInjection(input, isStrict)
|
||||
return result
|
||||
}
|
||||
|
||||
// DetectSQLInjection detect sql injection in string
|
||||
func DetectSQLInjection(input string, isStrict bool) bool {
|
||||
if len(input) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if !isStrict {
|
||||
if len(input) > 1024 {
|
||||
if !utf8.ValidString(input[:1024]) && !utf8.ValidString(input[:1023]) && !utf8.ValidString(input[:1022]) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !utf8.ValidString(input) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if detectSQLInjectionOne(input) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 兼容 /PATH?URI
|
||||
if (input[0] == '/' || strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://")) && len(input) < 1024 {
|
||||
var argsIndex = strings.Index(input, "?")
|
||||
if argsIndex > 0 {
|
||||
var args = input[argsIndex+1:]
|
||||
unescapeArgs, err := url.QueryUnescape(args)
|
||||
if err == nil && args != unescapeArgs {
|
||||
return detectSQLInjectionOne(args) || detectSQLInjectionOne(unescapeArgs)
|
||||
} else {
|
||||
return detectSQLInjectionOne(args)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unescapedInput, err := url.QueryUnescape(input)
|
||||
if err == nil && input != unescapedInput {
|
||||
return detectSQLInjectionOne(unescapedInput)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func detectSQLInjectionOne(input string) bool {
|
||||
if len(input) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var fingerprint [8]C.char
|
||||
var fingerprintPtr = (*C.char)(unsafe.Pointer(&fingerprint[0]))
|
||||
var cInput = C.CString(input)
|
||||
defer C.free(unsafe.Pointer(cInput))
|
||||
|
||||
return C.libinjection_sqli(cInput, C.size_t(len(input)), fingerprintPtr) == 1
|
||||
}
|
||||
21
internal/waf/injectionutils/utils_sqli_stub.go
Normal file
21
internal/waf/injectionutils/utils_sqli_stub.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !gcc
|
||||
|
||||
package injectionutils
|
||||
|
||||
// DetectSQLInjectionCache detect sql injection in string with cache
|
||||
func DetectSQLInjectionCache(input string, isStrict bool, cacheLife int) bool {
|
||||
// stub
|
||||
return false
|
||||
}
|
||||
|
||||
// DetectSQLInjection detect sql injection in string
|
||||
func DetectSQLInjection(input string, isStrict bool) bool {
|
||||
// stub
|
||||
return false
|
||||
}
|
||||
|
||||
func detectSQLInjectionOne(input string) bool {
|
||||
// stub
|
||||
return false
|
||||
}
|
||||
130
internal/waf/injectionutils/utils_sqli_test.go
Normal file
130
internal/waf/injectionutils/utils_sqli_test.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build gcc
|
||||
|
||||
package injectionutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/waf/injectionutils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDetectSQLInjection(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
for _, isStrict := range []bool{true, false} {
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("' UNION SELECT * FROM myTable", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("id=1 ' UNION select * from a", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--", isStrict))
|
||||
a.IsFalse(injectionutils.DetectSQLInjection("' UNION SELECT1 * FROM myTable", isStrict))
|
||||
a.IsFalse(injectionutils.DetectSQLInjection("1234", isStrict))
|
||||
a.IsFalse(injectionutils.DetectSQLInjection("", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("id=123 OR 1=1&b=2", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("id=123&b=456&c=1' or 2=2", isStrict))
|
||||
a.IsFalse(injectionutils.DetectSQLInjection("?", isStrict))
|
||||
a.IsFalse(injectionutils.DetectSQLInjection("/hello?age=22", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/sql/injection?id=123%20or%201=1", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("id=123%20or%201=1", isStrict))
|
||||
a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/' or 1=1", isStrict))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--", false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection_URL(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1", false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection_Normal_Small(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjection("a/sql/injection?id=1234", false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection_URL_Normal_Small(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjection("/sql/injection?id="+types.String(rands.Int64()%10000), false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection_URL_Normal_Middle(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjection("/search?q=libinjection+fingerprint&newwindow=1&sca_esv=589290862&sxsrf=AMwHvKnxuLoejn2XlNniffC12E_xc35M7Q%3A1702090118361&ei=htvzzebfFZfo1e8PvLGggAk&ved=0ahUKEwjTsYmnq4GDAxUWdPOHHbwkCJAQ4ddDCBA&uact=5&oq=libinjection+fingerprint&gs_lp=Egxnd3Mtd2l6LXNlcnAiGIxpYmluamVjdGlvbmBmaW5nKXJwcmludTIEEAAYHjIGVAAYCBgeSiEaUPkRWKFZcAJ4AZABAJgBHgGgAfoEqgwDMC40uAEGyAEA-AEBwgIKEAFYTxjWMuiwA-IDBBgAVteIBgGQBgI&sclient=gws-wiz-serp#ip=1", false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection_URL_Normal_Small_Cache(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjectionCache("/sql/injection?id="+types.String(rands.Int64()%10000), false, 1800)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection_Normal_Large(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
var s = strings.Repeat("A", 512)
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjection("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s+"&v=%20", false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection_Normal_Large_Cache(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
var s = strings.Repeat("A", 512)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjectionCache("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s, false, 1800)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectSQLInjection_URL_Unescape(b *testing.B) {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1", false)
|
||||
}
|
||||
})
|
||||
}
|
||||
89
internal/waf/injectionutils/utils_xss.go
Normal file
89
internal/waf/injectionutils/utils_xss.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build gcc
|
||||
|
||||
package injectionutils
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -O2 -I./libinjection/src
|
||||
|
||||
#include <libinjection.h>
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const MaxCacheDataSize = 1 << 20
|
||||
|
||||
func DetectXSSCache(input string, isStrict bool, cacheLife int) bool {
|
||||
var l = len(input)
|
||||
|
||||
if l == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if cacheLife <= 0 || l < 512 || l > MaxCacheDataSize {
|
||||
return DetectXSS(input, isStrict)
|
||||
}
|
||||
|
||||
var hash = xxhash.Sum64String(input)
|
||||
var key = "WAF@XSS@" + strconv.FormatUint(hash, 10)
|
||||
if isStrict {
|
||||
key += "@1"
|
||||
}
|
||||
|
||||
var result = DetectXSS(input, isStrict)
|
||||
return result
|
||||
}
|
||||
|
||||
// DetectXSS detect XSS in string
|
||||
func DetectXSS(input string, isStrict bool) bool {
|
||||
if len(input) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if detectXSSOne(input, isStrict) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 兼容 /PATH?URI
|
||||
if (input[0] == '/' || strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://")) && len(input) < 1024 {
|
||||
var argsIndex = strings.Index(input, "?")
|
||||
if argsIndex > 0 {
|
||||
var args = input[argsIndex+1:]
|
||||
unescapeArgs, err := url.QueryUnescape(args)
|
||||
if err == nil && args != unescapeArgs {
|
||||
return detectXSSOne(args, isStrict) || detectXSSOne(unescapeArgs, isStrict)
|
||||
} else {
|
||||
return detectXSSOne(args, isStrict)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unescapedInput, err := url.QueryUnescape(input)
|
||||
if err == nil && input != unescapedInput {
|
||||
return detectXSSOne(unescapedInput, isStrict)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func detectXSSOne(input string, isStrict bool) bool {
|
||||
if len(input) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var cInput = C.CString(input)
|
||||
defer C.free(unsafe.Pointer(cInput))
|
||||
|
||||
var isStrictInt = 0
|
||||
if isStrict {
|
||||
isStrictInt = 1
|
||||
}
|
||||
return C.libinjection_xss(cInput, C.size_t(len(input)), C.int(isStrictInt)) == 1
|
||||
}
|
||||
22
internal/waf/injectionutils/utils_xss_stub.go
Normal file
22
internal/waf/injectionutils/utils_xss_stub.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !gcc
|
||||
|
||||
package injectionutils
|
||||
|
||||
const MaxCacheDataSize = 1 << 20
|
||||
|
||||
func DetectXSSCache(input string, isStrict bool, cacheLife int) bool {
|
||||
// stub
|
||||
return false
|
||||
}
|
||||
|
||||
// DetectXSS detect XSS in string
|
||||
func DetectXSS(input string, isStrict bool) bool {
|
||||
// stub
|
||||
return false
|
||||
}
|
||||
|
||||
func detectXSSOne(input string, isStrict bool) bool {
|
||||
// stub
|
||||
return false
|
||||
}
|
||||
94
internal/waf/injectionutils/utils_xss_test.go
Normal file
94
internal/waf/injectionutils/utils_xss_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build gcc
|
||||
|
||||
package injectionutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/waf/injectionutils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDetectXSS(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
a.IsFalse(injectionutils.DetectXSS("", true))
|
||||
a.IsFalse(injectionutils.DetectXSS("abc", true))
|
||||
a.IsTrue(injectionutils.DetectXSS("<script>", true))
|
||||
a.IsTrue(injectionutils.DetectXSS("<link>", true))
|
||||
a.IsFalse(injectionutils.DetectXSS("<html><span>", true))
|
||||
a.IsFalse(injectionutils.DetectXSS("<script>", true))
|
||||
a.IsTrue(injectionutils.DetectXSS("/path?onmousedown=a", true))
|
||||
a.IsTrue(injectionutils.DetectXSS("/path?onkeyup=a", true))
|
||||
a.IsTrue(injectionutils.DetectXSS("onkeyup=a", true))
|
||||
a.IsTrue(injectionutils.DetectXSS("<iframe scrolling='no'>", true))
|
||||
a.IsFalse(injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", true))
|
||||
a.IsTrue(injectionutils.DetectXSS("name=s&description=%3Cscript+src%3D%22a.js%22%3Edddd%3C%2Fscript%3E", true))
|
||||
a.IsFalse(injectionutils.DetectXSS(`<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 6.0.0">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:tiff="http://ns.adobe.com/tiff/1.0/">
|
||||
<tiff:Orientation>1</tiff:Orientation>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>`, true)) // included in some photo files
|
||||
a.IsFalse(injectionutils.DetectXSS(`<xml></xml>`, false))
|
||||
}
|
||||
|
||||
func TestDetectXSS_Strict(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
a.IsFalse(injectionutils.DetectXSS(`<xml></xml>`, false))
|
||||
a.IsTrue(injectionutils.DetectXSS(`<xml></xml>`, true))
|
||||
a.IsFalse(injectionutils.DetectXSS(`<img src=\"\"/>`, false))
|
||||
a.IsFalse(injectionutils.DetectXSS(`<img src=\"test.jpg\"/>`, true))
|
||||
a.IsFalse(injectionutils.DetectXSS(`<a href="aaaa"></a>`, true))
|
||||
a.IsFalse(injectionutils.DetectXSS(`<span style="color: red"></span>`, false))
|
||||
a.IsTrue(injectionutils.DetectXSS(`<span style="color: red"></span>`, true))
|
||||
a.IsFalse(injectionutils.DetectXSS("https://example.com?style=list", false))
|
||||
a.IsTrue(injectionutils.DetectXSS("https://example.com?style=list", true))
|
||||
}
|
||||
|
||||
func BenchmarkDetectXSS_MISS(b *testing.B) {
|
||||
var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
|
||||
if result {
|
||||
b.Fatal("'result' should not be 'true'")
|
||||
}
|
||||
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectXSS_MISS_Cache(b *testing.B) {
|
||||
var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
|
||||
if result {
|
||||
b.Fatal("'result' should not be 'true'")
|
||||
}
|
||||
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectXSSCache("<html><body><span>RequestId: 1234567890</span></body></html>", false, 1800)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDetectXSS_HIT(b *testing.B) {
|
||||
var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>", false)
|
||||
if !result {
|
||||
b.Fatal("'result' should not be 'false'")
|
||||
}
|
||||
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>", false)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
@@ -106,7 +107,7 @@ func (this *ParentAction) CreateLog(level string, messageCode langs.MessageCode,
|
||||
}
|
||||
}
|
||||
}
|
||||
err := dao.SharedLogDAO.CreateAdminLog(this.AdminContext(), level, this.Request.URL.Path, desc, this.RequestRemoteIP(), messageCode, args)
|
||||
err := dao.SharedLogDAO.CreateAdminLog(this.AdminContext(), level, this.Request.URL.Path, desc, loginutils.RemoteIP(&this.ActionObject), messageCode, args)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
@@ -168,3 +169,14 @@ func (this *ParentAction) FailLang(messageCode langs.MessageCode, args ...any) {
|
||||
func (this *ParentAction) FailFieldLang(field string, messageCode langs.MessageCode, args ...any) {
|
||||
this.FailField(field, langs.Message(this.LangCode(), messageCode, args...))
|
||||
}
|
||||
|
||||
func (this *ParentAction) FilterHTTPFamily() bool {
|
||||
if this.Data.GetString("serverFamily") == "http" {
|
||||
return false
|
||||
}
|
||||
|
||||
this.ResponseWriter.WriteHeader(http.StatusNotFound)
|
||||
_, _ = this.ResponseWriter.Write([]byte("page not found"))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package admins
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/otputils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"github.com/xlzd/gotp"
|
||||
)
|
||||
@@ -19,7 +21,8 @@ func (this *OtpQrcodeAction) Init() {
|
||||
}
|
||||
|
||||
func (this *OtpQrcodeAction) RunGet(params struct {
|
||||
AdminId int64
|
||||
AdminId int64
|
||||
Download bool
|
||||
}) {
|
||||
loginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
|
||||
AdminId: params.AdminId,
|
||||
@@ -29,19 +32,19 @@ func (this *OtpQrcodeAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
login := loginResp.Login
|
||||
var login = loginResp.Login
|
||||
if login == nil || !login.IsOn {
|
||||
this.NotFound("adminLogin", params.AdminId)
|
||||
return
|
||||
}
|
||||
|
||||
loginParams := maps.Map{}
|
||||
var loginParams = maps.Map{}
|
||||
err = json.Unmarshal(login.ParamsJSON, &loginParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
secret := loginParams.GetString("secret")
|
||||
var secret = loginParams.GetString("secret")
|
||||
|
||||
// 当前用户信息
|
||||
adminResp, err := this.RPC().AdminRPC().FindEnabledAdmin(this.AdminContext(), &pb.FindEnabledAdminRequest{AdminId: params.AdminId})
|
||||
@@ -49,7 +52,7 @@ func (this *OtpQrcodeAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
admin := adminResp.Admin
|
||||
var admin = adminResp.Admin
|
||||
if admin == nil {
|
||||
this.NotFound("admin", params.AdminId)
|
||||
return
|
||||
@@ -60,12 +63,19 @@ func (this *OtpQrcodeAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
url := gotp.NewDefaultTOTP(secret).ProvisioningUri(admin.Username, uiConfig.AdminSystemName)
|
||||
data, err := qrcode.Encode(url, qrcode.Medium, 256)
|
||||
var url = gotp.NewDefaultTOTP(secret).ProvisioningUri(admin.Username, uiConfig.AdminSystemName)
|
||||
|
||||
data, err := qrcode.Encode(otputils.FixIssuer(url), qrcode.Medium, 256)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if params.Download {
|
||||
var filename = "OTP-ADMIN-" + admin.Username + ".png"
|
||||
this.AddHeader("Content-Disposition", "attachment; filename=\""+filename+"\";")
|
||||
}
|
||||
this.AddHeader("Content-Type", "image/png")
|
||||
this.AddHeader("Content-Length", types.String(len(data)))
|
||||
_, _ = this.Write(data)
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ func init() {
|
||||
Post("/start", new(node.StartAction)).
|
||||
Post("/stop", new(node.StopAction)).
|
||||
Post("/up", new(node.UpAction)).
|
||||
Post("/updateIsOn", new(node.UpdateIsOnAction)).
|
||||
Get("/detail", new(node.DetailAction)).
|
||||
GetPost("/updateDNSPopup", new(node.UpdateDNSPopupAction)).
|
||||
Post("/syncDomain", new(node.SyncDomainAction)).
|
||||
|
||||
@@ -3,11 +3,11 @@ package node
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
@@ -178,7 +178,7 @@ func (this *DetailAction) RunGet(params struct {
|
||||
|
||||
for _, route := range dnsInfo.Routes {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(addr.Ip) {
|
||||
if iputils.IsIPv6(addr.Ip) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
recordMaps = append(recordMaps, maps.Map{
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package nodeutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInstallLocalNode(t *testing.T) {
|
||||
err := nodeutils.InstallLocalNode()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package thresholds
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "node", "update")
|
||||
this.SecondMenu("threshold")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
}) {
|
||||
_, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
// 列出所有阈值
|
||||
thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{
|
||||
Role: "node",
|
||||
NodeClusterId: params.ClusterId,
|
||||
NodeId: params.NodeId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
thresholdMaps := []maps.Map{}
|
||||
for _, threshold := range thresholdsResp.NodeThresholds {
|
||||
thresholdMaps = append(thresholdMaps, maps.Map{
|
||||
"id": threshold.Id,
|
||||
"itemName": nodeconfigs.FindNodeValueItemName(threshold.Item),
|
||||
"paramName": nodeconfigs.FindNodeValueItemParamName(threshold.Item, threshold.Param),
|
||||
"operatorName": nodeconfigs.FindNodeValueOperatorName(threshold.Operator),
|
||||
"value": nodeconfigs.UnmarshalNodeValue(threshold.ValueJSON),
|
||||
"sumMethodName": nodeconfigs.FindNodeValueSumMethodName(threshold.SumMethod),
|
||||
"duration": threshold.Duration,
|
||||
"durationUnitName": nodeconfigs.FindNodeValueDurationUnitName(threshold.DurationUnit),
|
||||
"isOn": threshold.IsOn,
|
||||
})
|
||||
}
|
||||
this.Data["thresholds"] = thresholdMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type UpdateIsOnAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateIsOnAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
IsOn bool
|
||||
}) {
|
||||
if params.IsOn {
|
||||
defer this.CreateLogInfo(codes.Node_LogUpdateNodeOn, params.NodeId)
|
||||
} else {
|
||||
defer this.CreateLogInfo(codes.Node_LogUpdateNodeOff, params.NodeId)
|
||||
}
|
||||
|
||||
_, err := this.RPC().NodeRPC().UpdateNodeIsOn(this.AdminContext(), &pb.UpdateNodeIsOnRequest{
|
||||
NodeId: params.NodeId,
|
||||
IsOn: params.IsOn,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -14,7 +14,7 @@ type IndexAction struct {
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "setting", "")
|
||||
this.Nav("", "setting", "index")
|
||||
this.SecondMenu("dns")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type RecordsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *RecordsAction) Init() {
|
||||
this.Nav("", "setting", "records")
|
||||
this.SecondMenu("dns")
|
||||
}
|
||||
|
||||
func (this *RecordsAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
}) {
|
||||
// 集群信息
|
||||
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var cluster = clusterResp.NodeCluster
|
||||
if cluster == nil {
|
||||
this.NotFound("nodeCluster", params.ClusterId)
|
||||
return
|
||||
}
|
||||
this.Data["cluster"] = maps.Map{
|
||||
"id": cluster.Id,
|
||||
"name": cluster.Name,
|
||||
}
|
||||
|
||||
// DNS信息
|
||||
dnsResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var defaultRoute = dnsResp.DefaultRoute
|
||||
var domainName = ""
|
||||
var dnsMap = maps.Map{
|
||||
"dnsName": dnsResp.Name,
|
||||
"domainId": 0,
|
||||
"domainName": "",
|
||||
"providerId": 0,
|
||||
"providerName": "",
|
||||
"providerTypeName": "",
|
||||
}
|
||||
if dnsResp.Domain != nil {
|
||||
domainName = dnsResp.Domain.Name
|
||||
dnsMap["domainId"] = dnsResp.Domain.Id
|
||||
dnsMap["domainName"] = dnsResp.Domain.Name
|
||||
}
|
||||
if dnsResp.Provider != nil {
|
||||
dnsMap["providerId"] = dnsResp.Provider.Id
|
||||
dnsMap["providerName"] = dnsResp.Provider.Name
|
||||
dnsMap["providerTypeName"] = dnsResp.Provider.TypeName
|
||||
}
|
||||
|
||||
if len(dnsResp.CnameRecords) > 0 {
|
||||
dnsMap["cnameRecords"] = dnsResp.CnameRecords
|
||||
} else {
|
||||
dnsMap["cnameRecords"] = []string{}
|
||||
}
|
||||
|
||||
this.Data["dnsInfo"] = dnsMap
|
||||
|
||||
// 未安装的节点
|
||||
notInstalledNodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
IsInstalled: false,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var allNodes = notInstalledNodesResp.Nodes
|
||||
|
||||
// 节点DNS解析记录
|
||||
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
IsInstalled: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var installedNodeIdsMap = map[int64]bool{}
|
||||
for _, node := range nodesResp.Nodes {
|
||||
installedNodeIdsMap[node.Id] = true
|
||||
}
|
||||
|
||||
allNodes = append(allNodes, nodesResp.Nodes...)
|
||||
|
||||
var nodeMaps = []maps.Map{}
|
||||
for _, node := range allNodes {
|
||||
var isInstalled = installedNodeIdsMap[node.Id]
|
||||
|
||||
if len(node.Routes) > 0 {
|
||||
for _, route := range node.Routes {
|
||||
// 检查是否已解析
|
||||
var isResolved = false
|
||||
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
||||
var recordType = "A"
|
||||
if iputils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
DnsDomainId: cluster.DnsDomainId,
|
||||
Name: cluster.DnsName,
|
||||
Type: recordType,
|
||||
Route: route.Code,
|
||||
Value: node.IpAddr,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddr": node.IpAddr,
|
||||
"ipAddrId": node.NodeIPAddressId,
|
||||
"route": maps.Map{
|
||||
"name": route.Name,
|
||||
"code": route.Code,
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"isInstalled": isInstalled,
|
||||
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
|
||||
"isOffline": node.IsOffline,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 默认线路
|
||||
var isResolved = false
|
||||
if isInstalled && len(defaultRoute) > 0 {
|
||||
var recordType = "A"
|
||||
if iputils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
DnsDomainId: cluster.DnsDomainId,
|
||||
Name: cluster.DnsName,
|
||||
Type: recordType,
|
||||
Route: defaultRoute,
|
||||
Value: node.IpAddr,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddr": node.IpAddr,
|
||||
"ipAddrId": node.NodeIPAddressId,
|
||||
"route": maps.Map{
|
||||
"name": "",
|
||||
"code": "",
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"isInstalled": isInstalled,
|
||||
"isBackup": node.IsBackupForCluster || node.IsBackupForGroup,
|
||||
"isOffline": node.IsOffline,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["nodes"] = nodeMaps
|
||||
|
||||
// 代理服务解析记录
|
||||
serversResp, err := this.RPC().ServerRPC().FindAllEnabledServersDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledServersDNSWithNodeClusterIdRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var serverMaps = []maps.Map{}
|
||||
for _, server := range serversResp.Servers {
|
||||
// 检查是否已解析
|
||||
isResolved := false
|
||||
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(server.DnsName) > 0 && len(domainName) > 0 {
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
DnsDomainId: cluster.DnsDomainId,
|
||||
Name: server.DnsName,
|
||||
Type: "CNAME",
|
||||
Value: cluster.DnsName + "." + domainName,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
|
||||
serverMaps = append(serverMaps, maps.Map{
|
||||
"id": server.Id,
|
||||
"name": server.Name,
|
||||
"dnsName": server.DnsName,
|
||||
"isResolved": isResolved,
|
||||
})
|
||||
}
|
||||
this.Data["servers"] = serverMaps
|
||||
|
||||
// 检查解析记录是否有变化
|
||||
checkChangesResp, err := this.RPC().NodeClusterRPC().CheckNodeClusterDNSChanges(this.AdminContext(), &pb.CheckNodeClusterDNSChangesRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["dnsHasChanges"] = checkChangesResp.IsChanged
|
||||
|
||||
// 需要解决的问题
|
||||
issuesResp, err := this.RPC().DNSRPC().FindAllDNSIssues(this.AdminContext(), &pb.FindAllDNSIssuesRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var issueMaps = []maps.Map{}
|
||||
for _, issue := range issuesResp.Issues {
|
||||
issueMaps = append(issueMaps, maps.Map{
|
||||
"target": issue.Target,
|
||||
"targetId": issue.TargetId,
|
||||
"type": issue.Type,
|
||||
"description": issue.Description,
|
||||
"params": issue.Params,
|
||||
})
|
||||
}
|
||||
this.Data["issues"] = issueMaps
|
||||
|
||||
// 当前正在执行的任务
|
||||
resp, err := this.RPC().DNSTaskRPC().FindAllDoingDNSTasks(this.AdminContext(), &pb.FindAllDoingDNSTasksRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var taskMaps = []maps.Map{}
|
||||
for _, task := range resp.DnsTasks {
|
||||
var clusterMap maps.Map = nil
|
||||
var nodeMap maps.Map = nil
|
||||
var serverMap maps.Map = nil
|
||||
var domainMap maps.Map = nil
|
||||
|
||||
if task.NodeCluster != nil {
|
||||
clusterMap = maps.Map{
|
||||
"id": task.NodeCluster.Id,
|
||||
"name": task.NodeCluster.Name,
|
||||
}
|
||||
}
|
||||
if task.Node != nil {
|
||||
nodeMap = maps.Map{
|
||||
"id": task.Node.Id,
|
||||
"name": task.Node.Name,
|
||||
}
|
||||
}
|
||||
if task.Server != nil {
|
||||
serverMap = maps.Map{
|
||||
"id": task.Server.Id,
|
||||
"name": task.Server.Name,
|
||||
}
|
||||
}
|
||||
if task.DnsDomain != nil {
|
||||
domainMap = maps.Map{
|
||||
"id": task.DnsDomain.Id,
|
||||
"name": task.DnsDomain.Name,
|
||||
}
|
||||
}
|
||||
|
||||
taskMaps = append(taskMaps, maps.Map{
|
||||
"id": task.Id,
|
||||
"type": task.Type,
|
||||
"isDone": task.IsDone,
|
||||
"isOk": task.IsOk,
|
||||
"error": task.Error,
|
||||
"updatedTime": timeutil.FormatTime("Y-m-d H:i:s", task.UpdatedAt),
|
||||
"cluster": clusterMap,
|
||||
"node": nodeMap,
|
||||
"server": serverMap,
|
||||
"domain": domainMap,
|
||||
})
|
||||
}
|
||||
this.Data["tasks"] = taskMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -46,6 +46,9 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var httpAllDomainMismatchActionCode = serverconfigs.DomainMismatchActionPage
|
||||
var httpAllDomainMismatchActionContentHTML string
|
||||
var httpAllDomainMismatchActionStatusCode = "404"
|
||||
|
||||
var httpAllDomainMismatchActionRedirectURL = ""
|
||||
|
||||
if config.HTTPAll.DomainMismatchAction != nil {
|
||||
httpAllDomainMismatchActionCode = config.HTTPAll.DomainMismatchAction.Code
|
||||
|
||||
@@ -56,6 +59,10 @@ func (this *IndexAction) RunGet(params struct {
|
||||
if statusCode > 0 {
|
||||
httpAllDomainMismatchActionStatusCode = types.String(statusCode)
|
||||
}
|
||||
|
||||
if config.HTTPAll.DomainMismatchAction.Code == serverconfigs.DomainMismatchActionRedirect {
|
||||
httpAllDomainMismatchActionRedirectURL = config.HTTPAll.DomainMismatchAction.Options.GetString("url")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
httpAllDomainMismatchActionContentHTML = `<!DOCTYPE html>
|
||||
@@ -83,6 +90,8 @@ p { color: grey; }
|
||||
this.Data["httpAllDomainMismatchActionContentHTML"] = httpAllDomainMismatchActionContentHTML
|
||||
this.Data["httpAllDomainMismatchActionStatusCode"] = httpAllDomainMismatchActionStatusCode
|
||||
|
||||
this.Data["httpAllDomainMismatchActionRedirectURL"] = httpAllDomainMismatchActionRedirectURL
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -93,12 +102,14 @@ func (this *IndexAction) RunPost(params struct {
|
||||
HttpAllDomainMismatchActionCode string
|
||||
HttpAllDomainMismatchActionContentHTML string
|
||||
HttpAllDomainMismatchActionStatusCode string
|
||||
HttpAllDomainMismatchActionRedirectURL string
|
||||
HttpAllAllowMismatchDomainsJSON []byte
|
||||
HttpAllAllowNodeIP bool
|
||||
HttpAllDefaultDomain string
|
||||
HttpAllNodeIPPageHTML string
|
||||
HttpAllNodeIPShowPage bool
|
||||
HttpAllEnableServerAddrVariable bool
|
||||
HttpAllRequestOriginsWithEncodings bool
|
||||
|
||||
HttpAllDomainAuditingIsOn bool
|
||||
HttpAllDomainAuditingPrompt string
|
||||
@@ -156,11 +167,28 @@ func (this *IndexAction) RunPost(params struct {
|
||||
var domainMisMatchStatusCode = types.Int(domainMisMatchStatusCodeString)
|
||||
|
||||
config.HTTPAll.MatchDomainStrictly = params.HttpAllMatchDomainStrictly
|
||||
|
||||
// validate
|
||||
if config.HTTPAll.MatchDomainStrictly {
|
||||
// validate redirect
|
||||
if params.HttpAllDomainMismatchActionCode == serverconfigs.DomainMismatchActionRedirect {
|
||||
if len(params.HttpAllDomainMismatchActionRedirectURL) == 0 {
|
||||
this.FailField("httpAllDomainMismatchActionRedirectURL", "请输入跳转目标网址URL")
|
||||
return
|
||||
}
|
||||
if !regexp.MustCompile(`(?i)(http|https)://`).MatchString(params.HttpAllDomainMismatchActionRedirectURL) {
|
||||
this.FailField("httpAllDomainMismatchActionRedirectURL", "目标网址URL必须以http://或https://开头")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.HTTPAll.DomainMismatchAction = &serverconfigs.DomainMismatchAction{
|
||||
Code: params.HttpAllDomainMismatchActionCode,
|
||||
Options: maps.Map{
|
||||
"statusCode": domainMisMatchStatusCode,
|
||||
"contentHTML": params.HttpAllDomainMismatchActionContentHTML,
|
||||
"statusCode": domainMisMatchStatusCode, // page
|
||||
"contentHTML": params.HttpAllDomainMismatchActionContentHTML, // page
|
||||
"url": params.HttpAllDomainMismatchActionRedirectURL, // redirect
|
||||
},
|
||||
}
|
||||
|
||||
@@ -190,6 +218,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
config.HTTPAll.ForceLnRequest = params.HttpAllForceLnRequest
|
||||
config.HTTPAll.LnRequestSchedulingMethod = params.HttpAllLnRequestSchedulingMethod
|
||||
config.HTTPAll.EnableServerAddrVariable = params.HttpAllEnableServerAddrVariable
|
||||
config.HTTPAll.RequestOriginsWithEncodings = params.HttpAllRequestOriginsWithEncodings
|
||||
|
||||
// 访问日志
|
||||
config.HTTPAccessLog.IsOn = params.HttpAccessLogIsOn
|
||||
|
||||
@@ -113,6 +113,9 @@ func (this *IndexAction) RunGet(params struct {
|
||||
"autoRemoteStart": cluster.AutoRemoteStart,
|
||||
"autoInstallNftables": cluster.AutoInstallNftables,
|
||||
"autoSystemTuning": cluster.AutoSystemTuning,
|
||||
"autoTrimDisks": cluster.AutoTrimDisks,
|
||||
"maxConcurrentReads": cluster.MaxConcurrentReads,
|
||||
"maxConcurrentWrites": cluster.MaxConcurrentWrites,
|
||||
"sshParams": sshParams,
|
||||
"domainName": fullDomainName,
|
||||
}
|
||||
@@ -141,6 +144,9 @@ func (this *IndexAction) RunPost(params struct {
|
||||
AutoRemoteStart bool
|
||||
AutoInstallNftables bool
|
||||
AutoSystemTuning bool
|
||||
AutoTrimDisks bool
|
||||
MaxConcurrentReads int32
|
||||
MaxConcurrentWrites int32
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
@@ -196,7 +202,10 @@ func (this *IndexAction) RunPost(params struct {
|
||||
AutoRemoteStart: params.AutoRemoteStart,
|
||||
AutoInstallNftables: params.AutoInstallNftables,
|
||||
AutoSystemTuning: params.AutoSystemTuning,
|
||||
AutoTrimDisks: params.AutoTrimDisks,
|
||||
SshParamsJSON: sshParamsJSON,
|
||||
MaxConcurrentReads: params.MaxConcurrentReads,
|
||||
MaxConcurrentWrites: params.MaxConcurrentWrites,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -40,6 +40,7 @@ func init() {
|
||||
// DNS
|
||||
Prefix("/clusters/cluster/settings/dns").
|
||||
GetPost("", new(dns.IndexAction)).
|
||||
Get("/records", new(dns.RecordsAction)).
|
||||
Post("/randomName", new(dns.RandomNameAction)).
|
||||
|
||||
// 系统服务设置
|
||||
|
||||
@@ -45,6 +45,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
ClusterId int64
|
||||
IsOn bool
|
||||
Quality int
|
||||
RequireCache bool
|
||||
MinLengthJSON []byte
|
||||
MaxLengthJSON []byte
|
||||
@@ -59,6 +60,13 @@ func (this *IndexAction) RunPost(params struct {
|
||||
RequireCache: params.RequireCache,
|
||||
}
|
||||
|
||||
if params.Quality < 0 {
|
||||
params.Quality = 0
|
||||
} else if params.Quality > 100 {
|
||||
params.Quality = 100
|
||||
}
|
||||
config.Quality = params.Quality
|
||||
|
||||
if len(params.MinLengthJSON) > 0 {
|
||||
var minLength = &shared.SizeCapacity{}
|
||||
err := json.Unmarshal(params.MinLengthJSON, minLength)
|
||||
@@ -79,6 +87,11 @@ func (this *IndexAction) RunPost(params struct {
|
||||
config.MaxLength = maxLength
|
||||
}
|
||||
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
}
|
||||
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -87,7 +100,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
|
||||
_, err = this.RPC().NodeClusterRPC().UpdateNodeClusterWebPPolicy(this.AdminContext(), &pb.UpdateNodeClusterWebPPolicyRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
WebpPolicyJSON: configJSON,
|
||||
WebPPolicyJSON: configJSON,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -180,7 +180,7 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
|
||||
"name": this.Lang(actionPtr, codes.NodeClusterMenu_SettingWebP),
|
||||
"url": "/clusters/cluster/settings/webp?clusterId=" + clusterId,
|
||||
"isActive": selectedItem == "webp",
|
||||
"isOn": info != nil && info.WebpIsOn,
|
||||
"isOn": info != nil && info.WebPIsOn,
|
||||
})
|
||||
|
||||
items = this.filterMenuItems1(items, info, clusterId, selectedItem, actionPtr)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package clusters
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
@@ -9,6 +11,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
)
|
||||
|
||||
type CreateAction struct {
|
||||
@@ -44,6 +47,18 @@ func (this *CreateAction) RunGet(params struct{}) {
|
||||
}
|
||||
this.Data["totalNodes"] = totalNodesResp.Count
|
||||
|
||||
// 随机子域名
|
||||
var defaultDNSName = "g" + rands.HexString(6) + ".cdn"
|
||||
{
|
||||
var b = make([]byte, 3)
|
||||
_, err = rand.Read(b)
|
||||
if err == nil {
|
||||
defaultDNSName = fmt.Sprintf("g%x.cdn", b)
|
||||
}
|
||||
}
|
||||
this.Data["defaultDNSName"] = defaultDNSName
|
||||
this.Data["dnsName"] = defaultDNSName
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -65,6 +80,9 @@ func (this *CreateAction) RunPost(params struct {
|
||||
SystemdServiceIsOn bool
|
||||
AutoInstallNftables bool
|
||||
AutoSystemTuning bool
|
||||
AutoTrimDisks bool
|
||||
MaxConcurrentReads int32
|
||||
MaxConcurrentWrites int32
|
||||
|
||||
// DNS相关
|
||||
DnsDomainId int64
|
||||
@@ -134,6 +152,9 @@ func (this *CreateAction) RunPost(params struct {
|
||||
GlobalServerConfigJSON: globalServerConfigJSON,
|
||||
AutoInstallNftables: params.AutoInstallNftables,
|
||||
AutoSystemTuning: params.AutoSystemTuning,
|
||||
AutoTrimDisks: params.AutoTrimDisks,
|
||||
MaxConcurrentReads: params.MaxConcurrentReads,
|
||||
MaxConcurrentWrites: params.MaxConcurrentWrites,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -31,7 +31,14 @@ func (this *GrantAction) RunGet(params struct {
|
||||
|
||||
// TODO 处理节点专用的认证
|
||||
|
||||
grant := grantResp.NodeGrant
|
||||
var grant = grantResp.NodeGrant
|
||||
|
||||
var privateKey = grant.PrivateKey
|
||||
const maskLength = 64
|
||||
if len(privateKey) > maskLength+32 {
|
||||
privateKey = privateKey[:maskLength] + strings.Repeat("*", len(privateKey)-maskLength)
|
||||
}
|
||||
|
||||
this.Data["grant"] = maps.Map{
|
||||
"id": grant.Id,
|
||||
"name": grant.Name,
|
||||
@@ -39,7 +46,7 @@ func (this *GrantAction) RunGet(params struct {
|
||||
"methodName": grantutils.FindGrantMethodName(grant.Method, this.LangCode()),
|
||||
"username": grant.Username,
|
||||
"password": strings.Repeat("*", len(grant.Password)),
|
||||
"privateKey": grant.PrivateKey,
|
||||
"privateKey": privateKey,
|
||||
"passphrase": strings.Repeat("*", len(grant.Passphrase)),
|
||||
"description": grant.Description,
|
||||
"su": grant.Su,
|
||||
|
||||
@@ -27,7 +27,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
page := this.NewPage(countResp.Count)
|
||||
var page = this.NewPage(countResp.Count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
grantsResp, err := this.RPC().NodeGrantRPC().ListEnabledNodeGrants(this.AdminContext(), &pb.ListEnabledNodeGrantsRequest{
|
||||
@@ -39,7 +39,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
grantMaps := []maps.Map{}
|
||||
var grantMaps = []maps.Map{}
|
||||
for _, grant := range grantsResp.NodeGrants {
|
||||
// 集群数
|
||||
countClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithNodeGrantId(this.AdminContext(), &pb.CountAllEnabledNodeClustersWithNodeGrantIdRequest{NodeGrantId: grant.Id})
|
||||
@@ -47,7 +47,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countClusters := countClustersResp.Count
|
||||
var countClusters = countClustersResp.Count
|
||||
|
||||
// 节点数
|
||||
countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGrantId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGrantIdRequest{NodeGrantId: grant.Id})
|
||||
@@ -55,7 +55,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countNodes := countNodesResp.Count
|
||||
var countNodes = countNodesResp.Count
|
||||
|
||||
grantMaps = append(grantMaps, maps.Map{
|
||||
"id": grant.Id,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package grants
|
||||
|
||||
import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
@@ -34,15 +36,23 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
|
||||
// TODO 处理节点专用的认证
|
||||
|
||||
grant := grantResp.NodeGrant
|
||||
var grant = grantResp.NodeGrant
|
||||
|
||||
// private key
|
||||
var privateKey = grant.PrivateKey
|
||||
const maskLength = 64
|
||||
if len(privateKey) > maskLength+32 {
|
||||
privateKey = privateKey[:maskLength] + strings.Repeat("*", len(privateKey)-maskLength)
|
||||
}
|
||||
|
||||
this.Data["grant"] = maps.Map{
|
||||
"id": grant.Id,
|
||||
"name": grant.Name,
|
||||
"method": grant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grant.Method, this.LangCode()),
|
||||
"username": grant.Username,
|
||||
"password": grant.Password,
|
||||
"privateKey": grant.PrivateKey,
|
||||
"password": strings.Repeat("*", len(grant.Password)),
|
||||
"privateKey": privateKey,
|
||||
"passphrase": grant.Passphrase,
|
||||
"description": grant.Description,
|
||||
"su": grant.Su,
|
||||
@@ -85,15 +95,17 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 验证私钥
|
||||
var err error
|
||||
if len(params.Passphrase) > 0 {
|
||||
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
|
||||
} else {
|
||||
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
|
||||
}
|
||||
if err != nil {
|
||||
this.Fail("私钥验证失败,请检查格式:" + err.Error())
|
||||
return
|
||||
if !strings.HasSuffix(params.PrivateKey, "******") /* 非掩码 */ {
|
||||
var err error
|
||||
if len(params.Passphrase) > 0 {
|
||||
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
|
||||
} else {
|
||||
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
|
||||
}
|
||||
if err != nil {
|
||||
this.Fail("私钥验证失败,请检查格式:" + err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
this.Fail("请选择正确的认证方式")
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/sizes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
@@ -22,7 +21,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CheckDiskPartitions 检查服务器磁盘空间
|
||||
// CheckDiskPartitions 检查服务器硬盘空间
|
||||
func CheckDiskPartitions(thresholdPercent float64) (path string, usage uint64, usagePercent float64, shouldWarning bool) {
|
||||
partitions, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
@@ -58,7 +57,7 @@ func CheckDiskPartitions(thresholdPercent float64) (path string, usage uint64, u
|
||||
|
||||
stat, _ := disk.Usage(p.Mountpoint)
|
||||
if stat != nil {
|
||||
if stat.Used < 2*uint64(sizes.G) {
|
||||
if stat.Used < (5<<30) || stat.Free > (100<<30) {
|
||||
continue
|
||||
}
|
||||
if stat.UsedPercent > thresholdPercent {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package clusters
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
@@ -107,7 +107,7 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
var isResolved = false
|
||||
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
if iputils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
@@ -145,7 +145,7 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
var isResolved = false
|
||||
if isInstalled && len(defaultRoute) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
if iputils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package domains
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
@@ -65,7 +65,7 @@ func (this *NodesPopupAction) RunGet(params struct {
|
||||
var isResolved = false
|
||||
if len(route.Name) > 0 && len(node.IpAddr) > 0 && len(cluster.DnsName) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
if iputils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
@@ -99,7 +99,7 @@ func (this *NodesPopupAction) RunGet(params struct {
|
||||
var isResolved = false
|
||||
if len(defaultRoute) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
if iputils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
|
||||
@@ -52,9 +52,13 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
Type string
|
||||
|
||||
// DNSPod
|
||||
ParamId string
|
||||
ParamToken string
|
||||
ParamRegion string
|
||||
ParamDNSPodId string
|
||||
ParamDNSPodToken string
|
||||
ParamDNSPodRegion string
|
||||
|
||||
ParamDNSPodAPIType string
|
||||
ParamDNSPodAccessKeyId string
|
||||
ParamDNSPodAccessKeySecret string
|
||||
|
||||
// AliDNS
|
||||
ParamAliDNSAccessKeyId string
|
||||
@@ -89,18 +93,31 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
Field("type", params.Type).
|
||||
Require("请选择服务商厂家")
|
||||
|
||||
apiParams := maps.Map{}
|
||||
var apiParams = maps.Map{}
|
||||
switch params.Type {
|
||||
case "dnspod":
|
||||
params.Must.
|
||||
Field("paramId", params.ParamId).
|
||||
Require("请输入密钥ID").
|
||||
Field("paramToken", params.ParamToken).
|
||||
Require("请输入密钥Token")
|
||||
apiParams["apiType"] = params.ParamDNSPodAPIType
|
||||
switch params.ParamDNSPodAPIType {
|
||||
case "tencentDNS":
|
||||
params.Must.
|
||||
Field("paramDNSPodAccessKeyId", params.ParamDNSPodAccessKeyId).
|
||||
Require("请输入SecretId").
|
||||
Field("paramDNSPodAccessKeySecret", params.ParamDNSPodAccessKeySecret).
|
||||
Require("请输入SecretKey")
|
||||
apiParams["accessKeyId"] = params.ParamDNSPodAccessKeyId
|
||||
apiParams["accessKeySecret"] = params.ParamDNSPodAccessKeySecret
|
||||
apiParams["region"] = params.ParamDNSPodRegion
|
||||
default:
|
||||
params.Must.
|
||||
Field("paramId", params.ParamDNSPodId).
|
||||
Require("请输入密钥ID").
|
||||
Field("paramToken", params.ParamDNSPodToken).
|
||||
Require("请输入密钥Token")
|
||||
|
||||
apiParams["id"] = params.ParamId
|
||||
apiParams["token"] = params.ParamToken
|
||||
apiParams["region"] = params.ParamRegion
|
||||
apiParams["id"] = params.ParamDNSPodId
|
||||
apiParams["token"] = params.ParamDNSPodToken
|
||||
apiParams["region"] = params.ParamDNSPodRegion
|
||||
}
|
||||
case "alidns":
|
||||
params.Must.
|
||||
Field("paramAliDNSAccessKeyId", params.ParamAliDNSAccessKeyId).
|
||||
|
||||
@@ -24,7 +24,10 @@ func (this *ProviderAction) RunGet(params struct {
|
||||
this.Data["pageNo"] = params.Page
|
||||
this.Data["filter"] = params.Filter
|
||||
|
||||
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{DnsProviderId: params.ProviderId})
|
||||
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{
|
||||
DnsProviderId: params.ProviderId,
|
||||
MaskParams: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
|
||||
@@ -22,7 +22,10 @@ func (this *UpdatePopupAction) Init() {
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
ProviderId int64
|
||||
}) {
|
||||
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{DnsProviderId: params.ProviderId})
|
||||
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{
|
||||
DnsProviderId: params.ProviderId,
|
||||
MaskParams: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
@@ -79,9 +82,13 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
Type string
|
||||
|
||||
// DNSPod
|
||||
ParamId string
|
||||
ParamToken string
|
||||
ParamRegion string
|
||||
ParamDNSPodId string
|
||||
ParamDNSPodToken string
|
||||
ParamDNSPodRegion string
|
||||
|
||||
ParamDNSPodAPIType string
|
||||
ParamDNSPodAccessKeyId string
|
||||
ParamDNSPodAccessKeySecret string
|
||||
|
||||
// AliDNS
|
||||
ParamAliDNSAccessKeyId string
|
||||
@@ -118,18 +125,31 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
Field("type", params.Type).
|
||||
Require("请选择服务商厂家")
|
||||
|
||||
apiParams := maps.Map{}
|
||||
var apiParams = maps.Map{}
|
||||
switch params.Type {
|
||||
case "dnspod":
|
||||
params.Must.
|
||||
Field("paramId", params.ParamId).
|
||||
Require("请输入密钥ID").
|
||||
Field("paramToken", params.ParamToken).
|
||||
Require("请输入密钥Token")
|
||||
apiParams["apiType"] = params.ParamDNSPodAPIType
|
||||
switch params.ParamDNSPodAPIType {
|
||||
case "tencentDNS":
|
||||
params.Must.
|
||||
Field("paramDNSPodAccessKeyId", params.ParamDNSPodAccessKeyId).
|
||||
Require("请输入SecretId").
|
||||
Field("paramDNSPodAccessKeySecret", params.ParamDNSPodAccessKeySecret).
|
||||
Require("请输入SecretKey")
|
||||
apiParams["accessKeyId"] = params.ParamDNSPodAccessKeyId
|
||||
apiParams["accessKeySecret"] = params.ParamDNSPodAccessKeySecret
|
||||
apiParams["region"] = params.ParamDNSPodRegion
|
||||
default:
|
||||
params.Must.
|
||||
Field("paramId", params.ParamDNSPodId).
|
||||
Require("请输入密钥ID").
|
||||
Field("paramToken", params.ParamDNSPodToken).
|
||||
Require("请输入密钥Token")
|
||||
|
||||
apiParams["id"] = params.ParamId
|
||||
apiParams["token"] = params.ParamToken
|
||||
apiParams["region"] = params.ParamRegion
|
||||
apiParams["id"] = params.ParamDNSPodId
|
||||
apiParams["token"] = params.ParamDNSPodToken
|
||||
apiParams["region"] = params.ParamDNSPodRegion
|
||||
}
|
||||
case "alidns":
|
||||
params.Must.
|
||||
Field("paramAliDNSAccessKeyId", params.ParamAliDNSAccessKeyId).
|
||||
|
||||
@@ -18,8 +18,10 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"net"
|
||||
@@ -121,6 +123,19 @@ func (this *IndexAction) RunGet(params struct {
|
||||
// 删除Cookie
|
||||
loginutils.UnsetCookie(this.Object())
|
||||
|
||||
// 检查单体实例是否已经被初始化
|
||||
{
|
||||
settingResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeStandaloneInstanceInitialized})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if string(settingResp.ValueJSON) == "0" {
|
||||
this.RedirectURL("/initPassword")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -175,7 +190,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelError, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSystemError, err.Error()), this.RequestRemoteIP(), codes.AdminLogin_LogSystemError, []any{err.Error()})
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelError, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSystemError, err.Error()), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogSystemError, []any{err.Error()})
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
@@ -185,7 +200,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
if !resp.IsOk {
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelWarn, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogFailed, params.Username), this.RequestRemoteIP(), codes.AdminLogin_LogFailed, []any{params.Username})
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelWarn, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogFailed, params.Username), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogFailed, []any{params.Username})
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
@@ -222,10 +237,13 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 写入SESSION
|
||||
params.Auth.StoreAdmin(adminId, params.Remember)
|
||||
var localSid = rands.HexString(32)
|
||||
this.Data["localSid"] = localSid
|
||||
this.Data["ip"] = loginutils.RemoteIP(&this.ActionObject)
|
||||
params.Auth.StoreAdmin(adminId, params.Remember, localSid)
|
||||
|
||||
// 记录日志
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSuccess, params.Username), this.RequestRemoteIP(), codes.AdminLogin_LogSuccess, []any{params.Username})
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSuccess, params.Username), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogSuccess, []any{params.Username})
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
@@ -235,7 +253,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
|
||||
// 检查登录区域
|
||||
func (this *IndexAction) checkRegion() bool {
|
||||
var ip = this.RequestRemoteIP()
|
||||
var ip = loginutils.RemoteIP(&this.ActionObject)
|
||||
var result = iplibrary.LookupIP(ip)
|
||||
if result != nil && result.IsOk() && result.CountryId() > 0 && lists.ContainsInt64([]int64{9, 10}, result.CountryId()) {
|
||||
return false
|
||||
|
||||
@@ -10,6 +10,7 @@ func init() {
|
||||
Prefix("").
|
||||
GetPost("/", new(IndexAction)).
|
||||
GetPost("/index/otp", new(OtpAction)).
|
||||
GetPost("/initPassword", new(InitPasswordAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
104
internal/web/actions/default/index/initPassword.go
Normal file
104
internal/web/actions/default/index/initPassword.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type InitPasswordAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *InitPasswordAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *InitPasswordAction) RunGet(params struct{}) {
|
||||
isNotInitialized, err := this.isNotInitialized()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if !isNotInitialized {
|
||||
this.RedirectURL("/")
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["username"] = "admin"
|
||||
this.Data["password"] = ""
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *InitPasswordAction) RunPost(params struct {
|
||||
Username string
|
||||
Password string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
isNotInitialized, err := this.isNotInitialized()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if !isNotInitialized {
|
||||
this.ResponseWriter.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
params.Must.
|
||||
Field("username", params.Username).
|
||||
Require("请输入登录用户名").
|
||||
Match(`^[a-zA-Z0-9_]+$`, "用户名中只能包含英文、数字或下划线").
|
||||
Field("password", params.Password).
|
||||
Require("请输入密码")
|
||||
|
||||
// 查找ID
|
||||
adminResp, err := this.RPC().AdminRPC().FindAdminWithUsername(this.AdminContext(), &pb.FindAdminWithUsernameRequest{Username: "admin" /** 固定的 **/})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if adminResp.Admin == nil {
|
||||
this.Fail("数据错误,请将数据库中的edgeAdmins表中的用户名修改为admin后再试")
|
||||
return
|
||||
}
|
||||
var adminId = adminResp.Admin.Id
|
||||
|
||||
// 修改密码
|
||||
_, err = this.RPC().AdminRPC().UpdateAdminLogin(this.AdminContext(), &pb.UpdateAdminLoginRequest{
|
||||
AdminId: adminId,
|
||||
Username: params.Username,
|
||||
Password: params.Password, // raw
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 修改为初始化完成
|
||||
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
|
||||
Code: systemconfigs.SettingCodeStandaloneInstanceInitialized,
|
||||
ValueJSON: []byte("1"),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
func (this *InitPasswordAction) isNotInitialized() (bool, error) {
|
||||
settingResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeStandaloneInstanceInitialized})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return string(settingResp.ValueJSON) == "0", nil
|
||||
}
|
||||
@@ -3,12 +3,15 @@
|
||||
package loginutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CalculateClientFingerprint 计算客户端指纹
|
||||
@@ -17,8 +20,30 @@ func CalculateClientFingerprint(action *actions.ActionObject) string {
|
||||
}
|
||||
|
||||
// RemoteIP 获取客户端IP
|
||||
// TODO 将来增加是否使用代理设置(即从X-Real-IP中获取IP)
|
||||
func RemoteIP(action *actions.ActionObject) string {
|
||||
securityConfig, _ := configloaders.LoadSecurityConfig()
|
||||
|
||||
if securityConfig != nil {
|
||||
if len(securityConfig.ClientIPHeaderNames) > 0 {
|
||||
var headerNames = regexp.MustCompile(`[,;\s,、;]`).Split(securityConfig.ClientIPHeaderNames, -1)
|
||||
for _, headerName := range headerNames {
|
||||
headerName = http.CanonicalHeaderKey(strings.TrimSpace(headerName))
|
||||
if len(headerName) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var ipValue = action.Request.Header.Get(headerName)
|
||||
if net.ParseIP(ipValue) != nil {
|
||||
return ipValue
|
||||
}
|
||||
}
|
||||
|
||||
if securityConfig.ClientIPHeaderOnly {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ip, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
|
||||
return ip
|
||||
}
|
||||
|
||||
@@ -12,12 +12,14 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"github.com/xlzd/gotp"
|
||||
"time"
|
||||
@@ -131,7 +133,10 @@ func (this *OtpAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 写入SESSION
|
||||
params.Auth.StoreAdmin(adminId, params.Remember)
|
||||
var localSid = rands.HexString(32)
|
||||
this.Data["localSid"] = localSid
|
||||
this.Data["ip"] = loginutils.RemoteIP(&this.ActionObject)
|
||||
params.Auth.StoreAdmin(adminId, params.Remember, localSid)
|
||||
|
||||
// 删除OTP SESSION
|
||||
_, err = this.RPC().LoginSessionRPC().DeleteLoginSession(this.AdminContext(), &pb.DeleteLoginSessionRequest{Sid: sid})
|
||||
@@ -146,7 +151,7 @@ func (this *OtpAction) RunPost(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, this.Lang(codes.AdminLogin_LogOtpVerifiedSuccess), this.RequestRemoteIP(), codes.AdminLogin_LogOtpVerifiedSuccess, nil)
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, this.Lang(codes.AdminLogin_LogOtpVerifiedSuccess), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogOtpVerifiedSuccess, nil)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
|
||||
14
internal/web/actions/default/login/init.go
Normal file
14
internal/web/actions/default/login/init.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Prefix("/login").
|
||||
GetPost("/validate", new(ValidateAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
74
internal/web/actions/default/login/validate.go
Normal file
74
internal/web/actions/default/login/validate.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package login
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"net"
|
||||
)
|
||||
|
||||
type ValidateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ValidateAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *ValidateAction) RunGet(params struct {
|
||||
From string
|
||||
}) {
|
||||
this.Data["from"] = params.From
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *ValidateAction) RunPost(params struct {
|
||||
Must *actions.Must
|
||||
|
||||
LocalSid string
|
||||
Ip string
|
||||
}) {
|
||||
var isOk bool
|
||||
|
||||
defer func() {
|
||||
this.Data["isOk"] = isOk
|
||||
|
||||
if !isOk {
|
||||
loginutils.UnsetCookie(&this.ActionObject)
|
||||
this.Session().Delete()
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}()
|
||||
|
||||
if len(params.LocalSid) == 0 || len(params.LocalSid) != 32 {
|
||||
return
|
||||
}
|
||||
if len(params.Ip) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if net.ParseIP(params.Ip) == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if params.LocalSid == this.Session().GetString("@localSid") {
|
||||
isOk = true
|
||||
|
||||
// renew ip and local sid
|
||||
var newIP = loginutils.RemoteIP(&this.ActionObject)
|
||||
var newLocalSid = rands.HexString(32)
|
||||
|
||||
this.Session().Write("@ip", newIP)
|
||||
this.Session().Write("@localSid", newLocalSid)
|
||||
|
||||
this.Data["ip"] = newIP
|
||||
this.Data["localSid"] = newLocalSid
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -36,31 +36,43 @@ func (this *AddServerNamePopupAction) RunPost(params struct {
|
||||
// 去除空格
|
||||
serverName = regexp.MustCompile(`\s+`).ReplaceAllString(serverName, "")
|
||||
|
||||
// 处理URL
|
||||
if regexp.MustCompile(`^(?i)(http|https|ftp)://`).MatchString(serverName) {
|
||||
u, err := url.Parse(serverName)
|
||||
if err == nil && len(u.Host) > 0 {
|
||||
serverName = u.Host
|
||||
// 是否包含了多个域名
|
||||
var splitReg = regexp.MustCompile(`([,、|,;|])`)
|
||||
if splitReg.MatchString(serverName) {
|
||||
params.ServerNames = strings.Join(splitReg.Split(serverName, -1), "\n")
|
||||
params.Mode = "multiple"
|
||||
} else {
|
||||
// 处理URL
|
||||
if regexp.MustCompile(`^(?i)(http|https|ftp)://`).MatchString(serverName) {
|
||||
u, err := url.Parse(serverName)
|
||||
if err == nil && len(u.Host) > 0 {
|
||||
serverName = u.Host
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 去除端口
|
||||
if regexp.MustCompile(`:\d+$`).MatchString(serverName) {
|
||||
host, _, err := net.SplitHostPort(serverName)
|
||||
if err == nil && len(host) > 0 {
|
||||
serverName = host
|
||||
// 去除端口
|
||||
if regexp.MustCompile(`:\d+$`).MatchString(serverName) {
|
||||
host, _, err := net.SplitHostPort(serverName)
|
||||
if err == nil && len(host) > 0 {
|
||||
serverName = host
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.Must.
|
||||
Field("serverName", serverName).
|
||||
Require("请输入域名")
|
||||
params.Must.
|
||||
Field("serverName", serverName).
|
||||
Require("请输入域名")
|
||||
|
||||
this.Data["serverName"] = maps.Map{
|
||||
"name": serverName,
|
||||
"type": "full",
|
||||
this.Data["serverName"] = maps.Map{
|
||||
"name": serverName,
|
||||
"type": "full",
|
||||
}
|
||||
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
} else if params.Mode == "multiple" {
|
||||
}
|
||||
|
||||
if params.Mode == "multiple" {
|
||||
if len(params.ServerNames) == 0 {
|
||||
this.FailField("serverNames", "请输入至少域名")
|
||||
}
|
||||
@@ -99,8 +111,6 @@ func (this *AddServerNamePopupAction) RunPost(params struct {
|
||||
"type": "full",
|
||||
"subNames": serverNames,
|
||||
}
|
||||
} else {
|
||||
this.Fail("错误的mode参数")
|
||||
}
|
||||
|
||||
this.Success()
|
||||
|
||||
@@ -99,11 +99,24 @@ func (this *TaskAction) readTask(taskId int64) (ok bool) {
|
||||
})
|
||||
}
|
||||
|
||||
// 集群信息
|
||||
var clusterMap = maps.Map{
|
||||
"id": 0,
|
||||
"name": "",
|
||||
}
|
||||
if key.NodeCluster != nil {
|
||||
clusterMap = maps.Map{
|
||||
"id": key.NodeCluster.Id,
|
||||
"name": key.NodeCluster.Name,
|
||||
}
|
||||
}
|
||||
|
||||
keyMaps = append(keyMaps, maps.Map{
|
||||
"key": key.Key,
|
||||
"isDone": key.IsDone,
|
||||
"isDoing": key.IsDoing,
|
||||
"errors": errorMaps,
|
||||
"cluster": clusterMap,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
MaxSizeJSON []byte
|
||||
FetchTimeoutJSON []byte
|
||||
SyncCompressionCache bool
|
||||
EnableMMAP bool
|
||||
|
||||
Description string
|
||||
IsOn bool
|
||||
@@ -97,6 +98,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
OpenFileCache: openFileCacheConfig,
|
||||
EnableSendfile: params.FileEnableSendfile,
|
||||
MinFreeSize: minFreeSize,
|
||||
EnableMMAP: params.EnableMMAP,
|
||||
}
|
||||
case serverconfigs.CachePolicyStorageMemory:
|
||||
options = &serverconfigs.HTTPMemoryCacheStorage{}
|
||||
|
||||
@@ -40,13 +40,23 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
// fix min free size
|
||||
if cachePolicy.Type == serverconfigs.CachePolicyStorageFile && cachePolicy.Options != nil {
|
||||
_, ok := cachePolicy.Options["minFreeSize"]
|
||||
if !ok {
|
||||
cachePolicy.Options["minFreeSize"] = &shared.SizeCapacity{
|
||||
Count: 0,
|
||||
Unit: shared.SizeCapacityUnitGB,
|
||||
// fix min free size
|
||||
{
|
||||
_, ok := cachePolicy.Options["minFreeSize"]
|
||||
if !ok {
|
||||
cachePolicy.Options["minFreeSize"] = &shared.SizeCapacity{
|
||||
Count: 0,
|
||||
Unit: shared.SizeCapacityUnitGB,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fix enableMMAP
|
||||
{
|
||||
_, ok := cachePolicy.Options["enableMMAP"]
|
||||
if !ok {
|
||||
cachePolicy.Options["enableMMAP"] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +87,8 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
SyncCompressionCache bool
|
||||
FetchTimeoutJSON []byte
|
||||
|
||||
EnableMMAP bool
|
||||
|
||||
Description string
|
||||
IsOn bool
|
||||
|
||||
@@ -137,6 +149,7 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
OpenFileCache: openFileCacheConfig,
|
||||
EnableSendfile: params.FileEnableSendfile,
|
||||
MinFreeSize: minFreeSize,
|
||||
EnableMMAP: params.EnableMMAP,
|
||||
}
|
||||
case serverconfigs.CachePolicyStorageMemory:
|
||||
options = &serverconfigs.HTTPMemoryCacheStorage{}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (this *CreateSetPopupAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
// 所有可选的动作
|
||||
actionMaps := []maps.Map{}
|
||||
var actionMaps = []maps.Map{}
|
||||
for _, action := range firewallconfigs.AllActions {
|
||||
actionMaps = append(actionMaps, maps.Map{
|
||||
"name": action.Name,
|
||||
@@ -64,6 +64,9 @@ func (this *CreateSetPopupAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["actions"] = actionMaps
|
||||
|
||||
// 是否为全局
|
||||
this.Data["isGlobalPolicy"] = firewallPolicy.ServerId == 0
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count := countResp.Count
|
||||
page := this.NewPage(count)
|
||||
var count = countResp.Count
|
||||
var page = this.NewPage(count)
|
||||
|
||||
listResp, err := this.RPC().HTTPFirewallPolicyRPC().ListEnabledHTTPFirewallPolicies(this.AdminContext(), &pb.ListEnabledHTTPFirewallPoliciesRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
@@ -44,10 +44,10 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
policyMaps := []maps.Map{}
|
||||
var policyMaps = []maps.Map{}
|
||||
for _, policy := range listResp.HttpFirewallPolicies {
|
||||
countInbound := 0
|
||||
countOutbound := 0
|
||||
var countInbound = 0
|
||||
var countOutbound = 0
|
||||
if len(policy.InboundJSON) > 0 {
|
||||
inboundConfig := &firewallconfigs.HTTPFirewallInboundConfig{}
|
||||
err = json.Unmarshal(policy.InboundJSON, inboundConfig)
|
||||
@@ -72,7 +72,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countClusters := countClustersResp.Count
|
||||
var countClusters = countClustersResp.Count
|
||||
|
||||
// mode
|
||||
if len(policy.Mode) == 0 {
|
||||
|
||||
@@ -99,6 +99,7 @@ func (this *ListsAction) RunGet(params struct {
|
||||
|
||||
itemMaps = append(itemMaps, maps.Map{
|
||||
"id": item.Id,
|
||||
"value": item.Value,
|
||||
"ipFrom": item.IpFrom,
|
||||
"ipTo": item.IpTo,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d", item.CreatedAt),
|
||||
|
||||
@@ -57,6 +57,7 @@ func (this *TestAction) RunPost(params struct {
|
||||
if resp.IpItem != nil {
|
||||
resultMap["item"] = maps.Map{
|
||||
"id": resp.IpItem.Id,
|
||||
"value": resp.IpItem.Value,
|
||||
"ipFrom": resp.IpItem.IpFrom,
|
||||
"ipTo": resp.IpItem.IpTo,
|
||||
"reason": resp.IpItem.Reason,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ipadmin
|
||||
|
||||
import ( "github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
@@ -32,6 +33,7 @@ func (this *UpdateIPPopupAction) RunGet(params struct {
|
||||
|
||||
this.Data["item"] = maps.Map{
|
||||
"id": item.Id,
|
||||
"value": item.Value,
|
||||
"ipFrom": item.IpFrom,
|
||||
"ipTo": item.IpTo,
|
||||
"expiredAt": item.ExpiredAt,
|
||||
@@ -49,8 +51,7 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
|
||||
FirewallPolicyId int64
|
||||
ItemId int64
|
||||
|
||||
IpFrom string
|
||||
IpTo string
|
||||
Value string
|
||||
ExpiredAt int64
|
||||
Reason string
|
||||
Type string
|
||||
@@ -62,47 +63,25 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAF_LogUpdateIPFromWAFPolicy, params.FirewallPolicyId, params.ItemId)
|
||||
|
||||
// TODO 校验ItemId所属用户
|
||||
|
||||
switch params.Type {
|
||||
case "ipv4":
|
||||
case "ip":
|
||||
// 校验IP格式
|
||||
params.Must.
|
||||
Field("ipFrom", params.IpFrom).
|
||||
Require("请输入开始IP")
|
||||
Field("value", params.Value).
|
||||
Require("请输入IP或IP段")
|
||||
|
||||
// 校验IP格式(ipFrom/ipTo)
|
||||
var ipFromLong uint64
|
||||
if !utils.IsIPv4(params.IpFrom) {
|
||||
this.Fail("请输入正确的开始IP")
|
||||
}
|
||||
ipFromLong = utils.IP2Long(params.IpFrom)
|
||||
|
||||
var ipToLong uint64
|
||||
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
|
||||
this.Fail("请输入正确的结束IP")
|
||||
}
|
||||
ipToLong = utils.IP2Long(params.IpTo)
|
||||
|
||||
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
|
||||
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
|
||||
}
|
||||
case "ipv6":
|
||||
params.Must.
|
||||
Field("ipFrom", params.IpFrom).
|
||||
Require("请输入IP")
|
||||
|
||||
// 校验IP格式(ipFrom)
|
||||
if !utils.IsIPv6(params.IpFrom) {
|
||||
this.Fail("请输入正确的IPv6地址")
|
||||
_, _, _, ok := utils.ParseIPValue(params.Value)
|
||||
if !ok {
|
||||
this.FailField("value", "请输入正确的IP格式")
|
||||
return
|
||||
}
|
||||
case "all":
|
||||
params.IpFrom = "0.0.0.0"
|
||||
params.Value = "0.0.0.0"
|
||||
}
|
||||
|
||||
_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
|
||||
IpItemId: params.ItemId,
|
||||
IpFrom: params.IpFrom,
|
||||
IpTo: params.IpTo,
|
||||
Value: params.Value,
|
||||
ExpiredAt: params.ExpiredAt,
|
||||
Reason: params.Reason,
|
||||
Type: params.Type,
|
||||
|
||||
@@ -95,7 +95,9 @@ func (this *PolicyAction) RunGet(params struct {
|
||||
"modeInfo": firewallconfigs.FindFirewallMode(firewallPolicy.Mode),
|
||||
"groups": internalGroups,
|
||||
"blockOptions": firewallPolicy.BlockOptions,
|
||||
"pageOptions": firewallPolicy.PageOptions,
|
||||
"captchaOptions": firewallPolicy.CaptchaOptions,
|
||||
"jsCookieOptions": firewallPolicy.JSCookieOptions,
|
||||
"useLocalFirewall": firewallPolicy.UseLocalFirewall,
|
||||
"synFlood": firewallPolicy.SYNFlood,
|
||||
"log": firewallPolicy.Log,
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
@@ -34,13 +33,19 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
// block options
|
||||
if firewallPolicy.BlockOptions == nil {
|
||||
firewallPolicy.BlockOptions = &firewallconfigs.HTTPFirewallBlockAction{
|
||||
StatusCode: http.StatusForbidden,
|
||||
Body: "Blocked By WAF",
|
||||
URL: "",
|
||||
Timeout: 60,
|
||||
}
|
||||
firewallPolicy.BlockOptions = firewallconfigs.NewHTTPFirewallBlockAction()
|
||||
}
|
||||
|
||||
// page options
|
||||
if firewallPolicy.PageOptions == nil {
|
||||
firewallPolicy.PageOptions = firewallconfigs.NewHTTPFirewallPageAction()
|
||||
}
|
||||
|
||||
// jscookie options
|
||||
if firewallPolicy.JSCookieOptions == nil {
|
||||
firewallPolicy.JSCookieOptions = firewallconfigs.NewHTTPFirewallJavascriptCookieAction()
|
||||
}
|
||||
|
||||
// mode
|
||||
@@ -71,7 +76,9 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
"isOn": firewallPolicy.IsOn,
|
||||
"mode": firewallPolicy.Mode,
|
||||
"blockOptions": firewallPolicy.BlockOptions,
|
||||
"pageOptions": firewallPolicy.PageOptions,
|
||||
"captchaOptions": firewallPolicy.CaptchaOptions,
|
||||
"jsCookieOptions": firewallPolicy.JSCookieOptions,
|
||||
"useLocalFirewall": firewallPolicy.UseLocalFirewall,
|
||||
"synFloodConfig": firewallPolicy.SYNFlood,
|
||||
"log": firewallPolicy.Log,
|
||||
@@ -103,20 +110,22 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
FirewallPolicyId int64
|
||||
Name string
|
||||
GroupCodes []string
|
||||
BlockOptionsJSON []byte
|
||||
CaptchaOptionsJSON []byte
|
||||
Description string
|
||||
IsOn bool
|
||||
Mode string
|
||||
UseLocalFirewall bool
|
||||
SynFloodJSON []byte
|
||||
LogJSON []byte
|
||||
MaxRequestBodySize int64
|
||||
DenyCountryHTML string
|
||||
DenyProvinceHTML string
|
||||
FirewallPolicyId int64
|
||||
Name string
|
||||
GroupCodes []string
|
||||
BlockOptionsJSON []byte
|
||||
PageOptionsJSON []byte
|
||||
CaptchaOptionsJSON []byte
|
||||
JsCookieOptionsJSON []byte
|
||||
Description string
|
||||
IsOn bool
|
||||
Mode string
|
||||
UseLocalFirewall bool
|
||||
SynFloodJSON []byte
|
||||
LogJSON []byte
|
||||
MaxRequestBodySize int64
|
||||
DenyCountryHTML string
|
||||
DenyProvinceHTML string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
@@ -128,17 +137,58 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
Require("请输入策略名称")
|
||||
|
||||
// 校验拦截选项JSON
|
||||
var blockOptions = &firewallconfigs.HTTPFirewallBlockAction{}
|
||||
var blockOptions = firewallconfigs.NewHTTPFirewallBlockAction()
|
||||
err := json.Unmarshal(params.BlockOptionsJSON, blockOptions)
|
||||
if err != nil {
|
||||
this.Fail("拦截动作参数校验失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 校验显示页面选项JSON
|
||||
var pageOptions = firewallconfigs.NewHTTPFirewallPageAction()
|
||||
err = json.Unmarshal(params.PageOptionsJSON, pageOptions)
|
||||
if err != nil {
|
||||
this.Fail("校验显示页面动作配置失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
if pageOptions.Status < 100 && pageOptions.Status > 999 {
|
||||
this.Fail("显示页面动作的状态码配置错误:" + types.String(pageOptions.Status))
|
||||
return
|
||||
}
|
||||
|
||||
// 校验验证码选项JSON
|
||||
var captchaOptions = &firewallconfigs.HTTPFirewallCaptchaAction{}
|
||||
var captchaOptions = firewallconfigs.NewHTTPFirewallCaptchaAction()
|
||||
err = json.Unmarshal(params.CaptchaOptionsJSON, captchaOptions)
|
||||
if err != nil {
|
||||
this.Fail("验证码动作参数校验失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 检查极验配置
|
||||
if captchaOptions.CaptchaType == firewallconfigs.CaptchaTypeGeeTest || captchaOptions.GeeTestConfig.IsOn {
|
||||
if captchaOptions.CaptchaType == firewallconfigs.CaptchaTypeGeeTest && !captchaOptions.GeeTestConfig.IsOn {
|
||||
this.Fail("人机识别动作配置的默认验证方式为极验-行为验,所以需要选择允许用户使用极验")
|
||||
return
|
||||
}
|
||||
|
||||
if len(captchaOptions.GeeTestConfig.CaptchaId) == 0 {
|
||||
this.FailField("geetestCaptchaId", "请输入极验-验证ID")
|
||||
return
|
||||
}
|
||||
if len(captchaOptions.GeeTestConfig.CaptchaKey) == 0 {
|
||||
this.FailField("geetestCaptchaKey", "请输入极验-验证Key")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 校验JSCookie选项JSON
|
||||
var jsCookieOptions = firewallconfigs.NewHTTPFirewallJavascriptCookieAction()
|
||||
if len(params.JsCookieOptionsJSON) > 0 {
|
||||
err = json.Unmarshal(params.JsCookieOptionsJSON, jsCookieOptions)
|
||||
if err != nil {
|
||||
this.Fail("JSCookie动作参数校验失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 最大内容尺寸
|
||||
@@ -153,7 +203,9 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
Description: params.Description,
|
||||
FirewallGroupCodes: params.GroupCodes,
|
||||
BlockOptionsJSON: params.BlockOptionsJSON,
|
||||
PageOptionsJSON: params.PageOptionsJSON,
|
||||
CaptchaOptionsJSON: params.CaptchaOptionsJSON,
|
||||
JsCookieOptionsJSON: params.JsCookieOptionsJSON,
|
||||
Mode: params.Mode,
|
||||
UseLocalFirewall: params.UseLocalFirewall,
|
||||
SynFloodJSON: params.SynFloodJSON,
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"strings"
|
||||
@@ -101,7 +100,6 @@ func (this *CreateAction) RunPost(params struct {
|
||||
var httpsConfig *serverconfigs.HTTPSProtocolConfig = nil
|
||||
var tcpConfig *serverconfigs.TCPProtocolConfig = nil
|
||||
var tlsConfig *serverconfigs.TLSProtocolConfig = nil
|
||||
var unixConfig *serverconfigs.UnixProtocolConfig = nil
|
||||
var udpConfig *serverconfigs.UDPProtocolConfig = nil
|
||||
var webId int64 = 0
|
||||
|
||||
@@ -429,14 +427,6 @@ func (this *CreateAction) RunPost(params struct {
|
||||
}
|
||||
req.TlsJSON = data
|
||||
}
|
||||
if unixConfig != nil {
|
||||
data, err := json.Marshal(unixConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
req.UnixJSON = data
|
||||
}
|
||||
if udpConfig != nil {
|
||||
data, err := json.Marshal(udpConfig)
|
||||
if err != nil {
|
||||
@@ -454,15 +444,16 @@ func (this *CreateAction) RunPost(params struct {
|
||||
|
||||
// 开启访问日志和Websocket
|
||||
if params.ServerType == serverconfigs.ServerTypeHTTPProxy {
|
||||
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), serverId)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
} else {
|
||||
// 访问日志
|
||||
if params.AccessLogIsOn {
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.AdminContext(), &pb.UpdateHTTPWebAccessLogRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
AccessLogJSON: []byte(`{
|
||||
webConfig, findErr := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), serverId)
|
||||
if findErr != nil {
|
||||
this.ErrorPage(findErr)
|
||||
return
|
||||
}
|
||||
// 访问日志
|
||||
if params.AccessLogIsOn {
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.AdminContext(), &pb.UpdateHTTPWebAccessLogRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
AccessLogJSON: []byte(`{
|
||||
"isPrior": false,
|
||||
"isOn": true,
|
||||
"fields": [1, 2, 6, 7],
|
||||
@@ -477,90 +468,94 @@ func (this *CreateAction) RunPost(params struct {
|
||||
|
||||
"firewallOnly": false
|
||||
}`),
|
||||
})
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// websocket
|
||||
if params.WebsocketIsOn {
|
||||
createWebSocketResp, err := this.RPC().HTTPWebsocketRPC().CreateHTTPWebsocket(this.AdminContext(), &pb.CreateHTTPWebsocketRequest{
|
||||
HandshakeTimeoutJSON: []byte(`{
|
||||
// websocket
|
||||
if params.WebsocketIsOn {
|
||||
createWebSocketResp, err := this.RPC().HTTPWebsocketRPC().CreateHTTPWebsocket(this.AdminContext(), &pb.CreateHTTPWebsocketRequest{
|
||||
HandshakeTimeoutJSON: []byte(`{
|
||||
"count": 30,
|
||||
"unit": "second"
|
||||
}`),
|
||||
AllowAllOrigins: true,
|
||||
AllowedOrigins: nil,
|
||||
RequestSameOrigin: true,
|
||||
RequestOrigin: "",
|
||||
})
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
} else {
|
||||
websocketId := createWebSocketResp.WebsocketId
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebWebsocket(this.AdminContext(), &pb.UpdateHTTPWebWebsocketRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
WebsocketJSON: []byte(` {
|
||||
AllowAllOrigins: true,
|
||||
AllowedOrigins: nil,
|
||||
RequestSameOrigin: true,
|
||||
RequestOrigin: "",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
websocketId := createWebSocketResp.WebsocketId
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebWebsocket(this.AdminContext(), &pb.UpdateHTTPWebWebsocketRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
WebsocketJSON: []byte(`{
|
||||
"isPrior": false,
|
||||
"isOn": true,
|
||||
"websocketId": ` + types.String(websocketId) + `
|
||||
}`),
|
||||
})
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// cache
|
||||
if params.CacheIsOn {
|
||||
var cacheConfig = &serverconfigs.HTTPCacheConfig{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
AddStatusHeader: true,
|
||||
PurgeIsOn: false,
|
||||
PurgeKey: "",
|
||||
CacheRefs: []*serverconfigs.HTTPCacheRef{},
|
||||
}
|
||||
cacheConfigJSON, err := json.Marshal(cacheConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.AdminContext(), &pb.UpdateHTTPWebCacheRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
CacheJSON: cacheConfigJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
// cache
|
||||
if params.CacheIsOn {
|
||||
var cacheConfig = &serverconfigs.HTTPCacheConfig{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
AddStatusHeader: true,
|
||||
PurgeIsOn: false,
|
||||
PurgeKey: "",
|
||||
CacheRefs: []*serverconfigs.HTTPCacheRef{},
|
||||
}
|
||||
|
||||
// waf
|
||||
if params.WafIsOn {
|
||||
var firewallRef = &firewallconfigs.HTTPFirewallRef{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
FirewallPolicyId: 0,
|
||||
}
|
||||
firewallRefJSON, err := json.Marshal(firewallRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFirewall(this.AdminContext(), &pb.UpdateHTTPWebFirewallRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
FirewallJSON: firewallRefJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
cacheConfigJSON, err := json.Marshal(cacheConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.AdminContext(), &pb.UpdateHTTPWebCacheRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
CacheJSON: cacheConfigJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// remoteAddr
|
||||
// waf
|
||||
if params.WafIsOn {
|
||||
var firewallRef = &firewallconfigs.HTTPFirewallRef{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
FirewallPolicyId: 0,
|
||||
}
|
||||
firewallRefJSON, err := json.Marshal(firewallRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFirewall(this.AdminContext(), &pb.UpdateHTTPWebFirewallRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
FirewallJSON: firewallRefJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// remoteAddr
|
||||
{
|
||||
var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{
|
||||
IsOn: true,
|
||||
Value: "${rawRemoteAddr}",
|
||||
@@ -583,26 +578,26 @@ func (this *CreateAction) RunPost(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 统计
|
||||
if params.StatIsOn {
|
||||
var statConfig = &serverconfigs.HTTPStatRef{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
}
|
||||
statJSON, err := json.Marshal(statConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebStat(this.AdminContext(), &pb.UpdateHTTPWebStatRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
StatJSON: statJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
// 统计
|
||||
if params.StatIsOn {
|
||||
var statConfig = &serverconfigs.HTTPStatRef{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
}
|
||||
statJSON, err := json.Marshal(statConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebStat(this.AdminContext(), &pb.UpdateHTTPWebStatRequest{
|
||||
HttpWebId: webConfig.Id,
|
||||
StatJSON: statJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
internal/web/actions/default/servers/deleteServers.go
Normal file
28
internal/web/actions/default/servers/deleteServers.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package servers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
// DeleteServersAction 删除一组网站
|
||||
type DeleteServersAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteServersAction) RunPost(params struct {
|
||||
ServerIds []int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.Server_LogDeleteServers)
|
||||
|
||||
_, err := this.RPC().ServerRPC().DeleteServers(this.AdminContext(), &pb.DeleteServersRequest{ServerIds: params.ServerIds})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -108,14 +108,6 @@ func (this *IndexAction) RunGet(params struct {
|
||||
})
|
||||
}
|
||||
}
|
||||
if config.Unix != nil && config.Unix.IsOn {
|
||||
for _, listen := range config.Unix.Listen {
|
||||
portMaps = append(portMaps, maps.Map{
|
||||
"protocol": listen.Protocol,
|
||||
"portRange": listen.Host,
|
||||
})
|
||||
}
|
||||
}
|
||||
if config.UDP != nil && config.UDP.IsOn {
|
||||
for _, listen := range config.UDP.Listen {
|
||||
portMaps = append(portMaps, maps.Map{
|
||||
|
||||
@@ -53,12 +53,16 @@ func (this *IndexAction) RunPost(params struct {
|
||||
defer this.CreateLogInfo(codes.ServerCache_LogUpdateCacheSettings, params.WebId)
|
||||
|
||||
// 校验配置
|
||||
cacheConfig := &serverconfigs.HTTPCacheConfig{}
|
||||
var cacheConfig = &serverconfigs.HTTPCacheConfig{}
|
||||
err := json.Unmarshal(params.CacheJSON, cacheConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 分组不支持主域名
|
||||
cacheConfig.Key = nil
|
||||
|
||||
err = cacheConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("检查配置失败:" + err.Error())
|
||||
|
||||
@@ -111,6 +111,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
Retry40X: reverseProxyConfig.Retry40X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -111,6 +111,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
Retry40X: reverseProxyConfig.Retry40X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -109,6 +109,7 @@ func (this *AllowListAction) RunGet(params struct {
|
||||
|
||||
itemMaps = append(itemMaps, maps.Map{
|
||||
"id": item.Id,
|
||||
"value": item.Value,
|
||||
"ipFrom": item.IpFrom,
|
||||
"ipTo": item.IpTo,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d", item.CreatedAt),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user