Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Scanner fails when executing Java with admin rights (sudo) in Eclipse in Linux
(Brought over from SE.)
The problem
I have followed this procedure in order to run Java applications as root when needed from Eclipse (as I use Xubuntu, for instance, when using jnetpcap
to capture and analyse packets). The problem I have is that any Scanner
statement reading from System.in
fails with very vague information provided. Otherwise, the program runs fine.
Following is the code I'm using (a modification of the example jnetpcap
provides). When compiling this without IDE, it runs fine.
Test code
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();
}
}
}
Output
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)
Specs
- Xubuntu 15.04
- Eclipse Luna SR2 (4.4.2)
- Java 8 (Oracle)
1 answer
I've finally managed to get it working without using a wrapper for java
. It has to do with filesystem capabilities in Linux.
To let java
list and select network interfaces, as well as analyse and capture packets, go to the directory where the executable the IDE uses is (for example, /usr/lib/jvm/java-8-oracle/bin
; in Eclipse you can get it by seeing the console titlebar) and run the following command before doing so with your project:
sudo setcap cap_net_raw,cap_net_admin=eip java
Then run your project. Once you have tested your project, run the following command to leave things as they were before:
sudo setcap -r java
You can see if capabilities have been applied/removed by running:
getcap java
See this for more information regarding Linux behaviour when opening interfaces, and here is a complete procedure in case you have to configure something.
0 comment threads