Пример #1
0
    def connect_to_server(self, server_cls):
        server = client = None
        try:
            sock, port = bind_unused_port()
            server = server_cls(ssl_options=_server_ssl_options())
            server.add_socket(sock)

            ssl_ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
            ssl_ctx.check_hostname = False
            ssl_ctx.verify_mode = ssl.CERT_NONE
            # These tests fail with ConnectionAbortedErrors with TLS
            # 1.3 on windows python 3.7.4 (which includes an upgrade
            # to openssl 1.1.c. Other platforms might be affected with
            # newer openssl too). Disable it until we figure out
            # what's up.
            # Update 2021-12-28: Still happening with Python 3.10 on
            # Windows. OP_NO_TLSv1_3 now raises a DeprecationWarning.
            with ignore_deprecation():
                ssl_ctx.options |= getattr(ssl, "OP_NO_TLSv1_3", 0)
                client = SSLIOStream(socket.socket(), ssl_options=ssl_ctx)
            yield client.connect(("127.0.0.1", port))
            self.assertIsNotNone(client.socket.cipher())
        finally:
            if server is not None:
                server.stop()
            if client is not None:
                client.close()
Пример #2
0
class FeedbackConn():
    def __init__(self, host=None, certfile=None, loop=None):
        self.ioloop = loop or ioloop.IOLoop.instance()
        self.host = host or settings.get('feedback_host')
        self.certfile = certfile or settings.get('certfile')
        self.connect()

    def connect(self):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self.stream = SSLIOStream(self.s, read_chunk_size=38, ssl_options=dict(certfile=self.certfile))
        self.stream.connect(self.host, self._on_connect)
        self.stream.read_until_close(self._on_close, streaming_callback=self._on_read)

    def _on_connect(self):
        logging.info("connected to %s:%d" % (self.host[0], self.host[1]))

    def _on_read(self, data):
        #logging.info("read %d bytes" % len(data))
        if len(data) == 38:
            timestamp, toklen, token = struct.unpack_from('!IH32s', data, 0)
            logging.info("flagging user %s at %d" % (token.encode('hex'), timestamp))
            mc.set(token.encode('hex'), timestamp)

    def _on_close(self, data):
        logging.info("disconnected %s:%d" % (self.host[0], self.host[1]))
        try:
            self.stream.close()
            self.s.close()
        except:
            pass
        self.ioloop.add_timeout(time.time()+settings.get('feedback_reconnect_lag'), self.connect)
Пример #3
0
class _BaseSSLService(object):
  """Base SSL connection to Apple's push notification servers. Retry on
  disconnect is handled via an exponential backoff.
  """
  _MAX_BACKOFF_SECS = 600.0  # 10 minutes
  _PUSH_TOKEN_FMT = '%s:%s'

  def __init__(self, settings, host_key):
    self._settings = settings
    self._host = settings[host_key]
    self._retries = 0
    self._io_loop = IOLoop.current()
    self._ResetBackoff()
    self._Connect()

  def IsValid(self):
    return self._stream is not None

  def _FormatPushToken(self, token):
    return _BaseSSLService._PUSH_TOKEN_FMT % (self._settings['token-prefix'], token)

  def _Connect(self):
    try:
      ssl_options = {'certfile': secrets.GetSecretFile(self._settings['certfile'])}
      self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
      self._stream = SSLIOStream(self._sock, io_loop=self._io_loop, ssl_options=ssl_options)
      self._stream.set_close_callback(self._OnClose)
      self._stream.connect(self._host, self._OnConnect)
    except KeyError:
      logging.warning('failed to initialize connection to APN service at %s:%d '
                      'whose certificate is missing from secrets/%s' %
                      (self._host[0], self._host[1], self._settings['certfile']))
      return
    except:
      self._stream = None
      raise

  def _OnConnect(self):
    logging.info("connected to %s:%d" % (self._host[0], self._host[1]))

  def _ResetBackoff(self):
    """Resets backoff to 'reconnect_lag' setting."""
    self._backoff = self._settings.get('reconnect_lag')

  def _OnClose(self):
    logging.info("disconnected from %s:%d" % (self._host[0], self._host[1]))
    try:
      self._stream.close()
    except:
      pass
    finally:
      self._stream = None

    timeout = time.time() + self._backoff
    self._io_loop.add_timeout(timeout, self._Connect)
    self._backoff = min(_BaseSSLService._MAX_BACKOFF_SECS, self._backoff * 2)
Пример #4
0
    def connect_to_server(self, server_cls):
        server = client = None
        try:
            sock, port = bind_unused_port()
            server = server_cls(ssl_options=_server_ssl_options())
            server.add_socket(sock)

            client = SSLIOStream(socket.socket(),
                                 ssl_options=dict(cert_reqs=ssl.CERT_NONE))
            yield client.connect(("10.0.0.7", port))
            self.assertIsNotNone(client.socket.cipher())
        finally:
            if server is not None:
                server.stop()
            if client is not None:
                client.close()
Пример #5
0
    def connect_to_server(self, server_cls):
        server = client = None
        try:
            sock, port = bind_unused_port()
            server = server_cls(ssl_options=_server_ssl_options())
            server.add_socket(sock)

            client = SSLIOStream(socket.socket(),
                                 ssl_options=dict(cert_reqs=ssl.CERT_NONE))
            yield client.connect(('127.0.0.1', port))
            self.assertIsNotNone(client.socket.cipher())
        finally:
            if server is not None:
                server.stop()
            if client is not None:
                client.close()
class SSLStreamReader(object):
    def __init__(self, host, port):
        self._address = (host, port)
        self._stream = None
        self._clients = set()

    def add(self, client):
        self._clients.add(client)
        log.debug('Added client %s', client)
        if self._stream is None:
            self._connect()
            self._read_stream()

    def remove(self, client):
        self._clients.remove(client)
        log.debug('Removed client %s', client)

    @staticmethod
    def process(data):
        return data

    @staticmethod
    def write_message(client, data):
        pass

    def _connect(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self._stream = SSLIOStream(s)
        log.debug('Connecting to %s', self._address)
        self._stream.connect(self._address)

    def _disconnect(self):
        self._stream.close()
        self._stream = None
        log.debug('Disconnected from %s', self._address)

    @coroutine
    def _read_stream(self):
        log.debug('Reading stream from %s', self._address)
        while self._clients:
            data = yield Task(self._stream.read_until, "\n")
            output = self.process(data)
            for client in self._clients:
                self.write_message(client, output)
        self._disconnect()
        return
class SSLStreamReader(object):
    def __init__(self, host, port):
        self._address = (host, port)
        self._stream = None
        self._clients = set()

    def add(self, client):
        self._clients.add(client)
        log.debug('Added client %s', client)
        if self._stream is None:
            self._connect()
            self._read_stream()

    def remove(self, client):
        self._clients.remove(client)
        log.debug('Removed client %s', client)

    @staticmethod
    def process(data):
        return data

    @staticmethod
    def write_message(client, data):
        pass

    def _connect(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self._stream = SSLIOStream(s)
        log.debug('Connecting to %s', self._address)
        self._stream.connect(self._address)

    def _disconnect(self):
        self._stream.close()
        self._stream = None
        log.debug('Disconnected from %s', self._address)

    @coroutine
    def _read_stream(self):
        log.debug('Reading stream from %s', self._address)
        while self._clients:
            data = yield Task(self._stream.read_until, "\n")
            output = self.process(data)
            for client in self._clients:
                self.write_message(client, output)
        self._disconnect()
        return
Пример #8
0
class FeedbackConn():
    def __init__(self, host=None, certfile=None, loop=None):
        self.ioloop = loop or ioloop.IOLoop.instance()
        self.host = host or settings.get('feedback_host')
        self.certfile = certfile or settings.get('certfile')
        self.connect()

    def connect(self):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self.stream = SSLIOStream(self.s,
                                  read_chunk_size=38,
                                  ssl_options=dict(certfile=self.certfile))
        self.stream.connect(self.host, self._on_connect)
        self.stream.read_until_close(self._on_close,
                                     streaming_callback=self._on_read)

    def _on_connect(self):
        logging.info("connected to %s:%d" % (self.host[0], self.host[1]))

    def _on_read(self, data):
        #logging.info("read %d bytes" % len(data))
        if len(data) == 38:
            timestamp, toklen, token = struct.unpack_from('!IH32s', data, 0)
            logging.info("flagging user %s at %d" %
                         (token.encode('hex'), timestamp))
            mc.set(token.encode('hex'), timestamp)

    def _on_close(self, data):
        logging.info("disconnected %s:%d" % (self.host[0], self.host[1]))
        try:
            self.stream.close()
            self.s.close()
        except:
            pass
        self.ioloop.add_timeout(
            time.time() + settings.get('feedback_reconnect_lag'), self.connect)
Пример #9
0
class SSLStreamReader(object):
    def __init__(self, host, port):
        self._address = (host, port)
        self._stream = None
        self._clients = set()
        self._nodes=[]
        self._edges=[]

    def add(self, client):
        self._clients.add(client)
        log.debug('Added client %s', client)
        if self._stream is None:
            self._connect()
            self._read_stream()

    def remove(self, client):
        self._clients.remove(client)
        log.debug('Removed client %s', client)

    @staticmethod
    def process(data):
        return data

    @staticmethod
    def write_message(client, data):
        pass

    def _connect(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self._stream = SSLIOStream(s)
        log.debug('Connecting to %s', self._address)
        self._stream.connect(self._address)

    def _disconnect(self):
        self._stream.close()
        self._stream = None
        log.debug('Disconnected from %s', self._address)

    @coroutine
    def _read_stream(self):
        log.info('Reading stream from %s', self._address)
        lastVertex=None
        while self._clients:
            
            data = yield Task(self._stream.read_until, "\n")
            resultObject=None
            if '<hwstats' in data:
                result=None
                if lastVertex==None:
                    resultObject=None
                else:
                    result=[]
                    result.append('{"type":"vertex", "event":"update", ')
                    result.append('"id":')
                    result.append(lastVertex)
                    result.append(', "details":{"Temperature":')
                    Start=data.find('temperature="')+13
                    End=data.find('" ',Start)
                    result.append(data[Start:End])
                    result.append(', "Hostname":')
                    Start=data.find('hostname="')+9
                    End=data.find('"',Start+1)+1
                    result.append(data[Start:End])
                    result.append(', "CPU":')
                    Start=data.find('" cpuLoad="')+11
                    End=data.find('" ',Start)
                    result.append(data[Start:End])
                    result.append(', "Free Memory":')
                    Start=data.find('freeMem="')+9
                    End=data.find('" ',Start)
                    result.append(data[Start:End])
                    result.append('}}')
                resultObject=result
                lastVertex=None
            
            elif '<vertex' in data or ('<edge' in data and 'type="2"' in data):
                resultObject = self.process(data)
                
                if '<vertex' in data:
                    if 'add' in data:
                        start=data.find('id="')+4
                        end=data.find('"',start)
                        lastVertex=data[start:end]
                    if '"add", ' in resultObject and '"vertex",'  in resultObject:
                        for i in range(len(self._nodes)):
                            if (resultObject[5]==self._nodes[i][5]):
                                self._nodes.pop(i)
                                self._nodes.append(resultObject)
                                resultObject[3]='"update", '
                            elif i==len(self._nodes)-1:
                                self._nodes.append(resultObject)
                            
                        if len(self._nodes)==0:
                           self._nodes.append(resultObject)
                        
                    
                    elif('"remove", ' in resultObject):
                    
                        for i in range(len(self._nodes)):
                            if ((resultObject[5]==self._nodes[i][5])):
                                self._nodes.pop(i)
                                break
                        for i in range(len(self._edges)):
                            if resultObject[5]==self._edges[i][5] or resultObject[5]==self._edges[i][7]:
                                self._edges.pop(i)
                        
                elif '<edge' in data:
                    if 'event="add"' in resultObject and '"edge",'  in resultObject:
                        for i in range(len(self._edges)):
                            if(resultObject[0:8]==self._edges[i][0:8] and resultObject!=self._edges[i] ):
                                self._edges[i]=resultObject
                                resultObject[3]='"update",'
                                break
                            elif (i==len(self._edges)-1):
                                self._edges.append(resultObject)
                        if len(self._edges)==0:
                            self.edges.append(resultObject)
                    elif '"remove", ' in resultObject and '"edge", ' in resultObject:
                        for i in range(len(self._edges)):
                            if (resultObject[5] == self._edges[i][5] and resultObject[7] == self._edges[i][7]) or (resultObject[5] == self._edges[i][7] and resultObject[7] == self._edges[i][5]):
                                self._edges.pop(i)
            if resultObject!=None:            
                resultObject=''.join(resultObject)
                for client in self._clients:
                    print('data: '+data+'\nsending: '+resultObject)
                    self.write_message(client, resultObject)
        self._disconnect()
        return
Пример #10
0
class APNSConn():
    def __init__(self, host=None, certfile=None, loop=None):
        self.stats = {
            'disconnects':0,
            'notifications':0,
            'invalid_tokens':0,
        }
        self.started = time.time()
        self.ioloop = loop or ioloop.IOLoop.instance()
        self.host = host or settings.get('apns_host')
        self.certfile = certfile or settings.get('certfile')
        self.write_queue = deque()
        self.recent = deque(maxlen=100)
        # N.B. Python upgrades ints to longs and this is a binary protocol so...
        self.generation = ctypes.c_uint32(0)
        self.connect()

    def get_stats(self):
        stats = self.stats.copy()
        stats['queue_len'] = len(self.write_queue)
        stats['uptime'] = time.time() - self.started
        return stats
        
    def push(self, token, alert=None, badge=None, sound=None, expiry=None, extra=None, timestamp=None):
        '''
        This is really the only api you want to use. Pushes a notification onto the queue
        for non-flagged users. The queue is processed in the tornado event loop.
        '''
        flagged = mc.get(token)
        if flagged:
            if not timestamp or timestamp > flagged:
                self.stats['invalid_tokens'] += 1
                return False

        self.generation.value += 1
        identifier = self.generation.value
        msg = create_message(token, alert=alert, badge=badge, sound=sound,
            identifier=identifier, expiry=expiry, extra=extra)
        if len(msg) > MAX_PAYLOAD_BYTES:
            raise ValueError, u"max payload(%d) exceeded: %d" % (MAX_PAYLOAD_BYTES, len(msg))

        self.write_queue.append(msg)
        self.recent.append(dict(identifier=identifier, token=token))
        self.ioloop.add_callback(self.push_one)
        return True

    def push_one(self):
        if len(self.write_queue) and not self.stream.closed():
            msg = self.write_queue.popleft()
            try:
                self.stream.write(msg)
                self.stats['notifications'] += 1
            except:
                self.write_queue.appendleft(msg)
                return False
            return True
        return False

    def connect(self):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self.stream = SSLIOStream(self.s, ssl_options=dict(certfile=self.certfile))
        self.stream.connect(self.host, self._on_connect)
        self.stream.read_until_close(self._on_close, streaming_callback=self._on_read)

    def _on_connect(self):
        '''
        Process the backlog, hoss.
        '''
        logging.info('connected to %s:%d' % (self.host[0], self.host[1]))
        while self.push_one(): continue

    def _on_read(self, data):
        '''
        The only message we expect here is an error response... sadly, followed by
        a disconnect. 
        '''
        logging.info('_on_read: %d bytes' % (len(data)))
        try:
            status, identifier, err_string = parse_response(data)
            logging.warning('_on_read err: %d %d %s' % (status, identifier, err_string))
            if status == 8:
                for msg in self.recent:
                    if msg['identifier'] == identifier:
                        token = msg['token']
                        logging.info('flagging token: %s' % (token))
                        self.stats['invalid_tokens'] += 1
                        mc.set(token, int(time.time()))
        except:
            logging.info('parse_response failed')

    def _on_close(self, data):
        '''
        This triggers the reconnect given reconnect_lag from settings. This should
        probably be an exponential backoff.
        '''
        logging.info('_on_close')
        self.stats['disconnects'] += 1
        try:
            self.stream.close()
            self.s.close()
        except:
            pass
        self.ioloop.add_timeout(time.time()+settings.get('apns_reconnect_lag'), self.connect)
Пример #11
0
class _BaseSSLService(object):
    """Base SSL connection to Apple's push notification servers. Retry on
  disconnect is handled via an exponential backoff.
  """
    _MAX_BACKOFF_SECS = 600.0  # 10 minutes
    _PUSH_TOKEN_FMT = '%s:%s'

    def __init__(self, settings, host_key):
        self._settings = settings
        self._host = settings[host_key]
        self._retries = 0
        self._io_loop = IOLoop.current()
        self._ResetBackoff()
        self._Connect()

    def IsValid(self):
        return self._stream is not None

    def _FormatPushToken(self, token):
        return _BaseSSLService._PUSH_TOKEN_FMT % (
            self._settings['token-prefix'], token)

    def _Connect(self):
        try:
            ssl_options = {
                'certfile': secrets.GetSecretFile(self._settings['certfile'])
            }
            self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
            self._stream = SSLIOStream(self._sock,
                                       io_loop=self._io_loop,
                                       ssl_options=ssl_options)
            self._stream.set_close_callback(self._OnClose)
            self._stream.connect(self._host, self._OnConnect)
        except KeyError:
            logging.warning(
                'failed to initialize connection to APN service at %s:%d '
                'whose certificate is missing from secrets/%s' %
                (self._host[0], self._host[1], self._settings['certfile']))
            return
        except:
            self._stream = None
            raise

    def _OnConnect(self):
        logging.info("connected to %s:%d" % (self._host[0], self._host[1]))

    def _ResetBackoff(self):
        """Resets backoff to 'reconnect_lag' setting."""
        self._backoff = self._settings.get('reconnect_lag')

    def _OnClose(self):
        logging.info("disconnected from %s:%d" %
                     (self._host[0], self._host[1]))
        try:
            self._stream.close()
        except:
            pass
        finally:
            self._stream = None

        timeout = time.time() + self._backoff
        self._io_loop.add_timeout(timeout, self._Connect)
        self._backoff = min(_BaseSSLService._MAX_BACKOFF_SECS,
                            self._backoff * 2)
Пример #12
0
class APNSConn():
    def __init__(self, host=None, certfile=None, loop=None):
        self.stats = {
            'disconnects': 0,
            'notifications': 0,
            'invalid_tokens': 0,
        }
        self.started = time.time()
        self.ioloop = loop or ioloop.IOLoop.instance()
        self.host = host or settings.get('apns_host')
        self.certfile = certfile or settings.get('certfile')
        self.write_queue = deque()
        self.recent = deque(maxlen=100)
        # N.B. Python upgrades ints to longs and this is a binary protocol so...
        self.generation = ctypes.c_uint32(0)
        self.connect()

    def get_stats(self):
        stats = self.stats.copy()
        stats['queue_len'] = len(self.write_queue)
        stats['uptime'] = time.time() - self.started
        return stats

    def push(self,
             token,
             alert=None,
             badge=None,
             sound=None,
             expiry=None,
             extra=None,
             timestamp=None):
        '''
        This is really the only api you want to use. Pushes a notification onto the queue
        for non-flagged users. The queue is processed in the tornado event loop.
        '''
        flagged = mc.get(token)
        if flagged:
            if not timestamp or timestamp > flagged:
                self.stats['invalid_tokens'] += 1
                return False

        self.generation.value += 1
        identifier = self.generation.value
        msg = create_message(token,
                             alert=alert,
                             badge=badge,
                             sound=sound,
                             identifier=identifier,
                             expiry=expiry,
                             extra=extra)
        if len(msg) > MAX_PAYLOAD_BYTES:
            raise ValueError, u"max payload(%d) exceeded: %d" % (
                MAX_PAYLOAD_BYTES, len(msg))

        self.write_queue.append(msg)
        self.recent.append(dict(identifier=identifier, token=token))
        self.ioloop.add_callback(self.push_one)
        return True

    def push_one(self):
        if len(self.write_queue) and not self.stream.closed():
            msg = self.write_queue.popleft()
            try:
                self.stream.write(msg)
                self.stats['notifications'] += 1
            except:
                self.write_queue.appendleft(msg)
                return False
            return True
        return False

    def connect(self):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self.stream = SSLIOStream(self.s,
                                  ssl_options=dict(certfile=self.certfile))
        self.stream.connect(self.host, self._on_connect)
        self.stream.read_until_close(self._on_close,
                                     streaming_callback=self._on_read)

    def _on_connect(self):
        '''
        Process the backlog, hoss.
        '''
        logging.info('connected to %s:%d' % (self.host[0], self.host[1]))
        while self.push_one():
            continue

    def _on_read(self, data):
        '''
        The only message we expect here is an error response... sadly, followed by
        a disconnect. 
        '''
        logging.info('_on_read: %d bytes' % (len(data)))
        try:
            status, identifier, err_string = parse_response(data)
            logging.warning('_on_read err: %d %d %s' %
                            (status, identifier, err_string))
            if status == 8:
                for msg in self.recent:
                    if msg['identifier'] == identifier:
                        token = msg['token']
                        logging.info('flagging token: %s' % (token))
                        self.stats['invalid_tokens'] += 1
                        mc.set(token, int(time.time()))
        except:
            logging.info('parse_response failed')

    def _on_close(self, data):
        '''
        This triggers the reconnect given reconnect_lag from settings. This should
        probably be an exponential backoff.
        '''
        logging.info('_on_close')
        self.stats['disconnects'] += 1
        try:
            self.stream.close()
            self.s.close()
        except:
            pass
        self.ioloop.add_timeout(
            time.time() + settings.get('apns_reconnect_lag'), self.connect)