Skip to content

Commit

Permalink
Add isLoopback and isLinkLocal to IpAddress
Browse files Browse the repository at this point in the history
  • Loading branch information
mpilquist committed Feb 5, 2024
1 parent 4df99ca commit 01040d6
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 2 deletions.
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ ThisBuild / mimaBinaryIssueFilters ++= Seq(
ProblemFilters.exclude[MissingTypesProblem]("com.comcast.ip4s.Multicast$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("com.comcast.ip4s.SourceSpecificMulticast.ordinal"),
ProblemFilters.exclude[MissingTypesProblem]("com.comcast.ip4s.SourceSpecificMulticast$"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.IpAddress.isPrivate") // #562
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.IpAddress.isPrivate"), // #562
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.IpAddress.isLoopback"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.comcast.ip4s.IpAddress.isLinkLocal")
)

lazy val root = tlCrossRootProject.aggregate(core, testKit)
Expand Down
33 changes: 32 additions & 1 deletion shared/src/main/scala/com/comcast/ip4s/Host.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ sealed abstract class IpAddress extends IpAddressPlatform with Host with Seriali
def asSourceSpecificMulticastLenient: Option[SourceSpecificMulticast[this.type]] =
SourceSpecificMulticast.fromIpAddressLenient(this)

/** Returns true if this address is a loopback address. */
def isLoopback: Boolean

/** Returns true if this address is a link local address. */
def isLinkLocal: Boolean

/** Returns true if this address is in a private range. */
def isPrivate: Boolean

Expand Down Expand Up @@ -359,6 +365,12 @@ final class Ipv4Address private (protected val bytes: Array[Byte]) extends IpAdd
override def isSourceSpecificMulticast: Boolean =
this >= Ipv4Address.SourceSpecificMulticastRangeStart && this <= Ipv4Address.SourceSpecificMulticastRangeEnd

override def isLoopback: Boolean =
Ipv4Address.Classes.Loopback.contains(this)

override def isLinkLocal: Boolean =
Ipv4Address.Classes.LinkLocal.contains(this)

override def isPrivate: Boolean =
Ipv4Address.Classes.Private.A.contains(this) ||
Ipv4Address.Classes.Private.B.contains(this) ||
Expand Down Expand Up @@ -456,6 +468,12 @@ object Ipv4Address extends Ipv4AddressCompanionPlatform {
/** Class A: 192.168.0.0 - 192.168.255.255 */
val C: Cidr[Ipv4Address] = Cidr(fromBytes(192, 168, 0, 0), 16)
}

/** Loopback: 127.0.0.0 - 127.255.255.255. */
val Loopback: Cidr[Ipv4Address] = Cidr(fromBytes(127, 0, 0, 0), 8)

/** Link local: 169.254.0.0 - 169.254.255.255. */
val LinkLocal: Cidr[Ipv4Address] = Cidr(fromBytes(169, 254, 0, 0), 16)
}

/** Parses an IPv4 address from a dotted-decimal string, returning `None` if the string is not a valid IPv4 address.
Expand Down Expand Up @@ -649,6 +667,12 @@ final class Ipv6Address private (protected val bytes: Array[Byte]) extends IpAdd
override def isSourceSpecificMulticast: Boolean =
this >= Ipv6Address.SourceSpecificMulticastRangeStart && this <= Ipv6Address.SourceSpecificMulticastRangeEnd

override def isLoopback: Boolean =
this == Ipv6Address.Loopback || (isMappedV4 && collapseMappedV4.isLoopback)

override def isLinkLocal: Boolean =
Ipv6Address.LinkLocalBlock.contains(this) || (isMappedV4 && collapseMappedV4.isLinkLocal)

override def isPrivate: Boolean =
Ipv6Address.UniqueLocalBlock.contains(this) || (isMappedV4 && collapseMappedV4.isPrivate)

Expand Down Expand Up @@ -696,10 +720,17 @@ object Ipv6Address extends Ipv6AddressCompanionPlatform {
val MappedV4Block: Cidr[Ipv6Address] =
Cidr(Ipv6Address.fromBytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0), 96)

/** CIDR which defines unique local address block. */
/** Alias for ::1. */
val Loopback: Ipv6Address = Ipv6Address.fromBytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)

/** CIDR which defines the unique local address block. */
val UniqueLocalBlock: Cidr[Ipv6Address] =
Cidr(Ipv6Address.fromBytes(0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 7)

/** CIDR which defines the linked scope unicast address block. */
val LinkLocalBlock: Cidr[Ipv6Address] =
Cidr(Ipv6Address.fromBytes(0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 10)

/** Parses an IPv6 address from a string in RFC4291 notation, returning `None` if the string is not a valid IPv6
* address.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,17 @@ class Ipv4AddressTest extends BaseTestSuite {
assert(ipv4"192.168.255.255".isPrivate)
assert(!ipv4"192.168.255.255".next.isPrivate)
}

test("isLoopback") {
assert(ipv4"127.0.0.1".isLoopback)
assert(ipv4"127.255.255.255".isLoopback)
assert(!ipv4"128.0.0.0".isLoopback)
}

test("isLinkLocal") {
assert(!ipv4"127.0.0.1".isLinkLocal)
assert(ipv4"169.254.0.0".isLinkLocal)
assert(ipv4"169.254.255.255".isLinkLocal)
assert(!ipv4"169.254.255.255".next.isLinkLocal)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,14 @@ class Ipv6AddressTest extends BaseTestSuite {
// mapped v4
assert(ipv6"::ffff:10.1.1.1".isPrivate)
}

test("isLoopback") {
assert(ipv6"::1".isLoopback)
assert(ipv6"::ffff:127.0.0.1".isLoopback)
}

test("isLinkLocal") {
assert(ipv6"fe80::1".isLinkLocal)
assert(ipv6"::ffff:169.254.0.0".isLinkLocal)
}
}

0 comments on commit 01040d6

Please sign in to comment.