From 81ed281c8ee43ed0fafac736b693767e34bc947d Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 4 Aug 2012 12:41:03 +0200 Subject: [PATCH] Add check for probes such as dictionary password attacks. --- src/calibre/devices/errors.py | 3 +++ .../devices/smart_device_app/driver.py | 25 +++++++++++++++++-- src/calibre/gui2/device.py | 11 +++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/calibre/devices/errors.py b/src/calibre/devices/errors.py index 7290a06123..2ab49529a3 100644 --- a/src/calibre/devices/errors.py +++ b/src/calibre/devices/errors.py @@ -48,6 +48,9 @@ class OpenFeedback(DeviceError): ''' raise NotImplementedError +class InitialConnectionError(OpenFeedback): + """ Errors detected during connection after detection but before open """ + class OpenFailed(ProtocolError): """ Raised when device cannot be opened this time. No retry is to be done. The device should continue to be polled for future opens. If the diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index 19a6e23575..4fe815dc80 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -14,7 +14,8 @@ from functools import wraps from calibre import prints from calibre.constants import numeric_version, DEBUG -from calibre.devices.errors import OpenFailed, ControlError, TimeoutError +from calibre.devices.errors import (OpenFailed, ControlError, TimeoutError, + InitialConnectionError) from calibre.devices.interface import DevicePlugin from calibre.devices.usbms.books import Book, BookList from calibre.devices.usbms.deviceconfig import DeviceConfig @@ -82,6 +83,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): BASE_PACKET_LEN = 4096 PROTOCOL_VERSION = 1 MAX_CLIENT_COMM_TIMEOUT = 60.0 # Wait at most N seconds for an answer + MAX_UNSUCCESSFUL_CONNECTS = 5 opcodes = { 'NOOP' : 12, @@ -490,6 +492,19 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): self.listen_socket.settimeout(None) self.device_socket.settimeout(None) self.is_connected = True + try: + peer = self.device_socket.getpeername()[0] + attempts = self.connection_attempts.get(peer, 0) + if attempts >= self.MAX_UNSUCCESSFUL_CONNECTS: + self._debug('too many connection attempts from', peer) + self._close_device_socket() + raise InitialConnectionError(_('Too many connection attempts from %s')%peer) + else: + self.connection_attempts[peer] = attempts + 1 + except InitialConnectionError: + raise + except: + pass except socket.timeout: self._close_device_socket() except socket.error: @@ -497,7 +512,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): self._debug('unexpected socket exception', x.args[0]) self._close_device_socket() raise - return (True, self) + return (self.is_connected, self) return (False, None) @synchronous('sync_lock') @@ -570,6 +585,11 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): # Don't bother with a message. The user will be informed on # the device. raise OpenFailed('') + try: + peer = self.device_socket.getpeername() + self.connection_attempts[peer] = 0 + except: + pass return True except socket.timeout: self._close_device_socket() @@ -807,6 +827,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): self.debug_start_time = time.time() self.max_book_packet_len = 0 self.noop_counter = 0 + self.connection_attempts = {} try: self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except: diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index f9f400c1cd..f8dcde1e4a 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -13,7 +13,8 @@ from PyQt4.Qt import (QMenu, QAction, QActionGroup, QIcon, SIGNAL, from calibre.customize.ui import (available_input_formats, available_output_formats, device_plugins) from calibre.devices.interface import DevicePlugin -from calibre.devices.errors import UserFeedback, OpenFeedback, OpenFailed +from calibre.devices.errors import (UserFeedback, OpenFeedback, OpenFailed, + InitialConnectionError) from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog from calibre.utils.ipc.job import BaseJob from calibre.devices.scanner import DeviceScanner @@ -232,8 +233,12 @@ class DeviceManager(Thread): # {{{ for device in self.devices: if device in self.ejected_devices: continue - possibly_connected, detected_device = \ - self.scanner.is_device_connected(device) + try: + possibly_connected, detected_device = \ + self.scanner.is_device_connected(device) + except InitialConnectionError as e: + self.open_feedback_msg(device.get_gui_name(), e) + continue if possibly_connected: possibly_connected_devices.append((device, detected_device)) if possibly_connected_devices: