<commit_before>// Copyright (C) 2017 <NAME> <<EMAIL>>.
//
// See the LICENCE file distributed with this work for additional
// information regarding copyright ownership.

package org.symnet
package models.iptables
package filter

import core._
import types.net.{Ipv4, Port, PortRange}

import Parsing._
import Combinators._
import Parsing.ParserMP.monadPlusSyntax._

// TODO(calincru): Make sure that the IPs for snat and dnat are host IPs, not
// network IPs (no mask).

object SnatTargetExtension extends TargetExtension {
  val targetParser = Impl.targetParser

  object Impl {
    case class SnatTarget(
        lowerIp:   Ipv4,
        upperIp:   Option[Ipv4],
        portRange: Option[PortRange]) extends Target("SNAT") {

      override def isValid(
          rule: Rule,
          chain: Chain,
          table: Table): Boolean =
        table.name == "nat" && chain.name == "POSTROUTING"
    }

    def targetParser: Parser[Target] =
      for {
        _ <- jumpOptionParser

        // Parse the actual target.
        targetName <- someSpacesParser >> stringParser if targetName == "SNAT"

        // Parse the mandatory '--to-source' target option.
        _ <- someSpacesParser >> parseString("--to-source")
        lowerIp <- someSpacesParser >> ipParser

        // Parse the optional upper bound ip.
        upperIp <- optional(parseChar('-') >> ipParser)

        // Parse the optional port range.
        maybePortRange <- optional(parseChar(':') >> portRangeParser)
      } yield SnatTarget(lowerIp, upperIp, maybePortRange)
  }
}

object DnatTargetExtension extends TargetExtension {
  val targetParser = Impl.targetParser

  object Impl {
    case class DnatTarget(
        lowerIp:   Ipv4,
        upperIp:   Option[Ipv4],
        portRange: Option[PortRange]) extends Target("DNAT") {

      override def isValid(
          rule: Rule,
          chain: Chain,
          table: Table): Boolean =
        table.name == "nat" && (List("PREROUTING", "OUTPUT") contains chain.name)
    }

    def targetParser: Parser[Target] =
      for {
        _ <- jumpOptionParser

        // Parse the actual target.
        targetName <- someSpacesParser >> stringParser if targetName == "DNAT"

        // Parse the mandatory '--to-destination' target option.
        _ <- someSpacesParser >> parseString("--to-destination")
        lowerIp <- someSpacesParser >> ipParser

        // Parse the optional upper bound ip.
        upperIp <- optional(parseChar('-') >> ipParser)

        // Parse the optional port range.
        maybePortRange <- optional(parseChar(':') >> portRangeParser)
      } yield DnatTarget(lowerIp, upperIp, maybePortRange)
  }
}

object MasqueradeTargetExtension extends TargetExtension {
  val targetParser = Impl.targetParser

  object Impl {
    case class MasqueradeTarget(
        lowerPort: Option[Port],
        upperPort: Option[Port]) extends Target("MASQUERADE") {

      override def isValid(
          rule: Rule,
          chain: Chain,
          table: Table): Boolean =
        table.name == "nat" && chain.name == "POSTROUTING"
    }

    def targetParser: Parser[Target] =
      for {
        _ <- jumpOptionParser

        // Parse the actual  target
        targetName <- someSpacesParser >> stringParser
          if targetName == "MASQUERADE"

        // Parse the optional '--to-ports' target option
        // ([--to-ports port[-port]]).
        lowerPort <- optional(someSpacesParser >> parseString("--to-ports") >>
                              someSpacesParser >> portParser)

        // Try to parse the upper bound port only if the previous one succeeded.
        upperPort <-
          if (lowerPort.isDefined)<commit_msg>Masquerading is only valid if specifying tcp or udp as the matched port<commit_after>// Copyright (C) 2017 <NAME> <<EMAIL>>.
//
// See the LICENCE file distributed with this work for additional
// information regarding copyright ownership.

package org.symnet
package models.iptables
package filter

import core._
import types.net.{Ipv4, Port, PortRange}

import Parsing._
import Combinators._
import Parsing.ParserMP.monadPlusSyntax._

// TODO(calincru): Make sure that the IPs for snat and dnat are host IPs, not
// network IPs (no mask).

object SnatTargetExtension extends TargetExtension {
  val targetParser = Impl.targetParser

  object Impl {
    case class SnatTarget(
        lowerIp:   Ipv4,
        upperIp:   Option[Ipv4],
        portRange: Option[PortRange]) extends Target("SNAT") {

      override def isValid(
          rule: Rule,
          chain: Chain,
          table: Table): Boolean =
        table.name == "nat" && chain.name == "POSTROUTING"
    }

    def targetParser: Parser[Target] =
      for {
        _ <- jumpOptionParser

        // Parse the actual target.
        targetName <- someSpacesParser >> stringParser if targetName == "SNAT"

        // Parse the mandatory '--to-source' target option.
        _ <- someSpacesParser >> parseString("--to-source")
        lowerIp <- someSpacesParser >> ipParser

        // Parse the optional upper bound ip.
        upperIp <- optional(parseChar('-') >> ipParser)

        // Parse the optional port range.
        maybePortRange <- optional(parseChar(':') >> portRangeParser)
      } yield SnatTarget(lowerIp, upperIp, maybePortRange)
  }
}

object DnatTargetExtension extends TargetExtension {
  val targetParser = Impl.targetParser

  object Impl {
    case class DnatTarget(
        lowerIp:   Ipv4,
        upperIp:   Option[Ipv4],
        portRange: Option[PortRange]) extends Target("DNAT") {

      override def isValid(
          rule: Rule,
          chain: Chain,
          table: Table): Boolean =
        table.name == "nat" && (List("PREROUTING", "OUTPUT") contains chain.name)
    }

    def targetParser: Parser[Target] =
      for {
        _ <- jumpOptionParser

        // Parse the actual target.
        targetName <- someSpacesParser >> stringParser if targetName == "DNAT"

        // Parse the mandatory '--to-destination' target option.
        _ <- someSpacesParser >> parseString("--to-destination")
        lowerIp <- someSpacesParser >> ipParser

        // Parse the optional upper bound ip.
        upperIp <- optional(parseChar('-') >> ipParser)

        // Parse the optional port range.
        maybePortRange <- optional(parseChar(':') >> portRangeParser)
      } yield DnatTarget(lowerIp, upperIp, maybePortRange)
  }
}

object MasqueradeTargetExtension extends TargetExtension {
  val targetParser = Impl.targetParser

  object Impl {
    case class MasqueradeTarget(
        lowerPort: Option[Port],
        upperPort: Option[Port]) extends Target("MASQUERADE") {

      /** This target is only valid in the 'nat' table, in the 'POSTROUTING'
       *  chain.
       *
       *  The '--to-ports' option is only valid if the rule also specifies
       *  '-p tcp' or '-p udp'.
       */
      override def isValid(
          rule: Rule,
          chain: Chain,
          table: Table): Boolean = {
        import FilteringExtension.Impl.ProtocolMatch

        table.name == "nat" && chain.name == "POSTROUTING" &&
        rule.matches.exists(x => x match {
          case ProtocolMatch(p, true) => p == "tcp" || p == "udp"
          case _ => false
        })
      }
    }

    def targetParser: Parser[Target] =
      for {
        _ <- jumpOptionParser

        // Parse the actual  target
        targetName <- someSpacesParser >> stringParser
          if targetName == "MASQUERADE"

        // Parse the optional '--to-ports' target option
        // ([--to-ports port[-port]]).
        lowerPort <- optional(someSpacesParser >> parseString("--to-ports") >>
                              someSpacesParser >> portParser)

        // Try to parse the upper bound port only if the previous one succeeded.
        upperPort <-
          if (lowerPort.isDefined)
{'fraction_non_alphanumeric': 0.10626668361510105, 'fraction_numerical': 0.0022880386424304054, 'mean_word_length': 2.3452380952380953, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 68, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '978915', 'n_tokens_mistral': 2246, 'n_tokens_neox': 2154, 'n_words': 646}