Index: django/contrib/sites/middleware.py
===================================================================
--- django/contrib/sites/middleware.py	(revision 0)
+++ django/contrib/sites/middleware.py	(revision 0)
@@ -0,0 +1,11 @@
+class LazySite(object):
+    def __get__(self, request, obj_type=None):
+        if not hasattr(request, '_cached_site'):
+            from django.contrib.sites.models import Site
+            request._cached_site = Site.get_from_host(request)
+        return request._cached_site
+
+class CurrentSiteMiddleware(object):
+    def process_request(self, request):
+        request.__class__.site = LazySite()
+        return None
Index: django/contrib/sites/models.py
===================================================================
--- django/contrib/sites/models.py	(revision 6365)
+++ django/contrib/sites/models.py	(working copy)
@@ -6,9 +6,9 @@
 class SiteManager(models.Manager):
     def get_current(self):
         """
-        Returns the current ``Site`` based on the SITE_ID in the
-        project's settings. The ``Site`` object is cached the first
-        time it's retrieved from the database.
+        Returns the current ``Site`` based on the SITE_ID in the project's
+        settings. The ``Site`` object is cached the first time it's retrieved
+        from the database.
         """
         from django.conf import settings
         try:
@@ -28,6 +28,40 @@
         global SITE_CACHE
         SITE_CACHE = {}
 
+    def get_from_host(self, request, check_subdomain=True):
+        """
+        Returns the ``Site`` which matches the host name retreived from
+        ``request``.
+
+        If no match is found and ``check_subdomain`` is ``True``, the sites are
+        searched again for sub-domain matches.
+
+        If still no match, or if more than one ``Site`` matched the host name, a
+        ``RequestSite`` object is returned.
+
+        The returned ``Site`` or ``RequestSite`` object is cached for the host
+        name retrieved from ``request``.
+        """ 
+        host = request.get_host().lower()
+        if host in SITE_CACHE:
+            # The host name was found in cache, return it
+            return SITE_CACHE[host]
+        matches = Site.objects.filter(domain__iexact=host)
+        # We use len rather than count to save a second query if there was only
+        # one matching Site
+        count = len(matches)
+        if not count and check_subdomain:
+            matches = []
+            for site in Site.objects.all():
+                if host.endswith(site.domain.lower()):
+                    matches.append(site)
+            count = len(matches)
+        if count == 1:
+            # Return the single matching Site
+            return matches[0]
+        # Fall back to just using a RequestSite
+        return RequestSite(request)
+
 class Site(models.Model):
     domain = models.CharField(_('domain name'), max_length=100)
     name = models.CharField(_('display name'), max_length=50)
