1 /* 2 * Copyright 2014 The Netty Project 3 * 4 * The Netty Project licenses this file to you under the Apache License, 5 * version 2.0 (the "License"); you may not use this file except in compliance 6 * with the License. You may obtain a copy of the License at: 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 package io.netty5.handler.ipfilter; 17 18 import io.netty5.channel.Channel; 19 import io.netty5.channel.ChannelFutureListeners; 20 import io.netty5.channel.ChannelHandler; 21 import io.netty5.channel.ChannelHandlerContext; 22 import io.netty5.util.concurrent.Future; 23 24 import java.net.SocketAddress; 25 26 /** 27 * This class provides the functionality to either accept or reject new {@link Channel}s 28 * based on their IP address. 29 * <p> 30 * You should inherit from this class if you would like to implement your own IP-based filter. Basically you have to 31 * implement {@link #accept(ChannelHandlerContext, SocketAddress)} to decided whether you want to accept or reject 32 * a connection from the remote address. 33 * <p> 34 * Furthermore overriding {@link #channelRejected(ChannelHandlerContext, SocketAddress)} gives you the 35 * flexibility to respond to rejected (denied) connections. If you do not want to send a response, just have it return 36 * null. Take a look at {@link RuleBasedIpFilter} for details. 37 */ 38 public abstract class AbstractRemoteAddressFilter<T extends SocketAddress> implements ChannelHandler { 39 40 @Override 41 public void channelRegistered(ChannelHandlerContext ctx) throws Exception { 42 handleNewChannel(ctx, true); 43 } 44 45 @Override 46 public void channelActive(ChannelHandlerContext ctx) throws Exception { 47 if (!handleNewChannel(ctx, false)) { 48 throw new IllegalStateException("cannot determine to accept or reject a channel: " + ctx.channel()); 49 } 50 } 51 52 private boolean handleNewChannel(ChannelHandlerContext ctx, boolean register) throws Exception { 53 @SuppressWarnings("unchecked") 54 T remoteAddress = (T) ctx.channel().remoteAddress(); 55 boolean remove = false; 56 try { 57 // If the remote address is not available yet, defer the decision. 58 if (remoteAddress == null) { 59 return false; 60 } 61 62 if (accept(ctx, remoteAddress)) { 63 channelAccepted(ctx, remoteAddress); 64 remove = true; 65 } else { 66 Future<Void> rejectedFuture = channelRejected(ctx, remoteAddress); 67 if (rejectedFuture != null && !rejectedFuture.isDone()) { 68 rejectedFuture.addListener(ctx, ChannelFutureListeners.CLOSE); 69 } else { 70 ctx.close(); 71 } 72 } 73 return true; 74 } finally { 75 if (!ctx.isRemoved()) { 76 if (register) { 77 ctx.fireChannelRegistered(); 78 } else { 79 ctx.fireChannelActive(); 80 } 81 if (remove) { 82 // No need to keep this handler in the pipeline anymore because the decision is going to be made 83 // now. Also, this will prevent the subsequent events from being handled by this handler. 84 ctx.pipeline().remove(this); 85 } 86 } 87 } 88 } 89 90 /** 91 * This method is called immediately after a {@link io.netty5.channel.Channel} gets registered. 92 * 93 * @return Return true if connections from this IP address and port should be accepted. False otherwise. 94 */ 95 protected abstract boolean accept(ChannelHandlerContext ctx, T remoteAddress) throws Exception; 96 97 /** 98 * This method is called if {@code remoteAddress} gets accepted by 99 * {@link #accept(ChannelHandlerContext, SocketAddress)}. You should override it if you would like to handle 100 * (e.g. respond to) accepted addresses. 101 */ 102 @SuppressWarnings("UnusedParameters") 103 protected void channelAccepted(ChannelHandlerContext ctx, T remoteAddress) { } 104 105 /** 106 * This method is called if {@code remoteAddress} gets rejected by 107 * {@link #accept(ChannelHandlerContext, SocketAddress)}. You should override it if you would like to handle 108 * (e.g. respond to) rejected addresses. 109 * 110 * @return A {@link Future} if you perform I/O operations, so that 111 * the {@link Channel} can be closed once it completes. Null otherwise. 112 */ 113 @SuppressWarnings("UnusedParameters") 114 protected Future<Void> channelRejected(ChannelHandlerContext ctx, T remoteAddress) { 115 return null; 116 } 117 }