# Jafar

> We stepped up the game of simulating censorship upgrading from the
> evil genius to the evil grand vizier.

Jafar is a censorship simulation tool used for testing OONI. It builds on
any system but it really only works on Linux.

## Building

We use Go >= 1.18. Jafar also needs the C library headers,
iptables installed, and root permissions.

With Linux Alpine edge, you can compile Jafar with:

```bash
apk add go git musl-dev iptables
go build -v .
```

Otherwise, using Docker:

```bash
docker build -t jafar-runner .
docker run -it --privileged -v`pwd`:/jafar -w/jafar jafar-runner
go build -v .
```

## Usage

You need to run Jafar as root. You can get a complete list
of all flags using `./jafar -help`. Jafar is composed of modules. Each
module is controllable via flags. We describe modules below.

### main

The main module starts all the other modules. If you don't provide the
`-main-command <command>` flag, the code will run until interrupted. If
instead you use the `-main-command` flag, you can specify a command to
run inside the censored environment. In such case, the main module
will exit when the specified command terminates. Note that the main
module will propagate the child exit code, if the child fails.

The command can also include arguments. Make sure you quote the arguments
such that your shell passes the whole string to the specified option, as
in `-main-command 'ls -lha'`. This will execute the `ls -lha` command line
inside the censored Jafar context. You can also combine that with quoting
and variables interpolation, e.g., `-main-command "echo '$USER is the
walrus'"`. The `$USER` variable will be expanded by your shell. Assuming
your user name is `paul`, then Jafar will lex the main command as `echo
"paul is the walrus"` and will execute it.

Use the `-main-user <username>` flag to select the user to use for
running child commands. By default, we use the `nobody` user for this
purpose. We implement this feature using `sudo`, therefore you need
to make sure that `sudo` is installed.

### iptables

The iptables module is only available on Linux. It exports these flags:

```bash
  -iptables-drop-ip value
        Drop traffic to the specified IP address
  -iptables-drop-keyword-hex value
        Drop traffic containing the specified hex keyword
  -iptables-drop-keyword value
        Drop traffic containing the specified keyword
  -iptables-hijack-dns-to string
        Hijack all DNS UDP traffic to the specified endpoint
  -iptables-hijack-https-to string
        Hijack all HTTPS traffic to the specified endpoint
  -iptables-hijack-http-to string
        Hijack all HTTP traffic to the specified endpoint
  -iptables-reset-ip value
        Reset TCP/IP traffic to the specified IP address
  -iptables-reset-keyword-hex value
        Reset TCP/IP traffic containing the specified hex keyword
  -iptables-reset-keyword value
        Reset TCP/IP traffic containing the specified keyword
```

The difference between `drop` and `reset` is that in the former case
a packet is dropped, in the latter case a RST is sent.

The difference between `ip` and `keyword` flags is that the former
match an outgoing IP, the latter uses DPI.

The `drop` and `reset` rules allow you to simulate, respectively, when
operations timeout and when a connection cannot be established (with
`reset` and `ip`) or is reset after a keyword is seen (with `keyword`).

Hijacking DNS traffic is useful, for example, to redirect all DNS UDP
traffic from the box to the `dns-proxy` module.

Hijacking HTTP and HTTPS traffic actually hijacks based on ports rather
than on DPI. As a known bug, when hijacking HTTP or HTTPS traffic, we
do not hijack traffic owned by root. This is because Jafar runs as root
and therefore its traffic must not match the hijack rule.

When matching keywords, the simplest option is to use ASCII strings as
in `-iptables-drop-keyword ooni`. However, you can also specify a sequence
of hex bytes, as in `-iptables-drop-keyword-hex |6f 6f 6e 69|`.

Note that with `-iptables-drop-keyword`, DNS queries containing such
keyword will fail returning `EPERM`. For a more realistic approach to
dropping specific DNS packets, combine DNS traffic hijacking with
`-dns-proxy-ignore`, to "drop" packets at the DNS proxy.

### dns-proxy (aka resolver)

The DNS proxy or resolver allows to manipulate DNS. Unless you use DNS
hijacking, you will need to configure your application explicitly to use
the proxy with application specific command line flags.

```bash
  -dns-proxy-address string
        Address where the DNS proxy should listen (default "127.0.0.1:53")
  -dns-proxy-block value
        Register keyword triggering NXDOMAIN censorship
  -dns-proxy-hijack value
        Register keyword triggering redirection to 127.0.0.1
  -dns-proxy-ignore value
        Register keyword causing the proxy to ignore the query
```

The `-dns-proxy-address` flag controls the endpoint where the proxy is
listening.

The `-dns-proxy-block` tells the resolver that every incoming request whose
query contains the specifed string shall receive an `NXDOMAIN` reply.

The `-dns-proxy-hijack` is similar but instead lies and returns to the
client that the requested domain is at `127.0.0.1`. This is an opportunity
to redirect traffic to the HTTP and TLS proxies.

The `-dns-proxy-ignore` is similar but instead just ignores the query.

### http-proxy

The HTTP proxy is an HTTP proxy that may refuse to forward some
specific requests. It's controlled by these flags:

```bash
  -http-proxy-address string
        Address where the HTTP proxy should listen (default "127.0.0.1:80")
  -http-proxy-block value
        Register keyword triggering HTTP 451 censorship
```

The `-http-proxy-address` flag has the same semantics it has for the DNS
proxy.

The `-http-proxy-block` flag tells the proxy that it should return a `451`
response for every request whose `Host` contains the specified string.

### tls-proxy

TLS proxy is a proxy that routes traffic to specific servers depending
on their SNI value. It is controlled by the following flags:

```bash
  -tls-proxy-address string
        Address where the HTTP proxy should listen (default "127.0.0.1:443")
  -tls-proxy-block value
        Register keyword triggering TLS censorship
```

The `-tls-proxy-address` flags has the same semantics it has for the DNS
proxy.

The `-tls-proxy-block` specifies which string or strings should cause the
proxy to return an internal-erorr alert when the incoming ClientHello's SNI
contains one of the strings provided with this option.

### bad-proxy

```bash
  -bad-proxy-address string
        Address where to listen for TCP connections (default "127.0.0.1:7117")
  -bad-proxy-address-tls string
        Address where to listen for TLS connections (default "127.0.0.1:4114")
  -bad-proxy-tls-output-ca string
        File where to write the CA used by the bad proxy (default "badproxy.pem")
```

The bad proxy is a proxy that reads some bytes from any incoming connection
and then closes the connection without replying anything. This simulates a
proxy that is not working properly, hence the name of the module.

When connecting using TLS, the above behaviour happens after the handshake.

We write the CA on the file specified using `-bad-proxy-tls-output-ca` such that
tools like curl(1) can use such CA to avoid TLS handshake errors. The code will
generate on the fly a certificate for the provided SNI. Not providing any SNI in
the client Hello message will cause the TLS handshake to fail.

### uncensored

```bash
  -uncensored-resolver-doh string
     URL of an hopefully uncensored DoH resolver (default "https://1.1.1.1/dns-query")
```

The HTTP, DNS, and TLS proxies need to resolve domain names. If you setup DNS
censorship, they may be affected as well. To avoid this issue, we use a different
resolver for them, which by default is the one shown above. You can change such
default by using the `-uncensored-resolver-doh` command line flag. The input
URL is an HTTPS URL pointing to a DoH server. Here are some examples:

* `https://dns.google/dns-query`
* `https://dns.quad9.net/dns-query`

So, for example, if you are using Jafar to censor `1.1.1.1:443`, then you
most likely want to use `-uncensored-resolver-doh`.

## Examples

Block `play.google.com` with RST injection, force DNS traffic to use the our
DNS proxy, and force it to censor `play.google.com` with `NXDOMAIN`.

```bash
# ./jafar -iptables-reset-keyword play.google.com \
          -iptables-hijack-dns-to 127.0.0.1:5353  \
          -dns-proxy-address 127.0.0.1:5353       \
          -dns-proxy-block play.google.com
```

Force all traffic through the HTTP and TLS proxy and use them to censor
`play.google.com` using HTTP 451 and responding with TLS alerts:

```bash
# ./jafar -iptables-hijack-dns-to 127.0.0.1:5353 \
          -dns-proxy-address 127.0.0.1:5353      \
          -dns-proxy-hijack play.google.com      \
          -http-proxy-block play.google.com      \
          -tls-proxy-block play.google.com
```

Run `ping` in a censored environment:

```bash
# ./jafar -iptables-drop-ip 8.8.8.8 -main-command 'ping -c3 8.8.8.8'
```

Run `curl` in a censored environment where it cannot connect to
`play.google.com` using `https`:

```bash
# ./jafar -iptables-hijack-https-to 127.0.0.1:443         \
          -tls-proxy-block play.google.com                \
          -main-command 'curl -Lv http://play.google.com'
```

For more usage examples, see `../../testjafar.bash`.
