Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IPv4 and private networks #356

Merged
merged 2 commits into from
Nov 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,11 @@ lazy val moduleJvmSettings = Def.settings(
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.NumericValidate.moduloValidateNat"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.scalacheck.util.OurMath"),
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.scalacheck.util.OurMath$")
ProblemFilters.exclude[MissingClassProblem]("eu.timepit.refined.scalacheck.util.OurMath$"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.StringValidate.ipv4Validate"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"eu.timepit.refined.types.NetTypes.PrivateNetworks")
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ object string extends StringValidate with StringInference {
/** Predicate that checks if a `String` ends with the suffix `S`. */
final case class EndsWith[S](s: S)

/** Predicate that checks if a `String` is a valid IPv4 */
final case class IPv4()
object IPv4 {
val regex = "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$".r.pattern
val maxOctet = 255
val predicate: String => Boolean = s => {
val matcher = regex.matcher(s)
matcher.find() && matcher.matches() && {
val octet1 = matcher.group(1).toInt
val octet2 = matcher.group(2).toInt
val octet3 = matcher.group(3).toInt
val octet4 = matcher.group(4).toInt

(octet1 <= maxOctet) && (octet2 <= maxOctet) && (octet3 <= maxOctet) && (octet4 <= maxOctet)
}
}
}

/** Predicate that checks if a `String` matches the regular expression `S`. */
final case class MatchesRegex[S](s: S)

Expand All @@ -41,13 +59,15 @@ object string extends StringValidate with StringInference {
}

private[refined] trait StringValidate {

implicit def endsWithValidate[S <: String](
implicit ws: Witness.Aux[S]): Validate.Plain[String, EndsWith[S]] =
Validate.fromPredicate(_.endsWith(ws.value),
t => s""""$t".endsWith("${ws.value}")""",
EndsWith(ws.value))

implicit def ipv4Validate[S <: String]: Validate.Plain[String, IPv4] =
Validate.fromPredicate(IPv4.predicate, t => s"${t} is a valid IPv4", IPv4())

implicit def matchesRegexValidate[S <: String](
implicit ws: Witness.Aux[S]): Validate.Plain[String, MatchesRegex[S]] =
Validate.fromPredicate(_.matches(ws.value),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package eu.timepit.refined.types

import eu.timepit.refined.W
import eu.timepit.refined.api.Refined
import eu.timepit.refined.boolean.{And, Or}
import eu.timepit.refined.numeric.Interval
import eu.timepit.refined.string.{IPv4, MatchesRegex, StartsWith}

/** Module for refined types that are related to the Internet protocol suite. */
object net extends NetTypes
Expand All @@ -23,4 +25,58 @@ trait NetTypes {

/** An `Int` in the range from 1024 to 65535 representing a port number. */
type NonSystemPortNumber = Int Refined Interval.Closed[W.`1024`.T, W.`65535`.T]

import PrivateNetworks._

/** A `String` representing a valid IPv4 in the private network 10.0.0.0/8 (RFC1918) */
type Rfc1918ClassAPrivate = String Refined Rfc1918ClassAPrivateSpec

/** A `String` representing a valid IPv4 in the private network 172.15.0.0/12 (RFC1918) */
type Rfc1918ClassBPrivate = String Refined Rfc1918ClassBPrivateSpec

/** A `String` representing a valid IPv4 in the private network 192.168.0.0/16 (RFC1918) */
type Rfc1918ClassCPrivate = String Refined Rfc1918ClassCPrivateSpec

/** A `String` representing a valid IPv4 in a private network according to RFC1918 */
type Rfc1918Private = String Refined Rfc1918PrivateSpec

/** A `String` representing a valid IPv4 in the private network 192.0.2.0/24 (RFC5737) */
type Rfc5737Testnet1 = String Refined Rfc5737Testnet1Spec

/** A `String` representing a valid IPv4 in the private network 198.51.100.0/24 (RFC5737) */
type Rfc5737Testnet2 = String Refined Rfc5737Testnet2Spec

/** A `String` representing a valid IPv4 in the private network 203.0.113.0/24 (RFC5737) */
type Rfc5737Testnet3 = String Refined Rfc5737Testnet3Spec

/** A `String` representing a valid IPv4 in a private network according to RFC5737 */
type Rfc5737Testnet = String Refined Rfc5737TestnetSpec

/** A `String` representing a valid IPv4 in the local link network 169.254.0.0/16 (RFC3927) */
type Rfc3927LocalLink = String Refined Rfc3927LocalLinkSpec

/** A `String` representing a valid IPv4 in the benchmarking network 198.18.0.0/15 (RFC2544) */
type Rfc2544Benchmark = String Refined Rfc2544BenchmarkSpec

/** A `String` representing a valid IPv4 in a private network according to RFC1918, RFC5737, RFC3927 or RFC2544 */
type PrivateNetwork = String Refined Rfc1918PrivateSpec Or
Rfc5737TestnetSpec Or
Rfc3927LocalLinkSpec Or
Rfc2544BenchmarkSpec

object PrivateNetworks {
type Rfc1918ClassAPrivateSpec = IPv4 And StartsWith[W.`"10."`.T]
type Rfc1918ClassBPrivateSpec =
IPv4 And MatchesRegex[W.`"^172\\\\.(15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31).+"`.T]
type Rfc1918ClassCPrivateSpec = IPv4 And StartsWith[W.`"192.168."`.T]
type Rfc1918PrivateSpec =
Rfc1918ClassAPrivateSpec Or Rfc1918ClassBPrivateSpec Or Rfc1918ClassCPrivateSpec
type Rfc5737Testnet1Spec = IPv4 And StartsWith[W.`"192.0.2."`.T]
type Rfc5737Testnet2Spec = IPv4 And StartsWith[W.`"198.51.100."`.T]
type Rfc5737Testnet3Spec = IPv4 And StartsWith[W.`"203.0.113."`.T]
type Rfc5737TestnetSpec = Rfc5737Testnet1Spec Or Rfc5737Testnet2Spec Or Rfc5737Testnet3Spec
type Rfc3927LocalLinkSpec = IPv4 And StartsWith[W.`"169.254."`.T]
type Rfc2544BenchmarkSpec =
IPv4 And Or[StartsWith[W.`"198.18."`.T], StartsWith[W.`"198.19."`.T]]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,16 @@ class StringValidateSpec extends Properties("StringValidate") {
property("Uuid.showResult.Failed") = secure {
showResult[Uuid]("whops") ?= "Uuid predicate failed: Invalid UUID string: whops"
}

property("IPv4.isValid") = secure {
isValid[IPv4]("10.0.0.1")
}

property("IPv4.showResult.InvalidOctet") = secure {
showResult[IPv4]("10.0.256.1") ?= "Predicate failed: 10.0.256.1 is a valid IPv4."
}

property("IPv4.showResult.Failed") = secure {
showResult[IPv4]("::1") ?= "Predicate failed: ::1 is a valid IPv4."
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package eu.timepit.refined.types

import eu.timepit.refined.TestUtils._
import org.scalacheck.Prop._
import org.scalacheck.Properties

class NetSpec extends Properties("NetTypes") {
import net.PrivateNetworks._

property("Rfc1918ClassAPrivateSpec.before") = secure {
showResult[Rfc1918ClassAPrivateSpec]("9.255.255.255") ?= """Right predicate of (9.255.255.255 is a valid IPv4 && "9.255.255.255".startsWith("10.")) failed: Predicate failed: "9.255.255.255".startsWith("10.")."""
}
property("Rfc1918ClassAPrivateSpec.isValid.first") = secure {
isValid[Rfc1918ClassAPrivateSpec]("10.0.0.0")
}
property("Rfc1918ClassAPrivateSpec.isValid.inside") = secure {
isValid[Rfc1918ClassAPrivateSpec]("10.200.18.255")
}
property("Rfc1918ClassAPrivateSpec.isValid.last") = secure {
isValid[Rfc1918ClassAPrivateSpec]("10.255.255.255")
}
property("Rfc1918ClassAPrivateSpec.after") = secure {
showResult[Rfc1918ClassAPrivateSpec]("11.0.0.0") ?= """Right predicate of (11.0.0.0 is a valid IPv4 && "11.0.0.0".startsWith("10.")) failed: Predicate failed: "11.0.0.0".startsWith("10.")."""
}

property("Rfc1918ClassBPrivateSpec.before") = secure {
showResult[Rfc1918ClassBPrivateSpec]("172.14.255.255") ?= """Right predicate of (172.14.255.255 is a valid IPv4 && "172.14.255.255".matches("^172\.(15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31).+")) failed: Predicate failed: "172.14.255.255".matches("^172\.(15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31).+")."""
}
property("Rfc1918ClassBPrivateSpec.isValid.first") = secure {
isValid[Rfc1918ClassBPrivateSpec]("172.15.0.0")
}
property("Rfc1918ClassBPrivateSpec.isValid.inside") = secure {
isValid[Rfc1918ClassBPrivateSpec]("172.23.17.172")
}
property("Rfc1918ClassBPrivateSpec.isValid.last") = secure {
isValid[Rfc1918ClassBPrivateSpec]("172.31.255.255")
}
property("Rfc1918ClassBPrivateSpec.after") = secure {
showResult[Rfc1918ClassBPrivateSpec]("172.32.0.0") ?= """Right predicate of (172.32.0.0 is a valid IPv4 && "172.32.0.0".matches("^172\.(15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31).+")) failed: Predicate failed: "172.32.0.0".matches("^172\.(15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31).+")."""
}

property("Rfc1918ClassCPrivateSpec.before") = secure {
showResult[Rfc1918ClassCPrivateSpec]("192.167.255.255") ?= """Right predicate of (192.167.255.255 is a valid IPv4 && "192.167.255.255".startsWith("192.168.")) failed: Predicate failed: "192.167.255.255".startsWith("192.168.")."""
}
property("Rfc1918ClassCPrivateSpec.isValid.first") = secure {
isValid[Rfc1918ClassCPrivateSpec]("192.168.0.0")
}
property("Rfc1918ClassCPrivateSpec.isValid.inside") = secure {
isValid[Rfc1918ClassCPrivateSpec]("192.168.100.100")
}
property("Rfc1918ClassCPrivateSpec.isValid.last") = secure {
isValid[Rfc1918ClassCPrivateSpec]("192.168.255.255")
}
property("Rfc1918ClassCPrivateSpec.after") = secure {
showResult[Rfc1918ClassCPrivateSpec]("192.169.0.0") ?= """Right predicate of (192.169.0.0 is a valid IPv4 && "192.169.0.0".startsWith("192.168.")) failed: Predicate failed: "192.169.0.0".startsWith("192.168.")."""
}

property("Rfc5737Testnet1Spec.before") = secure {
showResult[Rfc5737Testnet1Spec]("192.0.1.255") ?= """Right predicate of (192.0.1.255 is a valid IPv4 && "192.0.1.255".startsWith("192.0.2.")) failed: Predicate failed: "192.0.1.255".startsWith("192.0.2.")."""
}
property("Rfc5737Testnet1Spec.isValid.first") = secure {
isValid[Rfc5737Testnet1Spec]("192.0.2.0")
}
property("Rfc5737Testnet1Spec.isValid.inside") = secure {
isValid[Rfc5737Testnet1Spec]("192.0.2.150")
}
property("Rfc5737Testnet1Spec.isValid.last") = secure {
isValid[Rfc5737Testnet1Spec]("192.0.2.255")
}
property("Rfc5737Testnet1Spec.after") = secure {
showResult[Rfc5737Testnet1Spec]("192.0.3.0") ?= """Right predicate of (192.0.3.0 is a valid IPv4 && "192.0.3.0".startsWith("192.0.2.")) failed: Predicate failed: "192.0.3.0".startsWith("192.0.2.")."""
}

property("Rfc5737Testnet2Spec.before") = secure {
showResult[Rfc5737Testnet2Spec]("198.51.99.255") ?= """Right predicate of (198.51.99.255 is a valid IPv4 && "198.51.99.255".startsWith("198.51.100.")) failed: Predicate failed: "198.51.99.255".startsWith("198.51.100.")."""
}
property("Rfc5737Testnet2Spec.isValid.first") = secure {
isValid[Rfc5737Testnet2Spec]("198.51.100.0")
}
property("Rfc5737Testnet2Spec.isValid.inside") = secure {
isValid[Rfc5737Testnet2Spec]("198.51.100.200")
}
property("Rfc5737Testnet2Spec.isValid.last") = secure {
isValid[Rfc5737Testnet2Spec]("198.51.100.255")
}
property("Rfc5737Testnet2Spec.after") = secure {
showResult[Rfc5737Testnet2Spec]("198.51.101.0") ?= """Right predicate of (198.51.101.0 is a valid IPv4 && "198.51.101.0".startsWith("198.51.100.")) failed: Predicate failed: "198.51.101.0".startsWith("198.51.100.")."""
}

property("Rfc5737Testnet3Spec.before") = secure {
showResult[Rfc5737Testnet3Spec]("203.0.112.255") ?= """Right predicate of (203.0.112.255 is a valid IPv4 && "203.0.112.255".startsWith("203.0.113.")) failed: Predicate failed: "203.0.112.255".startsWith("203.0.113.")."""
}
property("Rfc5737Testnet3Spec.isValid.first") = secure {
isValid[Rfc5737Testnet3Spec]("203.0.113.0")
}
property("Rfc5737Testnet3Spec.isValid.inside") = secure {
isValid[Rfc5737Testnet3Spec]("203.0.113.123")
}
property("Rfc5737Testnet3Spec.isValid.last") = secure {
isValid[Rfc5737Testnet3Spec]("203.0.113.255")
}
property("Rfc5737Testnet3Spec.after") = secure {
showResult[Rfc5737Testnet3Spec]("203.0.114.0") ?= """Right predicate of (203.0.114.0 is a valid IPv4 && "203.0.114.0".startsWith("203.0.113.")) failed: Predicate failed: "203.0.114.0".startsWith("203.0.113.")."""
}

property("Rfc3927LocalLinkSpec.before") = secure {
showResult[Rfc3927LocalLinkSpec]("169.253.255.255") ?= """Right predicate of (169.253.255.255 is a valid IPv4 && "169.253.255.255".startsWith("169.254.")) failed: Predicate failed: "169.253.255.255".startsWith("169.254.")."""
}
property("Rfc3927LocalLinkSpec.isValid.first") = secure {
isValid[Rfc3927LocalLinkSpec]("169.254.0.0")
}
property("Rfc3927LocalLinkSpec.isValid.inside") = secure {
isValid[Rfc3927LocalLinkSpec]("169.254.213.65")
}
property("Rfc3927LocalLinkSpec.isValid.last") = secure {
isValid[Rfc3927LocalLinkSpec]("169.254.255.255")
}
property("Rfc3927LocalLinkSpec.after") = secure {
showResult[Rfc3927LocalLinkSpec]("169.255.0.0") ?= """Right predicate of (169.255.0.0 is a valid IPv4 && "169.255.0.0".startsWith("169.254.")) failed: Predicate failed: "169.255.0.0".startsWith("169.254.")."""
}

property("Rfc2544BenchmarkSpec.before") = secure {
showResult[Rfc2544BenchmarkSpec]("198.17.255.255") ?= """Right predicate of (198.17.255.255 is a valid IPv4 && ("198.17.255.255".startsWith("198.18.") || "198.17.255.255".startsWith("198.19."))) failed: Both predicates of ("198.17.255.255".startsWith("198.18.") || "198.17.255.255".startsWith("198.19.")) failed. Left: Predicate failed: "198.17.255.255".startsWith("198.18."). Right: Predicate failed: "198.17.255.255".startsWith("198.19.")."""
}
property("Rfc2544BenchmarkSpec.isValid.first") = secure {
isValid[Rfc2544BenchmarkSpec]("198.18.0.0")
}
property("Rfc2544BenchmarkSpec.isValid.inside") = secure {
isValid[Rfc2544BenchmarkSpec]("198.19.12.2")
}
property("Rfc2544BenchmarkSpec.isValid.last") = secure {
isValid[Rfc2544BenchmarkSpec]("198.19.255.255")
}
property("Rfc2544BenchmarkSpec.after") = secure {
showResult[Rfc2544BenchmarkSpec]("198.20.0.0") ?= """Right predicate of (198.20.0.0 is a valid IPv4 && ("198.20.0.0".startsWith("198.18.") || "198.20.0.0".startsWith("198.19."))) failed: Both predicates of ("198.20.0.0".startsWith("198.18.") || "198.20.0.0".startsWith("198.19.")) failed. Left: Predicate failed: "198.20.0.0".startsWith("198.18."). Right: Predicate failed: "198.20.0.0".startsWith("198.19.")."""
}
}