
# Chapter IV: TLS handshaking

This chapter describes measuring TLS handshakes.

Without further ado, let's describe our example `main.go` program
and let's use it to better understand how to measure that.

(This file is auto-generated. Do not edit it directly! To apply
changes you need to modify `./internal/tutorial/measurex/chapter04/main.go`.)

## main.go

The initial part of the program is pretty much the same as the one
used in previous chapters, so I will not add further comments.

```Go
package main

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"time"

	"github.com/ooni/probe-cli/v3/internal/measurex"
	"github.com/ooni/probe-cli/v3/internal/netxlite"
	"github.com/ooni/probe-cli/v3/internal/runtimex"
)

func main() {
	sni := flag.String("sni", "dns.google", "domain to resolver")
	address := flag.String("address", "8.8.4.4:443", "remote endpoint address")
	timeout := flag.Duration("timeout", 60*time.Second, "timeout to use")
	flag.Parse()
	ctx, cancel := context.WithTimeout(context.Background(), *timeout)
	defer cancel()
	mx := measurex.NewMeasurerWithDefaultSettings()
```

### Connecting and handshaking.

We call the `ConnectAndHandshake` method. The arguments
are the context, the address, and a TLS config.

Under the hood, the code will call the TCP connect functionality
we have seen in chapter02, using the address argument. Then, if
successful, it will TLS handshake using the given TLS config.

```Go
	m := mx.TLSConnectAndHandshake(ctx, *address, &tls.Config{
		ServerName: *sni,
		NextProtos: []string{"h2", "http/1.1"},
		RootCAs:    netxlite.NewDefaultCertPool(),
	})
```

Regarding the TLS config, in particular,
the three fields above are the files you should always set
in a TLS config when doing handshakes manually. The `ServerName`
field forces the SNI, the NextProtos field forces the ALPN,
and the `RootCAs` field is overridden so that we use the
CA bundle that we bundle with OONI. (This CA bundle is the
same you can find at https://curl.haxx.se/ca/.)

As usual, the method to perform a measurement returns
the measurement itself, which we print below.

```
	data, err := json.Marshal(m)
	runtimex.PanicOnError(err, "json.Marshal failed")
	fmt.Printf("%s\n", string(data))
}

```
## Running the example program

As before, let us start off with a vanilla run:

```bash
go run -race ./internal/tutorial/measurex/chapter04
```

Let us comment the JSON in detail:

```JavaScript
{
  "network": "tcp",
  "address": "8.8.4.4:443",

  // This block is generated when connecting to a TCP
  // socket, as we've already seen in chapter02
  "connect": [
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "operation": "connect",
      "proto": "tcp",
      "t": 0.046959084,
      "started": 0.022998875,
      "oddity": ""
    }
  ],

  // These are the I/O events during the handshake
  "read_write": [
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 280,
      "operation": "write",
      "proto": "tcp",
      "t": 0.048752875,
      "started": 0.04874125,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 517,
      "operation": "read",
      "proto": "tcp",
      "t": 0.087221334,
      "started": 0.048760417,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 4301,
      "operation": "read",
      "proto": "tcp",
      "t": 0.088843584,
      "started": 0.088830959,
      "oddity": ""
    },
    {
      "address": "8.8.4.4:443",
      "failure": null,
      "num_bytes": 64,
      "operation": "write",
      "proto": "tcp",
      "t": 0.092078042,
      "started": 0.092064042,
      "oddity": ""
    }
  ],

  // This block contains information about the handshake
  "tls_handshake": [
    {
      "cipher_suite": "TLS_AES_128_GCM_SHA256",
      "failure": null,
      "negotiated_proto": "h2",
      "tls_version": "TLSv1.3",
      "peer_certificates": [
        {
          "data": " ",
          "format": "base64"
        },
        {
          "data": " ",
          "format": "base64"
        },
        {
          "data": " ",
          "format": "base64"
        }
      ],
      "t": 0.092117709,
      "address": "8.8.4.4:443",
      "server_name": "dns.google",
      "alpn": [
        "h2",
        "http/1.1"
      ],
      "no_tls_verify": false,
      "oddity": "",
      "proto": "tcp",
      "started": 0.047288542
    }
  ]
}
```

All the data formats we're using here are, by the way,
compatible with the data formats specified at
https://github.com/ooni/spec/tree/master/data-formats.

### Suggested follow-up experiments

Try to run experiments in the following scenarios, and
check the output JSON to familiarize with what changes in
different error conditions.

1. measurement that causes timeout

2. measurement with wrong SNI

3. measurement with self-signed certificate

4. measurement with expired certificate

5. measurement with connection reset during handshake

6. measurement with timeout during handshake

Here are the commands I used for each proposed exercise:

1. go run -race ./internal/tutorial/measurex/chapter04 -address 8.8.4.4:1

2. go run -race ./internal/tutorial/measurex/chapter04 -sni example.org

3. go run -race ./internal/tutorial/measurex/chapter04 -address 104.154.89.105:443 -sni self-signed.badssl.com

4. go run -race ./internal/tutorial/measurex/chapter04 -address 104.154.89.105:443 -sni expire.badssl.com

To emulate the two last scenario, if you're on Linux, a
possibility is building Jafar with this command:

```
go build -v ./internal/cmd/jafar
```

Then, for example, to provoke a connection reset you
can run in a terminal:

```
sudo ./jafar -iptables-reset-keyword dns.google
```

and you can run this tutorial with `dns.google` as
the SNI in another terminal.

Likewise, you can obtain a timeout using the
`-iptables-drop-keyword` flag instead.

(Jafar runs forever and censors. You need to use
`^C` to terminate it from running.)

## Conclusion

We have seen how to measure TLS handshakes. We have seen how
this flow produces different output on different error conditions.

