Files
modbus/tls_utils.go
2025-11-07 13:53:18 +08:00

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
}