-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnode.cs
327 lines (296 loc) · 13.4 KB
/
node.cs
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
namespace GNS3sharp {
/// <summary>
/// This class represents a node from a GNS3 project. Its main methods are:
/// <list type="bullet">
/// <item>
/// <term>Send</term>
/// <description>Allows you to send some message to a node</description>
/// </item>
/// <item>
/// <term>Receive</term>
/// <description>Allows you to receive messages from a node</description>
/// </item>
/// </list>
/// <remarks>
/// This class can interact directly by telnet to a node
/// </remarks>
/// </summary>
public class Node{
protected string consoleHost;
/// <summary>
/// IP of the machine where the node is hosted
/// </summary>
/// <value>String IP</value>
public string ConsoleHost { get => consoleHost; }
protected ushort port;
/// <summary>
/// Port of the machine where the node is hosted
/// </summary>
/// <value>Port number</value>
public ushort Port { get => port; }
protected string name;
/// <summary>
/// Name of the node stablished in the project
/// </summary>
/// <value>String name</value>
public string Name { get => name; }
protected string id;
/// <summary>
/// ID the node has implicitly
/// </summary>
/// <value>String ID</value>
public string ID { get => id; }
protected Dictionary<string,dynamic>[] ports;
/// <summary>
/// Array of dictionaries that contains information about every network interface
/// </summary>
/// <value>
/// Keys: adapterNumber, portNumber and link. If the value found in link
/// is null means that interface is not attached to anything yet
/// </value>
public Dictionary<string,dynamic>[] Ports{ get => ports; }
protected List<Link> linksAttached = new List<Link>();
/// <summary>
/// List of the links that the node is attach to
/// </summary>
/// <value>List of <c>Link</c></value>
public List<Link> LinksAttached { get => linksAttached; }
protected TcpClient tcpConnection;
/// <summary>
/// TCP client to stablish connections
/// </summary>
internal TcpClient TCPConnection { get => tcpConnection; }
protected NetworkStream netStream;
/// <summary>
/// Network stream through which messages are sent
/// </summary>
internal NetworkStream NetStream { get => netStream; }
///////////////////////////// Constructors ////////////////////////////////////////
/// <summary>
/// Constructor by default. Every property is empty
/// </summary>
internal Node(){
this.consoleHost = null; this.port = 0; this.name = null; this.id = null;
this.tcpConnection = null; this.netStream = null;
}
/// <summary>
/// Constructor of <c>Node</c>. It must be called from a <c>GNS3sharp</c> object
/// </summary>
/// <param name="_consoleHost">IP of the machine where the node is hosted</param>
/// <param name="_port">Port of the machine where the node is hosted</param>
/// <param name="_name">Name of the node stablished in the project</param>
/// <param name="_id">ID the node has implicitly</param>
/// <param name="_ports">Array of dictionaries that contains information about every network interface</param>
internal Node(string _consoleHost, ushort _port, string _name, string _id,
Dictionary<string,dynamic>[] _ports){
this.consoleHost = _consoleHost; this.port = _port; this.name = _name; this.id = _id;
this.ports = _ports;
(this.tcpConnection, this.netStream) = this.Connect();
}
/// <summary>
/// Constructor that replicates a node from another one
/// </summary>
/// <param name="clone">Node you want to make the copy from</param>
public Node(Node clone){
this.consoleHost = clone.ConsoleHost; this.port = clone.Port;
this.name = clone.Name; this.id = clone.ID; this.ports = clone.Ports;
this.tcpConnection = clone.TCPConnection; this.netStream = clone.NetStream;
}
/// <summary>
/// Close the connection with the node before leaving
/// </summary>
~Node(){
try{
// Close the network stream
if(this.netStream != null)
this.netStream.Close();
// Close the TCP connection
if(this.tcpConnection != null)
this.tcpConnection.Close();
} catch{}
}
///////////////////////////////// Methods ////////////////////////////////////////////
/// <summary>
/// Stablish a TCP connection with the node and makes a network stream out of it
/// </summary>
/// <param name="timeout">Timeout (in seconds) before quitting the connection</param>
/// <returns></returns>
protected (TcpClient Connection, NetworkStream Stream) Connect(int timeout = 10000){
// Network endpoint as an IP address and a port number
IPEndPoint address = new IPEndPoint(IPAddress.Parse(this.consoleHost),this.port);
// Set the socket for the connection
TcpClient newConnection = new TcpClient();
// Stream used to send and receive data
NetworkStream newStream = null;
try{
newConnection.Connect(address);
newStream = newConnection.GetStream();
newStream.ReadTimeout = timeout; newStream.WriteTimeout = timeout;
} catch(Exception err){
Console.Error.WriteLine("Impossible to connect to the node {0}: {1}", this.name, err.Message);
newConnection = null;
}
return (newConnection, newStream);
}
/// <summary>
/// Send a message to the node through a network stream
/// </summary>
/// <param name="message">String with the message that is intended to be sent</param>
/// <example>
/// <code>
/// PC.Send("ifconfig");
/// </code>
/// </example>
public void Send(string message){
if (this.tcpConnection == null)
(this.tcpConnection, this.netStream) = this.Connect();
if (this.tcpConnection == null)
Console.Error.WriteLine("The connection couldn't be stablished and so the message can not be sent");
else if (!this.netStream.CanWrite)
Console.Error.WriteLine("Impossible to send any messages right now");
else{
try{
// We need to convert the string into a bytes array first
byte[] out_txt = Encoding.Default.GetBytes($"{message}\n");
this.netStream.Write(buffer: out_txt, offset: 0, size: out_txt.Length);
this.netStream.Flush();
} catch(ObjectDisposedException err1){
Console.Error.WriteLine("Impossible to send anything, connection closed: {0}", err1.Message);
} catch(NullReferenceException){
Console.Error.WriteLine("Connection value is null. Probably it was not possible to initialize it");
} catch(IOException err2){
Console.Error.WriteLine("Time to write expired: {0}", err2.Message);
} catch(Exception err3){
Console.Error.WriteLine(
"Some error occured while sending '{0}': {1}",
message, err3.Message
);
}
}
}
/// <summary>
/// Receive messages from the buffer of the node network stream
/// </summary>
/// <param name="timeBetweenReads">
/// Seconds the method will wait for reading between messages in the server. By default is 2s
/// </param>
/// <returns>Messages as an array of strings</returns>
/// <example>
/// <code>
/// foreach(string line in PC.Receive())
/// Console.WriteLine("${line}");
/// </code>
/// </example>
public string[] Receive(int timeBetweenReads = 2){
// Reception variable as a string split by \n
string[] in_txt_split = null;
timeBetweenReads *= 1000;
if (this.tcpConnection != null)
(this.tcpConnection, this.netStream) = this.Connect();
if (this.netStream.CanRead){
// Reception variable as a bytes array
byte[] in_bytes = new byte[tcpConnection.ReceiveBufferSize];
// Reception variable as a string
string in_txt = "";
// Number of bytes read for every iteration
int numberOfBytesRead;
do{
do{
// We repeat this until there's no more to read
try{
numberOfBytesRead = this.netStream.Read(buffer: in_bytes, offset: 0, size: in_bytes.Length);
in_txt = $"{in_txt}{Encoding.Default.GetString(in_bytes, 0, numberOfBytesRead)}";
} catch(NullReferenceException){
Console.Error.WriteLine("Connection is null. Probably it was not possible to initialize it");
} catch(IOException err1){
Console.Error.WriteLine("Time to write expired: {0}", err1.Message);
} catch(Exception err2){
Console.Error.WriteLine("Some error occured while receiving text: {0}", err2.Message);
}
} while (this.netStream.DataAvailable);
// We need to wait for the server to process our messages
Thread.Sleep(timeBetweenReads);
// We double check the availability of data
} while (this.netStream.DataAvailable);
// Remove all the unnecesary characters contained in the buffer and split the text we have received in \n
in_txt_split = Regex.Replace(in_txt, @"(\0){2,}", "").Split('\n');
}
return in_txt_split;
}
/// <summary>
/// Send Ping to a certain IP
/// </summary>
/// <param name="IP">IP where ICMP packets will be sent</param>
/// <param name="count">Number of retries. By default 5</param>
/// <returns>The result messages of the ping</returns>
/// <example>
/// <code>
/// foreach(string line in PC.Ping("192.168.30.5"))
/// Console.WriteLine($"{line}");
/// </code>
/// </example>
public virtual string[] Ping(string IP, ushort count=5){
return Ping(IP, $"-c {count.ToString()}");
}
/// <summary>
/// Template child classes can used a template for creating 'Ping''s with different parameters
/// </summary>
/// <param name="IP">IP where to send the ICMP packets to</param>
/// <param name="additionalParameters">Additional parameters for the ping</param>
/// <returns>The result messages of the ping as an array of strings</returns>
/// <example>
/// <code>
/// foreach(string line in PC.Ping("192.168.30.5"))
/// Console.WriteLine($"{line}");
/// </code>
/// </example>
protected string[] Ping(
string IP, string additionalParameters){
// Reception variable as a string
string[] in_txt = null;
if(Aux.IsIP(IP)) {
Send($"ping {IP} {additionalParameters}");
in_txt = Receive();
} else{
Console.Error.WriteLine($"{IP} is not a valid IP");
}
// Return the response
return in_txt;
}
/// <summary>
/// Check whether a ping went right or wrong
/// </summary>
/// <param name="pingMessage">Result message of a ping</param>
/// <returns>True if the ping went right, False otherwise</returns>
/// <example>
/// <code>
/// if (PC.PingResult(PC.Ping("192.168.30.5")))
/// Console.WriteLine("The ping went ok");
/// </code>
/// </example>
public virtual bool PingResult(string[] pingMessage){
// We assume the result will be negative
bool result = false;
foreach(string line in pingMessage.Reverse<string>()){
// Search for the line with the results
if (Regex.IsMatch(line, @"\d+\spackets\stransmitted,\s\d+\spackets\sreceived,\s(\d+|\d+[.]\d+)%?%\spacket\sloss\s")){
string[] resultStr = line.Split(',');
// If "%d packets received" is different to zero means the ping went right
if (Int32.Parse(resultStr[1].TrimStart().Split(' ')[0]) != 0)
result = true;
break;
}
}
return result;
}
}
}