View Javadoc
1   /*
2    * Copyright 2020 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.pcap;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.channel.ChannelDuplexHandler;
21  import io.netty.channel.ChannelHandlerContext;
22  import io.netty.channel.ChannelInboundHandlerAdapter;
23  import io.netty.channel.ChannelPromise;
24  import io.netty.channel.ServerChannel;
25  import io.netty.channel.socket.DatagramChannel;
26  import io.netty.channel.socket.DatagramPacket;
27  import io.netty.channel.socket.ServerSocketChannel;
28  import io.netty.channel.socket.SocketChannel;
29  import io.netty.util.NetUtil;
30  import io.netty.util.internal.ObjectUtil;
31  import io.netty.util.internal.logging.InternalLogger;
32  import io.netty.util.internal.logging.InternalLoggerFactory;
33  
34  import java.io.Closeable;
35  import java.io.IOException;
36  import java.io.OutputStream;
37  import java.net.Inet4Address;
38  import java.net.Inet6Address;
39  import java.net.InetSocketAddress;
40  
41  /**
42   * <p> {@link PcapWriteHandler} captures {@link ByteBuf} from {@link SocketChannel} / {@link ServerChannel}
43   * or {@link DatagramPacket} and writes it into Pcap {@link OutputStream}. </p>
44   *
45   * <p>
46   * Things to keep in mind when using {@link PcapWriteHandler} with TCP:
47   *
48   *    <ul>
49   *        <li> Whenever {@link ChannelInboundHandlerAdapter#channelActive(ChannelHandlerContext)} is called,
50   *        a fake TCP 3-way handshake (SYN, SYN+ACK, ACK) is simulated as new connection in Pcap. </li>
51   *
52   *        <li> Whenever {@link ChannelInboundHandlerAdapter#handlerRemoved(ChannelHandlerContext)} is called,
53   *        a fake TCP 3-way handshake (FIN+ACK, FIN+ACK, ACK) is simulated as connection shutdown in Pcap.  </li>
54   *
55   *        <li> Whenever {@link ChannelInboundHandlerAdapter#exceptionCaught(ChannelHandlerContext, Throwable)}
56   *        is called, a fake TCP RST is sent to simulate connection Reset in Pcap. </li>
57   *
58   *        <li> ACK is sent each time data is send / received. </li>
59   *
60   *        <li> Zero Length Data Packets can cause TCP Double ACK error in Wireshark. To tackle this,
61   *        set {@code captureZeroByte} to {@code false}. </li>
62   *    </ul>
63   * </p>
64   */
65  public final class PcapWriteHandler extends ChannelDuplexHandler implements Closeable {
66  
67      /**
68       * Logger for logging events
69       */
70      private final InternalLogger logger = InternalLoggerFactory.getInstance(PcapWriteHandler.class);
71  
72      /**
73       * {@link PcapWriter} Instance
74       */
75      private PcapWriter pCapWriter;
76  
77      /**
78       * {@link OutputStream} where we'll write Pcap data.
79       */
80      private final OutputStream outputStream;
81  
82      /**
83       * {@code true} if we want to capture packets with zero bytes else {@code false}.
84       */
85      private final boolean captureZeroByte;
86  
87      /**
88       * {@code true} if we want to write Pcap Global Header on initialization of
89       * {@link PcapWriter} else {@code false}.
90       */
91      private final boolean writePcapGlobalHeader;
92  
93      /**
94       * TCP Sender Segment Number.
95       * It'll start with 1 and keep incrementing with number of bytes read/sent.
96       */
97      private int sendSegmentNumber = 1;
98  
99      /**
100      * TCP Receiver Segment Number.
101      * It'll start with 1 and keep incrementing with number of bytes read/sent.
102      */
103     private int receiveSegmentNumber = 1;
104 
105     /**
106      * Address of the initiator of the connection
107      */
108     private InetSocketAddress initiatiorAddr;
109 
110     /**
111      * Address of the receiver of the connection
112      */
113     private InetSocketAddress handlerAddr;
114 
115     private boolean isServerPipeline;
116 
117     /**
118      * Set to {@code true} if {@link #close()} is called and we should stop writing Pcap.
119      */
120     private boolean isClosed;
121 
122     /**
123      * Create new {@link PcapWriteHandler} Instance.
124      * {@code captureZeroByte} is set to {@code false} and
125      * {@code writePcapGlobalHeader} is set to {@code true}.
126      *
127      * @param outputStream OutputStream where Pcap data will be written. Call {@link #close()} to close this
128      *                     OutputStream.
129      * @throws NullPointerException If {@link OutputStream} is {@code null} then we'll throw an
130      *                              {@link NullPointerException}
131      */
132     public PcapWriteHandler(OutputStream outputStream) {
133         this(outputStream, false, true);
134     }
135 
136     /**
137      * Create new {@link PcapWriteHandler} Instance
138      *
139      * @param outputStream          OutputStream where Pcap data will be written. Call {@link #close()} to close this
140      *                              OutputStream.
141      * @param captureZeroByte       Set to {@code true} to enable capturing packets with empty (0 bytes) payload.
142      *                              Otherwise, if set to {@code false}, empty packets will be filtered out.
143      * @param writePcapGlobalHeader Set to {@code true} to write Pcap Global Header on initialization.
144      *                              Otherwise, if set to {@code false}, Pcap Global Header will not be written
145      *                              on initialization. This could when writing Pcap data on a existing file where
146      *                              Pcap Global Header is already present.
147      * @throws NullPointerException If {@link OutputStream} is {@code null} then we'll throw an
148      *                              {@link NullPointerException}
149      */
150     public PcapWriteHandler(OutputStream outputStream, boolean captureZeroByte, boolean writePcapGlobalHeader) {
151         this.outputStream = ObjectUtil.checkNotNull(outputStream, "OutputStream");
152         this.captureZeroByte = captureZeroByte;
153         this.writePcapGlobalHeader = writePcapGlobalHeader;
154     }
155 
156     @Override
157     public void channelActive(ChannelHandlerContext ctx) throws Exception {
158 
159         ByteBufAllocator byteBufAllocator = ctx.alloc();
160 
161         /*
162          * If `writePcapGlobalHeader` is `true`, we'll write Pcap Global Header.
163          */
164         if (writePcapGlobalHeader) {
165 
166             ByteBuf byteBuf = byteBufAllocator.buffer();
167             try {
168                 this.pCapWriter = new PcapWriter(this.outputStream, byteBuf);
169             } catch (IOException ex) {
170                 ctx.channel().close();
171                 ctx.fireExceptionCaught(ex);
172                 logger.error("Caught Exception While Initializing PcapWriter, Closing Channel.", ex);
173             } finally {
174                 byteBuf.release();
175             }
176         } else {
177             this.pCapWriter = new PcapWriter(this.outputStream);
178         }
179 
180         // If Channel belongs to `SocketChannel` then we're handling TCP.
181         if (ctx.channel() instanceof SocketChannel) {
182 
183             // Capture correct `localAddress` and `remoteAddress`
184             if (ctx.channel().parent() instanceof ServerSocketChannel) {
185                 isServerPipeline = true;
186                 initiatiorAddr = (InetSocketAddress) ctx.channel().remoteAddress();
187                 handlerAddr = (InetSocketAddress) ctx.channel().localAddress();
188             } else {
189                 isServerPipeline = false;
190                 initiatiorAddr = (InetSocketAddress) ctx.channel().localAddress();
191                 handlerAddr = (InetSocketAddress) ctx.channel().remoteAddress();
192             }
193 
194             logger.debug("Initiating Fake TCP 3-Way Handshake");
195 
196             ByteBuf tcpBuf = byteBufAllocator.buffer();
197 
198             try {
199                 // Write SYN with Normal Source and Destination Address
200                 TCPPacket.writePacket(tcpBuf, null, 0, 0,
201                         initiatiorAddr.getPort(), handlerAddr.getPort(), TCPPacket.TCPFlag.SYN);
202                 completeTCPWrite(initiatiorAddr, handlerAddr, tcpBuf, byteBufAllocator, ctx);
203 
204                 // Write SYN+ACK with Reversed Source and Destination Address
205                 TCPPacket.writePacket(tcpBuf, null, 0, 1,
206                         handlerAddr.getPort(), initiatiorAddr.getPort(), TCPPacket.TCPFlag.SYN, TCPPacket.TCPFlag.ACK);
207                 completeTCPWrite(handlerAddr, initiatiorAddr, tcpBuf, byteBufAllocator, ctx);
208 
209                 // Write ACK with Normal Source and Destination Address
210                 TCPPacket.writePacket(tcpBuf, null, 1, 1, initiatiorAddr.getPort(),
211                         handlerAddr.getPort(), TCPPacket.TCPFlag.ACK);
212                 completeTCPWrite(initiatiorAddr, handlerAddr, tcpBuf, byteBufAllocator, ctx);
213             } finally {
214                 tcpBuf.release();
215             }
216 
217             logger.debug("Finished Fake TCP 3-Way Handshake");
218         } else if (ctx.channel() instanceof DatagramChannel) {
219             DatagramChannel datagramChannel = (DatagramChannel) ctx.channel();
220 
221             // If `DatagramChannel` is connected then we can get
222             // `localAddress` and `remoteAddress` from Channel.
223             if (datagramChannel.isConnected()) {
224                 initiatiorAddr = (InetSocketAddress) ctx.channel().localAddress();
225                 handlerAddr = (InetSocketAddress) ctx.channel().remoteAddress();
226             }
227         }
228 
229         super.channelActive(ctx);
230     }
231 
232     @Override
233     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
234         if (!isClosed) {
235             if (ctx.channel() instanceof SocketChannel) {
236                 handleTCP(ctx, msg, false);
237             } else if (ctx.channel() instanceof DatagramChannel) {
238                 handleUDP(ctx, msg);
239             } else {
240                 logger.debug("Discarding Pcap Write for Unknown Channel Type: {}", ctx.channel());
241             }
242         }
243         super.channelRead(ctx, msg);
244     }
245 
246     @Override
247     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
248         if (!isClosed) {
249             if (ctx.channel() instanceof SocketChannel) {
250                 handleTCP(ctx, msg, true);
251             } else if (ctx.channel() instanceof DatagramChannel) {
252                 handleUDP(ctx, msg);
253             } else {
254                 logger.debug("Discarding Pcap Write for Unknown Channel Type: {}", ctx.channel());
255             }
256         }
257         super.write(ctx, msg, promise);
258     }
259 
260     /**
261      * Handle TCP L4
262      *
263      * @param ctx              {@link ChannelHandlerContext} for {@link ByteBuf} allocation and
264      *                         {@code fireExceptionCaught}
265      * @param msg              {@link Object} must be {@link ByteBuf} else it'll be discarded
266      * @param isWriteOperation Set {@code true} if we have to process packet when packets are being sent out
267      *                         else set {@code false}
268      */
269     private void handleTCP(ChannelHandlerContext ctx, Object msg, boolean isWriteOperation) {
270         if (msg instanceof ByteBuf) {
271 
272             // If bytes are 0 and `captureZeroByte` is false, we won't capture this.
273             if (((ByteBuf) msg).readableBytes() == 0 && !captureZeroByte) {
274                 logger.debug("Discarding Zero Byte TCP Packet. isWriteOperation {}", isWriteOperation);
275                 return;
276             }
277 
278             ByteBufAllocator byteBufAllocator = ctx.alloc();
279             ByteBuf packet = ((ByteBuf) msg).duplicate();
280             ByteBuf tcpBuf = byteBufAllocator.buffer();
281             int bytes = packet.readableBytes();
282 
283             try {
284                 if (isWriteOperation) {
285                     final InetSocketAddress srcAddr;
286                     final InetSocketAddress dstAddr;
287                     if (isServerPipeline) {
288                         srcAddr = handlerAddr;
289                         dstAddr = initiatiorAddr;
290                     } else {
291                         srcAddr = initiatiorAddr;
292                         dstAddr = handlerAddr;
293                     }
294 
295                     TCPPacket.writePacket(tcpBuf, packet, sendSegmentNumber, receiveSegmentNumber, srcAddr.getPort(),
296                             dstAddr.getPort(), TCPPacket.TCPFlag.ACK);
297                     completeTCPWrite(srcAddr, dstAddr, tcpBuf, byteBufAllocator, ctx);
298                     logTCP(true, bytes, sendSegmentNumber, receiveSegmentNumber, srcAddr, dstAddr, false);
299 
300                     sendSegmentNumber += bytes;
301 
302                     TCPPacket.writePacket(tcpBuf, null, receiveSegmentNumber, sendSegmentNumber, dstAddr.getPort(),
303                             srcAddr.getPort(), TCPPacket.TCPFlag.ACK);
304                     completeTCPWrite(dstAddr, srcAddr, tcpBuf, byteBufAllocator, ctx);
305                     logTCP(true, bytes, sendSegmentNumber, receiveSegmentNumber, dstAddr, srcAddr, true);
306                 } else {
307                     final InetSocketAddress srcAddr;
308                     final InetSocketAddress dstAddr;
309                     if (isServerPipeline) {
310                         srcAddr = initiatiorAddr;
311                         dstAddr = handlerAddr;
312                     } else {
313                         srcAddr = handlerAddr;
314                         dstAddr = initiatiorAddr;
315                     }
316 
317                     TCPPacket.writePacket(tcpBuf, packet, receiveSegmentNumber, sendSegmentNumber, srcAddr.getPort(),
318                             dstAddr.getPort(), TCPPacket.TCPFlag.ACK);
319                     completeTCPWrite(srcAddr, dstAddr, tcpBuf, byteBufAllocator, ctx);
320                     logTCP(false, bytes, receiveSegmentNumber, sendSegmentNumber, srcAddr, dstAddr, false);
321 
322                     receiveSegmentNumber += bytes;
323 
324                     TCPPacket.writePacket(tcpBuf, null, sendSegmentNumber, receiveSegmentNumber, dstAddr.getPort(),
325                             srcAddr.getPort(), TCPPacket.TCPFlag.ACK);
326                     completeTCPWrite(dstAddr, srcAddr, tcpBuf, byteBufAllocator, ctx);
327                     logTCP(false, bytes, sendSegmentNumber, receiveSegmentNumber, dstAddr, srcAddr, true);
328                 }
329             } finally {
330                 tcpBuf.release();
331             }
332         } else {
333             logger.debug("Discarding Pcap Write for TCP Object: {}", msg);
334         }
335     }
336 
337     /**
338      * Write TCP/IP L3 and L2 here.
339      *
340      * @param srcAddr          {@link InetSocketAddress} Source Address of this Packet
341      * @param dstAddr          {@link InetSocketAddress} Destination Address of this Packet
342      * @param tcpBuf           {@link ByteBuf} containing TCP L4 Data
343      * @param byteBufAllocator {@link ByteBufAllocator} for allocating bytes for TCP/IP L3 and L2 data.
344      * @param ctx              {@link ChannelHandlerContext} for {@code fireExceptionCaught}
345      */
346     private void completeTCPWrite(InetSocketAddress srcAddr, InetSocketAddress dstAddr, ByteBuf tcpBuf,
347                                   ByteBufAllocator byteBufAllocator, ChannelHandlerContext ctx) {
348 
349         ByteBuf ipBuf = byteBufAllocator.buffer();
350         ByteBuf ethernetBuf = byteBufAllocator.buffer();
351         ByteBuf pcap = byteBufAllocator.buffer();
352 
353         try {
354             if (srcAddr.getAddress() instanceof Inet4Address && dstAddr.getAddress() instanceof Inet4Address) {
355                 IPPacket.writeTCPv4(ipBuf, tcpBuf,
356                         NetUtil.ipv4AddressToInt((Inet4Address) srcAddr.getAddress()),
357                         NetUtil.ipv4AddressToInt((Inet4Address) dstAddr.getAddress()));
358 
359                 EthernetPacket.writeIPv4(ethernetBuf, ipBuf);
360             } else if (srcAddr.getAddress() instanceof Inet6Address && dstAddr.getAddress() instanceof Inet6Address) {
361                 IPPacket.writeTCPv6(ipBuf, tcpBuf,
362                         srcAddr.getAddress().getAddress(),
363                         dstAddr.getAddress().getAddress());
364 
365                 EthernetPacket.writeIPv6(ethernetBuf, ipBuf);
366             } else {
367                 logger.error("Source and Destination IP Address versions are not same. Source Address: {}, " +
368                         "Destination Address: {}", srcAddr.getAddress(), dstAddr.getAddress());
369                 return;
370             }
371 
372             // Write Packet into Pcap
373             pCapWriter.writePacket(pcap, ethernetBuf);
374         } catch (IOException ex) {
375             logger.error("Caught Exception While Writing Packet into Pcap", ex);
376             ctx.fireExceptionCaught(ex);
377         } finally {
378             ipBuf.release();
379             ethernetBuf.release();
380             pcap.release();
381         }
382     }
383 
384     /**
385      * Logger for TCP
386      */
387     private void logTCP(boolean isWriteOperation, int bytes, int sendSegmentNumber, int receiveSegmentNumber,
388                         InetSocketAddress srcAddr, InetSocketAddress dstAddr, boolean ackOnly) {
389         // If `ackOnly` is `true` when we don't need to write any data so we'll not
390         // log number of bytes being written and mark the operation as "TCP ACK".
391         if (logger.isDebugEnabled()) {
392             if (ackOnly) {
393                 logger.debug("Writing TCP ACK, isWriteOperation {}, Segment Number {}, Ack Number {}, Src Addr {}, "
394                         + "Dst Addr {}", isWriteOperation, sendSegmentNumber, receiveSegmentNumber, dstAddr, srcAddr);
395             } else {
396                 logger.debug("Writing TCP Data of {} Bytes, isWriteOperation {}, Segment Number {}, Ack Number {}, " +
397                                 "Src Addr {}, Dst Addr {}", bytes, isWriteOperation, sendSegmentNumber,
398                         receiveSegmentNumber, srcAddr, dstAddr);
399             }
400         }
401     }
402 
403     /**
404      * Handle UDP l4
405      *
406      * @param ctx {@link ChannelHandlerContext} for {@code localAddress} / {@code remoteAddress},
407      *            {@link ByteBuf} allocation and {@code fireExceptionCaught}
408      * @param msg {@link DatagramPacket} or {@link DatagramChannel}
409      */
410     private void handleUDP(ChannelHandlerContext ctx, Object msg) {
411         ByteBuf udpBuf = ctx.alloc().buffer();
412 
413         try {
414             if (msg instanceof DatagramPacket) {
415 
416                 // If bytes are 0 and `captureZeroByte` is false, we won't capture this.
417                 if (((DatagramPacket) msg).content().readableBytes() == 0 && !captureZeroByte) {
418                     logger.debug("Discarding Zero Byte UDP Packet");
419                     return;
420                 }
421 
422                 DatagramPacket datagramPacket = ((DatagramPacket) msg).duplicate();
423                 InetSocketAddress srcAddr = datagramPacket.sender();
424                 InetSocketAddress dstAddr = datagramPacket.recipient();
425 
426                 // If `datagramPacket.sender()` is `null` then DatagramPacket is initialized
427                 // `sender` (local) address. In this case, we'll get source address from Channel.
428                 if (srcAddr == null) {
429                     srcAddr = (InetSocketAddress) ctx.channel().localAddress();
430                 }
431 
432                 logger.debug("Writing UDP Data of {} Bytes, Src Addr {}, Dst Addr {}",
433                         datagramPacket.content().readableBytes(), srcAddr, dstAddr);
434 
435                 UDPPacket.writePacket(udpBuf, datagramPacket.content(), srcAddr.getPort(), dstAddr.getPort());
436                 completeUDPWrite(srcAddr, dstAddr, udpBuf, ctx.alloc(), ctx);
437             } else if (msg instanceof ByteBuf && ((DatagramChannel) ctx.channel()).isConnected()) {
438 
439                 // If bytes are 0 and `captureZeroByte` is false, we won't capture this.
440                 if (((ByteBuf) msg).readableBytes() == 0 && !captureZeroByte) {
441                     logger.debug("Discarding Zero Byte UDP Packet");
442                     return;
443                 }
444 
445                 ByteBuf byteBuf = ((ByteBuf) msg).duplicate();
446 
447                 logger.debug("Writing UDP Data of {} Bytes, Src Addr {}, Dst Addr {}",
448                         byteBuf.readableBytes(), initiatiorAddr, handlerAddr);
449 
450                 UDPPacket.writePacket(udpBuf, byteBuf, initiatiorAddr.getPort(), handlerAddr.getPort());
451                 completeUDPWrite(initiatiorAddr, handlerAddr, udpBuf, ctx.alloc(), ctx);
452             } else {
453                 logger.debug("Discarding Pcap Write for UDP Object: {}", msg);
454             }
455         } finally {
456             udpBuf.release();
457         }
458     }
459 
460     /**
461      * Write UDP/IP L3 and L2 here.
462      *
463      * @param srcAddr          {@link InetSocketAddress} Source Address of this Packet
464      * @param dstAddr          {@link InetSocketAddress} Destination Address of this Packet
465      * @param udpBuf           {@link ByteBuf} containing UDP L4 Data
466      * @param byteBufAllocator {@link ByteBufAllocator} for allocating bytes for UDP/IP L3 and L2 data.
467      * @param ctx              {@link ChannelHandlerContext} for {@code fireExceptionCaught}
468      */
469     private void completeUDPWrite(InetSocketAddress srcAddr, InetSocketAddress dstAddr, ByteBuf udpBuf,
470                                   ByteBufAllocator byteBufAllocator, ChannelHandlerContext ctx) {
471 
472         ByteBuf ipBuf = byteBufAllocator.buffer();
473         ByteBuf ethernetBuf = byteBufAllocator.buffer();
474         ByteBuf pcap = byteBufAllocator.buffer();
475 
476         try {
477             if (srcAddr.getAddress() instanceof Inet4Address && dstAddr.getAddress() instanceof Inet4Address) {
478                 IPPacket.writeUDPv4(ipBuf, udpBuf,
479                         NetUtil.ipv4AddressToInt((Inet4Address) srcAddr.getAddress()),
480                         NetUtil.ipv4AddressToInt((Inet4Address) dstAddr.getAddress()));
481 
482                 EthernetPacket.writeIPv4(ethernetBuf, ipBuf);
483             } else if (srcAddr.getAddress() instanceof Inet6Address && dstAddr.getAddress() instanceof Inet6Address) {
484                 IPPacket.writeUDPv6(ipBuf, udpBuf,
485                         srcAddr.getAddress().getAddress(),
486                         dstAddr.getAddress().getAddress());
487 
488                 EthernetPacket.writeIPv6(ethernetBuf, ipBuf);
489             } else {
490                 logger.error("Source and Destination IP Address versions are not same. Source Address: {}, " +
491                         "Destination Address: {}", srcAddr.getAddress(), dstAddr.getAddress());
492                 return;
493             }
494 
495             // Write Packet into Pcap
496             pCapWriter.writePacket(pcap, ethernetBuf);
497         } catch (IOException ex) {
498             logger.error("Caught Exception While Writing Packet into Pcap", ex);
499             ctx.fireExceptionCaught(ex);
500         } finally {
501             ipBuf.release();
502             ethernetBuf.release();
503             pcap.release();
504         }
505     }
506 
507     @Override
508     public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
509 
510         // If `isTCP` is true, then we'll simulate a `FIN` flow.
511         if (ctx.channel() instanceof SocketChannel) {
512             logger.debug("Starting Fake TCP FIN+ACK Flow to close connection");
513 
514             ByteBufAllocator byteBufAllocator = ctx.alloc();
515             ByteBuf tcpBuf = byteBufAllocator.buffer();
516 
517             try {
518                 // Write FIN+ACK with Normal Source and Destination Address
519                 TCPPacket.writePacket(tcpBuf, null, sendSegmentNumber, receiveSegmentNumber, initiatiorAddr.getPort(),
520                         handlerAddr.getPort(), TCPPacket.TCPFlag.FIN, TCPPacket.TCPFlag.ACK);
521                 completeTCPWrite(initiatiorAddr, handlerAddr, tcpBuf, byteBufAllocator, ctx);
522 
523                 // Write FIN+ACK with Reversed Source and Destination Address
524                 TCPPacket.writePacket(tcpBuf, null, receiveSegmentNumber, sendSegmentNumber, handlerAddr.getPort(),
525                         initiatiorAddr.getPort(), TCPPacket.TCPFlag.FIN, TCPPacket.TCPFlag.ACK);
526                 completeTCPWrite(handlerAddr, initiatiorAddr, tcpBuf, byteBufAllocator, ctx);
527 
528                 // Write ACK with Normal Source and Destination Address
529                 TCPPacket.writePacket(tcpBuf, null, sendSegmentNumber + 1, receiveSegmentNumber + 1,
530                         initiatiorAddr.getPort(), handlerAddr.getPort(), TCPPacket.TCPFlag.ACK);
531                 completeTCPWrite(initiatiorAddr, handlerAddr, tcpBuf, byteBufAllocator, ctx);
532             } finally {
533                 tcpBuf.release();
534             }
535 
536             logger.debug("Finished Fake TCP FIN+ACK Flow to close connection");
537         }
538 
539         close();
540         super.handlerRemoved(ctx);
541     }
542 
543     @Override
544     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
545 
546         if (ctx.channel() instanceof SocketChannel) {
547             ByteBuf tcpBuf = ctx.alloc().buffer();
548 
549             try {
550                 // Write RST with Normal Source and Destination Address
551                 TCPPacket.writePacket(tcpBuf, null, sendSegmentNumber, receiveSegmentNumber, initiatiorAddr.getPort(),
552                         handlerAddr.getPort(), TCPPacket.TCPFlag.RST, TCPPacket.TCPFlag.ACK);
553                 completeTCPWrite(initiatiorAddr, handlerAddr, tcpBuf, ctx.alloc(), ctx);
554             } finally {
555                 tcpBuf.release();
556             }
557 
558             logger.debug("Sent Fake TCP RST to close connection");
559         }
560 
561         close();
562         ctx.fireExceptionCaught(cause);
563     }
564 
565     /**
566      * <p> Close {@code PcapWriter} and {@link OutputStream}. </p>
567      * <p> Note: Calling this method does not close {@link PcapWriteHandler}.
568      * Only Pcap Writes are closed. </p>
569      *
570      * @throws IOException If {@link OutputStream#close()} throws an exception
571      */
572     @Override
573     public void close() throws IOException {
574         if (isClosed) {
575             logger.debug("PcapWriterHandler is already closed");
576         } else {
577             isClosed = true;
578             pCapWriter.close();
579             logger.debug("PcapWriterHandler is now closed");
580         }
581     }
582 }