[#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:
parent
59e511fdc6
commit
68e86d8667
@ -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;
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user