// // Copyright (c) Microsoft Corporation. All rights reserved. // 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)))); } } } } }