[GET] /server
This commit is contained in:
parent
1f4a477077
commit
b958ff7ca2
126
config.go
126
config.go
@ -11,95 +11,95 @@ import (
|
|||||||
|
|
||||||
const APILevel = 1
|
const APILevel = 1
|
||||||
|
|
||||||
type Config struct {
|
var SelfProcessID int
|
||||||
Namespace string
|
|
||||||
GinDebug bool `env:"GINDEBUG"`
|
|
||||||
ReturnRawErrors bool `env:"RETURNERRORS"`
|
|
||||||
Custom404 bool `env:"CUSTOM404"`
|
|
||||||
LogLevel zerolog.Level `env:"LOGLEVEL"`
|
|
||||||
ServerIP string `env:"IP"`
|
|
||||||
ServerPort string `env:"PORT"`
|
|
||||||
RequestTimeout time.Duration `env:"REQUEST_TIMEOUT"`
|
|
||||||
Cors bool `env:"CORS"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MailConfig struct {
|
type Config struct {
|
||||||
Host string `env:"HOST"`
|
Namespace string
|
||||||
Port int `env:"PORT"`
|
GinDebug bool `env:"GINDEBUG"`
|
||||||
Username string `env:"USERNAME"`
|
ReturnRawErrors bool `env:"RETURNERRORS"`
|
||||||
Password string `env:"PASSWORD"`
|
Custom404 bool `env:"CUSTOM404"`
|
||||||
Sender string `env:"SENDER"`
|
LogLevel zerolog.Level `env:"LOGLEVEL"`
|
||||||
|
ServerIP string `env:"IP"`
|
||||||
|
ServerPort int `env:"PORT"`
|
||||||
|
RequestTimeout time.Duration `env:"REQUEST_TIMEOUT"`
|
||||||
|
Cors bool `env:"CORS"`
|
||||||
|
VerifyConnTimeout time.Duration `env:"VERIFY_CONN_TIMEOUT"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var Conf Config
|
var Conf Config
|
||||||
|
|
||||||
var configLocHost = func() Config {
|
var configLocHost = func() Config {
|
||||||
return Config{
|
return Config{
|
||||||
Namespace: "local",
|
Namespace: "local",
|
||||||
GinDebug: true,
|
GinDebug: true,
|
||||||
ServerIP: "0.0.0.0",
|
ServerIP: "0.0.0.0",
|
||||||
ServerPort: "80",
|
ServerPort: 80,
|
||||||
Custom404: true,
|
Custom404: true,
|
||||||
ReturnRawErrors: true,
|
ReturnRawErrors: true,
|
||||||
RequestTimeout: 16 * time.Second,
|
RequestTimeout: 16 * time.Second,
|
||||||
LogLevel: zerolog.DebugLevel,
|
LogLevel: zerolog.DebugLevel,
|
||||||
Cors: true,
|
Cors: true,
|
||||||
|
VerifyConnTimeout: time.Second,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var configLocDocker = func() Config {
|
var configLocDocker = func() Config {
|
||||||
return Config{
|
return Config{
|
||||||
Namespace: "local-docker",
|
Namespace: "local-docker",
|
||||||
GinDebug: true,
|
GinDebug: true,
|
||||||
ServerIP: "0.0.0.0",
|
ServerIP: "0.0.0.0",
|
||||||
ServerPort: "80",
|
ServerPort: 80,
|
||||||
Custom404: true,
|
Custom404: true,
|
||||||
ReturnRawErrors: true,
|
ReturnRawErrors: true,
|
||||||
RequestTimeout: 16 * time.Second,
|
RequestTimeout: 16 * time.Second,
|
||||||
LogLevel: zerolog.DebugLevel,
|
LogLevel: zerolog.DebugLevel,
|
||||||
Cors: true,
|
Cors: true,
|
||||||
|
VerifyConnTimeout: time.Second,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var configDev = func() Config {
|
var configDev = func() Config {
|
||||||
return Config{
|
return Config{
|
||||||
Namespace: "develop",
|
Namespace: "develop",
|
||||||
GinDebug: true,
|
GinDebug: true,
|
||||||
ServerIP: "0.0.0.0",
|
ServerIP: "0.0.0.0",
|
||||||
ServerPort: "80",
|
ServerPort: 80,
|
||||||
Custom404: false,
|
Custom404: false,
|
||||||
ReturnRawErrors: false,
|
ReturnRawErrors: false,
|
||||||
RequestTimeout: 16 * time.Second,
|
RequestTimeout: 16 * time.Second,
|
||||||
LogLevel: zerolog.DebugLevel,
|
LogLevel: zerolog.DebugLevel,
|
||||||
Cors: false,
|
Cors: false,
|
||||||
|
VerifyConnTimeout: time.Second,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var configStag = func() Config {
|
var configStag = func() Config {
|
||||||
return Config{
|
return Config{
|
||||||
Namespace: "staging",
|
Namespace: "staging",
|
||||||
GinDebug: true,
|
GinDebug: true,
|
||||||
ServerIP: "0.0.0.0",
|
ServerIP: "0.0.0.0",
|
||||||
ServerPort: "80",
|
ServerPort: 80,
|
||||||
Custom404: false,
|
Custom404: false,
|
||||||
ReturnRawErrors: false,
|
ReturnRawErrors: false,
|
||||||
RequestTimeout: 16 * time.Second,
|
RequestTimeout: 16 * time.Second,
|
||||||
LogLevel: zerolog.DebugLevel,
|
LogLevel: zerolog.DebugLevel,
|
||||||
Cors: false,
|
Cors: false,
|
||||||
|
VerifyConnTimeout: time.Second,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var configProd = func() Config {
|
var configProd = func() Config {
|
||||||
return Config{
|
return Config{
|
||||||
Namespace: "production",
|
Namespace: "production",
|
||||||
GinDebug: false,
|
GinDebug: false,
|
||||||
ServerIP: "0.0.0.0",
|
ServerIP: "0.0.0.0",
|
||||||
ServerPort: "80",
|
ServerPort: 80,
|
||||||
Custom404: false,
|
Custom404: false,
|
||||||
ReturnRawErrors: false,
|
ReturnRawErrors: false,
|
||||||
RequestTimeout: 16 * time.Second,
|
RequestTimeout: 16 * time.Second,
|
||||||
LogLevel: zerolog.InfoLevel,
|
LogLevel: zerolog.InfoLevel,
|
||||||
Cors: false,
|
Cors: false,
|
||||||
|
VerifyConnTimeout: time.Second,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,4 +143,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Conf = cfg
|
Conf = cfg
|
||||||
|
|
||||||
|
SelfProcessID = os.Getpid()
|
||||||
}
|
}
|
||||||
|
@ -136,9 +136,6 @@ func (j *JobRunner[TData]) execute() (err error) {
|
|||||||
|
|
||||||
changes, err := langext.RunPanicSafeR2(func() (int, error) { return j.jobFunc(runCtx, j.app, lstr, j.data) })
|
changes, err := langext.RunPanicSafeR2(func() (int, error) { return j.jobFunc(runCtx, j.app, lstr, j.data) })
|
||||||
|
|
||||||
finCtx, cancelFinCtx := context.WithTimeout(context.Background(), time.Minute)
|
|
||||||
defer cancelFinCtx()
|
|
||||||
|
|
||||||
//goland:noinspection GoTypeAssertionOnErrors
|
//goland:noinspection GoTypeAssertionOnErrors
|
||||||
if panicerr, ok := err.(langext.PanicWrappedErr); ok {
|
if panicerr, ok := err.(langext.PanicWrappedErr); ok {
|
||||||
|
|
||||||
|
@ -2,15 +2,23 @@ package logic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/cakturk/go-netstat/netstat"
|
"github.com/cakturk/go-netstat/netstat"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/ginext"
|
"gogs.mikescher.com/BlackForestBytes/goext/ginext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/syncext"
|
"gogs.mikescher.com/BlackForestBytes/goext/syncext"
|
||||||
|
"io"
|
||||||
bunny "locbunny"
|
bunny "locbunny"
|
||||||
"locbunny/models"
|
"locbunny/models"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -45,7 +53,7 @@ func (app *Application) Stop() {
|
|||||||
|
|
||||||
func (app *Application) Run() {
|
func (app *Application) Run() {
|
||||||
|
|
||||||
addr := net.JoinHostPort(app.Config.ServerIP, app.Config.ServerPort)
|
addr := net.JoinHostPort(app.Config.ServerIP, strconv.Itoa(app.Config.ServerPort))
|
||||||
|
|
||||||
errChan, httpserver := app.Gin.ListenAndServeHTTP(addr, func(port string) {
|
errChan, httpserver := app.Gin.ListenAndServeHTTP(addr, func(port string) {
|
||||||
app.Port = port
|
app.Port = port
|
||||||
@ -104,18 +112,159 @@ func (app *Application) Run() {
|
|||||||
|
|
||||||
func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, error) {
|
func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, error) {
|
||||||
|
|
||||||
socks, err := netstat.TCPSocks(netstat.NoopFilter)
|
socks4, err := netstat.TCPSocks(netstat.NoopFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := make([]models.Server, 0)
|
socks6, err := netstat.TCP6Socks(netstat.NoopFilter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for _, sock := range socks {
|
sockCount := len(socks4) + len(socks6)
|
||||||
|
|
||||||
res = append(res, models.Server{Port: int(sock.LocalAddr.Port)})
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
echan := make(chan error, sockCount*3)
|
||||||
|
rchan := make(chan models.Server, sockCount*3)
|
||||||
|
|
||||||
|
for _i := range socks4 {
|
||||||
|
i := _i
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
con1, err := app.verifyHTTPConn(socks4[i], "HTTP", "v4")
|
||||||
|
if err == nil {
|
||||||
|
rchan <- con1
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
echan <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
con2, err := app.verifyHTTPConn(socks4[i], "HTTPS", "v4")
|
||||||
|
if err == nil {
|
||||||
|
rchan <- con2
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
echan <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _i := range socks6 {
|
||||||
|
i := _i
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
con1, err := app.verifyHTTPConn(socks6[i], "HTTP", "v6")
|
||||||
|
if err == nil {
|
||||||
|
rchan <- con1
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
echan <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
con2, err := app.verifyHTTPConn(socks6[i], "HTTPS", "v6")
|
||||||
|
if err == nil {
|
||||||
|
rchan <- con2
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
echan <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
close(echan)
|
||||||
|
close(rchan)
|
||||||
|
|
||||||
|
duplicates := make(map[int]bool, sockCount*3)
|
||||||
|
res := make([]models.Server, 0, sockCount*3)
|
||||||
|
for v := range rchan {
|
||||||
|
|
||||||
|
if _, ok := duplicates[v.Port]; !ok {
|
||||||
|
res = append(res, v)
|
||||||
|
duplicates[v.Port] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *Application) verifyHTTPConn(sock netstat.SockTabEntry, proto string, ipversion string) (models.Server, error) {
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), bunny.Conf.VerifyConnTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if sock.State != netstat.Listen {
|
||||||
|
log.Debug().Msg(fmt.Sprintf("Failed to verify socket [%s|%s] invalid state: %s", ipversion, strings.ToUpper(proto), sock.State.String()))
|
||||||
|
return models.Server{}, errors.New("invalid sock-state")
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(sock.LocalAddr.Port) == bunny.Conf.ServerPort && sock.Process != nil && sock.Process.Pid == bunny.SelfProcessID {
|
||||||
|
log.Debug().Msg(fmt.Sprintf("Skip socket [%s|%s] (this is our own server)", ipversion, strings.ToUpper(proto)))
|
||||||
|
return models.Server{}, errors.New("skip self")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := http.Client{}
|
||||||
|
url := fmt.Sprintf("%s://localhost:%d", strings.ToLower(proto), sock.LocalAddr.Port)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug().Msg(fmt.Sprintf("Failed to create [%s|%s] request to %d", ipversion, strings.ToUpper(proto), sock.LocalAddr.Port))
|
||||||
|
return models.Server{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp1, err := c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug().Msg(fmt.Sprintf("Failed to send [%s|%s] request to %s", ipversion, strings.ToUpper(proto), url))
|
||||||
|
return models.Server{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = resp1.Body.Close() }()
|
||||||
|
|
||||||
|
resbody, err := io.ReadAll(resp1.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug().Msg(fmt.Sprintf("Failed to read [%s|%s] response from %s", ipversion, strings.ToUpper(proto), url))
|
||||||
|
return models.Server{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ct := resp1.Header.Get("Content-Type")
|
||||||
|
if ct != "" {
|
||||||
|
|
||||||
|
var pnm *string = nil
|
||||||
|
var pid *int = nil
|
||||||
|
if sock.Process != nil {
|
||||||
|
pnm = langext.Ptr(sock.Process.Name)
|
||||||
|
pid = langext.Ptr(sock.Process.Pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.Server{
|
||||||
|
Port: int(sock.LocalAddr.Port),
|
||||||
|
IP: sock.LocalAddr.IP.String(),
|
||||||
|
Protocol: proto,
|
||||||
|
StatusCode: resp1.StatusCode,
|
||||||
|
Response: string(resbody),
|
||||||
|
ContentType: ct,
|
||||||
|
Process: pnm,
|
||||||
|
PID: pid,
|
||||||
|
UID: sock.UID,
|
||||||
|
SockState: sock.State.String(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msg(fmt.Sprintf("Failed to categorize [%s|%s] response from %s (Content-Type: '%s')", ipversion, strings.ToUpper(proto), url, ct))
|
||||||
|
return models.Server{}, errors.New("invalid response-type")
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@ import "go.mongodb.org/mongo-driver/bson/bsontype"
|
|||||||
import "go.mongodb.org/mongo-driver/bson/primitive"
|
import "go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
import "gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
import "gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
||||||
|
|
||||||
const ChecksumIDGenerator = "8fa696914bf8d1c1c4b9f80be45d0a9dfbd0fed789856bf84ced2979c789b958" // GoExtVersion: 0.0.288
|
const ChecksumIDGenerator = "cf5fe3d14932e535744418c0aa3751c08f392f9f117bfa5e8b04f70379879131" // GoExtVersion: 0.0.288
|
||||||
|
|
||||||
// ================================ AnyID (ids.go) ================================
|
// ================================ AnyID (ids.go) ================================
|
||||||
|
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Port int
|
Port int `json:"port"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
StatusCode int `json:"statusCode"`
|
||||||
|
Response string `json:"response"`
|
||||||
|
ContentType string `json:"contentType"`
|
||||||
|
Process *string `json:"process"`
|
||||||
|
PID *int `json:"pid"`
|
||||||
|
UID uint32 `json:"uid"`
|
||||||
|
SockState string `json:"sockState"`
|
||||||
}
|
}
|
||||||
|
@ -283,8 +283,35 @@
|
|||||||
"models.Server": {
|
"models.Server": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"contentType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ip": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pid": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"port": {
|
"port": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"process": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"protocol": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sockState": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"statusCode": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"uid": {
|
||||||
|
"type": "integer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,26 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
models.Server:
|
models.Server:
|
||||||
properties:
|
properties:
|
||||||
|
contentType:
|
||||||
|
type: string
|
||||||
|
ip:
|
||||||
|
type: string
|
||||||
|
pid:
|
||||||
|
type: integer
|
||||||
port:
|
port:
|
||||||
type: integer
|
type: integer
|
||||||
|
process:
|
||||||
|
type: string
|
||||||
|
protocol:
|
||||||
|
type: string
|
||||||
|
response:
|
||||||
|
type: string
|
||||||
|
sockState:
|
||||||
|
type: string
|
||||||
|
statusCode:
|
||||||
|
type: integer
|
||||||
|
uid:
|
||||||
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
host: localhost
|
host: localhost
|
||||||
info:
|
info:
|
||||||
|
Loading…
Reference in New Issue
Block a user