(The code listed below is VB .NET, taken from WinZO client)
1) HOW TO GET PARENT NODE INFORMATION
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The first thing we have to do is to fill a list with the peer cache address currently availables.
Dim list3 As New List(Of IPEndPoint)
list3.Add(New IPEndPoint(IPAddress.Parse("205.238.40.1"), 7950))
list3.Add(New IPEndPoint(IPAddress.Parse("67.18.233.36"), 7950))
list3.Add(New IPEndPoint(IPAddress.Parse("82.43.224.20"), 7950))
list3.Add(New IPEndPoint(IPAddress.Parse("209.67.209.50"), 7950))
list3.Add(New IPEndPoint(IPAddress.Parse("212.227.64.159"), 7950))
Now, before to connect to one of them, we randomize them in the way showed below.
Of course peercache is a List(Of IPEndPoint)
Dim enumerator1 As Enumerator(Of IPEndPoint) = SequenceUtils.Shuffle(Of IPEndPoint)(peercache).GetEnumerator
Now we start connecting...
Do While enumerator1.MoveNext
Dim point1 As IPEndPoint = enumerator1.get_Current
Dim connection1 As New Connection(LogUtils.Log(Of IPEndPoint)(point1, "CACHE"))
Try
If Not connection1.Connect Then
Continue Do
End If
....
When we are connected to a peer cache we need to read the 56 value (0x38) as the first byte.
If (connection1.Reader.ReadByte <> 56) Then
Continue Do
End If
....
If this is the case, we then read the next 16 bytes and so we expect to get the right crypt key.
(Refer to the the WinSock.dll library documentation for the GetCryptKeyType function)
If (WNPNEncryption.GetCryptKeyType(CType(connection1.Reader.ReadBytes(16), Byte())) <> CryptKeyType.NodeList) Then
Continue Do
End If
Just for reference
Public Enum CryptKeyType As Byte
ChatClient = 87
ChatServer = 88
NodeList = 84 <---- we expect this one (0x54)
PrimaryClient = 80
PrimaryServer = 81
SecondaryClient = 82
SecondaryServer = 83
End Enum
Now we are ready to get infos about the wpn nodes
So we read a 132 bytes long data packet and use the DecryptFrontCode tables to decrypt it.
(For the DecryptFrontCode function refer to the Winsock library documentation)
Dim list1 As List(Of NodeInfo) =
NodeInfo.ExtractNodeInfos(WNPNEncryption.DecryptFrontCode(CType(connection1.Reader.ReadBytes(132), Byte())))
Think about NodeInfo as a struct or just a class.
Through the ExtractNodeinfos method we get all the infos about the node
Dim info1 As New NodeInfo
info1.IP = New IPAddress(CType(r.ReadUInt, Long)) (4 bytes)
info1.UdpPort = r.ReadWord (2 bytes)
info1.TcpPort = r.ReadWord (2 bytes)
info1.FreePrimary = r.ReadByte (1 byte)
info1.FreeSecondary = r.ReadByte (1 byte)
info1.Reserved = r.ReadWord (2 byte)
info1.LastUpdated = DateTime.Now
2) HOW TO CONNECT TO A WPN PARENT NODE
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the NodeInfo data struct (or class) we have the Parent node IP address and the TCP port we need to connect to.
So we start connecting to it...
Dim connection1 As New Connection(New IPEndPoint(node.IP, node.TcpPort))
When connected we receive a byte (0x31 valued)
If (connection1.Reader.ReadByte <> 49) Then
Throw New CantConnectParentNodeException("First byte incorrect")
End If
Now we are entering a crypted session, so we send a 16 bytes long packet with our crypt key ()
connection1.Writer.Write(WNPNEncryption.CreateCryptKeyID(CryptKeyType.SecondaryClient))
Next we receive 16 bytes from the node and check it against the right value (0x53)
If (WNPNEncryption.GetCryptKeyType(buffer1) <> CryptKeyType.SecondaryServer) Then
Throw New CantConnectParentNodeException(...})
End If
So we get the two crypt key we need to encrypt/decrypt all the data packets we send/receive
from this secondary connection server.
class1.upkey = LogUtils.Log(Of UInt32)(WNPNEncryption.GetCryptKeyUp(buffer1), "UpKey")
class1.downkey = LogUtils.Log(Of UInt32)(WNPNEncryption.GetCryptKeyDown(buffer1), "DownKey")
Now that we obtained the two crypt key we have to send the login packet, as showed below:
this.Write(new LoginPacket(this.LoginName + "000", LineType.K64, ListenPort ));
return SecondaryConnection.State.SentLogin;
By a login packet we mean :
nickname (string) + linetype (byte) + reserved (byte) + port number (2 bytes)
Now we receive the login confirmation and we are able to make our name (look the code below):
ushort num1 = this.StreamReader.ReadWord();
ushort num2 = this.StreamReader.ReadWord();
MyBinaryReader reader1 = new MyBinaryReader(new MemoryStream((byte[]) this.StreamReader.ReadBytes(num2)));
ushort num3 = num1;
if (num3 == 0x460)
{
this.LoginUserID = LogUtils.Log<ushort>(reader1.ReadWord(), "UserID");
...
}
We are done ! we have established a secondary connection.
The thing we would do now is to send our shared files, but i will not deal with this topic here.