[#576] UDP socket bind to specific IP does not receive broadcast on Linux

- Log a warning message if a user attempts to bind to a non-wildcard
  address with SO_BROADCAST set on non-Windows
This commit is contained in:
Trustin Lee 2012-08-30 15:50:55 +09:00
parent 59e511fdc6
commit 68e86d8667
3 changed files with 81 additions and 1 deletions

View File

@ -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;
}

View File

@ -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()) {

View File

@ -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);