Skip to content

Commit 0c22f64

Browse files
committed
NetworkStream now rejects non-stream sockets (#418)
1 parent 121df0f commit 0c22f64

3 files changed

Lines changed: 141 additions & 46 deletions

File tree

‎Tests/SocketTests/NetworkStreamTests.cs‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using nanoFramework.TestFramework;
55
using System;
6+
using System.IO;
67
using System.Net;
78
using System.Net.Sockets;
89

@@ -68,6 +69,114 @@ public void NetworkStream_Write_ZeroLength_DoesNotThrow()
6869
});
6970
}
7071

72+
[TestMethod]
73+
public void Ctor_NullSocket_ThrowsArgumentNullException()
74+
{
75+
Assert.ThrowsException(typeof(ArgumentNullException), () =>
76+
{
77+
_ = new NetworkStream(null);
78+
});
79+
80+
Assert.ThrowsException(typeof(ArgumentNullException), () =>
81+
{
82+
_ = new NetworkStream(null, true);
83+
84+
});
85+
}
86+
87+
88+
public void Ctor_UnconnectedStreamSocket_ThrowsIOException()
89+
{
90+
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
91+
92+
try
93+
{
94+
Assert.ThrowsException(typeof(IOException), () =>
95+
{
96+
_ = new NetworkStream(socket);
97+
});
98+
99+
Assert.ThrowsException(typeof(IOException), () =>
100+
{
101+
_ = new NetworkStream(socket, false);
102+
});
103+
}
104+
finally
105+
{
106+
socket.Close();
107+
}
108+
}
109+
110+
[TestMethod]
111+
public void Ctor_ConnectedDgramSocket_ThrowsIOException()
112+
{
113+
SocketPair testSockets = new SocketPair(ProtocolType.Udp, SocketType.Dgram);
114+
115+
try
116+
{
117+
testSockets.Startup(0, 0);
118+
119+
// a connected datagram socket has a remote endpoint, so this exercises the socket type check
120+
testSockets.socketClient.Connect(testSockets.epServer);
121+
122+
Assert.ThrowsException(typeof(IOException), () =>
123+
{
124+
_ = new NetworkStream(testSockets.socketClient);
125+
});
126+
127+
Assert.ThrowsException(typeof(IOException), () =>
128+
{
129+
_ = new NetworkStream(testSockets.socketClient, false);
130+
});
131+
}
132+
finally
133+
{
134+
testSockets.TearDown();
135+
}
136+
}
137+
138+
[TestMethod]
139+
public void Ctor_ConnectedStreamSocket_Succeeds()
140+
{
141+
SocketPair testSockets = new SocketPair(ProtocolType.Tcp, SocketType.Stream);
142+
Socket acceptedSocket = null;
143+
144+
try
145+
{
146+
testSockets.Startup(0, 0);
147+
testSockets.socketServer.Listen(1);
148+
testSockets.socketClient.Connect(testSockets.epServer);
149+
acceptedSocket = testSockets.socketServer.Accept();
150+
151+
NetworkStream clientStream = new NetworkStream(testSockets.socketClient);
152+
NetworkStream serverStream = new NetworkStream(acceptedSocket, true);
153+
154+
clientStream.Write(testSockets.bufSend, 0, testSockets.bufSend.Length);
155+
156+
int bytesRead = serverStream.Read(testSockets.bufReceive, 0, testSockets.bufReceive.Length);
157+
158+
testSockets.AssertDataReceived(bytesRead);
159+
160+
clientStream.Dispose();
161+
162+
// server stream owns the accepted socket, so disposing it closes the socket
163+
serverStream.Dispose();
164+
acceptedSocket = null;
165+
}
166+
finally
167+
{
168+
acceptedSocket?.Close();
169+
testSockets.TearDown();
170+
171+
172+
173+
174+
175+
176+
177+
}
178+
}
179+
71180
private delegate void LoopbackTestAction(NetworkStream stream, Socket peer);
72181

73182
private static void RunLoopbackTest(LoopbackTestAction test)

‎nanoFramework.System.Net/Security/SslStream.cs‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,15 @@ public class SslStream : NetworkStream
4747
/// Initializes a new instance of the SslStream class using the specified Socket.
4848
/// </summary>
4949
/// <param name="socket">A valid socket that currently has a TCP connection.</param>
50+
/// <exception cref="ArgumentNullException"><paramref name="socket"/> is <see langword="null"/>.</exception>
51+
/// <exception cref="IOException"><paramref name="socket"/> is not connected. -or- The <see cref="Socket.SocketType"/> property of <paramref name="socket"/> is not <see cref="SocketType.Stream"/>.</exception>
5052
/// <remarks>
5153
/// The SslStream maintains the lifetime of the socket. When the SslStream object is disposed,
5254
/// the underlying TCP socket will be closed.
5355
/// </remarks>
5456
public SslStream(Socket socket)
5557
: base(socket, false)
5658
{
57-
if (SocketType.Stream != (SocketType)_socketType)
58-
{
59-
throw new NotSupportedException();
60-
}
61-
6259
_sslContext = -1;
6360
_isServer = false;
6461

‎nanoFramework.System.Net/Sockets/NetworkStream.cs‎

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class NetworkStream : Stream
2323
protected int _socketType;
2424

2525
/// <summary>
26-
/// Internal endpoint ref used for dgram sockets
26+
/// Internal endpoint ref of the remote peer
2727
/// </summary>
2828
protected EndPoint _remoteEndPoint;
2929

@@ -40,7 +40,7 @@ public class NetworkStream : Stream
4040
/// </summary>
4141
/// <param name="socket">The <see cref="Socket"/> that the <see cref="NetworkStream"/> will use to send and receive data.</param>
4242
/// <exception cref="ArgumentNullException"><paramref name="socket"/> is <see langword="null"/>.</exception>
43-
/// <exception cref="IOException">The <paramref name="socket"/> is not connected or the remote endpoint is not available.</exception>
43+
/// <exception cref="IOException"><paramref name="socket"/> is not connected. -or- The <see cref="Socket.SocketType"/> property of <paramref name="socket"/> is not <see cref="SocketType.Stream"/>.</exception>
4444
public NetworkStream(Socket socket)
4545
: this(socket, false)
4646
{
@@ -55,7 +55,7 @@ public NetworkStream(Socket socket)
5555
/// <param name="ownsSocket"><see langword="true"/> to indicate that the <see cref="NetworkStream"/> will take ownership of the <see cref="Socket"/>;
5656
/// otherwise, <see langword="false"/>.</param>
5757
/// <exception cref="ArgumentNullException"><paramref name="socket"/> is <see langword="null"/>.</exception>
58-
/// <exception cref="IOException">The <paramref name="socket"/> is not connected or the remote endpoint is not available.</exception>
58+
/// <exception cref="IOException"><paramref name="socket"/> is not connected. -or- The <see cref="Socket.SocketType"/> property of <paramref name="socket"/> is not <see cref="SocketType.Stream"/>.</exception>
5959
public NetworkStream(Socket socket, bool ownsSocket)
6060
{
6161
ArgumentNullException.ThrowIfNull(socket);
@@ -72,6 +72,11 @@ public NetworkStream(Socket socket, bool ownsSocket)
7272
throw new IOException(errCode.ToString(), e);
7373
}
7474

75+
if (socket.SocketType != SocketType.Stream)
76+
{
77+
throw new IOException();
78+
}
79+
7580
// Set the internal socket
7681
_socket = socket;
7782

@@ -84,7 +89,11 @@ public NetworkStream(Socket socket, bool ownsSocket)
8489
/// <summary>
8590
/// Gets a value that indicates whether the <see cref="NetworkStream"/> supports reading.
8691
/// </summary>
87-
/// <value><see langword="true"/> if data can be read from the stream; otherwise, <see langword="false"/>.</value>
92+
/// <value>true if data can be read from the stream; otherwise, false. The default value is true.</value>
93+
/// <remarks>
94+
/// If CanRead is true, <see cref="NetworkStream"/> allows calls to the <see cref="Read(byte[], int, int)"/> method. Provide the appropriate FileAccess enumerated value in the constructor to set
95+
/// the readability and write-ability of the <see cref="NetworkStream"/>. The CanRead property is set when the <see cref="NetworkStream"/> is initialized.
96+
/// </remarks>
8897
public override bool CanRead { get { return true; } }
8998

9099
/// <summary>
@@ -215,7 +224,7 @@ public virtual bool DataAvailable
215224
/// <summary>
216225
/// Closes the <see cref="NetworkStream"/> after waiting the specified time to allow data to be sent.
217226
/// </summary>
218-
/// <param name="timeout">A32-bit signed integer that specifies the number of milliseconds to wait to send any remaining data before closing.</param>
227+
/// <param name="timeout">A 32-bit signed integer that specifies the number of milliseconds to wait to send any remaining data before closing.</param>
219228
/// <exception cref="ArgumentOutOfRangeException"><paramref name="timeout"/> is less than -1.</exception>
220229
/// <remarks>
221230
/// <para>The <see cref="Close"/> method frees both unmanaged and managed resources associated with the <see cref="NetworkStream"/>. If the <see cref="NetworkStream"/> owns the underlying <see cref="Socket"/>, it is closed as well.</para>
@@ -272,16 +281,18 @@ public override void Flush()
272281
/// Reads data from the <see cref="NetworkStream"/>.
273282
/// </summary>
274283
/// <param name="buffer">An array of type <see cref="byte"/> that is the location in memory to store data read from the <see cref="NetworkStream"/>.</param>
275-
/// <param name="offset">The location in <paramref name="buffer"/> to begin storing the data.</param>
284+
/// <param name="offset">The location in <paramref name="buffer"/> to begin storing the data to.</param>
276285
/// <param name="count">The number of bytes to read from the <see cref="NetworkStream"/>.</param>
277-
/// <returns>The number of bytes read from the <see cref="NetworkStream"/>.</returns>
286+
/// <returns>The total number of bytes read into the buffer between zero (0) and the requested count. The method returns zero (0) only if zero bytes were requested or if no more bytes are available because the peer socket performed a graceful shutdown.</returns>
287+
/// <exception cref="IOException">The underlying <see cref="Socket"/> is closed. -or- There was a failure while reading from the network.</exception>
278288
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
279-
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is less than0 or greater than the length of <paramref name="buffer"/>; or <paramref name="count"/> is less than0 or greater than the length of <paramref name="buffer"/> minus <paramref name="offset"/>.</exception>
280289
/// <exception cref="ObjectDisposedException">The <see cref="NetworkStream"/> is closed.</exception>
281-
/// <exception cref="IOException">The underlying <see cref="Socket"/> is closed, or an error occurred when accessing the socket.</exception>
290+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is less than 0 or greater than the length of <paramref name="buffer"/>. -or- <paramref name="count"/> is less than 0 or greater than the length of <paramref name="buffer"/> minus the value of the <paramref name="offset"/> parameter.</exception>
282291
/// <remarks>
283-
/// <para>This method reads data into the <paramref name="buffer"/> parameter and returns the number of bytes successfully read. If no data is available for reading, the <see cref="Read(byte[], int, int)"/> method returns0. The read operation reads as much data as is available, up to the number of bytes specified by the <paramref name="count"/> parameter.</para>
284-
/// <para>If the remote host shuts down the connection, and all available data has been received, the <see cref="Read(byte[], int, int)"/> method completes immediately and returns zero bytes.</para>
292+
/// <para>This method reads data into the <paramref name="buffer"/> parameter and returns the number of bytes successfully read. The <see cref="Read(byte[], int, int)"/> operation reads as much data as is available, up to the number of bytes specified by the <paramref name="count"/> parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.</para>
293+
/// <note type="important">
294+
/// Check to see if the <see cref="NetworkStream"/> is readable by calling the <see cref="CanRead"/> property. If you attempt to read from a <see cref="NetworkStream"/> that is not readable, you will get an <see cref="IOException"/>.
295+
/// </note>
285296
/// </remarks>
286297
public override int Read(byte[] buffer, int offset, int count)
287298
{
@@ -367,18 +378,7 @@ private int ReadCore(byte[] buffer, int offset, int count)
367378
count = available;
368379
}
369380

370-
if (_socketType == (int)SocketType.Stream)
371-
{
372-
return _socket.Receive(buffer, offset, count, SocketFlags.None);
373-
}
374-
else if (_socketType == (int)SocketType.Dgram)
375-
{
376-
return _socket.ReceiveFrom(buffer, offset, count, SocketFlags.None, ref _remoteEndPoint);
377-
}
378-
else
379-
{
380-
throw new NotSupportedException();
381-
}
381+
return _socket.Receive(buffer, offset, count, SocketFlags.None);
382382
}
383383

384384
/// <summary>
@@ -403,19 +403,21 @@ public override void SetLength(long value)
403403
throw new NotSupportedException();
404404
}
405405

406-
/// <summary>
406+
/// <summary>
407407
/// Writes data to the <see cref="NetworkStream"/>.
408408
/// </summary>
409409
/// <param name="buffer">An array of type <see cref="byte"/> that contains the data to write to the <see cref="NetworkStream"/>.</param>
410410
/// <param name="offset">The location in <paramref name="buffer"/> from which to start writing data.</param>
411411
/// <param name="count">The number of bytes to write to the <see cref="NetworkStream"/>.</param>
412-
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
413-
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is less than0 or greater than the length of <paramref name="buffer"/>; or <paramref name="count"/> is less than0 or greater than the length of <paramref name="buffer"/> minus <paramref name="offset"/>.</exception>
412+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is less than 0 or greater than the length of <paramref name="buffer"/>. -or- <paramref name="count"/> is less than 0 or greater than the length of <paramref name="buffer"/> minus the value of the <paramref name="offset"/> parameter.</exception>
414413
/// <exception cref="ObjectDisposedException">The <see cref="NetworkStream"/> is closed.</exception>
415-
/// <exception cref="IOException">There was a failure while writing to the network, or the underlying <see cref="Socket"/> is closed.</exception>
414+
/// <exception cref="IOException">There was a failure while writing to the network. -or- An error occurred when accessing the socket. See the Remarks section for more information.</exception>
415+
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
416416
/// <remarks>
417417
/// The <see cref="Write(byte[], int, int)"/> method starts at the specified <paramref name="offset"/> and sends <paramref name="count"/> bytes from the contents of <paramref name="buffer"/> to the network.
418418
/// The <see cref="Write(byte[], int, int)"/> method blocks until the requested number of bytes is sent or a <see cref="SocketException"/> is thrown.
419+
/// If you receive a <see cref="SocketException"/>, use the <see cref="SocketException.ErrorCode"/> property to obtain
420+
/// the specific error code, and refer to the Windows Sockets version 2 API error code documentation in MSDN for a detailed description of the error.
419421
/// </remarks>
420422
public override void Write(byte[] buffer, int offset, int count)
421423
{
@@ -472,20 +474,7 @@ private void WriteCore(byte[] buffer, int offset, int count)
472474
throw new IOException();
473475
}
474476

475-
int bytesSent = 0;
476-
477-
if (_socketType == (int)SocketType.Stream)
478-
{
479-
bytesSent = _socket.Send(buffer, offset, count, SocketFlags.None);
480-
}
481-
else if (_socketType == (int)SocketType.Dgram)
482-
{
483-
bytesSent = _socket.SendTo(buffer, offset, count, SocketFlags.None, _socket.RemoteEndPoint);
484-
}
485-
else
486-
{
487-
throw new NotSupportedException();
488-
}
477+
int bytesSent = _socket.Send(buffer, offset, count, SocketFlags.None);
489478

490479
if (bytesSent != count)
491480
{

0 commit comments

Comments
 (0)