Skip to content

Commit b330e27

Browse files
committed
client/Main: add option --resolve-forwarded-to
1 parent 72952d4 commit b330e27

10 files changed

+258
-7
lines changed

debian/changelog

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cm4all-pond (0.33) unstable; urgency=low
22

3-
*
3+
* client: add option --resolve-forwarded-to
44

55
--
66

doc/index.rst

+4
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ The following command-line options are available:
224224

225225
Show the address of the server each request was forwarded to.
226226

227+
.. option:: --resolve-forwarded-to
228+
229+
Show the name of the server each request was forwarded to.
230+
227231
.. option:: --no-referer
228232

229233
Do not show the HTTP ``Referer`` request header.

meson.build

+10
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ executable('cm4all-pond',
173173
install: true,
174174
install_dir: 'sbin')
175175

176+
client_sources = []
177+
178+
if avahi_dep.found()
179+
client_sources += [
180+
'src/client/SimpleAddressResolver.cxx',
181+
'src/client/CachedAddressResolver.cxx',
182+
]
183+
endif
184+
176185
executable('cm4all-pond-client',
177186
'src/client/Main.cxx',
178187
'src/client/ResultWriter.cxx',
@@ -182,6 +191,7 @@ executable('cm4all-pond-client',
182191
'src/client/Client.cxx',
183192
'src/client/Send.cxx',
184193
'src/client/Open.cxx',
194+
client_sources,
185195
include_directories: inc,
186196
dependencies: [
187197
libgeoip,

src/client/CachedAddressResolver.cxx

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// SPDX-License-Identifier: BSD-2-Clause
2+
// Copyright CM4all GmbH
3+
// author: Max Kellermann <[email protected]>
4+
5+
#include "CachedAddressResolver.hxx"
6+
7+
const char *
8+
CachedAddressResolver::ResolveAddress(std::string address) noexcept
9+
{
10+
auto i = cache.find(address);
11+
if (i == cache.end()) {
12+
auto name = simple.ResolveAddress(address.c_str());
13+
i = cache.emplace(std::move(address), std::move(name)).first;
14+
}
15+
16+
return i->second.empty() ? nullptr : i->second.c_str();
17+
}

src/client/CachedAddressResolver.hxx

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: BSD-2-Clause
2+
// Copyright CM4all GmbH
3+
// author: Max Kellermann <[email protected]>
4+
5+
#pragma once
6+
7+
#include "SimpleAddressResolver.hxx"
8+
9+
#include <unordered_map>
10+
11+
/**
12+
* A caching wrapper for #SimpleAddressResolver.
13+
*/
14+
class CachedAddressResolver final {
15+
SimpleAddressResolver simple;
16+
17+
std::unordered_map<std::string, std::string> cache;
18+
19+
public:
20+
[[gnu::pure]]
21+
const char *ResolveAddress(std::string address) noexcept;
22+
};

src/client/Main.cxx

+17-2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ struct QueryOptions {
7373
#endif
7474
bool track_visitors = false;
7575
bool per_site_nested = false;
76+
77+
#ifdef HAVE_AVAHI
78+
bool resolve_forwarded_to = false;
79+
#endif
7680
};
7781

7882
static void
@@ -211,9 +215,14 @@ ParseFilterItem(Filter &filter, PondGroupSitePayload &group_site,
211215
options.track_visitors = true;
212216
else if (StringIsEqual(p, "--host"))
213217
options.one_line.show_host = true;
214-
else if (StringIsEqual(p, "--forwarded-to"))
218+
else if (StringIsEqual(p, "--forwarded-to")) {
219+
options.one_line.show_forwarded_to = true;
220+
#ifdef HAVE_AVAHI
221+
} else if (StringIsEqual(p, "--resolve-forwarded-to")) {
215222
options.one_line.show_forwarded_to = true;
216-
else if (StringIsEqual(p, "--no-referer"))
223+
options.resolve_forwarded_to = true;
224+
#endif
225+
} else if (StringIsEqual(p, "--no-referer"))
217226
options.one_line.show_http_referer = false;
218227
else if (StringIsEqual(p, "--no-agent"))
219228
options.one_line.show_user_agent = false;
@@ -311,6 +320,9 @@ Query(const PondServerSpecification &server, ConstBuffer<const char *> args)
311320
geoip_v4, geoip_v6,
312321
#endif
313322
options.track_visitors,
323+
#ifdef HAVE_AVAHI
324+
options.resolve_forwarded_to,
325+
#endif
314326
options.one_line,
315327
options.jsonl,
316328
single_site,
@@ -575,6 +587,9 @@ try {
575587
" [--anonymize] [--track-visitors]\n"
576588
" [--per-site=PATH] [--per-site-file=FILENAME] [--per-site-nested]\n"
577589
" [--host] [--forwarded-to] [--no-referer] [--no-agent]\n"
590+
#ifdef HAVE_AVAHI
591+
" [--resolve-forwarded-to]\n"
592+
#endif
578593
" [--iso8601]\n"
579594
" [--jsonl]\n"
580595
" [type=http_access|http_error|submission]\n"

src/client/ResultWriter.cxx

+27-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
#include "util/Macros.hxx"
1919
#include "util/SpanCast.hxx"
2020

21+
#ifdef HAVE_AVAHI
22+
#include "net/HostParser.hxx"
23+
#include "util/StringSplit.hxx"
24+
#endif
25+
2126
#include <algorithm>
2227

2328
#include <fcntl.h>
@@ -113,6 +118,9 @@ ResultWriter::ResultWriter(bool _raw, bool _gzip,
113118
GeoIP *_geoip_v4, GeoIP *_geoip_v6,
114119
#endif
115120
bool _track_visitors,
121+
#ifdef HAVE_AVAHI
122+
bool _resolve_forwarded_to,
123+
#endif
116124
const Net::Log::OneLineOptions _one_line_options,
117125
bool _jsonl,
118126
bool _single_site,
@@ -126,6 +134,9 @@ ResultWriter::ResultWriter(bool _raw, bool _gzip,
126134
#endif
127135
per_site(_per_site, _per_site_filename, _per_site_nested),
128136
one_line_options(_one_line_options),
137+
#ifdef HAVE_AVAHI
138+
resolve_forwarded_to(_resolve_forwarded_to),
139+
#endif
129140
jsonl(_jsonl),
130141
raw(_raw), gzip(_gzip),
131142
track_visitors(_track_visitors)
@@ -174,8 +185,21 @@ ResultWriter::LookupGeoIP(const char *address) const noexcept
174185
#endif // HAVE_LIBGEOIP
175186

176187
void
177-
ResultWriter::Append(const Net::Log::Datagram &d)
188+
ResultWriter::Append(Net::Log::Datagram &&d)
178189
{
190+
#ifdef HAVE_AVAHI
191+
if (resolve_forwarded_to && d.forwarded_to != nullptr) {
192+
/* extract the IP address, stripping the port and
193+
square brackets (IPv6) */
194+
if (const auto e = ExtractHost(d.forwarded_to); !e.host.empty())
195+
/* remove the scope name (IPv6) because Avahi
196+
doesn't understand it and query the Avahi
197+
address resolver */
198+
if (const char *name = address_resolver.ResolveAddress(std::string{Split(e.host, '%').first}))
199+
d.forwarded_to = name;
200+
}
201+
#endif // HAVE_AVAHI
202+
179203
if (buffer_fill > sizeof(buffer) - 16384)
180204
FlushBuffer();
181205

@@ -229,7 +253,7 @@ void
229253
ResultWriter::Write(std::span<const std::byte> payload)
230254
{
231255
if (per_site.IsDefined()) {
232-
const auto d = Net::Log::ParseDatagram(payload);
256+
auto d = Net::Log::ParseDatagram(payload);
233257
if (d.site == nullptr)
234258
// TODO: where to log datagrams without a site?
235259
return;
@@ -285,7 +309,7 @@ ResultWriter::Write(std::span<const std::byte> payload)
285309
/* skip this site */
286310
return;
287311

288-
Append(d);
312+
Append(std::move(d));
289313
} else if (socket.IsDefined()) {
290314
/* if fd2 is a packet socket, send raw
291315
datagrams to it */

src/client/ResultWriter.hxx

+14-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
#include "net/log/OneLine.hxx"
1313
#include "config.h"
1414

15+
#ifdef HAVE_AVAHI
16+
#include "CachedAddressResolver.hxx"
17+
#endif
18+
1519
#ifdef HAVE_LIBGEOIP
1620
#include <GeoIP.h>
1721
#endif
@@ -51,6 +55,12 @@ class ResultWriter {
5155

5256
Net::Log::OneLineOptions one_line_options;
5357

58+
#ifdef HAVE_AVAHI
59+
CachedAddressResolver address_resolver;
60+
61+
const bool resolve_forwarded_to;
62+
#endif // HAVE_AVAHI
63+
5464
const bool jsonl;
5565

5666
const bool raw, gzip, track_visitors;
@@ -64,6 +74,9 @@ public:
6474
GeoIP *_geoip_v4, GeoIP *_geoip_v6,
6575
#endif
6676
bool _track_visitors,
77+
#ifdef HAVE_AVAHI
78+
bool _resolve_forwarded_to,
79+
#endif
6780
Net::Log::OneLineOptions _one_line_options,
6881
bool _jsonl,
6982
bool _single_site,
@@ -104,5 +117,5 @@ private:
104117
const char *LookupGeoIP(const char *address) const noexcept;
105118
#endif
106119

107-
void Append(const Net::Log::Datagram &d);
120+
void Append(Net::Log::Datagram &&d);
108121
};

src/client/SimpleAddressResolver.cxx

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: BSD-2-Clause
2+
// Copyright CM4all GmbH
3+
// author: Max Kellermann <[email protected]>
4+
5+
#include "SimpleAddressResolver.hxx"
6+
#include "util/PrintException.hxx"
7+
#include "util/ScopeExit.hxx"
8+
9+
#include <avahi-client/lookup.h>
10+
11+
struct SimpleAddressResolver::Data {
12+
EventLoop &event_loop;
13+
14+
std::string name;
15+
16+
void Callback(AvahiResolverEvent event,
17+
const char *_name) noexcept {
18+
if (event == AVAHI_RESOLVER_FOUND)
19+
name = _name;
20+
event_loop.Break();
21+
}
22+
23+
static void Callback([[maybe_unused]] AvahiAddressResolver *r,
24+
[[maybe_unused]] AvahiIfIndex interface,
25+
[[maybe_unused]] AvahiProtocol protocol,
26+
AvahiResolverEvent event,
27+
[[maybe_unused]] const AvahiAddress *a,
28+
const char *name,
29+
[[maybe_unused]] AvahiLookupResultFlags flags,
30+
void *userdata) noexcept {
31+
auto &data = *(Data *)userdata;
32+
data.Callback(event, name);
33+
}
34+
};
35+
36+
std::string
37+
SimpleAddressResolver::ResolveAddress(const char *address_string) noexcept
38+
{
39+
AvahiAddress address_buffer;
40+
41+
if (state == State::ERROR)
42+
return {};
43+
44+
const auto *const address = avahi_address_parse(address_string,
45+
AVAHI_PROTO_UNSPEC,
46+
&address_buffer);
47+
if (address == nullptr)
48+
return {};
49+
50+
if (state != State::READY) {
51+
/* wait until the Avahi client is connected */
52+
event_loop.Run();
53+
if (state != State::READY)
54+
return {};
55+
}
56+
57+
Data data{event_loop};
58+
auto *const resolver =
59+
avahi_address_resolver_new(avahi_client.GetClient(),
60+
AVAHI_IF_UNSPEC,
61+
AVAHI_PROTO_UNSPEC,
62+
//address->proto,
63+
address,
64+
AvahiLookupFlags{},
65+
Data::Callback, &data);
66+
AtScopeExit(resolver) { avahi_address_resolver_free(resolver); };
67+
68+
/* wait until the Avahi address resolver finishes */
69+
event_loop.Run();
70+
71+
return std::move(data.name);
72+
}
73+
74+
void
75+
SimpleAddressResolver::OnAvahiConnect([[maybe_unused]] AvahiClient *client) noexcept
76+
{
77+
state = State::READY;
78+
event_loop.Break();
79+
}
80+
81+
void
82+
SimpleAddressResolver::OnAvahiDisconnect() noexcept
83+
{
84+
state = State::ERROR;
85+
event_loop.Break();
86+
}
87+
88+
bool
89+
SimpleAddressResolver::OnAvahiError(std::exception_ptr e) noexcept
90+
{
91+
state = State::ERROR;
92+
PrintException(std::move(e));
93+
event_loop.Break();
94+
return true;
95+
}

src/client/SimpleAddressResolver.hxx

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-License-Identifier: BSD-2-Clause
2+
// Copyright CM4all GmbH
3+
// author: Max Kellermann <[email protected]>
4+
5+
#pragma once
6+
7+
#include "event/Loop.hxx"
8+
#include "lib/avahi/Client.hxx"
9+
#include "lib/avahi/ConnectionListener.hxx"
10+
#include "lib/avahi/ErrorHandler.hxx"
11+
12+
#include <string>
13+
14+
/**
15+
* A simple synchronous wrapper for the Avahi address resolver. An
16+
* EventLoop is used to execute the Avahi client, and completion
17+
* invokes EventLoop::Break(). This is a kludge to make the client
18+
* synchronous.
19+
*/
20+
class SimpleAddressResolver final : Avahi::ConnectionListener, Avahi::ErrorHandler {
21+
EventLoop event_loop;
22+
Avahi::Client avahi_client{event_loop, *this};
23+
24+
enum class State {
25+
INIT,
26+
READY,
27+
ERROR,
28+
} state = State::INIT;
29+
30+
struct Data;
31+
32+
public:
33+
SimpleAddressResolver() noexcept {
34+
avahi_client.AddListener(*this);
35+
}
36+
37+
~SimpleAddressResolver() noexcept {
38+
avahi_client.RemoveListener(*this);
39+
}
40+
41+
[[gnu::pure]]
42+
std::string ResolveAddress(const char *address) noexcept;
43+
44+
private:
45+
// virtual methods from class Avahi::ConnectionListener
46+
void OnAvahiConnect(AvahiClient *client) noexcept override;
47+
void OnAvahiDisconnect() noexcept override;
48+
49+
// virtual methods from class Avahi::ErrorHandler
50+
bool OnAvahiError(std::exception_ptr e) noexcept override;
51+
};

0 commit comments

Comments
 (0)