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

Last change on this file since 2165 was 1742, checked in by mitchb, 12 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
Line 
1from twisted.application import internet, service
2from twisted.internet import protocol, reactor, defer
3from twisted.protocols import basic
4import ldap, ldap.filter
5import os, sys, pwd, glob
6
7class WhoisProtocol(basic.LineReceiver):
8    def lineReceived(self, hostname):
9        (key, hostname) = hostname.split('=',2)
10        if key != self.factory.key:
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()))
19class WhoisFactory(protocol.ServerFactory):
20    protocol = WhoisProtocol
21    def __init__(self, vhostDir, ldap_URL, ldap_base, keyFile):
22        self.vhostDir = vhostDir
23        self.ldap_URL = ldap_URL
24        self.ldap = ldap.initialize(self.ldap_URL)
25        self.ldap_base = ldap_base
26        self.vhosts = {}
27        if vhostDir:
28            self.rescanVhosts()
29        self.key = file(keyFile).read()
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>":
53                d = {'locker': locker, 'apacheDocumentRoot': docroot, 'apacheServerName': hostnames[0]}
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"
66    def searchLDAP(self, vhost):
67        results = self.ldap.search_st(self.ldap_base, ldap.SCOPE_SUBTREE,
68            ldap.filter.filter_format(
69                '(|(apacheServername=%s)(apacheServerAlias=%s))', (vhost,)*2),
70                timeout=5)
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:
78                attrs['locker'] = user.pw_name
79            else:
80                attrs['locker'] = None
81            return attrs
82        else:
83            return None
84    def getWhois(self, vhost):
85        vhost = self.canonicalize(vhost)
86        info = self.vhosts.get(vhost)
87        tries = 0
88        while (tries < 3) and not info:
89            tries += 1
90            try:
91                info = self.searchLDAP(vhost)
92                break
93            except (ldap.TIMEOUT, ldap.SERVER_DOWN):
94                self.ldap.unbind()
95                self.ldap = ldap.initialize(self.ldap_URL)
96        if info:
97            ret = "Hostname: %s\nAlias: %s\nLocker: %s\nDocument Root: %s" % \
98                (info['apacheServerName'], vhost, info['locker'], info['apacheDocumentRoot'])
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."
101        else:
102            ret = "No such hostname"
103        return defer.succeed(ret)
104
105application = service.Application('whois', uid=99, gid=99)
106factory = WhoisFactory(None,
107    "ldap://localhost", "ou=VirtualHosts,dc=scripts,dc=mit,dc=edu", "/etc/whoisd-password")
108internet.TCPServer(43, factory).setServiceParent(
109    service.IServiceCollection(application))
Note: See TracBrowser for help on using the repository browser.