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 }