在 Eclipse 中执行 "sudo java" 时扫描器失败

Scanner fails when executing "sudo java" in Eclipse

我已经关注 this procedure 以便在需要时从 Eclipse 运行 Java 应用程序作为根用户(例如,当我使用 Xubuntu 时,当使用 jnetpcap 来捕获和分析数据包)。我遇到的问题是,任何从 System.in 读取的 Scanner 语句都会失败,并提供非常模糊的信息。否则,程序 运行 没问题。

我将附上使用的代码(对示例 jnetpcap 提供的修改)。编译这个 w/o IDE 时,它 运行 没问题。

测试代码:

package pruebas;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapIf;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.PcapPacketHandler;
import org.jnetpcap.PcapBpfProgram;

public class Captura {

    private static Scanner sc;

    /**
     * Main startup method
     *
     * @param args
     *            ignored
     */
    private static String asString(final byte[] mac) {
        final StringBuilder buf = new StringBuilder();
        for (byte b : mac) {
            if (buf.length() != 0) buf.append(':');
            if (b >= 0 && b < 16) buf.append('0');
            buf.append(Integer.toHexString((b < 0) ? b + 256 : b).toUpperCase());
        }

        return buf.toString();
    }

    private static int readInteger(String msg) {
        int ans = 0;
        boolean validInput = false;
        while (!validInput) {
            System.out.print(msg);
            String buf = sc.nextLine();
            try {
                ans = Integer.parseInt(buf.trim());
                validInput = true;
            }
            catch(NumberFormatException nfe) {
                validInput = false;
                System.out.println("Sorry, this input is incorrect! Please try again.");
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        List<PcapIf> alldevs = new ArrayList<PcapIf>(); // Will be filled with NICs
        StringBuilder errbuf = new StringBuilder(); // For any error msgs

        /***************************************************************************
         * First get a list of devices on this system
         **************************************************************************/
        int r = Pcap.findAllDevs(alldevs, errbuf);
        if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
            System.err.printf("Can't read list of devices, error is %s", errbuf.toString());
            return;
        }

        System.out.println("Network devices found:");

        int i = 0;
        try {
            for (PcapIf device : alldevs) {
                String description = (device.getDescription() != null) ? device.getDescription() : "No description available";
                final byte[] mac = device.getHardwareAddress();
                String dir_mac = (mac == null) ? "No MAC address associated" : asString(mac);
                System.out.printf("#%d: %s [%s] MAC:[%s]\n", i++, device.getName(), description, dir_mac);
            }// for

            sc = new Scanner(System.in);
            int opc = readInteger("\nSelect device [0-" + (alldevs.size() - 1) + "]:\t");

            PcapIf device = alldevs.get(opc); // We know we have at least 1 device
            System.out.printf("\nChoosing '%s' on your behalf:\n", (device
                    .getDescription() != null) ? device.getDescription()
                    : device.getName());

            /***************************************************************************
             * Second we open up the selected device
             **************************************************************************/
            /*
             * "snaplen" is short for 'snapshot length', as it refers to the
             * amount of actual data captured from each packet passing through
             * the specified network interface. 64*1024 = 65536 bytes; campo len
             * en Ethernet(16 bits) tam máx de trama
             */

            int snaplen = 64 * 1024; // Capture all packets, no trucation
            int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
            int timeout = 10 * 1000; // 10 seconds in millis
            Pcap pcap = Pcap.openLive(device.getName(), snaplen, flags,
                    timeout, errbuf);

            if (pcap == null) {
                System.err.printf("Error while opening device for capture: "
                        + errbuf.toString());
                return;
            }// if

            /******** F I L T R O ********/
            PcapBpfProgram filter = new PcapBpfProgram();
            String expression = ""; // "port 80";
            int optimize = 0; // 1 means true, 0 means false
            int netmask = 0;
            int r2 = pcap.compile(filter, expression, optimize, netmask);
            if (r2 != Pcap.OK) {
                System.out.println("Filter error: " + pcap.getErr());
            }// if
            pcap.setFilter(filter);
            /****************/

            /***************************************************************************
             * Third we create a packet handler which will receive packets from
             * the libpcap loop.
             **********************************************************************/
            PcapPacketHandler<String> jpacketHandler = new PcapPacketHandler<String>() {

                public void nextPacket(PcapPacket packet, String user) {

                    System.out.printf(
                            "Received packet at %s caplen=%-4d len=%-4d %s\n",
                            new Date(packet.getCaptureHeader()
                                    .timestampInMillis()), packet
                                    .getCaptureHeader().caplen(),   // Length actually captured
                            packet.getCaptureHeader().wirelen(),    // Original length
                            user                                    // User supplied object
                            );
                    /****** Desencapsulado ********/
                    for (int i = 0; i < packet.size(); i++) {
                        System.out.printf("%02X ", packet.getUByte(i));
                        if (i % 16 == 15)
                            System.out.println("");
                    }
                    System.out.println("\n\nHeader: " + packet.toHexdump());

                }
            };

            /***************************************************************************
             * Fourth we enter the loop and tell it to capture 10 packets. The
             * loop method does a mapping of pcap.datalink() DLT value to
             * JProtocol ID, which is needed by JScanner. The scanner scans the
             * packet buffer and decodes the headers. The mapping is done
             * automatically, although a variation on the loop method exists
             * that allows the programmer to sepecify exactly which protocol ID
             * to use as the data link type for this pcap interface.
             **************************************************************************/
            pcap.loop(10, jpacketHandler, "jNetPcap rocks!");

            /***************************************************************************
             * Last thing to do is close the pcap handle
             **************************************************************************/
            pcap.close();
            sc.close();
        } catch (IOException e) {
            System.out.println(":'(");
            //e.printStackTrace();
        }
    }
}

输出:

Select device [0-14]:       at java.util.Scanner.nextLine(Scanner.java:1540)
    at pruebas.Captura.readInteger(Captura.java:50)
    at pruebas.Captura.main(Captura.java:88)

规格:

我终于设法在不使用 java 的包装器的情况下让它工作。它与 Linux.

中的文件系统功能有关

要让 java 列出和 select 网络接口,以及分析和捕获数据包,请转到 IDE 使用的可执行文件所在的目录(例如,/usr/lib/jvm/java-8-oracle/bin; 在 Eclipse 中,您可以通过查看控制台标题栏来获取它)和 运行 以下命令 对您的项目执行此操作之前:

sudo setcap cap_net_raw,cap_net_admin=eip java

然后 运行 你的项目。 一旦你测试了你的项目,运行下面的命令保持原样:

sudo setcap -r java

您可以查看 applied/removed 功能是否已被 运行ning:

getcap java

参见 this for more information regarding Linux behaviour when opening interfaces, and here 是一个完整的过程,以防您必须配置某些东西。