source: trunk/server/common/oursrc/whoisd/whoisd.tac @ 2615

Last change on this file since 2615 was 1742, checked in by mitchb, 14 years ago
Fix whoisd bug introduced in r1741 The code in r1741 would cause the message about having trouble looking up LDAP records to be returned for nonexistent host records as well as for actual LDAP problems. If we get an empty search result back, but don't experience an error, we're done and don't need to keep trying.
File size: 4.3 KB
RevLine 
[528]1from twisted.application import internet, service
2from twisted.internet import protocol, reactor, defer
3from twisted.protocols import basic
[627]4import ldap, ldap.filter
5import os, sys, pwd, glob
[528]6
7class WhoisProtocol(basic.LineReceiver):
8    def lineReceived(self, hostname):
[771]9        (key, hostname) = hostname.split('=',2)
[772]10        if key != self.factory.key:
[771]11            self.transport.write("Unauthorized to use whois"+"\r\n")
12            self.transport.loseConnection()
13        else:
14            self.factory.getWhois(hostname
15            ).addErrback(lambda _: "Internal error in server"
16            ).addCallback(lambda m:
17                          (self.transport.write(m+"\r\n"),
18                           self.transport.loseConnection()))
[528]19class WhoisFactory(protocol.ServerFactory):
20    protocol = WhoisProtocol
[772]21    def __init__(self, vhostDir, ldap_URL, ldap_base, keyFile):
[528]22        self.vhostDir = vhostDir
[627]23        self.ldap_URL = ldap_URL
24        self.ldap = ldap.initialize(self.ldap_URL)
25        self.ldap_base = ldap_base
[528]26        self.vhosts = {}
[762]27        if vhostDir:
28            self.rescanVhosts()
[772]29        self.key = file(keyFile).read()
[528]30    def rescanVhosts(self):
31        newVhosts = {}
32        for f in glob.iglob(os.path.join(self.vhostDir, "*.conf")):
33            locker = os.path.splitext(os.path.basename(f))[0]
34            newVhosts.update(self.parseApacheConf(file(f)))
35        self.vhosts = newVhosts
36        self.vhostTime = os.stat(self.vhostDir).st_mtime
37    def parseApacheConf(self, f):
38        vhosts = {}
39        hostnames = []
40        locker = None
41        docroot = None
42        for l in f:
43            parts = l.split()
44            if not parts: continue
45            command = parts.pop(0)
46            if command in ("ServerName", "ServerAlias"):
47                hostnames.extend(parts)
48            elif command in ("SuExecUserGroup",):
49                locker = parts[0]
50            elif command in ("DocumentRoot",):
51                docroot = parts[0]
52            elif command == "</VirtualHost>":
[627]53                d = {'locker': locker, 'apacheDocumentRoot': docroot, 'apacheServerName': hostnames[0]}
[528]54                for h in hostnames: vhosts[h] = d
55                hostnames = []
56                locker = None
57                docroot = None
58        return vhosts
59    def canonicalize(self, vhost):
60        vhost = vhost.lower().rstrip(".")
61        return vhost
62#        if vhost.endswith(".mit.edu"):
63#            return vhost
64#        else:
65#            return vhost + ".mit.edu"
[627]66    def searchLDAP(self, vhost):
[1741]67        results = self.ldap.search_st(self.ldap_base, ldap.SCOPE_SUBTREE,
[627]68            ldap.filter.filter_format(
[1741]69                '(|(apacheServername=%s)(apacheServerAlias=%s))', (vhost,)*2),
70                timeout=5)
[627]71        if len(results) >= 1:
72            result = results[0]
73            attrs = result[1]
74            for attr in ('apacheServerName','apacheDocumentRoot', 'apacheSuexecUid', 'apacheSuexecGid'):
75                attrs[attr] = attrs[attr][0]
76            user = pwd.getpwuid(int(attrs['apacheSuexecUid']))
77            if user:
[771]78                attrs['locker'] = user.pw_name
[627]79            else:
80                attrs['locker'] = None
81            return attrs
82        else:
83            return None
[528]84    def getWhois(self, vhost):
85        vhost = self.canonicalize(vhost)
86        info = self.vhosts.get(vhost)
[1741]87        tries = 0
88        while (tries < 3) and not info:
89            tries += 1
90            try:
91                info = self.searchLDAP(vhost)
[1742]92                break
[1741]93            except (ldap.TIMEOUT, ldap.SERVER_DOWN):
94                self.ldap.unbind()
95                self.ldap = ldap.initialize(self.ldap_URL)
[528]96        if info:
97            ret = "Hostname: %s\nAlias: %s\nLocker: %s\nDocument Root: %s" % \
[627]98                (info['apacheServerName'], vhost, info['locker'], info['apacheDocumentRoot'])
[1741]99        elif tries == 3:
100            ret = "The whois server is experiencing problems looking up LDAP records.\nPlease contact scripts@mit.edu for help if this problem persists."
[528]101        else:
102            ret = "No such hostname"
103        return defer.succeed(ret)
104
105application = service.Application('whois', uid=99, gid=99)
[762]106factory = WhoisFactory(None,
[772]107    "ldap://localhost", "ou=VirtualHosts,dc=scripts,dc=mit,dc=edu", "/etc/whoisd-password")
[528]108internet.TCPServer(43, factory).setServiceParent(
109    service.IServiceCollection(application))
Note: See TracBrowser for help on using the repository browser.