| [528] | 1 | from twisted.application import internet, service | 
|---|
 | 2 | from twisted.internet import protocol, reactor, defer | 
|---|
 | 3 | from twisted.protocols import basic | 
|---|
| [627] | 4 | import ldap, ldap.filter | 
|---|
 | 5 | import os, sys, pwd, glob | 
|---|
| [528] | 6 |  | 
|---|
 | 7 | class WhoisProtocol(basic.LineReceiver): | 
|---|
 | 8 |     def lineReceived(self, hostname): | 
|---|
 | 9 |         self.factory.getWhois(hostname | 
|---|
 | 10 |         ).addErrback(lambda _: "Internal error in server" | 
|---|
 | 11 |         ).addCallback(lambda m: | 
|---|
 | 12 |                       (self.transport.write(m+"\r\n"), | 
|---|
 | 13 |                        self.transport.loseConnection())) | 
|---|
 | 14 | class WhoisFactory(protocol.ServerFactory): | 
|---|
 | 15 |     protocol = WhoisProtocol | 
|---|
| [627] | 16 |     def __init__(self, vhostDir, ldap_URL, ldap_base): | 
|---|
| [528] | 17 |         self.vhostDir = vhostDir | 
|---|
| [627] | 18 |         self.ldap_URL = ldap_URL | 
|---|
 | 19 |         self.ldap = ldap.initialize(self.ldap_URL) | 
|---|
 | 20 |         self.ldap_base = ldap_base | 
|---|
| [528] | 21 |         self.vhosts = {} | 
|---|
 | 22 |         self.rescanVhosts() | 
|---|
 | 23 |     def rescanVhosts(self): | 
|---|
 | 24 |         newVhosts = {} | 
|---|
 | 25 |         for f in glob.iglob(os.path.join(self.vhostDir, "*.conf")): | 
|---|
 | 26 |             locker = os.path.splitext(os.path.basename(f))[0] | 
|---|
 | 27 |             newVhosts.update(self.parseApacheConf(file(f))) | 
|---|
 | 28 |         self.vhosts = newVhosts | 
|---|
 | 29 |         self.vhostTime = os.stat(self.vhostDir).st_mtime | 
|---|
 | 30 |     def parseApacheConf(self, f): | 
|---|
 | 31 |         vhosts = {} | 
|---|
 | 32 |         hostnames = [] | 
|---|
 | 33 |         locker = None | 
|---|
 | 34 |         docroot = None | 
|---|
 | 35 |         for l in f: | 
|---|
 | 36 |             parts = l.split() | 
|---|
 | 37 |             if not parts: continue | 
|---|
 | 38 |             command = parts.pop(0) | 
|---|
 | 39 |             if command in ("ServerName", "ServerAlias"): | 
|---|
 | 40 |                 hostnames.extend(parts) | 
|---|
 | 41 |             elif command in ("SuExecUserGroup",): | 
|---|
 | 42 |                 locker = parts[0] | 
|---|
 | 43 |             elif command in ("DocumentRoot",): | 
|---|
 | 44 |                 docroot = parts[0] | 
|---|
 | 45 |             elif command == "</VirtualHost>": | 
|---|
| [627] | 46 |                 d = {'locker': locker, 'apacheDocumentRoot': docroot, 'apacheServerName': hostnames[0]} | 
|---|
| [528] | 47 |                 for h in hostnames: vhosts[h] = d | 
|---|
 | 48 |                 hostnames = [] | 
|---|
 | 49 |                 locker = None | 
|---|
 | 50 |                 docroot = None | 
|---|
 | 51 |         return vhosts | 
|---|
 | 52 |     def canonicalize(self, vhost): | 
|---|
 | 53 |         vhost = vhost.lower().rstrip(".") | 
|---|
 | 54 |         return vhost | 
|---|
 | 55 | #        if vhost.endswith(".mit.edu"): | 
|---|
 | 56 | #            return vhost | 
|---|
 | 57 | #        else: | 
|---|
 | 58 | #            return vhost + ".mit.edu" | 
|---|
| [627] | 59 |     def searchLDAP(self, vhost): | 
|---|
 | 60 |         results = self.ldap.search_s(self.ldap_base, ldap.SCOPE_SUBTREE, | 
|---|
 | 61 |             ldap.filter.filter_format( | 
|---|
 | 62 |                 '(|(apacheServername=%s)(apacheServerAlias=%s))', (vhost,)*2)) | 
|---|
 | 63 |         if len(results) >= 1: | 
|---|
 | 64 |             result = results[0] | 
|---|
 | 65 |             attrs = result[1] | 
|---|
 | 66 |             for attr in ('apacheServerName','apacheDocumentRoot', 'apacheSuexecUid', 'apacheSuexecGid'): | 
|---|
 | 67 |                 attrs[attr] = attrs[attr][0] | 
|---|
 | 68 |             user = pwd.getpwuid(int(attrs['apacheSuexecUid'])) | 
|---|
 | 69 |             if user: | 
|---|
 | 70 |                 attrs['locker'] = user.pw_name | 
|---|
 | 71 |             else: | 
|---|
 | 72 |                 attrs['locker'] = None | 
|---|
 | 73 |             return attrs | 
|---|
 | 74 |         else: | 
|---|
 | 75 |             return None | 
|---|
| [528] | 76 |     def getWhois(self, vhost): | 
|---|
 | 77 |         vhost = self.canonicalize(vhost) | 
|---|
 | 78 |         info = self.vhosts.get(vhost) | 
|---|
| [627] | 79 |         if not info: | 
|---|
 | 80 |             info = self.searchLDAP(vhost) | 
|---|
| [528] | 81 |         if info: | 
|---|
 | 82 |             ret = "Hostname: %s\nAlias: %s\nLocker: %s\nDocument Root: %s" % \ | 
|---|
| [627] | 83 |                 (info['apacheServerName'], vhost, info['locker'], info['apacheDocumentRoot']) | 
|---|
| [528] | 84 |         else: | 
|---|
 | 85 |             ret = "No such hostname" | 
|---|
 | 86 |         return defer.succeed(ret) | 
|---|
 | 87 |  | 
|---|
 | 88 | application = service.Application('whois', uid=99, gid=99) | 
|---|
| [627] | 89 | factory = WhoisFactory("/etc/httpd/vhosts.d", | 
|---|
 | 90 |     "ldap://localhost", "ou=VirtualHosts,dc=scripts,dc=mit,dc=edu") | 
|---|
| [528] | 91 | internet.TCPServer(43, factory).setServiceParent( | 
|---|
 | 92 |     service.IServiceCollection(application)) | 
|---|