# .-=[ fdfc.py ]============================================================-. # # | Furcadia Data Flow Corrector 1.0 (c) by IceDragon of QuickFox.org | # # |==========================================================================| # # | This script aims to try and improve connectivity between Furcadia client | # # | and the Furcadia file server along with providing debugging ground for | # # | unusual fileserver behavior. This script was made for Farren Dustfur as | # # | an attempt to deal with his download stalls on dialup... Hope it works. | # # '-========================================================================-' # ###--# Imports #--### from socket import * from select import select from time import time ###--# Configuration #--### # Target address/port. target_addr = ( 'hades.furcadia.com', 21 ) # Local port. local_port = 1337 ###--# Global Variables #--### fdServer = None fdClients = [] ###--# Functions #--### def fdfc_listen (): global local_port, fdServer bind_addr = ( '127.0.0.1', local_port ) fd = socket( AF_INET, SOCK_STREAM, 0 ) fd.bind ( bind_addr ) fd.listen ( 3 ) fd.setblocking ( 0 ) fdServer = fd print "+++ Listening on %s:%d for incoming connections..." % bind_addr return True # fdfc_listen() def fdfc_handle_incoming (): global fdServer, fdClients, target_addr ( fdc, addr_info ) = fdServer.accept() print "+++ Incoming from %s:%d!" % addr_info fds = socket( AF_INET, SOCK_STREAM, 0 ) fds.connect ( target_addr ) fds.setblocking ( 0 ) fdc.setblocking ( 0 ) # Forming client data instance. client_data = { "client": fdc, "server": fds, "buffer": '', "bytesIn": 0, "bytesOut": 0, "connTime": time(), "packetSize": 1024, "phase": 'COMMAND' } fdClients.append( client_data ) return True # fdfc_handle_incoming() def fdfc_handle_S2C ( client_data ): global fdClients data = client_data['server'].recv( client_data['packetSize'] ) size = len( data ) client_data['bytesIn'] += size if( size <= 0 ): print "--- Received EOF from server - terminating corresponding connections..." client_data['server'].close() client_data['client'].close() print "iii Transfer rate: %ld bytes in, %ld bytes out." % ( client_data['bytesIn'], client_data['bytesOut'] ) fdClients.remove( client_data ) return if( client_data['phase'] == "RAW" ): sent_size = 0 while sent_size < size: ss = client_data['client'].send( data ) sent_size += ss data = data[ ss: ] # while return if( client_data['phase'] == "COMMAND" ): ( cmd, args ) = data.strip().split( ' ', 1 ) if( cmd == "44" ): ( fSize, nPackets, packetSize, startPacket ) = args.split( ' ' ) if( int( packetSize ) != client_data['packetSize'] ): print " !! WARNING: Server reset packetsize from %d to %d!" % ( client_data['packetSize'], int( packetSize ) ) print " >> (44) File exists (%s bytes), %s-packet burst from packet %s (Packet Size: %s) - entering transfer mode." % (fSize, nPackets, startPacket, packetSize) client_data['packetSize'] = int( packetSize ) client_data['phase'] = 'TRANSFER' elif( cmd == "41" ): print " >> (41) Warning: Target file not found on the server!" elif( cmd == "46" ): print " >> (46) Transfer abort command received!" elif( cmd == "47" ): print " >> (47) Transfer resume command received." elif( cmd == "59" ): print " >> (59) Transfer complete - switching to RAW mode (disabling functionality)..." client_data['phase'] = 'RAW' client_data['client'].send( data ) return if( client_data['phase'] == "TRANSFER" ): client_data['buffer'] += data size = len( client_data['buffer'] ) if( data.split( ' ', 1 )[0] == '59' ): print " ii Transfer complete - switching to RAW mode (disabling functionality)..." client_data['phase'] = 'RAW' client_data['client'].send( client_data['buffer'] ) client_data['client'].send( data ) return if( size < client_data['packetSize'] ): print " !! WARNING: Received data less than packet size (%d/%d) - awaiting completion..." % ( size, client_data['packetSize'] ) return elif( size > client_data['packetSize'] ): print " ii Buffer contains more than expected (%d/%d) - sending only the expected size..." % ( size, client_data['packetSize'] ) sent_size = 0 while sent_size < client_data['packetSize']: ss = client_data['client'].send( client_data['buffer'][ :(client_data['packetSize'] - sent_size) ] ) sent_size += ss client_data['buffer'] = client_data['buffer'][ ss: ] if( sent_size < client_data['packetSize'] ): print " !! WARNING: Sent less data than needed (%d/%d) - sending the rest..." % ( sent_size, client_data['packetSize'] ) continue elif( sent_size > client_data['packetSize'] ): print "!!! WARNING: Sent more data than needed (%d/%d) - programming error?.." % ( sent_size, client_data['packetSize'] ) # while return return # fdfc_handle_S2C() def fdfc_handle_C2S ( client_data ): global fdClients data = client_data['client'].recv( 1024 ) size = len( data ) client_data['bytesOut'] += size if( size <= 0 ): print "--- Received EOF from client - terminating corresponding connections..." client_data['server'].close() client_data['client'].close() print "iii Transfer rate: %ld bytes in, %ld bytes out." % ( client_data['bytesIn'], client_data['bytesOut'] ) fdClients.remove( client_data ) return if( client_data['phase'] == "RAW" ): sent_size = 0 while sent_size < size: ss = client_data['server'].send( data ) sent_size += ss data = data[ ss: ] # while return if( ' ' in data ): (cmd, args) = data.strip().split( ' ', 1 ) else: cmd = data.strip() if( client_data['phase'] == "COMMAND" ): if( cmd == "RC" ): client_data['packetSize'] = int( args.split( ' ' )[5] ) elif( cmd == "USER" ): print " ii File upload attempt detected - switching to RAW mode - corrector disabled." client_data['phase'] = 'RAW' elif( cmd == "SNCRC" ): print " >> Resynchronization from packet %s - entering TRANSFER mode..." % ( args ) client_data['phase'] = 'TRANSFER' client_data['server'].send( data ) return if( client_data['phase'] == "TRANSFER" ): if( cmd == "+MIS" ): print " !! WARNING: Client terminated the transfer (%s) - returning to COMMAND mode..." % ( data.strip() ) client_data['phase'] = 'COMMAND' client_data['server'].send( data ) return return # fdfc_handle_C2S() def fdfc_dispatch (): global fdClients, fdServer rlist = [] rlist.append( fdServer ) for client_data in fdClients: rlist.append( client_data['server'] ) rlist.append( client_data['client'] ) # for results = select( rlist, [], [], 0.2 )[0] if fdServer in results: fdfc_handle_incoming() for client_data in fdClients: if client_data['server'] in results: try: fdfc_handle_S2C( client_data ) except Exception, (errno,errstr): print "!!! WARNING: Unable to handle Server->Client data: [%d] %s" % ( errno, errstr ) print "--- Terminating connection..." client_data['server'].close() client_data['client'].close() fdClients.remove( client_data ) elif client_data['client'] in results: try: fdfc_handle_C2S( client_data ) except Exception, (errno,errstr): print "!!! WARNING: Unable to handle Client->Server data: [%d] %s" % ( errno, errstr ) print "--- Terminating connection..." client_data['server'].close() client_data['client'].close() fdClients.remove( client_data ) return # fdfc_dispatch() ###--# Initialization #--### print "--+ Furcadia Data Flow Corrector [FDFC] v1.0 by IceDragon +--\n" fdfc_listen() while True: fdfc_dispatch() print "!!! Program reached its end."