2024-11-23 12:30:23 +09:00
|
|
|
package hodu
|
|
|
|
|
2024-12-08 00:57:58 +09:00
|
|
|
import "net/http"
|
2024-12-12 21:09:16 +09:00
|
|
|
import "net/netip"
|
2024-12-08 00:57:58 +09:00
|
|
|
import "os"
|
|
|
|
import "runtime"
|
2024-12-12 21:09:16 +09:00
|
|
|
import "strings"
|
2024-11-23 12:30:23 +09:00
|
|
|
import "sync"
|
|
|
|
|
2024-12-12 21:09:16 +09:00
|
|
|
|
2024-12-07 21:24:06 +09:00
|
|
|
const HODU_RPC_VERSION uint32 = 0x010000
|
2024-11-23 12:30:23 +09:00
|
|
|
|
|
|
|
type LogLevel int
|
2024-12-09 01:51:04 +09:00
|
|
|
type LogMask int
|
2024-11-23 12:30:23 +09:00
|
|
|
|
|
|
|
const (
|
2024-12-09 01:51:04 +09:00
|
|
|
LOG_DEBUG LogLevel = 1 << iota
|
2024-11-23 12:30:23 +09:00
|
|
|
LOG_INFO
|
2024-12-09 01:51:04 +09:00
|
|
|
LOG_WARN
|
|
|
|
LOG_ERROR
|
2024-11-23 12:30:23 +09:00
|
|
|
)
|
|
|
|
|
2024-12-09 01:59:05 +09:00
|
|
|
const LOG_ALL LogMask = LogMask(LOG_DEBUG | LOG_INFO | LOG_WARN | LOG_ERROR)
|
|
|
|
const LOG_NONE LogMask = LogMask(0)
|
|
|
|
|
2024-12-12 21:09:16 +09:00
|
|
|
var IPV4_PREFIX_ZERO = netip.MustParsePrefix("0.0.0.0/0")
|
|
|
|
var IPV6_PREFIX_ZERO = netip.MustParsePrefix("::/0")
|
|
|
|
|
2024-11-23 12:30:23 +09:00
|
|
|
type Logger interface {
|
2024-12-08 23:16:43 +09:00
|
|
|
Write(id string, level LogLevel, fmtstr string, args ...interface{})
|
2024-11-23 12:30:23 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
type Service interface {
|
2024-12-08 23:16:43 +09:00
|
|
|
RunTask(wg *sync.WaitGroup) // blocking. run the actual task loop. it must call wg.Done() upon exit from itself.
|
2024-11-23 12:30:23 +09:00
|
|
|
StartService(data interface{}) // non-blocking. spin up a service. it may be invokded multiple times for multiple instances
|
|
|
|
StopServices() // non-blocking. send stop request to all services spun up
|
2024-12-14 14:04:33 +09:00
|
|
|
FixServices() // do some fixup as needed
|
2024-11-23 12:30:23 +09:00
|
|
|
WaitForTermination() // blocking. must wait until all services are stopped
|
2024-11-23 20:13:07 +09:00
|
|
|
WriteLog(id string, level LogLevel, fmtstr string, args ...interface{})
|
2024-11-23 12:30:23 +09:00
|
|
|
}
|
2024-12-07 22:18:07 +09:00
|
|
|
|
2024-12-07 23:03:23 +09:00
|
|
|
func tcp_addr_str_class(addr string) string {
|
2024-12-12 21:09:16 +09:00
|
|
|
// the string is supposed to be addr:port
|
|
|
|
|
2024-12-07 23:03:23 +09:00
|
|
|
if len(addr) > 0 {
|
2024-12-12 21:09:16 +09:00
|
|
|
var ap netip.AddrPort
|
|
|
|
var err error
|
|
|
|
ap, err = netip.ParseAddrPort(addr)
|
|
|
|
if err == nil {
|
|
|
|
if ap.Addr().Is6() { return "tcp6" }
|
|
|
|
if ap.Addr().Is4() { return "tcp4" }
|
2024-12-07 23:03:23 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "tcp"
|
|
|
|
}
|
2024-12-08 00:57:58 +09:00
|
|
|
|
2024-12-13 02:25:27 +09:00
|
|
|
func word_to_route_option(word string) RouteOption {
|
2024-12-12 21:09:16 +09:00
|
|
|
switch word {
|
|
|
|
case "tcp4":
|
|
|
|
return RouteOption(ROUTE_OPTION_TCP4)
|
|
|
|
case "tcp6":
|
|
|
|
return RouteOption(ROUTE_OPTION_TCP6)
|
|
|
|
case "tcp":
|
|
|
|
return RouteOption(ROUTE_OPTION_TCP)
|
|
|
|
case "tty":
|
|
|
|
return RouteOption(ROUTE_OPTION_TTY)
|
|
|
|
case "http":
|
|
|
|
return RouteOption(ROUTE_OPTION_HTTP)
|
|
|
|
case "https":
|
|
|
|
return RouteOption(ROUTE_OPTION_HTTPS)
|
2024-12-13 02:25:27 +09:00
|
|
|
case "ssh":
|
|
|
|
return RouteOption(ROUTE_OPTION_SSH)
|
2024-12-12 21:09:16 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
return RouteOption(ROUTE_OPTION_UNSPEC)
|
|
|
|
}
|
|
|
|
|
2024-12-13 02:25:27 +09:00
|
|
|
func string_to_route_option(desc string) RouteOption {
|
2024-12-12 21:09:16 +09:00
|
|
|
var fld string
|
2024-12-13 02:25:27 +09:00
|
|
|
var option RouteOption
|
2024-12-12 21:09:16 +09:00
|
|
|
var p RouteOption
|
|
|
|
|
2024-12-13 02:25:27 +09:00
|
|
|
option = RouteOption(0)
|
2024-12-12 21:09:16 +09:00
|
|
|
for _, fld = range strings.Fields(desc) {
|
2024-12-13 02:25:27 +09:00
|
|
|
p = word_to_route_option(fld)
|
2024-12-12 21:09:16 +09:00
|
|
|
if p == RouteOption(ROUTE_OPTION_UNSPEC) { return p }
|
2024-12-13 02:25:27 +09:00
|
|
|
option |= p
|
2024-12-12 21:09:16 +09:00
|
|
|
}
|
2024-12-13 02:25:27 +09:00
|
|
|
return option
|
2024-12-12 21:09:16 +09:00
|
|
|
}
|
|
|
|
|
2024-12-13 02:25:27 +09:00
|
|
|
func (option RouteOption) string() string {
|
2024-12-12 21:09:16 +09:00
|
|
|
var str string
|
|
|
|
str = ""
|
2024-12-13 02:25:27 +09:00
|
|
|
if option & RouteOption(ROUTE_OPTION_TCP6) != 0 { str += " tcp6" }
|
|
|
|
if option & RouteOption(ROUTE_OPTION_TCP4) != 0 { str += " tcp4" }
|
|
|
|
if option & RouteOption(ROUTE_OPTION_TCP) != 0 { str += " tcp" }
|
|
|
|
if option & RouteOption(ROUTE_OPTION_TTY) != 0 { str += " tty" }
|
|
|
|
if option & RouteOption(ROUTE_OPTION_HTTP) != 0 { str += " http" }
|
|
|
|
if option & RouteOption(ROUTE_OPTION_HTTPS) != 0 { str += " https" }
|
|
|
|
if option & RouteOption(ROUTE_OPTION_SSH) != 0 { str += " ssh" }
|
2024-12-12 21:09:16 +09:00
|
|
|
if str == "" { return str }
|
|
|
|
return str[1:] // remove the leading space
|
|
|
|
}
|
|
|
|
|
2024-12-08 00:57:58 +09:00
|
|
|
func dump_call_frame_and_exit(log Logger, req *http.Request, err interface{}) {
|
|
|
|
var buf []byte
|
|
|
|
buf = make([]byte, 65536); buf = buf[:min(65536, runtime.Stack(buf, false))]
|
|
|
|
log.Write("", LOG_ERROR, "[%s] %s %s - %v\n%s", req.RemoteAddr, req.Method, req.URL.String(), err, string(buf))
|
|
|
|
os.Exit(99) // fatal error. treat panic() as a fatal runtime error
|
|
|
|
}
|