init
This commit is contained in:
commit
1ea024e28f
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
bin
|
||||
.idea
|
115
aes/aes.go
Normal file
115
aes/aes.go
Normal file
@ -0,0 +1,115 @@
|
||||
package aes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"b612.me/starcrypto"
|
||||
"b612.me/staros"
|
||||
)
|
||||
|
||||
func EncodeStr(str string, key []byte) string {
|
||||
if len(key) < 16 {
|
||||
key = starcrypto.Md5(key)
|
||||
} else {
|
||||
key = key[:len(key)/16*16]
|
||||
if len(key) > 32 {
|
||||
key = key[:32]
|
||||
}
|
||||
}
|
||||
ensdata := starcrypto.AesEncryptCFBNoBlock([]byte(str), key)
|
||||
return starcrypto.Base91EncodeToString(ensdata)
|
||||
}
|
||||
|
||||
func DecodeStr(str string, key []byte) string {
|
||||
if len(key) < 16 {
|
||||
key = starcrypto.Md5(key)
|
||||
} else {
|
||||
key = key[:len(key)/16*16]
|
||||
}
|
||||
strtmp := starcrypto.Base91DecodeString(str)
|
||||
str = string(strtmp)
|
||||
return string(starcrypto.AesDecryptCFBNoBlock([]byte(str), key))
|
||||
}
|
||||
|
||||
func EncodeFile(fpath, out string, key []byte) error {
|
||||
if len(key) < 16 {
|
||||
key = starcrypto.Md5(key)
|
||||
} else {
|
||||
key = key[:len(key)/16*16]
|
||||
}
|
||||
if !staros.Exists(fpath) {
|
||||
return errors.New("SrcFile Not Exists")
|
||||
}
|
||||
fpsrc, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fpsrc.Close()
|
||||
fpdst, err := os.Create(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fpdst.Close()
|
||||
bufsize := 1024 * 1024 //1MB
|
||||
stat, _ := fpsrc.Stat()
|
||||
buf := make([]byte, bufsize)
|
||||
var sumAll int64
|
||||
for {
|
||||
n, err := fpsrc.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("已完成:%.2f%%\r", float64(sumAll)/float64(stat.Size())*100)
|
||||
sumAll += int64(n)
|
||||
encodeBytes := starcrypto.AesEncryptCFBNoBlock(buf[:n], key)
|
||||
fpdst.Write(encodeBytes)
|
||||
if err == io.EOF {
|
||||
fmt.Print("已完成:100% \n")
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeFile(fpath, out string, key []byte) error {
|
||||
if len(key) < 16 {
|
||||
key = starcrypto.Md5(key)
|
||||
} else {
|
||||
key = key[:len(key)/16*16]
|
||||
}
|
||||
if !staros.Exists(fpath) {
|
||||
return errors.New("SrcFile Not Exists")
|
||||
}
|
||||
fpsrc, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fpsrc.Close()
|
||||
fpdst, err := os.Create(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fpdst.Close()
|
||||
bufsize := 1024 * 1024 //1MB
|
||||
stat, _ := fpsrc.Stat()
|
||||
buf := make([]byte, bufsize)
|
||||
var sumAll int64
|
||||
for {
|
||||
n, err := fpsrc.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("已完成:%.2f%%\r", float64(sumAll)/float64(stat.Size())*100)
|
||||
sumAll += int64(n)
|
||||
encodeBytes := starcrypto.AesDecryptCFBNoBlock(buf[:n], key)
|
||||
fpdst.Write(encodeBytes)
|
||||
if err == io.EOF {
|
||||
fmt.Print("已完成:100% \n")
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
20
aes/aes_test.go
Normal file
20
aes/aes_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package aes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"b612.me/starcrypto"
|
||||
)
|
||||
|
||||
func Test_EncodeStr(t *testing.T) {
|
||||
//fmt.Println(EncodeStr("我喜欢你", "sakurasaiteruyogugugug"))
|
||||
}
|
||||
|
||||
func Test_DecodeStr(t *testing.T) {
|
||||
//fmt.Println(DecodeStr("Z_8aILbog@Kjm$P", "sakurasaiteruyogugugug"))
|
||||
}
|
||||
|
||||
func Test_Base91(t *testing.T) {
|
||||
fmt.Println(starcrypto.Base91EncodeToString([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}))
|
||||
}
|
93
aes/cmd.go
Normal file
93
aes/cmd.go
Normal file
@ -0,0 +1,93 @@
|
||||
package aes
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"b612.me/starcrypto"
|
||||
|
||||
"b612.me/starlog"
|
||||
"b612.me/staros"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "aes-cfb",
|
||||
Short: "使用aes-cfb处理文件或字符串",
|
||||
Long: "使用aes-cfb处理文件或字符串",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
var err error
|
||||
ok, _ := this.Flags().GetBool("file")
|
||||
de, _ := this.Flags().GetBool("decode")
|
||||
key, _ := this.Flags().GetString("key")
|
||||
keyfile, _ := this.Flags().GetString("keyfile")
|
||||
if len(key) == 0 && len(keyfile) == 0 {
|
||||
starlog.Errorln("请指定指定Key或KeyFile")
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(key) != 0 && len(keyfile) != 0 {
|
||||
starlog.Errorln("不能同时指定Key与KeyFile")
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(args) != 1 {
|
||||
starlog.Criticalln("参数不足,请输入文件地址或字符串")
|
||||
this.Help()
|
||||
os.Exit(2)
|
||||
}
|
||||
var byteKey []byte
|
||||
if len(key) != 0 {
|
||||
byteKey = []byte(key)
|
||||
} else {
|
||||
if !staros.Exists(keyfile) {
|
||||
starlog.Errorln("keyfile不存在:", keyfile)
|
||||
os.Exit(3)
|
||||
}
|
||||
fmt.Println("读取Key文件中……")
|
||||
tmpstr, err := starcrypto.FileSum(keyfile, "sha256", func(ok float64) { fmt.Printf("完成读取%.2f\r", ok) })
|
||||
if err != nil {
|
||||
starlog.Errorln("keyfile读取失败:", err)
|
||||
os.Exit(4)
|
||||
}
|
||||
fmt.Println()
|
||||
byteKey, _ = hex.DecodeString(tmpstr)
|
||||
}
|
||||
if ok {
|
||||
path, _ := this.Flags().GetString("out")
|
||||
if path == "" {
|
||||
ext := filepath.Ext(args[0])
|
||||
if ext != "" {
|
||||
path = args[0][:len(args[0])-len(ext)] + ".aescfb" + ext
|
||||
} else {
|
||||
path = args[0] + ".aescfb"
|
||||
}
|
||||
}
|
||||
if !de {
|
||||
err = EncodeFile(args[0], path, byteKey)
|
||||
} else {
|
||||
err = DecodeFile(args[0], path, byteKey)
|
||||
}
|
||||
} else {
|
||||
if !de {
|
||||
data := EncodeStr(args[0], byteKey)
|
||||
fmt.Println(data)
|
||||
} else {
|
||||
data := DecodeStr(args[0], byteKey)
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
starlog.Criticalln(err)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().BoolP("file", "f", false, "aes-cfb处理文件")
|
||||
Cmd.Flags().StringP("out", "o", "", "文件加解密输出地址")
|
||||
Cmd.Flags().StringP("key", "k", "", "加解密Key字符串")
|
||||
Cmd.Flags().StringP("keyfile", "s", "", "加解密Key文件(不能与key字符串同时选择)")
|
||||
Cmd.Flags().BoolP("decode", "d", false, "进行aes-cfb解码")
|
||||
}
|
78
attach/attach.go
Normal file
78
attach/attach.go
Normal file
@ -0,0 +1,78 @@
|
||||
package attach
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "attach",
|
||||
Short: "合并多个文件",
|
||||
Long: "合并多个文件",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
src, _ := this.Flags().GetStringArray("src")
|
||||
out, _ := this.Flags().GetString("out")
|
||||
if len(src) < 2 || out == "" {
|
||||
starlog.Criticalln("请输入至少2个输入路径,一个输出路径")
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(runAttach(src, out))
|
||||
},
|
||||
}
|
||||
|
||||
func runAttach(src []string, out string) int {
|
||||
fpOut, err := os.Create(out)
|
||||
if err != nil {
|
||||
starlog.Errorln("Err:无法创建输出文件!", err)
|
||||
return 2
|
||||
}
|
||||
defer fpOut.Close()
|
||||
for _, file := range src {
|
||||
fpFile, err := os.OpenFile(file, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
starlog.Errorln("Err:Cannot Openfile:", err)
|
||||
return 3
|
||||
}
|
||||
stats, err := fpFile.Stat()
|
||||
if err != nil {
|
||||
starlog.Errorln("Err:Cannot Get File Stats:", err)
|
||||
return 4
|
||||
}
|
||||
if stats.Size() == 0 {
|
||||
starlog.Warningf("文件:%s为空文件,跳过!\n", stats.Name())
|
||||
continue
|
||||
}
|
||||
buf := make([]byte, 65535)
|
||||
sumAll := 0
|
||||
for {
|
||||
n, err := fpFile.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
starlog.Errorln("Err:Error Occured While ReadFile:", err)
|
||||
fpFile.Close()
|
||||
return 5
|
||||
}
|
||||
sumAll += n
|
||||
_, errW := fpOut.Write(buf[:n])
|
||||
if errW != nil {
|
||||
starlog.Errorln("Error While Write Data to OutFile", errW)
|
||||
return 6
|
||||
}
|
||||
starlog.StdPrintf([]starlog.Attr{starlog.FgGreen}, "文件%s,已完成:%.2f%%\r", stats.Name(), (float64(sumAll) / float64(stats.Size()) * 100.0000))
|
||||
if err == io.EOF {
|
||||
starlog.StdPrintf([]starlog.Attr{starlog.FgGreen}, "文件:%v,已完成:100.00%% \n", stats.Name())
|
||||
fpFile.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
starlog.StdPrintln([]starlog.Attr{starlog.FgGreen}, "Ok!文件合并完成")
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringArrayP("src", "s", []string{}, "源文件路径")
|
||||
Cmd.Flags().StringP("out", "o", "", "输出文件路径")
|
||||
}
|
68
base64/base64.go
Normal file
68
base64/base64.go
Normal file
@ -0,0 +1,68 @@
|
||||
package base64
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"b612.me/starcrypto"
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "base64",
|
||||
Short: "使用base64处理文件或字符串",
|
||||
Long: "使用base64处理文件或字符串",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
var err error
|
||||
ok, _ := this.Flags().GetBool("file")
|
||||
de, _ := this.Flags().GetBool("decode")
|
||||
if len(args) != 1 {
|
||||
starlog.Criticalln("参数不足,请输入文件地址或字符串")
|
||||
this.Help()
|
||||
return
|
||||
}
|
||||
shell := func(pect float64) {
|
||||
if pect == 100 {
|
||||
fmt.Println("已处理:100.000000%")
|
||||
} else {
|
||||
fmt.Printf("已处理:%f%%\r", pect)
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
path, _ := this.Flags().GetString("out")
|
||||
if path == "" {
|
||||
ext := filepath.Ext(args[0])
|
||||
if ext != "" {
|
||||
path = args[0][:len(args[0])-len(ext)] + ".b64" + ext
|
||||
} else {
|
||||
path = args[0] + ".b64"
|
||||
}
|
||||
}
|
||||
if !de {
|
||||
err = starcrypto.Base64EncodeFile(args[0], path, shell)
|
||||
} else {
|
||||
err = starcrypto.Base64DecodeFile(args[0], path, shell)
|
||||
}
|
||||
} else {
|
||||
if !de {
|
||||
data := starcrypto.Base64Encode([]byte(args[0]))
|
||||
fmt.Println(data)
|
||||
} else {
|
||||
var data []byte
|
||||
data, err = starcrypto.Base64Decode(args[0])
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
starlog.Criticalln(err)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().BoolP("file", "f", false, "base64处理文件")
|
||||
Cmd.Flags().StringP("out", "o", "", "指定加解码输出地址")
|
||||
Cmd.Flags().BoolP("decode", "d", false, "base64解码")
|
||||
}
|
67
base85/base85.go
Normal file
67
base85/base85.go
Normal file
@ -0,0 +1,67 @@
|
||||
package base85
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"b612.me/starcrypto"
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "base85",
|
||||
Short: "使用base85处理文件或字符串",
|
||||
Long: "使用base85处理文件或字符串",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
var err error
|
||||
ok, _ := this.Flags().GetBool("file")
|
||||
de, _ := this.Flags().GetBool("decode")
|
||||
if len(args) != 1 {
|
||||
starlog.Criticalln("参数不足,请输入文件地址或字符串")
|
||||
return
|
||||
}
|
||||
shell := func(pec float64) {
|
||||
if pec == 100 {
|
||||
fmt.Println("已处理:100.000000%")
|
||||
} else {
|
||||
fmt.Printf("已处理:%f%%\r", pec)
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
path, _ := this.Flags().GetString("out")
|
||||
if path == "" {
|
||||
ext := filepath.Ext(args[0])
|
||||
if ext != "" {
|
||||
path = args[0][:len(args[0])-len(ext)] + ".b85" + ext
|
||||
} else {
|
||||
path = args[0] + ".b85"
|
||||
}
|
||||
}
|
||||
if !de {
|
||||
err = starcrypto.Base85EncodeFile(args[0], path, shell)
|
||||
} else {
|
||||
err = starcrypto.Base85DecodeFile(args[0], path, shell)
|
||||
}
|
||||
} else {
|
||||
if !de {
|
||||
data := starcrypto.Base85Encode([]byte(args[0]))
|
||||
fmt.Println(data)
|
||||
} else {
|
||||
var data []byte
|
||||
data, err = starcrypto.Base85Decode(args[0])
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
starlog.Criticalln(err)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().BoolP("file", "f", false, "base85处理文件")
|
||||
Cmd.Flags().StringP("out", "o", "", "指定加解码输出地址")
|
||||
Cmd.Flags().BoolP("decode", "d", false, "base85解码")
|
||||
}
|
35
base91/base91.go
Normal file
35
base91/base91.go
Normal file
@ -0,0 +1,35 @@
|
||||
package base91
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"b612.me/starcrypto"
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "base91",
|
||||
Short: "使用base91处理文件或字符串",
|
||||
Long: "使用base91处理文件或字符串",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
de, _ := this.Flags().GetBool("decode")
|
||||
if len(args) != 1 {
|
||||
starlog.Criticalln("参数不足,请输入文件地址或字符串")
|
||||
return
|
||||
}
|
||||
if !de {
|
||||
data := starcrypto.Base91EncodeToString([]byte(args[0]))
|
||||
fmt.Println(data)
|
||||
} else {
|
||||
var data []byte
|
||||
data = starcrypto.Base91DecodeString(args[0])
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringP("out", "o", "", "指定加解码输出地址")
|
||||
Cmd.Flags().BoolP("decode", "d", false, "base91解码")
|
||||
}
|
38
build.bat
Normal file
38
build.bat
Normal file
@ -0,0 +1,38 @@
|
||||
mkdir bin
|
||||
set GOOS=windows
|
||||
set GOARCH=amd64
|
||||
go build -o .\bin\b612_x86_64.exe -ldflags "-w -s" .
|
||||
upx -9 .\bin\b612_x86_64.exe
|
||||
set GOARCH=386
|
||||
go build -o .\bin\b612_x86.exe -ldflags "-w -s" .
|
||||
upx -9 .\bin\b612_x86.exe
|
||||
set GOARCH=arm64
|
||||
go build -o .\bin\b612_arm64.exe -ldflags "-w -s" .
|
||||
upx -9 .\bin\b612_arm64.exe
|
||||
set GOOS=linux
|
||||
set GOARCH=amd64
|
||||
go build -o .\bin\b612_x86_64 -ldflags "-w -s" .
|
||||
upx -9 .\bin\b612_x86_64
|
||||
set GOARCH=386
|
||||
go build -o .\bin\b612_x86 -ldflags "-w -s" .
|
||||
upx -9 .\bin\b612_x86
|
||||
set GOARCH=arm64
|
||||
go build -o .\bin\b612_arm64 -ldflags "-w -s" .
|
||||
upx -9 .\bin\b612_arm64
|
||||
set GOARCH=mips
|
||||
go build -o .\bin\b612_mips -ldflags "-w -s" .
|
||||
set GOARCH=mipsle
|
||||
go build -o .\bin\b612_mipsle -ldflags "-w -s" .
|
||||
set GOARCH=mips64
|
||||
go build -o .\bin\b612_mips64 -ldflags "-w -s" .
|
||||
set GOARCH=mips64le
|
||||
go build -o .\bin\b612_mips64le -ldflags "-w -s" .
|
||||
set GOOS=darwin
|
||||
set GOARCH=amd64
|
||||
go build -o .\bin\b612_darwin_x64 -ldflags "-w -s" .
|
||||
upx -9 .\bin\b612_darwin_x64
|
||||
set GOARCH=arm64
|
||||
go build -o .\bin\b612_darwin_arm64 -ldflags "-w -s" .
|
||||
upx -9 .\bin\b612_darwin_arm64
|
||||
|
||||
|
46
detach/detach.go
Normal file
46
detach/detach.go
Normal file
@ -0,0 +1,46 @@
|
||||
package detach
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"fmt"
|
||||
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "detach",
|
||||
Short: "分离两个文件",
|
||||
Long: "分离两个文件",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
var src, dst, out string
|
||||
if len(args) == 3 {
|
||||
src = args[0]
|
||||
dst = args[1]
|
||||
out = args[2]
|
||||
} else {
|
||||
src, _ = this.Flags().GetString("src")
|
||||
dst, _ = this.Flags().GetString("dst")
|
||||
out, _ = this.Flags().GetString("out")
|
||||
}
|
||||
num, _ := this.Flags().GetInt("num")
|
||||
if src == "" || dst == "" {
|
||||
starlog.Criticalln("ERROR PATH")
|
||||
this.Help()
|
||||
return
|
||||
}
|
||||
err := starcrypto.Detach(src, num, dst, out)
|
||||
if err != nil {
|
||||
starlog.Criticalln(err.Error)
|
||||
} else {
|
||||
fmt.Println("完成")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringP("src", "s", "", "源文件路径")
|
||||
Cmd.Flags().StringP("dst", "d", "", "目标文件路径1")
|
||||
Cmd.Flags().StringP("out", "o", "", "目标文件路径2")
|
||||
Cmd.Flags().IntP("num", "n", 0, "分割开始字节")
|
||||
}
|
116
df/df.go
Normal file
116
df/df.go
Normal file
@ -0,0 +1,116 @@
|
||||
package df
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"b612.me/staros"
|
||||
"b612.me/wincmd/ntfs/mft"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "df",
|
||||
Short: "分析nfts磁盘文件占用",
|
||||
Long: "分析nfts磁盘文件占用",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
os.Exit(folderSize("./"))
|
||||
}
|
||||
os.Exit(folderSize(args[0]))
|
||||
},
|
||||
}
|
||||
|
||||
func folderSize(path string) int {
|
||||
fullPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
starlog.Criticalln("filepath not a vaild path", path, err)
|
||||
return 1
|
||||
}
|
||||
vol := filepath.VolumeName(fullPath) + `\`
|
||||
fmt.Println(vol)
|
||||
if staros.IsFile(fullPath) {
|
||||
fullPath = filepath.Dir(fullPath)
|
||||
}
|
||||
fmt.Println("consider folder is", fullPath)
|
||||
folderLists, err := ioutil.ReadDir(fullPath)
|
||||
if err != nil {
|
||||
starlog.Criticalln("read folder failed", err)
|
||||
return 2
|
||||
}
|
||||
targetFolders := make(map[string]uint64)
|
||||
for _, v := range folderLists {
|
||||
if v.IsDir() {
|
||||
//fmt.Println(filepath.Join(fullPath, v.Name()))
|
||||
targetFolders[filepath.Join(fullPath, v.Name())] = 0
|
||||
}
|
||||
}
|
||||
fileLists, err := mft.GetFileListsByMft(vol)
|
||||
if err != nil {
|
||||
starlog.Errorln("read mft failed", err)
|
||||
return 3
|
||||
}
|
||||
var totalSize uint64
|
||||
var fc1, fc2 int
|
||||
for _, v := range fileLists {
|
||||
if strings.Contains(v.Path, fullPath) {
|
||||
if v.IsDir {
|
||||
fc2++
|
||||
} else {
|
||||
fc1++
|
||||
}
|
||||
totalSize += v.Aszie
|
||||
}
|
||||
for k, _ := range targetFolders {
|
||||
if strings.Contains(v.Path, k) {
|
||||
targetFolders[k] += v.Aszie
|
||||
}
|
||||
}
|
||||
}
|
||||
getSize := func(size uint64) string {
|
||||
floatSize := float64(size)
|
||||
var sizeC = []string{"byte", "KB", "MB", "GB", "TB"}
|
||||
if floatSize < 1024 {
|
||||
return ""
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
if floatSize/math.Pow(1024, float64(i+1)) < 1024 {
|
||||
return fmt.Sprintf("%.4f%s", floatSize/math.Pow(1024, float64(i+1)), sizeC[i+1])
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
target := sortMapByValue(targetFolders)
|
||||
for _, v := range target {
|
||||
fmt.Printf("%-20s %-10d %s\n", v.Key, v.Value, getSize(v.Value))
|
||||
}
|
||||
fmt.Printf("%-20s %-10d %s\n", fullPath, totalSize, getSize(totalSize))
|
||||
fmt.Println("file count:", fc1, "folder count:", fc2)
|
||||
return 0
|
||||
}
|
||||
|
||||
type Pair struct {
|
||||
Key string
|
||||
Value uint64
|
||||
}
|
||||
type PairList []Pair
|
||||
|
||||
func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
func (p PairList) Len() int { return len(p) }
|
||||
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }
|
||||
|
||||
func sortMapByValue(m map[string]uint64) PairList {
|
||||
p := make(PairList, len(m))
|
||||
i := 0
|
||||
for k, v := range m {
|
||||
p[i] = Pair{k, v}
|
||||
i++
|
||||
}
|
||||
sort.Sort(p)
|
||||
return p
|
||||
}
|
113
dfinder/dfinder.go
Normal file
113
dfinder/dfinder.go
Normal file
@ -0,0 +1,113 @@
|
||||
package dfinder
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"b612.me/staros"
|
||||
"b612.me/wincmd/ntfs/mft"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var dfpath, dfreg, dfoutpath string
|
||||
var dfonlyname, dfshow bool
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringVarP(&dfoutpath, "outpath", "o", "", "outpath")
|
||||
Cmd.Flags().StringVarP(&dfpath, "path", "p", "./", "path you want to search")
|
||||
Cmd.Flags().StringVarP(&dfreg, "regexp", "r", ".*", "search regexp")
|
||||
Cmd.Flags().BoolVarP(&dfonlyname, "only-filter-name", "f", false, "only regexp for name not path")
|
||||
Cmd.Flags().BoolVarP(&dfshow, "show", "s", true, "show on the stdout")
|
||||
}
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "dfinder",
|
||||
Short: "查找nfts磁盘文件",
|
||||
Long: "查找nfts磁盘文件",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
os.Exit(fileFinder(dfpath, dfreg, dfonlyname, dfoutpath, dfshow))
|
||||
},
|
||||
}
|
||||
|
||||
func fileFinder(path string, reg string, onlyFilterName bool, outpath string, show bool) int {
|
||||
fullPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
starlog.Criticalln("filepath not a vaild path", path, err)
|
||||
return 1
|
||||
}
|
||||
vol := filepath.VolumeName(fullPath) + `\`
|
||||
fmt.Println(vol)
|
||||
if staros.IsFile(fullPath) {
|
||||
fullPath = filepath.Dir(fullPath)
|
||||
}
|
||||
fmt.Println("consider folder is", fullPath)
|
||||
|
||||
fileLists, err := mft.GetFileListsByMftFn(vol, func(name string, isFolder bool) bool {
|
||||
if isFolder {
|
||||
return true
|
||||
}
|
||||
if onlyFilterName {
|
||||
if matched, _ := regexp.MatchString(reg, name); matched {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
starlog.Errorln("read mft failed", err)
|
||||
return 3
|
||||
}
|
||||
getSize := func(size uint64) string {
|
||||
floatSize := float64(size)
|
||||
var sizeC = []string{"byte", "KB", "MB", "GB", "TB"}
|
||||
if floatSize < 1024 {
|
||||
return ""
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
if floatSize/math.Pow(1024, float64(i+1)) < 1024 {
|
||||
return fmt.Sprintf("%.4f%s", floatSize/math.Pow(1024, float64(i+1)), sizeC[i+1])
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
fp := new(os.File)
|
||||
if outpath != "" {
|
||||
fp, err = os.Create(outpath)
|
||||
if err != nil {
|
||||
starlog.Criticalln(err)
|
||||
} else {
|
||||
defer fp.Close()
|
||||
fp.WriteString(`名称,路径,大小,大小(格式化),修改时间,是否是文件夹` + "\n")
|
||||
}
|
||||
}
|
||||
newReg := regexp.MustCompile(reg)
|
||||
for _, v := range fileLists {
|
||||
if !strings.Contains(v.Path, fullPath) {
|
||||
continue
|
||||
}
|
||||
if onlyFilterName {
|
||||
if show {
|
||||
fmt.Printf("name:%-10s path:%-20s size:%-10s modTime:%v\n", v.Name, v.Path, getSize(v.Size), v.ModTime)
|
||||
}
|
||||
if fp != nil {
|
||||
fp.WriteString(fmt.Sprintf("%s,%s,%v,%s,%v,%v\n", v.Name, v.Path, v.Size, getSize(v.Size), v.ModTime, v.IsDir))
|
||||
}
|
||||
} else {
|
||||
if newReg.MatchString(v.Path) {
|
||||
if show {
|
||||
fmt.Printf("name:%-10s path:%-20s size:%-10s modTime:%v\n", v.Name, v.Path, getSize(v.Size), v.ModTime)
|
||||
}
|
||||
if fp != nil {
|
||||
fp.WriteString(fmt.Sprintf("%s,%s,%v,%s,%v,%v\n", v.Name, v.Path, v.Size, getSize(v.Size), v.ModTime, v.IsDir))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0
|
||||
}
|
50
ftp/ftp.go
Normal file
50
ftp/ftp.go
Normal file
@ -0,0 +1,50 @@
|
||||
package ftp
|
||||
|
||||
import (
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
filedriver "github.com/goftp/file-driver"
|
||||
"github.com/goftp/server"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var ports int
|
||||
var username, pwd string
|
||||
var path, ip string
|
||||
|
||||
// ftpCmd represents the ftp command
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "ftp",
|
||||
Short: `FTP文件服务器`,
|
||||
Long: `FTP文件服务器`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
path, _ = filepath.Abs(path)
|
||||
factory := &filedriver.FileDriverFactory{
|
||||
RootPath: path,
|
||||
Perm: server.NewSimplePerm("user", "group"),
|
||||
}
|
||||
opts := &server.ServerOpts{
|
||||
Factory: factory,
|
||||
Port: ports,
|
||||
Hostname: ip,
|
||||
Auth: &server.SimpleAuth{Name: username, Password: pwd},
|
||||
}
|
||||
|
||||
log.Printf("Starting ftp server on %v:%v", opts.Hostname, opts.Port)
|
||||
log.Printf("Username %v, Password %v", username, pwd)
|
||||
server := server.NewServer(opts)
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Fatal("Error starting server:", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().IntVarP(&ports, "port", "p", 21, "监听端口")
|
||||
Cmd.Flags().StringVarP(&ip, "ip", "i", "0.0.0.0", "监听地址")
|
||||
Cmd.Flags().StringVarP(&username, "user", "u", "1", "用户名,默认为1")
|
||||
Cmd.Flags().StringVarP(&pwd, "pwd", "k", "1", "密码,默认为1")
|
||||
Cmd.Flags().StringVarP(&path, "folder", "f", "./", "本地文件地址")
|
||||
}
|
95
generate/generate.go
Normal file
95
generate/generate.go
Normal file
@ -0,0 +1,95 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: "生成随机文件",
|
||||
Long: "生成指定大小的随机文件",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
sum, _ := this.Flags().GetInt("sum")
|
||||
num, _ := this.Flags().GetInt("num")
|
||||
cap, _ := this.Flags().GetInt("cap")
|
||||
if len(args) != 1 {
|
||||
this.Help()
|
||||
return
|
||||
}
|
||||
if num <= 0 {
|
||||
fmt.Println("num不合法,不应该小于1!")
|
||||
os.Exit(2)
|
||||
}
|
||||
if sum < 0 {
|
||||
fmt.Println("sum不合法,不应该小于0!")
|
||||
os.Exit(2)
|
||||
}
|
||||
if cap <= 0 {
|
||||
fmt.Println("cap不合法,不应该小于1!")
|
||||
os.Exit(2)
|
||||
}
|
||||
err := FillWithRandom(args[0], num, cap, sum, func(pect float64) {
|
||||
fmt.Printf("文件已处理:%f%%\r", pect)
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("err:" + err.Error())
|
||||
}
|
||||
fmt.Println("文件已处理:100.0000000%")
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().IntP("sum", "s", 3, "随机的种子组数")
|
||||
Cmd.Flags().IntP("num", "n", 1024, "生成的文件大小")
|
||||
Cmd.Flags().IntP("cap", "c", 1048576, "bufcap大小")
|
||||
Cmd.MarkFlagRequired("num")
|
||||
}
|
||||
|
||||
// FillWithRandom 随机写filesize大小的文件,每次buf大小为bufcap,随机bufnum个字符
|
||||
func FillWithRandom(filepath string, filesize int, bufcap int, bufnum int, shell func(float64)) error {
|
||||
var buf [][]byte
|
||||
var buftmp []byte
|
||||
rand.Seed(time.Now().Unix())
|
||||
if bufnum <= 0 {
|
||||
bufnum = 1
|
||||
}
|
||||
if bufcap > filesize {
|
||||
bufcap = filesize
|
||||
}
|
||||
myfile, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer myfile.Close()
|
||||
writer := bufio.NewWriter(myfile)
|
||||
for i := 0; i < bufnum; i++ {
|
||||
buftmp = []byte{}
|
||||
for j := 0; j < bufcap; j++ {
|
||||
buftmp = append(buftmp, byte(rand.Intn(256)))
|
||||
}
|
||||
buf = append(buf, buftmp)
|
||||
}
|
||||
sum := 0
|
||||
for {
|
||||
if filesize-sum < bufcap {
|
||||
writer.Write(buf[rand.Intn(bufnum)][0 : filesize-sum])
|
||||
sum += filesize - sum
|
||||
} else {
|
||||
writer.Write(buf[rand.Intn(bufnum)])
|
||||
sum += bufcap
|
||||
}
|
||||
go shell(float64(sum) / float64(filesize) * 100)
|
||||
if sum >= filesize {
|
||||
break
|
||||
}
|
||||
}
|
||||
writer.Flush()
|
||||
return nil
|
||||
}
|
36
go.mod
Normal file
36
go.mod
Normal file
@ -0,0 +1,36 @@
|
||||
module b612.me/apps/b612
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
b612.me/notify v1.2.4
|
||||
b612.me/starcrypto v0.0.2
|
||||
b612.me/stario v0.0.8
|
||||
b612.me/starlog v1.3.2
|
||||
b612.me/staros v1.1.6
|
||||
b612.me/starssh v0.0.2
|
||||
b612.me/startext v0.0.0-20220314043758-22c6d5e5b1cd
|
||||
b612.me/wincmd v0.0.2
|
||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9
|
||||
github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/spf13/cobra v1.6.1
|
||||
)
|
||||
|
||||
require (
|
||||
b612.me/starmap v1.2.3 // indirect
|
||||
b612.me/starnet v0.1.7 // indirect
|
||||
b612.me/win32api v0.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jlaffaye/ftp v0.1.0 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/pkg/sftp v1.13.4 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.8.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
|
||||
golang.org/x/image v0.6.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/term v0.5.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
)
|
108
go.sum
Normal file
108
go.sum
Normal file
@ -0,0 +1,108 @@
|
||||
b612.me/notify v1.2.4 h1:cjP80V9FeM+ib1DztZdykusakcbjNI4dAB1pXE8U6bo=
|
||||
b612.me/notify v1.2.4/go.mod h1:SlCrG1kPRVhYUrIkwY/j0zAwCU4VeTHubcZoQXW8Anw=
|
||||
b612.me/starcrypto v0.0.2 h1:aRf1HcqK8GqHYxLAhWfFC4W/EqQLEFNEmxsBu3wG30o=
|
||||
b612.me/starcrypto v0.0.2/go.mod h1:hz0xRnfWNpYOlVrIPoGrQOWPibq4YiUZ7qN5tsQbzPo=
|
||||
b612.me/stario v0.0.7/go.mod h1:or4ssWcxQSjMeu+hRKEgtp0X517b3zdlEOAms8Qscvw=
|
||||
b612.me/stario v0.0.8 h1:kaA4pszAKLZJm2D9JmiuYSpgjTeE3VaO74vm+H0vBGM=
|
||||
b612.me/stario v0.0.8/go.mod h1:or4ssWcxQSjMeu+hRKEgtp0X517b3zdlEOAms8Qscvw=
|
||||
b612.me/starlog v1.3.2 h1:bFUJyZEpcOcBwPlzlhPBwlYxq7aDcR8pJNoaDk+SUNE=
|
||||
b612.me/starlog v1.3.2/go.mod h1:bxSvBSzlJoLfrZJ5b9CJFuQaXjFi8PYUbGWitNO1FYA=
|
||||
b612.me/starmap v1.2.3 h1:+ao++KgbSGMA4UzcHm/EXJoukbUudk8t5ac7rjwV9KA=
|
||||
b612.me/starmap v1.2.3/go.mod h1:K+exTSWg8i/taoUyGR6DPW6Ja0k6aIdpcniqByOf4O0=
|
||||
b612.me/starnet v0.1.7 h1:k3CUfYNRolC/xw5Ekus2NVWHlqeykSyAH8USGTPKA5o=
|
||||
b612.me/starnet v0.1.7/go.mod h1:DNC4i/ezgVLlmxnquf8AeljsL4mQ5vAyxh8vGPQqsys=
|
||||
b612.me/staros v1.1.6 h1:m3QaEmPyvPcJVomjWs8cDeauDYFNKv7cLHTiOHClKqM=
|
||||
b612.me/staros v1.1.6/go.mod h1:O657LC3qag4VSsHNmt5RM8gKJvzoEGq8IF8WegcRgq0=
|
||||
b612.me/starssh v0.0.2 h1:cYlrXjd7ZTesdZG+7XcoLsEEMROaeWMTYonScBLnvyY=
|
||||
b612.me/starssh v0.0.2/go.mod h1:1gvG/GT5Y5EvOx9ZKnLFUa+wOX20HaqS1IuTnU7BOlk=
|
||||
b612.me/startext v0.0.0-20220314043758-22c6d5e5b1cd h1:EsmnczYZhOV8JTxD/m0N0qBjfZN8JuLNrTJ6z3S8YqA=
|
||||
b612.me/startext v0.0.0-20220314043758-22c6d5e5b1cd/go.mod h1:yKdeLQHZ3scqyjw1ZODCoL+hLmkOp2eu5riP4agraz8=
|
||||
b612.me/win32api v0.0.1 h1:vLFB1xhO6pd9+zB2EyaapKB459Urv3v+C1YwgwOFEWo=
|
||||
b612.me/win32api v0.0.1/go.mod h1:MHu0JBQjzxQ2yxpZPUBbn5un45o67eF5iWKa4Q9e0yE=
|
||||
b612.me/wincmd v0.0.2 h1:Ub1WtelVT6a3vD4B6zDYo3UPO/t9ymnI3x1dQPJcrGw=
|
||||
b612.me/wincmd v0.0.2/go.mod h1:bwpyCKfSDY8scSMo3Lrd0Qnqvpz7/CILL7oodfG0wgo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9 h1:cC0Hbb+18DJ4i6ybqDybvj4wdIDS4vnD0QEci98PgM8=
|
||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9/go.mod h1:GpOj6zuVBG3Inr9qjEnuVTgBlk2lZ1S9DcoFiXWyKss=
|
||||
github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42 h1:JdOp2qR5PF4O75tzHeqrwnDDv8oHDptWyTbyYS4fD8E=
|
||||
github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jlaffaye/ftp v0.1.0 h1:DLGExl5nBoSFoNshAUHwXAezXwXBvFdx7/qwhucWNSE=
|
||||
github.com/jlaffaye/ftp v0.1.0/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/pkg/sftp v1.13.4 h1:Lb0RYJCmgUcBgZosfoi9Y9sbl6+LJgOIgk/2Y4YjMFg=
|
||||
github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4=
|
||||
golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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=
|
75
hash/hash.go
Normal file
75
hash/hash.go
Normal file
@ -0,0 +1,75 @@
|
||||
package hash
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "hash",
|
||||
Short: "多种方法哈希值校验",
|
||||
Long: "进行多种方法哈希值校验",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
var cumethod, method []string
|
||||
var result = make(map[string]string)
|
||||
var err error
|
||||
cumethod = []string{"md5", "crc32", "sha512", "sha384", "sha256", "sha224", "sha1"}
|
||||
if ok, _ := this.Flags().GetBool("all"); ok {
|
||||
method = cumethod
|
||||
} else {
|
||||
if len(args) == 0 {
|
||||
this.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, v := range cumethod {
|
||||
if ok, _ := this.Flags().GetBool(v); ok {
|
||||
method = append(method, v)
|
||||
}
|
||||
}
|
||||
if len(method) == 0 {
|
||||
method = append(method, "md5")
|
||||
}
|
||||
}
|
||||
if ok, _ := this.Flags().GetBool("file"); ok {
|
||||
result, err = starcrypto.FileSumAll(args[0], method, func(pect float64) {
|
||||
if pect != 100.0 {
|
||||
fmt.Printf("校验已完成:%f%%\r", pect)
|
||||
} else {
|
||||
fmt.Printf("校验已完成:%f%%\n", pect)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var bres map[string][]byte
|
||||
bres, err = starcrypto.SumAll([]byte(args[0]), method)
|
||||
if err == nil {
|
||||
for k, v := range bres {
|
||||
result[k] = hex.EncodeToString(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
starlog.Criticalln("错误:" + err.Error())
|
||||
}
|
||||
for _, v := range method {
|
||||
fmt.Printf("%s:%s\n", v, result[v])
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().BoolP("all", "a", false, "使用所有的校验方法")
|
||||
Cmd.Flags().BoolP("file", "f", false, "对指定文件进行校验")
|
||||
Cmd.Flags().BoolP("md5", "m", false, "进行MD5校验(默认)")
|
||||
Cmd.Flags().BoolP("crc32", "c", false, "进行CRC32校验")
|
||||
Cmd.Flags().BoolP("sha512", "s", false, "进行SHA512校验")
|
||||
Cmd.Flags().Bool("sha384", false, "进行SHA384校验")
|
||||
Cmd.Flags().Bool("sha256", false, "进行SHA256校验")
|
||||
Cmd.Flags().Bool("sha224", false, "进行SHA224校验")
|
||||
Cmd.Flags().Bool("sha1", false, "进行SHA1校验")
|
||||
}
|
15
httpreverse/cfg.ini
Normal file
15
httpreverse/cfg.ini
Normal file
@ -0,0 +1,15 @@
|
||||
[web1]
|
||||
addr=0.0.0.0
|
||||
port=9999
|
||||
key=C:\\tech\b612.me.key
|
||||
cert=C:\\tech\b612.me.cer
|
||||
enablessl=true
|
||||
reverse=/::https://www.b612.me
|
||||
replace=www.b612.me::127.0.0.1:9999
|
||||
inheader=Accept-Encoding::none
|
||||
host=b612.me
|
||||
authuser=b612
|
||||
authpasswd=b612
|
||||
whiteip=
|
||||
blackip=
|
||||
wanringpage=
|
92
httpreverse/cmd.go
Normal file
92
httpreverse/cmd.go
Normal file
@ -0,0 +1,92 @@
|
||||
package httpreverse
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
var remote, config string
|
||||
var addr, key, cert, log string
|
||||
var port int
|
||||
var enablessl bool
|
||||
var host string
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringVarP(&host, "host", "H", "", "host字段")
|
||||
Cmd.Flags().StringVarP(&remote, "remote", "r", "", "反向代理地址")
|
||||
Cmd.Flags().StringVarP(&config, "config", "C", "", "配置文件地址")
|
||||
Cmd.Flags().StringVarP(&addr, "addr", "a", "0.0.0.0", "监听地址")
|
||||
Cmd.Flags().StringVarP(&key, "key", "k", "", "ssl key地址")
|
||||
Cmd.Flags().StringVarP(&cert, "cert", "c", "", "ssl 证书地址")
|
||||
Cmd.Flags().StringVarP(&log, "log", "l", "", "log日志地址")
|
||||
Cmd.Flags().BoolVarP(&enablessl, "enable-ssl", "s", false, "启用ssl")
|
||||
Cmd.Flags().IntVarP(&port, "port", "p", 8080, "监听端口")
|
||||
}
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "hproxy",
|
||||
Short: "Http Reverse Proxy(Http反向代理)",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if log != "" {
|
||||
starlog.SetLogFile(log, starlog.Std, true)
|
||||
}
|
||||
if config != "" {
|
||||
r, err := Parse(config)
|
||||
if err != nil {
|
||||
starlog.Errorln(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
go func() {
|
||||
sig := make(chan os.Signal)
|
||||
signal.Notify(sig, os.Kill, os.Interrupt)
|
||||
starlog.Noticeln("Stop Due to Recv Siganl", <-sig)
|
||||
r.Close()
|
||||
}()
|
||||
err = r.Run()
|
||||
if err != nil {
|
||||
starlog.Errorln("Http Reverse Proxy Exit Error", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
starlog.Infoln("Http Reverse Proxy Exit Normally")
|
||||
return
|
||||
}
|
||||
if remote == "" {
|
||||
starlog.Errorln("please enter the reverse url")
|
||||
os.Exit(4)
|
||||
}
|
||||
u, err := url.Parse(remote)
|
||||
if err != nil {
|
||||
starlog.Errorln(err)
|
||||
os.Exit(3)
|
||||
}
|
||||
reverse := ReverseConfig{
|
||||
Name: "web",
|
||||
Addr: addr,
|
||||
Host: host,
|
||||
ReverseURL: map[string]*url.URL{
|
||||
"/": u,
|
||||
},
|
||||
Port: port,
|
||||
UsingSSL: enablessl,
|
||||
Key: key,
|
||||
Cert: cert,
|
||||
XForwardMode: 1,
|
||||
}
|
||||
go func() {
|
||||
sig := make(chan os.Signal)
|
||||
signal.Notify(sig, os.Kill, os.Interrupt)
|
||||
starlog.Noticeln("Stop Due to Recv Siganl", <-sig)
|
||||
reverse.Close()
|
||||
}()
|
||||
err = reverse.Run()
|
||||
if err != nil {
|
||||
starlog.Errorln("Http Reverse Proxy Exit Error", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
starlog.Infoln("Http Reverse Proxy Exit Normally")
|
||||
return
|
||||
},
|
||||
}
|
84
httpreverse/iputil.go
Normal file
84
httpreverse/iputil.go
Normal file
@ -0,0 +1,84 @@
|
||||
package httpreverse
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func IPCIDR(ip string) (string, int, error) {
|
||||
if !strings.Contains(ip, "/") {
|
||||
return ip, 32, nil
|
||||
}
|
||||
tmp := strings.Split(ip, "/")
|
||||
cidr, err := strconv.Atoi(tmp[1])
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
intIp, err := IP2Int(tmp[0])
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
ip, err = Int2IP(int((uint32(intIp) >> (32 - cidr)) << (32 - cidr)))
|
||||
return ip, cidr, err
|
||||
}
|
||||
|
||||
func IPEnd(ip string, cidr int) (string, error) {
|
||||
intIp, err := IP2Int(ip)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip, err = Int2IP(int(uint32(intIp) | ((uint32(4294967295) << cidr) >> cidr)))
|
||||
return ip, err
|
||||
}
|
||||
|
||||
func IPStart(ip string, cidr int) (string, error) {
|
||||
intIp, err := IP2Int(ip)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip, err = Int2IP(int((uint32(intIp) >> (32 - cidr)) << (32 - cidr)))
|
||||
return ip, err
|
||||
}
|
||||
|
||||
func IPInRange(cidrip, ip string) (bool, error) {
|
||||
w, c, err := IPCIDR(cidrip)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
f, _, err := IPCIDR(ip + "/" + strconv.Itoa(c))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return w == f, nil
|
||||
}
|
||||
|
||||
func IPinRange2(startIP string, cidr int, ip string) (bool, error) {
|
||||
f, _, err := IPCIDR(ip + "/" + strconv.Itoa(cidr))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return startIP == f, nil
|
||||
}
|
||||
|
||||
func IP2Int(ipAddr string) (int, error) {
|
||||
var ipNum uint32 = 0
|
||||
ipFilter := strings.Split(ipAddr, ".")
|
||||
for i := 0; i < len(ipFilter); i++ {
|
||||
num, err := strconv.ParseUint(ipFilter[len(ipFilter)-1-i], 10, 32)
|
||||
ipNum |= (uint32(num) << (8 * uint32(i)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return int(ipNum), nil
|
||||
}
|
||||
|
||||
func Int2IP(intIP int) (string, error) {
|
||||
var result string
|
||||
var ip uint32
|
||||
ip = uint32(intIP)
|
||||
for i := 0; i < 4; i++ {
|
||||
result += strconv.FormatUint(uint64(uint8(ip>>uint8((3-i)*8))), 10) + "."
|
||||
}
|
||||
return result[0 : len(result)-1], nil
|
||||
}
|
174
httpreverse/reverse.go
Normal file
174
httpreverse/reverse.go
Normal file
@ -0,0 +1,174 @@
|
||||
package httpreverse
|
||||
|
||||
import (
|
||||
"b612.me/staros/sysconf"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ReverseConfig struct {
|
||||
Name string
|
||||
Addr string
|
||||
ReverseURL map[string]*url.URL
|
||||
Port int
|
||||
UsingSSL bool
|
||||
Key string
|
||||
Cert string
|
||||
Host string
|
||||
InHeader [][2]string
|
||||
OutHeader [][2]string
|
||||
Cookie [][3]string //[3]string should contains path::key::value
|
||||
ReplaceList [][2]string
|
||||
ReplaceOnce bool
|
||||
proxy map[string]*httputil.ReverseProxy
|
||||
XForwardMode int //0=off 1=useremote 2=add
|
||||
httpmux http.ServeMux
|
||||
httpserver http.Server
|
||||
|
||||
basicAuthUser string
|
||||
basicAuthPwd string
|
||||
protectAuthPage []string
|
||||
blackip map[string]int
|
||||
whiteip map[string]int
|
||||
warningpage string
|
||||
warnpagedata []byte
|
||||
}
|
||||
|
||||
type HttpReverseServer struct {
|
||||
Config []*ReverseConfig
|
||||
}
|
||||
|
||||
func Parse(path string) (HttpReverseServer, error) {
|
||||
var res HttpReverseServer
|
||||
ini := sysconf.NewIni()
|
||||
err := ini.ParseFromFile(path)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
for _, v := range ini.Data {
|
||||
var ins = ReverseConfig{
|
||||
Name: v.Name,
|
||||
Host: v.Get("host"),
|
||||
Addr: v.Get("addr"),
|
||||
Port: v.Int("port"),
|
||||
UsingSSL: v.Bool("enablessl"),
|
||||
Key: v.Get("key"),
|
||||
Cert: v.Get("cert"),
|
||||
ReplaceOnce: v.Bool("replaceonce"),
|
||||
XForwardMode: v.Int("xforwardmode"),
|
||||
basicAuthUser: v.Get("authuser"),
|
||||
basicAuthPwd: v.Get("authpasswd"),
|
||||
warningpage: v.Get("warnpage"),
|
||||
}
|
||||
if ins.warningpage != "" {
|
||||
data, err := ioutil.ReadFile(ins.warningpage)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
ins.warnpagedata = data
|
||||
}
|
||||
ins.proxy = make(map[string]*httputil.ReverseProxy)
|
||||
ins.ReverseURL = make(map[string]*url.URL)
|
||||
for _, reverse := range v.GetAll("reverse") {
|
||||
kv := strings.SplitN(reverse, "::", 2)
|
||||
if len(kv) != 2 {
|
||||
return res, errors.New("reverse settings not correct:" + reverse)
|
||||
}
|
||||
ins.ReverseURL[strings.TrimSpace(kv[0])], err = url.Parse(strings.TrimSpace(kv[1]))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
for _, header := range v.GetAll("inheader") {
|
||||
kv := strings.SplitN(header, "::", 2)
|
||||
if len(kv) != 2 {
|
||||
return res, errors.New("header settings not correct:" + header)
|
||||
}
|
||||
ins.InHeader = append(ins.InHeader, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])})
|
||||
}
|
||||
for _, authpage := range v.GetAll("authpage") {
|
||||
ins.protectAuthPage = append(ins.protectAuthPage, authpage)
|
||||
}
|
||||
ins.blackip = make(map[string]int)
|
||||
for _, blackip := range v.GetAll("blackip") {
|
||||
ip, cidr, err := IPCIDR(blackip)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
ins.blackip[ip] = cidr
|
||||
}
|
||||
ins.whiteip = make(map[string]int)
|
||||
for _, whiteip := range v.GetAll("whiteip") {
|
||||
ip, cidr, err := IPCIDR(whiteip)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
ins.whiteip[ip] = cidr
|
||||
}
|
||||
for _, header := range v.GetAll("outheader") {
|
||||
kv := strings.SplitN(header, "::", 2)
|
||||
if len(kv) != 2 {
|
||||
return res, errors.New("header settings not correct:" + header)
|
||||
}
|
||||
ins.OutHeader = append(ins.OutHeader, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])})
|
||||
}
|
||||
for _, cookie := range v.GetAll("cookie") {
|
||||
kv := strings.SplitN(cookie, "::", 3)
|
||||
if len(kv) != 3 {
|
||||
return res, errors.New("cookie settings not correct:" + cookie)
|
||||
}
|
||||
ins.Cookie = append(ins.Cookie, [3]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]), strings.TrimSpace(kv[2])})
|
||||
}
|
||||
for _, replace := range v.GetAll("replace") {
|
||||
kv := strings.SplitN(replace, "::", 2)
|
||||
if len(kv) != 2 {
|
||||
return res, errors.New("replace settings not correct:" + replace)
|
||||
}
|
||||
ins.ReplaceList = append(ins.ReplaceList, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])})
|
||||
}
|
||||
res.Config = append(res.Config, &ins)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *HttpReverseServer) Run() error {
|
||||
var wg sync.WaitGroup
|
||||
var mu sync.Mutex
|
||||
var haveErr string
|
||||
for _, v := range h.Config {
|
||||
wg.Add(1)
|
||||
go func(v *ReverseConfig) {
|
||||
defer wg.Done()
|
||||
err := v.Run()
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
haveErr += err.Error() + "\n"
|
||||
mu.Unlock()
|
||||
}
|
||||
}(v)
|
||||
}
|
||||
wg.Wait()
|
||||
if haveErr != "" {
|
||||
return errors.New(haveErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HttpReverseServer) Close() error {
|
||||
var haveErr string
|
||||
for _, v := range h.Config {
|
||||
err := v.Close()
|
||||
if err != nil {
|
||||
haveErr += err.Error() + "\n"
|
||||
}
|
||||
}
|
||||
if haveErr != "" {
|
||||
return errors.New(haveErr)
|
||||
}
|
||||
return nil
|
||||
}
|
23
httpreverse/reverse_test.go
Normal file
23
httpreverse/reverse_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
package httpreverse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReverseParse(t *testing.T) {
|
||||
data, err := Parse("./cfg.ini")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("%+v\n", data)
|
||||
}
|
||||
|
||||
func TestReverse(t *testing.T) {
|
||||
data, err := Parse("./cfg.ini")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("%+v\n", data)
|
||||
data.Run()
|
||||
}
|
262
httpreverse/service.go
Normal file
262
httpreverse/service.go
Normal file
@ -0,0 +1,262 @@
|
||||
package httpreverse
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var version = "2.0.1"
|
||||
|
||||
func (h *ReverseConfig) Run() error {
|
||||
err := h.init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, proxy := range h.proxy {
|
||||
h.httpmux.HandleFunc(key, func(writer http.ResponseWriter, request *http.Request) {
|
||||
starlog.Infof("<%s> Req Path:%s Addr:%s UA:%s\n", h.Name, request.URL.Path, request.RemoteAddr, request.Header.Get("User-Agent"))
|
||||
|
||||
if !h.BasicAuth(writer, request) {
|
||||
h.SetResponseHeader(writer)
|
||||
return
|
||||
}
|
||||
if !h.filter(writer, request) {
|
||||
h.SetResponseHeader(writer)
|
||||
writer.WriteHeader(403)
|
||||
if len(h.warnpagedata) != 0 {
|
||||
writer.Write(h.warnpagedata)
|
||||
return
|
||||
}
|
||||
writer.Write([]byte(`
|
||||
<html>
|
||||
<head><title>403 Forbidden</title></head>
|
||||
<body>
|
||||
<center><h1>403 Forbidden</h1></center>
|
||||
<center><h3>You Are Not Allowed to Access This Page</h3></center>
|
||||
<center><h3>Please Contact Site Administrator For Help</h3></center>
|
||||
<hr><center>B612 HTTP REVERSE SERVER</center>
|
||||
</body>
|
||||
</html>`))
|
||||
return
|
||||
}
|
||||
proxy.ServeHTTP(writer, request)
|
||||
})
|
||||
}
|
||||
h.httpserver = http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", h.Addr, h.Port),
|
||||
Handler: &h.httpmux,
|
||||
}
|
||||
starlog.Infoln(h.Name + " Listening on " + h.Addr + ":" + strconv.Itoa(h.Port))
|
||||
if !h.UsingSSL {
|
||||
if err := h.httpserver.ListenAndServe(); err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if h.httpserver.ListenAndServeTLS(h.Cert, h.Key); err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ReverseConfig) Close() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
return h.httpserver.Shutdown(ctx)
|
||||
}
|
||||
|
||||
func (h *ReverseConfig) init() error {
|
||||
h.proxy = make(map[string]*httputil.ReverseProxy)
|
||||
for key, val := range h.ReverseURL {
|
||||
h.proxy[key] = httputil.NewSingleHostReverseProxy(val)
|
||||
h.proxy[key].ModifyResponse = h.ModifyResponse()
|
||||
originalDirector := h.proxy[key].Director
|
||||
h.proxy[key].Director = func(req *http.Request) {
|
||||
originalDirector(req)
|
||||
h.ModifyRequest(req, val)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ReverseConfig) SetResponseHeader(resp http.ResponseWriter) {
|
||||
resp.Header().Set("X-Powered-By", "B612.ME")
|
||||
resp.Header().Set("Server", "B612/"+version)
|
||||
resp.Header().Set("X-Proxy", "B612 Reverse Proxy")
|
||||
}
|
||||
|
||||
func (h *ReverseConfig) ModifyResponse() func(*http.Response) error {
|
||||
return func(resp *http.Response) error {
|
||||
for _, v := range h.OutHeader {
|
||||
resp.Header.Set(v[0], v[1])
|
||||
}
|
||||
resp.Header.Del("X-Powered-By")
|
||||
resp.Header.Del("Server")
|
||||
resp.Header.Del("X-Proxy")
|
||||
resp.Header.Set("X-Powered-By", "B612.ME")
|
||||
resp.Header.Set("Server", "B612/"+version)
|
||||
resp.Header.Set("X-Proxy", "B612 Reverse Proxy")
|
||||
if len(h.ReplaceList) != 0 && resp.ContentLength <= 20*1024*1024 && strings.Contains(resp.Header.Get("Content-Type"), "text") {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
strBody := string(body)
|
||||
replaceCount := -1
|
||||
if h.ReplaceOnce {
|
||||
replaceCount = 1
|
||||
}
|
||||
for _, v := range h.ReplaceList {
|
||||
strBody = strings.Replace(strBody, v[0], v[1], replaceCount)
|
||||
}
|
||||
body = []byte(strBody)
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
resp.ContentLength = int64(len(body))
|
||||
resp.Header.Set("Content-Length", strconv.Itoa(len(body)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ReverseConfig) ModifyRequest(req *http.Request, remote *url.URL) {
|
||||
if h.XForwardMode == 1 {
|
||||
req.Header.Set("X-Forwarded-For", strings.Split(req.RemoteAddr, ":")[0])
|
||||
} else if h.XForwardMode == 2 {
|
||||
xforward := strings.Split(strings.TrimSpace(req.Header.Get("X-Forwarded-For")), ",")
|
||||
xforward = append(xforward, strings.Split(req.RemoteAddr, ":")[0])
|
||||
req.Header.Set("X-Forwarded-For", strings.Join(xforward, ", "))
|
||||
}
|
||||
for _, v := range h.Cookie {
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: v[1],
|
||||
Value: v[2],
|
||||
Path: v[0],
|
||||
})
|
||||
}
|
||||
host := h.Host
|
||||
if host == "" {
|
||||
host = remote.Host
|
||||
}
|
||||
targetQuery := remote.RawQuery
|
||||
req.URL.Scheme = remote.Scheme
|
||||
req.URL.Host = remote.Host
|
||||
req.Host = host
|
||||
req.URL.Path, req.URL.RawPath = joinURLPath(remote, req.URL)
|
||||
if targetQuery == "" || req.URL.RawQuery == "" {
|
||||
req.URL.RawQuery = targetQuery + req.URL.RawQuery
|
||||
} else {
|
||||
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
|
||||
}
|
||||
for _, v := range h.InHeader {
|
||||
req.Header.Set(v[0], v[1])
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ReverseConfig) GiveBasicAuth(w http.ResponseWriter) {
|
||||
w.Header().Set("WWW-Authenticate", ` Basic realm="Please Enter Passwd"`)
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte(`
|
||||
<html>
|
||||
<head><title>401 Authorization Required</title></head>
|
||||
<body>
|
||||
<center><h1>401 Authorization Required</h1></center>
|
||||
<hr><center>B612 HTTP SERVER</center>
|
||||
</body>
|
||||
</html>`))
|
||||
}
|
||||
|
||||
func (h *ReverseConfig) BasicAuth(w http.ResponseWriter, r *http.Request) bool {
|
||||
if h.basicAuthPwd != "" {
|
||||
if len(h.protectAuthPage) != 0 {
|
||||
for _, v := range h.protectAuthPage {
|
||||
if !(strings.Index(r.URL.Path, v) == 0 || strings.Contains(r.URL.RawQuery, v)) {
|
||||
return true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
authHeader := strings.TrimSpace(r.Header.Get("Authorization"))
|
||||
if len(authHeader) == 0 {
|
||||
h.GiveBasicAuth(w)
|
||||
return false
|
||||
} else {
|
||||
userAuth := base64.StdEncoding.EncodeToString([]byte(h.basicAuthUser + ":" + h.basicAuthPwd))
|
||||
authStr := strings.Split(authHeader, " ")
|
||||
if strings.TrimSpace(authStr[1]) != userAuth || strings.ToLower(strings.TrimSpace(authStr[0])) != "basic" {
|
||||
h.GiveBasicAuth(w)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *ReverseConfig) filter(w http.ResponseWriter, r *http.Request) bool {
|
||||
if len(h.blackip) == 0 && len(h.whiteip) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(h.whiteip) != 0 {
|
||||
if _, ok := h.whiteip[strings.Split(r.RemoteAddr, ":")[0]]; ok {
|
||||
return true
|
||||
}
|
||||
for k, v := range h.whiteip {
|
||||
if match, _ := IPinRange2(k, v, strings.Split(r.RemoteAddr, ":")[0]); match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if _, ok := h.blackip[strings.Split(r.RemoteAddr, ":")[0]]; ok {
|
||||
return false
|
||||
}
|
||||
for k, v := range h.blackip {
|
||||
if match, _ := IPinRange2(k, v, strings.Split(r.RemoteAddr, ":")[0]); match {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func joinURLPath(a, b *url.URL) (path, rawpath string) {
|
||||
if a.RawPath == "" && b.RawPath == "" {
|
||||
return singleJoiningSlash(a.Path, b.Path), ""
|
||||
}
|
||||
// Same as singleJoiningSlash, but uses EscapedPath to determine
|
||||
// whether a slash should be added
|
||||
apath := a.EscapedPath()
|
||||
bpath := b.EscapedPath()
|
||||
|
||||
aslash := strings.HasSuffix(apath, "/")
|
||||
bslash := strings.HasPrefix(bpath, "/")
|
||||
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a.Path + b.Path[1:], apath + bpath[1:]
|
||||
case !aslash && !bslash:
|
||||
return a.Path + "/" + b.Path, apath + "/" + bpath
|
||||
}
|
||||
return a.Path + b.Path, apath + bpath
|
||||
}
|
||||
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash:
|
||||
return a + "/" + b
|
||||
}
|
||||
return a + b
|
||||
}
|
73
httpserver/cmd.go
Normal file
73
httpserver/cmd.go
Normal file
@ -0,0 +1,73 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"b612.me/staros"
|
||||
"context"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var s HttpServer
|
||||
|
||||
var daemon bool
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringVarP(&s.port, "port", "p", "80", "监听端口")
|
||||
Cmd.Flags().StringVarP(&s.addr, "ip", "i", "0.0.0.0", "监听ip")
|
||||
Cmd.Flags().StringVarP(&s.envPath, "folder", "f", "./", "本地文件地址")
|
||||
Cmd.Flags().StringVarP(&s.uploadFolder, "upload", "u", "", "文件上传文件夹路径")
|
||||
Cmd.Flags().BoolVarP(&daemon, "daemon", "d", false, "以后台进程运行")
|
||||
Cmd.Flags().StringVarP(&s.basicAuthUser, "auth", "a", "", "HTTP BASIC AUTH认证(用户名:密码)")
|
||||
Cmd.Flags().StringVarP(&s.indexFile, "index", "n", "", "Index文件名,如index.html")
|
||||
Cmd.Flags().StringVarP(&s.logpath, "log", "l", "", "log地址")
|
||||
Cmd.Flags().StringVarP(&s.cert, "ssl-cert", "c", "", "TLS证书路径")
|
||||
Cmd.Flags().StringVarP(&s.key, "ssl-key", "k", "", "TLS密钥路径")
|
||||
Cmd.Flags().BoolVarP(&s.disableMIME, "disablemime", "m", false, "停止解析MIME,全部按下载文件处理")
|
||||
Cmd.Flags().StringSliceVarP(&s.protectAuthPage, "protect-page", "P", []string{}, "Basic Auth 开启白名单")
|
||||
Cmd.Flags().Bool("daeapplied", false, "")
|
||||
Cmd.Flags().MarkHidden("daeapplied")
|
||||
}
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "http",
|
||||
Short: "HTTP文件服务器(HTTP File Browser Server)",
|
||||
Long: `HTTP文件服务器(HTTP File Browser Server)`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
apply, _ := cmd.Flags().GetBool("daeapplied")
|
||||
if daemon && !apply {
|
||||
nArgs := append(os.Args[1:], "--daeapplied")
|
||||
pid, err := staros.Daemon(os.Args[0], nArgs...)
|
||||
if err != nil {
|
||||
starlog.Criticalln("Daemon Error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
starlog.StdPrintf([]starlog.Attr{starlog.FgGreen}, "Success,PID=%v\n", pid)
|
||||
return
|
||||
}
|
||||
sig := make(chan os.Signal)
|
||||
signal.Notify(sig, os.Kill, os.Interrupt)
|
||||
ctx, fn := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
starlog.Infoln("Recv Signal", <-sig)
|
||||
fn()
|
||||
}()
|
||||
if s.basicAuthUser != "" {
|
||||
tmp := strings.SplitN(s.basicAuthUser, ":", 2)
|
||||
if len(tmp) != 2 {
|
||||
starlog.Errorln("basic Auth should have user and password")
|
||||
os.Exit(2)
|
||||
}
|
||||
s.basicAuthUser = strings.TrimSpace(tmp[0])
|
||||
s.basicAuthPwd = tmp[1]
|
||||
}
|
||||
err := s.Run(ctx)
|
||||
if err != nil {
|
||||
starlog.Errorln("Http Server Closed by Errors", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
starlog.Infoln("Http Server Closed Normally")
|
||||
},
|
||||
}
|
2037
httpserver/mime.go
Normal file
2037
httpserver/mime.go
Normal file
File diff suppressed because it is too large
Load Diff
467
httpserver/server.go
Normal file
467
httpserver/server.go
Normal file
@ -0,0 +1,467 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"b612.me/starlog"
|
||||
"b612.me/staros"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var version = "2.0.0"
|
||||
|
||||
type HttpServerCfgs func(cfg *HttpServerCfg)
|
||||
|
||||
type HttpServerCfg struct {
|
||||
basicAuthUser string
|
||||
basicAuthPwd string
|
||||
envPath string
|
||||
uploadFolder string
|
||||
logpath string
|
||||
indexFile string
|
||||
cert string
|
||||
key string
|
||||
addr string
|
||||
port string
|
||||
protectAuthPage []string
|
||||
disableMIME bool
|
||||
ctx context.Context
|
||||
}
|
||||
type HttpServer struct {
|
||||
HttpServerCfg
|
||||
}
|
||||
|
||||
func WithTLSCert(cert, key string) HttpServerCfgs {
|
||||
return func(cfg *HttpServerCfg) {
|
||||
cfg.key = key
|
||||
cfg.cert = cert
|
||||
}
|
||||
}
|
||||
|
||||
func WithUploadFolder(path string) HttpServerCfgs {
|
||||
return func(cfg *HttpServerCfg) {
|
||||
cfg.uploadFolder = path
|
||||
}
|
||||
}
|
||||
|
||||
func NewHttpServer(addr, port, path string, opts ...HttpServerCfgs) *HttpServer {
|
||||
var server = HttpServer{
|
||||
HttpServerCfg: HttpServerCfg{
|
||||
addr: addr,
|
||||
port: port,
|
||||
envPath: path,
|
||||
},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&server.HttpServerCfg)
|
||||
}
|
||||
return &server
|
||||
}
|
||||
|
||||
func (h *HttpServer) Run(ctx context.Context) error {
|
||||
h.ctx = ctx
|
||||
server := http.Server{
|
||||
Addr: h.addr + ":" + h.port,
|
||||
Handler: h,
|
||||
}
|
||||
go func() {
|
||||
select {
|
||||
case <-h.ctx.Done():
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
server.Shutdown(ctx)
|
||||
}
|
||||
}()
|
||||
if h.logpath != "" {
|
||||
starlog.SetLogFile(h.logpath, starlog.Std, true)
|
||||
}
|
||||
netcards, err := net.Interfaces()
|
||||
if err == nil {
|
||||
for _, v := range netcards {
|
||||
if strings.Contains(v.Flags.String(), "up") {
|
||||
addrs, err := v.Addrs()
|
||||
if err == nil {
|
||||
var ips []string
|
||||
for _, ip := range addrs {
|
||||
ips = append(ips, ip.String())
|
||||
}
|
||||
starlog.Noticef("Name:%s IP:%s MAC:%s\n", v.Name, strings.Join(ips, ","), v.HardwareAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
starlog.Infoln("Listening on " + h.addr + ":" + h.port)
|
||||
if h.cert == "" {
|
||||
if err := server.ListenAndServe(); err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := server.ListenAndServeTLS(h.cert, h.key); err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.Listen(w, r)
|
||||
}
|
||||
|
||||
func (h *HttpServer) GiveBasicAuth(w http.ResponseWriter) {
|
||||
w.Header().Set("WWW-Authenticate", ` Basic realm="Please Enter Passwd"`)
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte(`
|
||||
<html>
|
||||
<head><title>401 Authorization Required</title></head>
|
||||
<body>
|
||||
<center><h1>401 Authorization Required</h1></center>
|
||||
<hr><center>B612 HTTP SERVER</center>
|
||||
</body>
|
||||
</html>`))
|
||||
}
|
||||
|
||||
func (h *HttpServer) BasicAuth(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request) bool {
|
||||
if h.basicAuthPwd != "" {
|
||||
if len(h.protectAuthPage) != 0 {
|
||||
for _, v := range h.protectAuthPage {
|
||||
if !(strings.Index(r.URL.Path, v) == 0 || strings.Contains(r.URL.RawQuery, v)) {
|
||||
return true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
authHeader := strings.TrimSpace(r.Header.Get("Authorization"))
|
||||
if len(authHeader) == 0 {
|
||||
log.Noticeln("No Authed! Get Path is", r.URL.Path, r.RemoteAddr)
|
||||
h.GiveBasicAuth(w)
|
||||
return false
|
||||
} else {
|
||||
userAuth := base64.StdEncoding.EncodeToString([]byte(h.basicAuthUser + ":" + h.basicAuthPwd))
|
||||
authStr := strings.Split(authHeader, " ")
|
||||
if strings.TrimSpace(authStr[1]) != userAuth || strings.ToLower(strings.TrimSpace(authStr[0])) != "basic" {
|
||||
log.Noticeln("Auth Failed! Get Path is", r.URL.Path, r.RemoteAddr, "pwd enter is", authHeader)
|
||||
h.GiveBasicAuth(w)
|
||||
return false
|
||||
}
|
||||
log.Infof("Path %s Authoried by %s:%s\n", r.URL.Path, r.RemoteAddr, authHeader)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *HttpServer) SetUpload(w http.ResponseWriter, r *http.Request, path string) bool {
|
||||
if h.uploadFolder != "" {
|
||||
if len(r.URL.Query()["upload"]) != 0 {
|
||||
w.Write([]byte(`<html><body><form id= "uploadForm" action= "/recv?upload=true" method= "post" enctype ="multipart/form-data">
|
||||
<h1 >B612 File Upload Page </h1>
|
||||
<p >上传文件: <input type ="file" name="victorique" /></p>
|
||||
<input type ="submit" value="上传"/>
|
||||
</form>
|
||||
<h2>Copyright@b612.me </h2></body></html>`))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (h *HttpServer) Listen(w http.ResponseWriter, r *http.Request) {
|
||||
log := starlog.Std.NewFlag()
|
||||
w.Header().Set("X-Powered-By", "B612.ME")
|
||||
w.Header().Set("Server", "B612/"+version)
|
||||
if !h.BasicAuth(log, w, r) {
|
||||
return
|
||||
}
|
||||
path := r.URL.Path
|
||||
if h.uploadFolder != "" && path == "/recv" && len(r.URL.Query()["upload"]) != 0 {
|
||||
h.uploadFile(w, r)
|
||||
return
|
||||
}
|
||||
fullpath := filepath.Join(h.envPath, path)
|
||||
if path == "/" && h.indexFile != "" {
|
||||
if staros.Exists(filepath.Join(h.envPath, h.indexFile)) {
|
||||
fullpath = filepath.Join(h.envPath, h.indexFile)
|
||||
path = "/" + h.indexFile
|
||||
}
|
||||
}
|
||||
log.Noticef("Start Method:%s Path:%s IP:%s\n", r.Method, path, r.RemoteAddr)
|
||||
if h.SetUpload(w, r, path) {
|
||||
return
|
||||
}
|
||||
switch r.Method {
|
||||
case "OPTIONS", "HEAD":
|
||||
err := h.BuildHeader(w, r, fullpath)
|
||||
if err != nil {
|
||||
log.Warningf("Finished Method:%s Path:%s IP:%s Err:%v\n", r.Method, path, r.RemoteAddr, err)
|
||||
} else {
|
||||
log.Infof("Finished Method:%s Path:%s IP:%s\n", r.Method, path, r.RemoteAddr)
|
||||
}
|
||||
case "GET":
|
||||
err := h.BuildHeader(w, r, fullpath)
|
||||
if err != nil {
|
||||
log.Warningf("GET Header Build Failed Path:%s IP:%s Err:%v\n", path, r.RemoteAddr, err)
|
||||
}
|
||||
err = h.ResponseGet(log, w, r, fullpath)
|
||||
if err != nil {
|
||||
log.Warningf("Finished Method %s Path:%s IP:%s Err:%v\n", r.Method, path, r.RemoteAddr, err)
|
||||
return
|
||||
}
|
||||
log.Infof("Finished Method:%s Path:%s IP:%s\n", r.Method, path, r.RemoteAddr)
|
||||
default:
|
||||
log.Warningf("Invalid Method %s Path:%s IP:%s\n", r.Method, path, r.RemoteAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HttpServer) CalcRange(r *http.Request) (int64, int64) {
|
||||
var rangeStart, rangeEnd int64
|
||||
rangeStart, rangeEnd = -1, -1
|
||||
for k, v := range r.Header {
|
||||
if strings.ToLower(k) == "range" {
|
||||
if strings.Contains(v[0], "bytes=") {
|
||||
v[0] = strings.Replace(v[0], "bytes=", "", -1)
|
||||
}
|
||||
data := strings.Split(v[0], "-")
|
||||
if len(data) == 0 {
|
||||
break
|
||||
}
|
||||
rangeStart, _ = strconv.ParseInt(data[0], 10, 64)
|
||||
if len(data) > 1 {
|
||||
rangeEnd, _ = strconv.ParseInt(data[1], 10, 64)
|
||||
}
|
||||
//w.WriteHeader(206) //206 支持断点续传
|
||||
break
|
||||
}
|
||||
}
|
||||
return rangeStart, rangeEnd
|
||||
}
|
||||
|
||||
func (h *HttpServer) BuildHeader(w http.ResponseWriter, r *http.Request, fullpath string) error {
|
||||
if r.Method == "OPTIONS" {
|
||||
w.Header().Set("Allow", "OPTIONS,GET,HEAD")
|
||||
w.Header().Set("Content-Length", "0")
|
||||
}
|
||||
w.Header().Set("Date", strings.ReplaceAll(time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT"))
|
||||
if staros.IsFolder(fullpath) {
|
||||
return nil
|
||||
}
|
||||
mime := h.MIME(fullpath)
|
||||
if h.disableMIME || mime == "" {
|
||||
w.Header().Set("Content-Type", "application/download")
|
||||
w.Header().Set("Content-Disposition", "attachment;filename="+filepath.Base(fullpath))
|
||||
w.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
} else {
|
||||
w.Header().Set("Content-Type", mime)
|
||||
}
|
||||
if staros.Exists(fullpath) {
|
||||
finfo, err := os.Stat(fullpath)
|
||||
if err != nil {
|
||||
w.WriteHeader(502)
|
||||
w.Write([]byte("Failed to Read " + fullpath + ",reason is " + err.Error()))
|
||||
return err
|
||||
}
|
||||
w.Header().Set("Accept-Ranges", "bytes")
|
||||
w.Header().Set("ETag", starcrypto.Md5Str([]byte(finfo.ModTime().String())))
|
||||
w.Header().Set("Last-Modified", strings.ReplaceAll(finfo.ModTime().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT"))
|
||||
if r.Method != "OPTIONS" {
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(finfo.Size(), 10))
|
||||
start, end := h.CalcRange(r)
|
||||
if start != -1 {
|
||||
if end == -1 {
|
||||
w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(finfo.Size(), 10)+"/"+strconv.FormatInt(finfo.Size(), 10))
|
||||
//w.Header().Set("Content-Length", strconv.FormatInt(fpinfo.Size()-rangeStart, 10))
|
||||
} else {
|
||||
w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10)+"/"+strconv.FormatInt(finfo.Size(), 10))
|
||||
//w.Header().Set("Content-Length", strconv.FormatInt(1+rangeEnd-rangeStart, 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HttpServer) ResponseGet(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error {
|
||||
if staros.IsFolder(fullpath) {
|
||||
return h.getFolder(log, w, r, fullpath)
|
||||
}
|
||||
return h.getFile(log, w, r, fullpath)
|
||||
}
|
||||
|
||||
func (h *HttpServer) getFolder(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error {
|
||||
dir, err := ioutil.ReadDir(fullpath)
|
||||
if err != nil {
|
||||
log.Errorf("Read Folder %s failed:%v\n", fullpath, err)
|
||||
w.WriteHeader(403)
|
||||
if r.Method == "HEAD" {
|
||||
return err
|
||||
}
|
||||
w.Write([]byte("<h1>Cannot Access!</h1>"))
|
||||
}
|
||||
if r.Method != "GET" {
|
||||
return nil
|
||||
}
|
||||
w.Write([]byte("<html>\n<style>\np{margin: 2px auto}\n</style>\n<h1>B612 Http Server - " + version + "</h1>"))
|
||||
if h.uploadFolder != "" {
|
||||
w.Write([]byte("<a href=/b612?upload=true>Upload Web Page Is Openned!</a><br /><br />"))
|
||||
}
|
||||
w.Write([]byte("<hr /><pre>\n"))
|
||||
if r.URL.Path != "/" {
|
||||
w.Write([]byte(fmt.Sprintf("<p><a href='%s'>%s</a> %s</p>\n", r.URL.Path+"/..", "..", "上层文件夹")))
|
||||
}
|
||||
if r.URL.Path == "/" {
|
||||
r.URL.Path = ""
|
||||
} else if r.URL.Path[len(r.URL.Path)-1:] == "/" {
|
||||
r.URL.Path = r.URL.Path[0 : len(r.URL.Path)-1]
|
||||
}
|
||||
|
||||
for _, v := range dir {
|
||||
if v.Name() != "." || v.Name() != ".." {
|
||||
if !v.IsDir() {
|
||||
w.Write([]byte(fmt.Sprintf("<p><a href='%s'>%s</a> %d %s</p>\n", r.URL.Path+"/"+v.Name(), v.Name(), int(v.Size()), v.ModTime().Format("2006-01-02 15:04:05"))))
|
||||
} else {
|
||||
w.Write([]byte(fmt.Sprintf("<p><a href='%s'>%s</a> %s %s</p>\n", r.URL.Path+"/"+v.Name(), v.Name(), "文件夹", v.ModTime().Format("2006-01-02 15:04:05"))))
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Write([]byte("</pre>\n</html>"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error {
|
||||
if !staros.Exists(fullpath) {
|
||||
w.WriteHeader(404)
|
||||
return errors.New("File Not Found! 404 ERROR")
|
||||
}
|
||||
//starlog.Debugln(r.Header)
|
||||
startRange, endRange := h.CalcRange(r)
|
||||
fp, err := os.Open(fullpath)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to open file %s,reason:%v\n", r.URL.Path, err)
|
||||
w.WriteHeader(502)
|
||||
w.Write([]byte("<h1>502 SERVER ERROR</h1>"))
|
||||
return err
|
||||
}
|
||||
defer fp.Close()
|
||||
var transferData int
|
||||
defer func() {
|
||||
if transferData != 0 {
|
||||
var tani string
|
||||
tani = fmt.Sprintf("%v Byte", transferData)
|
||||
if f64 := float64(transferData) / 1024; f64 > 1 {
|
||||
tani = fmt.Sprintf("%v KB", f64)
|
||||
if f64 = float64(f64) / 1024; f64 > 1 {
|
||||
tani = fmt.Sprintf("%v MB", f64)
|
||||
if f64 = float64(f64) / 1024; f64 > 1 {
|
||||
tani = fmt.Sprintf("%v GB", f64)
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Infof("Tranfered File %s %d bytes (%s) to remote %v\n", r.URL.Path,
|
||||
transferData, tani, r.RemoteAddr)
|
||||
}
|
||||
}()
|
||||
if startRange == -1 {
|
||||
w.WriteHeader(200)
|
||||
for {
|
||||
buf := make([]byte, 1048576)
|
||||
n, err := fp.Read(buf)
|
||||
if n != 0 {
|
||||
ns, err := w.Write(buf[0:n])
|
||||
transferData += ns
|
||||
if err != nil {
|
||||
log.Errorf("Transfer File %s to Remote Failed:%v\n", fullpath, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Errorln("Read File %s Failed:%v\n", fullpath, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
log.Debugf("206 transfer mode for %v %v\n", r.URL.Path, r.RemoteAddr)
|
||||
w.WriteHeader(206)
|
||||
fp.Seek(int64(startRange), 0)
|
||||
count := startRange
|
||||
for {
|
||||
buf := make([]byte, 1048576)
|
||||
n, err := fp.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Errorf("Read File %s Failed:%v\n", r.URL.Path, err)
|
||||
return err
|
||||
}
|
||||
if endRange == -1 {
|
||||
ns, err := w.Write(buf[0:n])
|
||||
transferData += ns
|
||||
if err != nil {
|
||||
log.Errorf("Transfer File %s to Remote Failed:%v\n", r.URL.Path, err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if count > endRange {
|
||||
break
|
||||
}
|
||||
writeNum := n
|
||||
if count+int64(n) > endRange {
|
||||
writeNum = int(endRange - count + 1)
|
||||
}
|
||||
ns, err := w.Write(buf[0:writeNum])
|
||||
transferData += ns
|
||||
if err != nil {
|
||||
log.Errorln("Transfer Error:", err)
|
||||
return err
|
||||
}
|
||||
count += int64(n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HttpServer) uploadFile(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte("USE POST METHOD!"))
|
||||
return
|
||||
}
|
||||
r.ParseMultipartForm(10485760)
|
||||
file, handler, err := r.FormFile("victorique")
|
||||
if err != nil {
|
||||
starlog.Errorf("Parse File From Form Failed:%v\n", err)
|
||||
w.WriteHeader(502)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
starlog.Noticef("Uploading %s From %s\n", handler.Filename, r.RemoteAddr)
|
||||
os.MkdirAll(filepath.Join(h.envPath, h.uploadFolder), 0755)
|
||||
f, err := os.OpenFile(filepath.Join(h.uploadFolder, handler.Filename), os.O_WRONLY|os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
starlog.Errorf("Open Local File %s Form %v Failed:%v\n", handler.Filename, r.RemoteAddr, err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, file)
|
||||
if err != nil {
|
||||
starlog.Errorf("Write File %s Form %v Failed:%v\n", handler.Filename, r.RemoteAddr, err)
|
||||
return
|
||||
}
|
||||
starlog.Infof("Write File %s Form %v Finished\n", handler.Filename, r.RemoteAddr)
|
||||
fmt.Fprintf(w, `<html><body><p>%v</p><h2><a href="/b612?upload=true">Return To Web Page</a></h2></body></html>`, handler.Header)
|
||||
}
|
193
image/image-basic.go
Normal file
193
image/image-basic.go
Normal file
@ -0,0 +1,193 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"b612.me/staros"
|
||||
"errors"
|
||||
"image"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/golang/freetype"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
|
||||
"image/color"
|
||||
"image/draw"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
"image/png"
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
func OpenImage(name string) (image.Image, error) {
|
||||
if !staros.Exists(name) {
|
||||
return nil, errors.New("File Not Exists")
|
||||
}
|
||||
fso, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img, _, err := image.Decode(fso)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func MergePhoto(big, small image.Image, bigsize, smallsize uint, x, y int) image.Image {
|
||||
big = resize.Resize(bigsize, bigsize, big, resize.Lanczos3)
|
||||
small = resize.Resize(smallsize, smallsize, small, resize.Lanczos3)
|
||||
offset := image.Pt(x, y)
|
||||
b := big.Bounds()
|
||||
nimg := image.NewRGBA(b)
|
||||
draw.Draw(nimg, b, big, image.ZP, draw.Src)
|
||||
draw.Draw(nimg, small.Bounds(), small, offset, draw.Over)
|
||||
return nimg
|
||||
}
|
||||
|
||||
func SavePhoto(path string, img image.Image) error {
|
||||
imgf, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer imgf.Close()
|
||||
return png.Encode(imgf, img)
|
||||
}
|
||||
|
||||
func SetAlpha(img image.Image, alpha uint8) image.Image {
|
||||
size := img.Bounds()
|
||||
nimg := image.NewRGBA(size)
|
||||
for x := 0; x < size.Dx(); x++ {
|
||||
for y := 0; y < size.Dy(); y++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
r = r >> 8
|
||||
g = g >> 8
|
||||
b = b >> 8
|
||||
a = a >> 8
|
||||
nimg.Set(x, y, color.NRGBA{uint8(r), uint8(g), uint8(b), alpha})
|
||||
//nimg.Set(x, y, color.Alpha{alpha})
|
||||
//nimg.Set(x, y, img.At(x, y))
|
||||
}
|
||||
}
|
||||
return nimg
|
||||
}
|
||||
|
||||
func AddText(text string, img image.Image, x, y int, dpi, size float64, colors color.RGBA, ttf string) (image.Image, error) {
|
||||
if !staros.Exists(ttf) {
|
||||
return nil, errors.New("File Not Exists")
|
||||
}
|
||||
fontbyte, err := ioutil.ReadFile(ttf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
font, err := freetype.ParseFont(fontbyte)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nimg, ok := img.(draw.Image)
|
||||
if !ok {
|
||||
size := img.Bounds()
|
||||
nimg = image.NewRGBA(img.Bounds())
|
||||
for x := 0; x < size.Dx(); x++ {
|
||||
for y := 0; y < size.Dy(); y++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
nimg.Set(x, y, color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)})
|
||||
}
|
||||
}
|
||||
}
|
||||
f := freetype.NewContext()
|
||||
f.SetDPI(dpi)
|
||||
f.SetFontSize(size)
|
||||
f.SetFont(font)
|
||||
f.SetClip(nimg.Bounds())
|
||||
f.SetDst(nimg)
|
||||
f.SetSrc(image.NewUniform(colors))
|
||||
_, err = f.DrawString(text, freetype.Pt(x, y))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nimg, nil
|
||||
}
|
||||
|
||||
type TextImg struct {
|
||||
Text string
|
||||
X int
|
||||
Y int
|
||||
Dpi float64
|
||||
Size float64
|
||||
Color color.NRGBA
|
||||
}
|
||||
|
||||
type TextList struct {
|
||||
List []TextImg
|
||||
TTF []byte
|
||||
}
|
||||
|
||||
func AddListTests(list TextList, img image.Image) (image.Image, error) {
|
||||
font, err := freetype.ParseFont(list.TTF)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nimg, ok := img.(draw.Image)
|
||||
if !ok {
|
||||
size := img.Bounds()
|
||||
nimg = image.NewRGBA(img.Bounds())
|
||||
for x := 0; x < size.Dx(); x++ {
|
||||
for y := 0; y < size.Dy(); y++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
nimg.Set(x, y, color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range list.List {
|
||||
f := freetype.NewContext()
|
||||
f.SetDPI(v.Dpi)
|
||||
f.SetFontSize(v.Size)
|
||||
f.SetFont(font)
|
||||
f.SetClip(nimg.Bounds())
|
||||
f.SetDst(nimg)
|
||||
f.SetSrc(image.NewUniform(v.Color))
|
||||
_, err = f.DrawString(v.Text, freetype.Pt(v.X, v.Y))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nimg, nil
|
||||
}
|
||||
|
||||
func AddTexts(text string, img image.Image, x, y int, dpi, size float64, colors color.NRGBA, ttf string) (image.Image, error) {
|
||||
if !staros.Exists(ttf) {
|
||||
return nil, errors.New("File Not Exists")
|
||||
}
|
||||
fontbyte, err := ioutil.ReadFile(ttf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
font, err := freetype.ParseFont(fontbyte)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nimg, ok := img.(draw.Image)
|
||||
if !ok {
|
||||
size := img.Bounds()
|
||||
nimg = image.NewRGBA(img.Bounds())
|
||||
for x := 0; x < size.Dx(); x++ {
|
||||
for y := 0; y < size.Dy(); y++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
nimg.Set(x, y, color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)})
|
||||
}
|
||||
}
|
||||
}
|
||||
f := freetype.NewContext()
|
||||
f.SetDPI(dpi)
|
||||
f.SetFontSize(size)
|
||||
f.SetFont(font)
|
||||
f.SetClip(nimg.Bounds())
|
||||
f.SetDst(nimg)
|
||||
f.SetSrc(image.NewUniform(colors))
|
||||
_, err = f.DrawString(text, freetype.Pt(x, y))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nimg, nil
|
||||
}
|
82
image/image.go
Normal file
82
image/image.go
Normal file
@ -0,0 +1,82 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Cmd.AddCommand(imgMirrorCmd)
|
||||
}
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "图像处理",
|
||||
Long: "简单的图像处理工具",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
this.Help()
|
||||
},
|
||||
}
|
||||
|
||||
var imgMirrorCmd = &cobra.Command{
|
||||
Use: "mirror",
|
||||
Short: "图像镜像翻转",
|
||||
Long: "图像镜像翻转<水平>",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
starlog.Errorln("请指定需要转换的图像!")
|
||||
return
|
||||
}
|
||||
for _, v := range args {
|
||||
img, err := OpenImage(v)
|
||||
if err != nil {
|
||||
starlog.Errorln(err, v)
|
||||
continue
|
||||
}
|
||||
size := img.Bounds()
|
||||
nimg := image.NewRGBA(size)
|
||||
for x := 0; x < size.Dx(); x++ {
|
||||
for y := 0; y < size.Dy(); y++ {
|
||||
nimg.Set(size.Dx()-x, y, img.At(x, y))
|
||||
}
|
||||
}
|
||||
if err := SavePhoto(v, nimg); err != nil {
|
||||
starlog.Errorln(err, v)
|
||||
continue
|
||||
} else {
|
||||
fmt.Println(v, "转换已完成!")
|
||||
}
|
||||
}
|
||||
fmt.Println("任务完成!")
|
||||
},
|
||||
}
|
||||
|
||||
var imgAlpha = &cobra.Command{
|
||||
Use: "alpha",
|
||||
Short: "设置透明度",
|
||||
Long: "设置alpha通道透明度",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
starlog.Errorln("请指定需要转换的图像!")
|
||||
return
|
||||
}
|
||||
for _, v := range args {
|
||||
img, err := OpenImage(v)
|
||||
if err != nil {
|
||||
starlog.Errorln(err, v)
|
||||
continue
|
||||
}
|
||||
img = SetAlpha(img, 4)
|
||||
if err := SavePhoto(v, img); err != nil {
|
||||
starlog.Errorln(err, v)
|
||||
continue
|
||||
} else {
|
||||
fmt.Println(v, "转换已完成!")
|
||||
}
|
||||
}
|
||||
fmt.Println("任务完成!")
|
||||
},
|
||||
}
|
39
main.go
Normal file
39
main.go
Normal file
@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"b612.me/apps/b612/attach"
|
||||
"b612.me/apps/b612/base64"
|
||||
"b612.me/apps/b612/base85"
|
||||
"b612.me/apps/b612/base91"
|
||||
"b612.me/apps/b612/detach"
|
||||
"b612.me/apps/b612/df"
|
||||
"b612.me/apps/b612/dfinder"
|
||||
"b612.me/apps/b612/ftp"
|
||||
"b612.me/apps/b612/generate"
|
||||
"b612.me/apps/b612/hash"
|
||||
"b612.me/apps/b612/httpreverse"
|
||||
"b612.me/apps/b612/httpserver"
|
||||
"b612.me/apps/b612/image"
|
||||
"b612.me/apps/b612/merge"
|
||||
"b612.me/apps/b612/search"
|
||||
"b612.me/apps/b612/split"
|
||||
"b612.me/apps/b612/tcping"
|
||||
"b612.me/apps/b612/uac"
|
||||
"b612.me/apps/b612/vic"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cmdRoot = &cobra.Command{
|
||||
Use: "b612",
|
||||
Version: "2.0.1",
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(tcping.Cmd, uac.Cmd, httpserver.Cmd, httpreverse.Cmd,
|
||||
base64.Cmd, base85.Cmd, base91.Cmd, attach.Cmd, detach.Cmd, df.Cmd, dfinder.Cmd,
|
||||
ftp.Cmd, generate.Cmd, hash.Cmd, image.Cmd, merge.Cmd, search.Cmd, split.Cmd, vic.Cmd)
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmdRoot.Execute()
|
||||
}
|
46
merge/merge.go
Normal file
46
merge/merge.go
Normal file
@ -0,0 +1,46 @@
|
||||
package merge
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"fmt"
|
||||
|
||||
"b612.me/starlog"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "merge",
|
||||
Short: "合并文件",
|
||||
Long: "按路径自动合并分割的文件",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
var src, dst string
|
||||
if len(args) == 2 {
|
||||
src = args[0]
|
||||
dst = args[1]
|
||||
} else {
|
||||
src, _ = this.Flags().GetString("src")
|
||||
dst, _ = this.Flags().GetString("dst")
|
||||
}
|
||||
if src == "" || dst == "" {
|
||||
this.Help()
|
||||
return
|
||||
}
|
||||
err := starcrypto.MergeFile(src, dst, func(pect float64) {
|
||||
if pect == 100 {
|
||||
fmt.Println("文件已处理:100.000000%")
|
||||
} else {
|
||||
fmt.Printf("文件已处理:%f%%\r", pect)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
starlog.Errorln(err.Error)
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringP("src", "s", "", "源文件地址,用*替换文件数字")
|
||||
Cmd.Flags().StringP("dst", "d", "", "目标文件地址")
|
||||
}
|
1
net/cmd.go
Normal file
1
net/cmd.go
Normal file
@ -0,0 +1 @@
|
||||
package net
|
213
net/forward.go
Normal file
213
net/forward.go
Normal file
@ -0,0 +1,213 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NetForward struct {
|
||||
LocalAddr string
|
||||
LocalPort int
|
||||
RemoteURI string
|
||||
EnableTCP bool
|
||||
EnableUDP bool
|
||||
DialTimeout time.Duration
|
||||
UDPTimeout time.Duration
|
||||
stopCtx context.Context
|
||||
stopFn context.CancelFunc
|
||||
running int32
|
||||
}
|
||||
|
||||
func (n *NetForward) Close() {
|
||||
n.stopFn()
|
||||
}
|
||||
func (n *NetForward) Run() error {
|
||||
if !atomic.CompareAndSwapInt32(&n.running, 0, 1) {
|
||||
return errors.New("already running")
|
||||
}
|
||||
n.stopCtx, n.stopFn = context.WithCancel(context.Background())
|
||||
if n.DialTimeout == 0 {
|
||||
n.DialTimeout = time.Second * 10
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
if n.EnableTCP {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
n.runTCP()
|
||||
}()
|
||||
}
|
||||
|
||||
if n.EnableUDP {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
n.runUDP()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NetForward) runTCP() error {
|
||||
listen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort))
|
||||
if err != nil {
|
||||
starlog.Errorln("Listening On Tcp Failed:", err)
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
<-n.stopCtx.Done()
|
||||
listen.Close()
|
||||
}()
|
||||
starlog.Infof("Listening TCP on %v\n", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort))
|
||||
for {
|
||||
conn, err := listen.Accept()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
log := starlog.Std.NewFlag()
|
||||
log.Infof("Accept New TCP Conn from %v\n", conn.RemoteAddr().String())
|
||||
go func(conn net.Conn) {
|
||||
rmt, err := net.DialTimeout("tcp", n.RemoteURI, n.DialTimeout)
|
||||
if err != nil {
|
||||
log.Errorf("Dial Remote %s Failed:%v\n", n.RemoteURI, err)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
log.Infof("Connect %s <==> %s\n", conn.RemoteAddr().String(), n.RemoteURI)
|
||||
Copy(rmt, conn)
|
||||
log.Noticef("Connection Closed %s <==> %s", conn.RemoteAddr().String(), n.RemoteURI)
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
|
||||
type UDPConn struct {
|
||||
net.Conn
|
||||
listen *net.UDPConn
|
||||
remoteAddr *net.UDPAddr
|
||||
lastbeat int64
|
||||
}
|
||||
|
||||
func (u UDPConn) Write(p []byte) (n int, err error) {
|
||||
u.lastbeat = time.Now().Unix()
|
||||
return u.Conn.Write(p)
|
||||
}
|
||||
|
||||
func (u UDPConn) Read(p []byte) (n int, err error) {
|
||||
u.lastbeat = time.Now().Unix()
|
||||
return u.Conn.Read(p)
|
||||
}
|
||||
|
||||
func (u UDPConn) Work() {
|
||||
buf := make([]byte, 8192)
|
||||
for {
|
||||
count, err := u.Read(buf)
|
||||
if err != nil {
|
||||
u.Close()
|
||||
u.lastbeat = 0
|
||||
return
|
||||
}
|
||||
_, err = u.listen.Write(buf[0:count])
|
||||
if err != nil {
|
||||
u.lastbeat = 0
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NetForward) runUDP() error {
|
||||
var mu sync.RWMutex
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%v", n.LocalAddr, n.LocalPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listen, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
starlog.Infof("Listening UDP on %v\n", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort))
|
||||
go func() {
|
||||
<-n.stopCtx.Done()
|
||||
listen.Close()
|
||||
}()
|
||||
udpMap := make(map[string]UDPConn)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-n.stopCtx.Done():
|
||||
return
|
||||
case <-time.After(time.Second * 60):
|
||||
mu.Lock()
|
||||
for k, v := range udpMap {
|
||||
if time.Now().Unix() > int64(n.UDPTimeout.Seconds())+v.lastbeat {
|
||||
delete(udpMap, k)
|
||||
starlog.Noticef("Connection Closed %s <==> %s", v.remoteAddr.String(), n.RemoteURI)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
buf := make([]byte, 8192)
|
||||
for {
|
||||
count, rmt, err := listen.ReadFromUDP(buf)
|
||||
if err != nil || rmt.String() == n.RemoteURI {
|
||||
continue
|
||||
}
|
||||
go func(data []byte, rmt *net.UDPAddr) {
|
||||
log := starlog.Std.NewFlag()
|
||||
mu.Lock()
|
||||
addr, ok := udpMap[rmt.String()]
|
||||
if !ok {
|
||||
log.Infof("Accept New UDP Conn from %v\n", rmt.String())
|
||||
conn, err := net.Dial("udp", n.RemoteURI)
|
||||
if err != nil {
|
||||
log.Errorf("Dial Remote %s Failed:%v\n", n.RemoteURI, err)
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
addr = UDPConn{
|
||||
Conn: conn,
|
||||
remoteAddr: rmt,
|
||||
listen: listen,
|
||||
lastbeat: time.Now().Unix(),
|
||||
}
|
||||
udpMap[rmt.String()] = addr
|
||||
go addr.Work()
|
||||
log.Infof("Connect %s <==> %s\n", rmt.String(), n.RemoteURI)
|
||||
}
|
||||
mu.Unlock()
|
||||
_, err := addr.Write(data)
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
addr.Close()
|
||||
delete(udpMap, addr.remoteAddr.String())
|
||||
mu.Unlock()
|
||||
log.Noticef("Connection Closed %s <==> %s", rmt.String(), n.RemoteURI)
|
||||
}
|
||||
}(buf[0:count], rmt)
|
||||
}
|
||||
}
|
||||
|
||||
func Copy(dst, src net.Conn) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(dst, src)
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(src, dst)
|
||||
}()
|
||||
wg.Wait()
|
||||
dst.Close()
|
||||
src.Close()
|
||||
}
|
16
net/forward_test.go
Normal file
16
net/forward_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package net
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestForward(t *testing.T) {
|
||||
var f = NetForward{
|
||||
LocalAddr: "127.0.0.1",
|
||||
LocalPort: 22232,
|
||||
RemoteURI: "127.0.0.1:1127",
|
||||
EnableTCP: true,
|
||||
EnableUDP: true,
|
||||
DialTimeout: 0,
|
||||
UDPTimeout: 0,
|
||||
}
|
||||
f.Run()
|
||||
}
|
27
net/natclient.go
Normal file
27
net/natclient.go
Normal file
@ -0,0 +1,27 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type SimpleNatClient struct {
|
||||
mu sync.RWMutex
|
||||
cmdTCPConn net.Conn
|
||||
cmdUDPConn *net.UDPAddr
|
||||
ServiceTarget string
|
||||
CmdTarget string
|
||||
tcpAlived bool
|
||||
}
|
||||
|
||||
func (s *SimpleNatClient) tcpCmdConn() net.Conn {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.cmdTCPConn
|
||||
}
|
||||
|
||||
func (s *SimpleNatClient) tcpCmdConnAlived() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.tcpAlived
|
||||
}
|
138
net/natserver.go
Normal file
138
net/natserver.go
Normal file
@ -0,0 +1,138 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var MSG_CMD_HELLO = []byte{11, 27, 19, 96, 182, 18, 25, 150, 17, 39}
|
||||
var MSG_NEW_CONN = []byte{0, 0, 0, 0, 255, 255, 255, 255, 11, 27}
|
||||
var MSG_NEW_CONN_REQ = []byte{0, 0, 0, 0, 255, 255, 255, 255, 19, 96}
|
||||
var MSG_CLOSE = []byte{255, 255, 0, 0, 255, 0, 0, 255, 255, 27}
|
||||
var MSG_HEARTBEAT = []byte{6, 66, 66, 6, 6, 66, 6, 66, 11, 27}
|
||||
|
||||
type SimpleNatServer struct {
|
||||
mu sync.RWMutex
|
||||
cmdTCPConn net.Conn
|
||||
cmdUDPConn *net.UDPAddr
|
||||
listenTcp net.Listener
|
||||
listenUDP *net.UDPConn
|
||||
Addr string
|
||||
Port int
|
||||
lastTCPHeart int64
|
||||
lastUDPHeart int64
|
||||
Passwd string
|
||||
DialTimeout int64
|
||||
UDPTimeout int64
|
||||
running int32
|
||||
|
||||
tcpConnPool chan net.Conn
|
||||
tcpAlived bool
|
||||
}
|
||||
|
||||
func (s *SimpleNatServer) getConnfromTCPPool() (net.Conn, error) {
|
||||
select {
|
||||
case conn := <-s.tcpConnPool:
|
||||
return conn, nil
|
||||
case <-time.After(time.Second * 10):
|
||||
return nil, errors.New("no connection got")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleNatServer) tcpCmdConn() net.Conn {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.cmdTCPConn
|
||||
}
|
||||
|
||||
func (s *SimpleNatServer) tcpCmdConnAlived() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.tcpAlived
|
||||
}
|
||||
|
||||
func (s *SimpleNatServer) listenTCP() error {
|
||||
var err error
|
||||
s.tcpConnPool = make(chan net.Conn, 10)
|
||||
s.listenTcp, err = net.Listen("tcp", fmt.Sprintf("%s:d", s.Addr, s.Port))
|
||||
if err != nil {
|
||||
starlog.Errorln("failed to listen tcp", err)
|
||||
return err
|
||||
}
|
||||
for {
|
||||
conn, err := s.listenTcp.Accept()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if s.tcpCmdConnAlived() {
|
||||
go s.tcpClientServe(conn.(*net.TCPConn))
|
||||
continue
|
||||
}
|
||||
go s.waitingForTCPCmd(conn.(*net.TCPConn))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SimpleNatServer) tcpClientServe(conn *net.TCPConn) {
|
||||
if !s.tcpCmdConnAlived() {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Split(conn.RemoteAddr().String(), ":")[0] == strings.Split(s.tcpCmdConn().RemoteAddr().String(), ":")[0] {
|
||||
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||||
cmdBuf := make([]byte, 10)
|
||||
if _, err := io.ReadFull(conn, cmdBuf); err == nil {
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
if bytes.Equal(cmdBuf, MSG_NEW_CONN) {
|
||||
starlog.Noticef("Nat Server Recv New Client Conn From %v\n", conn.RemoteAddr().String())
|
||||
s.tcpConnPool <- conn
|
||||
return
|
||||
}
|
||||
}
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
}
|
||||
starlog.Noticef("Nat Server Recv New Side Conn From %v\n", conn.RemoteAddr().String())
|
||||
_, err := s.tcpCmdConn().Write(MSG_NEW_CONN_REQ)
|
||||
if err != nil {
|
||||
s.mu.Lock()
|
||||
s.cmdTCPConn.Close()
|
||||
s.tcpAlived = false
|
||||
s.mu.Unlock()
|
||||
starlog.Errorf("Failed to Write CMD To Client:%v\n", err)
|
||||
return
|
||||
}
|
||||
reverse, err := s.getConnfromTCPPool()
|
||||
if err != nil {
|
||||
starlog.Errorf("Nat Server Conn to %v Closed %v\n", conn.RemoteAddr(), err)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
starlog.Infof("Nat Server Conn %v<==>%v Connected\n", conn.RemoteAddr(), reverse.RemoteAddr())
|
||||
Copy(reverse, conn)
|
||||
starlog.Warningf("Nat Server Conn %v<==>%v Closed\n", conn.RemoteAddr(), reverse.RemoteAddr())
|
||||
}
|
||||
|
||||
func (s *SimpleNatServer) waitingForTCPCmd(conn *net.TCPConn) {
|
||||
conn.SetReadDeadline(time.Now().Add(time.Duration(s.DialTimeout) * time.Second))
|
||||
cmdBuf := make([]byte, 10)
|
||||
if _, err := io.ReadFull(conn, cmdBuf); err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
if bytes.Equal(cmdBuf, MSG_CMD_HELLO) {
|
||||
s.mu.Lock()
|
||||
s.cmdTCPConn = conn
|
||||
s.tcpAlived = true
|
||||
conn.SetKeepAlive(true)
|
||||
conn.SetKeepAlivePeriod(time.Second * 20)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
}
|
69
rmt/remoteCmdClient.go
Normal file
69
rmt/remoteCmdClient.go
Normal file
@ -0,0 +1,69 @@
|
||||
package rmt
|
||||
|
||||
import (
|
||||
"b612.me/notify"
|
||||
"b612.me/notify/starnotify"
|
||||
"b612.me/starlog"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
rmtRmt string
|
||||
rmtCmd RmtCmd
|
||||
)
|
||||
|
||||
func init() {
|
||||
Cmdc.Flags().StringVarP(&rmtRmt, "remote", "r", "", "Remote Address")
|
||||
Cmdc.Flags().StringVarP(&rmtPol, "protocol", "o", "tcp", "Remote protocol")
|
||||
Cmdc.Flags().StringVarP(&rmtCmd.Cmd, "cmd", "c", "", "command")
|
||||
Cmdc.Flags().StringVarP(&rmtCmd.WorkDir, "workdir", "w", "", "workdir")
|
||||
Cmdc.Flags().StringSliceVarP(&rmtCmd.Env, "env", "e", []string{}, "env")
|
||||
Cmdc.Flags().BoolVarP(&rmtCmd.Daemon, "daemon", "d", false, "daemon")
|
||||
Cmdc.Flags().BoolVarP(&rmtCmd.Stream, "stream", "s", false, "stream")
|
||||
Cmdc.Flags().StringVarP(&rmtListenPort, "port", "p", "5780", "Remote Port")
|
||||
}
|
||||
|
||||
var Cmdc = &cobra.Command{
|
||||
Use: "rmtc",
|
||||
Short: "simple remote shell client",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if rmtRmt == "" {
|
||||
starlog.Errorln("Please Enter Remote Path")
|
||||
os.Exit(1)
|
||||
}
|
||||
err := starnotify.NewClient("c").Connect(rmtPol, fmt.Sprintf("%s:%s", rmtRmt, rmtListenPort))
|
||||
if err != nil {
|
||||
starlog.Errorln("Create Remote Failed", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
starnotify.C("c").SetLink("stream", RmtSteam)
|
||||
defer starnotify.C("c").Stop()
|
||||
cdata, err := starnotify.C("c").SendWaitObj("cmd", rmtCmd, time.Second*3600)
|
||||
if err != nil {
|
||||
starlog.Errorln("Got Answer Failed:", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
data, err := cdata.Value.ToInterface()
|
||||
if err != nil {
|
||||
starlog.Errorln("Decode FAILED:", err)
|
||||
os.Exit(4)
|
||||
}
|
||||
rtnData, ok := data.(RmtCmdBack)
|
||||
if !ok {
|
||||
starlog.Errorln("Decode FAILED2:", err)
|
||||
os.Exit(5)
|
||||
}
|
||||
fmt.Println("Return Pid:", rtnData.RetCode)
|
||||
if !rmtCmd.Stream {
|
||||
fmt.Println("Return OutPut\n", rtnData.OutPut)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func RmtSteam(msg *notify.Message) {
|
||||
fmt.Println(strings.TrimSpace(msg.Value.MustToString()))
|
||||
}
|
166
rmt/remoteCmdServer.go
Normal file
166
rmt/remoteCmdServer.go
Normal file
@ -0,0 +1,166 @@
|
||||
package rmt
|
||||
|
||||
import (
|
||||
"b612.me/notify"
|
||||
"b612.me/notify/starnotify"
|
||||
"b612.me/starlog"
|
||||
"b612.me/staros"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RmtCmd struct {
|
||||
Env []string
|
||||
WorkDir string
|
||||
Cmd string
|
||||
Daemon bool
|
||||
Stream bool
|
||||
}
|
||||
|
||||
type RmtCmdBack struct {
|
||||
RetCode int
|
||||
OutPut string
|
||||
}
|
||||
|
||||
var (
|
||||
rmtListenPort string
|
||||
rmtPol string
|
||||
)
|
||||
|
||||
func init() {
|
||||
notify.RegisterName("remoteback", RmtCmdBack{})
|
||||
notify.RegisterName("remotecmd", RmtCmd{})
|
||||
Cmds.Flags().StringVarP(&rmtListenPort, "port", "p", "5780", "Listen Port")
|
||||
Cmds.Flags().StringVarP(&rmtPol, "protocol", "o", "tcp", "Remote protocol")
|
||||
}
|
||||
|
||||
var Cmds = &cobra.Command{
|
||||
Use: "rmts",
|
||||
Short: "simple remote shell server",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if rmtListenPort == "" {
|
||||
starlog.Errorln("Please Enter Port")
|
||||
os.Exit(1)
|
||||
}
|
||||
err := starnotify.NewServer("s").Listen(rmtPol, "0.0.0.0:"+rmtListenPort)
|
||||
if err != nil {
|
||||
starlog.Errorln("Create Listener Failed", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
starnotify.S("s").SetLink("cmd", ListenAndServe)
|
||||
starlog.Infoln("Service Running")
|
||||
stopSig := make(chan os.Signal)
|
||||
signal.Notify(stopSig, os.Kill, os.Interrupt)
|
||||
<-stopSig
|
||||
starlog.Noticeln("Recv Stop Sig")
|
||||
starnotify.S("s").Stop()
|
||||
},
|
||||
}
|
||||
|
||||
func ListenAndServe(msg *notify.Message) {
|
||||
data, err := msg.Value.ToInterface()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cmd, ok := data.(RmtCmd)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var sysName, sysArg string = "bash", "-c"
|
||||
if runtime.GOOS == "windows" {
|
||||
sysName = "cmd.exe"
|
||||
sysArg = "/c"
|
||||
}
|
||||
if cmd.Cmd == "" {
|
||||
msg.ReplyObj(RmtCmdBack{
|
||||
RetCode: 255,
|
||||
OutPut: "Please enter the command",
|
||||
})
|
||||
return
|
||||
}
|
||||
starlog.Noticef("Recv Command:%+v\n", cmd)
|
||||
var myCmd *staros.StarCmd
|
||||
myCmd, err = staros.Command(sysName, sysArg, cmd.Cmd)
|
||||
if err != nil {
|
||||
msg.ReplyObj(RmtCmdBack{
|
||||
RetCode: 255,
|
||||
OutPut: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
if cmd.WorkDir != "" {
|
||||
myCmd.CMD.Dir = cmd.WorkDir
|
||||
}
|
||||
if len(cmd.Env) > 0 {
|
||||
myCmd.CMD.Env = cmd.Env
|
||||
}
|
||||
if cmd.Daemon {
|
||||
err := myCmd.Release()
|
||||
if err != nil {
|
||||
msg.ReplyObj(RmtCmdBack{
|
||||
RetCode: 254,
|
||||
OutPut: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
pid := myCmd.CMD.Process.Pid
|
||||
msg.ReplyObj(RmtCmdBack{
|
||||
RetCode: 0,
|
||||
OutPut: fmt.Sprintf("Runned,PID is %d", pid),
|
||||
})
|
||||
return
|
||||
}
|
||||
err = myCmd.Start()
|
||||
if err != nil {
|
||||
msg.ReplyObj(RmtCmdBack{
|
||||
RetCode: 254,
|
||||
OutPut: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
for myCmd.IsRunning() {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
if cmd.Stream {
|
||||
std, err := myCmd.NowAllOutput()
|
||||
if err != nil {
|
||||
std += "\n" + err.Error()
|
||||
}
|
||||
std = strings.TrimSpace(std)
|
||||
if len(std) == 0 {
|
||||
continue
|
||||
}
|
||||
msg.ClientConn.Server().SendObj(msg.ClientConn, "stream", std)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
if !cmd.Stream {
|
||||
std := myCmd.AllStdOut()
|
||||
if myCmd.AllStdErr() != nil {
|
||||
std += "\n" + myCmd.AllStdErr().Error()
|
||||
}
|
||||
msg.ReplyObj(RmtCmdBack{
|
||||
RetCode: myCmd.ExitCode(),
|
||||
OutPut: std,
|
||||
})
|
||||
} else {
|
||||
std, err := myCmd.NowAllOutput()
|
||||
if err != nil {
|
||||
std += "\n" + err.Error()
|
||||
}
|
||||
msg.ClientConn.Server().SendObj(msg.ClientConn, "stream", std)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
err = msg.ReplyObj(RmtCmdBack{
|
||||
RetCode: myCmd.ExitCode(),
|
||||
OutPut: "",
|
||||
})
|
||||
if err != nil {
|
||||
starlog.Warningln("Reply failed:", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
98
search/search.go
Normal file
98
search/search.go
Normal file
@ -0,0 +1,98 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"b612.me/stario"
|
||||
"b612.me/starlog"
|
||||
"b612.me/startext"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var stFolder string
|
||||
var stNum, stMax, stMin int
|
||||
var stautoGBK bool
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringVarP(&stFolder, "folder", "f", "./", "搜索的文件夹")
|
||||
Cmd.Flags().IntVarP(&stNum, "thread-num", "n", 5, "并发搜寻协程数")
|
||||
Cmd.Flags().BoolVarP(&stautoGBK, "autogbk", "g", true, "自动GBK识别")
|
||||
Cmd.Flags().IntVar(&stMax, "max", 0, "行最大字数")
|
||||
Cmd.Flags().IntVar(&stMin, "min", 0, "行最小字数")
|
||||
}
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "st",
|
||||
Short: "搜索文件中特定字符串",
|
||||
Long: "搜索文件中特定字符串",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
starlog.Errorln("应当传入两个参数,搜寻文件后缀和搜寻文本")
|
||||
os.Exit(1)
|
||||
}
|
||||
err := searchText(stFolder, args[0], args[1], stNum, stautoGBK, stMax, stMin)
|
||||
if err != nil {
|
||||
os.Exit(2)
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
func searchText(folder string, filematch string, text string, thread int, autoGBK bool, max, min int) error {
|
||||
data, err := ioutil.ReadDir(folder)
|
||||
if err != nil {
|
||||
starlog.Errorln("read folder failed", folder, err)
|
||||
return err
|
||||
}
|
||||
wg := stario.NewWaitGroup(thread)
|
||||
searchFn := func(filepath string, text string) {
|
||||
//starlog.Debugln("searching", filepath, text)
|
||||
defer wg.Done()
|
||||
fp, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
starlog.Errorln("open file failed", filepath, err)
|
||||
return
|
||||
}
|
||||
defer fp.Close()
|
||||
reader := bufio.NewReader(fp)
|
||||
count := 0
|
||||
for {
|
||||
origin, err := reader.ReadString('\n')
|
||||
count++
|
||||
if stautoGBK && startext.IsGBK([]byte(origin)) {
|
||||
originByte, _ := startext.GBK2UTF8([]byte(origin))
|
||||
origin = string(originByte)
|
||||
}
|
||||
origin = strings.TrimSpace(origin)
|
||||
if max != 0 && len(origin) > max {
|
||||
continue
|
||||
}
|
||||
if min != 0 && len(origin) < min {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(origin, text) {
|
||||
fmt.Printf("file:%s line:%d matched:%s\n", filepath, count, origin)
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range data {
|
||||
if v.IsDir() {
|
||||
searchText(filepath.Join(folder, v.Name()), filematch, text, thread, autoGBK, stMax, stMin)
|
||||
}
|
||||
filepath := filepath.Join(folder, v.Name())
|
||||
if matched, _ := regexp.MatchString(filematch, filepath); matched {
|
||||
wg.Add(1)
|
||||
go searchFn(filepath, text)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
26
sftp/sftp.go
Normal file
26
sftp/sftp.go
Normal file
@ -0,0 +1,26 @@
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"b612.me/starssh"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func TransferFile(s *starssh.StarSSH, local, remote string, isPull, fullname bool) error {
|
||||
if !fullname && !isPull {
|
||||
s.ShellOne("mkdir -p " + remote)
|
||||
remote = remote + "/" + filepath.Base(local)
|
||||
}
|
||||
if !fullname && isPull {
|
||||
os.MkdirAll(local, 0755)
|
||||
local = filepath.Join(local, path.Base(remote))
|
||||
}
|
||||
if fullname && isPull {
|
||||
os.MkdirAll(filepath.Dir(local), 0755)
|
||||
}
|
||||
if fullname && !isPull {
|
||||
os.MkdirAll(path.Dir(remote), 0755)
|
||||
}
|
||||
return nil
|
||||
}
|
60
split/split.go
Normal file
60
split/split.go
Normal file
@ -0,0 +1,60 @@
|
||||
package split
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"b612.me/starlog"
|
||||
"b612.me/staros"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "split",
|
||||
Short: "分割文件",
|
||||
Long: "按字节或文件数分割文件",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
var src, dst string
|
||||
var num int
|
||||
if len(args) == 3 {
|
||||
src = args[0]
|
||||
dst = args[1]
|
||||
num, _ = strconv.Atoi(args[2])
|
||||
} else {
|
||||
src, _ = this.Flags().GetString("src")
|
||||
dst, _ = this.Flags().GetString("dst")
|
||||
num, _ = this.Flags().GetInt("num")
|
||||
}
|
||||
if !staros.Exists(src) {
|
||||
starlog.Errorln("源文件不存在")
|
||||
this.Help()
|
||||
return
|
||||
}
|
||||
if num == 0 {
|
||||
starlog.Errorln("参数num不合法", "red")
|
||||
this.Help()
|
||||
return
|
||||
}
|
||||
ok, _ := this.Flags().GetBool("byte")
|
||||
err := starcrypto.SplitFile(src, dst, num, !ok, func(pect float64) {
|
||||
if pect == 100 {
|
||||
fmt.Println("文件已处理:100.000000%")
|
||||
} else {
|
||||
fmt.Printf("文件已处理:%f%%\r", pect)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
starlog.Errorln(err.Error)
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringP("src", "s", "", "源文件地址")
|
||||
Cmd.Flags().StringP("dst", "d", "./split*.vicque", "目标文件地址,用*替换文件数字")
|
||||
Cmd.Flags().BoolP("byte", "b", false, "按byte分割")
|
||||
Cmd.Flags().IntP("num", "n", 0, "分割数/byte数")
|
||||
}
|
174
tcping/cmd.go
Normal file
174
tcping/cmd.go
Normal file
@ -0,0 +1,174 @@
|
||||
package tcping
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
counter int
|
||||
timeout string
|
||||
interval string
|
||||
sigs chan os.Signal
|
||||
|
||||
httpMode bool
|
||||
httpHead bool
|
||||
httpPost bool
|
||||
httpUA string
|
||||
|
||||
permanent bool
|
||||
|
||||
dnsServer []string
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "tcping",
|
||||
Short: "tcp/http ping",
|
||||
Long: "使用进行Tcp或Http协议进行ping探测",
|
||||
Example: `
|
||||
1. ping over tcp
|
||||
> tcping google.com
|
||||
2. ping over tcp with custom port
|
||||
> tcping google.com 443
|
||||
3. ping over http
|
||||
> tcping -H google.com
|
||||
4. ping with URI schema
|
||||
> tcping http://hui.lu
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
sigs = make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
if permanent && counter != 4 {
|
||||
fmt.Println("不能同时指定-t与-c,请检查您的输入")
|
||||
return
|
||||
}
|
||||
if permanent {
|
||||
counter = 0
|
||||
}
|
||||
if len(args) != 2 && len(args) != 1 {
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
host := args[0]
|
||||
|
||||
var (
|
||||
err error
|
||||
port int
|
||||
schema string
|
||||
)
|
||||
if len(args) == 2 {
|
||||
port, err = strconv.Atoi(args[1])
|
||||
if err != nil {
|
||||
fmt.Println("端口应当为Int类型")
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
schema = TCP.String()
|
||||
} else {
|
||||
var matched bool
|
||||
schema, host, port, matched = CheckURI(host)
|
||||
if !matched {
|
||||
fmt.Println("不是一个合法的URI")
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
}
|
||||
var timeoutDuration time.Duration
|
||||
if res, err := strconv.Atoi(timeout); err == nil {
|
||||
timeoutDuration = time.Duration(res) * time.Millisecond
|
||||
} else {
|
||||
timeoutDuration, err = time.ParseDuration(timeout)
|
||||
if err != nil {
|
||||
fmt.Println("parse timeout failed", err)
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var intervalDuration time.Duration
|
||||
if res, err := strconv.Atoi(interval); err == nil {
|
||||
intervalDuration = time.Duration(res) * time.Millisecond
|
||||
} else {
|
||||
intervalDuration, err = time.ParseDuration(interval)
|
||||
if err != nil {
|
||||
fmt.Println("parse interval failed", err)
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
}
|
||||
var protocol Protocol
|
||||
if httpMode {
|
||||
protocol = HTTP
|
||||
} else {
|
||||
protocol, err = NewProtocol(schema)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(dnsServer) != 0 {
|
||||
UseCustomeDNS(dnsServer)
|
||||
}
|
||||
|
||||
parseHost := FormatIP(host)
|
||||
target := Target{
|
||||
Timeout: timeoutDuration,
|
||||
Interval: intervalDuration,
|
||||
Host: parseHost,
|
||||
Port: port,
|
||||
Counter: counter,
|
||||
Protocol: protocol,
|
||||
}
|
||||
var pinger Pinger
|
||||
switch protocol {
|
||||
case TCP:
|
||||
pinger = NewTCPing()
|
||||
case HTTP, HTTPS:
|
||||
var httpMethod string
|
||||
switch {
|
||||
case httpHead:
|
||||
httpMethod = "HEAD"
|
||||
case httpPost:
|
||||
httpMethod = "POST"
|
||||
default:
|
||||
httpMethod = "GET"
|
||||
}
|
||||
pinger = NewHTTPing(httpMethod)
|
||||
default:
|
||||
fmt.Printf("schema: %s not support\n", schema)
|
||||
cmd.Usage()
|
||||
return
|
||||
}
|
||||
pinger.SetTarget(&target)
|
||||
pingerDone := pinger.Start()
|
||||
select {
|
||||
case <-pingerDone:
|
||||
break
|
||||
case <-sigs:
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Println(pinger.Result())
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().IntVarP(&counter, "counter", "c", 4, "ping的次数")
|
||||
Cmd.Flags().BoolVarP(&permanent, "permanent", "t", false, "一直ping下去")
|
||||
Cmd.Flags().StringVarP(&timeout, "timeout", "T", "1s", `超时时间, 单位为 "ns", "us" (or "µs"), "ms", "s", "m", "h"`)
|
||||
Cmd.Flags().StringVarP(&interval, "interval", "I", "1s", `ping间隔时间, 单位为 "ns", "us" (or "µs"), "ms", "s", "m", "h"`)
|
||||
|
||||
Cmd.Flags().BoolVarP(&httpMode, "http", "H", false, `Use "HTTP" mode. will ignore URI Schema, force to http`)
|
||||
Cmd.Flags().BoolVar(&httpHead, "head", false, `使用http head模式`)
|
||||
Cmd.Flags().BoolVar(&httpPost, "post", false, `使用http post模式`)
|
||||
Cmd.Flags().StringVar(&httpUA, "user-agent", "victorique/tcping", `自定义UA`)
|
||||
|
||||
Cmd.Flags().StringArrayVarP(&dnsServer, "dns-server", "D", nil, `使用自定义DNS服务器`)
|
||||
|
||||
}
|
17
tcping/fqdn.go
Normal file
17
tcping/fqdn.go
Normal file
@ -0,0 +1,17 @@
|
||||
package tcping
|
||||
|
||||
import "net"
|
||||
|
||||
// GetIP ...
|
||||
func GetIP(hostname string) string {
|
||||
addrs, err := net.LookupIP(hostname)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if ipv4 := addr.To4(); ipv4 != nil {
|
||||
return ipv4.String()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
120
tcping/http.go
Normal file
120
tcping/http.go
Normal file
@ -0,0 +1,120 @@
|
||||
package tcping
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPing ...
|
||||
type HTTPing struct {
|
||||
target *Target
|
||||
done chan struct{}
|
||||
result *Result
|
||||
Method string
|
||||
}
|
||||
|
||||
var _ Pinger = (*HTTPing)(nil)
|
||||
|
||||
// NewHTTPing return new HTTPing
|
||||
func NewHTTPing(method string) *HTTPing {
|
||||
return &HTTPing{
|
||||
done: make(chan struct{}),
|
||||
Method: method,
|
||||
}
|
||||
}
|
||||
|
||||
// SetTarget ...
|
||||
func (ping *HTTPing) SetTarget(target *Target) {
|
||||
ping.target = target
|
||||
if ping.result == nil {
|
||||
ping.result = &Result{Target: target}
|
||||
}
|
||||
}
|
||||
|
||||
// Start ping
|
||||
func (ping *HTTPing) Start() <-chan struct{} {
|
||||
go func() {
|
||||
t := time.NewTicker(ping.target.Interval)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if ping.result.Counter >= ping.target.Counter && ping.target.Counter != 0 {
|
||||
ping.Stop()
|
||||
return
|
||||
}
|
||||
duration, resp, remoteAddr, err := ping.ping()
|
||||
ping.result.Counter++
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Ping %s - failed: %s\n", ping.target, err)
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
length, _ := io.Copy(ioutil.Discard, resp.Body)
|
||||
fmt.Printf("Ping %s(%s) - %s is open - time=%s method=%s status=%d bytes=%d\n", ping.target, remoteAddr, ping.target.Protocol, duration, ping.Method, resp.StatusCode, length)
|
||||
if ping.result.MinDuration == 0 {
|
||||
ping.result.MinDuration = duration
|
||||
}
|
||||
if ping.result.MaxDuration == 0 {
|
||||
ping.result.MaxDuration = duration
|
||||
}
|
||||
ping.result.SuccessCounter++
|
||||
if duration > ping.result.MaxDuration {
|
||||
ping.result.MaxDuration = duration
|
||||
} else if duration < ping.result.MinDuration {
|
||||
ping.result.MinDuration = duration
|
||||
}
|
||||
ping.result.TotalDuration += duration
|
||||
}
|
||||
case <-ping.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ping.done
|
||||
}
|
||||
|
||||
// Result return ping result
|
||||
func (ping *HTTPing) Result() *Result {
|
||||
return ping.result
|
||||
}
|
||||
|
||||
// Stop the tcping
|
||||
func (ping *HTTPing) Stop() {
|
||||
ping.done <- struct{}{}
|
||||
}
|
||||
|
||||
func (ping HTTPing) ping() (time.Duration, *http.Response, string, error) {
|
||||
var resp *http.Response
|
||||
var body io.Reader
|
||||
if ping.Method == "POST" {
|
||||
body = bytes.NewBufferString("{}")
|
||||
}
|
||||
req, err := http.NewRequest(ping.Method, ping.target.String(), body)
|
||||
req.Header.Set(http.CanonicalHeaderKey("User-Agent"), "tcping")
|
||||
if err != nil {
|
||||
return 0, nil, "", err
|
||||
}
|
||||
var remoteAddr string
|
||||
trace := &httptrace.ClientTrace{
|
||||
ConnectStart: func(network, addr string) {
|
||||
remoteAddr = addr
|
||||
},
|
||||
}
|
||||
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
|
||||
duration, errIfce := timeIt(func() interface{} {
|
||||
client := http.Client{Timeout: ping.target.Timeout}
|
||||
resp, err = client.Do(req)
|
||||
return err
|
||||
})
|
||||
if errIfce != nil {
|
||||
err := errIfce.(error)
|
||||
return 0, nil, "", err
|
||||
}
|
||||
return time.Duration(duration), resp, remoteAddr, nil
|
||||
}
|
149
tcping/ping.go
Normal file
149
tcping/ping.go
Normal file
@ -0,0 +1,149 @@
|
||||
package tcping
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Protocol ...
|
||||
type Protocol int
|
||||
|
||||
func (protocol Protocol) String() string {
|
||||
switch protocol {
|
||||
case TCP:
|
||||
return "tcp"
|
||||
case HTTP:
|
||||
return "http"
|
||||
case HTTPS:
|
||||
return "https"
|
||||
}
|
||||
return "unkown"
|
||||
}
|
||||
|
||||
const (
|
||||
// TCP is tcp protocol
|
||||
TCP Protocol = iota
|
||||
// HTTP is http protocol
|
||||
HTTP
|
||||
// HTTPS is https protocol
|
||||
HTTPS
|
||||
)
|
||||
|
||||
// NewProtocol convert protocol stirng to Protocol
|
||||
func NewProtocol(protocol string) (Protocol, error) {
|
||||
switch strings.ToLower(protocol) {
|
||||
case TCP.String():
|
||||
return TCP, nil
|
||||
case HTTP.String():
|
||||
return HTTP, nil
|
||||
case HTTPS.String():
|
||||
return HTTPS, nil
|
||||
}
|
||||
return 0, fmt.Errorf("protocol %s not support", protocol)
|
||||
}
|
||||
|
||||
// Target is a ping
|
||||
type Target struct {
|
||||
Protocol Protocol
|
||||
Host string
|
||||
Port int
|
||||
|
||||
Counter int
|
||||
Interval time.Duration
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func (target Target) String() string {
|
||||
return fmt.Sprintf("%s://%s:%d", target.Protocol, target.Host, target.Port)
|
||||
}
|
||||
|
||||
// Pinger is a ping interface
|
||||
type Pinger interface {
|
||||
Start() <-chan struct{}
|
||||
Stop()
|
||||
Result() *Result
|
||||
SetTarget(target *Target)
|
||||
}
|
||||
|
||||
// Ping is a ping interface
|
||||
type Ping interface {
|
||||
Start() <-chan struct{}
|
||||
|
||||
Host() string
|
||||
Port() int
|
||||
Protocol() Protocol
|
||||
Counter() int
|
||||
|
||||
Stop()
|
||||
|
||||
Result() Result
|
||||
}
|
||||
|
||||
// Result ...
|
||||
type Result struct {
|
||||
Counter int
|
||||
SuccessCounter int
|
||||
Target *Target
|
||||
|
||||
MinDuration time.Duration
|
||||
MaxDuration time.Duration
|
||||
TotalDuration time.Duration
|
||||
}
|
||||
|
||||
// Avg return the average time of ping
|
||||
func (result Result) Avg() time.Duration {
|
||||
if result.SuccessCounter == 0 {
|
||||
return 0
|
||||
}
|
||||
return result.TotalDuration / time.Duration(result.SuccessCounter)
|
||||
}
|
||||
|
||||
// Failed return failed counter
|
||||
func (result Result) Failed() int {
|
||||
return result.Counter - result.SuccessCounter
|
||||
}
|
||||
|
||||
func (result Result) String() string {
|
||||
const resultTpl = `
|
||||
Ping statistics {{.Target}}
|
||||
{{.Counter}} probes sent.
|
||||
{{.SuccessCounter}} successful, {{.Failed}} failed.
|
||||
Approximate trip times:
|
||||
Minimum = {{.MinDuration}}, Maximum = {{.MaxDuration}}, Average = {{.Avg}}`
|
||||
t := template.Must(template.New("result").Parse(resultTpl))
|
||||
res := bytes.NewBufferString("")
|
||||
t.Execute(res, result)
|
||||
return res.String()
|
||||
}
|
||||
|
||||
// CheckURI check uri
|
||||
func CheckURI(uri string) (schema, host string, port int, matched bool) {
|
||||
const reExp = `^((?P<schema>((ht|f)tp(s?))|tcp)\://)?((([a-zA-Z0-9_\-]+\.)+[a-zA-Z]{2,})|((?:(?:25[0-5]|2[0-4]\d|[01]\d\d|\d?\d)((\.?\d)\.)){4})|(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9]))(:([0-9]+))?(/[a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~]*)?$`
|
||||
pattern := regexp.MustCompile(reExp)
|
||||
res := pattern.FindStringSubmatch(uri)
|
||||
if len(res) == 0 {
|
||||
return
|
||||
}
|
||||
matched = true
|
||||
schema = res[2]
|
||||
if schema == "" {
|
||||
schema = "tcp"
|
||||
}
|
||||
host = res[6]
|
||||
if res[17] == "" {
|
||||
if schema == HTTPS.String() {
|
||||
port = 443
|
||||
} else {
|
||||
port = 80
|
||||
}
|
||||
} else {
|
||||
port, _ = strconv.Atoi(res[17])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
102
tcping/tcp.go
Normal file
102
tcping/tcp.go
Normal file
@ -0,0 +1,102 @@
|
||||
package tcping
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TCPing ...
|
||||
type TCPing struct {
|
||||
target *Target
|
||||
done chan struct{}
|
||||
result *Result
|
||||
}
|
||||
|
||||
var _ Pinger = (*TCPing)(nil)
|
||||
|
||||
// NewTCPing return a new TCPing
|
||||
func NewTCPing() *TCPing {
|
||||
tcping := TCPing{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
return &tcping
|
||||
}
|
||||
|
||||
// SetTarget set target for TCPing
|
||||
func (tcping *TCPing) SetTarget(target *Target) {
|
||||
tcping.target = target
|
||||
if tcping.result == nil {
|
||||
tcping.result = &Result{Target: target}
|
||||
}
|
||||
}
|
||||
|
||||
// Result return the result
|
||||
func (tcping TCPing) Result() *Result {
|
||||
return tcping.result
|
||||
}
|
||||
|
||||
// Start a tcping
|
||||
func (tcping TCPing) Start() <-chan struct{} {
|
||||
go func() {
|
||||
t := time.NewTicker(tcping.target.Interval)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if tcping.result.Counter >= tcping.target.Counter && tcping.target.Counter != 0 {
|
||||
tcping.Stop()
|
||||
return
|
||||
}
|
||||
duration, remoteAddr, err := tcping.ping()
|
||||
tcping.result.Counter++
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Ping %s - failed: %s\n", tcping.target, err)
|
||||
} else {
|
||||
fmt.Printf("Ping %s(%s) - Connected - time=%s\n", tcping.target, remoteAddr, duration)
|
||||
|
||||
if tcping.result.MinDuration == 0 {
|
||||
tcping.result.MinDuration = duration
|
||||
}
|
||||
if tcping.result.MaxDuration == 0 {
|
||||
tcping.result.MaxDuration = duration
|
||||
}
|
||||
tcping.result.SuccessCounter++
|
||||
if duration > tcping.result.MaxDuration {
|
||||
tcping.result.MaxDuration = duration
|
||||
} else if duration < tcping.result.MinDuration {
|
||||
tcping.result.MinDuration = duration
|
||||
}
|
||||
tcping.result.TotalDuration += duration
|
||||
}
|
||||
case <-tcping.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return tcping.done
|
||||
}
|
||||
|
||||
// Stop the tcping
|
||||
func (tcping *TCPing) Stop() {
|
||||
tcping.done <- struct{}{}
|
||||
}
|
||||
|
||||
func (tcping TCPing) ping() (time.Duration, net.Addr, error) {
|
||||
var remoteAddr net.Addr
|
||||
duration, errIfce := timeIt(func() interface{} {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", tcping.target.Host, tcping.target.Port), tcping.target.Timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
remoteAddr = conn.RemoteAddr()
|
||||
conn.Close()
|
||||
return nil
|
||||
})
|
||||
if errIfce != nil {
|
||||
err := errIfce.(error)
|
||||
return 0, remoteAddr, err
|
||||
}
|
||||
return time.Duration(duration), remoteAddr, nil
|
||||
}
|
51
tcping/utils.go
Normal file
51
tcping/utils.go
Normal file
@ -0,0 +1,51 @@
|
||||
package tcping
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func timeIt(f func() interface{}) (int64, interface{}) {
|
||||
startAt := time.Now()
|
||||
res := f()
|
||||
endAt := time.Now()
|
||||
return endAt.UnixNano() - startAt.UnixNano(), res
|
||||
}
|
||||
|
||||
// UseCustomeDNS will set the dns to default DNS resolver for global
|
||||
func UseCustomeDNS(dns []string) {
|
||||
resolver := net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (conn net.Conn, err error) {
|
||||
for _, addr := range dns {
|
||||
if conn, err = net.Dial("udp", addr+":53"); err != nil {
|
||||
continue
|
||||
} else {
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
net.DefaultResolver = &resolver
|
||||
}
|
||||
|
||||
// FormatIP - trim spaces and format IP
|
||||
//
|
||||
// IP - the provided IP
|
||||
//
|
||||
// string - return "" if the input is neither valid IPv4 nor valid IPv6
|
||||
// return IPv4 in format like "192.168.9.1"
|
||||
// return IPv6 in format like "[2002:ac1f:91c5:1::bd59]"
|
||||
func FormatIP(IP string) string {
|
||||
|
||||
host := strings.Trim(IP, "[ ]")
|
||||
if parseIP := net.ParseIP(host); parseIP != nil && parseIP.To4() == nil {
|
||||
host = fmt.Sprintf("[%s]", host)
|
||||
}
|
||||
|
||||
return host
|
||||
}
|
54
uac/cmd.go
Normal file
54
uac/cmd.go
Normal file
@ -0,0 +1,54 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package uac
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var fpath string
|
||||
var fargs string
|
||||
var workdir string
|
||||
var showWindow bool
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringVarP(&fpath, "path", "p", "", "filepath/文件路径")
|
||||
Cmd.Flags().StringVarP(&fargs, "args", "a", "", "args use space to split/参数,空格分隔")
|
||||
Cmd.Flags().StringVarP(&workdir, "workdir", "d", "./", "workdir path")
|
||||
Cmd.Flags().BoolVarP(&showWindow, "hide-window", "w", false, "hide the window show")
|
||||
}
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "uac",
|
||||
Short: "run process with administrator permission",
|
||||
Example: "vtqe uac 'c:\\program.exe arg1 arg2'",
|
||||
Version: "2.0.0",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
showWindow = !showWindow
|
||||
workdir, _ = filepath.Abs(workdir)
|
||||
if fpath == "" && len(args) == 0 {
|
||||
starlog.Errorln("Please enter a filepath")
|
||||
os.Exit(1)
|
||||
}
|
||||
if fpath != "" {
|
||||
err := RunAsUacSimple(fpath, fargs, workdir, showWindow)
|
||||
if err != nil {
|
||||
starlog.Errorln("StartAsUac Failed", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(args) > 0 {
|
||||
err := RunAsUac(args[0], workdir, showWindow)
|
||||
if err != nil {
|
||||
starlog.Errorln("StartAsUac Failed", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
75
uac/uac.go
Normal file
75
uac/uac.go
Normal file
@ -0,0 +1,75 @@
|
||||
//go:build windows
|
||||
|
||||
package uac
|
||||
|
||||
import (
|
||||
"b612.me/wincmd"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RunAsUacSimple(fpath string, cmdArgs string, workDir string, showWindow bool) error {
|
||||
intShow := 0
|
||||
if showWindow {
|
||||
intShow = 1
|
||||
}
|
||||
return wincmd.StartProcess(fpath, cmdArgs, workDir, true, intShow)
|
||||
}
|
||||
|
||||
func RunAsUac(cmdLine, workDir string, showWindow bool) error {
|
||||
intShow := 0
|
||||
if showWindow {
|
||||
intShow = 1
|
||||
}
|
||||
fpath, args := getPathArgsFromString(cmdLine)
|
||||
return wincmd.StartProcess(fpath, strings.Join(args, " "), workDir, true, intShow)
|
||||
}
|
||||
|
||||
func getPathArgsFromString(cmdLine string) (string, []string) {
|
||||
var fpath string
|
||||
var cmdArgs []string
|
||||
var markStart rune = -1
|
||||
|
||||
var tmp []rune
|
||||
var lastRune rune
|
||||
cmdLine = strings.TrimSpace(cmdLine)
|
||||
for k, v := range cmdLine {
|
||||
if v == ' ' || v == '"' || v == '\'' {
|
||||
if k == 0 {
|
||||
markStart = v
|
||||
continue
|
||||
}
|
||||
if markStart == v && v == lastRune {
|
||||
continue
|
||||
}
|
||||
if lastRune != '\\' {
|
||||
lastRune = v
|
||||
if 0 == markStart {
|
||||
markStart = v
|
||||
continue
|
||||
} else if markStart == v || markStart == -1 {
|
||||
markStart = 0
|
||||
if v == ' ' {
|
||||
markStart = v
|
||||
}
|
||||
if fpath == "" {
|
||||
fpath = string(tmp)
|
||||
} else {
|
||||
cmdArgs = append(cmdArgs, string(tmp))
|
||||
}
|
||||
tmp = []rune{}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
lastRune = v
|
||||
tmp = append(tmp, v)
|
||||
}
|
||||
if len(tmp) != 0 {
|
||||
if fpath == "" {
|
||||
fpath = string(tmp)
|
||||
} else {
|
||||
cmdArgs = append(cmdArgs, string(tmp))
|
||||
}
|
||||
}
|
||||
return fpath, cmdArgs
|
||||
}
|
12
uac/uac_nowindows.go
Normal file
12
uac/uac_nowindows.go
Normal file
@ -0,0 +1,12 @@
|
||||
//go:build !windows
|
||||
|
||||
package uac
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "uac",
|
||||
Short: "run process with administrator permission",
|
||||
Example: "vtqe uac 'c:\\program.exe arg1 arg2'",
|
||||
Hidden: true,
|
||||
}
|
93
vic/vic.go
Normal file
93
vic/vic.go
Normal file
@ -0,0 +1,93 @@
|
||||
package vic
|
||||
|
||||
import (
|
||||
"b612.me/starcrypto"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"b612.me/starlog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "vicque",
|
||||
Short: "嵐を乗り越えて",
|
||||
Long: "あの子の未来を照らし出せ",
|
||||
Run: func(this *cobra.Command, args []string) {
|
||||
var err error
|
||||
ok, _ := this.Flags().GetBool("file")
|
||||
de, _ := this.Flags().GetBool("decode")
|
||||
pwd, _ := this.Flags().GetString("key")
|
||||
rep, _ := this.Flags().GetBool("replace")
|
||||
ext, _ := this.Flags().GetBool("extension")
|
||||
if len(args) != 2 || args[1] != "sakura" {
|
||||
starlog.Errorln("ヴィクトリカだけが使えるよ")
|
||||
return
|
||||
}
|
||||
shell := func(pect float64) {
|
||||
if pect == 100 {
|
||||
fmt.Println("已处理:100.000000%")
|
||||
} else {
|
||||
fmt.Printf("已处理:%f%%\r", pect)
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
path, _ := this.Flags().GetString("path")
|
||||
if !de {
|
||||
err = starcrypto.VicqueEncodeV1File(args[0], path, pwd, shell)
|
||||
if err == nil {
|
||||
if rep {
|
||||
os.Remove(args[0])
|
||||
os.Rename(path, args[0])
|
||||
path = args[0]
|
||||
}
|
||||
if ext {
|
||||
os.Rename(path, path+".victorique")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = starcrypto.VicqueDecodeV1File(args[0], path, pwd, shell)
|
||||
if err == nil {
|
||||
if rep {
|
||||
os.Remove(args[0])
|
||||
os.Rename(path, args[0])
|
||||
path = args[0]
|
||||
}
|
||||
if ext {
|
||||
reg := regexp.MustCompile(`(.*?)\.victorique$`)
|
||||
if reg.MatchString(path) {
|
||||
paths := reg.FindStringSubmatch(path)
|
||||
os.Rename(path, paths[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !de {
|
||||
data := starcrypto.VicqueEncodeV1([]byte(args[0]), pwd)
|
||||
fmt.Println(data)
|
||||
fmt.Println(starcrypto.Base64Encode(data))
|
||||
} else {
|
||||
var data []byte
|
||||
src, _ := starcrypto.Base64Decode(args[0])
|
||||
data = starcrypto.VicqueDecodeV1(src, pwd)
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
starlog.Errorln(err)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().BoolP("file", "f", false, "VICQUE处理文件")
|
||||
Cmd.Flags().StringP("path", "p", "./v64.encode", "指定处理地址,默认为./v64.encode")
|
||||
Cmd.Flags().BoolP("decode", "d", false, "VICQUE解码")
|
||||
Cmd.Flags().StringP("key", "k", "", "密钥")
|
||||
Cmd.Flags().BoolP("replace", "r", false, "覆盖原文件")
|
||||
Cmd.Flags().BoolP("extension", "e", false, "添加/取消.victorique后缀")
|
||||
Cmd.MarkFlagRequired("key")
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user