View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  
21  package org.apache.mina.filter.errorgenerating;
22  
23  import java.util.Random;
24  
25  import org.apache.mina.core.buffer.IoBuffer;
26  import org.apache.mina.core.filterchain.IoFilter;
27  import org.apache.mina.core.filterchain.IoFilterAdapter;
28  import org.apache.mina.core.session.IoSession;
29  import org.apache.mina.core.write.DefaultWriteRequest;
30  import org.apache.mina.core.write.WriteRequest;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  /**
35   * An {@link IoFilter} implementation generating random bytes and PDU modification in
36   * your communication streams.
37   * It's quite simple to use :
38   * <code>ErrorGeneratingFilter egf = new ErrorGeneratingFilter();</code>
39   * For activate the change of some bytes in your {@link IoBuffer}, for a probability of 200 out
40   * of 1000 {@link IoBuffer} processed :
41   * <code>egf.setChangeByteProbability(200);</code>
42   * For activate the insertion of some bytes in your {@link IoBuffer}, for a
43   * probability of 200 out of 1000 :
44   * <code>egf.setInsertByteProbability(200);</code>
45   * And for the removing of some bytes :
46   * <code>egf.setRemoveByteProbability(200);</code>
47   * You can activate the error generation for write or read with the
48   * following methods :
49   * <code>egf.setManipulateReads(true);
50   * egf.setManipulateWrites(true); </code>
51   * 
52   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
53   * @org.apache.xbean.XBean
54   */
55  public class ErrorGeneratingFilter extends IoFilterAdapter {
56      private int removeByteProbability = 0;
57  
58      private int insertByteProbability = 0;
59  
60      private int changeByteProbability = 0;
61  
62      private int removePduProbability = 0;
63  
64      private int duplicatePduProbability = 0;
65  
66      private int resendPduLasterProbability = 0;
67  
68      private int maxInsertByte = 10;
69  
70      private boolean manipulateWrites = false;
71  
72      private boolean manipulateReads = false;
73  
74      private Random rng = new Random();
75  
76      final private Logger logger = LoggerFactory
77              .getLogger(ErrorGeneratingFilter.class);
78  
79      @Override
80      public void filterWrite(NextFilter nextFilter, IoSession session,
81              WriteRequest writeRequest) throws Exception {
82          if (manipulateWrites) {
83              // manipulate bytes
84              if (writeRequest.getMessage() instanceof IoBuffer) {
85                  manipulateIoBuffer(session, (IoBuffer) writeRequest
86                          .getMessage());
87                  IoBuffer buffer = insertBytesToNewIoBuffer(session,
88                          (IoBuffer) writeRequest.getMessage());
89                  if (buffer != null) {
90                      writeRequest = new DefaultWriteRequest(buffer, writeRequest
91                              .getFuture(), writeRequest.getDestination());
92                  }
93                  // manipulate PDU
94              } else {
95                  if (duplicatePduProbability > rng.nextInt()) {
96                      nextFilter.filterWrite(session, writeRequest);
97                  }
98                  
99                  if (resendPduLasterProbability > rng.nextInt()) {
100                     // store it somewhere and trigger a write execution for
101                     // later
102                     // TODO
103                 }
104                 if (removePduProbability > rng.nextInt()) {
105                     return;
106                 }
107             }
108         }
109         nextFilter.filterWrite(session, writeRequest);
110     }
111 
112     @Override
113     public void messageReceived(NextFilter nextFilter, IoSession session,
114             Object message) throws Exception {
115         if (manipulateReads) {
116             if (message instanceof IoBuffer) {
117                 // manipulate bytes
118                 manipulateIoBuffer(session, (IoBuffer) message);
119                 IoBuffer buffer = insertBytesToNewIoBuffer(session,
120                         (IoBuffer) message);
121                 if (buffer != null) {
122                     message = buffer;
123                 }
124             } else {
125                 // manipulate PDU
126                 // TODO
127             }
128         }
129         nextFilter.messageReceived(session, message);
130     }
131 
132     private IoBuffer insertBytesToNewIoBuffer(IoSession session, IoBuffer buffer) {
133         if (insertByteProbability > rng.nextInt(1000)) {
134             logger.info(buffer.getHexDump());
135             // where to insert bytes ?
136             int pos = rng.nextInt(buffer.remaining()) - 1;
137 
138             // how many byte to insert ?
139             int count = rng.nextInt(maxInsertByte-1)+1;
140 
141             IoBuffer newBuff = IoBuffer.allocate(buffer.remaining() + count);
142             for (int i = 0; i < pos; i++)
143                 newBuff.put(buffer.get());
144             for (int i = 0; i < count; i++) {
145                 newBuff.put((byte) (rng.nextInt(256)));
146             }
147             while (buffer.remaining() > 0) {
148                 newBuff.put(buffer.get());
149             }
150             newBuff.flip();
151 
152             logger.info("Inserted " + count + " bytes.");
153             logger.info(newBuff.getHexDump());
154             return newBuff;
155         }
156         return null;
157     }
158 
159     private void manipulateIoBuffer(IoSession session, IoBuffer buffer) {
160         if ((buffer.remaining() > 0) && (removeByteProbability > rng.nextInt(1000))) {
161             logger.info(buffer.getHexDump());
162             // where to remove bytes ?
163             int pos = rng.nextInt(buffer.remaining());
164             // how many byte to remove ?
165             int count = rng.nextInt(buffer.remaining() - pos) + 1;
166             if (count == buffer.remaining())
167                 count = buffer.remaining() - 1;
168 
169             IoBuffer newBuff = IoBuffer.allocate(buffer.remaining() - count);
170             for (int i = 0; i < pos; i++)
171                 newBuff.put(buffer.get());
172 
173             buffer.skip(count); // hole
174             while (newBuff.remaining() > 0)
175                 newBuff.put(buffer.get());
176             newBuff.flip();
177             // copy the new buffer in the old one
178             buffer.rewind();
179             buffer.put(newBuff);
180             buffer.flip();
181             logger.info("Removed " + count + " bytes at position " + pos + ".");
182             logger.info(buffer.getHexDump());
183         }
184         if ((buffer.remaining() > 0) && (changeByteProbability > rng.nextInt(1000))) {
185             logger.info(buffer.getHexDump());
186             // how many byte to change ?
187             int count = rng.nextInt(buffer.remaining() - 1) + 1;
188 
189             byte[] values = new byte[count];
190             rng.nextBytes(values);
191             for (int i = 0; i < values.length; i++) {
192                 int pos = rng.nextInt(buffer.remaining());
193                 buffer.put(pos, values[i]);
194             }
195             logger.info("Modified " + count + " bytes.");
196             logger.info(buffer.getHexDump());
197         }
198     }
199 
200     public int getChangeByteProbability() {
201         return changeByteProbability;
202     }
203     
204     /**
205      * Set the probability for the change byte error.
206      * If this probability is > 0 the filter will modify a random number of byte
207      * of the processed {@link IoBuffer}.
208      * @param changeByteProbability probability of modifying an IoBuffer out of 1000 processed {@link IoBuffer} 
209      */
210     public void setChangeByteProbability(int changeByteProbability) {
211         this.changeByteProbability = changeByteProbability;
212     }
213 
214     public int getDuplicatePduProbability() {
215         return duplicatePduProbability;
216     }
217     
218     /**
219      * not functional ATM
220      * @param duplicatePduProbability
221      */
222     public void setDuplicatePduProbability(int duplicatePduProbability) {
223         this.duplicatePduProbability = duplicatePduProbability;
224     }
225 
226     public int getInsertByteProbability() {
227         return insertByteProbability;
228     }
229 
230     /**
231      * Set the probability for the insert byte error.
232      * If this probability is > 0 the filter will insert a random number of byte
233      * in the processed {@link IoBuffer}.
234      * @param changeByteProbability probability of inserting in IoBuffer out of 1000 processed {@link IoBuffer} 
235      */
236     public void setInsertByteProbability(int insertByteProbability) {
237         this.insertByteProbability = insertByteProbability;
238     }
239 
240     public boolean isManipulateReads() {
241         return manipulateReads;
242     }
243 
244     /**
245      * Set to true if you want to apply error to the read {@link IoBuffer}
246      * @param manipulateReads
247      */
248     public void setManipulateReads(boolean manipulateReads) {
249         this.manipulateReads = manipulateReads;
250     }
251 
252     public boolean isManipulateWrites() {
253         return manipulateWrites;
254     }
255 
256     /**
257      * Set to true if you want to apply error to the written {@link IoBuffer}
258      * @param manipulateWrites
259      */
260     public void setManipulateWrites(boolean manipulateWrites) {
261         this.manipulateWrites = manipulateWrites;
262     }
263 
264     public int getRemoveByteProbability() {
265         return removeByteProbability;
266     }
267 
268     /**
269      * Set the probability for the remove byte error.
270      * If this probability is > 0 the filter will remove a random number of byte
271      * in the processed {@link IoBuffer}.
272      * @param changeByteProbability probability of modifying an {@link IoBuffer} out of 1000 processed IoBuffer 
273      */
274     public void setRemoveByteProbability(int removeByteProbability) {
275         this.removeByteProbability = removeByteProbability;
276     }
277 
278     public int getRemovePduProbability() {
279         return removePduProbability;
280     }
281 
282     /**
283      * not functional ATM
284      * @param removePduProbability
285      */
286     public void setRemovePduProbability(int removePduProbability) {
287         this.removePduProbability = removePduProbability;
288     }
289 
290     public int getResendPduLasterProbability() {
291         return resendPduLasterProbability;
292     }
293     /**
294      * not functional ATM
295      * @param resendPduLasterProbability
296      */
297     public void setResendPduLasterProbability(int resendPduLasterProbability) {
298         this.resendPduLasterProbability = resendPduLasterProbability;
299     }
300 
301     public int getMaxInsertByte() {
302         return maxInsertByte;
303     }
304 
305     /**
306      * Set the maximum number of byte the filter can insert in a {@link IoBuffer}.
307      * The default value is 10.
308      * @param maxInsertByte maximum bytes inserted in a {@link IoBuffer} 
309      */
310     public void setMaxInsertByte(int maxInsertByte) {
311         this.maxInsertByte = maxInsertByte;
312     }
313 }