e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
305 lines
12 KiB
C#
305 lines
12 KiB
C#
// <copyright>
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
|
|
namespace System.ServiceModel.Channels
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Runtime;
|
|
using System.Runtime.Diagnostics;
|
|
using System.ServiceModel.Diagnostics;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
|
|
internal class ServerUdpOutputChannel : UdpOutputChannel
|
|
{
|
|
public ServerUdpOutputChannel(ChannelManagerBase factory, MessageEncoder encoder, BufferManager bufferManager, UdpSocket[] sendSockets, UdpRetransmissionSettings retransmissionSettings, Uri via, bool isMulticast)
|
|
: base(factory, encoder, bufferManager, sendSockets, retransmissionSettings, via, isMulticast)
|
|
{
|
|
}
|
|
|
|
// will either return a valid socket or will set exceptionToBeThrown
|
|
protected UdpSocket GetSendSocket(IPAddress address, Uri destination, out Exception exceptionToBeThrown)
|
|
{
|
|
Fx.Assert(this.IsMulticast == false, "This overload should only be used for unicast.");
|
|
|
|
UdpSocket result = null;
|
|
exceptionToBeThrown = null;
|
|
AddressFamily family = address.AddressFamily;
|
|
|
|
lock (ThisLock)
|
|
{
|
|
if (this.State == CommunicationState.Opened)
|
|
{
|
|
for (int i = 0; i < this.SendSockets.Length; i++)
|
|
{
|
|
if (family == this.SendSockets[i].AddressFamily)
|
|
{
|
|
result = this.SendSockets[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result == null)
|
|
{
|
|
exceptionToBeThrown = new InvalidOperationException(SR.RemoteAddressUnreachableDueToIPVersionMismatch(destination));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exceptionToBeThrown = CreateObjectDisposedException();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// will either return a valid socket or will set exceptionToBeThrown
|
|
protected UdpSocket GetSendSocket(int interfaceIndex, out Exception exceptionToBeThrown)
|
|
{
|
|
Fx.Assert(this.IsMulticast == true, "This overload should only be used for multicast.");
|
|
|
|
UdpSocket result = null;
|
|
exceptionToBeThrown = null;
|
|
|
|
lock (ThisLock)
|
|
{
|
|
if (this.State == CommunicationState.Opened)
|
|
{
|
|
for (int i = 0; i < this.SendSockets.Length; i++)
|
|
{
|
|
if (interfaceIndex == this.SendSockets[i].InterfaceIndex)
|
|
{
|
|
result = this.SendSockets[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result == null)
|
|
{
|
|
exceptionToBeThrown = new InvalidOperationException(SR.UdpSendFailedInterfaceIndexMatchNotFound(interfaceIndex));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
exceptionToBeThrown = CreateObjectDisposedException();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Must return non-null/non-empty array unless exceptionToBeThrown is has been set
|
|
protected override UdpSocket[] GetSendSockets(Message message, out IPEndPoint remoteEndPoint, out Exception exceptionToBeThrown)
|
|
{
|
|
Fx.Assert(message != null, "message can't be null");
|
|
|
|
UdpSocket[] socketList = null;
|
|
exceptionToBeThrown = null;
|
|
|
|
remoteEndPoint = null;
|
|
Uri destination;
|
|
bool isVia = false;
|
|
|
|
if (message.Properties.Via != null)
|
|
{
|
|
destination = message.Properties.Via;
|
|
isVia = true;
|
|
}
|
|
else if (message.Headers.To != null)
|
|
{
|
|
destination = message.Headers.To;
|
|
}
|
|
else
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ToOrViaRequired));
|
|
}
|
|
|
|
this.ValidateDestinationUri(destination, isVia);
|
|
|
|
if (destination.HostNameType == UriHostNameType.IPv4 || destination.HostNameType == UriHostNameType.IPv6)
|
|
{
|
|
remoteEndPoint = new IPEndPoint(IPAddress.Parse(destination.DnsSafeHost), destination.Port);
|
|
|
|
if (this.IsMulticast)
|
|
{
|
|
UdpSocket socket = this.GetSendSocketUsingInterfaceIndex(message.Properties, out exceptionToBeThrown);
|
|
|
|
if (socket != null)
|
|
{
|
|
if (socket.AddressFamily == remoteEndPoint.AddressFamily)
|
|
{
|
|
socketList = new UdpSocket[] { socket };
|
|
}
|
|
else
|
|
{
|
|
exceptionToBeThrown = new InvalidOperationException(SR.RemoteAddressUnreachableDueToIPVersionMismatch(destination.DnsSafeHost));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UdpSocket socket = this.GetSendSocket(remoteEndPoint.Address, destination, out exceptionToBeThrown);
|
|
if (socket != null)
|
|
{
|
|
socketList = new UdpSocket[] { socket };
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IPAddress[] remoteAddresses = DnsCache.Resolve(destination).AddressList;
|
|
|
|
if (this.IsMulticast)
|
|
{
|
|
UdpSocket socket = this.GetSendSocketUsingInterfaceIndex(message.Properties, out exceptionToBeThrown);
|
|
|
|
if (socket != null)
|
|
{
|
|
socketList = new UdpSocket[] { socket };
|
|
|
|
for (int i = 0; i < remoteAddresses.Length; i++)
|
|
{
|
|
if (remoteAddresses[i].AddressFamily == socket.AddressFamily)
|
|
{
|
|
remoteEndPoint = new IPEndPoint(remoteAddresses[i], destination.Port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (remoteEndPoint == null)
|
|
{
|
|
// for multicast, we only listen on either IPv4 or IPv6 (not both).
|
|
// if we didn't find a matching remote endpoint, then it would indicate that
|
|
// the remote host didn't resolve to an address we can use...
|
|
exceptionToBeThrown = new InvalidOperationException(SR.RemoteAddressUnreachableDueToIPVersionMismatch(destination.DnsSafeHost));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool useIPv4 = true;
|
|
bool useIPv6 = true;
|
|
|
|
for (int i = 0; i < remoteAddresses.Length; i++)
|
|
{
|
|
IPAddress address = remoteAddresses[i];
|
|
|
|
if (address.AddressFamily == AddressFamily.InterNetwork && useIPv4)
|
|
{
|
|
UdpSocket socket = this.GetSendSocket(address, destination, out exceptionToBeThrown);
|
|
if (socket == null)
|
|
{
|
|
if (this.State != CommunicationState.Opened)
|
|
{
|
|
// time to exit, the channel is closing down.
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// no matching socket on IPv4, so ignore future IPv4 addresses
|
|
// in the remoteAddresses list
|
|
useIPv4 = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
remoteEndPoint = new IPEndPoint(address, destination.Port);
|
|
socketList = new UdpSocket[] { socket };
|
|
break;
|
|
}
|
|
}
|
|
else if (address.AddressFamily == AddressFamily.InterNetworkV6 && useIPv6)
|
|
{
|
|
UdpSocket socket = this.GetSendSocket(address, destination, out exceptionToBeThrown);
|
|
if (socket == null)
|
|
{
|
|
if (this.State != CommunicationState.Opened)
|
|
{
|
|
// time to exit, the channel is closing down.
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// no matching socket on IPv6, so ignore future IPv6 addresses
|
|
// in the remoteAddresses list
|
|
useIPv6 = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
remoteEndPoint = new IPEndPoint(address, destination.Port);
|
|
socketList = new UdpSocket[] { socket };
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return socketList;
|
|
}
|
|
|
|
private UdpSocket GetSendSocketUsingInterfaceIndex(MessageProperties properties, out Exception exceptionToBeThrown)
|
|
{
|
|
NetworkInterfaceMessageProperty property;
|
|
UdpSocket socket = null;
|
|
exceptionToBeThrown = null;
|
|
|
|
if (!NetworkInterfaceMessageProperty.TryGet(properties, out property))
|
|
{
|
|
if (this.SendSockets.Length > 1)
|
|
{
|
|
// this property is required on all messages sent from the channel listener.
|
|
// the client channel does not use this method to get the send SendSockets or the
|
|
// remote endpoint, so it is safe to throw...
|
|
exceptionToBeThrown = new InvalidOperationException(SR.NetworkInterfaceMessagePropertyMissing(typeof(NetworkInterfaceMessageProperty)));
|
|
}
|
|
else
|
|
{
|
|
// there is only one socket, so just send it on that one.
|
|
socket = this.SendSockets[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
socket = this.GetSendSocket(property.InterfaceIndex, out exceptionToBeThrown);
|
|
}
|
|
|
|
return socket;
|
|
}
|
|
|
|
private void ValidateDestinationUri(Uri destination, bool isVia)
|
|
{
|
|
if (!destination.Scheme.Equals(UdpConstants.Scheme, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (isVia)
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ViaUriIsNotValid(destination, SR.UriSchemeNotSupported(destination.Scheme))));
|
|
}
|
|
else
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ToAddressIsNotValid(destination, SR.UriSchemeNotSupported(destination.Scheme))));
|
|
}
|
|
}
|
|
|
|
if (destination.Port < 1 || destination.Port > IPEndPoint.MaxPort)
|
|
{
|
|
if (isVia)
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ViaUriIsNotValid(destination, SR.PortNumberInvalid(1, IPEndPoint.MaxPort))));
|
|
}
|
|
else
|
|
{
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ToAddressIsNotValid(destination, SR.PortNumberInvalid(1, IPEndPoint.MaxPort))));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|