Commit Graph

548 Commits

Author SHA1 Message Date
Simone Basso
99ec7ffca9
fix: ensure experiments return nil when we want to submit (#654)
Since https://github.com/ooni/probe-cli/pull/527, if an experiment
returns an error, the corresponding measurement is not submitted since
the semantics of returning an error is that something fundamental
went wrong (e.g., we could not parse the input URL).

This diff ensures that all experiments only return and error when
something fundamental was wrong and return nil otherwise.

Reference issue: https://github.com/ooni/probe/issues/1808.
2022-01-07 13:17:20 +01:00
Simone Basso
60a3c372f5
cleanup: remove the original netx implementation (#653)
This commit completely removes the original netx implementation,
which was only used by `tor`, since this has changed in
https://github.com/ooni/probe-cli/pull/652.

The original netx implementation was my first attempt at performing
network measurements using Go. It started its life inside of the
https://github.com/ooni/netx repository. It was later merged into
the https://github.com/ooni/probe-engine repository. It finally
ended up into this repository when we merged probe-engine with it.

The main issue with the original implementation is that it was
a bit too complex and used channels where they were probably not
necessary. Because of that, later I introduced a second netx
implementation, which currently lives in ./internal/engine/netx.

The current netx implementation, the third one, lives in the
./internal/netxlite package. We are currently working to replace
the second implementation with the third one, but this is happening
at a slow pace. Also, the second implementation does not have big
maintenance concerns but it's just a bit too bureaucratic to use
since it involves creating lots of `Config` structures.

The reference issue is probably https://github.com/ooni/probe/issues/1688,
since this diff has been enabled by rewriting Tor to use `measurex`
(a library living on top of `netxlite`).
2022-01-05 19:00:50 +01:00
Simone Basso
dfa5e708fe
refactor(tor): rewrite using measurex (#652)
This diff rewrites the tor experiment to use measurex "easy" API.

To this end, we need to introduce an "easy" measurex API, which basically
performs easy measurements returning two pieces of data:

1. the resulting measurement, which is already using the OONI
archival data format and is always non-nil

2. a failure (i.e., the pointer to an error string), which
is nil on success and points to a string on failure

With this change, we should now be able to completely dispose of
the original netx API, which was only used by tor.

Reference issue: https://github.com/ooni/probe/issues/1688.
2022-01-05 18:41:11 +01:00
Simone Basso
f0181c432f
refactor: move httpx into the internal package (#646)
This concludes the TODO list at https://github.com/ooni/probe/issues/1951
2022-01-05 17:17:20 +01:00
Simone Basso
dba861d262
feat(httpx): implement optional body logging also on http error (#651)
1. we want optionally to log the body (we don't want to log the body
when we're fetching psiphon secrets or tor targets)

2. we want body logging to _also_ happen on error since this is quite
useful to debug possible errors when accessing the API

This diff adds the above functionality, which were previously
described in https://github.com/ooni/probe/issues/1951.

This diff also adds comprehensive testing.
2022-01-05 16:26:51 +01:00
Simone Basso
ee0aa18616
refactor(httpx): use mocks to implement tests (#650)
* refactor(httpx): use mocks to implement tests

While there, make sure no test depends on external services by
replacing such tests with httptest.

See https://github.com/ooni/probe/issues/1951.

* fix(httpx): ensure we honour the context
2022-01-05 16:13:42 +01:00
Simone Basso
93f084598e
feat(httpx): improve testing using the fakefiller (#649)
This diff extracts the fakefiller inside of internal/ooapi (a
currently unused package) into its own package.

The fakefiller knows how to fill many fields that are typically
shared as data structures across processes.

It is not perfect in that it cannot fill logger or http client
fields, but still helps with better filling and testing.

So, here we're using the fakefiller to improve testing of httpx
and, nicely enough, we've already catched a bug in the way in
which APIClientTemplate.Build misses to forward Authorization from
the original template. Yay!

Work part of https://github.com/ooni/probe/issues/1951
2022-01-05 14:49:31 +01:00
Simone Basso
eed51978ca
refactor(httpx): hide the real APIClient (#648)
As mentioned in https://github.com/ooni/probe/issues/1951, one of
the main issues I did see with httpx.APIClient is that in some cases
it's used in a very fragile way by probeservices.Client.

This happens in psiphon.go and tor.go, where we create a copy of
the APIClient and then modify it's Authorization field.

If we ever refactor probeservices.Client to take a pointer to
httpx.Client, we are now mutating the httpx.Client.

Of course, we don't want that to happen.

This diff attempts to address such a problem as follows:

1. we create a new APIClientTemplate type that holds the same
fields of an APIClient and allows to build an APIClient

2. we modify every user of APIClient to use APIClientTemplate

3. when we need an APIClient, we build it from the corresponding
template and, when we need to use a specific Authorization, we
use a build factory that sets APIClient.Authorization

4. we hide APIClient by renaming it apiClient and by defining
an interface called APIClient that allows to use it

So, now the codebase always uses the opaque APIClient interface to
issue API calls and always uses the APIClientTemplate to build an
opaque APIClient.

Boom! We have separated construction from usage and we are not
mutating in weird ways the APIClient anymore.
2022-01-05 14:15:42 +01:00
Simone Basso
7b7df2c6af
refactor(httpx): improve and modernize (1/n) (#647)
This PR starts to implement the refactoring described at https://github.com/ooni/probe/issues/1951. I originally wrote more patches than the ones in this PR, but overall they were not readable. Since I want to squash and merge, here's a reasonable subset of the original patches that will still be readable and understandable in the future.
2022-01-05 12:48:32 +01:00
Simone Basso
0a630c1716
refactor(measurex): allow to configure timeouts and max-snapshot-size (#645)
This diff lightly refactors the code in measurex to allow a user
to configure all possible timeouts and the max-snapshot-size.

There is currently a little bit of tension between setting timeouts
inside of measurex and the watchdog timeouts inside of netxlite.

This tension has been documented.

Let us repeat the issue also in this commit message. If you are
using a masurex.Measurer configured with very large timeouts and
the underlying netxlite implementation uses shorter whatchdog
timeouts, then you are going to see shorter than expected timeouts.

Ideally, we would like to have just a single timeout but there is
no way to ask the context "hey, can you tell me if you already have
a configured timeout?".

It may be that the right solution is to modify netxlite to have
some sort of root/library object with this configuration.

If that's the case, then a Measurer could be refactored as follows:

- create the underlying netxlite "library"

- initialize the timeouts desired by the Measurer

- create a Dialer, of whatever is needed

- use it

Now this is not possible because netxlite timeouts are internal
static settings rather than attributes of a structure.

Anyway, for now I'm happy with this just being documented.

(I suspect this issue will need to be addresses when we'll write
unit tests for measurex; at that time a proper solution should
come out naturally due to the unit tests constraints.)

I'm working on this refactoring, BTW, to facilitate rewriting `tor`
using measurex (see https://github.com/ooni/probe/issues/1688).
2022-01-04 13:20:48 +01:00
Simone Basso
8afb3ee0d5
cleanup(ptx): remove redundant dialer definition (#644)
We can just use model.SimpleDialer here.

See https://github.com/ooni/probe/issues/1885.
2022-01-04 12:46:55 +01:00
Simone Basso
43161a8138
cleanup: remove redundant HTTPClient definition (#643)
This counts as a follow-up cleanup as part of doing
https://github.com/ooni/probe/issues/1885.
2022-01-03 16:47:54 +01:00
Simone Basso
273b70bacc
refactor: interfaces and data types into the model package (#642)
## Checklist

- [x] I have read the [contribution guidelines](https://github.com/ooni/probe-cli/blob/master/CONTRIBUTING.md)
- [x] reference issue for this pull request: https://github.com/ooni/probe/issues/1885
- [x] related ooni/spec pull request: N/A

Location of the issue tracker: https://github.com/ooni/probe

## Description

This PR contains a set of changes to move important interfaces and data types into the `./internal/model` package.

The criteria for including an interface or data type in here is roughly that the type should be important and used by several packages. We are especially interested to move more interfaces here to increase modularity.

An additional side effect is that, by reading this package, one should be able to understand more quickly how different parts of the codebase interact with each other.

This is what I want to move in `internal/model`:

- [x] most important interfaces from `internal/netxlite`
- [x] everything that was previously part of `internal/engine/model`
- [x] mocks from `internal/netxlite/mocks` should also be moved in here as a subpackage
2022-01-03 13:53:23 +01:00
Arturo Filastò
41cf4a8671
feature: use go-libtor on mobile, OONI_TOR_BINARY env on desktop (#614)
This branch adds support for running:

1. `go-libtor` on mobile.

2. the tor provided by the desktop app via the `OONI_TOR_BINARY` environment variable.

See https://github.com/ooni/ooni.org/issues/761.

Co-authored-by: Simone Basso <bassosimone@gmail.com>
2021-12-15 14:16:22 +01:00
Simone Basso
6cb8e3d785
chore: set master's version to 3.14.0-alpha (#638)
As mentioned in https://github.com/ooni/probe/issues/1845#issuecomment-986540868,
I did a mistake and published an `-alpha` debian package, so I need to
bump master's version to 3.14 and create a 3.13 release train.

やれやれだぜ
2021-12-06 17:54:58 +01:00
Simone Basso
611fed05f4
[forwardport] release 3.11: update all the dependencies (#636) (#637)
* [forwardport] release 3.11: update all the dependencies (#636)

This diff forward ports e291e436b3c332300f5567796f9c48bb9bc1e652.

* chore: use go1.17.4 everywhere

Part of https://github.com/ooni/probe/issues/1843

* chore: update to the latest ooni/oohttp

Part of https://github.com/ooni/probe/issues/1843

* chore: update the dependencies

Note: I did an update and not an upgrade (i.e., I didn't check
whether we have next-major-versions of dependencies).

Part of https://github.com/ooni/probe/issues/1843

* chore: update the user-agent we use

Part of https://github.com/ooni/probe/issues/1843

* chore: update ooni/oohttp and ooni/probe-assets

Part of https://github.com/ooni/probe/issues/1843

* chore: run go generate again

Part of https://github.com/ooni/probe/issues/1843

* fix(jafar): skip currently broken test

Created issue for it here: https://github.com/ooni/probe/issues/1913
2021-12-06 17:46:13 +01:00
Simone Basso
dc9fbe9c64
fix(oonimkall): run tests with InputOrStaticDefault policy (#634)
Previous work to make https://github.com/ooni/probe/issues/1814
possible has broken running stunreachability on mobile.

This diff repairs the blunder and allows to run any experiment
using InputOrStaticDefault with oonimkall.

Diff extracted from https://github.com/ooni/probe-cli/pull/539.
2021-12-03 17:43:09 +01:00
Simone Basso
2044b78a5a
refactor: introduce and use InputOrStaticDefault (#632)
This commit introduces a new `InputLoader` policy by which, if no
input is provided, we use a static default input list.

We also modify the code to use this policy for dnscheck and
stunreachability, with proper input.

We also modify `miniooni` to pass the new `ExperimentName` field to
the `InputLoader` to indicate which default input list to use.

This diff is part of a set of diffs aiming at fixing
https://github.com/ooni/probe/issues/1814 and has been
extracted from https://github.com/ooni/probe-cli/pull/539.

What remains to be done, after this diff has landed is to ensure
things also work for ooniprobe and oonimkall.
2021-12-03 15:30:56 +01:00
Simone Basso
13414e0abc
refactor: create common package for holding STUN input (#631)
We want stunreachability to use the same STUN servers used by
snowflake, so let's start by making a common package holding the
servers. Let's also use this new package in Snowflake.

We're currently not using this package in stunreachability, but
I am going to apply this as a subsequent diff.

Reference issue: https://github.com/ooni/probe/issues/1814. This
issue is a bit complex to address in a single PR, so we are going
to proceed incremntally.

This diff was extracted from https://github.com/ooni/probe-cli/pull/539.
2021-12-03 14:45:25 +01:00
Simone Basso
cba72d1ca3
refactor(stunreachability): input required and must be an URL (#630)
Here we're refactoring stunreachability to not provide internally a
default input and to take in input an URL rather than a string.

The related ooni/spec change is https://github.com/ooni/spec/pull/227.

This diff has been extracted from https://github.com/ooni/probe-cli/pull/539.

Because the original diff was large, I'm splitting it in a set of
more easily manageable diffs.

The reference issue is https://github.com/ooni/probe/issues/1814, which
is complex enough to require us to proceed incrementally.

This diff WILL need to be backported to release/3.11.
2021-12-03 14:27:04 +01:00
Simone Basso
9cdca4137d
forwardport: pull the patches mentioned in ooni/probe#1908 (#629)
* [forwardport] fix(oonimkall): make logger used by tasks unit testable (#623)

This diff forward ports e4b04642c51e7461728b25941624e1b97ef0ec83.

Reference issue: https://github.com/ooni/probe/issues/1903

* [forwardport] feat(oonimkall): improve taskEmitter testability (#624)

This diff forward ports 3e0f01a389c1f4cdd7878ec151aff91870a0bdff.

1. rename eventemitter{,_test}.go => taskemitter{,_test}.go because
the new name is more proper after we merged the internal/task package
inside of the oonimkall package;

2. rename runner.go's `run` function to `runTask`;

3. modify `runTask` to use the new `taskEmitterUsingChan` abstraction
on which we will spend more works in a later point of this list;

4. introduce `runTaskWithEmitter` factory that is called by `runTask`
and allows us to more easily write unit tests;

5. acknowledge that `runner` was not using its `out` field;

6. use the new `taskEmitterWrapper` in `newRunner`;

7. acknowledge that `runnerCallbacks` could use a generic
`taskEmitter` as field type rather than a specific type;

8. rewrite tests to use `runTaskWithEmitter` which leads to
simpler code that does not require a goroutine;

9. acknowledge that the code has been ignoring the `DisabledEvents`
settings for quite some time, so stop supporting it;

10. refactor the `taskEmitter` implementation to be like:

    1. we still have the `taskEmitter` interface;

    2. `taskEmitterUsingChan` wraps the channel and allows for
    emitting events using the channel;

    3. `taskEmitterUsingChan` owns an `eof` channel that is
    closed by `Close` (which is idempotent) and signals we
    should be stop emitting;

    4. make sure `runTask` creates a `taskEmitterUsingChan`
    and calls its `Close` method when done;

    5. completely remove the code for disabling events
    since the code was actually ignoring the stting;

    6. add a `taskEmitterWrapper` that adds common functions
    for emitting events to _any_ `taskWrapper`;

    7. write unit tests for `taskEmitterUsingChan` and
    for `taskEmitterWrapper`;

11. acknowledge that the abstraction we need for testing is
actually a thread-safe thing that collects events into a
vector containing events and refactor all tests accordingly.

See https://github.com/ooni/probe/issues/1903

* [forwardport] refactor(oonimkall): make the runner unit-testable (#625)

This diff forward ports 9423947faf6980d92d2fe67efe3829e8fef76586.

See https://github.com/ooni/probe/issues/1903

* [forwardport] feat(oonimkall): write unit tests for the runner component (#626)

This diff forward ports 35dd0e3788b8fa99c541452bbb5e0ae4871239e1.

Forward porting note: compared to 35dd0e3788b8fa99c541452bbb5e0ae4871239e1,
the diff I'm committing here is slightly different. In `master` we do not
have the case where a measurement fails and a measurement is returned, thus
I needed to adapt the test to become like this:

```diff
diff --git a/pkg/oonimkall/runner_internal_test.go b/pkg/oonimkall/runner_internal_test.go
index 334b574..84c7436 100644
--- a/pkg/oonimkall/runner_internal_test.go
+++ b/pkg/oonimkall/runner_internal_test.go
@@ -568,15 +568,6 @@ func TestTaskRunnerRun(t *testing.T) {
                }, {
                        Key:   failureMeasurement,
                        Count: 1,
-               }, {
-                       Key:   measurement,
-                       Count: 1,
-               }, {
-                       Key:   statusMeasurementSubmission,
-                       Count: 1,
-               }, {
-                       Key:   statusMeasurementDone,
-                       Count: 1,
                }, {
                        Key:   statusEnd,
                        Count: 1,
```

I still need to write more assertions for each emitted event
but the code we've here is already a great starting point.

See https://github.com/ooni/probe/issues/1903

* [forwardport] refactor(oonimkall): merge files, use proper names, zap unneeded integration tests (#627)

This diff forward ports f894427d24edc9a03fc78306d0093e7b51c46c25.

Forward porting note: this diff is slightly different from the original
mentioned above because it carries forward changes mentioned in the
previous diff caused by a different way of handling a failed measurement
in the master branch compared to the release/3.11 branch.

Move everything that looked like "task's model" inside of the
taskmodel.go file, for consistency.

Make sure it's clear some variables are event types.

Rename the concrete `runner` as `runnerForTask`.

Also, remove now-unnecessary (and flaky!) integration tests
for the `runnerForTask` type.

While there, notice there were wrong URLs that were generated
during the probe-engine => probe-cli move and fix them.

See https://github.com/ooni/probe/issues/1903

* [forwardport] refactor(oonimkall): we can simplify StartTask tests (#628)

This diff forward ports dcf2986c2032d8185d58d24130a7f2c2d61ef2fb.

* refactor(oonimkall): we can simplify StartTask tests

We have enough checks for runnerForTask. So we do not need to
duplicate them when checking for StartTask.

While there, refactor how we start tasks to remove the need for
extra runner functions.

This is the objective I wanted to achieve for oonimkall:

1. less duplicate tests, and

2. more unit tests (which are less flaky)

At this point, we're basically done (pending forwardporting to
master) with https://github.com/ooni/probe/issues/1903.

* fix(oonimkall): TestStartTaskGood shouldn't cancel the test

This creates a race condition where the test may fail if we cannot
complete the whole "Example" test in less than one second.

This should explain the build failures I've seen so far and why
I didn't see those failures when running locally.
2021-12-02 12:47:07 +01:00
Simone Basso
ece6f3d48d
fix(websteps, webconnectivity): send the correct user agent (#616)
* [forwardport] fix(webconnectivity): send specific user agent (#615)

This forward ports b8c530388e66b2cc86abad26d077202782e4a823 to `master`.

See https://github.com/ooni/probe/issues/1902

* fix(websteps): send the correct user agent

Also related to https://github.com/ooni/probe/issues/1902: let's just
ensure that also websteps behaves in the correct way.
2021-11-26 19:20:24 +01:00
Simone Basso
56440bec43
chore: set dev version to v3.12.0-alpha.4 (#612) 2021-11-23 16:16:30 +01:00
Simone Basso
fdbf871103
[forwardport] ci/cd: publish binaries onto a release when we create a tag (#609) (#611)
This diff forwardports 856e436e20d511a4f0d618546da7921fa9f8c5f6 to the master branch

Original commit message:

- - -

This pull request changes `mk` and github workflows to build and publish binaries on tag. We also update the documentation to explain this new branching model. Basically, we have release branches where we produce binary packages and we add extra code, on tag, to publish such packages inside a release.

We discussed removing most secrets from builds in this repository and having a different tool/repository that takes in input also secrets for doing follow-up actions after publishing. As a consequence, this pull request also removes all pieces of code that require secrets. The next step is to reinstate this code in this new repository/tool.

The existing code in `mk` also implemented caching. This feature was useful when doing local builds because it reduced the time required to obtain binary releases. With builds running as part of GitHub actions, we don't need caching because we spawn parallel machines to build binaries. Therefore, let us also remove caching, which makes the code simpler. (Caching in itself is hard and in https://github.com/ooni/probe/issues/1875 I noted that, for example, caching of the `ooni/go` repository was leading to some unwanted behaviour when changing the branch. Without caching, this behaviour is gone and we always generally use fresh information to produce builds.) Of course, this means that local builds are now slower, but I do not think this is a problem _because_ we want to use GitHub actions for building in the common case.

Reference issues: https://github.com/ooni/probe/issues/1879 and https://github.com/ooni/probe/issues/1875.

The final aspect to mention to conclude this description is an implementation one:

```
          gh release create -p $tag --target $GITHUB_SHA || true
```

The code above uses `|| true` because there could already be a release. So, basically, it means that, if a release does not already exist, then we're going to create one. Otherwise, it does not matter because there's already a release.
2021-11-23 15:56:25 +01:00
Simone Basso
5b9f701317
fix(measurex): handle case where TH is nil (#610)
This case happens when the TH fails.

See https://github.com/ooni/probe/issues/1895.
2021-11-22 20:16:38 +01:00
Simone Basso
264e30f016
[forwardport] fix(mk): do nothing if we already have psiphon config (#605) (#606)
This diff forward ports ea44e99451f345474738b9010ff791759a1f1367.

Original commit message:

- - -

This change allows for producing cloud builds using the psiphon
config files. We will add those files as build secrets. Only people
in the organization and collaborators with at least "write"
access could trigger builds containing such secrets.

Before this change, `./mk` unconditionally attempted to clone
github.com/ooni/probe-private. Now, it only checks whether
we need to clone _if_ files are not already there.

This allows us to use GitHub actions and secrets to copy the
files in there _without_ needing to clone a private repo.

Cloning a private repo would require us to include as repository
secret an access token with full `repo` scope, which is a very
broad scope. Instead, by using secrets to include psiphon config,
we are narrowing down the secrets required to make a release build.

See https://github.com/ooni/probe/issues/1878

This diff WILL require forward porting to the master branch.
2021-11-19 12:40:10 +01:00
Simone Basso
0cb535b022
[forwardport] fix(signal): use the correct string to score a measurement (#602) (#603)
This diff forwardports 3b1cc1b6afbc29fbc9ff8221d2cd84f34032df60.

Original commit message:

- - -

See https://github.com/ooni/probe/issues/1858#issuecomment-970322363

This diff will need forward porting to master.
2021-11-16 15:57:16 +01:00
Simone Basso
56d179d88a
chore: set version to v3.12.0-alpha.3 (#601) 2021-11-16 13:17:30 +01:00
Simone Basso
50576daec8
[forwardport] fix(ios): make sure we can build an iOS release (#598) (#599)
This diff forward ports f47b0c6c16e0cd417e3591358eb85b45962f307d to master.

Original commit message:

- - -

1. we now need to name the framework `.xcframework` otherwise
gomobile refuses to build a new framework for us ¯\_(ツ)_/¯

2. remove duplicate errno definition for iOS (iOS and darwin
are considered the same, therefore we don't need iOS defs)

Reference issue for this PR: https://github.com/ooni/probe/issues/1876

This diff WILL need to be forwardported to master.
2021-11-15 14:24:20 +01:00
Simone Basso
c527ca1f61
fix(psiphon): increase the maximum runtime to 300s (#596)
See: https://github.com/ooni/probe/issues/1856.

This diff will need to be backported to release/3.11.
2021-11-15 12:43:19 +01:00
Simone Basso
5a481b395a
fix(geolocate): use powerdns.org whoami service (#592)
This diff needs to be backported to the release/3.11 branch.

Reference issue https://github.com/ooni/probe/issues/1865.
2021-11-12 14:53:15 +01:00
Simone Basso
0a322ebab0
[forwardport] fix: avoid http3 for dns.google and www.google.com (#593) (#594)
This commit forward ports dedd84fa7ecb09f718f6b1a9c83999cb37b34dfa.

Original commit message:

- - -

This diff changes code the release/3.11 branch to ensure we're not using dns.google and www.google.com over HTTP3. As documented in https://github.com/ooni/probe/issues/1873, since this morning (approx) these services do not support HTTP3 anymore. (I didn't bother with checking whether this issue affects _other_ Google services; I just limited my analysis to the services that we were using as part of testing.)

This patch WILL require forward porting to the master branch.
2021-11-12 14:43:28 +01:00
Simone Basso
0d65438ea1
[forwardport] fix(netxlite): remove android-specific errno (#586) (#587)
This commit forward ports 74947dbbd12266c12a38fad51a70fc78a21720fd from
the `release/3.11` branch to `master`. Here's the original commit message:

- - -

Android is also Linux. The Android build fails because both
errno_linux.go and errno_android.go are compiled.

There's no difference between the files except into a comment
that mentions "linux" or "android".

Therefore, it's safe to remove the android-specific file
and just keep and use the linux-specific one.

Part of https://github.com/ooni/probe/issues/1863, where we're
forward porting ooni/go patches to go1.17.

I'm still trying to figure out whether I can build oonimkall
using the forward ported patches and this error prevents me
from building, because the build fails.

"やれやれだぜ"

Note that this patch WILL need to be forward ported to master.

This bug was previosuly reported to me by @hellais.

Because I did run `go generate ./internal/netxlite/...` we also
get for free updated certificates, which is OK.
2021-11-10 13:00:41 +01:00
Simone Basso
6a935d5407
fix(netxlite): ensure HTTP errors are always wrapped (#584)
1. introduce implementations of HTTPTransport and HTTPClient
that apply an error wrapping policy using the constructor
for a generic top-level error wrapper

2. make sure we use the implementations in point 1 when we
are constructing HTTPTransport and HTTPClient

3. make sure we apply error wrapping using the constructor for
a generic top-level error wrapper when reading bodies

4. acknowledge that error wrapping would be broken if we do
not return the same classification _and_ operation when we wrap
an already wrapped error, so fix the to code to do that

5. acknowledge that the classifiers already deal with preserving
the error string and explain why this is a quirk and why we
cannot remove it right now and what needs to happen to safely
remove this quirk from the codebase

Closes https://github.com/ooni/probe/issues/1860
2021-11-06 17:49:58 +01:00
Simone Basso
be89878dd4
doc(tutorial): explain the big picture (#582)
Each tutorial provides you a piece of knowledge. Add some text in
the main readme file explaining the big picture to the reader.

Closes https://github.com/ooni/probe/issues/1819
2021-11-05 14:51:14 +01:00
Simone Basso
ba7b981fcb
fix(measurex): allow API user to choose parallelism (#581)
Closes https://github.com/ooni/probe/issues/1818
2021-11-05 14:37:03 +01:00
Simone Basso
3b27780836
fix(webconnectivity): ignore any status code <= 0 (#579)
This diff changes the algorithm used by webconnectivity's
httpanalysis.go to ignore any status code <= 0 rather
than just ignoring the == 0 case.

Make sure we add test cases for when the control's status
code is negative rather than being zero.

While there, simplify code where boolean checks could be
more compact according to staticcheck.

Closes https://github.com/ooni/probe/issues/1825
2021-11-05 13:51:22 +01:00
Simone Basso
fab7909281
chore: version is now 3.12.0-alpha.2 (#576) 2021-11-05 11:54:19 +01:00
Simone Basso
1f348803dc
fix(websteps): use a single Do53 resolver (#575)
Closes https://github.com/ooni/probe/issues/1807

While there, bump the experiment version number, because we have
applied changes since the last alpha release.
2021-11-05 11:40:58 +01:00
Simone Basso
dad8db4a36
fix(httpheader): use same Accept-Language as Brave (#574)
Closes https://github.com/ooni/probe/issues/1792
2021-11-05 11:31:10 +01:00
Simone Basso
60cfa68b5a
fix(measurex): avoid data race with HTTP request headers (#573)
Closes https://github.com/ooni/probe/issues/1859
2021-11-05 11:11:43 +01:00
Simone Basso
aa27bbe33f
fix(measurex): use same keys of the OONI data format (#572)
This change should simplify the pipeline's job.

Reference issue: https://github.com/ooni/probe/issues/1817.

I previously dismissed this possibility, but now it seems clear it
is simpler to have a very tabular data format internally and to
convert such a format to OONI's data format when serializing.

The OONI data format is what the pipeline expects, but processing
is easier with a more linear/tabular format.
2021-11-05 10:46:45 +01:00
Simone Basso
6f90d29bfa
feat(miniooni): add the --censor flag (#571)
This flag is similar to the previous --self-censor-spec and tells
miniooni what censorship to implement for itself.

This concludes the design at https://github.com/ooni/probe/issues/1803#issuecomment-957323297
2021-11-03 14:23:47 +01:00
Simone Basso
100cc559a2
Revert "feat(filtering): implement the divert policy (#569)" (#570)
This reverts commit 851b9913fa because
it seems it's not enough to allow us to see certificate errors with
quic, plus it's complex code. So, we'd rather develop a better approach,
and perhaps a simpler one, that works with QUIC as well.
2021-11-03 14:01:32 +01:00
Simone Basso
851b9913fa
feat(filtering): implement the divert policy (#569)
This is the policy we need to provoke certificate errors. We'll divert
from, say, `8.8.8.8:443/udp` to, say, `1.1.1.1:443/udp`.

We'll do something similar for `443/tcp`.

This will cause certificate validation errors.

With this change, we have now implemented the simple design described
by https://github.com/ooni/probe/issues/1803#issuecomment-957323297.
2021-11-03 00:29:14 +01:00
Simone Basso
675e3a5ba5
feat(filtering): introduce DNS cache (#568)
When we're testing multiple endpoints, it's quite important to control
the order with which they are returned to the code.

This feature is especially relevant to Web Connectivity, which will
check the endpoints to connect to in order.

Therefore, we need to force deterministic results to ensure that we can
have deterministic tests when doing Web Connectivity QA.

This diff gives us the guarantee that we can have determinism.

Part of https://github.com/ooni/probe/issues/1803#issuecomment-957323297.
2021-11-02 23:35:09 +01:00
Simone Basso
11ccd16a0c
feat(filtering): add transparent proxy with censorship policies (#566)
This PR implements the core concept described at
https://github.com/ooni/probe/issues/1803#issuecomment-957323297
2021-11-02 21:52:32 +01:00
Simone Basso
560b1a9a97
fix(filtering): avoid the if err == nil pattern (#567)
1. in normal code is better to always do if err != nil so that
the ifs only contain error code (this is ~coding policy)

2. in tests we want to ensure we narrow down the error to the
real error that happened, to have greater confidence

Written while working on https://github.com/ooni/probe/issues/1803#issuecomment-957323297
2021-11-02 19:48:10 +01:00
Simone Basso
374577f5a8
feat(filtering): implement HTTP filtering proxy (#565)
Needed to finish the design at https://github.com/ooni/probe/issues/1803#issuecomment-957323297
2021-11-02 14:13:54 +01:00
Simone Basso
a6f5388bac
fix(filtering/dns.go): serve requests in parallel (#564)
Without this change, it takes too much to serve a single query and
we cannot properly use this code for QA.

See https://github.com/ooni/probe/issues/1803#issuecomment-957323297
2021-11-02 12:40:18 +01:00
Simone Basso
d9c43f1d1b
refactor(netxlite/filtering): use strings for blocking policies (#563)
This change will simplify follow-up work done as part of
https://github.com/ooni/probe/issues/1803#issuecomment-957323297 to
implement a comprehensive self-censoring solution.

While there, rename the "proxy" action to "pass" because what we
are effectively doing is passing traffic to the network (that's a
minor change but it seems a better analogy).
2021-11-02 12:31:42 +01:00
Simone Basso
f91de2ecd6
cleanup: move bogon checking code in netxlite (#562)
I develop this diff while working on https://github.com/ooni/probe/issues/1803#issuecomment-957323297.

While there, make sure we don't have duplicate bogon code
and always use the code inside netxlite.
2021-11-02 12:20:04 +01:00
Simone Basso
ffdafaf351
feat(netxlite): implement hooks for transparent proxy (#561)
See https://github.com/ooni/probe/issues/1803#issuecomment-957323297.
2021-11-02 12:04:40 +01:00
Simone Basso
eaa3d6eae0
chore: bump version to 3.12.0-alpha.1 (#559)
Part of https://github.com/ooni/probe/issues/1815
2021-10-22 17:52:48 +02:00
Simone Basso
1f0fcafb8a
fix: ensure we bind oohelperd with the repo's version number (#558)
Work related to https://github.com/ooni/probe/issues/1506#issuecomment-949715707.

This diff cherry-picks from the release/3.11 branch.
2021-10-22 17:21:38 +02:00
Srijan Srivastava
3f0da81bf8
made tutorial outputs more presentable (#555)
Closes https://github.com/ooni/probe/issues/1831
2021-10-22 16:17:57 +02:00
Simone Basso
d44970a43f
master: update deps and set version to 3.12.0-alpha (#548)
* chore: update all workflows to use go1.17.2

See https://github.com/ooni/probe/issues/1815

* chore: update all dependencies

See https://github.com/ooni/probe/issues/1815

* chore: run `go generate`

See https://github.com/ooni/probe/issues/1815

* chore: update the user-agent

Part of https://github.com/ooni/probe/issues/1815

* Set version to 3.12.0-alpha

Part of https://github.com/ooni/probe/issues/1815

* fix: update to ooni/probe-assets@v0.5.0

This overcomes https://github.com/ooni/probe/issues/1836 in the CLI and,
while there, let us also make maxminddb tests stricter.

* fix(QA/Dockerfile): build using go1.17

See https://github.com/ooni/probe-cli/pull/547#issuecomment-947760839

* chore(mk): use go1.17.2

Part of https://github.com/ooni/probe/issues/1815

* fix(codeql): always run for master

Otherwise we see a warning that there is no CodeQL information
available for the base branch and this is sub-optimal.

Part of https://github.com/ooni/probe/issues/1815
2021-10-20 18:16:40 +02:00
Simone Basso
2fa87f0e00
fix(wcth): match legacy TH w/ empty DNS reply (#546)
See https://github.com/ooni/probe/issues/1707#issuecomment-944322725
2021-10-15 16:20:07 +02:00
Simone Basso
2d1666b88b
fix(wcth): emit empty Addrs when input URL contains addr (#545)
Matches the behavior that the legacy TH implements in this
situation and reduces slightly the differences.

See https://github.com/ooni/probe/issues/1707#issuecomment-944143329
2021-10-15 12:00:20 +02:00
Simone Basso
4b8cae692b
fix(oohelperd): reduce errors to what the old TH would emit (#543)
Reducing the errors is not done in a perfect way.

We have documented the most striking differences inside
https://github.com/ooni/probe/issues/1707#issuecomment-942283746 and
some attempts to improve the situation further inside
https://github.com/ooni/probe/issues/1707#issuecomment-942341255.

A better strategy for the future would be to introduce more
specific timeout errors, such as dns_timeout_error, etc.

More testing may be needed to further validate and compare the
old and the new TH, but this requires Jafar improvements to
more precisely simulate more complex censorship.
2021-10-13 16:37:02 +02:00
Simone Basso
299834174a
fix(webconnectivity): gather longer HTML titles (#542)
Allows us to get http://www.isa.gov.il/Pages/default.aspx's one.

Discovered when working on https://github.com/ooni/probe/issues/1707.
2021-10-13 13:50:22 +02:00
Simone Basso
9e365661f0
fix(oohelperd): return HTTP headers as empty map on error (#541)
Part of https://github.com/ooni/probe/issues/1707
2021-10-13 13:27:09 +02:00
Simone Basso
43c349fe38
feat: annotate measurements with their architecture (#540)
Closes https://github.com/ooni/probe/issues/1772
2021-10-13 10:31:46 +02:00
Arturo Filastò
46e00888ae
Edits to the measurex tutorial (#534) 2021-10-11 17:48:45 +02:00
Simone Basso
1ffb2db0ca
fix(measurex): quic follow-up only with https scheme (#538)
Closes https://github.com/ooni/probe/issues/1799
2021-10-05 13:02:56 +02:00
Simone Basso
a6318d6de9
feat(webconnectivity): collect timing information (#537)
Work related to https://github.com/ooni/probe/issues/1797
2021-10-05 12:52:19 +02:00
Simone Basso
3f511d100b
fix(websteps): use correct input for each URL measurement (#536)
Closes https://github.com/ooni/probe/issues/1798
2021-10-05 12:29:00 +02:00
Simone Basso
9b52a050ba
fix: add missing reference to netxlite's tutorial (#535)
See https://github.com/ooni/probe-cli/pull/506
2021-10-04 15:37:48 +02:00
Simone Basso
762fb27c0b
fix(netxlite/quic): close udp conn after failed handshake (#533)
Closes https://github.com/ooni/probe/issues/1794
2021-09-30 18:00:38 +02:00
Simone Basso
86018ecb82
fix(measurex): make sure we don't redirect loop forever (#532)
This is the most immediate fix to the issue described by
https://github.com/ooni/probe/issues/1792.

So, the logic was actually miss the increment, which
would have been noticed with proper unit testing.

Anyway, I am not sure why the loop ensues in the first
time. By looking at the headers, it seems we're passing
the headers correctly.

So, even though this fix interrupts the loop, it still
remains the question of whether the loop is legit or
whether we're missing extra logic to properly redirect.
2021-09-30 14:07:25 +02:00
Simone Basso
ba9151d4fa
feat(webstepsx): websteps using measurex (#530)
This diff adds the prototype websteps implementation that used
to live at https://github.com/ooni/probe-cli/pull/506.

The code is reasonably good already and it's pointing to a roaming
test helper that I've properly configured.

You can run websteps with:

```
./miniooni -n websteps
```

This will go over the test list for your country.

At this stage the mechanics of the experiment is set, but we
still need to have a conversation on the following topics:

1. whether we're okay with reusing the data format used by other
OONI experiments, or we would like to use a more compact data
format (which may either be a more compact JSON or we can choose
to always submit compressed measurements for websteps);

2. the extent to which we would like to keep the measurement as
a collection of "the experiment saw this" and "the test helper
saw that" and let the pipeline choose an overall score: this is
clearly an option, but there is also the opposite option to
build a summary of the measurement on the probe.

Compared to the previous prototype of websteps, the main
architectural change we have here is that we are following
the point of view of the probe and the test helper is
much more dumb. Basically, the probe will choose which
redirection to follow and ask the test helper every time
it discovers a new URL to measure it w/o redirections.

Reference issue: https://github.com/ooni/probe/issues/1733
2021-09-30 02:06:27 +02:00
Simone Basso
d45e58c14f
doc(measurex): explain how to write experiments (#529)
Part of https://github.com/ooni/ooni.org/issues/361

Co-authored-by: Arturo Filastò <arturo@openobservatory.org>
2021-09-30 01:36:03 +02:00
Simone Basso
399d2f65da
feat(measurex): refactored measurement library (#528)
This commit introduce a measurement library that consists of
refactored code from earlier websteps experiments.

I am not going to add tests for the time being, because this library
is still a bit in flux, as we finalize websteps.

I will soon though commit documentation explaining in detail how
to use it, which currrently is at https://github.com/ooni/probe-cli/pull/506
and adds a new directory to internal/tutorial.

The core idea of this measurement library is to allow two
measurement modes:

1. tracing, which is what we're currently doing now, and the
tutorial shows how we can rewrite the measurement part of web
connectivity with measurex using less code. Under a tracing
approach, we construct a normal http.Client that however has
tracing configured, we gather events for resolve, connect, TLS
handshake, QUIC handshake, HTTP round trip, etc. and then we
try to make sense of what happened from the events stream;

2. step-by-step, which is what websteps does, and basically
means that after each operation you immediately write into
a Measurement structure its results and immediately draw the
conclusions on what seems odd (which later may become an
anomaly if we see what the test helper measured).

This library is also such that it produces a data format
compatible with the current OONI spec.

This work is part of https://github.com/ooni/probe/issues/1733.
2021-09-30 01:24:08 +02:00
Simone Basso
ff1c170562
feat(engine): allow runner to return many measurements (#527)
This is required to implement websteps, which is currently tracked
by https://github.com/ooni/probe/issues/1733.

We introduce the concept of async runner. An async runner will
post measurements on a channel until it is done. When it is done,
it will close the channel to notify the reader about that.

This change causes sync experiments now to strictly return either
a non-nil measurement or a non-nil error.

While this is a pretty much obvious situation in golang, we had
some parts of the codebase that were not robust to this assumption
and attempted to submit a measurement after the measure call
returned an error.

Luckily, we had enough tests to catch this change in our assumption
and this is why there are extra docs and tests changes.
2021-09-30 00:54:52 +02:00
Simone Basso
8931a36cb3
fix(netxlite): make test less flaky on macOS (#526)
The explanatory comment in the diff says it all.

Work done while I was converging with https://github.com/ooni/probe/issues/1733.
2021-09-30 00:25:05 +02:00
Simone Basso
24a6ac9a5f
fix(ooapi): disable tests we're not using (#525)
At the moment ooapi is not used. It will eventually be used since
it's a better way of accessing the OONI backend API.

To fix these tests, we need to fix the swagger emitted by the
backend API, which is not a priority at the moment, since we are
working instead to integrate websteps in miniooni.

Issue https://github.com/ooni/probe/issues/1790 tracks the work
required to re-enabled the tests I'm skipping with this diff.

This work is part of https://github.com/ooni/probe/issues/1733.
2021-09-30 00:04:11 +02:00
Simone Basso
26f84ccc20
fix(resolver_test.go): remove race and make deterministic (#524)
Should fix https://github.com/ooni/probe-cli/pull/523#issuecomment-930430124.

Work part of https://github.com/ooni/probe-cli/pull/506.
2021-09-29 20:33:39 +02:00
Simone Basso
b2b1a4b2f1
doc(netxlite): revamp the documentation (#523)
Part of https://github.com/ooni/probe-cli/pull/506. In parallel with
tutorials, we also need to make sure we have good documentation.
2021-09-29 20:21:25 +02:00
Simone Basso
b9a844ecee
feat: run ~always netxlite integration tests (#522)
* feat: run ~always netxlite integration tests

This diff ensures that we check on windows, linux, macos that our
fundamental networking library (netxlite) works.

We combine unit and integration tests.

This work is part of https://github.com/ooni/probe/issues/1733, where
I want to have more strong guarantees about the foundations.

* fix(filtering/tls_test.go): make portable on Windows

The trick here is to use the wrapped error so to normalize the
different errors messages we see on Windows.

* fix(netxlite/quic_test.go): make portable on windows

Rather than using the zero port, use the `x` port which fails
when the stdlib is parsing the address.

The zero port seems to work on Windows while it does not on Unix.

* fix(serialresolver_test.go): make error more timeout than before

This seems enough to convince Go on Windows about this error
being really a timeout timeouty timeouted thingie.
2021-09-29 16:04:26 +02:00
Simone Basso
9967803c31
fix(netxlite): map additional GetAddrInfoW errors (#521)
On Windows, GetAddrInfoW is a syscall and the Go resolver does
not attempt to map errors beyond WSA_HOST_NOT_FOUND, which becomes
"no such host", which we map to "dns_nxdomain_error".

See https://github.com/golang/go/blob/go1.17.1/src/net/lookup_windows.go#L16.

To map more GetAddrInfoW errors, thus, we need to enhance our
error classifier to have system specific errors.

Then, we need to filter for the WSA errors that are most likely
to pop up and map them to OONI failures. Those are three:

- WSANO_DATA which we have from our own UDP resolver as well
and which we can map to `dns_no_answer`

- WSANO_RECOVERY which we don't have but existed for MK so
we will use `dns_non_recoverable_failure`, which was an MK error

- WSATRY_AGAIN which likewise we map to the error that MK
used to emit, so `dns_temporary_failure`

This diff should address https://github.com/ooni/probe/issues/1467.
2021-09-29 11:21:28 +02:00
Simone Basso
9523753b87
fix: allow to build miniooni for windows (#520)
I need to run test on Windows and I just discovered that:

1. the `errno_unix.go` filename does not mean anything because
`unix` is not a valid platform, so we need a filename for
each platform that we care about;

2. on Windows we need to use WSA prefixed names;

3. `i/e/session_psiphon.go` was not building because of the
migration from `netxlite/iox` to `netxlite`.

This diff attempts to fix all three issues.

The reference issue is https://github.com/ooni/probe/issues/1733,
because I was working on such an issue.
2021-09-28 18:27:28 +02:00
Simone Basso
9ef4d9df7d
doc: add tutorial on how to use netxlite (#519)
The main tutorial will be the one at https://github.com/ooni/probe-cli/pull/506, but
it's useful to also document the primitives used by measurex.

So, here's the companion tutorial, which explains how to use the
features in netxlite to perform measurements.

This work is part of https://github.com/ooni/ooni.org/issues/361.
2021-09-28 18:15:38 +02:00
Simone Basso
6d3a4f1db8
refactor: merge dnsx and errorsx into netxlite (#517)
When preparing a tutorial for netxlite, I figured it is easier
to tell people "hey, this is the package you should use for all
low-level networking stuff" rather than introducing people to
a set of packages working together where some piece of functionality
is here and some other piece is there.

Part of https://github.com/ooni/probe/issues/1591
2021-09-28 12:42:01 +02:00
Simone Basso
de130d249c
refactor(dnsx): group tests together (#516)
Part of https://github.com/ooni/probe/issues/1591
2021-09-28 11:26:16 +02:00
Simone Basso
12cf4b9990
refactor(dnsx): prepare for merging with netxlite (#515)
Part of https://github.com/ooni/probe/issues/1591
2021-09-28 10:47:59 +02:00
Simone Basso
8b9fe1a160
feat(netxlite): implement LookupHTTPS (#514)
This new API call performs DNS lookups for HTTPS records.

Part of https://github.com/ooni/probe/issues/1733 and diff has been
extracted from https://github.com/ooni/probe-cli/pull/506.
2021-09-27 23:09:41 +02:00
Simone Basso
24b230fd38
fix(netxlite/dns): more stricly mirror stdlib error strings (#513)
This diff attempts to modify the errors reported by our custom
resolver by matching more strings from the stdlib.

Part of https://github.com/ooni/probe/issues/1733 and diff has been
extracted from https://github.com/ooni/probe-cli/pull/506.
2021-09-27 16:48:46 +02:00
Simone Basso
201f602a40
feat(errorsx): introduce NewTopLevelGenericErrWrapper (#511)
Part of https://github.com/ooni/probe/issues/1733 and diff has been
extracted from https://github.com/ooni/probe-cli/pull/506.
2021-09-27 14:55:47 +02:00
Simone Basso
d7b9c8f0a8
fix(netxlite/errorsx): map "use of closed network connection" (#510)
Part of https://github.com/ooni/probe/issues/1733 and diff has been
extracted from https://github.com/ooni/probe-cli/pull/506.
2021-09-27 14:40:25 +02:00
Simone Basso
273774bb03
fix(netxlite/quic): wrap Close (#509)
While there, make sure netxlite has 100% coverage.

Part of https://github.com/ooni/probe/issues/1733 and diff
has been extracted from https://github.com/ooni/probe-cli/pull/506.
2021-09-27 14:14:17 +02:00
Simone Basso
deb1589bdb
fix(netxlite): do not mutate outgoing requests (#508)
I have recently seen a data race related our way of
mutating the outgoing request to set the host header.

Unfortunately, I've lost track of the race output,
because I rebooted my Linux box before saving it.

Though, after inspecting why and and where we're mutating
outgoing requets, I've found that:

1. we add the host header when logging to have it logged,
which is not a big deal since we already emit the URL
rather than just the URL path when logging a request, and
so we can safely zap this piece of code;

2. as a result, in measurements we may omit the host header
but again this is pretty much obvious from the URL itself
and so it should not be very important (nonetheless,
avoid surprises and keep the existing behavior);

3. when the User-Agent header is not set, we default to
a `miniooni/0.1.0-dev` user agent, which is probably not
very useful anyway, so we can actually remove it.

Part of https://github.com/ooni/probe/issues/1733 (this diff
has been extracted from https://github.com/ooni/probe-cli/pull/506).
2021-09-27 13:35:47 +02:00
Simone Basso
741a8bc4c2
feat(netxlite): introduce wrapping constructors (#507)
This diff has been extracted from https://github.com/ooni/probe-cli/pull/506.

In it, we introduce wrapping constructors for types and we
update the docs. These new constructures are used by the code
in https://github.com/ooni/probe-cli/pull/506.

In itself, this work is part of https://github.com/ooni/probe/issues/1733.
2021-09-27 12:00:43 +02:00
Simone Basso
985c1ba761
fix(oohelperd): reduce differences with legacy helper (#504)
Part of https://github.com/ooni/probe/issues/1707
2021-09-27 08:13:30 +02:00
Simone Basso
3cb782f0a2
refactor(netx): move dns transports in netxlite/dnsx (#503)
While there, modernize the way in which we run tests to avoid
depending on the fake files scattered around the tree and to
use some well defined mock structures instead.

Part of https://github.com/ooni/probe/issues/1591
2021-09-09 21:24:27 +02:00
Simone Basso
b3c36b5c7f
refactor(resolver): add CloseIdleConnections to SerialResolver (#502)
While there, generally convert more code to internal testing
and to using pointer receivers as well.

Part of https://github.com/ooni/probe/issues/1591.
2021-09-09 20:58:04 +02:00
Simone Basso
1eb9e8c9b0
refactor(netx/resolver): add CloseIdleConnections to RoundTripper (#501)
While there, also change to pointer receiver and use internal
testing for what are clearly unit tests.

Part of https://github.com/ooni/probe/issues/1591.
2021-09-09 20:49:12 +02:00
Simone Basso
5ab3c3b689
refactor(netx): use netxlite for AddressResolver (#500)
Part of https://github.com/ooni/probe/issues/1591.
2021-09-09 20:21:43 +02:00
Simone Basso
254a5511e9
doc(oohelper): tweak documentation (#499)
Part of https://github.com/ooni/probe/issues/1733
2021-09-09 19:55:23 +02:00
Simone Basso
c54cc73afa
refactor(oohelper): remove unnecessary dep from netx (#498)
There are a bunch of packages where we don't really need to depend
on netx but we can use local definitions that describe what we are
expecting from data structures we receive in input. This diff
addresses one of such cases.

Part of https://github.com/ooni/probe/issues/1591
2021-09-09 19:40:03 +02:00
Simone Basso
b5826a0c44
refactor(jafar): remove unnecessary dep on netx (#497) 2021-09-09 18:01:20 +02:00