├── .gitattributes ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── client_all.go ├── connect_other.go ├── connect_windows.go ├── encryption.go ├── example ├── .DS_Store └── example.go ├── go.mod ├── go.sum ├── handshake.go ├── headers.go ├── ipc_test.go ├── server_all.go ├── shared.go ├── types.go └── vars.go /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [] 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 james-barrow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang-ipc 2 | Golang Inter-process communication library for Window, Mac and Linux. 3 | 4 | 5 | ### Overview 6 | 7 | A simple to use package that uses unix sockets on Macos/Linux and named pipes on Windows to create a communication channel between two go processes. 8 | 9 | ### Intergration 10 | 11 | As well as using this library just for go processes it was also designed to work with other languages, with the go process as the server and the other languages processing being the client. 12 | 13 | 14 | #### NodeJs 15 | 16 | I currently use this library to comunicate between a ElectronJS GUI and a go program. 17 | 18 | Below is a link to the nodeJS client library: 19 | 20 | https://github.com/james-barrow/node-ipc-client 21 | 22 | #### Python 23 | 24 | To do 25 | 26 | ## Usage 27 | 28 | Create a server with the default configuation and start listening for the client: 29 | 30 | ```go 31 | 32 | s, err := ipc.StartServer("", nil) 33 | if err != nil { 34 | log.Println(err) 35 | return 36 | } 37 | 38 | ``` 39 | Create a client and connect to the server: 40 | 41 | ```go 42 | 43 | c, err := ipc.StartClient("", nil) 44 | if err != nil { 45 | log.Println(err) 46 | return 47 | } 48 | 49 | ``` 50 | 51 | ### Read messages 52 | 53 | Read each message sent: 54 | 55 | ```go 56 | 57 | for { 58 | 59 | // message, err := s.Read() server 60 | message, err := c.Read() // client 61 | 62 | if err == nil { 63 | // handle error 64 | } 65 | 66 | // do something with the received messages 67 | } 68 | 69 | ``` 70 | 71 | All received messages are formated into the type Message 72 | 73 | ```go 74 | 75 | type Message struct { 76 | Err error // details of any error 77 | MsgType int // 0 = reserved , -1 is an internal message (disconnection or error etc), all messages recieved will be > 0 78 | Data []byte // message data received 79 | Status string // the status of the connection 80 | } 81 | 82 | ``` 83 | 84 | ### Write a message 85 | 86 | 87 | ```go 88 | 89 | //err := s.Write(1, []byte(" c.maxMsgSize { 204 | return errors.New("Message exceeds maximum message length") 205 | } 206 | 207 | c.toWrite <- &Message{MsgType: msgType, Data: message} 208 | 209 | return nil 210 | } 211 | 212 | func (c *Client) write() { 213 | 214 | for { 215 | 216 | m, ok := <-c.toWrite 217 | 218 | if !ok { 219 | break 220 | } 221 | 222 | toSend := intToBytes(m.MsgType) 223 | 224 | writer := bufio.NewWriter(c.conn) 225 | 226 | if c.encryption { 227 | toSend = append(toSend, m.Data...) 228 | toSendEnc, err := encrypt(*c.enc.cipher, toSend) 229 | if err != nil { 230 | log.Println("error encrypting data", err) 231 | continue 232 | } 233 | toSend = toSendEnc 234 | } else { 235 | 236 | toSend = append(toSend, m.Data...) 237 | 238 | } 239 | 240 | writer.Write(intToBytes(len(toSend))) 241 | writer.Write(toSend) 242 | 243 | err := writer.Flush() 244 | if err != nil { 245 | log.Println("error flushing data", err) 246 | continue 247 | } 248 | 249 | } 250 | } 251 | 252 | // getStatus - get the current status of the connection 253 | func (c *Client) getStatus() Status { 254 | 255 | return c.status 256 | } 257 | 258 | // StatusCode - returns the current connection status 259 | func (c *Client) StatusCode() Status { 260 | return c.status 261 | } 262 | 263 | // Status - returns the current connection status as a string 264 | func (c *Client) Status() string { 265 | 266 | return c.status.String() 267 | } 268 | 269 | // Close - closes the connection 270 | func (c *Client) Close() { 271 | 272 | c.status = Closing 273 | 274 | if c.conn != nil { 275 | c.conn.Close() 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /connect_other.go: -------------------------------------------------------------------------------- 1 | //go:build linux || darwin 2 | // +build linux darwin 3 | 4 | package ipc 5 | 6 | import ( 7 | "errors" 8 | "net" 9 | "os" 10 | "strings" 11 | "syscall" 12 | "time" 13 | ) 14 | 15 | // Server create a unix socket and start listening connections - for unix and linux 16 | func (s *Server) run() error { 17 | 18 | base := "/tmp/" 19 | sock := ".sock" 20 | 21 | if err := os.RemoveAll(base + s.name + sock); err != nil { 22 | return err 23 | } 24 | 25 | var oldUmask int 26 | if s.unMask { 27 | oldUmask = syscall.Umask(0) 28 | } 29 | 30 | listen, err := net.Listen("unix", base+s.name+sock) 31 | 32 | if s.unMask { 33 | syscall.Umask(oldUmask) 34 | } 35 | 36 | if err != nil { 37 | return err 38 | } 39 | 40 | s.listen = listen 41 | 42 | go s.acceptLoop() 43 | 44 | s.status = Listening 45 | 46 | return nil 47 | 48 | } 49 | 50 | // Client connect to the unix socket created by the server - for unix and linux 51 | func (c *Client) dial() error { 52 | 53 | base := "/tmp/" 54 | sock := ".sock" 55 | 56 | startTime := time.Now() 57 | 58 | for { 59 | 60 | if c.timeout != 0 { 61 | 62 | if time.Since(startTime).Seconds() > c.timeout { 63 | c.status = Closed 64 | return errors.New("timed out trying to connect") 65 | } 66 | } 67 | 68 | conn, err := net.Dial("unix", base+c.Name+sock) 69 | if err != nil { 70 | 71 | if strings.Contains(err.Error(), "connect: no such file or directory") { 72 | 73 | } else if strings.Contains(err.Error(), "connect: connection refused") { 74 | 75 | } else { 76 | c.received <- &Message{Err: err, MsgType: -1} 77 | } 78 | 79 | } else { 80 | 81 | c.conn = conn 82 | 83 | err = c.handshake() 84 | if err != nil { 85 | return err 86 | } 87 | 88 | return nil 89 | } 90 | 91 | time.Sleep(c.retryTimer * time.Second) 92 | 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /connect_windows.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | "time" 7 | 8 | "github.com/Microsoft/go-winio" 9 | ) 10 | 11 | // Server function 12 | // Create the named pipe (if it doesn't already exist) and start listening for a client to connect. 13 | // when a client connects and connection is accepted the read function is called on a go routine. 14 | func (s *Server) run() error { 15 | 16 | var pipeBase = `\\.\pipe\` 17 | 18 | var config *winio.PipeConfig 19 | 20 | if s.unMask { 21 | config = &winio.PipeConfig{SecurityDescriptor: "D:P(A;;GA;;;AU)"} 22 | } 23 | 24 | listen, err := winio.ListenPipe(pipeBase+s.name, config) 25 | if err != nil { 26 | 27 | return err 28 | } 29 | 30 | s.listen = listen 31 | 32 | s.status = Listening 33 | 34 | go s.acceptLoop() 35 | 36 | return nil 37 | 38 | } 39 | 40 | // Client function 41 | // dial - attempts to connect to a named pipe created by the server 42 | func (c *Client) dial() error { 43 | 44 | var pipeBase = `\\.\pipe\` 45 | 46 | startTime := time.Now() 47 | 48 | for { 49 | if c.timeout != 0 { 50 | if time.Since(startTime).Seconds() > c.timeout { 51 | c.status = Closed 52 | return errors.New("timed out trying to connect") 53 | } 54 | } 55 | pn, err := winio.DialPipe(pipeBase+c.Name, nil) 56 | if err != nil { 57 | 58 | if strings.Contains(err.Error(), "the system cannot find the file specified.") == true { 59 | 60 | } else { 61 | return err 62 | } 63 | 64 | } else { 65 | 66 | c.conn = pn 67 | 68 | err = c.handshake() 69 | if err != nil { 70 | return err 71 | } 72 | return nil 73 | } 74 | 75 | time.Sleep(c.retryTimer * time.Second) 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /encryption.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/ecdsa" 7 | "crypto/elliptic" 8 | "crypto/rand" 9 | "crypto/sha256" 10 | "errors" 11 | "io" 12 | "net" 13 | ) 14 | 15 | func (sc *Server) keyExchange() ([32]byte, error) { 16 | 17 | var shared [32]byte 18 | 19 | priv, pub, err := generateKeys() 20 | if err != nil { 21 | return shared, err 22 | } 23 | 24 | // send servers public key 25 | err = sendPublic(sc.conn, pub) 26 | if err != nil { 27 | return shared, err 28 | } 29 | 30 | // received clients public key 31 | pubRecvd, err := recvPublic(sc.conn) 32 | if err != nil { 33 | return shared, err 34 | } 35 | 36 | b, _ := pubRecvd.Curve.ScalarMult(pubRecvd.X, pubRecvd.Y, priv.D.Bytes()) 37 | 38 | shared = sha256.Sum256(b.Bytes()) 39 | 40 | return shared, nil 41 | 42 | } 43 | 44 | func (cc *Client) keyExchange() ([32]byte, error) { 45 | 46 | var shared [32]byte 47 | 48 | priv, pub, err := generateKeys() 49 | if err != nil { 50 | return shared, err 51 | } 52 | 53 | // received servers public key 54 | pubRecvd, err := recvPublic(cc.conn) 55 | if err != nil { 56 | return shared, err 57 | } 58 | 59 | // send clients public key 60 | err = sendPublic(cc.conn, pub) 61 | if err != nil { 62 | return shared, err 63 | } 64 | 65 | b, _ := pubRecvd.Curve.ScalarMult(pubRecvd.X, pubRecvd.Y, priv.D.Bytes()) 66 | 67 | shared = sha256.Sum256(b.Bytes()) 68 | 69 | return shared, nil 70 | } 71 | 72 | func generateKeys() (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) { 73 | 74 | priva, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) 75 | if err != nil { 76 | return nil, nil, err 77 | } 78 | 79 | puba := &priva.PublicKey 80 | 81 | if !priva.IsOnCurve(puba.X, puba.Y) { 82 | return nil, nil, errors.New("keys created arn't on curve") 83 | } 84 | 85 | return priva, puba, err 86 | 87 | } 88 | 89 | func sendPublic(conn net.Conn, pub *ecdsa.PublicKey) error { 90 | 91 | pubSend := publicKeyToBytes(pub) 92 | if pubSend == nil { 93 | return errors.New("public key cannot be converted to bytes") 94 | } 95 | 96 | _, err := conn.Write(pubSend) 97 | if err != nil { 98 | return errors.New("could not sent public key") 99 | } 100 | 101 | return nil 102 | } 103 | 104 | func recvPublic(conn net.Conn) (*ecdsa.PublicKey, error) { 105 | 106 | buff := make([]byte, 98) 107 | i, err := conn.Read(buff) 108 | if err != nil { 109 | return nil, errors.New("didn't received public key") 110 | } 111 | 112 | if i != 97 { 113 | return nil, errors.New("public key received isn't valid length") 114 | } 115 | 116 | recvdPub := bytesToPublicKey(buff[:i]) 117 | 118 | if !recvdPub.IsOnCurve(recvdPub.X, recvdPub.Y) { 119 | return nil, errors.New("didn't received valid public key") 120 | } 121 | 122 | return recvdPub, nil 123 | } 124 | 125 | func publicKeyToBytes(pub *ecdsa.PublicKey) []byte { 126 | 127 | if pub == nil || pub.X == nil || pub.Y == nil { 128 | return nil 129 | } 130 | 131 | return elliptic.Marshal(elliptic.P384(), pub.X, pub.Y) 132 | } 133 | 134 | func bytesToPublicKey(recvdPub []byte) *ecdsa.PublicKey { 135 | 136 | if len(recvdPub) == 0 { 137 | return nil 138 | } 139 | 140 | x, y := elliptic.Unmarshal(elliptic.P384(), recvdPub) 141 | return &ecdsa.PublicKey{Curve: elliptic.P384(), X: x, Y: y} 142 | 143 | } 144 | 145 | func createCipher(shared [32]byte) (*cipher.AEAD, error) { 146 | 147 | b, err := aes.NewCipher(shared[:]) 148 | if err != nil { 149 | return nil, err 150 | } 151 | 152 | gcm, err := cipher.NewGCM(b) 153 | if err != nil { 154 | return nil, err 155 | } 156 | 157 | return &gcm, nil 158 | } 159 | 160 | func encrypt(g cipher.AEAD, data []byte) ([]byte, error) { 161 | 162 | nonce := make([]byte, g.NonceSize()) 163 | 164 | _, err := io.ReadFull(rand.Reader, nonce) 165 | 166 | return g.Seal(nonce, nonce, data, nil), err 167 | 168 | } 169 | 170 | func decrypt(g cipher.AEAD, recdData []byte) ([]byte, error) { 171 | 172 | nonceSize := g.NonceSize() 173 | if len(recdData) < nonceSize { 174 | return nil, errors.New("not enough data to decrypt") 175 | } 176 | 177 | nonce, recdData := recdData[:nonceSize], recdData[nonceSize:] 178 | plain, err := g.Open(nil, nonce, recdData, nil) 179 | if err != nil { 180 | return nil, err 181 | } 182 | 183 | return plain, nil 184 | 185 | } 186 | -------------------------------------------------------------------------------- /example/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-barrow/golang-ipc/cd515d151eb51b599c5a86c80ba51068e9543657/example/.DS_Store -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | ipc "github.com/james-barrow/golang-ipc" 7 | ) 8 | 9 | func main() { 10 | 11 | go server() 12 | 13 | c, err := ipc.StartClient("example1", nil) 14 | if err != nil { 15 | log.Println(err) 16 | return 17 | } 18 | 19 | for { 20 | 21 | message, err := c.Read() 22 | 23 | if err == nil { 24 | 25 | if message.MsgType == -1 { 26 | 27 | log.Println("client status", c.Status()) 28 | 29 | if message.Status == "Reconnecting" { 30 | c.Close() 31 | return 32 | } 33 | 34 | } else { 35 | 36 | log.Println("Client received: "+string(message.Data)+" - Message type: ", message.MsgType) 37 | c.Write(5, []byte("Message from client - PONG")) 38 | 39 | } 40 | 41 | } else { 42 | log.Println(err) 43 | break 44 | } 45 | } 46 | 47 | } 48 | 49 | func server() { 50 | 51 | s, err := ipc.StartServer("example1", nil) 52 | if err != nil { 53 | log.Println("server error", err) 54 | return 55 | } 56 | 57 | log.Println("server status", s.Status()) 58 | 59 | for { 60 | 61 | message, err := s.Read() 62 | 63 | if err == nil { 64 | 65 | if message.MsgType == -1 { 66 | 67 | if message.Status == "Connected" { 68 | 69 | log.Println("server status", s.Status()) 70 | s.Write(1, []byte("server - PING")) 71 | 72 | } 73 | 74 | } else { 75 | 76 | log.Println("Server received: "+string(message.Data)+" - Message type: ", message.MsgType) 77 | s.Close() 78 | return 79 | } 80 | 81 | } else { 82 | break 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/james-barrow/golang-ipc 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/Microsoft/go-winio v0.6.1 7 | golang.org/x/tools v0.9.1 // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 2 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 8 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 9 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 10 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 11 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 12 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 13 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 14 | golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= 15 | golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 16 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 17 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 18 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 19 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 20 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 21 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 22 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 23 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 24 | golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= 25 | golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 26 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 27 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 28 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 29 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 30 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 31 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 33 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 34 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 36 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 37 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 38 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 39 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 40 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 41 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 42 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 43 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 44 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 45 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 46 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 47 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 48 | golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= 49 | golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 50 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 51 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 52 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 53 | -------------------------------------------------------------------------------- /handshake.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | ) 8 | 9 | // 1st message sent from the server 10 | // byte 0 = protocal version no. 11 | // byte 1 = whether encryption is to be used - 0 no , 1 = encryption 12 | func (sc *Server) handshake() error { 13 | 14 | err := sc.one() 15 | if err != nil { 16 | return err 17 | } 18 | 19 | if sc.encryption { 20 | err = sc.startEncryption() 21 | if err != nil { 22 | return err 23 | } 24 | } 25 | 26 | err = sc.msgLength() 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return nil 32 | 33 | } 34 | 35 | func (sc *Server) one() error { 36 | 37 | buff := make([]byte, 2) 38 | 39 | buff[0] = byte(version) 40 | 41 | if sc.encryption { 42 | buff[1] = byte(1) 43 | } else { 44 | buff[1] = byte(0) 45 | } 46 | 47 | _, err := sc.conn.Write(buff) 48 | if err != nil { 49 | return errors.New("unable to send handshake ") 50 | } 51 | 52 | recv := make([]byte, 1) 53 | _, err = sc.conn.Read(recv) 54 | if err != nil { 55 | return errors.New("failed to received handshake reply") 56 | } 57 | 58 | switch result := recv[0]; result { 59 | case 0: 60 | return nil 61 | case 1: 62 | return errors.New("client has a different version number") 63 | case 2: 64 | return errors.New("client is enforcing encryption") 65 | case 3: 66 | return errors.New("server failed to get handshake reply") 67 | 68 | } 69 | 70 | return errors.New("other error - handshake failed") 71 | 72 | } 73 | 74 | func (sc *Server) startEncryption() error { 75 | 76 | shared, err := sc.keyExchange() 77 | if err != nil { 78 | return err 79 | } 80 | 81 | gcm, err := createCipher(shared) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | sc.enc = &encryption{ 87 | keyExchange: "ecdsa", 88 | encryption: "AES-GCM-256", 89 | cipher: gcm, 90 | } 91 | 92 | return nil 93 | 94 | } 95 | 96 | func (sc *Server) msgLength() error { 97 | 98 | toSend := make([]byte, 4) 99 | 100 | buff := make([]byte, 4) 101 | binary.BigEndian.PutUint32(buff, uint32(sc.maxMsgSize)) 102 | 103 | if sc.encryption { 104 | maxMsg, err := encrypt(*sc.enc.cipher, buff) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | binary.BigEndian.PutUint32(toSend, uint32(len(maxMsg))) 110 | toSend = append(toSend, maxMsg...) 111 | 112 | } else { 113 | 114 | binary.BigEndian.PutUint32(toSend, uint32(len(buff))) 115 | toSend = append(toSend, buff...) 116 | } 117 | 118 | _, err := sc.conn.Write(toSend) 119 | if err != nil { 120 | return errors.New("unable to send max message length ") 121 | } 122 | 123 | reply := make([]byte, 1) 124 | 125 | _, err = sc.conn.Read(reply) 126 | if err != nil { 127 | return errors.New("did not received message length reply") 128 | } 129 | 130 | return nil 131 | 132 | } 133 | 134 | // 1st message received by the client 135 | func (cc *Client) handshake() error { 136 | 137 | err := cc.one() 138 | if err != nil { 139 | return err 140 | } 141 | 142 | if cc.encryption { 143 | err := cc.startEncryption() 144 | if err != nil { 145 | return err 146 | } 147 | } 148 | 149 | err = cc.msgLength() 150 | if err != nil { 151 | return err 152 | } 153 | 154 | return nil 155 | 156 | } 157 | 158 | func (cc *Client) one() error { 159 | 160 | recv := make([]byte, 2) 161 | _, err := cc.conn.Read(recv) 162 | if err != nil { 163 | return errors.New("failed to received handshake message") 164 | } 165 | 166 | if recv[0] != version { 167 | cc.handshakeSendReply(1) 168 | return errors.New("server has sent a different version number") 169 | } 170 | 171 | if recv[1] != 1 && cc.encryptionReq { 172 | cc.handshakeSendReply(2) 173 | return errors.New("server tried to connect without encryption") 174 | } 175 | 176 | if recv[1] == 0 { 177 | cc.encryption = false 178 | } else { 179 | cc.encryption = true 180 | } 181 | 182 | cc.handshakeSendReply(0) // 0 is ok 183 | return nil 184 | 185 | } 186 | 187 | func (cc *Client) startEncryption() error { 188 | 189 | shared, err := cc.keyExchange() 190 | 191 | if err != nil { 192 | return err 193 | } 194 | 195 | gcm, err := createCipher(shared) 196 | if err != nil { 197 | return err 198 | } 199 | 200 | cc.enc = &encryption{ 201 | keyExchange: "ECDSA", 202 | encryption: "AES-GCM-256", 203 | cipher: gcm, 204 | } 205 | 206 | return nil 207 | } 208 | 209 | func (cc *Client) msgLength() error { 210 | 211 | buff := make([]byte, 4) 212 | 213 | _, err := cc.conn.Read(buff) 214 | if err != nil { 215 | return errors.New("failed to received max message length 1") 216 | } 217 | 218 | var msgLen uint32 219 | binary.Read(bytes.NewReader(buff), binary.BigEndian, &msgLen) // message length 220 | 221 | buff = make([]byte, int(msgLen)) 222 | 223 | _, err = cc.conn.Read(buff) 224 | if err != nil { 225 | return errors.New("failed to received max message length 2") 226 | } 227 | var buff2 []byte 228 | if cc.encryption { 229 | buff2, err = decrypt(*cc.enc.cipher, buff) 230 | if err != nil { 231 | return errors.New("failed to received max message length 3") 232 | } 233 | 234 | } else { 235 | buff2 = buff 236 | } 237 | 238 | var maxMsgSize uint32 239 | binary.Read(bytes.NewReader(buff2), binary.BigEndian, &maxMsgSize) // message length 240 | 241 | cc.maxMsgSize = int(maxMsgSize) 242 | cc.handshakeSendReply(0) 243 | 244 | return nil 245 | 246 | } 247 | 248 | func (cc *Client) handshakeSendReply(result byte) { 249 | 250 | buff := make([]byte, 1) 251 | buff[0] = result 252 | 253 | cc.conn.Write(buff) 254 | 255 | } 256 | -------------------------------------------------------------------------------- /headers.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | ) 7 | 8 | func intToBytes(mLen int) []byte { 9 | 10 | b := make([]byte, 4) 11 | binary.BigEndian.PutUint32(b, uint32(mLen)) 12 | 13 | return b 14 | 15 | } 16 | 17 | func bytesToInt(b []byte) int { 18 | 19 | var mlen uint32 20 | 21 | binary.Read(bytes.NewReader(b[:]), binary.BigEndian, &mlen) // message length 22 | 23 | return int(mlen) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /ipc_test.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestStartUp_Name(t *testing.T) { 12 | 13 | _, err := StartServer("", nil) 14 | if err.Error() != "ipcName cannot be an empty string" { 15 | t.Error("server - should have an error becuse the ipc name is empty") 16 | } 17 | _, err2 := StartClient("", nil) 18 | if err2.Error() != "ipcName cannot be an empty string" { 19 | t.Error("client - should have an error becuse the ipc name is empty") 20 | } 21 | } 22 | 23 | func TestStartUp_Configs(t *testing.T) { 24 | 25 | _, err := StartServer("test", nil) 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | 30 | _, err2 := StartClient("test", nil) 31 | if err2 != nil { 32 | t.Error(err) 33 | } 34 | 35 | scon := &ServerConfig{} 36 | 37 | ccon := &ClientConfig{} 38 | 39 | _, err3 := StartServer("test", scon) 40 | if err3 != nil { 41 | t.Error(err2) 42 | } 43 | 44 | _, err4 := StartClient("test", ccon) 45 | if err4 != nil { 46 | t.Error(err) 47 | } 48 | 49 | scon.MaxMsgSize = -1 50 | 51 | _, err5 := StartServer("test", scon) 52 | if err5 != nil { 53 | t.Error(err2) 54 | } 55 | 56 | ccon.Timeout = -1 57 | ccon.RetryTimer = -1 58 | 59 | _, err6 := StartClient("test", ccon) 60 | if err6 != nil { 61 | t.Error(err) 62 | } 63 | 64 | scon.MaxMsgSize = 1025 65 | ccon.RetryTimer = 1 66 | 67 | _, err7 := StartServer("test", scon) 68 | if err7 != nil { 69 | t.Error(err2) 70 | } 71 | 72 | _, err8 := StartClient("test", ccon) 73 | if err8 != nil { 74 | t.Error(err) 75 | } 76 | 77 | t.Run("Unmask Server Socket Permissions", func(t *testing.T) { 78 | scon.UnmaskPermissions = true 79 | 80 | srv, err := StartServer("test_perm", scon) 81 | if err != nil { 82 | t.Error(err) 83 | } 84 | 85 | // test would not work in windows 86 | // can check test_perm.sock in /tmp after running tests to see perms 87 | 88 | time.Sleep(time.Second / 4) 89 | 90 | info, err := os.Stat(srv.listen.Addr().String()) 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | got := fmt.Sprintf("%04o", info.Mode().Perm()) 95 | want := "0777" 96 | 97 | if got != want { 98 | t.Errorf("Got %q, Wanted %q", got, want) 99 | } 100 | 101 | scon.UnmaskPermissions = false 102 | }) 103 | } 104 | 105 | /* 106 | func TestStartUp_Timeout(t *testing.T) { 107 | 108 | scon := &ServerConfig{ 109 | Timeout: 1, 110 | } 111 | 112 | sc, _ := StartServer("test_dummy", scon) 113 | 114 | for { 115 | _, err1 := sc.Read() 116 | 117 | if err1 != nil { 118 | if err1.Error() != "timed out waiting for client to connect" { 119 | t.Error("should of got server timeout") 120 | } 121 | break 122 | } 123 | 124 | } 125 | 126 | ccon := &ClientConfig{ 127 | Timeout: 2, 128 | RetryTimer: 1, 129 | } 130 | 131 | cc, _ := StartClient("test2", ccon) 132 | 133 | for { 134 | _, err := cc.Read() 135 | if err != nil { 136 | if err.Error() != "timed out trying to connect" { 137 | t.Error("should of got timeout as client was trying to connect") 138 | } 139 | 140 | break 141 | 142 | } 143 | } 144 | 145 | } 146 | */ 147 | 148 | func TestWrite(t *testing.T) { 149 | 150 | sc, err := StartServer("test10", nil) 151 | if err != nil { 152 | t.Error(err) 153 | } 154 | 155 | time.Sleep(time.Second / 4) 156 | 157 | cc, err2 := StartClient("test10", nil) 158 | if err2 != nil { 159 | t.Error(err) 160 | } 161 | 162 | connected := make(chan bool, 1) 163 | 164 | go func() { 165 | 166 | for { 167 | 168 | m, _ := cc.Read() 169 | if m.Status == "Connected" { 170 | connected <- true 171 | } 172 | } 173 | }() 174 | 175 | go func() { 176 | 177 | for { 178 | sc.Read() 179 | } 180 | 181 | }() 182 | 183 | <-connected 184 | 185 | buf := make([]byte, 1) 186 | 187 | err3 := sc.Write(0, buf) 188 | if err3.Error() != "message type 0 is reserved" { 189 | t.Error("0 is not allowed as a message type") 190 | } 191 | 192 | buf = make([]byte, sc.maxMsgSize+5) 193 | err4 := sc.Write(2, buf) 194 | 195 | if err4.Error() != "message exceeds maximum message length" { 196 | t.Error("There should be an error as the data we're attempting to write is bigger than the maxMsgSize") 197 | } 198 | 199 | sc.status = NotConnected 200 | 201 | buf2 := make([]byte, 5) 202 | err5 := sc.Write(2, buf2) 203 | if err5.Error() != "Not Connected" { 204 | t.Error("we should have an error becuse there is no connection") 205 | } 206 | 207 | sc.status = Connected 208 | 209 | buf = make([]byte, 1) 210 | 211 | err = cc.Write(0, buf) 212 | if err == nil { 213 | t.Error("0 is not allowwed as a message try") 214 | } 215 | 216 | buf = make([]byte, maxMsgSize+5) 217 | err = cc.Write(2, buf) 218 | if err == nil { 219 | t.Error("There should be an error is the data we're attempting to write is bigger than the maxMsgSize") 220 | } 221 | 222 | cc.status = NotConnected 223 | 224 | buf = make([]byte, 5) 225 | err = cc.Write(2, buf) 226 | if err.Error() == "Not Connected" { 227 | 228 | } else { 229 | t.Error("we should have an error becuse there is no connection") 230 | } 231 | } 232 | 233 | func TestRead(t *testing.T) { 234 | 235 | sIPC := &Server{ 236 | name: "Test", 237 | status: NotConnected, 238 | received: make(chan *Message), 239 | timeout: 0, 240 | } 241 | 242 | sIPC.status = Connected 243 | 244 | serverFinished := make(chan bool, 1) 245 | 246 | go func(s *Server) { 247 | 248 | _, err := sIPC.Read() 249 | if err != nil { 250 | t.Error("err should be nill as tbe read function should read the 1st message added to received") 251 | } 252 | _, err2 := sIPC.Read() 253 | if err2 != nil { 254 | t.Error("err should be nill as tbe read function should read the 1st message added to received") 255 | } 256 | 257 | _, err3 := sIPC.Read() 258 | if err3 == nil { 259 | t.Error("we should get an error as the messages have been read and the channel closed") 260 | 261 | } else { 262 | serverFinished <- true 263 | } 264 | 265 | }(sIPC) 266 | 267 | sIPC.received <- &Message{MsgType: 1, Data: []byte("message 1")} 268 | sIPC.received <- &Message{MsgType: 1, Data: []byte("message 2")} 269 | close(sIPC.received) // close channel 270 | 271 | <-serverFinished 272 | 273 | // Client - read tests 274 | 275 | // 3 x client side tests 276 | cIPC := &Client{ 277 | Name: "test", 278 | timeout: 2, 279 | retryTimer: 1, 280 | status: NotConnected, 281 | received: make(chan *Message), 282 | } 283 | 284 | cIPC.status = Connected 285 | 286 | clientFinished := make(chan bool, 1) 287 | 288 | go func() { 289 | 290 | _, err4 := cIPC.Read() 291 | if err4 != nil { 292 | t.Error("err should be nill as tbe read function should read the 1st message added to received") 293 | } 294 | _, err5 := cIPC.Read() 295 | if err5 != nil { 296 | t.Error("err should be nill as tbe read function should read the 1st message added to received") 297 | } 298 | 299 | _, err6 := cIPC.Read() 300 | if err6 == nil { 301 | t.Error("we should get an error as the messages have been read and the channel closed") 302 | } else { 303 | clientFinished <- true 304 | } 305 | 306 | }() 307 | 308 | cIPC.received <- &Message{MsgType: 1, Data: []byte("message 1")} 309 | cIPC.received <- &Message{MsgType: 1, Data: []byte("message 1")} 310 | close(cIPC.received) // close received channel 311 | 312 | <-clientFinished 313 | } 314 | 315 | func TestStatus(t *testing.T) { 316 | 317 | sc := &Server{ 318 | status: NotConnected, 319 | } 320 | 321 | s := sc.getStatus() 322 | 323 | if s.String() != "Not Connected" { 324 | t.Error("status string should have returned Not Connected") 325 | } 326 | 327 | sc.status = Listening 328 | 329 | s1 := sc.getStatus() 330 | 331 | if s1.String() != "Listening" { 332 | t.Error("status string should have returned Listening") 333 | } 334 | 335 | sc.status = Connecting 336 | 337 | s1 = sc.getStatus() 338 | 339 | if s1.String() != "Connecting" { 340 | t.Error("status string should have returned Connecting") 341 | } 342 | 343 | sc.status = Connected 344 | 345 | s2 := sc.getStatus() 346 | 347 | if s2.String() != "Connected" { 348 | t.Error("status string should have returned Connected") 349 | } 350 | 351 | sc.status = ReConnecting 352 | 353 | s3 := sc.getStatus() 354 | 355 | if s3.String() != "Reconnecting" { 356 | t.Error("status string should have returned Reconnecting") 357 | } 358 | 359 | sc.status = Closed 360 | 361 | s4 := sc.getStatus() 362 | 363 | if s4.String() != "Closed" { 364 | t.Error("status string should have returned Closed") 365 | } 366 | 367 | sc.status = Error 368 | 369 | s5 := sc.getStatus() 370 | 371 | if s5.String() != "Error" { 372 | t.Error("status string should have returned Error") 373 | } 374 | 375 | sc.status = Closing 376 | 377 | s6 := sc.getStatus() 378 | 379 | if s6.String() != "Closing" { 380 | t.Error("status string should have returned Error") 381 | } 382 | 383 | if s6.String() != "Closing" { 384 | t.Error("status string should have returned Error") 385 | } 386 | 387 | sc.status = 33 388 | 389 | s7 := sc.getStatus() 390 | 391 | fmt.Println(s7.String()) 392 | if s7.String() != "Status not found" { 393 | t.Error("status string should have returned 'Status not found'") 394 | } 395 | 396 | cc := &Client{ 397 | status: NotConnected, 398 | } 399 | 400 | cc.getStatus() 401 | cc.Status() 402 | 403 | cc2 := &Client{ 404 | status: 9, 405 | } 406 | 407 | cc2.getStatus() 408 | cc2.Status() 409 | } 410 | 411 | func TestGetConnected(t *testing.T) { 412 | 413 | sc, err := StartServer("test22", nil) 414 | if err != nil { 415 | t.Error(err) 416 | } 417 | 418 | time.Sleep(time.Second / 2) 419 | 420 | cc, err2 := StartClient("test22", nil) 421 | if err2 != nil { 422 | t.Error(err) 423 | } 424 | 425 | for { 426 | cc.Read() 427 | m, _ := sc.Read() 428 | 429 | if m.Status == "Connected" { 430 | break 431 | } 432 | } 433 | } 434 | 435 | func TestServerWrongMessageType(t *testing.T) { 436 | 437 | sc, err := StartServer("test333", nil) 438 | if err != nil { 439 | t.Error(err) 440 | } 441 | 442 | time.Sleep(time.Second / 4) 443 | 444 | cc, err2 := StartClient("test333", nil) 445 | if err2 != nil { 446 | t.Error(err) 447 | } 448 | 449 | connected := make(chan bool, 1) 450 | connected2 := make(chan bool, 1) 451 | complete := make(chan bool, 1) 452 | 453 | go func() { 454 | 455 | ready := false 456 | 457 | for { 458 | m, _ := sc.Read() 459 | if m.Status == "Connected" { 460 | connected <- true 461 | ready = true 462 | continue 463 | } 464 | 465 | if ready == true { 466 | if m.MsgType != 5 { 467 | // received wrong message type 468 | 469 | } else { 470 | t.Error("should have got wrong message type") 471 | } 472 | complete <- true 473 | break 474 | } 475 | } 476 | 477 | }() 478 | 479 | go func() { 480 | for { 481 | m, _ := cc.Read() 482 | 483 | if m.Status == "Connected" { 484 | connected2 <- true 485 | } 486 | } 487 | }() 488 | 489 | <-connected 490 | <-connected2 491 | 492 | // test wrong message type 493 | cc.Write(2, []byte("hello server 1")) 494 | 495 | <-complete 496 | } 497 | func TestClientWrongMessageType(t *testing.T) { 498 | 499 | sc, err := StartServer("test3", nil) 500 | if err != nil { 501 | t.Error(err) 502 | } 503 | 504 | time.Sleep(time.Second / 4) 505 | 506 | cc, err2 := StartClient("test3", nil) 507 | if err2 != nil { 508 | t.Error(err) 509 | } 510 | 511 | connected := make(chan bool, 1) 512 | connected2 := make(chan bool, 1) 513 | complete := make(chan bool, 1) 514 | 515 | go func() { 516 | for { 517 | m, _ := sc.Read() 518 | if m.Status == "Connected" { 519 | connected2 <- true 520 | continue 521 | 522 | } 523 | 524 | } 525 | }() 526 | 527 | go func() { 528 | 529 | ready := false 530 | 531 | for { 532 | 533 | m, err45 := cc.Read() 534 | 535 | if m.Status == "Connected" { 536 | connected <- true 537 | ready = true 538 | continue 539 | 540 | } 541 | 542 | if ready == true { 543 | 544 | if err45 == nil { 545 | if m.MsgType != 5 { 546 | // received wrong message type 547 | } else { 548 | t.Error("should have got wrong message type") 549 | } 550 | complete <- true 551 | break 552 | 553 | } else { 554 | t.Error(err45) 555 | break 556 | } 557 | } 558 | 559 | } 560 | }() 561 | 562 | <-connected 563 | <-connected2 564 | sc.Write(2, []byte("")) 565 | 566 | <-complete 567 | } 568 | func TestServerCorrectMessageType(t *testing.T) { 569 | 570 | sc, err := StartServer("test358", nil) 571 | if err != nil { 572 | t.Error(err) 573 | } 574 | 575 | time.Sleep(time.Second / 4) 576 | 577 | cc, err2 := StartClient("test358", nil) 578 | if err2 != nil { 579 | t.Error(err) 580 | } 581 | 582 | connected := make(chan bool, 1) 583 | connected2 := make(chan bool, 1) 584 | complete := make(chan bool, 1) 585 | 586 | go func() { 587 | for { 588 | m, _ := sc.Read() 589 | if m.Status == "Connected" { 590 | connected2 <- true 591 | } 592 | } 593 | }() 594 | 595 | go func() { 596 | 597 | ready := false 598 | 599 | for { 600 | 601 | m, err23 := cc.Read() 602 | 603 | if m.Status == "Connected" { 604 | ready = true 605 | connected <- true 606 | continue 607 | } 608 | 609 | if ready == true { 610 | if err23 == nil { 611 | if m.MsgType == 5 { 612 | // received correct message type 613 | } else { 614 | t.Error("should have got correct message type") 615 | } 616 | 617 | complete <- true 618 | 619 | } else { 620 | t.Error(err23) 621 | break 622 | } 623 | } 624 | 625 | } 626 | }() 627 | 628 | <-connected 629 | <-connected2 630 | 631 | sc.Write(5, []byte("")) 632 | 633 | <-complete 634 | } 635 | 636 | func TestClientCorrectMessageType(t *testing.T) { 637 | 638 | sc, err := StartServer("test355", nil) 639 | if err != nil { 640 | t.Error(err) 641 | } 642 | 643 | time.Sleep(time.Second / 4) 644 | 645 | cc, err2 := StartClient("test355", nil) 646 | if err2 != nil { 647 | t.Error(err) 648 | } 649 | 650 | connected := make(chan bool, 1) 651 | connected2 := make(chan bool, 1) 652 | complete := make(chan bool, 1) 653 | 654 | go func() { 655 | 656 | for { 657 | m, _ := cc.Read() 658 | 659 | if m.Status == "Connected" { 660 | connected2 <- true 661 | } 662 | } 663 | 664 | }() 665 | 666 | go func() { 667 | 668 | ready := false 669 | 670 | for { 671 | 672 | m, err34 := sc.Read() 673 | 674 | if m.Status == "Connected" { 675 | ready = true 676 | connected <- true 677 | continue 678 | } 679 | 680 | if ready == true { 681 | if err34 == nil { 682 | if m.MsgType == 5 { 683 | // received correct message type 684 | } else { 685 | t.Error("should have got correct message type") 686 | } 687 | 688 | complete <- true 689 | 690 | } else { 691 | t.Error(err34) 692 | break 693 | } 694 | } 695 | } 696 | }() 697 | 698 | <-connected2 699 | <-connected 700 | 701 | cc.Write(5, []byte("")) 702 | <-complete 703 | } 704 | func TestServerSendMessage(t *testing.T) { 705 | 706 | sc, err := StartServer("test377", nil) 707 | if err != nil { 708 | t.Error(err) 709 | } 710 | 711 | time.Sleep(time.Second / 4) 712 | 713 | cc, err2 := StartClient("test377", nil) 714 | if err2 != nil { 715 | t.Error(err) 716 | } 717 | 718 | connected := make(chan bool, 1) 719 | connected2 := make(chan bool, 1) 720 | complete := make(chan bool, 1) 721 | 722 | go func() { 723 | 724 | for { 725 | 726 | m, _ := sc.Read() 727 | 728 | if m.Status == "Connected" { 729 | connected <- true 730 | } 731 | } 732 | }() 733 | 734 | go func() { 735 | 736 | ready := false 737 | 738 | for { 739 | 740 | m, err56 := cc.Read() 741 | 742 | if m.Status == "Connected" { 743 | ready = true 744 | connected2 <- true 745 | continue 746 | } 747 | 748 | if ready == true { 749 | if err56 == nil { 750 | if m.MsgType == 5 { 751 | if string(m.Data) == "Here is a test message sent from the server to the client... -/and some more test data to pad it out a bit" { 752 | // correct msg has been received 753 | } else { 754 | t.Error("Message recreceivedieved is wrong") 755 | } 756 | } else { 757 | t.Error("should have got correct message type") 758 | } 759 | 760 | complete <- true 761 | break 762 | 763 | } else { 764 | t.Error(err56) 765 | complete <- true 766 | break 767 | } 768 | 769 | } 770 | } 771 | 772 | }() 773 | 774 | <-connected2 775 | <-connected 776 | 777 | sc.Write(5, []byte("Here is a test message sent from the server to the client... -/and some more test data to pad it out a bit")) 778 | 779 | <-complete 780 | } 781 | func TestClientSendMessage(t *testing.T) { 782 | 783 | sc, err := StartServer("test3661", nil) 784 | if err != nil { 785 | t.Error(err) 786 | } 787 | 788 | time.Sleep(time.Second / 4) 789 | 790 | cc, err2 := StartClient("test3661", nil) 791 | if err2 != nil { 792 | t.Error(err) 793 | } 794 | 795 | connected := make(chan bool, 1) 796 | connected2 := make(chan bool, 1) 797 | complete := make(chan bool, 1) 798 | 799 | go func() { 800 | 801 | for { 802 | 803 | m, _ := cc.Read() 804 | if m.Status == "Connected" { 805 | connected <- true 806 | } 807 | 808 | } 809 | }() 810 | 811 | go func() { 812 | 813 | ready := false 814 | 815 | for { 816 | 817 | m, _ := sc.Read() 818 | 819 | if m.Status == "Connected" { 820 | ready = true 821 | connected2 <- true 822 | continue 823 | } 824 | 825 | if ready == true { 826 | if err == nil { 827 | if m.MsgType == 5 { 828 | 829 | if string(m.Data) == "Here is a test message sent from the client to the server... -/and some more test data to pad it out a bit" { 830 | // correct msg has been received 831 | } else { 832 | t.Error("Message recreceivedieved is wrong") 833 | } 834 | 835 | } else { 836 | t.Error("should have got correct message type") 837 | } 838 | complete <- true 839 | break 840 | 841 | } else { 842 | t.Error(err) 843 | complete <- true 844 | break 845 | } 846 | } 847 | 848 | } 849 | }() 850 | 851 | <-connected 852 | <-connected2 853 | 854 | cc.Write(5, []byte("Here is a test message sent from the client to the server... -/and some more test data to pad it out a bit")) 855 | 856 | <-complete 857 | } 858 | 859 | func TestEncryptionFunctions(t *testing.T) { 860 | 861 | res := publicKeyToBytes(nil) 862 | if len(res) != 0 { 863 | t.Error("should have returned 0 bytes") 864 | 865 | } 866 | 867 | buff := make([]byte, 0) 868 | 869 | if bytesToPublicKey(buff) != nil { 870 | t.Error("should have failed as buff is 0 bytes") 871 | } 872 | 873 | 874 | 875 | } 876 | 877 | func TestNoEncrytion(t *testing.T) { 878 | 879 | config := &ServerConfig{Encryption: false} 880 | 881 | sc, err := StartServer("test11", config) 882 | if err != nil { 883 | t.Error(err) 884 | } 885 | 886 | time.Sleep(time.Second / 4) 887 | 888 | config2 := &ClientConfig{Encryption: false} 889 | 890 | cc, err2 := StartClient("test11", config2) 891 | if err2 != nil { 892 | t.Error(err) 893 | } 894 | 895 | connected := make(chan bool, 1) 896 | connected2 := make(chan bool, 1) 897 | complete := make(chan bool, 1) 898 | complete2 := make(chan bool, 1) 899 | 900 | go func() { 901 | for { 902 | m, err := sc.Read() 903 | 904 | if m.Status == "Connected" { 905 | connected <- true 906 | continue 907 | } 908 | 909 | if err != nil { 910 | t.Error(err) 911 | } else if string(m.Data) == "Message to server" { 912 | complete2 <- true 913 | } 914 | 915 | } 916 | }() 917 | 918 | go func() { 919 | for { 920 | 921 | m, err := cc.Read() 922 | 923 | if m.Status == "Connected" { 924 | connected2 <- true 925 | continue 926 | } 927 | 928 | if err != nil { 929 | t.Error(err) 930 | } else if string(m.Data) == "Message to client" { 931 | complete <- true 932 | } 933 | } 934 | }() 935 | 936 | <-connected 937 | <-connected2 938 | 939 | sc.Write(2, []byte("Message to client")) 940 | cc.Write(2, []byte("Message to server")) 941 | 942 | <-complete 943 | <-complete2 944 | } 945 | func TestServerWrongEncrytion(t *testing.T) { 946 | 947 | config := &ServerConfig{Encryption: false} 948 | 949 | sc, err := StartServer("test11", config) 950 | if err != nil { 951 | t.Error(err) 952 | } 953 | 954 | time.Sleep(time.Second / 4) 955 | 956 | config2 := &ClientConfig{Encryption: true} 957 | 958 | cc, err2 := StartClient("test11", config2) 959 | if err2 != nil { 960 | t.Error(err) 961 | } 962 | 963 | go func() { 964 | for { 965 | m, err := cc.Read() 966 | if err != nil { 967 | if err.Error() != "server tried to connect without encryption" && m.MsgType != -2 { 968 | t.Error(err) 969 | } 970 | break 971 | } 972 | } 973 | }() 974 | 975 | for { 976 | mm, err2 := sc.Read() 977 | if err2 != nil { 978 | if err2.Error() != "client is enforcing encryption" && mm.MsgType != -2 { 979 | t.Error(err2) 980 | } else { 981 | break 982 | } 983 | break 984 | } 985 | } 986 | } 987 | 988 | func TestClientClose(t *testing.T) { 989 | 990 | sc, err := StartServer("test10A", nil) 991 | if err != nil { 992 | t.Error(err) 993 | } 994 | 995 | time.Sleep(time.Second / 4) 996 | 997 | cc, err2 := StartClient("test10A", nil) 998 | if err2 != nil { 999 | t.Error(err) 1000 | } 1001 | 1002 | holdIt := make(chan bool, 1) 1003 | 1004 | go func() { 1005 | 1006 | for { 1007 | 1008 | m, _ := sc.Read() 1009 | 1010 | if m.Status == "Disconnected" { 1011 | holdIt <- false 1012 | break 1013 | } 1014 | 1015 | } 1016 | 1017 | }() 1018 | 1019 | for { 1020 | 1021 | mm, err := cc.Read() 1022 | 1023 | if err == nil { 1024 | if mm.Status == "Connected" { 1025 | cc.Close() 1026 | } 1027 | 1028 | if mm.Status == "Closed" { 1029 | break 1030 | } 1031 | } 1032 | 1033 | } 1034 | 1035 | <-holdIt 1036 | } 1037 | 1038 | func TestServerClose(t *testing.T) { 1039 | 1040 | sc, err := StartServer("test1010", nil) 1041 | if err != nil { 1042 | t.Error(err) 1043 | } 1044 | 1045 | time.Sleep(time.Second / 4) 1046 | 1047 | cc, err2 := StartClient("test1010", nil) 1048 | if err2 != nil { 1049 | t.Error(err) 1050 | } 1051 | 1052 | holdIt := make(chan bool, 1) 1053 | 1054 | go func() { 1055 | 1056 | for { 1057 | 1058 | m, _ := cc.Read() 1059 | 1060 | if m.Status == "Reconnecting" { 1061 | holdIt <- false 1062 | break 1063 | } 1064 | } 1065 | 1066 | }() 1067 | 1068 | for { 1069 | 1070 | mm, err2 := sc.Read() 1071 | 1072 | if err2 == nil { 1073 | if mm.Status == "Connected" { 1074 | sc.Close() 1075 | } 1076 | 1077 | if mm.Status == "Closed" { 1078 | break 1079 | } 1080 | } 1081 | 1082 | } 1083 | 1084 | <-holdIt 1085 | } 1086 | 1087 | func TestClientReconnect(t *testing.T) { 1088 | 1089 | sc, err := StartServer("test127", nil) 1090 | if err != nil { 1091 | t.Error(err) 1092 | } 1093 | 1094 | time.Sleep(time.Second / 4) 1095 | 1096 | cc, err2 := StartClient("test127", nil) 1097 | if err2 != nil { 1098 | t.Error(err) 1099 | } 1100 | connected := make(chan bool, 1) 1101 | clientConfirm := make(chan bool, 1) 1102 | clientConnected := make(chan bool, 1) 1103 | 1104 | go func() { 1105 | 1106 | for { 1107 | 1108 | m, _ := sc.Read() 1109 | if m.Status == "Connected" { 1110 | connected <- true 1111 | break 1112 | } 1113 | 1114 | } 1115 | }() 1116 | 1117 | go func() { 1118 | 1119 | reconnectCheck := 0 1120 | 1121 | for { 1122 | 1123 | m, _ := cc.Read() 1124 | if m.Status == "Connected" { 1125 | clientConnected <- true 1126 | } 1127 | 1128 | if m.Status == "Reconnecting" { 1129 | reconnectCheck = 2 1130 | } 1131 | 1132 | if m.Status == "Connected" && reconnectCheck == 2 { 1133 | clientConfirm <- true 1134 | break 1135 | } 1136 | } 1137 | }() 1138 | 1139 | <-connected 1140 | <-clientConnected 1141 | 1142 | sc.Close() 1143 | 1144 | sc2, err := StartServer("test127", nil) 1145 | if err != nil { 1146 | t.Error(err) 1147 | } 1148 | 1149 | for { 1150 | 1151 | m, _ := sc2.Read() 1152 | if m.Status == "Connected" { 1153 | <-clientConfirm 1154 | break 1155 | } 1156 | } 1157 | } 1158 | 1159 | func TestClientReconnectTimeout(t *testing.T) { 1160 | 1161 | server, err := StartServer("test7", nil) 1162 | if err != nil { 1163 | t.Error(err) 1164 | } 1165 | 1166 | time.Sleep(time.Second / 4) 1167 | 1168 | config := &ClientConfig{ 1169 | Timeout: 2, 1170 | RetryTimer: 1, 1171 | } 1172 | 1173 | cc, err2 := StartClient("test7", config) 1174 | if err2 != nil { 1175 | t.Error(err) 1176 | } 1177 | 1178 | go func() { 1179 | 1180 | for { 1181 | 1182 | m, _ := server.Read() 1183 | if m.Status == "Connected" { 1184 | server.Close() 1185 | break 1186 | } 1187 | 1188 | } 1189 | }() 1190 | 1191 | connect := false 1192 | reconnect := false 1193 | 1194 | for { 1195 | 1196 | mm, err5 := cc.Read() 1197 | 1198 | if err5 == nil { 1199 | if mm.Status == "Connected" { 1200 | connect = true 1201 | } 1202 | 1203 | if mm.Status == "Reconnecting" { 1204 | reconnect = true 1205 | } 1206 | 1207 | if mm.Status == "Timeout" && reconnect == true && connect == true { 1208 | return 1209 | } 1210 | } 1211 | 1212 | if err5 != nil { 1213 | if err5.Error() != "timed out trying to re-connect" { 1214 | t.Fatal("should have got the timed out error") 1215 | } 1216 | 1217 | break 1218 | 1219 | } 1220 | } 1221 | } 1222 | 1223 | func TestServerReconnect(t *testing.T) { 1224 | 1225 | sc, err := StartServer("test127", nil) 1226 | if err != nil { 1227 | t.Error(err) 1228 | } 1229 | 1230 | cc, err2 := StartClient("test127", nil) 1231 | if err2 != nil { 1232 | t.Error(err2) 1233 | } 1234 | connected := make(chan bool, 1) 1235 | clientConfirm := make(chan bool, 1) 1236 | clientConnected := make(chan bool, 1) 1237 | 1238 | go func() { 1239 | 1240 | for { 1241 | 1242 | m, _ := cc.Read() 1243 | if m.Status == "Connected" { 1244 | <-clientConnected 1245 | 1246 | connected <- true 1247 | break 1248 | } 1249 | 1250 | } 1251 | }() 1252 | 1253 | go func() { 1254 | 1255 | reconnectCheck := 0 1256 | 1257 | for { 1258 | 1259 | m, err := sc.Read() 1260 | if err != nil { 1261 | fmt.Println(err) 1262 | return 1263 | } 1264 | 1265 | if m.Status == "Connected" { 1266 | clientConnected <- true 1267 | } 1268 | 1269 | if m.Status == "Disconnected" { 1270 | reconnectCheck = 1 1271 | } 1272 | 1273 | if m.Status == "Connected" && reconnectCheck == 1 { 1274 | clientConfirm <- true 1275 | break 1276 | } 1277 | } 1278 | }() 1279 | 1280 | <-connected 1281 | 1282 | cc.Close() 1283 | 1284 | c2, err := StartClient("test127", nil) 1285 | if err != nil { 1286 | t.Error(err) 1287 | } 1288 | 1289 | for { 1290 | 1291 | m, _ := c2.Read() 1292 | if m.Status == "Connected" { 1293 | break 1294 | } 1295 | } 1296 | 1297 | <-clientConfirm 1298 | } 1299 | 1300 | func TestServerReconnect2(t *testing.T) { 1301 | 1302 | sc, err := StartServer("test337", nil) 1303 | if err != nil { 1304 | t.Error(err) 1305 | } 1306 | 1307 | time.Sleep(time.Second / 4) 1308 | 1309 | cc, err2 := StartClient("test337", nil) 1310 | if err2 != nil { 1311 | t.Error(err) 1312 | } 1313 | 1314 | hasConnected := make(chan bool) 1315 | hasDisconnected := make(chan bool) 1316 | hasReconnected := make(chan bool) 1317 | 1318 | go func() { 1319 | 1320 | for { 1321 | 1322 | m, _ := cc.Read() 1323 | if m.Status == "Connected" { 1324 | 1325 | <-hasConnected 1326 | 1327 | cc.Close() 1328 | 1329 | <-hasDisconnected 1330 | 1331 | c2, err2 := StartClient("test337", nil) 1332 | if err2 != nil { 1333 | t.Error(err) 1334 | } 1335 | 1336 | for { 1337 | 1338 | m, _ := c2.Read() 1339 | if m.Status == "Connected" { 1340 | break 1341 | } 1342 | } 1343 | 1344 | <-hasReconnected 1345 | 1346 | return 1347 | } 1348 | 1349 | } 1350 | }() 1351 | 1352 | connect := false 1353 | disconnect := false 1354 | 1355 | for { 1356 | 1357 | m, _ := sc.Read() 1358 | if m.Status == "Connected" && connect == false { 1359 | hasConnected <- true 1360 | connect = true 1361 | 1362 | } 1363 | 1364 | if m.Status == "Disconnected" { 1365 | hasDisconnected <- true 1366 | disconnect = true 1367 | 1368 | } 1369 | 1370 | if m.Status == "Connected" && connect == true && disconnect == true { 1371 | hasReconnected <- true 1372 | return 1373 | } 1374 | } 1375 | } 1376 | 1377 | func TestClientReadClose(t *testing.T) { 1378 | 1379 | sc, err := StartServer("test7R", nil) 1380 | if err != nil { 1381 | t.Error(err) 1382 | } 1383 | 1384 | time.Sleep(time.Second / 4) 1385 | 1386 | config := &ClientConfig{ 1387 | Timeout: 2, 1388 | RetryTimer: 1, 1389 | } 1390 | 1391 | cc, err2 := StartClient("test7R", config) 1392 | if err2 != nil { 1393 | t.Error(err) 1394 | } 1395 | connected := make(chan bool, 1) 1396 | clientTimout := make(chan bool, 1) 1397 | clientConnected := make(chan bool, 1) 1398 | clientError := make(chan bool, 1) 1399 | 1400 | go func() { 1401 | 1402 | for { 1403 | 1404 | m, _ := sc.Read() 1405 | if m.Status == "Connected" { 1406 | connected <- true 1407 | break 1408 | } 1409 | 1410 | } 1411 | }() 1412 | 1413 | go func() { 1414 | 1415 | reconnect := false 1416 | 1417 | for { 1418 | 1419 | m, err3 := cc.Read() 1420 | 1421 | if err3 != nil { 1422 | if err3.Error() == "the received channel has been closed" { 1423 | clientError <- true // after the connection times out the received channel is closed, so we're now testing that the close error is returned. 1424 | // This is the only error the received function returns. 1425 | break 1426 | } 1427 | } 1428 | 1429 | if err3 == nil { 1430 | if m.Status == "Connected" { 1431 | clientConnected <- true 1432 | } 1433 | 1434 | if m.Status == "Reconnecting" { 1435 | reconnect = true 1436 | } 1437 | 1438 | if m.Status == "Timeout" && reconnect == true { 1439 | clientTimout <- true 1440 | } 1441 | } 1442 | 1443 | } 1444 | }() 1445 | 1446 | <-connected 1447 | <-clientConnected 1448 | 1449 | sc.Close() 1450 | 1451 | <-clientTimout 1452 | <-clientError 1453 | } 1454 | 1455 | func TestServerReceiveWrongVersionNumber(t *testing.T) { 1456 | 1457 | sc, err := StartServer("test5", nil) 1458 | if err != nil { 1459 | t.Error(err) 1460 | } 1461 | 1462 | go func() { 1463 | 1464 | cc := &Client{ 1465 | Name: "", 1466 | status: NotConnected, 1467 | received: make(chan *Message), 1468 | encryptionReq: false, 1469 | } 1470 | 1471 | time.Sleep(3 * time.Second) 1472 | 1473 | base := "/tmp/" 1474 | sock := ".sock" 1475 | conn, _ := net.Dial("unix", base+"test5"+sock) 1476 | 1477 | cc.conn = conn 1478 | 1479 | recv := make([]byte, 2) 1480 | _, err2 := cc.conn.Read(recv) 1481 | if err2 != nil { 1482 | return 1483 | } 1484 | 1485 | if recv[0] != 4 { 1486 | cc.handshakeSendReply(1) 1487 | return 1488 | } 1489 | 1490 | }() 1491 | 1492 | for { 1493 | 1494 | m, err := sc.Read() 1495 | if err != nil { 1496 | fmt.Println(err) 1497 | return 1498 | } 1499 | 1500 | if m.Err != nil { 1501 | if m.Err.Error() != "client has a different version number" { 1502 | t.Error("should have error because server sent the client the wrong version number 1") 1503 | } 1504 | } 1505 | } 1506 | } 1507 | 1508 | func TestClientReceiveWrongVersionNumber(t *testing.T) { 1509 | 1510 | //conn, err := s.listen.Accept() 1511 | //if err != nil { 1512 | // break 1513 | //} 1514 | } 1515 | 1516 | /* 1517 | // This test will not pass on Windows unless the net.Listen part for unix sockets is replaced with winio.ListenPipe 1518 | func TestServerWrongVersionNumber(t *testing.T) { 1519 | 1520 | sc := &Server{ 1521 | name: "test6", 1522 | status: NotConnected, 1523 | received: make(chan *Message), 1524 | } 1525 | 1526 | go func() { 1527 | 1528 | //var pipeBase = `\\.\pipe\` 1529 | 1530 | //listen, err := winio.ListenPipe(pipeBase+sc.name, nil) 1531 | //if err != nil { 1532 | 1533 | // return err 1534 | //} 1535 | 1536 | //sc.listen = listen 1537 | 1538 | base := "/tmp/" 1539 | sock := ".sock" 1540 | 1541 | os.RemoveAll(base + sc.name + sock) 1542 | 1543 | sc.listen, _ = net.Listen("unix", base+sc.name+sock) 1544 | 1545 | conn, _ := sc.listen.Accept() 1546 | sc.conn = conn 1547 | 1548 | buff := make([]byte, 2) 1549 | 1550 | buff[0] = byte(8) 1551 | 1552 | buff[1] = byte(0) 1553 | 1554 | _, err := sc.conn.Write(buff) 1555 | if err != nil { 1556 | //return errors.New("unable to send handshake ") 1557 | } 1558 | 1559 | m := sc.Read() 1560 | if m.Err == nil { 1561 | t.Error("Should have server sent wrong version number") 1562 | } 1563 | 1564 | }() 1565 | 1566 | time.Sleep(time.Second) 1567 | 1568 | cc, err := StartClient("test6", nil) 1569 | if err != nil { 1570 | t.Error(err) 1571 | } 1572 | 1573 | mm := cc.Read() 1574 | if mm.Err.Error() != "server has sent a different version number" { 1575 | t.Error("Should have wrong version number") 1576 | } 1577 | 1578 | } 1579 | 1580 | */ 1581 | -------------------------------------------------------------------------------- /server_all.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "io" 7 | "log" 8 | "time" 9 | ) 10 | 11 | // StartServer - starts the ipc server. 12 | // 13 | // ipcName - is the name of the unix socket or named pipe that will be created, the client needs to use the same name 14 | func StartServer(ipcName string, config *ServerConfig) (*Server, error) { 15 | 16 | err := checkIpcName(ipcName) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | s := &Server{ 22 | name: ipcName, 23 | status: NotConnected, 24 | received: make(chan *Message), 25 | toWrite: make(chan *Message), 26 | } 27 | 28 | if config == nil { 29 | s.timeout = 0 30 | s.maxMsgSize = maxMsgSize 31 | s.encryption = true 32 | s.unMask = false 33 | 34 | } else { 35 | 36 | if config.MaxMsgSize < 1024 { 37 | s.maxMsgSize = maxMsgSize 38 | } else { 39 | s.maxMsgSize = config.MaxMsgSize 40 | } 41 | 42 | if !config.Encryption { 43 | s.encryption = false 44 | } else { 45 | s.encryption = true 46 | } 47 | 48 | if config.UnmaskPermissions { 49 | s.unMask = true 50 | } else { 51 | s.unMask = false 52 | } 53 | } 54 | 55 | err = s.run() 56 | 57 | return s, err 58 | } 59 | 60 | func (s *Server) acceptLoop() { 61 | 62 | for { 63 | conn, err := s.listen.Accept() 64 | if err != nil { 65 | break 66 | } 67 | 68 | if s.status == Listening || s.status == Disconnected { 69 | 70 | s.conn = conn 71 | 72 | err2 := s.handshake() 73 | if err2 != nil { 74 | s.received <- &Message{Err: err2, MsgType: -1} 75 | s.status = Error 76 | s.listen.Close() 77 | s.conn.Close() 78 | 79 | } else { 80 | 81 | go s.read() 82 | go s.write() 83 | 84 | s.status = Connected 85 | s.received <- &Message{Status: s.status.String(), MsgType: -1} 86 | } 87 | 88 | } 89 | 90 | } 91 | 92 | } 93 | 94 | func (s *Server) read() { 95 | 96 | bLen := make([]byte, 4) 97 | 98 | for { 99 | 100 | res := s.readData(bLen) 101 | if !res { 102 | s.conn.Close() 103 | 104 | break 105 | } 106 | 107 | mLen := bytesToInt(bLen) 108 | 109 | msgRecvd := make([]byte, mLen) 110 | 111 | res = s.readData(msgRecvd) 112 | if !res { 113 | s.conn.Close() 114 | 115 | break 116 | } 117 | 118 | if s.encryption { 119 | msgFinal, err := decrypt(*s.enc.cipher, msgRecvd) 120 | if err != nil { 121 | s.received <- &Message{Err: err, MsgType: -1} 122 | continue 123 | } 124 | 125 | if bytesToInt(msgFinal[:4]) == 0 { 126 | // type 0 = control message 127 | } else { 128 | s.received <- &Message{Data: msgFinal[4:], MsgType: bytesToInt(msgFinal[:4])} 129 | } 130 | 131 | } else { 132 | if bytesToInt(msgRecvd[:4]) == 0 { 133 | // type 0 = control message 134 | } else { 135 | s.received <- &Message{Data: msgRecvd[4:], MsgType: bytesToInt(msgRecvd[:4])} 136 | } 137 | } 138 | 139 | } 140 | 141 | } 142 | 143 | func (s *Server) readData(buff []byte) bool { 144 | 145 | _, err := io.ReadFull(s.conn, buff) 146 | if err != nil { 147 | 148 | if s.status == Closing { 149 | 150 | s.status = Closed 151 | s.received <- &Message{Status: s.status.String(), MsgType: -1} 152 | s.received <- &Message{Err: errors.New("server has closed the connection"), MsgType: -1} 153 | return false 154 | } 155 | 156 | if err == io.EOF { 157 | 158 | s.status = Disconnected 159 | s.received <- &Message{Status: s.status.String(), MsgType: -1} 160 | return false 161 | } 162 | 163 | } 164 | 165 | return true 166 | } 167 | 168 | // Read - blocking function, reads each message recieved 169 | // if MsgType is a negative number its an internal message 170 | func (s *Server) Read() (*Message, error) { 171 | 172 | m, ok := (<-s.received) 173 | if !ok { 174 | return nil, errors.New("the received channel has been closed") 175 | } 176 | 177 | if m.Err != nil { 178 | //close(s.received) 179 | //close(s.toWrite) 180 | return nil, m.Err 181 | } 182 | 183 | return m, nil 184 | } 185 | 186 | // Write - writes a message to the ipc connection 187 | // msgType - denotes the type of data being sent. 0 is a reserved type for internal messages and errors. 188 | func (s *Server) Write(msgType int, message []byte) error { 189 | 190 | if msgType == 0 { 191 | return errors.New("message type 0 is reserved") 192 | } 193 | 194 | mlen := len(message) 195 | 196 | if mlen > s.maxMsgSize { 197 | return errors.New("message exceeds maximum message length") 198 | } 199 | 200 | if s.status == Connected { 201 | 202 | s.toWrite <- &Message{MsgType: msgType, Data: message} 203 | 204 | } else { 205 | return errors.New(s.status.String()) 206 | } 207 | 208 | return nil 209 | } 210 | 211 | func (s *Server) write() { 212 | 213 | for { 214 | 215 | m, ok := <-s.toWrite 216 | 217 | if !ok { 218 | break 219 | } 220 | 221 | toSend := intToBytes(m.MsgType) 222 | 223 | writer := bufio.NewWriter(s.conn) 224 | 225 | if s.encryption { 226 | toSend = append(toSend, m.Data...) 227 | toSendEnc, err := encrypt(*s.enc.cipher, toSend) 228 | if err != nil { 229 | log.Println("error encrypting data", err) 230 | continue 231 | } 232 | 233 | toSend = toSendEnc 234 | } else { 235 | 236 | toSend = append(toSend, m.Data...) 237 | 238 | } 239 | 240 | writer.Write(intToBytes(len(toSend))) 241 | writer.Write(toSend) 242 | 243 | err := writer.Flush() 244 | if err != nil { 245 | log.Println("error flushing data", err) 246 | continue 247 | } 248 | 249 | time.Sleep(2 * time.Millisecond) 250 | 251 | } 252 | } 253 | 254 | 255 | // getStatus - get the current status of the connection 256 | func (s *Server) getStatus() Status { 257 | 258 | return s.status 259 | } 260 | 261 | 262 | // StatusCode - returns the current connection status 263 | func (s *Server) StatusCode() Status { 264 | return s.status 265 | } 266 | 267 | // Status - returns the current connection status as a string 268 | func (s *Server) Status() string { 269 | 270 | return s.status.String() 271 | } 272 | 273 | // Close - closes the connection 274 | func (s *Server) Close() { 275 | 276 | s.status = Closing 277 | 278 | if s.listen != nil { 279 | s.listen.Close() 280 | } 281 | 282 | if s.conn != nil { 283 | s.conn.Close() 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /shared.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import "errors" 4 | 5 | // returns the status of the connection as a string 6 | func (status *Status) String() string { 7 | 8 | switch *status { 9 | case NotConnected: 10 | return "Not Connected" 11 | case Connecting: 12 | return "Connecting" 13 | case Connected: 14 | return "Connected" 15 | case Listening: 16 | return "Listening" 17 | case Closing: 18 | return "Closing" 19 | case ReConnecting: 20 | return "Reconnecting" 21 | case Timeout: 22 | return "Timeout" 23 | case Closed: 24 | return "Closed" 25 | case Error: 26 | return "Error" 27 | case Disconnected: 28 | return "Disconnected" 29 | default: 30 | return "Status not found" 31 | } 32 | } 33 | 34 | // checks the name passed into the start function to ensure it's ok/will work. 35 | func checkIpcName(ipcName string) error { 36 | 37 | if len(ipcName) == 0 { 38 | return errors.New("ipcName cannot be an empty string") 39 | } 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | import ( 4 | "crypto/cipher" 5 | "net" 6 | "time" 7 | ) 8 | 9 | // Server - holds the details of the server connection & config. 10 | type Server struct { 11 | name string 12 | listen net.Listener 13 | conn net.Conn 14 | status Status 15 | received chan (*Message) 16 | toWrite chan (*Message) 17 | timeout time.Duration 18 | encryption bool 19 | maxMsgSize int 20 | enc *encryption 21 | unMask bool 22 | } 23 | 24 | // Client - holds the details of the client connection and config. 25 | type Client struct { 26 | Name string 27 | conn net.Conn 28 | status Status 29 | timeout float64 // 30 | retryTimer time.Duration // number of seconds before trying to connect again 31 | received chan (*Message) 32 | toWrite chan (*Message) 33 | encryption bool 34 | encryptionReq bool 35 | maxMsgSize int 36 | enc *encryption 37 | } 38 | 39 | // Message - contains the received message 40 | type Message struct { 41 | Err error // details of any error 42 | MsgType int // 0 = reserved , -1 is an internal message (disconnection or error etc), all messages recieved will be > 0 43 | Data []byte // message data received 44 | Status string // the status of the connection 45 | } 46 | 47 | // Status - Status of the connection 48 | type Status int 49 | 50 | const ( 51 | 52 | // NotConnected - 0 53 | NotConnected Status = iota 54 | // Listening - 1 55 | Listening 56 | // Connecting - 2 57 | Connecting 58 | // Connected - 3 59 | Connected 60 | // ReConnecting - 4 61 | ReConnecting 62 | // Closed - 5 63 | Closed 64 | // Closing - 6 65 | Closing 66 | // Error - 7 67 | Error 68 | // Timeout - 8 69 | Timeout 70 | // Disconnected - 9 71 | Disconnected 72 | ) 73 | 74 | // ServerConfig - used to pass configuation overrides to ServerStart() 75 | type ServerConfig struct { 76 | MaxMsgSize int 77 | Encryption bool 78 | UnmaskPermissions bool 79 | } 80 | 81 | // ClientConfig - used to pass configuation overrides to ClientStart() 82 | type ClientConfig struct { 83 | Timeout float64 84 | RetryTimer time.Duration 85 | Encryption bool 86 | } 87 | 88 | // Encryption - encryption settings 89 | type encryption struct { 90 | keyExchange string 91 | encryption string 92 | cipher *cipher.AEAD 93 | } 94 | -------------------------------------------------------------------------------- /vars.go: -------------------------------------------------------------------------------- 1 | package ipc 2 | 3 | const version = 2 // ipc package version 4 | 5 | const maxMsgSize = 3145728 // 3Mb - Maximum bytes allowed for each message 6 | --------------------------------------------------------------------------------