-
Notifications
You must be signed in to change notification settings - Fork 0
/
ping.php
233 lines (171 loc) · 7.16 KB
/
ping.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
<?php
//First we implement basic functions to read/write the first part of the protocol
/* gm($data): function for generating messages
$data: data to be sent
it appends to the start of the data string a byte value of the length of the string
i.e. if we want to write "abc", so string length of 3
it will return the string "\0x03" . "abc"
this function is valid up to length of 254, above is more complex
*/
function gm($data) {
$len = strlen($data);
return chr($len) . $data; //we return length byte and data
}
/* rm(): function for reading messages
it reads first the first byte, which is the length of the message
then it reads the stream for the received amount of bytes and returns it
this function is valid up to length of 254, above is more complex
*/
function rm() {
global $fp;
$len = ord(fgetc($fp)); //first byte is length
if ($len == 0) {
return false;
}
$data = fread($fp, $len); //we read that much data
return $data; //we return data without the length byte
}
//Now we implement functions to read/write the multiplex part of the protocol
//We first define the constants that define header flags (types)
define("NewStream", 0);
define("MessageReceiver", 1);
define("MessageInitiator", 2);
define("CloseReceiver", 3);
define("CloseInitiator", 4);
define("ResetReceiver", 5);
define("ResetInitiator", 6);
/* gm_mplex($data, $id, $flag, $dbllen): function for generating messages in multiplex streams of mplex type
$data: data to be sent
$id: stream ID
$flag: flag/type of message defined with constants few lines above
$dbllen: if it should write two byte lengths, one for mplex protocol, the other for libp2p like gm(); defaults to false
it appends to the start of the string a byte header followed by a byte value of the length of the data string
i.e. if we want to write "abc", so string length of 3 with $id = 1, $flag = 1;
it will return the string "\0x09" . "\0x03" . "abc"
Header is composed from ID (first 5 bits) and type/flag (last 3 bits)
mplex protocol is further described here https://github.com/libp2p/specs/tree/master/mplex
this function is valid up to length of 254, above is more complex
*/
function gm_mplex($data, $id = 0, $flag = 0, $dbllen = false) {
$header = ($id << 3) | $flag; //we shift ID by 3 bits to the left and add type bits
$len = strlen($data); //read length of data
//returns the header byte, byte length of the data, optionally an another byte length and data
return chr($header) . ($dbllen ? chr($len+1):'') . chr($len) . $data;
}
/* rm_mplex(): function for reading messages in multiplex streams of mplex type
it reads the first byte header followed by a byte value of the length of the data string
it returns the stream ID, type of message (flag) and data in an associative array
Header is composed from ID (first 5 bits) and type/flag (last 3 bits)
mplex protocol is further described here https://github.com/libp2p/specs/tree/master/mplex
this function is valid up to length of 254, above is more complex
*/
function rm_mplex() {
global $fp;
$header = ord(fgetc($fp)); //we read first the header byte
$flag = $header & 0x07; //right 3 bits are flag
$id = $header >> 3; //left 5 bits are ID
$len = ord(fgetc($fp)); //length is next byte
$data = fread($fp, $len); //we read that much bytes
return array('id' => $id, 'flag' => $flag, 'data' => $data); //we return an array with ID, flag, data
}
/* rand_ping_str(): function gor generating random 32 bytes for a ping
it returns a random string made out 32 numbers so that it's nicely readable
*/
function rand_ping_str() {
$rand_str = 0;
for ($i = 0; $i < 31; $i++) {
$rand_str .= rand(0,9);
}
return $rand_str;
}
//we open a localhost connection to IPFS, started with --disable-transport-encryption
//this is to disable encryption to make things simpler
$fp = fsockopen('localhost', 4001);
//read intro which should be /multistream/1.0.0\n
echo "Rx: " . rm();
//we send back the same thing
$tx = "/multistream/1.0.0\n";
echo "Tx: " . $tx;
fwrite($fp, gm($tx));
//we send that we want to initiate a plaintext session
$tx = "/plaintext/1.0.0\n";
echo "Tx: " . $tx;
fwrite($fp, gm($tx));
//normally we receive back the same thing, but not if encryption is on
$rx = rm();
echo "Rx: " . $rx;
//if we got na\n, that means that IPFS requires secio encryption and we can't proceed with plaintext
if ($rx == "na\n") {
exit("IPFS wasn't started with: ipfs daemon --disable-transport-encryption\n");
}
//we send now again /multistream/1.0.0, now that we started a plaintext session
$tx = "/multistream/1.0.0\n";
echo "Tx: " . $tx;
fwrite($fp, gm($tx));
//we receive back the same thing
echo "Rx: " . rm();
//we initiate now the multiplex mplex
$tx = "/mplex/6.7.0\n";
echo "Tx: " . $tx;
fwrite($fp, gm($tx));
//we receive back the same thing
echo "Rx: " . rm();
echo "\nMultiplex data [id,flag]:\n"; //now Multiplex data will start
//IPFS opens 3 distinct streams and each starts the handshake, but we'll ignore that
//we will though read the messages we received
for ($i = 0; $i < 6; $i++) { //we repeat the following 6 times, because we get 6 messages
$r = rm_mplex();
echo "Rx[$r[id],$r[flag]]: $r[data]";
substr($r['data'], -1) != "\n" && print("\n"); //if data doesn't end with \n we print a \n for clarity
}
//we initiate a new stream ID=3
$id = 3;
$flag = NewStream;
$tx = 3;
echo "Tx[$id,$flag]: $tx\n";
fwrite($fp, gm_mplex($tx, $id, $flag));
//we read the response
$r = rm_mplex();
echo "Rx[$r[id],$r[flag]]: $r[data]";
substr($r['data'], -1) != "\n" && print("\n"); //if data doesn't end with \n we print a \n for clarity
//we send now back the multistream. we append double length byte, one for mplex, other for libp2p.
$id = 3;
$flag = MessageInitiator;
$tx = "/multistream/1.0.0\n";
echo "Tx[$id,$flag]: $tx";
fwrite($fp, gm_mplex($tx, $id, $flag, true));
//we start the ping handshake, again double length byte
$id = 3;
$flag = MessageInitiator;
$tx = "/ipfs/ping/1.0.0\n";
echo "Tx[$id,$flag]: $tx";
fwrite($fp, gm_mplex($tx, $id, $flag, true));
//we read the response
$r = rm_mplex();
echo "Rx[$r[id],$r[flag]]: $r[data]";
substr($r['data'], -1) != "\n" && print("\n"); //if data doesn't end with \n we print a \n for clarity
$avg = 0; //for averaging ping
$n = 5;
//we do ping 5 times
for ($i = 0; $i < $n; $i++) {
//we start the ping, by sending a random 32 byte string. this time we send just once the byte length!
$id = 3;
$flag = MessageInitiator;
$tx = rand_ping_str();
echo "Tx[$id,$flag]: $tx\n";
$t0 = microtime(1); //just before sending we mark time at start
fwrite($fp, gm_mplex($tx, $id, $flag));
//we read the response, which is just the same what we've sent
$r = rm_mplex();
echo "Rx[$r[id],$r[flag]]: $r[data]";
substr($r['data'], -1) != "\n" && print("\n"); //if data doesn't end with \n we print a \n for clarity
$t1 = microtime(1); //time of receiving
//we write how much the roundtrip took
echo "Ping took " . round(($t1-$t0)*1e3,2) . " ms\n";
//we check if sent data matches to the received data
echo "Ping data " . ($r['data'] == $tx ? 'matches':'differs') . "\n\n";
$avg += ($t1-$t0)*1e3;
}
$avg /= $n; //we divide the ping time sum with N, so that we get the mean
echo "Average ping took " . round($avg,2) . " ms\n";
?>