Skip to content

Commit

Permalink
fixes + new example.
Browse files Browse the repository at this point in the history
  • Loading branch information
james-barrow committed May 24, 2023
1 parent f314a9b commit 71e822d
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 608 deletions.
7 changes: 7 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": []
}
130 changes: 82 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ As well as using this library just for go processes it was also designed to work

#### NodeJs

I currently use this library to comunicate between a ElectronJs GUI and a go program.
I currently use this library to comunicate between a ElectronJS GUI and a go program.

Below is a link to the nodeJS client library:

https://github.com/james-barrow/node-ipc-client

Expand All @@ -27,95 +29,127 @@ Create a server with the default configuation and start listening for the client

```go

sc, err := ipc.StartServer("<name of socket or pipe>", nil)
s, err := ipc.StartServer("<name of socket or pipe>", nil)
if err != nil {
log.Println(err)
return
}

```

Create a client and connect to the server:

```go

cc, err := ipc.StartClient("<name of socket or pipe>", nil)
c, err := ipc.StartClient("<name of socket or pipe>", nil)
if err != nil {
log.Println(err)
return
}

```
Read and write data to the connection:

### Read messages

Read each message sent:

```go

for {

// message, err := s.Read() server
message, err := c.Read() // client

if err == nil {
// handle error
}

// do something with the received messages
}

```

All received messages are formated into the type Message

```go

type Message struct {
Err error // details of any error
MsgType int // 0 = reserved , -1 is an internal message (disconnection or error etc), all messages recieved will be > 0
Data []byte // message data received
Status string // the status of the connection
}

```

### Write a message


```go

//err := s.Write(1, []byte("<Message for client"))
err := c.Write(1, []byte("<Message for server"))

if err == nil {
// handle error
}

```

## Advanced Configuaration

Server options:

```go

config := &ipc.ServerConfig{
Encryption: (bool), // allows encryption to be switched off (bool - default is true)
MaxMsgSize: (int) , // the maximum size in bytes of each message ( default is 3145728 / 3Mb)
UnmaskPermissions: (bool), // make the socket writeable for other users (default is false)
}


```

Client options:

```go
// write data
_ = sc.Write(1, []byte("Message from server"))

_ = cc.Write(5, []byte("Message from client"))


// Read data
for {

dataType, data, err := sc.Read()

if err == nil {
log.Println("Server received: "+string(data)+" - Message type: ", dataType)
} else {
log.Println(err)
break
}
}


for {

dataType, data, err := cc.Read()

if err == nil {
log.Println("Client recireceivedeved: "+string(data)+" - Message type: ", dataType)
} else {
log.Println(err)
break
}
}

config := ClientConfig {
Encryption (bool), // allows encryption to be switched off (bool - default is true)
Timeout (float64), // number of seconds to wait before timing out trying to connect/reconnect (default is 0 no timeout)
RetryTimer (time.Duration), // number of seconds to wait before connection retry (default is 20)

}

```

### Encryption

By default the connection established will be encypted, ECDH384 is used for the key exchange and AES 256 GCM is used for the cipher.

Encryption can be swithed off by passing in a custom configuation to the server & client start functions.
Encryption can be swithed off by passing in a custom configuation to the server & client start function:

```go

config := &ipc.ServerConfig{Encryption: false}
sc, err := ipc.StartServer("<name of socket or pipe>", config)

Encryption: false
```

### Unix Socket Permissions

Under most configurations, a socket created by a user will by default not be writable by another user, making it impossible for the client and server to communicate if being run by separate users.

The permission mask can be dropped during socket creation by passing custom configuration to the server start function. **This will make the socket writable by any user.**
The permission mask can be dropped during socket creation by passing a custom configuration to the server start function. **This will make the socket writable for any user.**

```go

config := &ipc.ServerConfig{UnmaskPermissions: true}
sc, err := ipc.StartServer("<name of socket or pipe>", config)

UnmaskPermissions: true
```
Note: Tested on Linux, not tested on Mac, not implemented on Windows.



### Testing
## Testing

The package has been tested on Mac, Windows and Linux and has extensive test coverage.

### Licence
## Licence

MIT
23 changes: 10 additions & 13 deletions client_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ import (
)

// StartClient - start the ipc client.
//
// ipcName = is the name of the unix socket or named pipe that the client will try and connect to.
// timeout = number of seconds before the socket/pipe times out trying to connect/re-cconnect - if -1 or 0 it never times out.
// retryTimer = number of seconds before the client tries to connect again.
func StartClient(ipcName string, config *ClientConfig) (*Client, error) {

err := checkIpcName(ipcName)
Expand All @@ -32,7 +29,7 @@ func StartClient(ipcName string, config *ClientConfig) (*Client, error) {
if config == nil {

cc.timeout = 0
cc.retryTimer = time.Duration(1)
cc.retryTimer = time.Duration(20)
cc.encryptionReq = true

} else {
Expand Down Expand Up @@ -68,7 +65,7 @@ func startClient(c *Client) {

err := c.dial()
if err != nil {
c.received <- &Message{err: err, MsgType: -2}
c.received <- &Message{Err: err, MsgType: -1}
return
}

Expand Down Expand Up @@ -137,7 +134,7 @@ func (c *Client) readData(buff []byte) bool {
if c.status == Closing {
c.status = Closed
c.received <- &Message{Status: c.status.String(), MsgType: -1}
c.received <- &Message{err: errors.New("client has closed the connection"), MsgType: -2}
c.received <- &Message{Err: errors.New("client has closed the connection"), MsgType: -2}
return false
}

Expand All @@ -157,10 +154,10 @@ func (c *Client) reconnect() {

err := c.dial() // connect to the pipe
if err != nil {
if err.Error() == "Timed out trying to connect" {
if err.Error() == "timed out trying to connect" {
c.status = Timeout
c.received <- &Message{Status: c.status.String(), MsgType: -1}
c.received <- &Message{err: errors.New("timed out trying to re-connect"), MsgType: -2}
c.received <- &Message{Err: errors.New("timed out trying to re-connect"), MsgType: -1}
}

return
Expand All @@ -172,25 +169,25 @@ func (c *Client) reconnect() {
go c.read()
}

// Read - blocking function that waits until an non multipart message is received
// returns the message type, data and any error.
// Read - blocking function that receices messages
// if MsgType is a negative number its an internal message
func (c *Client) Read() (*Message, error) {

m, ok := (<-c.received)
if !ok {
return nil, errors.New("the received channel has been closed")
}

if m.err != nil {
if m.Err != nil {
close(c.received)
close(c.toWrite)
return nil, m.err
return nil, m.Err
}

return m, nil
}

// Write - writes a non multipart message to the ipc connection.
// Write - writes a message to the ipc connection.
// msgType - denotes the type of data being sent. 0 is a reserved type for internal messages and errors.
func (c *Client) Write(msgType int, message []byte) error {

Expand Down
14 changes: 4 additions & 10 deletions connect_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,9 @@ func (s *Server) run() error {

s.listen = listen

s.status = Listening
s.received <- &Message{Status: s.status.String(), MsgType: -1}
s.connChannel = make(chan bool)

go s.acceptLoop()

err = s.connectionTimer()
if err != nil {
return err
}
s.status = Listening

return nil

Expand All @@ -63,9 +56,10 @@ func (c *Client) dial() error {
startTime := time.Now()

for {

if c.timeout != 0 {

if time.Now().Sub(startTime).Seconds() > c.timeout {
if time.Since(startTime).Seconds() > c.timeout {
c.status = Closed
return errors.New("timed out trying to connect")
}
Expand All @@ -79,7 +73,7 @@ func (c *Client) dial() error {
} else if strings.Contains(err.Error(), "connect: connection refused") {

} else {
c.received <- &Message{err: err, MsgType: -2}
c.received <- &Message{Err: err, MsgType: -1}
}

} else {
Expand Down
2 changes: 1 addition & 1 deletion connect_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (c *Client) dial() error {

for {
if c.timeout != 0 {
if time.Now().Sub(startTime).Seconds() > c.timeout {
if time.Since(startTime).Seconds() > c.timeout {
c.status = Closed
return errors.New("timed out trying to connect")
}
Expand Down
9 changes: 3 additions & 6 deletions encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func sendPublic(conn net.Conn, pub *ecdsa.PublicKey) error {

func recvPublic(conn net.Conn) (*ecdsa.PublicKey, error) {

buff := make([]byte, 300)
buff := make([]byte, 98)
i, err := conn.Read(buff)
if err != nil {
return nil, errors.New("didn't received public key")
Expand Down Expand Up @@ -145,7 +145,6 @@ func bytesToPublicKey(recvdPub []byte) *ecdsa.PublicKey {
func createCipher(shared [32]byte) (*cipher.AEAD, error) {

b, err := aes.NewCipher(shared[:])

if err != nil {
return nil, err
}
Expand All @@ -162,11 +161,9 @@ func encrypt(g cipher.AEAD, data []byte) ([]byte, error) {

nonce := make([]byte, g.NonceSize())

if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
_, err := io.ReadFull(rand.Reader, nonce)

return g.Seal(nonce, nonce, data, nil), nil
return g.Seal(nonce, nonce, data, nil), err

}

Expand Down
Loading

0 comments on commit 71e822d

Please sign in to comment.