singrdk/base/Libraries/System.Net/Sockets/UdpSocket.cs

554 lines
18 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: UdpSocket.cs
//
// Note:
//
using System;
using Microsoft.SingSharp;
using Microsoft.SingSharp.Runtime;
using Microsoft.Singularity;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
using NetStack.Contracts;
using Microsoft.Singularity.V1.Services;
using Microsoft.Contracts;
namespace System.Net.Sockets
{
internal class UdpSocket : InternalSocket
{
private const int BAD_SOCKET_STATE_ERR = (int)SocketErrors.WSAEOPNOTSUPP;
private TRef<UdpConnectionContract.Imp:ReadyState> m_Conn;
private IPEndPoint m_LocalEndPoint;
private IPEndPoint m_RemoteEndPoint;
// ================ Private Methods ================
private void Initialize([Claims]UdpConnectionContract.Imp:ReadyState! conn)
{
m_Conn = new TRef<UdpConnectionContract.Imp:ReadyState>(conn);
}
private static void ValidateBufferOffsets(byte[] buffer,
int offset,
int size)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException("offset");
}
if (size < 0 || size > buffer.Length - offset)
{
throw new ArgumentOutOfRangeException("size");
}
}
// ================ Internal Methods ================
[NotDelayed]
internal UdpSocket()
{
// Connect to the Udp module
UdpContract.Imp! impConn;
UdpContract.Exp! expConn;
UdpContract.NewChannel(out impConn, out expConn);
DirectoryServiceContract.Imp epNS = DirectoryService.NewClientEndpoint();
try
{
epNS.SendBind(Bitter.FromString2(UdpContract.ModuleName), expConn);
switch receive
{
case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) :
// failure
if (rejectedEP != null) {
delete rejectedEP;
}
throw new SocketException(SocketErrors.WSASYSNOTREADY);
break;
case epNS.AckBind() :
impConn.RecvReady();
// success
break;
case epNS.ChannelClosed():
Tracing.Log(Tracing.Debug," NS ep closed");
break;
case unsatisfiable:
DebugService.Break();
break;
}
}
finally
{
delete epNS;
}
try
{
// Create a new Udp connection
UdpConnectionContract.Exp! udpExpConn;
UdpConnectionContract.Imp! udpImpConn;
UdpConnectionContract.NewChannel(out udpImpConn, out udpExpConn);
impConn.SendCreateUdpSession(udpExpConn);
impConn.RecvAck();
// Move the new connection to the ReadyState
udpImpConn.RecvReady();
Initialize(udpImpConn);
}
finally
{
delete impConn;
}
}
internal override InternalSocket Accept()
{
throw new SocketException(SocketErrors.WSAEOPNOTSUPP);
return null;
}
internal override void Bind(EndPoint localEndPoint)
{
if (localEndPoint == null)
{
throw new ArgumentNullException("localEndPoint");
}
IPEndPoint ep = localEndPoint as IPEndPoint;
if (ep == null)
{
// Illegal argument
throw new SocketException(SocketErrors.WSAEINVAL);
}
// Make sure we're in the ReadyState
UdpConnectionContract.Imp conn = m_Conn.Acquire();
try
{
if (!conn.InState(UdpConnectionContract.ReadyState.Value))
{
throw new SocketException(BAD_SOCKET_STATE_ERR);
}
conn.SendBindLocalEndPoint((uint) ep.Address.m_addr,
unchecked((ushort) ep.Port));
switch receive
{
case conn.OK() :
// Make a copy of the argument data
m_LocalEndPoint = new IPEndPoint(ep.Address, ep.Port);
break;
case conn.InvalidEndPoint(uint addr, ushort port) :
throw new SocketException(SocketErrors.WSAEADDRNOTAVAIL);
break;
}
}
finally
{
m_Conn.Release(conn);
}
}
internal override void Close()
{
UdpConnectionContract.Imp conn = m_Conn.Acquire();
try
{
if (!conn.InState(UdpConnectionContract.Closed.Value))
{
conn.SendClose();
}
}
finally
{
m_Conn.Release(conn);
}
}
internal override void Connect(EndPoint remoteEndPoint)
{
if (remoteEndPoint == null)
{
throw new ArgumentNullException("remoteEP");
}
IPEndPoint ep = remoteEndPoint as IPEndPoint;
if (ep == null)
{
// Illegal argument
throw new SocketException(SocketErrors.WSAEINVAL);
}
UdpConnectionContract.Imp conn = m_Conn.Acquire();
if (! conn.InState(UdpConnectionContract.ReadyState.Value) &&
! conn.InState(UdpConnectionContract.LocallyBound.Value))
{
// Need to be in the start state to issue a Connect
throw new SocketException(BAD_SOCKET_STATE_ERR);
}
try
{
uint localIP = 0;
ushort localPort = 0;
if (m_LocalEndPoint != null)
{
if (m_LocalEndPoint.Address != IPAddress.Any)
{ localIP = (uint)m_LocalEndPoint.Address.m_addr; }
if (m_LocalEndPoint.Port != 0)
{ localPort = unchecked((ushort)m_LocalEndPoint.Port); }
}
if ((localIP != 0) && (localPort != 0))
{
conn.SendConnect(localIP, localPort,
(uint)ep.Address.m_addr,
unchecked((ushort)ep.Port));
}
else
{
conn.SendConnectWithAnyLocalEndPoint(
(uint)ep.Address.m_addr,
unchecked((ushort)ep.Port));
}
switch receive
{
case conn.OK() :
// Success
m_RemoteEndPoint = ep;
break;
case conn.InvalidEndPoint(uint addr, ushort port) :
// NOTE could probably stand to retrieve and
// return a better error code here
throw new SocketException(SocketErrors.SocketError);
break;
}
}
finally
{
m_Conn.Release(conn);
}
}
internal override void Dispose()
{
UdpConnectionContract.Imp conn = m_Conn.Acquire();
delete conn;
// Leak our TRef!
}
internal override void Listen(int backlog)
{
throw new SocketException(SocketErrors.WSAEOPNOTSUPP);
}
private bool PollSelectRead(UdpConnectionContract.Imp! conn,
int millis)
{
bool result = false;
conn.SendPollSelectRead(millis);
conn.RecvPollSelectResult(out result);
return result;
}
private bool PollSelectWrite(UdpConnectionContract.Imp! conn,
int millis)
{
bool result = false;
conn.SendPollSelectWrite(millis);
conn.RecvPollSelectResult(out result);
return result;
}
internal override bool Poll(int microSeconds, SelectMode mode)
{
int millis = 0;
if (microSeconds < 0)
{
millis = Int32.MaxValue;
}
else
{
millis = microSeconds / 1000;
}
UdpConnectionContract.Imp conn = m_Conn.Acquire();
try
{
if (conn.InState(UdpConnectionContract.Closed.Value))
{
throw new ObjectDisposedException("socket");
}
switch (mode)
{
case SelectMode.SelectRead:
return PollSelectRead(conn, millis);
break;
case SelectMode.SelectWrite:
return PollSelectWrite(conn, millis);
break;
case SelectMode.SelectError:
return false;
// We don't support any of the requisite operations
break;
default:
throw new NotSupportedException();
}
}
finally
{
m_Conn.Release(conn);
}
}
internal override int ReceiveFrom(byte[]! buffer,
int offset,
int size,
SocketFlags socketFlags,
ref EndPoint remoteEP)
{
ValidateBufferOffsets(buffer, offset, size);
if (socketFlags != SocketFlags.None)
{
// Invalid flags for this operation
throw new SocketException(SocketErrors.WSAEINVAL);
}
UdpConnectionContract.Imp conn = m_Conn.Acquire();
try
{
if (conn.InState(UdpConnectionContract.Closed.Value))
{
throw new SocketException(BAD_SOCKET_STATE_ERR);
}
conn.SendRead();
switch receive
{
case conn.Data(uint addr, ushort port, byte[]! in ExHeap rdata) :
{
remoteEP = new IPEndPoint(new IPAddress(addr), port);
int amount = Math.Min(rdata.Length, size);
Bitter.ToByteArray(rdata, 0, amount, buffer, offset);
delete rdata;
return amount;
}
break;
}
}
finally
{
m_Conn.Release(conn);
}
}
internal override int Receive(byte[]! buffer,
int offset,
int size,
SocketFlags socketFlags)
{
EndPoint dummy = null;
return ReceiveFrom(buffer, offset, size, socketFlags, ref dummy);
}
internal override int Send([Claims] byte[]! in ExHeap data,
SocketFlags socketFlags)
{
int byteCount = data.Length;
if (socketFlags != SocketFlags.None)
{
// Invalid flags for this operation
throw new SocketException(SocketErrors.WSAEINVAL);
}
UdpConnectionContract.Imp conn = m_Conn.Acquire();
try
{
if (!conn.InState(UdpConnectionContract.Connected.Value))
{
throw new SocketException(BAD_SOCKET_STATE_ERR);
}
conn.SendWrite(data); // Give away data
switch receive
{
case conn.OK() :
// Success
return byteCount;
break;
case conn.DataTooLarge() :
// uh oh
throw new SocketException(SocketErrors.WSAEMSGSIZE);
break;
}
}
finally
{
m_Conn.Release(conn);
}
}
internal override int Send(byte[]! buffer,
int offset,
int size,
SocketFlags socketFlags)
{
ValidateBufferOffsets(buffer, offset, size);
byte[] in ExHeap dataToWrite = Bitter.FromByteArray(buffer, offset, size);
return Send(dataToWrite, socketFlags);
}
internal override int SendTo(byte[]! buffer,
int offset,
int size,
SocketFlags socketFlags,
EndPoint remoteEP)
{
ValidateBufferOffsets(buffer, offset, size);
if (socketFlags != SocketFlags.None)
{
// Invalid flags for this operation
throw new SocketException(SocketErrors.WSAEINVAL);
}
IPEndPoint ep = remoteEP as IPEndPoint;
if (ep == null)
{
throw new SocketException(SocketErrors.WSAEINVAL);
}
UdpConnectionContract.Imp conn = m_Conn.Acquire();
try
{
if (conn.InState(UdpConnectionContract.Closed.Value))
{
throw new SocketException(BAD_SOCKET_STATE_ERR);
}
// Copy the data to write into an ExHeap vector and send it
byte[] in ExHeap dataToWrite = Bitter.FromByteArray(buffer, offset, size);
conn.SendWriteTo((uint)ep.Address.m_addr,
unchecked((ushort)ep.Port),
dataToWrite);
// no longer own dataToWrite!
switch receive
{
case conn.OK() :
// Success
return size;
break;
case conn.DataTooLarge() :
// uh oh
throw new SocketException(SocketErrors.WSAEMSGSIZE);
break;
}
}
finally
{
m_Conn.Release(conn);
}
}
internal override void Shutdown(SocketShutdown how)
{
throw new SocketException(SocketErrors.WSAEOPNOTSUPP);
}
internal override int Available { get { return 0; /* XXX */ } }
internal override EndPoint LocalEndPoint
{
get { return m_LocalEndPoint; }
}
internal override EndPoint RemoteEndPoint
{
get { return m_RemoteEndPoint; }
}
internal override bool Blocking
{
get {
return true;
}
set {
if (value != true)
{
// TODO: non-blocking mode!
throw new NotSupportedException();
}
}
}
internal override bool Connected
{
get {
UdpConnectionContract.Imp conn = m_Conn.Acquire();
try
{
return conn.InState(UdpConnectionContract.Connected.Value);
}
catch
{
return false;
}
finally
{
m_Conn.Release(conn);
}
}
}
internal override AddressFamily AddressFamily
{
get { return AddressFamily.InterNetwork; }
}
internal override SocketType SocketType
{
get { return SocketType.Dgram; }
}
internal override ProtocolType ProtocolType
{
get { return ProtocolType.Udp; }
}
}
}