356 lines
7.2 KiB
Go
356 lines
7.2 KiB
Go
package cryptparse
|
|
|
|
import (
|
|
`crypto`
|
|
`crypto/tls`
|
|
`fmt`
|
|
`net`
|
|
`net/url`
|
|
`os`
|
|
`strconv`
|
|
`strings`
|
|
)
|
|
|
|
/*
|
|
SetHost allows one to explicitly set the host component of the URI.
|
|
A host can be removed from a TlsUri by invoking this method with an empty hostAddr string.
|
|
|
|
No validation is performed.
|
|
*/
|
|
func (t *TlsUri) SetHost(hostAddr string) {
|
|
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
if t.Port() == "" {
|
|
t.Host = hostAddr
|
|
} else {
|
|
t.Host = fmt.Sprintf("%s:%s", hostAddr, t.Port())
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
SetHostPort is a small wrapper around the SetHost and SetPort methods, combining them into one.
|
|
|
|
Refer to the comments for each on usage.
|
|
*/
|
|
func (t *TlsUri) SetHostPort(host string, port *uint16) {
|
|
|
|
t.SetPort(port)
|
|
t.SetHost(host)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
SetHostPortStr is a small wrapper around the SetHost and SetPortStr methods, combining them into one.
|
|
|
|
Refer to the comments for each on usage.
|
|
*/
|
|
func (t *TlsUri) SetHostPortStr(host string, port string) (err error) {
|
|
|
|
if err = t.SetPortStr(port); err != nil {
|
|
return
|
|
}
|
|
t.SetHost(host)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
SetPort allows one to explicitly set the port component of the URI.
|
|
A port can be removed from a TlsUri by invoking this method with a nil port.
|
|
*/
|
|
func (t *TlsUri) SetPort(port *uint16) {
|
|
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
if port == nil {
|
|
t.Host = t.Hostname()
|
|
} else {
|
|
t.Host = fmt.Sprintf("%s:%d", t.Hostname(), *port)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
SetPortStr allows one to specify the port number as a string instead of a uint16 ptr.
|
|
If port is an empty string, any existing defined port will be removed from t.
|
|
*/
|
|
func (t *TlsUri) SetPortStr(port string) (err error) {
|
|
|
|
var n uint64
|
|
var u uint16
|
|
|
|
if port == "" {
|
|
t.Host = t.Hostname()
|
|
} else {
|
|
if n, err = strconv.ParseUint(port, 10, 16); err == nil {
|
|
return
|
|
}
|
|
if n > 65535 {
|
|
err = ErrBadPortRange
|
|
return
|
|
}
|
|
u = uint16(n)
|
|
t.Host = fmt.Sprintf("%s:%d", t.Hostname(), u)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
WithConn returns a (crypto/)tls.Conn from an existing/already dialed net.Conn.
|
|
|
|
underlying should be a "bare" net.Conn; behavior is undefined/unknown if the underlying conn is already a (crypto/)tls.Conn.
|
|
*/
|
|
func (t *TlsUri) WithConn(underlying net.Conn) (conn *tls.Conn, err error) {
|
|
|
|
var cfg *tls.Config
|
|
|
|
if cfg, err = t.ToTlsConfig(); err != nil {
|
|
return
|
|
}
|
|
|
|
conn = tls.Client(underlying, cfg)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
ToConn returns a "bare" net.Conn (already dialed) from a TlsUri.
|
|
|
|
Note that this does NOT include the TLS configured or initialized; use TlsUri.ToTlsConn for that.
|
|
(A (crypto/)tls.Conn conforms to net.Conn.)
|
|
|
|
An error will be returned if no port is explicitly defined in the TlsUri.
|
|
*/
|
|
func (t *TlsUri) ToConn() (conn net.Conn, err error) {
|
|
|
|
var ok bool
|
|
var connHost string
|
|
var params map[string][]string
|
|
var netType string = DefaultNetType
|
|
|
|
params = t.Query()
|
|
|
|
if params != nil {
|
|
if _, ok = params[ParamNet]; ok {
|
|
netType = params[ParamNet][0]
|
|
}
|
|
}
|
|
netType = strings.ToLower(netType)
|
|
|
|
switch netType {
|
|
case "unix", "unixgram", "unixpacket":
|
|
connHost = t.Path
|
|
default:
|
|
connHost = t.Host
|
|
}
|
|
|
|
if conn, err = net.Dial(netType, connHost); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
ToTlsConfig returns a *tls.Config from a TlsUri.
|
|
|
|
Unfortunately it's not possible for this library to do the reverse, as CA certificates are not able to be extracted from an x509.CertPool.
|
|
*/
|
|
func (t *TlsUri) ToTlsConfig() (cfg *tls.Config, err error) {
|
|
|
|
if cfg, err = ParseTlsUri(t.URL); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
ToTlsConn returns a (crypto/)tls.Conn (already dialed) from a TlsUri.
|
|
|
|
An error will be returned if no port is explicitly defined in the TlsUri.
|
|
*/
|
|
func (t *TlsUri) ToTlsConn() (conn *tls.Conn, err error) {
|
|
|
|
var ok bool
|
|
var cfg *tls.Config
|
|
var connHost string
|
|
var params map[string][]string
|
|
var netType string = DefaultNetType
|
|
|
|
if cfg, err = t.ToTlsConfig(); err != nil {
|
|
return
|
|
}
|
|
|
|
params = t.Query()
|
|
|
|
if params != nil {
|
|
if _, ok = params[ParamNet]; ok {
|
|
netType = params[ParamNet][0]
|
|
}
|
|
}
|
|
netType = strings.ToLower(netType)
|
|
|
|
switch netType {
|
|
case "unix", "unixgram", "unixpacket":
|
|
connHost = t.Path
|
|
default:
|
|
connHost = t.Host
|
|
}
|
|
|
|
if conn, err = tls.Dial(netType, connHost, cfg); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// ToTlsFlat returns a *TlsFlat from a TlsUri.
|
|
func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
|
|
|
var b []byte
|
|
var params url.Values
|
|
// These also have maps so they can backmap filenames.
|
|
var privKeys []crypto.PrivateKey
|
|
var privKeyMap map[string][]crypto.PrivateKey
|
|
var tlsCerts []tls.Certificate
|
|
var tlsCertMap map[string][]tls.Certificate
|
|
var isMatch bool
|
|
var fCert *TlsFlatCert
|
|
var val string
|
|
var ok bool
|
|
var paramMap map[tlsUriParam][]string = make(map[tlsUriParam][]string)
|
|
var f TlsFlat = TlsFlat{
|
|
SniName: t.Hostname(),
|
|
SkipVerify: false,
|
|
Certs: nil,
|
|
CaFiles: nil,
|
|
CipherSuites: nil,
|
|
MinTlsProtocol: nil,
|
|
MaxTlsProtocol: nil,
|
|
Curves: nil,
|
|
}
|
|
|
|
params = t.Query()
|
|
if params == nil {
|
|
tlsFlat = &f
|
|
return
|
|
}
|
|
|
|
for k, v := range params {
|
|
paramMap[tlsUriParam(k)] = v
|
|
}
|
|
|
|
// CA cert(s).
|
|
if _, ok = paramMap[ParamCa]; ok {
|
|
f.CaFiles = append(f.CaFiles, paramMap[ParamCa]...)
|
|
}
|
|
|
|
// Keys and Certs. These are done first so we can match to a leaf certificate.
|
|
if _, ok = paramMap[ParamKey]; ok {
|
|
privKeyMap = make(map[string][]crypto.PrivateKey)
|
|
for _, kFile := range paramMap[ParamKey] {
|
|
if b, err = os.ReadFile(kFile); err != nil {
|
|
return
|
|
}
|
|
if privKeyMap[kFile], err = ParsePrivateKey(b); err != nil {
|
|
return
|
|
}
|
|
privKeys = append(privKeys, privKeyMap[kFile]...)
|
|
}
|
|
}
|
|
if t_, ok = paramMap[ParamCert]; ok {
|
|
tlsCertMap = make(map[string][]tls.Certificate)
|
|
for _, cFile := range paramMap[ParamCert] {
|
|
if b, err = os.ReadFile(cFile); err != nil {
|
|
return
|
|
}
|
|
if tlsCertMap[cFile], _, err = ParseLeafCert(b, privKeys); err != nil {
|
|
return
|
|
}
|
|
tlsCerts = append(tlsCerts, tlsCertMap[cFile]...)
|
|
}
|
|
}
|
|
// We then correlate. Whew, lads.
|
|
for cFile, c := range tlsCertMap {
|
|
for _, cert := range c {
|
|
for kFile, k := range privKeyMap {
|
|
if isMatch, err = IsMatchedPair(k, cert.Leaf); err != nil {
|
|
return
|
|
} else if isMatch {
|
|
fCert = &TlsFlatCert{
|
|
CertFile: cFile,
|
|
KeyFile: new(string),
|
|
}
|
|
*fCert.KeyFile = kFile
|
|
f.Certs = append(f.Certs, fCert)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hostname.
|
|
if t.Query().Has(ParamSni) {
|
|
f.SniName = t.Query().Get(ParamSni)
|
|
}
|
|
|
|
// Disable verification.
|
|
if t.Query().Has(ParamNoVerify) {
|
|
val = strings.ToLower(t.Query().Get(ParamNoVerify))
|
|
for _, i := range paramBoolValsTrue {
|
|
if val == i {
|
|
f.SkipVerify = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ciphers.
|
|
if t.Query().Has(ParamCipher) {
|
|
f.CipherSuites = params[ParamCipher]
|
|
}
|
|
|
|
// Minimum TLS Protocol Version.
|
|
if t.Query().Has(ParamMinTls) {
|
|
f.MinTlsProtocol = new(string)
|
|
*f.MinTlsProtocol = t.Query().Get(ParamMinTls)
|
|
}
|
|
|
|
// Maximum TLS Protocol Version.
|
|
if t.Query().Has(ParamMaxTls) {
|
|
f.MaxTlsProtocol = new(string)
|
|
*f.MaxTlsProtocol = t.Query().Get(ParamMaxTls)
|
|
}
|
|
|
|
// Curves.
|
|
if t.Query().Has(ParamCurve) {
|
|
f.Curves = params[ParamCurve]
|
|
}
|
|
|
|
tlsFlat = &f
|
|
|
|
return
|
|
}
|
|
|
|
// ToURL returns the *url.URL representation of a TlsUri. Note that the params will remain, so remove them explicitly if needed.
|
|
func (t *TlsUri) ToURL() (u *url.URL) {
|
|
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
u = t.URL
|
|
|
|
return
|
|
}
|