117 lines
2.6 KiB
Go
117 lines
2.6 KiB
Go
package modbus
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// LoadCertPool loads a certificate store from a file into a CertPool object.
|
|
func LoadCertPool(filePath string) (cp *x509.CertPool, err error) {
|
|
var buf []byte
|
|
|
|
// read the entire cert store, which may contain zero, one
|
|
// or more certificates
|
|
buf, err = ioutil.ReadFile(filePath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if len(buf) == 0 {
|
|
err = fmt.Errorf("%v: empty file", filePath)
|
|
return
|
|
}
|
|
|
|
// add these certs to the pool
|
|
cp = x509.NewCertPool()
|
|
cp.AppendCertsFromPEM(buf)
|
|
|
|
// let the caller know if no usable certificate was found
|
|
if len(cp.Subjects()) == 0 {
|
|
err = fmt.Errorf("%v: no certificate found", filePath)
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// tlsSockWrapper wraps a TLS socket to work around odd error handling in
|
|
// TLSConn on internal connection state corruption.
|
|
// tlsSockWrapper implements the net.Conn interface to allow its
|
|
// use by the modbus TCP transport.
|
|
type tlsSockWrapper struct {
|
|
sock net.Conn
|
|
}
|
|
|
|
func newTLSSockWrapper(sock net.Conn) (tsw *tlsSockWrapper) {
|
|
tsw = &tlsSockWrapper{
|
|
sock: sock,
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (tsw *tlsSockWrapper) Read(buf []byte) (rlen int, err error) {
|
|
rlen, err = tsw.sock.Read(buf)
|
|
|
|
return
|
|
}
|
|
|
|
func (tsw *tlsSockWrapper) Write(buf []byte) (wlen int, err error) {
|
|
wlen, err = tsw.sock.Write(buf)
|
|
|
|
// since write timeouts corrupt the internal state of TLS sockets,
|
|
// any subsequent read/write operation will fail and return the same write
|
|
// timeout error (see https://pkg.go.dev/crypto/tls#Conn.SetWriteDeadline).
|
|
// this isn't all that helpful to clients, which may be tricked into
|
|
// retrying forever, treating timeout errors as transient.
|
|
// to avoid this, close the TLS socket after the first write timeout.
|
|
// this ensures that clients 1) get a timeout error on the first write timeout
|
|
// and 2) get an ErrNetClosing "use of closed network connection" on subsequent
|
|
// operations.
|
|
if err != nil && os.IsTimeout(err) {
|
|
tsw.sock.Close()
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (tsw *tlsSockWrapper) Close() (err error) {
|
|
err = tsw.sock.Close()
|
|
|
|
return
|
|
}
|
|
|
|
func (tsw *tlsSockWrapper) SetDeadline(deadline time.Time) (err error) {
|
|
err = tsw.sock.SetDeadline(deadline)
|
|
|
|
return
|
|
}
|
|
|
|
func (tsw *tlsSockWrapper) SetReadDeadline(deadline time.Time) (err error) {
|
|
err = tsw.sock.SetReadDeadline(deadline)
|
|
|
|
return
|
|
}
|
|
|
|
func (tsw *tlsSockWrapper) SetWriteDeadline(deadline time.Time) (err error) {
|
|
err = tsw.sock.SetWriteDeadline(deadline)
|
|
|
|
return
|
|
}
|
|
|
|
func (tsw *tlsSockWrapper) LocalAddr() (addr net.Addr) {
|
|
addr = tsw.sock.LocalAddr()
|
|
|
|
return
|
|
}
|
|
|
|
func (tsw *tlsSockWrapper) RemoteAddr() (addr net.Addr) {
|
|
addr = tsw.sock.RemoteAddr()
|
|
|
|
return
|
|
}
|