diff --git a/common/src/main/java/io/netty/util/internal/DetectionUtil.java b/common/src/main/java/io/netty/util/internal/DetectionUtil.java index e8ff95fddf..c5b4548391 100644 --- a/common/src/main/java/io/netty/util/internal/DetectionUtil.java +++ b/common/src/main/java/io/netty/util/internal/DetectionUtil.java @@ -15,6 +15,8 @@ */ package io.netty.util.internal; +import java.net.InetSocketAddress; +import java.net.ServerSocket; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; @@ -35,11 +37,47 @@ public final class DetectionUtil { private static final int JAVA_VERSION = javaVersion0(); private static final boolean HAS_UNSAFE = hasUnsafe(AtomicInteger.class.getClassLoader()); private static final boolean IS_WINDOWS; + private static final boolean IS_ROOT; static { String os = System.getProperty("os.name").toLowerCase(); // windows IS_WINDOWS = os.contains("win"); + + boolean root = false; + if (!IS_WINDOWS) { + for (int i = 1023; i > 0; i --) { + ServerSocket ss = null; + try { + ss = new ServerSocket(); + ss.setReuseAddress(true); + ss.bind(new InetSocketAddress(i)); + root = true; + break; + } catch (Exception e) { + // Failed to bind. + // Check the error message so that we don't always need to bind 1023 times. + String message = e.getMessage(); + if (message == null) { + message = ""; + } + message = message.toLowerCase(); + if (message.matches(".*permission.*denied.*")) { + break; + } + } finally { + if (ss != null) { + try { + ss.close(); + } catch (Exception e) { + // Ignore. + } + } + } + } + } + + IS_ROOT = root; } /** @@ -49,6 +87,14 @@ public final class DetectionUtil { return IS_WINDOWS; } + /** + * Return {@code true} if the current user is root. Note that this method returns + * {@code false} if on Windows. + */ + public static boolean isRoot() { + return IS_ROOT; + } + public static boolean hasUnsafe() { return HAS_UNSAFE; } diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index 6f371e3bcf..46096f882d 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -20,8 +20,10 @@ import io.netty.buffer.MessageBuf; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; import io.netty.util.DefaultAttributeMap; +import io.netty.util.internal.DetectionUtil; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; import java.util.Random; @@ -316,7 +318,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha */ @Override public final int hashCode() { - return this.id; + return id; } /** @@ -450,6 +452,20 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha try { boolean wasActive = isActive(); + + // See: https://github.com/netty/netty/issues/576 + if (!DetectionUtil.isWindows() && !DetectionUtil.isRoot() && + Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) && + localAddress instanceof InetSocketAddress && + !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress()) { + // Warn a user about the fact that a non-root user can't receive a + // broadcast packet on *nix if the socket is bound on non-wildcard address. + logger.warn( + "A non-root user can't receive a broadcast packet if the socket " + + "is not bound to a wildcard address; binding to a non-wildcard " + + "address (" + localAddress + ") anyway as requested."); + } + doBind(localAddress); future.setSuccess(); if (!wasActive && isActive()) { diff --git a/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java index 4fda13879d..665196a1b4 100644 --- a/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java @@ -19,6 +19,9 @@ import static io.netty.channel.ChannelOption.*; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; +import io.netty.util.internal.DetectionUtil; import java.io.IOException; import java.net.DatagramSocket; @@ -33,6 +36,8 @@ import java.util.Map; */ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implements DatagramChannelConfig { + private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultDatagramChannelConfig.class); + private static final int DEFAULT_RECEIVE_PACKET_SIZE = 2048; private final DatagramSocket socket; @@ -137,6 +142,19 @@ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implement @Override public void setBroadcast(boolean broadcast) { try { + // See: https://github.com/netty/netty/issues/576 + if (broadcast && + !DetectionUtil.isWindows() && !DetectionUtil.isRoot() && + !socket.getLocalAddress().isAnyLocalAddress()) { + // Warn a user about the fact that a non-root user can't receive a + // broadcast packet on *nix if the socket is bound on non-wildcard address. + logger.warn( + "A non-root user can't receive a broadcast packet if the socket " + + "is not bound to a wildcard address; setting the SO_BROADCAST flag " + + "anyway as requested on the socket which is bound to " + + socket.getLocalSocketAddress() + "."); + } + socket.setBroadcast(broadcast); } catch (SocketException e) { throw new ChannelException(e);