diff --git a/sstpd/constants.py b/sstpd/constants.py index 0ada402..ed45bae 100644 --- a/sstpd/constants.py +++ b/sstpd/constants.py @@ -1,3 +1,12 @@ +class SimpleEnumMeta(type): + """Metaclass. Add a dict `str` to allow looking up class's attribute name + by its value. Ideal for debugging. + """ + def __new__(meta, name, bases, attrs): + attrs['str'] = { v: k for k, v in attrs.items() + if not k.startswith('_') } + return super(SimpleEnumMeta, meta).__new__(meta, name, bases, attrs) + # Log level VERBOSE = 5 @@ -18,15 +27,17 @@ CALL_ABORT_PENDING = 'Call_Abort_Timeout_Pending' # Message Type -SSTP_MSG_CALL_CONNECT_REQUEST = '\x00\x01' -SSTP_MSG_CALL_CONNECT_ACK = '\x00\x02' -SSTP_MSG_CALL_CONNECT_NAK = '\x00\x03' -SSTP_MSG_CALL_CONNECTED = '\x00\x04' -SSTP_MSG_CALL_ABORT = '\x00\x05' -SSTP_MSG_CALL_DISCONNECT = '\x00\x06' -SSTP_MSG_CALL_DISCONNECT_ACK = '\x00\x07' -SSTP_MSG_ECHO_REQUEST = '\x00\x08' -SSTP_MSG_ECHO_RESPONSE = '\x00\x09' +class MsgType: + __metaclass__ = SimpleEnumMeta + CALL_CONNECT_REQUEST = '\x00\x01' + CALL_CONNECT_ACK = '\x00\x02' + CALL_CONNECT_NAK = '\x00\x03' + CALL_CONNECTED = '\x00\x04' + CALL_ABORT = '\x00\x05' + CALL_DISCONNECT = '\x00\x06' + CALL_DISCONNECT_ACK = '\x00\x07' + ECHO_REQUEST = '\x00\x08' + ECHO_RESPONSE = '\x00\x09' # Attribute ID SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID = '\x01' diff --git a/sstpd/sstp.py b/sstpd/sstp.py index 86dceb1..5958c72 100644 --- a/sstpd/sstp.py +++ b/sstpd/sstp.py @@ -154,32 +154,33 @@ def sstpDataPacketReceived(self, data): def sstpControlPacketReceived(self, messageType, attributes): - logging.info('SSTP control packet (type %s) received.' % ord(messageType[1])) - if messageType == SSTP_MSG_CALL_CONNECT_REQUEST: + logging.info('SSTP control packet (%s) received.', + MsgType.str.get(messageType, messageType)) + if messageType == MsgType.CALL_CONNECT_REQUEST: protocolId = attributes[0][1] self.sstpMsgCallConnectRequestReceived(protocolId) - elif messageType == SSTP_MSG_CALL_CONNECTED: + elif messageType == MsgType.CALL_CONNECTED: attr = attributes[0][1] hashType = attr[3:4] nonce = attr[4:36] certHash = attr[36:68] macHash = attr[68:72] self.sstpMsgCallConnectedReceived(hashType, nonce, certHash, macHash) - elif messageType == SSTP_MSG_CALL_ABORT: + elif messageType == MsgType.CALL_ABORT: if attributes: self.sstpMsgCallAbort(attributes[0][1]) else: self.sstpMsgCallAbort() - elif messageType == SSTP_MSG_CALL_DISCONNECT: + elif messageType == MsgType.CALL_DISCONNECT: if attributes: self.sstpMsgCallDisconnect(attributes[0][1]) else: self.sstpMsgCallDisconnect() - elif messageType == SSTP_MSG_CALL_DISCONNECT_ACK: + elif messageType == MsgType.CALL_DISCONNECT_ACK: self.sstpMsgCallDisconnectAck() - elif messageType == SSTP_MSG_ECHO_REQUEST: + elif messageType == MsgType.ECHO_REQUEST: self.sstpMsgEchoRequest() - elif messageType == SSTP_MSG_ECHO_RESPONSE: + elif messageType == MsgType.ECHO_RESPONSE: self.sstpMsgEchoResponse() else: logging.warn('Unknown type of SSTP control packet.') @@ -196,13 +197,13 @@ def sstpMsgCallConnectRequestReceived(self, protocolId): return if protocolId != SSTP_ENCAPSULATED_PROTOCOL_PPP: logging.warn('Unsupported encapsulated protocol.') - nak = SSTPControlPacket(SSTP_MSG_CALL_CONNECT_NAK) + nak = SSTPControlPacket(MsgType.CALL_CONNECT_NAK) nak.attributes = [(SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID, ATTRIB_STATUS_VALUE_NOT_SUPPORTED)] self.addRetryCounterOrAbrot() return self.nonce = os.urandom(32) - ack = SSTPControlPacket(SSTP_MSG_CALL_CONNECT_ACK) + ack = SSTPControlPacket(MsgType.CALL_CONNECT_ACK) # 3 bytes reserved + 1 byte hash bitmap (SHA-1 only) + nonce. ack.attributes = [(SSTP_ATTRIB_CRYPTO_BINDING_REQ, '\x00\x00\x00' + '\x03' + self.nonce)] @@ -266,7 +267,7 @@ def sstpMsgCallAbort(self, status=None): reactor.callLater(1, self.transport.loseConnection) return self.state = CALL_ABORT_IN_PROGRESS_2 - msg = SSTPControlPacket(SSTP_MSG_CALL_ABORT) + msg = SSTPControlPacket(MsgType.CALL_ABORT) msg.writeTo(self.transport.write) self.state = CALL_ABORT_PENDING reactor.callLater(1, self.transport.loseConnection) @@ -278,7 +279,7 @@ def sstpMsgCallDisconnect(self, status=None): return logging.info('Received call disconnect request.') self.state = CALL_DISCONNECT_IN_PROGRESS_2 - ack = SSTPControlPacket(SSTP_MSG_CALL_DISCONNECT_ACK) + ack = SSTPControlPacket(MsgType.CALL_DISCONNECT_ACK) ack.writeTo(self.transport.write) self.state = CALL_DISCONNECT_TIMEOUT_PENDING reactor.callLater(1, self.transport.loseConnection) @@ -296,7 +297,7 @@ def sstpMsgCallDisconnectAck(self): def sstpMsgEchoRequest(self): if self.state == SERVER_CALL_CONNECTED: - response = SSTPControlPacket(SSTP_MSG_ECHO_RESPONSE) + response = SSTPControlPacket(MsgType.ECHO_RESPONSE) response.writeTo(self.transport.write) elif self.state in (CALL_ABORT_TIMEOUT_PENDING, CALL_ABORT_PENDING, CALL_DISCONNECT_ACK_PENDING, CALL_DISCONNECT_TIMEOUT_PENDING): @@ -326,7 +327,7 @@ def helloTimerExpired(self, close=False): self.abort(ATTRIB_STATUS_NEGOTIATION_TIMEOUT) else: logging.info('Send echo request.') - echo = SSTPControlPacket(SSTP_MSG_ECHO_REQUEST) + echo = SSTPControlPacket(MsgType.ECHO_REQUEST) echo.writeTo(self.transport.write) self.helloTimer = reactor.callLater(HELLO_TIMEOUT, self.helloTimerExpired, True) @@ -344,7 +345,7 @@ def abort(self, status=None): else: logging.warn('Abort (%s).' % ord(status[-1])) self.state = CALL_DISCONNECT_IN_PROGRESS_1 - msg = SSTPControlPacket(SSTP_MSG_CALL_ABORT) + msg = SSTPControlPacket(MsgType.CALL_ABORT) if status is not None: msg.attributes = [(SSTP_ATTRIB_STATUS_INFO, status)] msg.writeTo(self.transport.write) @@ -377,7 +378,7 @@ def pppStoped(self): self.transport.loseConnection() return self.state = CALL_DISCONNECT_IN_PROGRESS_1 - msg = SSTPControlPacket(SSTP_MSG_CALL_DISCONNECT) + msg = SSTPControlPacket(MsgType.CALL_DISCONNECT) msg.attributes = [(SSTP_ATTRIB_NO_ERROR, ATTRIB_STATUS_NO_ERROR)] msg.writeTo(self.transport.write) self.state = CALL_DISCONNECT_ACK_PENDING