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.netty.handler.ipfilter;
17
18 import io.netty.channel.Channel;
19 import io.netty.channel.ChannelFuture;
20 import io.netty.channel.ChannelFutureListener;
21 import io.netty.channel.ChannelHandlerContext;
22 import io.netty.channel.ChannelInboundHandlerAdapter;
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> extends ChannelInboundHandlerAdapter {
39
40 @Override
41 public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
42 handleNewChannel(ctx);
43 ctx.fireChannelRegistered();
44 }
45
46 @Override
47 public void channelActive(ChannelHandlerContext ctx) throws Exception {
48 if (!handleNewChannel(ctx)) {
49 throw new IllegalStateException("cannot determine to accept or reject a channel: " + ctx.channel());
50 } else {
51 ctx.fireChannelActive();
52 }
53 }
54
55 private boolean handleNewChannel(ChannelHandlerContext ctx) throws Exception {
56 @SuppressWarnings("unchecked")
57 T remoteAddress = (T) ctx.channel().remoteAddress();
58
59 // If the remote address is not available yet, defer the decision.
60 if (remoteAddress == null) {
61 return false;
62 }
63
64 // No need to keep this handler in the pipeline anymore because the decision is going to be made now.
65 // Also, this will prevent the subsequent events from being handled by this handler.
66 ctx.pipeline().remove(this);
67
68 if (accept(ctx, remoteAddress)) {
69 channelAccepted(ctx, remoteAddress);
70 } else {
71 ChannelFuture rejectedFuture = channelRejected(ctx, remoteAddress);
72 if (rejectedFuture != null) {
73 rejectedFuture.addListener(ChannelFutureListener.CLOSE);
74 } else {
75 ctx.close();
76 }
77 }
78
79 return true;
80 }
81
82 /**
83 * This method is called immediately after a {@link io.netty.channel.Channel} gets registered.
84 *
85 * @return Return true if connections from this IP address and port should be accepted. False otherwise.
86 */
87 protected abstract boolean accept(ChannelHandlerContext ctx, T remoteAddress) throws Exception;
88
89 /**
90 * This method is called if {@code remoteAddress} gets accepted by
91 * {@link #accept(ChannelHandlerContext, SocketAddress)}. You should override it if you would like to handle
92 * (e.g. respond to) accepted addresses.
93 */
94 @SuppressWarnings("UnusedParameters")
95 protected void channelAccepted(ChannelHandlerContext ctx, T remoteAddress) { }
96
97 /**
98 * This method is called if {@code remoteAddress} gets rejected by
99 * {@link #accept(ChannelHandlerContext, SocketAddress)}. You should override it if you would like to handle
100 * (e.g. respond to) rejected addresses.
101 *
102 * @return A {@link ChannelFuture} if you perform I/O operations, so that
103 * the {@link Channel} can be closed once it completes. Null otherwise.
104 */
105 @SuppressWarnings("UnusedParameters")
106 protected ChannelFuture channelRejected(ChannelHandlerContext ctx, T remoteAddress) {
107 return null;
108 }
109 }