fix(dockerCompose): add further methods of port

This patch adds the following methods:

ports.ExistsDstIP()
ports.ExistsDstPort()
ports.ExistsProtocol()
ports.ExistsSrcIP()
ports.ExistsSrcPort()
ports.GetDstIP()
ports.GetDstPort()
ports.GetProtocol()
ports.GetSrcIP()
ports.GetSrcPort()
This commit is contained in:
Markus Pesch 2025-02-20 16:48:55 +01:00
parent 0a68b2d8e7
commit 13301cb367
Signed by: volker.raschek
GPG Key ID: 852BCC170D81A982
2 changed files with 519 additions and 49 deletions

View File

@ -560,7 +560,7 @@ func (s *Service) ExistsLabel(name string) bool {
// ExistsPort returns true if the port definition is already present. // ExistsPort returns true if the port definition is already present.
func (s *Service) ExistsPort(src string, dest string, protocol string) bool { func (s *Service) ExistsPort(src string, dest string, protocol string) bool {
for _, port := range s.Ports { for _, port := range s.Ports {
s, d, p := splitStringInPort(port) s, d, p := splitStringInPortMapping(port)
if s == src && d == dest && p == protocol { if s == src && d == dest && p == protocol {
return true return true
} }
@ -572,7 +572,7 @@ func (s *Service) ExistsPort(src string, dest string, protocol string) bool {
// ExistsDestinationPort returns true if the destination port is already used. // ExistsDestinationPort returns true if the destination port is already used.
func (s *Service) ExistsDestinationPort(dest string) bool { func (s *Service) ExistsDestinationPort(dest string) bool {
for _, port := range s.Ports { for _, port := range s.Ports {
_, d, _ := splitStringInPort(port) _, d, _ := splitStringInPortMapping(port)
if d == dest { if d == dest {
return true return true
} }
@ -584,7 +584,7 @@ func (s *Service) ExistsDestinationPort(dest string) bool {
// ExistsSourcePort returns true if the source port is already used. // ExistsSourcePort returns true if the source port is already used.
func (s *Service) ExistsSourcePort(src string) bool { func (s *Service) ExistsSourcePort(src string) bool {
for _, port := range s.Ports { for _, port := range s.Ports {
s, _, _ := splitStringInPort(port) s, _, _ := splitStringInPortMapping(port)
if s == src { if s == src {
return true return true
} }
@ -861,7 +861,7 @@ func (s *Service) mergeExistingWinPorts(ports []string) {
continue continue
} }
src, dest, protocol := splitStringInPort(port) src, dest, protocol := splitStringInPortMapping(port)
if !s.ExistsDestinationPort(dest) { if !s.ExistsDestinationPort(dest) {
s.SetPort(src, dest, protocol) s.SetPort(src, dest, protocol)
} }
@ -1057,7 +1057,7 @@ func (s *Service) mergeLastWinPorts(ports []string) {
continue continue
} }
src, dest, protocol := splitStringInPort(port) src, dest, protocol := splitStringInPortMapping(port)
s.SetPort(src, dest, protocol) s.SetPort(src, dest, protocol)
} }
} }
@ -1139,7 +1139,7 @@ func (s *Service) RemoveLabel(name string) {
func (s *Service) RemovePort(dest string) { func (s *Service) RemovePort(dest string) {
ports := make([]string, 0) ports := make([]string, 0)
for _, port := range s.Ports { for _, port := range s.Ports {
srcPort, destPort, protocol := splitStringInPort(port) srcPort, destPort, protocol := splitStringInPortMapping(port)
switch { switch {
case destPort == dest && len(protocol) <= 0: case destPort == dest && len(protocol) <= 0:
@ -1951,17 +1951,33 @@ func splitStringInKeyValue(s, sep string) (string, string) {
return key, value return key, value
} }
func splitStringInPort(s string) (string, string, string) { // splitStringInPortMapping parses a string and returns the src, dest port including an optional protocol.
parts := strings.Split(s, portDelimiter) //
src := parts[0] // // Example
rest := parts[1] // s, d, p := splitStringInPortMapping("80:80/tcp")
// // Output: "80" "80" "tcp"
// s, d, p := splitStringInPortMapping("0.0.0.0:80:80/tcp")
// // Output: "0.0.0.0:80" "80" "tcp"
func splitStringInPortMapping(s string) (string, string, string) {
p := port(s)
parts = strings.Split(rest, portProtocolDelimiter) var src string
if len(parts) == 2 { switch {
return src, parts[0], parts[1] case p.existsSrcIP() && p.existsSrcPort():
src = fmt.Sprintf("%s:%s", p.getSrcIP(), p.getSrcPort())
case !p.existsSrcIP():
src = p.getSrcPort()
} }
return src, parts[0], "" var dst string
switch {
case p.existsDstIP() && p.existsDstPort():
dst = fmt.Sprintf("%s:%s", p.getDstIP(), p.getDstPort())
case !p.existsDstIP():
dst = p.getDstPort()
}
return src, dst, p.getProtocol()
} }
func splitStringInVolume(s string) (string, string, string) { func splitStringInVolume(s string) (string, string, string) {
@ -1975,22 +1991,108 @@ func splitStringInVolume(s string) (string, string, string) {
return src, dest, "" return src, dest, ""
} }
var protocolRegExp = regexp.MustCompile(`/(?<protocol>[a-z]*)$`) var (
regExpPort = regexp.MustCompile(`^((?<srcIP>([\d]{1,3}\.){3}[\d]{1,3}):)?(?<srcPort>[\d]{1,5}):((?<dstIP>([\d]{1,3}\.){3}[\d]{1,3}):)?(?<dstPort>[\d]{1,5})(\/(?<protocol>[a-z]*))?$`)
)
type port string type port string
// existsDstPort returns true, if the port string contains a trailing destination port definition.
func (p port) existsDstPort() bool {
return len(p.getDstPort()) > 0
}
// existsDstIP returns true, if the port string contains a trailing destination ip definition.
func (p port) existsDstIP() bool {
return len(p.getDstIP()) > 0
}
// existsProtocol returns true, if the port string contains a protocol definition.
func (p port) existsProtocol() bool { func (p port) existsProtocol() bool {
return protocolRegExp.MatchString(string(p)) return len(p.getProtocol()) > 0
} }
// existsSrcIP returns true, if the port string contains a leading src ip definition.
func (p port) existsSrcIP() bool {
return len(p.getSrcIP()) > 0
}
// existsSrcPort returns true, if the port string contains a leading src port definition.
func (p port) existsSrcPort() bool {
return len(p.getSrcPort()) > 0
}
// getSrcIP returns the destination ip, if the port string contains a destination ip definition.
func (p port) getDstIP() string {
matches := regExpPort.FindStringSubmatch(string(p))
i := regExpPort.SubexpIndex("dstIP")
switch {
case len(matches) <= 0:
return ""
case i < 0:
return ""
}
return matches[i]
}
// getSrcPort returns the destination port, if the port string contains an destination port definition.
func (p port) getDstPort() string {
matches := regExpPort.FindStringSubmatch(string(p))
i := regExpPort.SubexpIndex("dstPort")
switch {
case len(matches) <= 0:
return ""
case i < 0:
return ""
}
return matches[i]
}
// getProtocol returns the protocol, if the port string contains a protocol definition.
func (p port) getProtocol() string { func (p port) getProtocol() string {
result := make(map[string]string, 0) matches := regExpPort.FindStringSubmatch(string(p))
matches := protocolRegExp.FindStringSubmatch(string(p)) i := regExpPort.SubexpIndex("protocol")
for i, name := range protocolRegExp.SubexpNames() {
if i != 0 && len(name) > 0 { switch {
result[name] = matches[i] case len(matches) <= 0:
} return ""
case i < 0:
return ""
} }
return result["protocol"] return matches[i]
}
// getSrcIP returns the source ip, if the port string contains an src ip definition.
func (p port) getSrcIP() string {
matches := regExpPort.FindStringSubmatch(string(p))
i := regExpPort.SubexpIndex("srcIP")
switch {
case len(matches) <= 0:
return ""
case i < 0:
return ""
}
return matches[i]
}
// getSrcPort returns the source port, if the port string contains an src port definition.
func (p port) getSrcPort() string {
matches := regExpPort.FindStringSubmatch(string(p))
i := regExpPort.SubexpIndex("srcPort")
switch {
case len(matches) <= 0:
return ""
case i < 0:
return ""
}
return matches[i]
} }

View File

@ -6,45 +6,413 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestPort_ExistsProtocol(t *testing.T) { func Test_splitStringInPortMapping(t *testing.T) {
require := require.New(t) require := require.New(t)
testCases := []struct { testCases := []struct {
s string s string
expectedResult bool expectedSrc string
expectedDst string
expectedProtocol string
}{ }{
{
s: "0",
expectedResult: false,
},
{
s: "53",
expectedResult: false,
},
{
s: "53/tcp",
expectedResult: true,
},
{
s: "53/udp",
expectedResult: true,
},
{ {
s: "53:53", s: "53:53",
expectedResult: false, expectedSrc: "53",
expectedDst: "53",
expectedProtocol: "",
}, },
{ {
s: "53:53/udp", s: "0.0.0.0:53:53",
expectedResult: true, expectedSrc: "0.0.0.0:53",
expectedDst: "53",
expectedProtocol: "",
}, },
{ {
s: "53:53/tcp", s: "0.0.0.0:53:10.11.12.13:53",
expectedResult: true, expectedSrc: "0.0.0.0:53",
expectedDst: "10.11.12.13:53",
expectedProtocol: "",
},
{
s: "0.0.0.0:53:10.11.12.13:53/tcp",
expectedSrc: "0.0.0.0:53",
expectedDst: "10.11.12.13:53",
expectedProtocol: "tcp",
}, },
} }
for _, testCase := range testCases { for i, testCase := range testCases {
p := port(testCase.s) actualSrc, actualDst, actualProtocol := splitStringInPortMapping(testCase.s)
require.Equal(testCase.expectedResult, p.existsProtocol()) require.Equal(testCase.expectedSrc, actualSrc, "TestCase %v", i)
require.Equal(testCase.expectedDst, actualDst, "TestCase %v", i)
require.Equal(testCase.expectedProtocol, actualProtocol, "TestCase %v", i)
}
}
func TestPort_DstIP(t *testing.T) {
require := require.New(t)
testCases := []struct {
s string
expectedBool bool
expectedString string
}{
{
s: "",
expectedBool: false,
expectedString: "",
},
{
s: "53:53",
expectedBool: false,
expectedString: "",
},
{
s: "53:53/tcp",
expectedBool: false,
expectedString: "",
},
{
s: "53:53/udp",
expectedBool: false,
expectedString: "",
},
{
s: "0.0.0.0:53",
expectedBool: false,
expectedString: "",
},
{
s: "53:0.0.0.0:53",
expectedBool: true,
expectedString: "0.0.0.0",
},
{
s: "53:0.0.0.0:53/tcp",
expectedBool: true,
expectedString: "0.0.0.0",
},
{
s: "53:0.0.0.0:53/udp",
expectedBool: true,
expectedString: "0.0.0.0",
},
{
s: "10.11.12.13:53",
expectedBool: false,
expectedString: "",
},
{
s: "53:10.11.12.13:53",
expectedBool: true,
expectedString: "10.11.12.13",
},
{
s: "53:10.11.12.13:53/tcp",
expectedBool: true,
expectedString: "10.11.12.13",
},
{
s: "53:10.11.12.13:53/udp",
expectedBool: true,
expectedString: "10.11.12.13",
},
}
for i, testCase := range testCases {
p := port(testCase.s)
require.Equal(testCase.expectedBool, p.existsDstIP(), "TestCase %v", i)
require.Equal(testCase.expectedString, p.getDstIP(), "TestCase %v", i)
}
}
func TestPort_DstPort(t *testing.T) {
require := require.New(t)
testCases := []struct {
s string
expectedBool bool
expectedString string
}{
{
s: "",
expectedBool: false,
expectedString: "",
},
{
s: "53:53",
expectedBool: true,
expectedString: "53",
},
{
s: "53:53/tcp",
expectedBool: true,
expectedString: "53",
},
{
s: "53:53/udp",
expectedBool: true,
expectedString: "53",
},
{
s: "53:0.0.0.0:53",
expectedBool: true,
expectedString: "53",
},
{
s: "53:0.0.0.0:53/tcp",
expectedBool: true,
expectedString: "53",
},
{
s: "53:0.0.0.0:53/udp",
expectedBool: true,
expectedString: "53",
},
{
s: "53:10.11.12.13:53",
expectedBool: true,
expectedString: "53",
},
{
s: "53:10.11.12.13:53/tcp",
expectedBool: true,
expectedString: "53",
},
{
s: "53:10.11.12.13:53/udp",
expectedBool: true,
expectedString: "53",
},
}
for i, testCase := range testCases {
p := port(testCase.s)
require.Equal(testCase.expectedBool, p.existsDstPort(), "TestCase %v", i)
require.Equal(testCase.expectedString, p.getDstPort(), "TestCase %v", i)
}
}
func TestPort_Protocol(t *testing.T) {
require := require.New(t)
testCases := []struct {
s string
expectedBool bool
expectedString string
}{
{
s: "0",
expectedBool: false,
expectedString: "",
},
{
s: "53/tcp",
expectedBool: false,
expectedString: "",
},
{
s: "53/udp",
expectedBool: false,
expectedString: "",
},
{
s: "53:53",
expectedBool: false,
expectedString: "",
},
{
s: "53:53/tcp",
expectedBool: true,
expectedString: "tcp",
},
{
s: "53:53/udp",
expectedBool: true,
expectedString: "udp",
},
{
s: "0.0.0.0:53:53/tcp",
expectedBool: true,
expectedString: "tcp",
},
{
s: "0.0.0.0:53:53/udp",
expectedBool: true,
expectedString: "udp",
},
{
s: "0.0.0.0:53:53/tcp",
expectedBool: true,
expectedString: "tcp",
},
{
s: "0.0.0.0:53:11.12.13.14:53/tcp",
expectedBool: true,
expectedString: "tcp",
},
{
s: "0.0.0.0:53:11.12.13.14:53/udp",
expectedBool: true,
expectedString: "udp",
},
}
for i, testCase := range testCases {
p := port(testCase.s)
require.Equal(testCase.expectedBool, p.existsProtocol(), "TestCase %v", i)
require.Equal(testCase.expectedString, p.getProtocol(), "TestCase %v", i)
}
}
func TestPort_SrcIP(t *testing.T) {
require := require.New(t)
testCases := []struct {
s string
expectedBool bool
expectedString string
}{
{
s: "",
expectedBool: false,
expectedString: "",
},
{
s: "53:53",
expectedBool: false,
expectedString: "",
},
{
s: "53:53/tcp",
expectedBool: false,
expectedString: "",
},
{
s: "53:53/udp",
expectedBool: false,
expectedString: "",
},
{
s: "0.0.0.0:53",
expectedBool: false,
expectedString: "",
},
{
s: "0.0.0.0:53:53",
expectedBool: true,
expectedString: "0.0.0.0",
},
{
s: "0.0.0.0:53:53/tcp",
expectedBool: true,
expectedString: "0.0.0.0",
},
{
s: "0.0.0.0:53:53/udp",
expectedBool: true,
expectedString: "0.0.0.0",
},
{
s: "10.11.12.13:53",
expectedBool: false,
expectedString: "",
},
{
s: "10.11.12.13:53:53",
expectedBool: true,
expectedString: "10.11.12.13",
},
{
s: "10.11.12.13:53:53/tcp",
expectedBool: true,
expectedString: "10.11.12.13",
},
{
s: "10.11.12.13:53:53/udp",
expectedBool: true,
expectedString: "10.11.12.13",
},
}
for i, testCase := range testCases {
p := port(testCase.s)
require.Equal(testCase.expectedBool, p.existsSrcIP(), "TestCase %v", i)
require.Equal(testCase.expectedString, p.getSrcIP(), "TestCase %v", i)
}
}
func TestPort_SrcPort(t *testing.T) {
require := require.New(t)
testCases := []struct {
s string
expectedBool bool
expectedString string
}{
{
s: "",
expectedBool: false,
expectedString: "",
},
{
s: "53:53",
expectedBool: true,
expectedString: "53",
},
{
s: "53:53/tcp",
expectedBool: true,
expectedString: "53",
},
{
s: "53:53/udp",
expectedBool: true,
expectedString: "53",
},
{
s: "0.0.0.0:53:53",
expectedBool: true,
expectedString: "53",
},
{
s: "0.0.0.0:53:53/tcp",
expectedBool: true,
expectedString: "53",
},
{
s: "0.0.0.0:53:53/udp",
expectedBool: true,
expectedString: "53",
},
{
s: "10.11.12.13:53:53",
expectedBool: true,
expectedString: "53",
},
{
s: "10.11.12.13:53:53/tcp",
expectedBool: true,
expectedString: "53",
},
{
s: "10.11.12.13:53:53/udp",
expectedBool: true,
expectedString: "53",
},
}
for i, testCase := range testCases {
p := port(testCase.s)
require.Equal(testCase.expectedBool, p.existsSrcPort(), "TestCase %v", i)
require.Equal(testCase.expectedString, p.getSrcPort(), "TestCase %v", i)
} }
} }