diff --git a/src/main/java/ru/serega6531/packmate/ApplicationConfiguration.java b/src/main/java/ru/serega6531/packmate/ApplicationConfiguration.java index ee7e15f..c049d50 100644 --- a/src/main/java/ru/serega6531/packmate/ApplicationConfiguration.java +++ b/src/main/java/ru/serega6531/packmate/ApplicationConfiguration.java @@ -1,20 +1,33 @@ package ru.serega6531.packmate; +import org.pcap4j.core.PcapNativeException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; +import ru.serega6531.packmate.model.enums.CaptureMode; +import ru.serega6531.packmate.pcap.FilePcapWorker; +import ru.serega6531.packmate.pcap.LivePcapWorker; +import ru.serega6531.packmate.pcap.PcapWorker; +import ru.serega6531.packmate.service.ServicesService; +import ru.serega6531.packmate.service.StreamService; + +import java.net.UnknownHostException; @Configuration @EnableWebSecurity +@EnableScheduling +@EnableWebSocket public class ApplicationConfiguration extends WebSecurityConfigurerAdapter implements WebSocketConfigurer { @Value("${account-login}") @@ -30,6 +43,21 @@ public class ApplicationConfiguration extends WebSecurityConfigurerAdapter imple this.webSocketHandler = webSocketHandler; } + @Bean(destroyMethod = "stop") + @Autowired + public PcapWorker pcapWorker(ServicesService servicesService, + StreamService streamService, + @Value("${local-ip}") String localIpString, + @Value("${interface-name}") String interfaceName, + @Value("${pcap-file}") String filename, + @Value("${capture-mode}") CaptureMode captureMode) throws PcapNativeException, UnknownHostException { + if(captureMode == CaptureMode.LIVE) { + return new LivePcapWorker(servicesService, streamService, localIpString, interfaceName); + } else { + return new FilePcapWorker(servicesService, streamService, localIpString, filename); + } + } + @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() diff --git a/src/main/java/ru/serega6531/packmate/PackmateApplication.java b/src/main/java/ru/serega6531/packmate/PackmateApplication.java index 4054f15..2cf49fb 100644 --- a/src/main/java/ru/serega6531/packmate/PackmateApplication.java +++ b/src/main/java/ru/serega6531/packmate/PackmateApplication.java @@ -6,12 +6,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.web.socket.config.annotation.EnableWebSocket; +import ru.serega6531.packmate.pcap.PcapWorker; @SpringBootApplication -@EnableScheduling -@EnableWebSocket public class PackmateApplication { @Value("${enable-capture}") diff --git a/src/main/java/ru/serega6531/packmate/TimeoutStreamsSaver.java b/src/main/java/ru/serega6531/packmate/TimeoutStreamsSaver.java index 68acaab..b4bd9bf 100644 --- a/src/main/java/ru/serega6531/packmate/TimeoutStreamsSaver.java +++ b/src/main/java/ru/serega6531/packmate/TimeoutStreamsSaver.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import ru.serega6531.packmate.model.enums.Protocol; +import ru.serega6531.packmate.pcap.PcapWorker; import java.util.concurrent.TimeUnit; diff --git a/src/main/java/ru/serega6531/packmate/model/enums/CaptureMode.java b/src/main/java/ru/serega6531/packmate/model/enums/CaptureMode.java new file mode 100644 index 0000000..3a9e751 --- /dev/null +++ b/src/main/java/ru/serega6531/packmate/model/enums/CaptureMode.java @@ -0,0 +1,7 @@ +package ru.serega6531.packmate.model.enums; + +public enum CaptureMode { + + LIVE, FILE + +} diff --git a/src/main/java/ru/serega6531/packmate/PcapWorker.java b/src/main/java/ru/serega6531/packmate/pcap/AbstractPcapWorker.java similarity index 83% rename from src/main/java/ru/serega6531/packmate/PcapWorker.java rename to src/main/java/ru/serega6531/packmate/pcap/AbstractPcapWorker.java index e19744d..be37015 100644 --- a/src/main/java/ru/serega6531/packmate/PcapWorker.java +++ b/src/main/java/ru/serega6531/packmate/pcap/AbstractPcapWorker.java @@ -1,25 +1,22 @@ -package ru.serega6531.packmate; +package ru.serega6531.packmate.pcap; import com.google.common.collect.*; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.tuple.ImmutablePair; -import org.pcap4j.core.*; +import org.pcap4j.core.PacketListener; +import org.pcap4j.core.PcapHandle; import org.pcap4j.packet.IpV4Packet; import org.pcap4j.packet.Packet; import org.pcap4j.packet.TcpPacket; import org.pcap4j.packet.UdpPacket; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; import ru.serega6531.packmate.model.CtfService; import ru.serega6531.packmate.model.enums.Protocol; import ru.serega6531.packmate.model.pojo.UnfinishedStream; import ru.serega6531.packmate.service.ServicesService; import ru.serega6531.packmate.service.StreamService; -import javax.annotation.PreDestroy; import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; @@ -29,16 +26,14 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; -@Component @Slf4j -public class PcapWorker implements PacketListener { +public abstract class AbstractPcapWorker implements PcapWorker, PacketListener { private final ServicesService servicesService; private final StreamService streamService; - private final PcapNetworkInterface device; - private PcapHandle pcap = null; - private final ExecutorService listenerExecutorService; + protected PcapHandle pcap = null; + protected final ExecutorService listenerExecutorService; private final InetAddress localIp; @@ -51,11 +46,9 @@ public class PcapWorker implements PacketListener { private final SetMultimap> fins = HashMultimap.create(); private final SetMultimap> acks = HashMultimap.create(); - @Autowired - public PcapWorker(ServicesService servicesService, - StreamService streamService, - @Value("${interface-name}") String interfaceName, - @Value("${local-ip}") String localIpString) throws PcapNativeException, UnknownHostException { + public AbstractPcapWorker(ServicesService servicesService, + StreamService streamService, + String localIpString) throws UnknownHostException { this.servicesService = servicesService; this.streamService = streamService; @@ -67,37 +60,6 @@ public class PcapWorker implements PacketListener { BasicThreadFactory factory = new BasicThreadFactory.Builder() .namingPattern("pcap-worker-listener").build(); listenerExecutorService = Executors.newSingleThreadExecutor(factory); - device = Pcaps.getDevByName(interfaceName); - } - - void start() throws PcapNativeException { - log.info("Using interface " + device.getName()); - pcap = device.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 100); - - BasicThreadFactory factory = new BasicThreadFactory.Builder() - .namingPattern("pcap-worker-loop").build(); - ExecutorService loopExecutorService = Executors.newSingleThreadExecutor(factory); - try { - log.info("Intercept started"); - pcap.loop(-1, this, loopExecutorService); - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - // выходим - } catch (Exception e) { - log.error("Error while capturing packet", e); - stop(); - } - } - - @PreDestroy - @SneakyThrows - private void stop() { - if (pcap != null && pcap.isOpen()) { - pcap.breakLoop(); - pcap.close(); - } - - log.info("Intercept stopped"); } public void gotPacket(Packet rawPacket) { @@ -241,7 +203,7 @@ public class PcapWorker implements PacketListener { } @SneakyThrows - int closeTimeoutStreams(Protocol protocol, long timeoutMillis) { + public int closeTimeoutStreams(Protocol protocol, long timeoutMillis) { return listenerExecutorService.submit(() -> { int streamsClosed = 0; diff --git a/src/main/java/ru/serega6531/packmate/pcap/FilePcapWorker.java b/src/main/java/ru/serega6531/packmate/pcap/FilePcapWorker.java new file mode 100644 index 0000000..7a8f1ea --- /dev/null +++ b/src/main/java/ru/serega6531/packmate/pcap/FilePcapWorker.java @@ -0,0 +1,59 @@ +package ru.serega6531.packmate.pcap; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.pcap4j.core.PcapNativeException; +import org.pcap4j.core.Pcaps; +import org.pcap4j.packet.Packet; +import ru.serega6531.packmate.service.ServicesService; +import ru.serega6531.packmate.service.StreamService; + +import java.io.EOFException; +import java.io.File; +import java.net.UnknownHostException; + +@Slf4j +public class FilePcapWorker extends AbstractPcapWorker { + + private final File file; + + public FilePcapWorker(ServicesService servicesService, + StreamService streamService, + String localIpString, + String filename) throws UnknownHostException { + super(servicesService, streamService, localIpString); + + file = new File(filename); + if(!file.exists()) { + throw new IllegalArgumentException("File " + file.getAbsolutePath() + " does not exist"); + } + } + + @SneakyThrows + @Override + public void start() { + pcap = Pcaps.openOffline(file.getAbsolutePath()); + + while (pcap.isOpen()) { + try { + final Packet packet = pcap.getNextPacketEx(); + gotPacket(packet); + } catch (PcapNativeException e) { + log.error("Pcap read", e); + Thread.sleep(100); + } catch (EOFException e) { + stop(); + break; + } + } + } + + @SneakyThrows + public void stop() { + if (pcap != null && pcap.isOpen()) { + pcap.close(); + } + + log.info("Intercept stopped"); + } +} diff --git a/src/main/java/ru/serega6531/packmate/pcap/LivePcapWorker.java b/src/main/java/ru/serega6531/packmate/pcap/LivePcapWorker.java new file mode 100644 index 0000000..0c69a6d --- /dev/null +++ b/src/main/java/ru/serega6531/packmate/pcap/LivePcapWorker.java @@ -0,0 +1,62 @@ +package ru.serega6531.packmate.pcap; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.pcap4j.core.PcapNativeException; +import org.pcap4j.core.PcapNetworkInterface; +import org.pcap4j.core.Pcaps; +import ru.serega6531.packmate.service.ServicesService; +import ru.serega6531.packmate.service.StreamService; + +import java.net.UnknownHostException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Slf4j +public class LivePcapWorker extends AbstractPcapWorker { + + private final PcapNetworkInterface device; + + public LivePcapWorker(ServicesService servicesService, + StreamService streamService, + String localIpString, + String interfaceName) throws PcapNativeException, UnknownHostException { + super(servicesService, streamService, localIpString); + device = Pcaps.getDevByName(interfaceName); + + if(device == null) { + throw new IllegalArgumentException("Device " + interfaceName + " does not exist"); + } + } + + public void start() throws PcapNativeException { + log.info("Using interface " + device.getName()); + pcap = device.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 100); + + BasicThreadFactory factory = new BasicThreadFactory.Builder() + .namingPattern("pcap-worker-loop").build(); + ExecutorService loopExecutorService = Executors.newSingleThreadExecutor(factory); + try { + log.info("Intercept started"); + pcap.loop(-1, this, loopExecutorService); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + // выходим + } catch (Exception e) { + log.error("Error while capturing packet", e); + stop(); + } + } + + @SneakyThrows + public void stop() { + if (pcap != null && pcap.isOpen()) { + pcap.breakLoop(); + pcap.close(); + } + + log.info("Intercept stopped"); + } + +} diff --git a/src/main/java/ru/serega6531/packmate/pcap/PcapWorker.java b/src/main/java/ru/serega6531/packmate/pcap/PcapWorker.java new file mode 100644 index 0000000..62ee4c6 --- /dev/null +++ b/src/main/java/ru/serega6531/packmate/pcap/PcapWorker.java @@ -0,0 +1,12 @@ +package ru.serega6531.packmate.pcap; + +import org.pcap4j.core.PcapNativeException; +import ru.serega6531.packmate.model.enums.Protocol; + +public interface PcapWorker { + + void start() throws PcapNativeException; + void stop(); + int closeTimeoutStreams(Protocol protocol, long timeoutMillis); + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3b39146..ed78283 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,6 +15,7 @@ spring: enable-capture: true +capture-mode: LIVE # LIVE, FILE interface-name: enp0s31f6 local-ip: "192.168.0.125" account-login: BinaryBears