# -*- coding: utf-8 -*-
#
# Author: Bo Maryniuk <bo@suse.de>
#
# Copyright 2017 SUSE LLC
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

'''
Salt Service Discovery Protocol.
JSON-based service discovery protocol, used by minions to find running Master.
'''

import socket
import logging
import datetime

try:
    import asyncio
except ImportError:
    try:
        # Python 2 doesn't have asyncio
        import trollius as asyncio
    except ImportError:
        asyncio = None


class SSDPBase(object):
    '''
    Salt Service Discovery Protocol.
    '''
    log = logging.getLogger(__name__)

    # Fields
    SIGNATURE = 'signature'
    ANSWER = 'answer'

    # Default values
    DEFAULTS = {
        SIGNATURE: '__salt_master_service'
    }

    @staticmethod
    def _is_available():
        '''
        Return True if the USSDP dependencies are satisfied.
        :return:
        '''
        return bool(asyncio)

    @staticmethod
    def get_self_ip():
        '''
        Find out localhost outside IP.

        :return:
        '''
        sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            sck.connect(('1.255.255.255', 1))  # Does not needs to be reachable
            ip_addr = sck.getsockname()[0]
        except Exception:
            ip_addr = socket.gethostbyname(socket.gethostname())
        finally:
            sck.close()
        return ip_addr

@@ -1886,8 +1886,2321 @@
 ip_addr%0A
+%0A%0Aclass SSDPFactory(SSDPBase):%0A    '''%0A    Socket protocol factory.%0A    '''%0A%0A    def __init__(self, **config):%0A        '''%0A        Initialize%0A%0A        :param config:%0A        '''%0A        for attr in (self.SIGNATURE, self.ANSWER):%0A            setattr(self, attr, config.get(attr, self.DEFAULTS%5Battr%5D))%0A        self.disable_hidden = False%0A        self.transport = None%0A        self.my_ip = socket.gethostbyname(socket.gethostname())%0A        self.DEFAULTS%5Bself.ANSWER%5D = '%7B%7D'%0A%0A    def __call__(self, *args, **kwargs):%0A        '''%0A        Return instance on Factory call.%0A        :param args:%0A        :param kwargs:%0A        :return:%0A        '''%0A        return self%0A%0A    def connection_made(self, transport):%0A        '''%0A        On connection.%0A%0A        :param transport:%0A        :return:%0A        '''%0A        self.transport = transport%0A%0A    def datagram_received(self, data, addr):%0A        '''%0A        On datagram receive.%0A%0A        :param data:%0A        :param addr:%0A        :return:%0A        '''%0A        message = data.decode()%0A        if message.startswith(self.signature):%0A            try:%0A                timestamp = float(message%5Blen(self.signature):%5D)%0A            except TypeError:%0A                self.log.debug('Received invalid timestamp in package from %25s' %25 (%22%25s:%25s%22 %25 addr))%0A                if self.disable_hidden:%0A                    self.transport.sendto('%7B0%7D#ERROR#%7B1%7D'.format(self.signature, 'Invalid timestamp'), addr)%0A                return%0A%0A            if datetime.datetime.fromtimestamp(timestamp) %3C (datetime.datetime.now() - datetime.timedelta(seconds=20)):%0A                if self.disable_hidden:%0A                    self.transport.sendto('%7B0%7D#ERROR#%7B1%7D'.format(self.signature, 'Timestamp is too old'), addr)%0A                self.log.debug('Received outdated package from %25s' %25 (%22%25s:%25s%22 %25 addr))%0A                return%0A%0A            self.log.debug('Received %25r from %25s' %25 (message, %22%25s:%25s%22 %25 addr))%0A            self.transport.sendto('%7B0%7D#OK#%7B1%7D'.format(self.signature, self.answer), addr)%0A        else:%0A            if self.disable_hidden:%0A                self.transport.sendto('%7B0%7D#ERROR#%7B1%7D'.format(self.signature,%0A                                                             'Invalid packet signature').encode(), addr)%0A            self.log.debug('Received bad magic or password from %25s:%25s' %25 addr)%0A

Implement SSDP factory protocol