001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021package org.apache.mina.filter.errorgenerating; 022 023import java.util.Random; 024 025import org.apache.mina.core.buffer.IoBuffer; 026import org.apache.mina.core.filterchain.IoFilter; 027import org.apache.mina.core.filterchain.IoFilterAdapter; 028import org.apache.mina.core.session.IoSession; 029import org.apache.mina.core.write.DefaultWriteRequest; 030import org.apache.mina.core.write.WriteRequest; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034/** 035 * An {@link IoFilter} implementation generating random bytes and PDU modification in 036 * your communication streams. 037 * It's quite simple to use : 038 * <code>ErrorGeneratingFilter egf = new ErrorGeneratingFilter();</code> 039 * For activate the change of some bytes in your {@link IoBuffer}, for a probability of 200 out 040 * of 1000 {@link IoBuffer} processed : 041 * <code>egf.setChangeByteProbability(200);</code> 042 * For activate the insertion of some bytes in your {@link IoBuffer}, for a 043 * probability of 200 out of 1000 : 044 * <code>egf.setInsertByteProbability(200);</code> 045 * And for the removing of some bytes : 046 * <code>egf.setRemoveByteProbability(200);</code> 047 * You can activate the error generation for write or read with the 048 * following methods : 049 * <code>egf.setManipulateReads(true); 050 * egf.setManipulateWrites(true); </code> 051 * 052 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 053 * @org.apache.xbean.XBean 054 */ 055public class ErrorGeneratingFilter extends IoFilterAdapter { 056 private int removeByteProbability = 0; 057 058 private int insertByteProbability = 0; 059 060 private int changeByteProbability = 0; 061 062 private int removePduProbability = 0; 063 064 private int duplicatePduProbability = 0; 065 066 private int resendPduLasterProbability = 0; 067 068 private int maxInsertByte = 10; 069 070 private boolean manipulateWrites = false; 071 072 private boolean manipulateReads = false; 073 074 private Random rng = new Random(); 075 076 final private Logger logger = LoggerFactory.getLogger(ErrorGeneratingFilter.class); 077 078 @Override 079 public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception { 080 if (manipulateWrites) { 081 // manipulate bytes 082 if (writeRequest.getMessage() instanceof IoBuffer) { 083 manipulateIoBuffer(session, (IoBuffer) writeRequest.getMessage()); 084 IoBuffer buffer = insertBytesToNewIoBuffer(session, (IoBuffer) writeRequest.getMessage()); 085 if (buffer != null) { 086 writeRequest = new DefaultWriteRequest(buffer, writeRequest.getFuture(), 087 writeRequest.getDestination()); 088 } 089 // manipulate PDU 090 } else { 091 if (duplicatePduProbability > rng.nextInt()) { 092 nextFilter.filterWrite(session, writeRequest); 093 } 094 095 if (resendPduLasterProbability > rng.nextInt()) { 096 // store it somewhere and trigger a write execution for 097 // later 098 // TODO 099 } 100 if (removePduProbability > rng.nextInt()) { 101 return; 102 } 103 } 104 } 105 nextFilter.filterWrite(session, writeRequest); 106 } 107 108 @Override 109 public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception { 110 if (manipulateReads) { 111 if (message instanceof IoBuffer) { 112 // manipulate bytes 113 manipulateIoBuffer(session, (IoBuffer) message); 114 IoBuffer buffer = insertBytesToNewIoBuffer(session, (IoBuffer) message); 115 if (buffer != null) { 116 message = buffer; 117 } 118 } else { 119 // manipulate PDU 120 // TODO 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 public int getChangeByteProbability() { 195 return changeByteProbability; 196 } 197 198 /** 199 * Set the probability for the change byte error. 200 * If this probability is > 0 the filter will modify a random number of byte 201 * of the processed {@link IoBuffer}. 202 * @param changeByteProbability probability of modifying an IoBuffer out of 1000 processed {@link IoBuffer} 203 */ 204 public void setChangeByteProbability(int changeByteProbability) { 205 this.changeByteProbability = changeByteProbability; 206 } 207 208 public int getDuplicatePduProbability() { 209 return duplicatePduProbability; 210 } 211 212 /** 213 * not functional ATM 214 * @param duplicatePduProbability The probability for generating duplicated PDU 215 */ 216 public void setDuplicatePduProbability(int duplicatePduProbability) { 217 this.duplicatePduProbability = duplicatePduProbability; 218 } 219 220 public int getInsertByteProbability() { 221 return insertByteProbability; 222 } 223 224 /** 225 * Set the probability for the insert byte error. 226 * If this probability is > 0 the filter will insert a random number of byte 227 * in the processed {@link IoBuffer}. 228 * @param insertByteProbability probability of inserting in IoBuffer out of 1000 processed {@link IoBuffer} 229 */ 230 public void setInsertByteProbability(int insertByteProbability) { 231 this.insertByteProbability = insertByteProbability; 232 } 233 234 public boolean isManipulateReads() { 235 return manipulateReads; 236 } 237 238 /** 239 * Set to true if you want to apply error to the read {@link IoBuffer} 240 * 241 * @param manipulateReads The number of manipulated reads 242 */ 243 public void setManipulateReads(boolean manipulateReads) { 244 this.manipulateReads = manipulateReads; 245 } 246 247 public boolean isManipulateWrites() { 248 return manipulateWrites; 249 } 250 251 /** 252 * Set to true if you want to apply error to the written {@link IoBuffer} 253 * 254 * @param manipulateWrites The umber of manipulated writes 255 */ 256 public void setManipulateWrites(boolean manipulateWrites) { 257 this.manipulateWrites = manipulateWrites; 258 } 259 260 public int getRemoveByteProbability() { 261 return removeByteProbability; 262 } 263 264 /** 265 * Set the probability for the remove byte error. 266 * If this probability is > 0 the filter will remove a random number of byte 267 * in the processed {@link IoBuffer}. 268 * 269 * @param removeByteProbability probability of modifying an {@link IoBuffer} out of 1000 processed IoBuffer 270 */ 271 public void setRemoveByteProbability(int removeByteProbability) { 272 this.removeByteProbability = removeByteProbability; 273 } 274 275 public int getRemovePduProbability() { 276 return removePduProbability; 277 } 278 279 /** 280 * not functional ATM 281 * @param removePduProbability The PDU removal probability 282 */ 283 public void setRemovePduProbability(int removePduProbability) { 284 this.removePduProbability = removePduProbability; 285 } 286 287 public int getResendPduLasterProbability() { 288 return resendPduLasterProbability; 289 } 290 291 /** 292 * not functional ATM 293 * @param resendPduLasterProbability The delay before a resend 294 */ 295 public void setResendPduLasterProbability(int resendPduLasterProbability) { 296 this.resendPduLasterProbability = resendPduLasterProbability; 297 } 298 299 public int getMaxInsertByte() { 300 return maxInsertByte; 301 } 302 303 /** 304 * Set the maximum number of byte the filter can insert in a {@link IoBuffer}. 305 * The default value is 10. 306 * @param maxInsertByte maximum bytes inserted in a {@link IoBuffer} 307 */ 308 public void setMaxInsertByte(int maxInsertByte) { 309 this.maxInsertByte = maxInsertByte; 310 } 311}