Beispiel #1
0
    def _listen(self):
        #Wait for connection
        self.recv_sock, self.remote_addr = self.sock_fd.accept()
        print "Accepted connection!"

        #Update observers while running
        while not self.disconnected:
            first_byte = self.recv_sock.recv(1)

            #Test whether client has dropped
            if not first_byte:
                self.disconnected = True
                #Inform observers
                for callback in self.dc_listeners:
                    callback()

            else:
                msg_len = ord(first_byte)

                #Receive until we have an entire message
                msg_body = self.recv_sock.recv(msg_len)
                while len(msg_body) < msg_len:
                    msg_body = msg_body + self.recv_sock.recv(msg_len -
                                                              len(msg_body))

                msg_parsed = msg.Msg(msg_body)

                #Give message to all registered observers
                for callback in self.data_listeners:
                    callback(msg_parsed)
Beispiel #2
0
def deserialize(byte_data):
    """
    Converts byte data into message object

    :param byte_data: bytes to convert to message

    :returns: Message object
    """
    # Validate byte_data
    if not is_valid_msg(byte_data):
        raise ValueError('Attempt to deserialize invalid message')

    # Calculate the size of the payload and construct unpacking format
    payload_sz = len(byte_data) - g_HEADER_SZ_BYTES
    dynamic_fmt = unpack_fmt % (payload_sz)

    # Unpack the binary data
    msg_len, raw_msg_type, raw_timestamp, raw_payload \
        = struct.unpack(dynamic_fmt, byte_data)

    msg_type = msg.MsgType(raw_msg_type)
    payload = msg.decode_payload(msg_type, raw_payload)
    timestamp = raw_timestamp.decode(c.Config.get(c.ConfigEnum.BYTE_ENCODING))

    return msg.Msg(msg_type, payload, timestamp)
Beispiel #3
0
def execute(port, src_guid, net_id):
    '''
    Executes message broadcasts to all valid network adapters. Blocks until
    all broadcasts have been sent out.

    :param port: Destination port to broadcast to
    :param src_guid: Host GUID
    :param net_id: Host's NetID
    '''
    broadcast_ips = netutils.get_broadcast_ips()  # Check adapter info
    _g_logger.debug(f'Broadcast IPs: {broadcast_ips}')

    # Sanity check that network adapters are available for broadcasting
    num_broadcast_ips = len(broadcast_ips)
    if num_broadcast_ips == 0:
        raise RuntimeError('Unable to find valid network adapter')

    _g_logger.info('Broadcasting IPs retrieved')

    # Construct broadcasting message
    broadcast_msg = msg.Msg(msg.MsgType.ENDPOINT_CONNECTION_BROADCAST, net_id)

    _g_logger.debug(f'Constructed broadcast message: {broadcast_msg}')

    # Send out broadcasts
    for ip in broadcast_ips:
        __broadcast(broadcast_msg, src_guid, (ip, port))
Beispiel #4
0
def test():
    # Construct message
    mt = msg.MsgType.ENDPOINT_TEXT_COMMUNICATION
    data = 'this is a payload'

    message = msg.Msg(mt, data)

    print('Message Pre-serialization')
    print(message)
    print()

    # Serialize message
    serialized_msg = serialize(message)

    print('Message Post-serialization')
    print(serialized_msg)
    print()

    # Check validation methods
    if not is_valid_msg(serialized_msg):
        raise ValueError('Unable to validate message during testing')

    msg_type = decode_msgtype(serialized_msg)
    print('Decoded MsgType: %s' % (msg_type))
    print()

    # Deserialize message
    deserialized_msg = deserialize(serialized_msg)

    print('Message Post-deserialization')
    print(deserialized_msg)
Beispiel #5
0
def main():
    # actually don't know if script could work on Python earlier
    # than 3.5.x, but that's the earliest I tested with
    assert sys.version_info >= (3, 5)

    path = config_file
    if not path.startswith('/'):
        path = os.path.join(SCRIPT_DIR, path)
    config = read_config(path)
    if not config:
        sys.exit("Please fill in the 'config.txt' file.")

    for section in config.sections():
        # don't iterate over config, that will process the DEFAULT section
        sec = config[section]
        apply_config_defaults(sec)
        m = msg.Msg()
        m.setlevel(sec['verbosity'])
        for protocol in sec['protocols']:
            m.put(
                msg.INFO, '%s - section %s - IPv%s' %
                (str(datetime.now()), section, protocol))

            # deduce DNS record type
            sec['recordtype'] = {'4': 'A', '6': 'AAAA'}[protocol]
            # deduce DNS record name
            sec['recordname'] = {
                '4': sec['a_name'],
                '6': sec['aaaa_name']
            }[protocol]

            # Set URL. It ends up looking like:
            # https://dns.api.gandi.net/api/v5/domains/example.com/raspian/A
            sec['url'] = '%sdomains/%s/records/%s/%s' % (
                sec['api'], sec['domain'], sec['recordname'],
                sec['recordtype'])
            m.put(msg.INFO, 'Request API URL is: %s' % sec['url'])

            # Recently I started getting occasional 502 ("Bad Gateway")
            # errors.  Interestingly, this only happens when I run
            # the script from a cron job, and only at the run
            # at the top of the hour (hh:00),  The runs at
            # hh:20 and hh:40 never fail and another system whic runs
            # hourly at hh:07 never fails.  My guess is that there are
            # a lot of people running some form of dns update from
            # cron, with a run at the top of the hour, resulting in
            # server overload (kind of a unintentional DDOS.)

            # To combat this, if a 502 error is received, wait a little
            # and retry, but give up if the error persists.  I'm also
            # going to change my cron job to move off the top of the hour.

            # Check current record

            max_retries = 5
            retry_delay = 23

            for attempt in range(max_retries):
                record = get_record(m, sec)
                if record.status_code != 502:
                    break
                m.put(
                    msg.ERROR, 'Got error %d fetching %s record. Retry %d.' %
                    (record.status_code, sec['recordtype'], attempt))
                time.sleep(retry_delay)

            if record.status_code == 502:
                m.put(
                    msg.ERROR,
                    'Got error %d repeatedly fetching %s record. Giving up.' %
                    (record.status_code, sec['recordtype']))
                sys.exit(2)

            if record.status_code == 404:
                # no old record, add it
                # Discover External IP
                external_ip = get_ip(m, sec['ipservice'], protocol)
                m.put(
                    msg.ACTION, 'No old %s record, adding as %s' %
                    (sec['recordtype'], external_ip))

                update_record(m, sec, external_ip)
            elif record.status_code == 200:
                # old record exists, check and replace if needed
                old_ip = json.loads(record.text)['rrset_values'][0]
                m.put(
                    msg.INFO, 'Current %s record value is: %s' %
                    (sec['recordtype'], old_ip))

                # this is a very particular quirk for my own home config.
                # if there is a nonroutable IP address in an A record,
                # I have put it there for convenience in using one host on
                # my home network from another, do not update it with
                # the external IP.  This path should never be executed
                # if I've configured the script right on my own systems,
                # since the systems where <hostname>.my.domain/A is set to
                # an unroutable IP should be configured to only update
                # the AAAA records, and I doubt anybody else is crazy
                # enough to put private IPs in DNS (I actually don't
                # even do it any more since I now depend on mDNS
                # for local name resolution, which is much saner.)

                if (protocol == '4'
                        and ipaddress.IPv4Address(old_ip).is_private):
                    m.put(
                        msg.ERROR,
                        'Not updating %s record for %s, check config' %
                        (names[protocol], old_ip))
                    continue  # next protlocol

                # Discover External IP
                external_ip = get_ip(m, sec['ipservice'], protocol)
                m.put(msg.INFO, 'External IP is: %s' % external_ip)

                if old_ip == external_ip:
                    m.put(
                        msg.NOACTION,
                        'No change in IPv%s address, nothing done.' %
                        (protocol))
                else:
                    m.put(
                        msg.ACTION, 'Updating %s/%s from %s to %s' %
                        (sec['recordname'], sec['recordtype'], old_ip,
                         external_ip))
                    update_record(m, sec, external_ip)
            else:
                # some unexpected condition fetching old record.
                # perhaps config is bad or api has changed?
                m.put(
                    msg.ERROR, 'Got error %d fetching %s record. Stop.' %
                    (record.status_code, sec['recordtype']))
                sys.exit(2)

            continue  # next protocol
        continue  # next config secion