/// <summary> /// Starts this SSDP listener. When this method was called, the listener will receive SSDP messages. /// </summary> public void Start() { lock (_cpData.SyncObj) { if (_isActive) { throw new IllegalCallException("SSDPClientController is already active"); } _isActive = true; IList <IPAddress> addresses = NetworkHelper.OrderAddressesByScope(NetworkHelper.GetUPnPEnabledIPAddresses(UPnPConfiguration.IP_ADDRESS_BINDINGS)); // Add endpoints foreach (IPAddress address in addresses) { AddressFamily family = address.AddressFamily; if (family == AddressFamily.InterNetwork && !UPnPConfiguration.USE_IPV4) { continue; } if (family == AddressFamily.InterNetworkV6 && !UPnPConfiguration.USE_IPV6) { continue; } EndpointConfiguration config = new EndpointConfiguration { SSDPMulticastAddress = NetworkHelper.GetSSDPMulticastAddressForInterface(address), EndPointIPAddress = address }; // Multicast receiver socket - used for receiving multicast messages Socket socket = new Socket(family, SocketType.Dgram, ProtocolType.Udp); config.SSDP_UDP_MulticastReceiveSocket = socket; try { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); NetworkHelper.BindAndConfigureSSDPMulticastSocket(socket, address); StartMulticastReceive(new UDPAsyncReceiveState <EndpointConfiguration>(config, UPnPConsts.UDP_SSDP_RECEIVE_BUFFER_SIZE, socket)); } catch (Exception) // SocketException, SecurityException { UPnPConfiguration.LOGGER.Info("SSDPClientController: Unable to bind to multicast address(es) for endpoint '{0}'", NetworkHelper.IPAddrToString(config.EndPointIPAddress)); } // Unicast sender and receiver socket - used for sending M-SEARCH queries and receiving its responses. // We need a second socket here because the search responses which arrive at this port are structured // in another way than the notifications which arrive at our multicast socket. socket = new Socket(family, SocketType.Dgram, ProtocolType.Udp); config.SSDP_UDP_UnicastSocket = socket; try { socket.Bind(new IPEndPoint(config.EndPointIPAddress, 0)); _cpData.Endpoints.Add(config); StartUnicastReceive(new UDPAsyncReceiveState <EndpointConfiguration>(config, UPnPConsts.UDP_SSDP_RECEIVE_BUFFER_SIZE, socket)); } catch (Exception e) // SocketException, SecurityException { UPnPConfiguration.LOGGER.Info("SSDPClientController: Unable to bind to unicast address '{0}'", e, NetworkHelper.IPAddrToString(config.EndPointIPAddress)); } } _expirationTimer = new Timer(OnExpirationTimerElapsed, null, EXPIRATION_TIMER_INTERVAL, EXPIRATION_TIMER_INTERVAL); } }
/// <summary> /// Configures the SSDP part of the given endpoint <paramref name="config"/>. /// Starts the SSDP UDP listener client for the given network endpoint. /// </summary> /// <param name="config">The endpoint configuration which should be started.</param> public void StartSSDPEndpoint(EndpointConfiguration config) { IPAddress address = config.EndPointIPAddress; AddressFamily family = address.AddressFamily; config.SSDPMulticastAddress = NetworkHelper.GetSSDPMulticastAddressForInterface(address); config.SSDPSearchPort = UPnPConsts.DEFAULT_SSDP_SEARCH_PORT; // Multicast socket - used for sending and receiving multicast messages Socket socket = new Socket(family, SocketType.Dgram, ProtocolType.Udp); config.SSDP_UDP_MulticastReceiveSocket = socket; try { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); NetworkHelper.BindAndConfigureSSDPMulticastSocket(socket, address); StartReceive(new UDPAsyncReceiveState <EndpointConfiguration>(config, UPnPConsts.UDP_SSDP_RECEIVE_BUFFER_SIZE, socket)); } catch (Exception e) // SocketException, SecurityException { UPnPConfiguration.LOGGER.Warn("SSDPServerController: Unable to bind to multicast address(es) for endpoint '{0}'", e, NetworkHelper.IPAddrToString(address)); } // Unicast sender and receiver socket - used for receiving unicast M-SEARCH queries and sending M-SEARCH responses socket = new Socket(family, SocketType.Dgram, ProtocolType.Udp); config.SSDP_UDP_UnicastSocket = socket; try { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); try { // Try to bind our unicast receiver socket to the default SSDP port socket.Bind(new IPEndPoint(address, config.SSDPSearchPort)); } catch (SocketException e) { if (e.SocketErrorCode != SocketError.AddressAlreadyInUse) { throw; } // If binding to the default SSDP port doesn't work, try a random port... socket.Bind(new IPEndPoint(address, 0)); // ... which will be stored in the SSDPSearchPort variable which will be used for the SEARCHPORT.UPNP.ORG SSDP header. config.SSDPSearchPort = ((IPEndPoint)socket.LocalEndPoint).Port; } UPnPConfiguration.LOGGER.Info("UPnPServerController: SSDP enabled for IP endpoint '{0}', search port is {1}", NetworkHelper.IPAddrToString(address), config.SSDPSearchPort); // The following is necessary to retrieve the remote IP address when we receive SSDP packets if (family == AddressFamily.InterNetwork) { try { // Receiving options socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true); // Sending options socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, UPnPConfiguration.SSDP_UDP_TTL_V4); } catch (SocketException e) { UPnPConfiguration.LOGGER.Warn("GENAServerController: Could not set IPv4 options", e); } } else { try { // Receiving options socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.PacketInformation, true); // Sending options socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.HopLimit, UPnPConfiguration.SSDP_UDP_HOP_LIMIT_V6); } catch (SocketException e) { UPnPConfiguration.LOGGER.Warn("GENAServerController: Could not set IPv6 options", e); } } StartReceive(new UDPAsyncReceiveState <EndpointConfiguration>(config, UPnPConsts.UDP_SSDP_RECEIVE_BUFFER_SIZE, socket)); } catch (Exception e) // SocketException, SecurityException { UPnPConfiguration.LOGGER.Warn("SSDPServerController: Unable to bind to unicast address '{0}'", e, NetworkHelper.IPAddrToString(address)); } }