~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/protocols/ident.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_ident -*-
 
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
 
 
6
"""
 
7
Ident protocol implementation.
 
8
 
 
9
@author: Jp Calderone
 
10
"""
 
11
 
 
12
from __future__ import generators
 
13
 
 
14
import struct
 
15
 
 
16
from twisted.internet import defer
 
17
from twisted.protocols import basic
 
18
from twisted.python import log, failure
 
19
 
 
20
_MIN_PORT = 1
 
21
_MAX_PORT = 2 ** 16 - 1
 
22
 
 
23
class IdentError(Exception):
 
24
    """
 
25
    Can't determine connection owner; reason unknown.
 
26
    """
 
27
 
 
28
    identDescription = 'UNKNOWN-ERROR'
 
29
 
 
30
    def __str__(self):
 
31
        return self.identDescription
 
32
 
 
33
 
 
34
class NoUser(IdentError):
 
35
    """
 
36
    The connection specified by the port pair is not currently in use or
 
37
    currently not owned by an identifiable entity.
 
38
    """
 
39
    identDescription = 'NO-USER'
 
40
 
 
41
 
 
42
class InvalidPort(IdentError):
 
43
    """
 
44
    Either the local or foreign port was improperly specified. This should
 
45
    be returned if either or both of the port ids were out of range (TCP
 
46
    port numbers are from 1-65535), negative integers, reals or in any
 
47
    fashion not recognized as a non-negative integer.
 
48
    """
 
49
    identDescription = 'INVALID-PORT'
 
50
 
 
51
 
 
52
class HiddenUser(IdentError):
 
53
    """
 
54
    The server was able to identify the user of this port, but the
 
55
    information was not returned at the request of the user.
 
56
    """
 
57
    identDescription = 'HIDDEN-USER'
 
58
 
 
59
 
 
60
class IdentServer(basic.LineOnlyReceiver):
 
61
    """
 
62
    The Identification Protocol (a.k.a., "ident", a.k.a., "the Ident
 
63
    Protocol") provides a means to determine the identity of a user of a
 
64
    particular TCP connection. Given a TCP port number pair, it returns a
 
65
    character string which identifies the owner of that connection on the
 
66
    server's system.
 
67
 
 
68
    Server authors should subclass this class and override the lookup method.
 
69
    The default implementation returns an UNKNOWN-ERROR response for every
 
70
    query.
 
71
    """
 
72
 
 
73
    def lineReceived(self, line):
 
74
        parts = line.split(',')
 
75
        if len(parts) != 2:
 
76
            self.invalidQuery()
 
77
        else:
 
78
            try:
 
79
                portOnServer, portOnClient = map(int, parts)
 
80
            except ValueError:
 
81
                self.invalidQuery()
 
82
            else:
 
83
                if _MIN_PORT <= portOnServer <= _MAX_PORT and _MIN_PORT <= portOnClient <= _MAX_PORT:
 
84
                    self.validQuery(portOnServer, portOnClient)
 
85
                else:
 
86
                    self._ebLookup(failure.Failure(InvalidPort()), portOnServer, portOnClient)
 
87
 
 
88
    def invalidQuery(self):
 
89
        self.transport.loseConnection()
 
90
 
 
91
    def validQuery(self, portOnServer, portOnClient):
 
92
        serverAddr = self.transport.getHost()[1], portOnServer
 
93
        clientAddr = self.transport.getPeer()[1], portOnClient
 
94
        defer.maybeDeferred(self.lookup, serverAddr, clientAddr
 
95
            ).addCallback(self._cbLookup, portOnServer, portOnClient
 
96
            ).addErrback(self._ebLookup, portOnServer, portOnClient
 
97
            )
 
98
 
 
99
    def _cbLookup(self, (sysName, userId), sport, cport):
 
100
        self.sendLine('%d, %d : USERID : %s : %s' % (sport, cport, sysName, userId))
 
101
 
 
102
    def _ebLookup(self, failure, sport, cport):
 
103
        if failure.check(IdentError):
 
104
            self.sendLine('%d, %d : ERROR : %s' % (sport, cport, failure.value))
 
105
        else:
 
106
            log.err(failure)
 
107
            self.sendLine('%d, %d : ERROR : %s' % (sport, cport, IdentError(failure.value)))
 
108
 
 
109
    def lookup(self, serverAddress, clientAddress):
 
110
        """Lookup user information about the specified address pair.
 
111
 
 
112
        Return value should be a two-tuple of system name and username.
 
113
        Acceptable values for the system name may be found online at::
 
114
 
 
115
            U{http://www.iana.org/assignments/operating-system-names}
 
116
 
 
117
        This method may also raise any IdentError subclass (or IdentError
 
118
        itself) to indicate user information will not be provided for the
 
119
        given query.
 
120
 
 
121
        A Deferred may also be returned.
 
122
 
 
123
        @param serverAddress: A two-tuple representing the server endpoint
 
124
        of the address being queried.  The first element is a string holding
 
125
        a dotted-quad IP address.  The second element is an integer
 
126
        representing the port.
 
127
 
 
128
        @param clientAddress: Like L{serverAddress}, but represents the
 
129
        client endpoint of the address being queried.
 
130
        """
 
131
        raise IdentError()
 
132
 
 
133
class ProcServerMixin:
 
134
    """Implements lookup() to grab entries for responses from /proc/net/tcp
 
135
    """
 
136
 
 
137
    SYSTEM_NAME = 'LINUX'
 
138
 
 
139
    try:
 
140
        from pwd import getpwuid
 
141
        def getUsername(self, uid, getpwuid=getpwuid):
 
142
            return getpwuid(uid)[0]
 
143
        del getpwuid
 
144
    except ImportError:
 
145
        def getUsername(self, uid):
 
146
            raise IdentError()
 
147
 
 
148
    def entries(self):
 
149
        f = file('/proc/net/tcp')
 
150
        f.readline()
 
151
        for L in f:
 
152
            yield L.strip()
 
153
 
 
154
    def dottedQuadFromHexString(self, hexstr):
 
155
        return '.'.join(map(str, struct.unpack('4B', struct.pack('=L', int(hexstr, 16)))))
 
156
 
 
157
    def unpackAddress(self, packed):
 
158
        addr, port = packed.split(':')
 
159
        addr = self.dottedQuadFromHexString(addr)
 
160
        port = int(port, 16)
 
161
        return addr, port
 
162
 
 
163
    def parseLine(self, line):
 
164
        parts = line.strip().split()
 
165
        localAddr, localPort = self.unpackAddress(parts[1])
 
166
        remoteAddr, remotePort = self.unpackAddress(parts[2])
 
167
        uid = int(parts[7])
 
168
        return (localAddr, localPort), (remoteAddr, remotePort), uid
 
169
 
 
170
    def lookup(self, serverAddress, clientAddress):
 
171
        for ent in self.entries():
 
172
            localAddr, remoteAddr, uid = self.parseLine(ent)
 
173
            if remoteAddr == clientAddress and localAddr[1] == serverAddress[1]:
 
174
                return (self.SYSTEM_NAME, self.getUsername(uid))
 
175
 
 
176
        raise NoUser()
 
177
 
 
178
 
 
179
class IdentClient(basic.LineOnlyReceiver):
 
180
 
 
181
    errorTypes = (IdentError, NoUser, InvalidPort, HiddenUser)
 
182
 
 
183
    def __init__(self):
 
184
        self.queries = []
 
185
 
 
186
    def lookup(self, portOnServer, portOnClient):
 
187
        """Lookup user information about the specified address pair.
 
188
        """
 
189
        self.queries.append((defer.Deferred(), portOnServer, portOnClient))
 
190
        if len(self.queries) > 1:
 
191
            return self.queries[-1][0]
 
192
 
 
193
        self.sendLine('%d, %d' % (portOnServer, portOnClient))
 
194
        return self.queries[-1][0]
 
195
 
 
196
    def lineReceived(self, line):
 
197
        if not self.queries:
 
198
            log.msg("Unexpected server response: %r" % (line,))
 
199
        else:
 
200
            d, _, _ = self.queries.pop(0)
 
201
            self.parseResponse(d, line)
 
202
            if self.queries:
 
203
                self.sendLine('%d, %d' % (self.queries[0][1], self.queries[0][2]))
 
204
 
 
205
    def connectionLost(self, reason):
 
206
        for q in self.queries:
 
207
            q[0].errback(IdentError(reason))
 
208
        self.queries = []
 
209
 
 
210
    def parseResponse(self, deferred, line):
 
211
        parts = line.split(':', 2)
 
212
        if len(parts) != 3:
 
213
            deferred.errback(IdentError(line))
 
214
        else:
 
215
            ports, type, addInfo = map(str.strip, parts)
 
216
            if type == 'ERROR':
 
217
                for et in self.errorTypes:
 
218
                    if et.identDescription == addInfo:
 
219
                        deferred.errback(et(line))
 
220
                        return
 
221
                deferred.errback(IdentError(line))
 
222
            else:
 
223
                deferred.callback((type, addInfo))
 
224
 
 
225
__all__ = ['IdentError', 'NoUser', 'InvalidPort', 'HiddenUser',
 
226
           'IdentServer', 'IdentClient',
 
227
           'ProcServerMixin']