| 1 | from twisted.application import internet, service | 
|---|
| 2 | from twisted.internet import protocol, reactor, defer | 
|---|
| 3 | from twisted.protocols import basic | 
|---|
| 4 | import ldap, ldap.filter | 
|---|
| 5 | import os, sys, pwd, glob | 
|---|
| 6 |  | 
|---|
| 7 | class 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())) | 
|---|
| 19 | class 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 |  | 
|---|
| 105 | application = service.Application('whois', uid=99, gid=99) | 
|---|
| 106 | factory = WhoisFactory(None, | 
|---|
| 107 |     "ldap://localhost", "ou=VirtualHosts,dc=scripts,dc=mit,dc=edu", "/etc/whoisd-password") | 
|---|
| 108 | internet.TCPServer(43, factory).setServiceParent( | 
|---|
| 109 |     service.IServiceCollection(application)) | 
|---|