github.com/iDigitalFlame/xmt@v0.5.4/man/link.go (about)

     1  // Copyright (C) 2020 - 2023 iDigitalFlame
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  //
    16  
    17  package man
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/sha512"
    23  	"net"
    24  	"strconv"
    25  	"strings"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	"github.com/iDigitalFlame/xmt/com"
    30  	"github.com/iDigitalFlame/xmt/com/pipe"
    31  	"github.com/iDigitalFlame/xmt/util"
    32  	"github.com/iDigitalFlame/xmt/util/bugtrack"
    33  )
    34  
    35  const (
    36  	// TCP is a Linker type that can be used with a Guardian.
    37  	// This Linker uses raw TCP sockets to determine Guardian status.
    38  	TCP = netSync(false)
    39  	// Pipe is a Linker type that can be used with a Guardian.
    40  	// This Linker uses Unix Domain Sockets in *nix devices and Named Pipes
    41  	// in Windows devices.
    42  	//
    43  	// Pipe names are prefixed with the appropriate namespace before being
    44  	// checked or created (if it doesn't exist already).
    45  	//
    46  	// This is the default Linker used if a nil Linker is used.
    47  	Pipe = netSync(true)
    48  
    49  	// Mutex is a Linker type that can be used with a Guardian.
    50  	// This Linker uses Windows Mutexes to determine Guardian status.
    51  	//
    52  	// This Linker type is only available on Windows devices.
    53  	// non-Windows devices will always return a 'device.ErrNoWindows' error.
    54  	Mutex = objSync(0)
    55  	// Event is a Linker type that can be used with a Guardian.
    56  	// This Linker uses Windows Events to determine Guardian status.
    57  	//
    58  	// This Linker type is only available on Windows devices.
    59  	// non-Windows devices will always return a 'device.ErrNoWindows' error.
    60  	Event = objSync(1)
    61  	// Semaphore is a Linker type that can be used with a Guardian.
    62  	// This Linker uses Windows Semaphores to determine Guardian status.
    63  	//
    64  	// This Linker type is only available on Windows devices.
    65  	// non-Windows devices will always return a 'device.ErrNoWindows' error.
    66  	Semaphore = objSync(2)
    67  	// Mailslot is a Linker type that can be used with a Guardian.
    68  	// This Linker uses Windows Mailslots to determine Guardian status.
    69  	//
    70  	// This Linker type is only available on Windows devices.
    71  	// non-Windows devices will always return a 'device.ErrNoWindows' error.
    72  	Mailslot = objSync(3)
    73  )
    74  
    75  type netSync bool
    76  type objSync uint8
    77  
    78  // Linker us an interface that specifies an object that can be used to check
    79  // for a Guardian instance.
    80  type Linker interface {
    81  	check(s string) (bool, error)
    82  	create(s string) (listener, error)
    83  }
    84  type netListener struct {
    85  	_    [0]func()
    86  	l    net.Listener
    87  	done uint32
    88  }
    89  type listener interface {
    90  	Listen()
    91  	Close() error
    92  }
    93  
    94  func (n *netListener) Listen() {
    95  	for atomic.LoadUint32(&n.done) == 0 {
    96  		c, err := n.l.Accept()
    97  		if err != nil {
    98  			e, ok := err.(net.Error)
    99  			if ok && e.Timeout() {
   100  				continue
   101  			}
   102  			if ok && !e.Timeout() {
   103  				break
   104  			}
   105  			continue
   106  		}
   107  		go netHandleConn(c)
   108  	}
   109  	n.l.Close()
   110  }
   111  func netHandleConn(c net.Conn) {
   112  	var (
   113  		b      [65]byte
   114  		n, err = c.Read(b[:])
   115  	)
   116  	if err == nil && n == 65 && b[0] == 0xFF {
   117  		if bugtrack.Enabled {
   118  			bugtrack.Track(`man.netHandleConn(): Connection from "%s" handled.`, c.RemoteAddr().String())
   119  		}
   120  		h := sha512.New()
   121  		h.Write(b[1:])
   122  		copy(b[1:], h.Sum(nil))
   123  		b[0], h = 0xA0, nil
   124  		c.Write(b[:])
   125  	}
   126  	c.Close()
   127  }
   128  func (n netSync) String() string {
   129  	if n {
   130  		return com.NamePipe
   131  	}
   132  	return com.NameTCP
   133  }
   134  func (n *netListener) Close() error {
   135  	atomic.StoreUint32(&n.done, 1)
   136  	return n.l.Close()
   137  }
   138  func formatTCPName(s string) string {
   139  	if i := strings.IndexByte(s, ':'); i >= 0 || len(s) == 0 {
   140  		return s
   141  	}
   142  	if _, err := strconv.ParseUint(s, 10, 16); err == nil {
   143  		return local + s
   144  	}
   145  	h := uint32(2166136261)
   146  	for x := range s {
   147  		h *= 16777619
   148  		h ^= uint32(s[x])
   149  	}
   150  	v := uint16(h)
   151  	if v < 1024 {
   152  		v += 1024
   153  	}
   154  	return local + util.Uitoa(uint64(v))
   155  }
   156  
   157  // LinkerFromName will attempt to map the name provided to an appropriate Linker
   158  // interface.
   159  //
   160  // If no linker is found, the 'Pipe' Linker will be returned.
   161  func LinkerFromName(n string) Linker {
   162  	if len(n) == 0 {
   163  		return Pipe
   164  	}
   165  	if len(n) == 1 {
   166  		switch n[0] {
   167  		case 't', 'T':
   168  			return TCP
   169  		case 'p', 'P':
   170  			return Pipe
   171  		case 'e', 'E':
   172  			return Event
   173  		case 'm', 'M':
   174  			return Mutex
   175  		case 'n', 'N':
   176  			return Mailslot
   177  		case 's', 'S':
   178  			return Semaphore
   179  		}
   180  		return Pipe
   181  	}
   182  	switch {
   183  	case len(n) == 3 && (n[0] == 't' || n[0] == 'T'):
   184  		return TCP
   185  	case len(n) == 4 && (n[0] == 'p' || n[0] == 'P'):
   186  		return Pipe
   187  	case len(n) == 5 && (n[0] == 'e' || n[0] == 'E'):
   188  		return Event
   189  	case len(n) == 5 && (n[0] == 'm' || n[0] == 'M'):
   190  		return Mutex
   191  	case len(n) == 8 && (n[0] == 'm' || n[0] == 'M'):
   192  		return Mailslot
   193  	case len(n) == 9 && (n[0] == 's' || n[0] == 'S'):
   194  		return Semaphore
   195  	}
   196  	return Pipe
   197  }
   198  func netCheckConn(c net.Conn) (bool, error) {
   199  	var (
   200  		b    [65]byte
   201  		_, _ = util.Rand.Read(b[1:])
   202  		v    = sha512.New()
   203  		_, _ = v.Write(b[1:])
   204  		h    = v.Sum(nil)
   205  	)
   206  	b[0], v = 0xFF, nil
   207  	c.SetDeadline(time.Now().Add(timeout))
   208  	if _, err := c.Write(b[:]); err != nil {
   209  		return false, err
   210  	}
   211  	if n, err := c.Read(b[:]); err != nil || n != 65 {
   212  		return false, err
   213  	}
   214  	return b[0] == 0xA0 && bytes.Equal(b[1:], h), nil
   215  }
   216  func (n netSync) check(s string) (bool, error) {
   217  	var (
   218  		c   net.Conn
   219  		err error
   220  	)
   221  	if n {
   222  		c, err = pipe.DialTimeout(pipe.Format(s), timeout)
   223  	} else {
   224  		c, err = net.DialTimeout(com.NameTCP, formatTCPName(s), timeout)
   225  	}
   226  	if err != nil {
   227  		return false, nil
   228  	}
   229  	v, err := netCheckConn(c)
   230  	c.Close()
   231  	return v, err
   232  }
   233  func (n netSync) create(s string) (listener, error) {
   234  	var (
   235  		l   net.Listener
   236  		err error
   237  	)
   238  	if n {
   239  		l, err = pipe.ListenPerms(pipe.Format(s), pipe.PermEveryone)
   240  	} else {
   241  		l, err = com.ListenConfig.Listen(context.Background(), com.NameTCP, formatTCPName(s))
   242  	}
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  	return &netListener{l: l}, nil
   247  }