Jump to content
Sign in to follow this  
nullschritt

TCP P2P, Event Driven Udf (Better!)

Recommended Posts

nullschritt

Hello, I am re-releasing hyperzap's Event Driven P2P UDF with some modifications.

Thanks to hyperzap for the original UDF.
 
New Features

  • Dynamic RegEx Headers/Footers
  • Supports Messages of Any Length (Including file transfers) [automatically]
  • Allows use of all symbols/letters in messages*

Pre-Modification Features

  • Relays messages over P2P network
  • Delivers messages to client only machines [that cannot port forward]
  • BootStrapping (optional) [Automatically connect to more peers]
  • Even Driven
  • LAN or WAN Mode
  • FAST (Takes 5-50ms per hop)

Known Limitations

  • Does not relay messages when emulated on linux with WINE. (still receives and sends messages)

* = You CANNOT use the "`" symbol in long range messages. To support this symbol, please replace it with it's html entity, urlencode, or use the INCLUDED _base64encode function on data you want to send long range.
 


How it works:
##################
When you start the UDF, it will start listening and accepting incoming connections. The UDF will, in the backround, continue to manage all your connections and initiate any new ones should you use the _P2P_Connect( IP) function. It will also manage the recieving and sending of data.
As a P2P node should, it will route data destined for others, and it will also maintain a healthy number of connections (the number which you define when you start the node).

This entire system is event driven, so you will register your functions with the UDF and the function will get called when the corresponding event happens. See the comments in the UDF for more information.
This works in the same way as Kip's TCP UDF.


Structure:
##################
The first thing you must do is start the Node:
_P2P_Start_Node( Node Identifier, Port, Max peers, Bootstrap mode, Bootstrap max, local/global, IP)

All the Parameters are pretty self expanatory. The Node identifier is the name of your computer in the network. It must not change across sessions and it must be unique. It is used to identify you in your network.

Bootstrapping means autoconnecting. if Bootstrap mode is 1, It will autoconnect you to others until you reach your Bootstrap Max.

Local/Global - Put 0 if working on a LAN, 1 if over internet.

IP - (Optional). You shouldn't need to change this. It is the IP of the listening port.

The second thing is declare your functions. This tells the UDF which functions to call when something happens. EG:
_P2P_Register_Event($P2P_RECEIVE, "my_recieve_function")
So, this will call the function my_recieve_function when data from a peer is recieved. See the comments at the beginning of the UDF for a complete list of Event values, and the parameters for your functions.

Then fire away!
_P2P_Send( $socket, "MY DATA!!)
_P2P_Connect( "127.0.0.1")
_P2P_Broadcast( "My data")

When your done,

_P2P_Stop_Node()

The UDF:

#cs ----------------------------------------------------------------------------

 AutoIt Version: 3.3.0.0
 Author:         Hyperzap.
 Modified By: NullSchritt (Stormageddon)
                 Design concepts were first coined by Kip in his "Event driven TCP UDF"
                 Zatorg's Asynchronous Sockets UDF is also used.
 Script Function:
 
    This UDF is a re-write of Kip's 'Event driven TCP UDF'. Thx Kip.
    It aims to generate simple TCP, event driven functionality for P2P programs, As
    opposed to client-server oriented communication as in Kip's UDF.
    Evidently, this functionality is not possible in Kips original releases.
    
    Apart from the obvious conponents of such a code, like Recv, Send, Connect,
    Listen, other important P2P routines have been coded. These include things 
    like; Universal identifiers, bootstrapping mechanisms, peers
    discovery, and message routing.
    
    MINOR DRAWBACKS:
    You must replace "`" in messages with it's html entity or some other 
    symbol during long range message routing.
#ce ----------------------------------------------------------------------------

$UDF_Version = "1.33 STABLE"
$console_out = False
$STANDARD_MESSAGE_LIFE = 200
#cs
Functions:

    _P2P_Start_Node( Node Identifier, Port, Max peers, Bootstrap mode, Boostrap max, local/global, IP)
    _P2P_Stop_Node()
    _P2P_Connect( IP)
    _P2P_Send( Socket, Data)
    _P2P_Disconnect_Peer( Socket)
    _P2P_Send_Message( Address, Data)
    
    _P2P_Register_Event($iEvent, $sFunction)


Register event values:

    $P2P_AUX_DATA           ; Function ($hSocket, $DataType, $Data);When things like IP and Node identifier are discovered.
    $P2P_MESSAGE            ; Function ($hSocket, $message, $iError) ;When long distance messages are recieved.
    $P2P_RECEIVE            ; Function ($hSocket, $sReceived, $iError)
    $P2P_CONNECT            ; Function ($hSocket, $iError)                  
    $P2P_DISCONNECT         ; Function ($hSocket, $iError)
    $P2P_NEWCONNECTION      ; Function ($hSocket, $iError) 



   Also, please call the function  peer_broadcast() periodically in your program. Failing to do this will
   Seriously cripple the bootstrapping mechanism (unless you program is ALWAYS recieving data at least once
   every 40 seconds, In which case the mechanism will trigger automatically).
#ce

Global Const $fd_read = 1
Global Const $fd_write = 2
Global Const $fd_oob = 4
Global Const $fd_accept = 8
Global Const $fd_connect = 16
Global Const $fd_close = 32
Global $hws2_32 = -1
Global Const $tcp_send = 1
Global Const $tcp_receive = 2
Global Const $tcp_connect = 4
Global Const $tcp_disconnect = 8
Global Const $tcp_newclient = 16
Global Const $iploc_local = 64
Global Const $iploc_global = 128
Global Const $p2p_message = 256
Global Const $p2p_receive = 512
Global Const $p2p_connect = 1024
Global Const $p2p_disconnect = 2048
Global Const $p2p_newconnection = 4096
Global Const $p2p_aux_data = 8192
Global $total_connected = 0
Global $max_connections
Global $listening_socket
Global $node_ip
Global $node_port
Global $node_identifier
Global $node_ext_ip
Global $bootstrap_mode
Global $bootstrap_max
Global $peer_timer
Global $total_id_known = 0
Global $known_id[200]
Global $connectfunc = ""
Global $recievefunc = ""
Global $disconnectfunc = ""
Global $newconnectionfunc = ""
Global $messagefunc = ""
Global $auxfunc = ""
Global $main_socket_address = ""
TCPStartup()
Global Const $__tcp_window = GUICreate("Async Sockets UDF")

Func _p2p_start_node($node_id, $port, $max_peers, $bootstrapmode, $bootstrapmax, $location, $ip = "0.0.0.0")
    Local $startuptimer = TimerInit()
    $listening_socket = ___asocket()
    $main_socket_address = $listening_socket
    ___asockselect($listening_socket, $__tcp_window, 1024, $fd_accept)
    GUIRegisterMsg(1024, "Listensocket_data_")
    ___asocklisten($listening_socket, $ip, $port)
    If $bootstrapmode = 0 Then $bootstrap_mode = $iploc_local
    If $bootstrapmode = 1 Then $bootstrap_mode = $iploc_global
    $bootstrap_max = $bootstrapmax
    $max_connections = $max_peers
    $node_ip = $ip
    $node_port = $port
    $node_identifier = $node_id
    $peer_timer = TimerInit()
    If $location = 1 Then $node_ext_ip = _get_ip()
    If $location = 0 Then $node_ext_ip = @IPAddress1
    Global $socket_handle_array[$max_peers + 1]
    Global $node_identifier_array[$max_peers + 1]
    Global $node_ip_array[$max_peers + 1]
    Global $node_peer_reachable_list[$max_peers + 1]
    For $x = 0 To $max_peers Step 1
        $socket_handle_array[$x] = -1
        $node_identifier_array[$x] = -1
        $node_ip_array[$x] = -1
        $node_peer_reachable_list[$x] = -1
    Next
    If $console_out = True Then ConsoleWrite(@CRLF & @CRLF & "P2P_: Node started")
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Port: " & $node_port)
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Ext IP: " & $node_ext_ip)
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Max peers: " & $max_connections)
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Identifier: " & $node_identifier)
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Engine Version: " & $udf_version)
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Startup time: " & TimerDiff($startuptimer) & "ms" & @CRLF)
EndFunc

Func _p2p_stop_node()
    ___asockshutdown($listening_socket)
    TCPCloseSocket($listening_socket)

        $socket_handle_array= -1
        $node_identifier_array= -1
        $node_ip_array= -1
        $node_peer_reachable_list= -1
    $max_connections = 0
    $listening_socket = ""
    $node_ip = ""
    $node_port = ""
    $node_identifier = -1
    $node_ext_ip = ""
    $bootstrap_mode = ""
    $bootstrap_max = 0
    $connectfunc = ""
    $recievefunc = ""
    $disconnectfunc = ""
    $newconnectionfunc = ""
    $messagefunc = ""
    $auxfunc = ""
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Engine shutdown successful.")
EndFunc

Func listensocket_data_($hwnd, $imsgid, $wparam, $lparam)
    Local $socketinquestion = $wparam
    Local $ierror = ___hiword($lparam)
    Local $ievent = ___loword($lparam)
    Abs($hwnd)
    If $imsgid = 1024 Then
        If $ievent = $fd_accept Then
            If NOT $ierror Then
                $arrayslot = findfreearrayslot()
                If $arrayslot = "error" Then
                    $newsocket = TCPAccept($socketinquestion)
                    TCPSend($newsocket, "`" & _generateheader(2) & "`NOSLOT`" & _generateheader(1))
                    TCPCloseSocket($newsocket)
                EndIf
                $newsocket = TCPAccept($socketinquestion)
                ___asockselect($newsocket, $__tcp_window, 1024 + $arrayslot, BitOR($fd_read, $fd_connect, $fd_close))
                GUIRegisterMsg(1024 + $arrayslot, "Opensocket_data_")
                $socket_handle_array[$arrayslot] = $newsocket
                $node_identifier_array[$arrayslot] = -1
                $node_ip_array[$arrayslot] = -1
                TCPSend($newsocket, "`" & "`IP`" & $node_ext_ip & _generateheader(1))
                TCPSend($newsocket, "`" & "`ID`" & $node_identifier & _generateheader(1))
                peer_broadcast_to_peer($newsocket)
                $total_connected += 1
                Call($newconnectionfunc, $newsocket, $ierror)
            Else
                Call($newconnectionfunc, 0, $ierror)
            EndIf
        ElseIf $ievent = $fd_connect Then
            Call($connectfunc, $newsocket, $ierror)
        EndIf
    EndIf
EndFunc

Func _generateheader($type = 1)
    $alpha = StringSplit("PRODY", "", 3)
    $str = ""
    If $type = 1 Then
        $str = "--P2P:"
        For $i = 1 To 5
            $str &= Random(0, 9, 1) & $alpha[Random(0, 4, 1)]
        Next
        Return $str
    EndIf
    If $type = 2 Then
        $str = "--P2P:"
        For $i = 1 To 5
            $str &= $alpha[Random(0, 4, 1)] & Random(0, 9, 1)
        Next
        Return $str
    EndIf
EndFunc

Func findfreearrayslot()
    $newconnnum = -1
    For $x = 1 To $max_connections
        If $socket_handle_array[$x] = -1 Then
            $newconnnum = $x
            ExitLoop
        EndIf
    Next
    If $newconnnum = -1 Then Return "error"
    Return $newconnnum
EndFunc

Func opensocket_data_($hwnd, $imsgid, $wparam, $lparam)
    Local $ierror = ___hiword($lparam)
    Local $ievent = ___loword($lparam)
    Abs($hwnd)
    Local $array_slot = $imsgid - 1024
    Local $socketid = $socket_handle_array[$array_slot]
    If $socketid = -1 Then Return 0
    Switch $ievent
        Case $fd_read
            $rawrecv = TCPRecv($socketid, 2048)
            If $rawrecv <> "" Then
                $regex = StringRegExp($rawrecv, "--P2P:([0-9](.*?)[PRODY]){5}", 2)
                If IsArray($regex) Then
                    $rawrecv = StringReplace($rawrecv, $regex[0], "")
                    recvprocess($rawrecv, $socketid, $array_slot, $ierror)
                Else
                    $dre = 0
                    Do
                        $recvtmp = TCPRecv($socketid, 2048)
                        If $recvtmp = "" Then
                            ExitLoop
                        EndIf
                        $rawrecv &= $recvtmp
                        $regex = StringRegExp($rawrecv, "--P2P:([0-9](.*?)[PRODY]){5}", 2)
                        Sleep(1)
                    Until IsArray($regex)
                    If IsArray($regex) Then
                        $rawrecv = StringReplace($rawrecv, $regex[0], "")
                        recvprocess($rawrecv, $socketid, $array_slot, $ierror)
                    EndIf
                EndIf
            EndIf
        Case $fd_close
            ___asockshutdown($socketid)
            TCPCloseSocket($socketid)
            Call($disconnectfunc, $socketid, $ierror)
            $socket_handle_array[$array_slot] = -1
            $node_identifier_array[$array_slot] = -1
            $node_ip_array[$array_slot] = -1
            $node_peer_reachable_list[$array_slot] = -1
            $total_connected -= 1
            If TimerDiff($peer_timer) > 40000 Then
                peer_broadcast()
            EndIf
        Case $fd_connect
            If $ierror Then
                If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Connection failed: " & $socketid)
                $socket_handle_array[$array_slot] = -1
                $node_identifier_array[$array_slot] = -1
                $node_ip_array[$array_slot] = -1
                $node_peer_reachable_list[$array_slot] = -1
                $total_connected -= 1
                Call($connectfunc, $socketid, $ierror)
            Else
                Call($connectfunc, $socketid, $ierror)
                If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Connection established: " & $socketid)
                TCPSend($socket_handle_array[$array_slot], "`" & "`IP`" & $node_ext_ip & _generateheader(1))
                TCPSend($socket_handle_array[$array_slot], "`" & "`ID`" & $node_identifier & _generateheader(1))
                peer_broadcast_to_peer($socket_handle_array[$array_slot])
            EndIf
            If TimerDiff($peer_timer) > 40000 Then
                peer_broadcast()
            EndIf
    EndSwitch
EndFunc

Func recvprocess($rawrecv, $socketid, $array_slot, $ierror)
    If StringLen($rawrecv) < 1 Then Return 0
    $regex = StringRegExp($rawrecv, "--P2P:([0-9](.*?)[PRODY]){5}", 2)
    If IsArray($regex) Then
        Local $single_data = StringSplit($rawrecv, $regex[0], 1)
    Else
        Local $single_data = $rawrecv
    EndIf
    If UBound($single_data) < 1 Then Return 
    For $k = 1 To $single_data[0] Step 1
        If $single_data[$k] = "" Then ContinueLoop
        If $single_data[$k] = " " Then ContinueLoop
        If StringRegExp($single_data[$k], "--P2P:([PRODY](.*?)[0-9]){5}") Then
            node_data_process($socketid, $single_data[$k], $array_slot, $ierror)
        Else
            Call($recievefunc, $socketid, $single_data[$k], $ierror)
        EndIf
    Next
    If TimerDiff($peer_timer) > 40000 Then
        peer_broadcast()
    EndIf
EndFunc

Func node_data_process($socketid, $data, $array_slot, $ierror)
    Local $split = StringSplit($data, "`")
    If $split[3] = "IP" Then
        $node_ip_array[$array_slot] = $split[4]
        Call($auxfunc, $socketid, "IP", $split[4])
    EndIf
    If $split[3] = "ID" Then
        $node_identifier_array[$array_slot] = $split[4]
        Call($auxfunc, $socketid, "ID", $split[4])
    EndIf
    If $split[3] = "PEER" Then bootstrap($socketid, $split[4], $array_slot, $ierror)
    If $split[3] = "IDLIST" Then
        $node_peer_reachable_list[$array_slot] = $split[4]
        Call($auxfunc, $socketid, "NODE-REACHABLE", $split[4])
    EndIf
    If $split[3] = "MESSAGE" Then
        route_message($socketid, $data, $array_slot)
    EndIf
EndFunc

Func bootstrap($socketid, $split, $array_slot, $ierror)
    If $bootstrap_mode = $iploc_local Then Return 9
    If $total_connected >= $bootstrap_max Then Return 8
    Local $connectpeers = Round(($bootstrap_max / $total_connected / 2), 0)
    If $connectpeers < $bootstrap_max AND $connectpeers = 0 Then $connectpeers = 1
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Bootstrap stats: " & $connectpeers & ":" & $total_connected & "/" & $bootstrap_max)
    $peers = StringSplit($split, ";")
    For $d = 1 To $connectpeers Step 1
        $error = False
        $rndarray = Random(1, $peers[0], 1)
        If $peers[$rndarray] = "" OR $peers[$rndarray] = " " Then ContinueLoop
        For $u = 0 To $max_connections Step 1
            If $peers[$rndarray] = $node_ip_array[$u] Then
                If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Bootstrap: connectee already connected! reselecting...")
                $error = True
                ExitLoop
            EndIf
        Next
        If $error = True Then ContinueLoop
        _p2p_connect($peers[$rndarray])
    Next
EndFunc

Func peer_broadcast()
    $peer_timer = TimerInit()
    Local $peers = ""
    Local $idlist = ""
    For $a = 0 To $max_connections Step 1
        If $node_ip_array[$a] <> -1 Then $peers &= $node_ip_array[$a] & ";"
        If $node_identifier_array[$a] <> -1 Then $idlist &= $node_identifier_array[$a] & ";"
    Next
    _p2p_broadcast("`" & _generateheader(2) & "`PEER`" & $peers)
    _p2p_broadcast("`" & _generateheader(2) & "`IDLIST`" & $idlist)
EndFunc

Func peer_broadcast_to_peer($socket)
    Local $peers = ""
    Local $idlist = ""
    For $a = 0 To $max_connections Step 1
        If $node_ip_array[$a] <> -1 Then $peers &= $node_ip_array[$a] & ";"
        If $node_identifier_array[$a] <> -1 Then $idlist &= $node_identifier_array[$a] & ";"
    Next
    TCPSend($socket, "`" & _generateheader(2) & "`PEER`" & $peers & _generateheader(1))
    TCPSend($socket, "`" & _generateheader(2) & "`IDLIST`" & $idlist & _generateheader(1))
EndFunc

Func route_message($socket, $message, $array_slot)
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: MessageRouting started")
    Local $timer = TimerInit()
    Local $split = StringSplit($message, "`")
    If $split[0] < 6 Then Return 1
    Local $ttl = $split[4]
    Local $from = $split[5]
    Local $address = $split[6]
    Local $identifier = $split[7]
    Local $begin = "`" & _generateheader(2) & "`MESSAGE`" & $ttl & "`" & $from & "`" & $address & "`" & $identifier & "`"
    Local $cutlen = StringLen($message) - StringLen($begin)
    If idknown($split[7]) = True Then
        If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message-ID known.")
        If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message Routing Finished: " & Round(TimerDiff($timer), 1))
        Return 1
    EndIf
    If $address = $node_identifier Then
        Local $messagetrim = StringRight($message, $cutlen)
        Local $messagetrim = StringRight($message, $cutlen)
        If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message-Inbound")
        If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message Routing Finished: " & Round(TimerDiff($timer), 1))
        Call($messagefunc, $socket, $messagetrim, $from, 0)
        Return 1
    EndIf
    If $ttl = 0 Then
        If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message-TTL Exceeded.")
        If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message Routing Finished: " & Round(TimerDiff($timer), 1))
        Return 1
    EndIf
    $messagefull = "`" & _generateheader(2) & "`MESSAGE`" & ($ttl - 1) & "`" & $from & "`" & $address & "`" & $identifier & "`" & StringRight($message, $cutlen)
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Starting First level Routing: " & $total_connected)
    For $qwe = 0 To $max_connections Step 1
        If $address = $node_identifier_array[$qwe] Then
            _p2p_send($socket_handle_array[$qwe], $messagefull)
            If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message-Destination located.")
            If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message Routing Finished: " & Round(TimerDiff($timer), 1))
            Return 1
        EndIf
    Next
    If $socket = -1 Then
        _p2p_broadcast($messagefull)
        If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message Routing Finished: " & Round(TimerDiff($timer), 1))
        Return 1
    Else
        For $qwe = 0 To $max_connections Step 1
            If $socket_handle_array[$qwe] = -1 Then ContinueLoop
            If StringInStr($node_peer_reachable_list[$qwe], $address) > 0 Then
                _p2p_send($socket_handle_array[$qwe], $messagefull)
                If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message-2nd Level destination located. ")
                If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message Routing Finished: " & Round(TimerDiff($timer), 1))
                Return 1
            EndIf
        Next
    EndIf
    For $qwe = 0 To $max_connections Step 1
        If $socket_handle_array[$qwe] = -1 Then ContinueLoop
        If $node_identifier_array[$qwe] = $node_identifier_array[$array_slot] Then ContinueLoop
        If StringInStr($node_peer_reachable_list[$array_slot], $node_identifier_array[$qwe]) > 0 Then ContinueLoop
        _p2p_send($socket_handle_array[$qwe], $messagefull)
    Next
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message Routing Finished: " & Round(TimerDiff($timer), 1))
EndFunc

Func _p2p_send_message($idaddress, $indata, $from = "Null")
    Local $messageid = Round(((Random(0, 99999999999, 1) * @YDAY) / @MIN) * (Random(5, 1376, 1) / (@SEC * 100)), 0)
    Local $ttl = $standard_message_life
    Local $fullmessage = "`" & _generateheader(2) & "`MESSAGE`" & $ttl & "`" & $from & "`" & $idaddress & "`" & $messageid & "`" & $indata & _generateheader(1)
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Message generated: " & $messageid)
    route_message(-1, $fullmessage, -1)
EndFunc

Func idknown($id)
    For $idcount = 0 To $total_id_known Step 1
        If $known_id[$idcount] = $id Then Return True
    Next
    $total_id_known += 1
    If $total_id_known > 198 Then $total_id_known = 0
    $known_id[$total_id_known] = $id
    Return False
EndFunc

Func _p2p_send($socket, $data)
    TCPSend($socket, $data & _generateheader(1))
EndFunc

Func _p2p_disconnect_peer($socket)
    Local $it_socket = ""
    Local $array_slot = ""
    For $count = 0 To $max_connections Step 1
        If $socket_handle_array[$count] = $socket Then
            $it_socket = $socket
            $array_slot = $count
            ExitLoop
        EndIf
    Next
    If $it_socket = "" Then Return False
    ___asockshutdown($it_socket)
    TCPCloseSocket($it_socket)
    $socket_handle_array[$array_slot] = -1
    $node_identifier_array[$array_slot] = -1
    $node_ip_array[$array_slot] = -1
    $node_peer_reachable_list[$array_slot] = -1
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Peer " & $it_socket & " closed.")
    Return True
EndFunc

Func _p2p_connect($conn_ip)
    For $u = 0 To $max_connections Step 1
        If $conn_ip = $node_ip_array[$u] Then
            If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Unable to connect-IP is already connected!")
            Return 1
        EndIf
    Next
    If $conn_ip = "" Then Return 0
    If $conn_ip = " " Then Return 0
    If $conn_ip = "0" Then Return 0
    If $conn_ip = ";" Then Return 0
    If $conn_ip = @CRLF Then Return 0
    $arrayslot = findfreearrayslot()
    If $arrayslot = "error" Then
        If $listening_socket = "" Then
            If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Unable to connect-node services offline!")
            Return 3
        EndIf
        If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Unable to connect-peer limit reached!")
        Return 2
    EndIf
    If $console_out = True Then ConsoleWrite(@CRLF & "P2P_: Attempting Connection: " & $conn_ip)
    Local $socketid = ___asocket()
    ___asockselect($socketid, $__tcp_window, 1024 + $arrayslot, BitOR($fd_connect, $fd_read, $fd_close))
    GUIRegisterMsg(1024 + $arrayslot, "Opensocket_data_")
    $socket_handle_array[$arrayslot] = $socketid
    $node_identifier_array[$arrayslot] = -1
    $node_ip_array[$arrayslot] = $conn_ip
    $return = ___asockconnect($socketid, $conn_ip, $node_port)
    $total_connected += 1
    If @extended Then
        If $console_out = True Then ConsoleWrite(@CRLF & "_P2P: Connection Established.")
        Call($connectfunc, $socketid, 0)
    EndIf
EndFunc

Func _p2p_broadcast($data)
    Local $i
    For $i = 0 To $max_connections Step 1
        If $socket_handle_array[$i] <> -1 Then TCPSend($socket_handle_array[$i], $data & _generateheader(1))
    Next
    Return True
EndFunc

Func _p2p_peerlist()
    Local $alist[$max_connections + 1][3], $alist_count = 1
    For $i = 1 To $max_connections
        If $socket_handle_array[$i] <> -1 Then
            $alist[$alist_count][0] = $socket_handle_array[$i]
            $alist[$alist_count][1] = $node_identifier_array[$i]
            $alist[$alist_count][2] = $node_ip_array[$i]
            $alist_count += 1
        EndIf
    Next
    $alist[0][0] = $alist_count
    Return $alist
EndFunc

Func _p2p_register_event($ievent, $sfunction)
    If $ievent = $p2p_aux_data Then $auxfunc = $sfunction
    If $ievent = $p2p_receive Then $recievefunc = $sfunction
    If $ievent = $p2p_connect Then $connectfunc = $sfunction
    If $ievent = $p2p_disconnect Then $disconnectfunc = $sfunction
    If $ievent = $p2p_newconnection Then $newconnectionfunc = $sfunction
    If $ievent = $p2p_message Then $messagefunc = $sfunction
EndFunc

Func ___asocket($iaddressfamily = 2, $itype = 1, $iprotocol = 6)
    If $hws2_32 = -1 Then $hws2_32 = DllOpen("Ws2_32.dll")
    Local $hsocket = DllCall($hws2_32, "uint", "socket", "int", $iaddressfamily, "int", $itype, "int", $iprotocol)
    If @error Then
        SetError(1, @error)
        Return -1
    EndIf
    If $hsocket[0] = -1 Then
        SetError(2, ___wsagetlasterror())
        Return -1
    EndIf
    Return $hsocket[0]
EndFunc

Func ___asockshutdown($hsocket)
    If $hws2_32 = -1 Then $hws2_32 = DllOpen("Ws2_32.dll")
    Local $iret = DllCall($hws2_32, "int", "shutdown", "uint", $hsocket, "int", 2)
    If @error Then
        SetError(1, @error)
        Return False
    EndIf
    If $iret[0] <> 0 Then
        SetError(2, ___wsagetlasterror())
        Return False
    EndIf
    Return True
EndFunc

Func ___asockclose($hsocket)
    If $hws2_32 = -1 Then $hws2_32 = DllOpen("Ws2_32.dll")
    Local $iret = DllCall($hws2_32, "int", "closesocket", "uint", $hsocket)
    If @error Then
        SetError(1, @error)
        Return False
    EndIf
    If $iret[0] <> 0 Then
        SetError(2, ___wsagetlasterror())
        Return False
    EndIf
    Return True
EndFunc

Func ___asockselect($hsocket, $hwnd, $uimsg, $ievent)
    If $hws2_32 = -1 Then $hws2_32 = DllOpen("Ws2_32.dll")
    Local $iret = DllCall($hws2_32, "int", "WSAAsyncSelect", "uint", $hsocket, "hwnd", $hwnd, "uint", $uimsg, "int", $ievent)
    If @error Then
        SetError(1, @error)
        Return False
    EndIf
    If $iret[0] <> 0 Then
        SetError(2, ___wsagetlasterror())
        Return False
    EndIf
    Return True
EndFunc

Func ___asocklisten($hsocket, $sip, $uiport, $imaxpending = 5)
    Local $iret
    Local $staddress
    If $hws2_32 = -1 Then $hws2_32 = DllOpen("Ws2_32.dll")
    $staddress = ___sockaddr($sip, $uiport)
    If @error Then
        SetError(@error, @extended)
        Return False
    EndIf
    $iret = DllCall($hws2_32, "int", "bind", "uint", $hsocket, "ptr", DllStructGetPtr($staddress), "int", DllStructGetSize($staddress))
    If @error Then
        SetError(3, @error)
        Return False
    EndIf
    If $iret[0] <> 0 Then
        $staddress = 0
        SetError(4, ___wsagetlasterror())
        Return False
    EndIf
    $iret = DllCall($hws2_32, "int", "listen", "uint", $hsocket, "int", $imaxpending)
    If @error Then
        SetError(5, @error)
        Return False
    EndIf
    If $iret[0] <> 0 Then
        $staddress = 0
        SetError(6, ___wsagetlasterror())
        Return False
    EndIf
    Return True
EndFunc

Func ___asockconnect($hsocket, $sip, $uiport)
    Local $iret
    Local $staddress
    If $hws2_32 = -1 Then $hws2_32 = DllOpen("Ws2_32.dll")
    $staddress = ___sockaddr($sip, $uiport)
    If @error Then
        SetError(@error, @extended)
        Return False
    EndIf
    $iret = DllCall($hws2_32, "int", "connect", "uint", $hsocket, "ptr", DllStructGetPtr($staddress), "int", DllStructGetSize($staddress))
    If @error Then
        SetError(3, @error)
        Return False
    EndIf
    $iret = ___wsagetlasterror()
    If $iret = 10035 Then
        Return True
    EndIf
    SetExtended(1)
    Return True
EndFunc

Func ___sockaddr($sip, $iport, $iaddressfamily = 2)
    Local $iret
    Local $staddress
    If $hws2_32 = -1 Then $hws2_32 = DllOpen("Ws2_32.dll")
    $staddress = DllStructCreate("short; ushort; uint; char[8]")
    If @error Then
        SetError(1, @error)
        Return False
    EndIf
    DllStructSetData($staddress, 1, $iaddressfamily)
    $iret = DllCall($hws2_32, "ushort", "htons", "ushort", $iport)
    DllStructSetData($staddress, 2, $iret[0])
    $iret = DllCall($hws2_32, "uint", "inet_addr", "str", $sip)
    If $iret[0] = -1 Then
        $staddress = 0
        SetError(2, ___wsagetlasterror())
        Return False
    EndIf
    DllStructSetData($staddress, 3, $iret[0])
    Return $staddress
EndFunc

Func ___wsagetlasterror()
    If $hws2_32 = -1 Then $hws2_32 = DllOpen("Ws2_32.dll")
    Local $iret = DllCall($hws2_32, "int", "WSAGetLastError")
    If @error Then
        SetExtended(1)
        Return 0
    EndIf
    Return $iret[0]
EndFunc

Func ___makelong($loword, $hiword)
    Return BitOR($hiword * 65536, BitAND($loword, 65535))
EndFunc

Func ___hiword($long)
    Return BitShift($long, 16)
EndFunc

Func ___loword($long)
    Return BitAND($long, 65535)
EndFunc

Func _get_ip()
    Local $ip, $t_ip
    If InetGet("http://checkip.dyndns.org/?rnd1=" & Random(1, 65536) & "&rnd2=" & Random(1, 65536), @TempDir & "\~ip.tmp") Then
        $ip = FileRead(@TempDir & "\~ip.tmp", FileGetSize(@TempDir & "\~ip.tmp"))
        FileDelete(@TempDir & "\~ip.tmp")
        $ip = StringTrimLeft($ip, StringInStr($ip, ":") + 1)
        $ip = StringTrimRight($ip, StringLen($ip) - StringInStr($ip, "/") + 2)
        $t_ip = StringSplit($ip, ".")
        If $t_ip[0] = 4 AND StringIsDigit($t_ip[1]) AND StringIsDigit($t_ip[2]) AND StringIsDigit($t_ip[3]) AND StringIsDigit($t_ip[4]) Then
            Return $ip
        EndIf
    EndIf
    If InetGet("http://www.whatismyip.com/?rnd1=" & Random(1, 65536) & "&rnd2=" & Random(1, 65536), @TempDir & "\~ip.tmp") Then
        $ip = FileRead(@TempDir & "\~ip.tmp", FileGetSize(@TempDir & "\~ip.tmp"))
        FileDelete(@TempDir & "\~ip.tmp")
        $ip = StringTrimLeft($ip, StringInStr($ip, "Your ip is") + 10)
        $ip = StringLeft($ip, StringInStr($ip, " ") - 1)
        $ip = StringStripWS($ip, 8)
        $t_ip = StringSplit($ip, ".")
        If $t_ip[0] = 4 AND StringIsDigit($t_ip[1]) AND StringIsDigit($t_ip[2]) AND StringIsDigit($t_ip[3]) AND StringIsDigit($t_ip[4]) Then
            Return $ip
        EndIf
    EndIf
    SetError(1)
    Return -1
EndFunc

Func _base64decode($data)
    Local $opcode = "0xC81000005356578365F800E8500000003EFFFFFF3F3435363738393A3B3C3DFFFFFF00FFFFFF000102030405060708090A0B0C0D0E0F10111213141516171819FFFFFFFFFFFF1A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132338F45F08B7D0C8B5D0831D2E9910000008365FC00837DFC047D548A034384C0750383EA033C3D75094A803B3D75014AB00084C0751A837DFC047D0D8B75FCC64435F400FF45FCEBED6A018F45F8EB1F3C2B72193C7A77150FB6F083EE2B0375F08A068B75FC884435F4FF45FCEBA68D75F4668B06C0E002C0EC0408E08807668B4601C0E004C0EC0208E08847018A4602C0E00624C00A46038847028D7F038D5203837DF8000F8465FFFFFF89D05F5E5BC9C21000"
    Local $codebuffer = DllStructCreate("byte[" & BinaryLen($opcode) & "]")
    DllStructSetData($codebuffer, 1, $opcode)
    Local $ouput = DllStructCreate("byte[" & BinaryLen($data) & "]")
    Local $ret = DllCall("user32.dll", "int", "CallWindowProc", "ptr", DllStructGetPtr($codebuffer), "str", $data, "ptr", DllStructGetPtr($ouput), "int", 0, "int", 0)
    Return BinaryMid(DllStructGetData($ouput, 1), 1, $ret[0])
EndFunc

Func _base64encode($data, $linebreak = 76)
    Local $opcode = "0x5589E5FF7514535657E8410000004142434445464748494A4B4C4D4E4F505152535455565758595A6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435363738392B2F005A8B5D088B7D108B4D0CE98F0000000FB633C1EE0201D68A06880731C083F901760C0FB6430125F0000000C1E8040FB63383E603C1E60409C601D68A0688470183F90176210FB6430225C0000000C1E8060FB6730183E60FC1E60209C601D68A06884702EB04C647023D83F90276100FB6730283E63F01D68A06884703EB04C647033D8D5B038D7F0483E903836DFC04750C8B45148945FC66B80D0A66AB85C90F8F69FFFFFFC607005F5E5BC9C21000"
    Local $codebuffer = DllStructCreate("byte[" & BinaryLen($opcode) & "]")
    DllStructSetData($codebuffer, 1, $opcode)
    $data = Binary($data)
    Local $input = DllStructCreate("byte[" & BinaryLen($data) & "]")
    DllStructSetData($input, 1, $data)
    $linebreak = Floor($linebreak / 4) * 4
    Local $oputputsize = Ceiling(BinaryLen($data) * 4 / 3)
    $oputputsize = $oputputsize + Ceiling($oputputsize / $linebreak) * 2 + 4
    Local $ouput = DllStructCreate("char[" & $oputputsize & "]")
    DllCall("user32.dll", "none", "CallWindowProc", "ptr", DllStructGetPtr($codebuffer), "ptr", DllStructGetPtr($input), "int", BinaryLen($data), "ptr", DllStructGetPtr($ouput), "uint", $linebreak)
    Return DllStructGetData($ouput, 1)
EndFunc 

 
Usage Example:
P2Ptest.au3

Edited by nullschritt

Share this post


Link to post
Share on other sites
nullschritt

Reserved for previous versions.

Share this post


Link to post
Share on other sites
nullschritt

Hi this looks interesting.

Can you aid me a boost in the right direction?

Is lets say I develop a app for voting (poll) 

Do I setup my client as the destination and then give out my WAN and PORT to the peers and this way they build a list of peers?

Also I plan on using SQLite for the voting so I guess the peers need to build a voting list?

?

All nodes act as both a server and a client. You can easily build a simple server node though. The server node needs to simply be running on the same port as the node in your program. In your program enable bootstrapping. Whenever a new pc connects to this server, it will notify all connected clients of the new peer, and they will all try to connect.

You can always resolve a hostname to an ip address, and attempt to connect to it automatically with your code. You could also ask the user for an ip if that fails, or try a pre-generated list of other ip addresses.

If you want to make a pure peer-to-peer voting system, and want each node to store the votes in a sqlite file you will need to do two things.

1)Make a way to verify votes. This is to ensure a user doesnt send false votes into the network, or edit it into the file.

2)Make an update system for clients who have not been online. (Allow these clients to ask any other clients for any vote info they dont already have)

Share this post


Link to post
Share on other sites
TLOTS

Hi!

i have a problem :/

if i try to run P2PTest.au3, it throws me "Problem connection attempt: " and a number, every time i connect (press d)

if i turn on the debug on the UDF, it says: "P2P_: Connection failed: " and the same number.

The twitchyliquid64's udf works fine in my computer.

What can i do to fix the problem?

Share this post


Link to post
Share on other sites
nullschritt

Hi!

i have a problem :/

if i try to run P2PTest.au3, it throws me "Problem connection attempt: " and a number, every time i connect (press d)

if i turn on the debug on the UDF, it says: "P2P_: Connection failed: " and the same number.

The twitchyliquid64's udf works fine in my computer.

What can i do to fix the problem?

It's probably because the bootstrapper has already connected you in this case.

Try pressing e to send some data. (with console out on)

Edited by nullschritt

Share this post


Link to post
Share on other sites
MikahS

Just for the general public, I would put the bug you are having into the first post of this UDF -- >link

"I am having this same problem with the Event Driven P2P script based off this script, it's not sendign the data as packets, it's simply cutting off the string when it's send over a network connection, locally there is no limit for TCPSend, but it seems that over a network connection, only so many bytes can be transferred at once."

As I'm seeing people looking for the same thing, this could save someone a lot of time testing your code.. :)


Snips & Scripts


My Snips: graphCPUTemp ~ getENVvars
My Scripts: Short-Order Encrypter - message and file encryption V1.6.1 ~ AuPad - Notepad written entirely in AutoIt V1.9.4

Feel free to use any of my code for your own use.                                                                                                                                                           Forum FAQ

 

Share this post


Link to post
Share on other sites
zelles

Can you use _P2P_Connect($ip) to connect to some peers also? If so would you call it for each one or can you only call the _P2P_Connect($ip) function 1 time?

Thanks in advance for any informatin on this.

Share this post


Link to post
Share on other sites
MikahS

Can you use _P2P_Connect($ip) to connect to some peers also? If so would you call it for each one or can you only call the _P2P_Connect($ip) function 1 time?

Thanks in advance for any informatin on this.

 

Every time you are trying to make a connection you call the connect function. But, please see the below quote as this could create errors; This whole library is based off a library that the author said not to use, just FYI :)>link

 

"I am having this same problem with the Event Driven P2P script based off this script, it's not sendign the data as packets, it's simply cutting off the string when it's send over a network connection, locally there is no limit for TCPSend, but it seems that over a network connection, only so many bytes can be transferred at once."


Snips & Scripts


My Snips: graphCPUTemp ~ getENVvars
My Scripts: Short-Order Encrypter - message and file encryption V1.6.1 ~ AuPad - Notepad written entirely in AutoIt V1.9.4

Feel free to use any of my code for your own use.                                                                                                                                                           Forum FAQ

 

Share this post


Link to post
Share on other sites
zelles

Thank you MikahS, for the information. Looks like I'll have to stick with what I had before looking at this...

Share this post


Link to post
Share on other sites
MikahS

My pleasure ;)

What were you doing before?


Snips & Scripts


My Snips: graphCPUTemp ~ getENVvars
My Scripts: Short-Order Encrypter - message and file encryption V1.6.1 ~ AuPad - Notepad written entirely in AutoIt V1.9.4

Feel free to use any of my code for your own use.                                                                                                                                                           Forum FAQ

 

Share this post


Link to post
Share on other sites
zelles

Using two central servers. I'd like to learn about setting up p2p connections though.

Share this post


Link to post
Share on other sites
MikahS

Have you had a look at Kealper's TCP server example by chance? >link

very nice way of doing a TCP server.


Snips & Scripts


My Snips: graphCPUTemp ~ getENVvars
My Scripts: Short-Order Encrypter - message and file encryption V1.6.1 ~ AuPad - Notepad written entirely in AutoIt V1.9.4

Feel free to use any of my code for your own use.                                                                                                                                                           Forum FAQ

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Similar Content

    • ripdad
      By ripdad
      I have had several people ask for this, so I decided to work the algorithm for it and this is the result.
      What is it?
      A Gateway Proxy Sends and Receives Data Unmodified.
      https://en.wikipedia.org/wiki/Proxy_server
      What is it used for?
      You can use it as a gateway, relay or router between two known static IP addresses.
      More information is in the header of the script.
      Download: LocalGatewayProxy.au3
      You will need WSA_NBTCP.au3 from here:
      https://www.autoitscript.com/forum/topic/191954-wsa_nbtcp-v100-udf/
      If you have any questions or problems, let me know.
       
    • ripdad
      By ripdad

      This script is based on algorithm code from EnrMa.
      Updated: January 22, 2018
      Made improvements. Changes are in the script header.
      Known Issues: AutoIt x64 does not work properly with this script.
      Download: LocalProxyServer_v1.00.zip
       
    • ripdad
      By ripdad
      WSA_NBTCP.au3  (Windows Sockets API - Non-Blocking Transmission Control Protocol)
      Version: 1.00
      Type: UDF
      This is an accumulation of WSA code from many sources and modified to suit myself.
      These functions have been thoroughly tested using a Local Proxy Server, which
      is about the most strenuous test you can use.
      Includes my rendition of how a TCPRecv Timeout should work. Also includes a
      timewait/timeout using Select for TCP Send, which works great for that function.
      You will need a loop to use _WSA_TCPRecv(). An example will be forthcoming in a second post.
      Functions:
      #CURRENT_USER_FUNCTIONS
      _WSA_Cleanup
      _WSA_FormatMessage
      _WSA_GetLastError
      _WSA_TCPAccept
      _WSA_TCPCloseSocket
      _WSA_TCPConnect
      _WSA_TCPListen
      _WSA_TCPRecv
      _WSA_TCPSend

      #INTERNAL_FUNCTIONS
      __TimeoutManager
      __TimeoutReset

      #EXTRA_FUNCTIONS
      _WSA_GetAddrInfo
      _WSA_GetHostByAddr
      _WSAAsyncGetHostByName
      _WSAAsyncGetHostByName_Callback
      _WSA_GetNameInfo
       
      Requirements:
      - AutoIt Versions: 3.3.8.1 thru 3.3.15.0 (32Bit only).
      - TCPStartup() at beginning of script on startup.
      - TCPShutDown() and _WSA_Cleanup() on exit.
      Download UDF: WSA_NBTCP.au3
       
    • tarretarretarre
      By tarretarretarre
      AutoIt-SocketIo
      Yep yep, this is pretty much an attempt to port the existing project's concept https://socket.io/ to AutoIt's Codebase. So i will not go in to so much detail.
      This is how the communication is done http://i.imgur.com/0mMfsBD.png Each client is isolated to the server http://i.imgur.com/rVO2LFb.png Features
      Easy API VarType Translation (Example: If the server sends an int, the client will receive an int and vice versa) Fully featured examples Data encryption (Using Autoit's UDF Crypt.au3) Limitations / Drawbacks
      It is not possible to Broadcast/Emit objects Only 1D-arrays are allowed to be Broadcasted/Emitted (2D arrays will probably never be supported) Changelog
      Version 1.5.0 (This update DOES NOT break scripts)
      Added AutoIt like docs under Docs\. The docs are generated so its a 1 to 1 reflection of the UDF headers. Print of documentation Added a production ready server example. Added a new method: _Io_DevDebug which will display useful information when debugging. Added a new method: _Io_SetMaxRecvPackageSize which defaults to whatever _Io_setRecvPackageSize is set to. Added a new method: _Io_setOnPrefix which defaults to _On_ Added a new default client & server event called flood. Flood occurs when exceeding the $__g_io_nMaxPacketSize. $__g_io_nMaxPacketSize is set by _Io_SetMaxRecvPackageSize Fixed the 16 parameter limit when sending data with _Io_Emit, _Io_Broadcast, _Io_BroadcastToAll and _Io_BroadcastToRoom. This works on the same premise that AutoIt's Call Does Fixed a TRUNCATION problem when receiving packages which could cause crashes! Fixed a programming error which caused $__g_ionPacketSize to reset to default 4096 if _Io_Connect or _Io_listenwere called after _Io_setRecvPackageSize Fixed _Io_setEventPreScript and _Io_setEventPostScript They didnt work. Lol. Changed how events are fired so the client cannot crash the server by sending the wrong number of parameters (This also allows for optional parameters on callbacks) Changed _Io_On. The second parameter $fCallback can now be set to null. Doing this, the function assumes that the callback is: _On_<eventName>.  
      Changelog History
      Api methods
      Please see the docs provided
      Default events
      Server events
      connection Client events
      banned Server and Client events
      disconnect flood View source on github
       
      Autoit-Socket-IO-1.0.0.zip (OLD!)
      Autoit-Socket-IO-1.1.0.zip (OLD)
      Autoit-Socket-IO-1.3.0.zip (OLD)
      Autoit-Socket-IO-1.4.0.zip (OLD)
      Autoit-Socket-IO-1.5.0.zip (NEWEST 2017-12-20)
×