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      private final Logger logger = LoggerFactory.getLogger(ErrorGeneratingFilter.class);
77  
78      @Override
79      public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
80          if (manipulateWrites) {
81              // manipulate bytes
82              if (writeRequest.getMessage() instanceof IoBuffer) {
83                  manipulateIoBuffer(session, (IoBuffer) writeRequest.getMessage());
84                  IoBuffer buffer = insertBytesToNewIoBuffer(session, (IoBuffer) writeRequest.getMessage());
85                  
86                  if (buffer != null) {
87                      writeRequest = new DefaultWriteRequest(buffer, writeRequest.getFuture(),
88                              writeRequest.getDestination());
89                  }
90                  // manipulate PDU
91              } else {
92                  if (duplicatePduProbability > rng.nextInt()) {
93                      nextFilter.filterWrite(session, writeRequest);
94                  }
95  
96                  if (resendPduLasterProbability > rng.nextInt()) {
97                      // store it somewhere and trigger a write execution for
98                      // later
99                      // TODO
100                 }
101                 
102                 if (removePduProbability > rng.nextInt()) {
103                     return;
104                 }
105             }
106         }
107         
108         nextFilter.filterWrite(session, writeRequest);
109     }
110 
111     @Override
112     public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
113         if (manipulateReads && (message instanceof IoBuffer)) {
114             // manipulate bytes
115             manipulateIoBuffer(session, (IoBuffer) message);
116             IoBuffer buffer = insertBytesToNewIoBuffer(session, (IoBuffer) message);
117             
118             if (buffer != null) {
119                 message = buffer;
120             }
121         }
122         
123         nextFilter.messageReceived(session, message);
124     }
125 
126     private IoBuffer insertBytesToNewIoBuffer(IoSession session, IoBuffer buffer) {
127         if (insertByteProbability > rng.nextInt(1000)) {
128             logger.info(buffer.getHexDump());
129             // where to insert bytes ?
130             int pos = rng.nextInt(buffer.remaining()) - 1;
131 
132             // how many byte to insert ?
133             int count = rng.nextInt(maxInsertByte - 1) + 1;
134 
135             IoBuffer newBuff = IoBuffer.allocate(buffer.remaining() + count);
136             for (int i = 0; i < pos; i++)
137                 newBuff.put(buffer.get());
138             for (int i = 0; i < count; i++) {
139                 newBuff.put((byte) (rng.nextInt(256)));
140             }
141             while (buffer.remaining() > 0) {
142                 newBuff.put(buffer.get());
143             }
144             newBuff.flip();
145 
146             logger.info("Inserted " + count + " bytes.");
147             logger.info(newBuff.getHexDump());
148             return newBuff;
149         }
150         return null;
151     }
152 
153     private void manipulateIoBuffer(IoSession session, IoBuffer buffer) {
154         if ((buffer.remaining() > 0) && (removeByteProbability > rng.nextInt(1000))) {
155             logger.info(buffer.getHexDump());
156             // where to remove bytes ?
157             int pos = rng.nextInt(buffer.remaining());
158             // how many byte to remove ?
159             int count = rng.nextInt(buffer.remaining() - pos) + 1;
160             if (count == buffer.remaining())
161                 count = buffer.remaining() - 1;
162 
163             IoBuffer newBuff = IoBuffer.allocate(buffer.remaining() - count);
164             for (int i = 0; i < pos; i++)
165                 newBuff.put(buffer.get());
166 
167             buffer.skip(count); // hole
168             while (newBuff.remaining() > 0)
169                 newBuff.put(buffer.get());
170             newBuff.flip();
171             // copy the new buffer in the old one
172             buffer.rewind();
173             buffer.put(newBuff);
174             buffer.flip();
175             logger.info("Removed " + count + " bytes at position " + pos + ".");
176             logger.info(buffer.getHexDump());
177         }
178         if ((buffer.remaining() > 0) && (changeByteProbability > rng.nextInt(1000))) {
179             logger.info(buffer.getHexDump());
180             // how many byte to change ?
181             int count = rng.nextInt(buffer.remaining() - 1) + 1;
182 
183             byte[] values = new byte[count];
184             rng.nextBytes(values);
185             for (int i = 0; i < values.length; i++) {
186                 int pos = rng.nextInt(buffer.remaining());
187                 buffer.put(pos, values[i]);
188             }
189             logger.info("Modified " + count + " bytes.");
190             logger.info(buffer.getHexDump());
191         }
192     }
193 
194     /**
195      * @return The probably that a byte changes
196      */
197     public int getChangeByteProbability() {
198         return changeByteProbability;
199     }
200 
201     /**
202      * Set the probability for the change byte error.
203      * If this probability is &gt; 0 the filter will modify a random number of byte
204      * of the processed {@link IoBuffer}.
205      * @param changeByteProbability probability of modifying an IoBuffer out of 1000 processed {@link IoBuffer} 
206      */
207     public void setChangeByteProbability(int changeByteProbability) {
208         this.changeByteProbability = changeByteProbability;
209     }
210 
211     /**
212      * @return The probability for generating duplicated PDU
213      */
214     public int getDuplicatePduProbability() {
215         return duplicatePduProbability;
216     }
217 
218     /**
219      * not functional ATM
220      * @param duplicatePduProbability The probability for generating duplicated PDU
221      */
222     public void setDuplicatePduProbability(int duplicatePduProbability) {
223         this.duplicatePduProbability = duplicatePduProbability;
224     }
225 
226     /**
227      * @return the probability for the insert byte error.
228      */
229     public int getInsertByteProbability() {
230         return insertByteProbability;
231     }
232 
233     /**
234      * Set the probability for the insert byte error.
235      * If this probability is &gt; 0 the filter will insert a random number of byte
236      * in the processed {@link IoBuffer}.
237      * @param insertByteProbability probability of inserting in IoBuffer out of 1000 processed {@link IoBuffer} 
238      */
239     public void setInsertByteProbability(int insertByteProbability) {
240         this.insertByteProbability = insertByteProbability;
241     }
242 
243     /**
244      * @return The number of manipulated reads
245      */
246     public boolean isManipulateReads() {
247         return manipulateReads;
248     }
249 
250     /**
251      * Set to true if you want to apply error to the read {@link IoBuffer}
252      * 
253      * @param manipulateReads The number of manipulated reads
254      */
255     public void setManipulateReads(boolean manipulateReads) {
256         this.manipulateReads = manipulateReads;
257     }
258 
259     /**
260      * @return If manipulated writes are expected or not
261      */
262     public boolean isManipulateWrites() {
263         return manipulateWrites;
264     }
265 
266     /**
267      * Set to true if you want to apply error to the written {@link IoBuffer}
268      * 
269      * @param manipulateWrites If manipulated writes are expected or not
270      */
271     public void setManipulateWrites(boolean manipulateWrites) {
272         this.manipulateWrites = manipulateWrites;
273     }
274 
275     /**
276      * @return The probability for the remove byte error
277      */
278     public int getRemoveByteProbability() {
279         return removeByteProbability;
280     }
281 
282     /**
283      * Set the probability for the remove byte error.
284      * If this probability is &gt; 0 the filter will remove a random number of byte
285      * in the processed {@link IoBuffer}.
286      * 
287      * @param removeByteProbability probability of modifying an {@link IoBuffer} out of 1000 processed IoBuffer 
288      */
289     public void setRemoveByteProbability(int removeByteProbability) {
290         this.removeByteProbability = removeByteProbability;
291     }
292 
293     /**
294      * @return The PDU removal probability
295      */
296     public int getRemovePduProbability() {
297         return removePduProbability;
298     }
299 
300     /**
301      * not functional ATM
302      * @param removePduProbability The PDU removal probability
303      */
304     public void setRemovePduProbability(int removePduProbability) {
305         this.removePduProbability = removePduProbability;
306     }
307 
308     /**
309      * @return The delay before a resend
310      */
311     public int getResendPduLasterProbability() {
312         return resendPduLasterProbability;
313     }
314 
315     /**
316      * not functional ATM
317      * @param resendPduLasterProbability The delay before a resend
318      */
319     public void setResendPduLasterProbability(int resendPduLasterProbability) {
320         this.resendPduLasterProbability = resendPduLasterProbability;
321     }
322 
323     /**
324      * @return maximum bytes inserted in a {@link IoBuffer}
325      */
326     public int getMaxInsertByte() {
327         return maxInsertByte;
328     }
329 
330     /**
331      * Set the maximum number of byte the filter can insert in a {@link IoBuffer}.
332      * The default value is 10.
333      * @param maxInsertByte maximum bytes inserted in a {@link IoBuffer} 
334      */
335     public void setMaxInsertByte(int maxInsertByte) {
336         this.maxInsertByte = maxInsertByte;
337     }
338 }