|
@@ -5,45 +5,149 @@ import (
|
|
|
"net"
|
|
"net"
|
|
|
"os"
|
|
"os"
|
|
|
"syscall"
|
|
"syscall"
|
|
|
|
|
+ "time"
|
|
|
|
|
+
|
|
|
|
|
+ "v2ray.com/core/common/bitmask"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
type Listener struct {
|
|
type Listener struct {
|
|
|
ln net.Listener
|
|
ln net.Listener
|
|
|
- listenerChan <-chan net.Conn
|
|
|
|
|
|
|
+ listenerChan chan<- net.Conn
|
|
|
ctx context.Context
|
|
ctx context.Context
|
|
|
path string
|
|
path string
|
|
|
- lockfile os.File
|
|
|
|
|
|
|
+ lockfile *os.File
|
|
|
|
|
+ state bitmask.Byte
|
|
|
|
|
+ cancal func()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const (
|
|
|
|
|
+ STATE_UNDEFINED = 0
|
|
|
|
|
+ STATE_INITIALIZED = 1 << iota
|
|
|
|
|
+ STATE_LOWERUP = 1 << iota
|
|
|
|
|
+ STATE_UP = 1 << iota
|
|
|
|
|
+ STATE_TAINT = 1 << iota
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
func ListenDS(ctx context.Context, path string) (*Listener, error) {
|
|
func ListenDS(ctx context.Context, path string) (*Listener, error) {
|
|
|
|
|
|
|
|
- vln := &Listener{path: path}
|
|
|
|
|
|
|
+ vln := &Listener{path: path, state: STATE_INITIALIZED, ctx: ctx}
|
|
|
return vln, nil
|
|
return vln, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (ls *Listener) Down() error {
|
|
func (ls *Listener) Down() error {
|
|
|
- err := ls.ln.Close()
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- newError(err).AtDebug().WriteToLog()
|
|
|
|
|
|
|
+ var err error
|
|
|
|
|
+ if !ls.state.Has(STATE_LOWERUP | STATE_UP) {
|
|
|
|
|
+ err = newError(ls.state).Base(newError("Invalid State:Down"))
|
|
|
|
|
+ if ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR {
|
|
|
|
|
+ panic(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- return err
|
|
|
|
|
|
|
+
|
|
|
|
|
+ ls.cancal()
|
|
|
|
|
+ closeerr := ls.ln.Close()
|
|
|
|
|
+ var lockerr error
|
|
|
|
|
+ if isUnixDomainSocketFileSystemBased(ls.path) {
|
|
|
|
|
+ lockerr = giveupLock(ls.lockfile)
|
|
|
|
|
+ }
|
|
|
|
|
+ if closeerr != nil && lockerr != nil {
|
|
|
|
|
+ if ___DEBUG_PANIC_WHEN_ERROR_UNPROPAGATEABLE {
|
|
|
|
|
+ panic(closeerr.Error() + lockerr.Error())
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if closeerr != nil {
|
|
|
|
|
+ return newError("Cannot Close Unix domain socket listener").Base(closeerr)
|
|
|
|
|
+ }
|
|
|
|
|
+ if lockerr != nil {
|
|
|
|
|
+ return newError("Cannot release lock for Unix domain socket listener").Base(lockerr)
|
|
|
|
|
+ }
|
|
|
|
|
+ ls.state.Clear(STATE_LOWERUP | STATE_UP)
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-//Setup systen level Listener
|
|
|
|
|
|
|
+//LowerUP Setup systen level Listener
|
|
|
func (ls *Listener) LowerUP() error {
|
|
func (ls *Listener) LowerUP() error {
|
|
|
|
|
+ var err error
|
|
|
|
|
+
|
|
|
|
|
+ if !ls.state.Has(STATE_INITIALIZED) || ls.state.Has(STATE_LOWERUP) {
|
|
|
|
|
+ err = newError(ls.state).Base(newError("Invalid State:LowerUP"))
|
|
|
|
|
+ if ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR {
|
|
|
|
|
+ panic(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if isUnixDomainSocketFileSystemBased(ls.path) && !___DEBUG_IGNORE_FLOCK {
|
|
if isUnixDomainSocketFileSystemBased(ls.path) && !___DEBUG_IGNORE_FLOCK {
|
|
|
|
|
+ ls.lockfile, err = acquireLock(ls.path + ".lock")
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ newError(err).AtDebug().WriteToLog()
|
|
|
|
|
+ return newError("Unable to acquire lock for filesystem based unix domain socket").Base(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ err = cleansePath(ls.path)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return newError("Unable to cleanse path for the creation of unix domain socket").Base(err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
addr := new(net.UnixAddr)
|
|
addr := new(net.UnixAddr)
|
|
|
addr.Name = ls.path
|
|
addr.Name = ls.path
|
|
|
addr.Net = "unix"
|
|
addr.Net = "unix"
|
|
|
li, err := net.ListenUnix("unix", addr)
|
|
li, err := net.ListenUnix("unix", addr)
|
|
|
|
|
+ ls.ln = li
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
|
|
+ return newError("Unable to listen unix domain socket").Base(err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ls.state.Set(STATE_LOWERUP)
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (ls *Listener) UP(listener chan<- net.Conn, allowkick bool) error {
|
|
|
|
|
+ var err error
|
|
|
|
|
+ if !ls.state.Has(STATE_INITIALIZED|STATE_LOWERUP) || (ls.state.Has(STATE_UP) && !allowkick) {
|
|
|
|
|
+ err = newError(ls.state).Base(newError("Invalid State:UP"))
|
|
|
|
|
+ if ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR {
|
|
|
|
|
+ panic(err)
|
|
|
|
|
+ }
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
|
|
+ ls.listenerChan = listener
|
|
|
|
|
+ if ls.state.Has(STATE_UP) {
|
|
|
|
|
+ cctx, cancel := context.WithCancel(ls.ctx)
|
|
|
|
|
+ ls.cancal = cancel
|
|
|
|
|
+ go ls.uploop(cctx)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (ls *Listener) uploop(cctx context.Context) {
|
|
|
|
|
+ var lasterror error
|
|
|
|
|
+ errortolerance := 5
|
|
|
|
|
+ for {
|
|
|
|
|
+ if cctx.Err() != nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ conn, err := ls.ln.Accept()
|
|
|
|
|
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ newError("Cannot Accept socket from listener").Base(err).AtDebug().WriteToLog()
|
|
|
|
|
+ if err == lasterror {
|
|
|
|
|
+ errortolerance--
|
|
|
|
|
+ if errortolerance == 0 {
|
|
|
|
|
+ newError("unix domain socket melt down as the error is repeating").Base(err).AtError().WriteToLog()
|
|
|
|
|
+ ls.cancal()
|
|
|
|
|
+ }
|
|
|
|
|
+ newError("unix domain socket listener is throttling accept as the error is repeating").Base(err).AtError().WriteToLog()
|
|
|
|
|
+ time.Sleep(time.Second * 5)
|
|
|
|
|
+ }
|
|
|
|
|
+ lasterror = err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ls.listenerChan <- conn
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func isUnixDomainSocketFileSystemBased(path string) bool {
|
|
func isUnixDomainSocketFileSystemBased(path string) bool {
|
|
@@ -51,7 +155,7 @@ func isUnixDomainSocketFileSystemBased(path string) bool {
|
|
|
return path[0] != 0
|
|
return path[0] != 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func AcquireLock(lockfilepath string) (*os.File, error) {
|
|
|
|
|
|
|
+func acquireLock(lockfilepath string) (*os.File, error) {
|
|
|
f, err := os.Create(lockfilepath)
|
|
f, err := os.Create(lockfilepath)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
newError(err).AtDebug().WriteToLog()
|
|
newError(err).AtDebug().WriteToLog()
|
|
@@ -69,6 +173,37 @@ func AcquireLock(lockfilepath string) (*os.File, error) {
|
|
|
}
|
|
}
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func giveupLock(locker *os.File) error {
|
|
|
|
|
+ err := syscall.Flock(int(locker.Fd()), syscall.LOCK_UN)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ closeerr := locker.Close()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ if ___DEBUG_PANIC_WHEN_ERROR_UNPROPAGATEABLE {
|
|
|
|
|
+ panic(closeerr)
|
|
|
|
|
+ }
|
|
|
|
|
+ newError(closeerr).AtDebug().WriteToLog()
|
|
|
|
|
+ }
|
|
|
|
|
+ newError(err).AtDebug().WriteToLog()
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ closeerr := locker.Close()
|
|
|
|
|
+ if closeerr != nil {
|
|
|
|
|
+ newError(closeerr).AtDebug().WriteToLog()
|
|
|
|
|
+ return closeerr
|
|
|
|
|
+ }
|
|
|
|
|
+ return closeerr
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func cleansePath(path string) error {
|
|
|
|
|
+ _, err := os.Stat(path)
|
|
|
|
|
+ if err == os.ErrNotExist {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ err = os.Remove(path)
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//DEBUG CONSTS
|
|
//DEBUG CONSTS
|