ping: add macos support for icmp ping
This commit is contained in:
@@ -29,6 +29,7 @@ import com.google.common.primitives.Bytes;
|
|||||||
import com.sun.jna.Memory;
|
import com.sun.jna.Memory;
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.Inet4Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
@@ -43,6 +44,7 @@ public class Ping
|
|||||||
private static final byte[] RUNELITE_PING = "RuneLitePing".getBytes(Charsets.UTF_8);
|
private static final byte[] RUNELITE_PING = "RuneLitePing".getBytes(Charsets.UTF_8);
|
||||||
private static final int TIMEOUT = 2000; // ms
|
private static final int TIMEOUT = 2000; // ms
|
||||||
private static final int PORT = 43594;
|
private static final int PORT = 43594;
|
||||||
|
private static final int MAX_IPV4_HEADER_SIZE = 60;
|
||||||
|
|
||||||
private static short seq;
|
private static short seq;
|
||||||
|
|
||||||
@@ -59,19 +61,27 @@ public class Ping
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(inetAddress instanceof Inet4Address))
|
||||||
|
{
|
||||||
|
log.debug("Only ipv4 ping is supported");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
switch (OSType.getOSType())
|
switch (OSType.getOSType())
|
||||||
{
|
{
|
||||||
case Windows:
|
case Windows:
|
||||||
return windowsPing(inetAddress);
|
return windowsPing(inetAddress);
|
||||||
|
case MacOS:
|
||||||
case Linux:
|
case Linux:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return linuxPing(inetAddress);
|
return icmpPing(inetAddress, OSType.getOSType() == OSType.MacOS);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
log.debug("error during icmp ping", ex);
|
||||||
return tcpPing(inetAddress);
|
return tcpPing(inetAddress);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -111,7 +121,7 @@ public class Ping
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int linuxPing(InetAddress inetAddress) throws IOException
|
private static int icmpPing(InetAddress inetAddress, boolean includeIpHeader) throws IOException
|
||||||
{
|
{
|
||||||
RLLibC libc = RLLibC.INSTANCE;
|
RLLibC libc = RLLibC.INSTANCE;
|
||||||
byte[] address = inetAddress.getAddress();
|
byte[] address = inetAddress.getAddress();
|
||||||
@@ -137,12 +147,19 @@ public class Ping
|
|||||||
byte[] request = {
|
byte[] request = {
|
||||||
8, // type 8 - ipv4 echo request
|
8, // type 8 - ipv4 echo request
|
||||||
0, // code
|
0, // code
|
||||||
0, 0, // checksum (set by kernel)
|
0, 0, // checksum
|
||||||
0, 0, // id (set by kernel)
|
0, 0, // id - set by kernel on Linux. MacOS uses pid, however sending 0 appears to work fine.
|
||||||
(byte) (((seqno >> 8) & 0xff)), (byte) (seqno & 0xff)
|
(byte) (((seqno >> 8) & 0xff)), (byte) (seqno & 0xff)
|
||||||
};
|
};
|
||||||
// append payload
|
// append payload
|
||||||
request = Bytes.concat(request, RUNELITE_PING);
|
request = Bytes.concat(request, RUNELITE_PING);
|
||||||
|
|
||||||
|
final short checksum = checksum(request);
|
||||||
|
// on Linux the kernel automatically sets the checksum and ignores what we set,
|
||||||
|
// however it is required on MacOS
|
||||||
|
request[2] = (byte) ((checksum >> 8) & 0xff);
|
||||||
|
request[3] = (byte) (checksum & 0xff);
|
||||||
|
|
||||||
// struct sockaddr_in
|
// struct sockaddr_in
|
||||||
byte[] addr = {
|
byte[] addr = {
|
||||||
(byte) libc.AF_INET, 0, // sin_family
|
(byte) libc.AF_INET, 0, // sin_family
|
||||||
@@ -151,21 +168,44 @@ public class Ping
|
|||||||
0, 0, 0, 0, 0, 0, 0, 0 // padding
|
0, 0, 0, 0, 0, 0, 0, 0 // padding
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// response size/buffer
|
||||||
|
int size = 8 + RUNELITE_PING.length + // struct icmphdr + response
|
||||||
|
(includeIpHeader ? MAX_IPV4_HEADER_SIZE : 0); // struct ip
|
||||||
|
Memory response = new Memory(size);
|
||||||
|
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
if (libc.sendto(sock, request, request.length, 0, addr, addr.length) != request.length)
|
if (libc.sendto(sock, request, request.length, 0, addr, addr.length) != request.length)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int size = 8 + RUNELITE_PING.length; // struct icmphdr + response
|
int rlen = libc.recvfrom(sock, response, size, 0, null, null);
|
||||||
Memory response = new Memory(size);
|
long end = System.nanoTime();
|
||||||
if (libc.recvfrom(sock, response, size, 0, null, null) != size)
|
if (rlen <= 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
long end = System.nanoTime();
|
|
||||||
|
|
||||||
short seq = (short) (((response.getByte(6) & 0xff) << 8) | response.getByte(7) & 0xff);
|
int icmpHeaderOffset = 0;
|
||||||
|
if (includeIpHeader)
|
||||||
|
{
|
||||||
|
int ihl = response.getByte(0) & 0xf;
|
||||||
|
icmpHeaderOffset = ihl << 2; // to bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icmpHeaderOffset + 7 >= rlen)
|
||||||
|
{
|
||||||
|
log.warn("packet too short (received {} bytes but icmp header offset is {})", rlen, icmpHeaderOffset);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getByte(icmpHeaderOffset) != 0) // ICMP type - echo reply
|
||||||
|
{
|
||||||
|
log.warn("non-echo reply");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
short seq = (short) (((response.getByte(icmpHeaderOffset + 6) & 0xff) << 8) | response.getByte(icmpHeaderOffset + 7) & 0xff);
|
||||||
if (seqno != seq)
|
if (seqno != seq)
|
||||||
{
|
{
|
||||||
log.warn("sequence number mismatch ({} != {})", seqno, seq);
|
log.warn("sequence number mismatch ({} != {})", seqno, seq);
|
||||||
@@ -180,6 +220,24 @@ public class Ping
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IP checksum
|
||||||
|
private static short checksum(byte[] data)
|
||||||
|
{
|
||||||
|
int a = 0;
|
||||||
|
for (int i = 0; i < data.length - 1; i += 2)
|
||||||
|
{
|
||||||
|
a += ((data[i] & 0xff) << 8) | (data[i + 1] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data.length & 1) != 0)
|
||||||
|
{
|
||||||
|
a += (data[data.length - 1] & 0xff) << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = (a >> 16 & 0xffff) + (a & 0xffff);
|
||||||
|
return (short) (~a & 0xffff);
|
||||||
|
}
|
||||||
|
|
||||||
private static int tcpPing(InetAddress inetAddress) throws IOException
|
private static int tcpPing(InetAddress inetAddress) throws IOException
|
||||||
{
|
{
|
||||||
try (Socket socket = new Socket())
|
try (Socket socket = new Socket())
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ package net.runelite.client.plugins.worldhopper.ping;
|
|||||||
import com.sun.jna.Native;
|
import com.sun.jna.Native;
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
import com.sun.jna.platform.unix.LibC;
|
import com.sun.jna.platform.unix.LibC;
|
||||||
|
import net.runelite.client.util.OSType;
|
||||||
|
|
||||||
interface RLLibC extends LibC
|
interface RLLibC extends LibC
|
||||||
{
|
{
|
||||||
@@ -34,9 +35,9 @@ interface RLLibC extends LibC
|
|||||||
|
|
||||||
int AF_INET = 2;
|
int AF_INET = 2;
|
||||||
int SOCK_DGRAM = 2;
|
int SOCK_DGRAM = 2;
|
||||||
int SOL_SOCKET = 1;
|
int SOL_SOCKET = OSType.getOSType() == OSType.MacOS ? 0xffff : 1;
|
||||||
int IPPROTO_ICMP = 1;
|
int IPPROTO_ICMP = 1;
|
||||||
int SO_RCVTIMEO = 20;
|
int SO_RCVTIMEO = OSType.getOSType() == OSType.MacOS ? 0x1006 : 20;
|
||||||
|
|
||||||
int socket(int domain, int type, int protocol);
|
int socket(int domain, int type, int protocol);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user