Include a user agent in federation requests. (#7677)

This commit is contained in:
Patrick Cloke 2020-06-16 10:43:29 -04:00 committed by GitHub
parent a3f11567d9
commit ac51bd581a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 43 additions and 4 deletions

1
changelog.d/7677.bugfix Normal file
View File

@ -0,0 +1 @@
Include a user-agent for federation and well-known requests.

View File

@ -48,6 +48,9 @@ class MatrixFederationAgent(object):
tls_client_options_factory (FederationPolicyForHTTPS|None): tls_client_options_factory (FederationPolicyForHTTPS|None):
factory to use for fetching client tls options, or none to disable TLS. factory to use for fetching client tls options, or none to disable TLS.
user_agent (bytes):
The user agent header to use for federation requests.
_srv_resolver (SrvResolver|None): _srv_resolver (SrvResolver|None):
SRVResolver impl to use for looking up SRV records. None to use a default SRVResolver impl to use for looking up SRV records. None to use a default
implementation. implementation.
@ -61,6 +64,7 @@ class MatrixFederationAgent(object):
self, self,
reactor, reactor,
tls_client_options_factory, tls_client_options_factory,
user_agent,
_srv_resolver=None, _srv_resolver=None,
_well_known_resolver=None, _well_known_resolver=None,
): ):
@ -78,6 +82,7 @@ class MatrixFederationAgent(object):
), ),
pool=self._pool, pool=self._pool,
) )
self.user_agent = user_agent
if _well_known_resolver is None: if _well_known_resolver is None:
_well_known_resolver = WellKnownResolver( _well_known_resolver = WellKnownResolver(
@ -87,6 +92,7 @@ class MatrixFederationAgent(object):
pool=self._pool, pool=self._pool,
contextFactory=tls_client_options_factory, contextFactory=tls_client_options_factory,
), ),
user_agent=self.user_agent,
) )
self._well_known_resolver = _well_known_resolver self._well_known_resolver = _well_known_resolver
@ -149,7 +155,7 @@ class MatrixFederationAgent(object):
parsed_uri = urllib.parse.urlparse(uri) parsed_uri = urllib.parse.urlparse(uri)
# We need to make sure the host header is set to the netloc of the # We need to make sure the host header is set to the netloc of the
# server. # server and that a user-agent is provided.
if headers is None: if headers is None:
headers = Headers() headers = Headers()
else: else:
@ -157,6 +163,8 @@ class MatrixFederationAgent(object):
if not headers.hasHeader(b"host"): if not headers.hasHeader(b"host"):
headers.addRawHeader(b"host", parsed_uri.netloc) headers.addRawHeader(b"host", parsed_uri.netloc)
if not headers.hasHeader(b"user-agent"):
headers.addRawHeader(b"user-agent", self.user_agent)
res = yield make_deferred_yieldable( res = yield make_deferred_yieldable(
self._agent.request(method, uri, headers, bodyProducer) self._agent.request(method, uri, headers, bodyProducer)

View File

@ -23,6 +23,7 @@ import attr
from twisted.internet import defer from twisted.internet import defer
from twisted.web.client import RedirectAgent, readBody from twisted.web.client import RedirectAgent, readBody
from twisted.web.http import stringToDatetime from twisted.web.http import stringToDatetime
from twisted.web.http_headers import Headers
from synapse.logging.context import make_deferred_yieldable from synapse.logging.context import make_deferred_yieldable
from synapse.util import Clock from synapse.util import Clock
@ -78,7 +79,12 @@ class WellKnownResolver(object):
""" """
def __init__( def __init__(
self, reactor, agent, well_known_cache=None, had_well_known_cache=None self,
reactor,
agent,
user_agent,
well_known_cache=None,
had_well_known_cache=None,
): ):
self._reactor = reactor self._reactor = reactor
self._clock = Clock(reactor) self._clock = Clock(reactor)
@ -92,6 +98,7 @@ class WellKnownResolver(object):
self._well_known_cache = well_known_cache self._well_known_cache = well_known_cache
self._had_valid_well_known_cache = had_well_known_cache self._had_valid_well_known_cache = had_well_known_cache
self._well_known_agent = RedirectAgent(agent) self._well_known_agent = RedirectAgent(agent)
self.user_agent = user_agent
@defer.inlineCallbacks @defer.inlineCallbacks
def get_well_known(self, server_name): def get_well_known(self, server_name):
@ -227,6 +234,10 @@ class WellKnownResolver(object):
uri = b"https://%s/.well-known/matrix/server" % (server_name,) uri = b"https://%s/.well-known/matrix/server" % (server_name,)
uri_str = uri.decode("ascii") uri_str = uri.decode("ascii")
headers = {
b"User-Agent": [self.user_agent],
}
i = 0 i = 0
while True: while True:
i += 1 i += 1
@ -234,7 +245,9 @@ class WellKnownResolver(object):
logger.info("Fetching %s", uri_str) logger.info("Fetching %s", uri_str)
try: try:
response = yield make_deferred_yieldable( response = yield make_deferred_yieldable(
self._well_known_agent.request(b"GET", uri) self._well_known_agent.request(
b"GET", uri, headers=Headers(headers)
)
) )
body = yield make_deferred_yieldable(readBody(response)) body = yield make_deferred_yieldable(readBody(response))

View File

@ -197,7 +197,14 @@ class MatrixFederationHttpClient(object):
self.reactor = Reactor() self.reactor = Reactor()
self.agent = MatrixFederationAgent(self.reactor, tls_client_options_factory) user_agent = hs.version_string
if hs.config.user_agent_suffix:
user_agent = "%s %s" % (user_agent, hs.config.user_agent_suffix)
user_agent = user_agent.encode("ascii")
self.agent = MatrixFederationAgent(
self.reactor, tls_client_options_factory, user_agent
)
# Use a BlacklistingAgentWrapper to prevent circumventing the IP # Use a BlacklistingAgentWrapper to prevent circumventing the IP
# blacklist via IP literals in server names # blacklist via IP literals in server names

View File

@ -86,6 +86,7 @@ class MatrixFederationAgentTests(unittest.TestCase):
self.well_known_resolver = WellKnownResolver( self.well_known_resolver = WellKnownResolver(
self.reactor, self.reactor,
Agent(self.reactor, contextFactory=self.tls_factory), Agent(self.reactor, contextFactory=self.tls_factory),
b"test-agent",
well_known_cache=self.well_known_cache, well_known_cache=self.well_known_cache,
had_well_known_cache=self.had_well_known_cache, had_well_known_cache=self.had_well_known_cache,
) )
@ -93,6 +94,7 @@ class MatrixFederationAgentTests(unittest.TestCase):
self.agent = MatrixFederationAgent( self.agent = MatrixFederationAgent(
reactor=self.reactor, reactor=self.reactor,
tls_client_options_factory=self.tls_factory, tls_client_options_factory=self.tls_factory,
user_agent="test-agent", # Note that this is unused since _well_known_resolver is provided.
_srv_resolver=self.mock_resolver, _srv_resolver=self.mock_resolver,
_well_known_resolver=self.well_known_resolver, _well_known_resolver=self.well_known_resolver,
) )
@ -186,6 +188,9 @@ class MatrixFederationAgentTests(unittest.TestCase):
# check the .well-known request and send a response # check the .well-known request and send a response
self.assertEqual(len(well_known_server.requests), 1) self.assertEqual(len(well_known_server.requests), 1)
request = well_known_server.requests[0] request = well_known_server.requests[0]
self.assertEqual(
request.requestHeaders.getRawHeaders(b"user-agent"), [b"test-agent"]
)
self._send_well_known_response(request, content, headers=response_headers) self._send_well_known_response(request, content, headers=response_headers)
return well_known_server return well_known_server
@ -231,6 +236,9 @@ class MatrixFederationAgentTests(unittest.TestCase):
self.assertEqual( self.assertEqual(
request.requestHeaders.getRawHeaders(b"host"), [b"testserv:8448"] request.requestHeaders.getRawHeaders(b"host"), [b"testserv:8448"]
) )
self.assertEqual(
request.requestHeaders.getRawHeaders(b"user-agent"), [b"test-agent"]
)
content = request.content.read() content = request.content.read()
self.assertEqual(content, b"") self.assertEqual(content, b"")
@ -719,10 +727,12 @@ class MatrixFederationAgentTests(unittest.TestCase):
agent = MatrixFederationAgent( agent = MatrixFederationAgent(
reactor=self.reactor, reactor=self.reactor,
tls_client_options_factory=tls_factory, tls_client_options_factory=tls_factory,
user_agent=b"test-agent", # This is unused since _well_known_resolver is passed below.
_srv_resolver=self.mock_resolver, _srv_resolver=self.mock_resolver,
_well_known_resolver=WellKnownResolver( _well_known_resolver=WellKnownResolver(
self.reactor, self.reactor,
Agent(self.reactor, contextFactory=tls_factory), Agent(self.reactor, contextFactory=tls_factory),
b"test-agent",
well_known_cache=self.well_known_cache, well_known_cache=self.well_known_cache,
had_well_known_cache=self.had_well_known_cache, had_well_known_cache=self.had_well_known_cache,
), ),