1 /*
2 * Copyright 2012 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 * http://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 org.jboss.netty.handler.ipfilter;
17
18 import java.net.InetSocketAddress;
19
20 import org.jboss.netty.channel.ChannelEvent;
21 import org.jboss.netty.channel.ChannelFuture;
22 import org.jboss.netty.channel.ChannelFutureListener;
23 import org.jboss.netty.channel.ChannelHandlerContext;
24 import org.jboss.netty.channel.ChannelStateEvent;
25 import org.jboss.netty.channel.ChannelUpstreamHandler;
26 import org.jboss.netty.channel.Channels;
27
28 // TODO: Auto-generated Javadoc
29
30 /** General class that handle Ip Filtering. */
31 public abstract class IpFilteringHandlerImpl implements ChannelUpstreamHandler, IpFilteringHandler {
32
33 private IpFilterListener listener;
34
35 /**
36 * Called when the channel is connected. It returns True if the corresponding connection
37 * is to be allowed. Else it returns False.
38 *
39 * @param inetSocketAddress the remote {@link InetSocketAddress} from client
40 * @return True if the corresponding connection is allowed, else False.
41 */
42 protected abstract boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress)
43 throws Exception;
44
45 /**
46 * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept().
47 * This method enables your implementation to send a message back to the client before closing
48 * or whatever you need. This method returns a ChannelFuture on which the implementation
49 * will wait uninterruptibly before closing the channel.<br>
50 * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned.
51 *
52 * @param inetSocketAddress the remote {@link InetSocketAddress} from client
53 * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed.
54 */
55 protected ChannelFuture handleRefusedChannel(ChannelHandlerContext ctx, ChannelEvent e,
56 InetSocketAddress inetSocketAddress) throws Exception {
57 if (listener == null) {
58 return null;
59 }
60 return listener.refused(ctx, e, inetSocketAddress);
61 }
62
63 protected ChannelFuture handleAllowedChannel(ChannelHandlerContext ctx, ChannelEvent e,
64 InetSocketAddress inetSocketAddress) throws Exception {
65 if (listener == null) {
66 return null;
67 }
68 return listener.allowed(ctx, e, inetSocketAddress);
69 }
70
71 /**
72 * Internal method to test if the current channel is blocked. Should not be overridden.
73 *
74 * @return True if the current channel is blocked, else False
75 */
76 protected boolean isBlocked(ChannelHandlerContext ctx) {
77 return ctx.getAttachment() != null;
78 }
79
80 /**
81 * Called in handleUpstream, if this channel was previously blocked,
82 * to check if whatever the event, it should be passed to the next entry in the pipeline.<br>
83 * If one wants to not block events, just overridden this method by returning always true.<br><br>
84 * <b>Note that OPENED and BOUND events are still passed to the next entry in the pipeline since
85 * those events come out before the CONNECTED event and so the possibility to filter the connection.</b>
86 *
87 * @return True if the event should continue, False if the event should not continue
88 * since this channel was blocked by this filter
89 */
90 protected boolean continues(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
91 if (listener != null) {
92 return listener.continues(ctx, e);
93 } else {
94 return false;
95 }
96 }
97
98 public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
99 if (e instanceof ChannelStateEvent) {
100 ChannelStateEvent evt = (ChannelStateEvent) e;
101 switch (evt.getState()) {
102 case OPEN:
103 case BOUND:
104 if (evt.getValue() != Boolean.TRUE) {
105 ctx.sendUpstream(e);
106 return;
107 }
108
109 // Special case: OPEND and BOUND events are before CONNECTED,
110 // but CLOSED and UNBOUND events are after DISCONNECTED: should those events be blocked too?
111 if (isBlocked(ctx) && !continues(ctx, evt)) {
112 // don't pass to next level since channel was blocked early
113 return;
114 } else {
115 ctx.sendUpstream(e);
116 return;
117 }
118 case CONNECTED:
119 if (evt.getValue() != null) {
120 // CONNECTED
121 InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getChannel().getRemoteAddress();
122 if (!accept(ctx, e, inetSocketAddress)) {
123 ctx.setAttachment(Boolean.TRUE);
124 ChannelFuture future = handleRefusedChannel(ctx, e, inetSocketAddress);
125 if (future != null) {
126 future.addListener(ChannelFutureListener.CLOSE);
127 } else {
128 Channels.close(e.getChannel());
129 }
130 if (isBlocked(ctx) && !continues(ctx, evt)) {
131 // don't pass to next level since channel was blocked early
132 return;
133 }
134 } else {
135 handleAllowedChannel(ctx, e, inetSocketAddress);
136 }
137 // This channel is not blocked
138 ctx.setAttachment(null);
139 } else {
140 // DISCONNECTED
141 if (isBlocked(ctx) && !continues(ctx, evt)) {
142 // don't pass to next level since channel was blocked early
143 return;
144 }
145 }
146 break;
147 }
148 }
149 if (isBlocked(ctx) && !continues(ctx, e)) {
150 // don't pass to next level since channel was blocked early
151 return;
152 }
153 // Whatever it is, if not blocked, goes to the next level
154 ctx.sendUpstream(e);
155 }
156
157 public void setIpFilterListener(IpFilterListener listener) {
158 this.listener = listener;
159 }
160
161 public void removeIpFilterListener() {
162 listener = null;
163 }
164 }