Hi,

I assume that hanging is not the proper behavior for freenetlib.py when it
encounters a non-existant key.
It has been doing that for the last week or so - I have only been testing
sporadically in that time. 
I believe that it didn't do it before - though I think I only test few
successfully. 

Most of my testing has been simulating freenet as a black box - just
write-once dictionary. Hopefully, this still should "theoretically."

I set the host to a remote host and changed the test string to gibberish
and the module just hangs on receive message from the sock. I have tried a
couple of servers and it does it with all of them.

I am attaching my modified version of freenetlib.py - all the mods have
"jsolbrig" in the comment



-------------- next part --------------
#!/usr/bin/env python

# A Freenet client module.  May rapidly change during the development of
# the Freenet protocal.
#
# freenetlib.py 0.03
#
# Copyright (C) 2000 Travis Bemann, Itamar Shtull-Trauring
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# more information on Freenet is availiable at http://freenet.sourceforge.net/

import re
import socket
import string
import os
import whrandom
import sha
import struct
import sys

# Default values for Freenet init parameters
#FREENET_DEFAULT_HOST = 'localhost' jsolbrig
FREENET_DEFAULT_HOST = socket.gethostbyaddr('63.248.222.29')[0]

FREENET_DEFAULT_PORT = 19114
FREENET_DEFAULT_SEARCH_DEPTH = 10
FREENET_VERSION = '1.2'
FREENET_DEFAULT_MIN_DEPTH = 5
FREENET_DEFAULT_MAX_DEPTH = 30
FREENET_DEFAULT_KEEP_ALIVE = 1
FREENET_DEFAULT_AUTO_CONNECT = 1

DEBUG = 0

error_reply = 'freenetlib.error_reply'
error_uniqueid = 'freenetlib.error_uniqueid'
error_version = 'freenetlib.error_version'
error_nodata = 'freenetlib.error_nodata'
error_keyexists = 'freenetlib.error_keyexists'

# Note -  This is a slightly modified version of a bit of Python
# code sent to me by Bill Trost <trost at cloud.rain.com>.
# This is fast as in it is fast to use in a program, not that the
# implementation used is necessarily fast.
def quick_sha1(s):
    return "%X%X%X%X%X" % struct.unpack("!lllll", sha.new(s).digest())

class Freenet:

    # Initialize an instance
    # - host: host of FreeNet server
    # - port: port of FreeNet server
    # - search_depth: maximum key search depth
    # - min_depth: minimum starting message depth
    # - max_depth: maximum starting message depth
    # - keep_alive: whether to keep the connection open
    # - auto_connect: whether to handshake automatically

    def __init__(self,
            host = FREENET_DEFAULT_HOST,
            port = FREENET_DEFAULT_PORT,
            search_depth = FREENET_DEFAULT_SEARCH_DEPTH,
            min_depth = FREENET_DEFAULT_MIN_DEPTH,
            max_depth = FREENET_DEFAULT_MAX_DEPTH,
            keep_alive = FREENET_DEFAULT_KEEP_ALIVE,
            auto_connect = FREENET_DEFAULT_AUTO_CONNECT):
        self.host = host
        self.port = port
        if search_depth < 1:
            raise ValueError, \
                'search_depth must be larger than zero'
        self.search_depth = search_depth
        if min_depth < 1:
            raise ValueError, 'min_depth must be larger than zero'
        if max_depth < 1:
            raise ValueError, 'max_depth must be larger than zero'
        if min_depth > max_depth:
            raise ValueError, \
                'min_depth may not be larger than max_depth'
        self.min_depth = min_depth
        self.max_depth = max_depth
        self.keep_alive = keep_alive
        self.response_closed = 1        
        if auto_connect:
            self.connect()

    def sendmesg(self, type, items, datafile=None, datalength=0):
        """ Construct message from dictionary, with optional file for data """
        if DEBUG:
            print "Sent:", type, items
        f = self.file
        f.write(type + '\n')
        for key, value in items.items():
            f.write("%s=%s\n" % (key, value))

        if datafile:
            if datalength:
                f.write("DataLength=%s\n" % datalength)
                f.write("Data\n")
                line = datafile.readline()
                while line:
                    f.write(line)
                    line = datafile.readline()
            else:
                datafile.seek(0, 2)
                datalength = datafile.tell()
                datafile.seek(0, 0)
                f.write('DataLength=%s\n' % datalength)
                f.write('Data\n')
                line = datafile.readline()
                while line:
                    f.write(line)
                    line = datafile.readline()
        else:
            f.write("EndMessage\n")

        f.flush()            

    def recvmesg(self, savefile=None):
        """ Read a message, writing it to a given file if need be """
        f = self.file
        dict = {}
        messagetype = string.rstrip(f.readline())

        line = string.rstrip(f.readline())
        while line and (line not in ['Data', 'EndMessage']):
            key, value = string.split(line, '=')
            dict[key] = value
            line = string.rstrip(f.readline())
        if line == 'Data':            
            #if not dict.has_key('DataLength'):
            #    raise error_nodatalength, "Message has Data but no DataLength."
            line = f.readline()
            readlength = 0
            while line:
                savefile.write(line)
                readlength = readlength + len(line)
                line = f.readline()
            #if readlength != int(dict['DataLength']):
            #    raise error_nodata, """DataLength supposed to be %s, but
            #        actual Data length is %s.""" % (dict['DataLength'], 
readlength)
        if DEBUG:
            print "Received:", messagetype, dict
        return messagetype, dict

    def getranduniqueid(self):
        """ Generate 64 bit hex id number """
        max = pow(2, 16) - 1
        result = ""
        for i in range(0, 4):
            result = result + ("%04x" % whrandom.randint(0, max))
        # the server strip leading 0 from uniqueids, so we do the same
        while result[0] == '0': 
            result = result[1:]
        return result

    def getranddepth(self):
        """ Returns a random number between self.min_depth 
            and self.max_depth.
        """
        return whrandom.randint(self.min_depth, self.max_depth)

    def connect(self):
        """ Connect to server """
        self.sock = socket.socket(socket.AF_INET,
            socket.SOCK_STREAM)
        self.sock.connect(self.host, self.port)
        self.response_closed = 0
        self.file = self.sock.makefile('rb+')            
        self._handshake()

    def close(self):
        """ close from server """
        self.response_closed = 1
        self.sock.close()

    def _handshake(self):
        """ Handshake with server.  """

        uid = self.getranduniqueid()
        if self.keep_alive:
            keep_alive = 'true'
        else:
            keep_alive = 'false'
        self.sendmesg('HandshakeRequest',
            {'UniqueID': uid,
            'Depth': 1,
            'HopsToLive': self.search_depth,
            'KeepAlive': keep_alive})
        type, items = self.recvmesg()
        if type == 'HandshakeRequest':
            try:
                self.sendmesg('HandshakeReply',
                    {'UniqueID': items['UniqueID'],
                    'Depth': items['Depth'],
                    'HopsToLive': items['HopsToLive'],
                    'KeepAlive': keep_alive,
                    'Version': FREENET_VERSION})
            except KeyError:
                self.close()
                raise error_reply, \
                    'Necessary request fields are missing'
        elif type == 'HandshakeReply':
            try:
                if items['Version'] != FREENET_VERSION:
                    self.close()
                    raise error_version, \
                        'Incompatible FreeNet version'
                if items['UniqueID'] != uid:
                    self.close()
                    raise error_uniqueid, \
                        'Incorrect unique ID'
            except KeyError:

                raise error_version, \
                    'Necessary reply fields are missing'
        else:
            self.close()
            raise error_reply, 'Unexpected message type'


    def insert(self, key, datafile, datalength = None):
        """ Insert file under key
            key: key to insert file under (unencrypted)
            data: file data
        """
        if not self.keep_alive and self.response_closed:
            self.connect()
        uid = self.getranduniqueid()
        if self.keep_alive:
            keep_alive = 'true'
        else:
            keep_alive = 'false'
        self.sendmesg('InsertRequest',
            {'UniqueID': uid,
            'Depth': self.getranddepth(),
            'HopsToLive': self.search_depth,
            'KeepAlive': keep_alive,
            'Source': 'tcp/127.0.0.1:19114',
            'SearchKey': quick_sha1(key)})
        type, items = self.recvmesg()
        if type == 'RequestFailed':
            if not self.keep_alive:
                self.close()
            raise error_reply, 'Request failed'
        elif type == 'DataReply':
            if not self.keep_alive:
                self.close()
            raise error_keyexists, 'Key already exists'
        elif type == 'TimedOut':
            if not self.keep_alive:
                self.close()
            raise error_nodata, \
                'Key already exists but no data was found'
        elif type == 'InsertReply':
            if items['UniqueID'] != uid:
                if not self.keep_alive:
                    self.close()
                raise error_uniqueid, \
                    'Unique ID mismatch'
            self.sendmesg('DataInsert',
                {'UniqueID': uid,
                'Depth': self.getranddepth(),
                'HopsToLive': self.search_depth,
                'KeepAlive': keep_alive,
                'Source': 'tcp/127.0.0.1:19114',
                'DataSource': 'tcp/127.0.0.1:19114'},
                datafile, datalength)
        else:
            if not self.keep_alive:
                self.close()
            raise error_reply, 'Unexpected message type'
        if not self.keep_alive:
            self.close()



    def request(self, key, savefile=sys.stdout):
        """ Request file under key
            key: key to request file from (unencrypted)
            returns: file data
        """
        if not self.keep_alive and self.response_closed:
            self.connect()
        uid = self.getranduniqueid()
        if self.keep_alive:
            keep_alive = 'true'
        else:
            keep_alive = 'false'
        self.sendmesg('DataRequest',
            {'UniqueID': uid,
            'Depth': self.getranddepth(),
            'HopsToLive': self.search_depth,
            'KeepAlive': keep_alive,
            'Source': 'tcp/127.0.0.1:19114',
            'SearchKey': quick_sha1(key)})
        type, items = self.recvmesg(savefile)
        if type == 'RequestFailed':
            if not self.keep_alive:
                self.close()
            raise error_reply, 'Request failed'
        elif type == 'TimedOut':
            if not self.keep_alive:
                self.close()
            raise error_keyexists, \
                'Key exists but no data was found'
        elif type == 'DataReply':
            if items['UniqueID'] != uid:
                if not self.keep_alive:
                    self.close()
                raise error_uniqueid, \
                    'Unique ID mismatch'
            return None
        else:
            if not self.keep_alive:
                self.close()
            raise error_reply, 'Unexpected message type'
        return None # shouldn't happen(?)

if __name__ == '__main__':
    fn = Freenet()
    #fn.request("keyindex.pl") jsolbrig
    fn.request("asdfsadf")

Reply via email to