/* * Copyright 2010 NCHOVY * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.pcap.live; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.krakenapps.pcap.PcapInputStream; import org.krakenapps.pcap.PcapOutputStream; import org.krakenapps.pcap.packet.PcapPacket; import org.krakenapps.pcap.util.Buffer; /** * PcapDevice is a JNI wrapper for libpcap. It can capture live traffic from * network interface, and inject arbitrary packets to network interface. * * @author delmitz * @since 1.1 */ public class PcapDevice implements PcapInputStream, PcapOutputStream { static { // kpcap.dll or libkpcap.so System.loadLibrary("kpcap"); } private boolean isOpen = true; private int handle; private PcapDeviceMetadata metadata; private Set<PcapDeviceEventListener> callbacks; PcapDevice(PcapDeviceMetadata metadata, int handle, String name, int snaplen, boolean promisc, int milliseconds) throws IOException { this.metadata = metadata; this.handle = handle; this.callbacks = Collections.synchronizedSet(new HashSet<PcapDeviceEventListener>()); open(handle, name, snaplen, promisc, milliseconds); } public int getHandle() { return handle; } public PcapDeviceMetadata getMetadata() { return metadata; } public void addListener(PcapDeviceEventListener callback) { callbacks.add(callback); } public void removeListener(PcapDeviceEventListener callback) { callbacks.remove(callback); } private native void open(int handle, String name, int snaplen, boolean promisc, int milliseconds) throws IOException; /** * Receives a packet from the device. * * @throws IOException * if the device is not opened, or timeout occurred in blocking * mode. */ @Override public PcapPacket getPacket() throws IOException { verify(); return getPacket(handle); } private native PcapPacket getPacket(int id) throws IOException; /** * Injects a packet to the device. You can even send malformed packet. * * @throws IOException * if the device is not opened. */ @Override public void write(PcapPacket packet) throws IOException { verify(); Buffer buffer = packet.getPacketData(); byte[] b = new byte[buffer.readableBytes()]; buffer.gets(b); write(handle, b, 0, b.length); } public void write(Buffer buffer) throws IOException { verify(); byte[] b = new byte[buffer.readableBytes()]; buffer.gets(b); write(handle, b, 0, b.length); } /** * Injects a raw packet * * @param b * the raw data * @throws IOException * if the device is not opened. */ public void write(byte[] b) throws IOException { verify(); write(handle, b, 0, b.length); } private native void write(int id, byte[] packet, int offset, int limit) throws IOException; /** * Changes blocking mode of the device. * * @param nonblock * true for non-blocking mode. * @throws IOException * if the device is not opened. */ public void setNonblock(boolean nonblock) throws IOException { verify(); setNonblock(handle, nonblock ? 1 : 0); } private native void setNonblock(int id, int nonblock) throws IOException; /** * Gets blocking mode of the device. * * @return true if the devices is in non-blocking mode. * @throws IOException * if the device is not opened. */ public boolean isNonblock() throws IOException { verify(); return isNonblock(handle); } private native boolean isNonblock(int id) throws IOException; public void setFilter(String filter) throws IOException { setFilter(filter, false); } /** * Sets berkeley packet filter or BPF. * * @see http://biot.com/capstats/bpf.html * @param filter * the bpf expression * @param optimize * true for optimization * @throws IOException * if the device is not opened. * @throws IllegalArgumentException * if error occurred in pcap_compile. */ public void setFilter(String filter, boolean optimize) throws IOException, IllegalArgumentException { verify(); setFilter(handle, (filter != null ? filter : ""), optimize ? 1 : 0, metadata.getNetworkPrefixLength()); } private native void setFilter(int id, String filter, int optimize, int netmask) throws IOException, IllegalArgumentException; /** * Gets pcap packet capture statistics from libpcap. * * @return the packet capture statistics * @throws IOException * if the device is not opened. */ public PcapStat getStat() throws IOException { verify(); return getStat(handle); } private native PcapStat getStat(int id) throws IOException; /** * s the packet capture device. */ @Override public void close() throws IOException { verify(); close(handle); isOpen = false; for (PcapDeviceEventListener callback : callbacks) { callback.onClosed(this); } } private native void close(int id) throws IOException; public static native String getPcapLibVersion(); @Override public void flush() throws IOException { // nothing, live injection does not buffering. } @Override public String toString() { return String.format("NetworkInterface [name=%s, description=%s, macAddress=%s]", metadata.getName(), metadata .getDescription(), metadata.getMacAddress()); } public boolean isOpen() { return isOpen; } private void verify() throws IOException { if (!isOpen) throw new IOException("Device is not opened"); } }