JNA samples - ping, gethostbyname

ちょっと試してみたメモ。pingとgethostbyname。 WindowsXP-SP3とWindows2000SP4のjava-1.6.0_12, jna-3.1.0にて。 (2009/09/25 書き直しました)

by kei.nazaki 2009

javaでping - Ping.java

2009/09/25

IcmpSendEchoでPing。

//[Ping.java]
import com.sun.jna.Pointer;
import com.sun.jna.Memory;

import jnasamples.Winsock2;
import jnasamples.Winsock2.Hostent;
import jnasamples.Winsock2.WSAData;
import jnasamples.Icmp;
import jnasamples.Icmp.IpAddr;
import jnasamples.Icmp.IpAddrByVal;
import jnasamples.Icmp.IpOptionInformation;
import jnasamples.Icmp.IpOptionInformationByVal;
import jnasamples.Icmp.IpOptionInformationByRef;
import jnasamples.Icmp.IcmpEchoReply;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class Ping {

  public static String getStatusText(int status) {
    switch(status){
      case 0 :     return "SUCCESS";
      case 11000 : return "IP_SUCCESS";
      case 11001 : return "IP_BUF_TOO_SMALL";
      case 11002 : return "IP_DEST_NET_UNREACHABLE";
      case 11003 : return "IP_DEST_HOST_UNREACHABLE";
      case 11004 : return "IP_DEST_PROT_UNREACHABLE";
      case 11005 : return "IP_DEST_PORT_UNREACHABLE";
      case 11006 : return "IP_NO_RESOURCES";
      case 11007 : return "IP_BAD_OPTION";
      case 11008 : return "IP_HW_ERROR";
      case 11009 : return "IP_PACKET_TOO_BIG";
      case 11010 : return "IP_REQ_TIMED_OUT";
      case 11011 : return "IP_BAD_REQ";
      case 11012 : return "IP_BAD_ROUTE";
      case 11013 : return "IP_TTL_EXPIRED_TRANSIT";
      case 11014 : return "IP_TTL_EXPIRED_REASSEM";
      case 11015 : return "IP_PARAM_PROBLEM";
      case 11016 : return "IP_SOURCE_QUENCH";
      case 11017 : return "IP_OPTION_TOO_BIG";
      case 11018 : return "IP_BAD_DESTINATION";
      case 11050 : return "IP_GENERAL_FAILURE";
    }
    return "UNKNOWN_STATUS";
  }

  public static String dumpReply(
    boolean isSuccess,
    String address,
    String message,
    int status,
    int dataSize,
    int rtt,
    int ttl
  ){
    return isSuccess?
      String.format(
        "%s from=%s size=%d rtt=%d ttl=%d",
          message, address, dataSize, rtt, ttl):
      String.format(
        "%s from=%s",
          message, address);
  }

  public static String execPing(
    byte[] ipAddress,
    int ttl,
    int sendDataSize,
    int timeout
  ){
    //socket initialize
    WSAData wsadata = new WSAData();
    if( Winsock2.INSTANCE.WSAStartup((short)2, wsadata) != 0 ){
      throw new RuntimeException("WSAStartup failed");
    }

    IpAddrByVal ipaddr = new IpAddrByVal();
    ipaddr.bytes = (byte[])ipAddress.clone();

    int replyDataSize = sendDataSize + (new IcmpEchoReply().size());
    Pointer sendData  = new Memory(sendDataSize);
    Pointer replyData = new Memory(replyDataSize);
    IpOptionInformationByRef ipOption = new IpOptionInformationByRef();
    ipOption.ttl = (byte)ttl;
    ipOption.tos = (byte)0;
    ipOption.flags = (byte)0;
    ipOption.optionsSize = (byte)0;
    ipOption.optionsData = null;

    //call function IcmpSendEcho
    Pointer hIcmp = Icmp.INSTANCE.IcmpCreateFile();
    int returnCode = Icmp.INSTANCE.IcmpSendEcho(
      hIcmp,
      ipaddr,
      sendData,
      (short)sendDataSize,
      ipOption,
      replyData,
      replyDataSize,
      timeout
    );
    Icmp.INSTANCE.IcmpCloseHandle(hIcmp);

    //socket cleanup
    Winsock2.INSTANCE.WSACleanup();

    IcmpEchoReply echoReply = new IcmpEchoReply(replyData);
    if( (returnCode == 0) && (echoReply.status == 0) ){
      throw new RuntimeException(
        "IcmpSendEcho fails. Call GetLastError for additional error information.");
    }
    boolean isSuccess = (echoReply.status == 0)? true: false;

    String address = String.format("%d.%d.%d.%d",
      echoReply.address.bytes[0] & 0xff,
      echoReply.address.bytes[1] & 0xff,
      echoReply.address.bytes[2] & 0xff,
      echoReply.address.bytes[3] & 0xff
    );
    String message = getStatusText(echoReply.status);

    return dumpReply(
      isSuccess,
      address,
      message,
      echoReply.status,
      sendDataSize,
      echoReply.roundTripTime,
      echoReply.options.ttl & 0xff
    );
  }


  public static void main(String[] args){
    byte[] ipaddress = new byte[4];
    try{
      InetAddress address = InetAddress.getByName(args[0]);
      ipaddress = address.getAddress();
    }catch(UnknownHostException e){
      throw new RuntimeException(e);
    }
    System.out.println( execPing(ipaddress, 255, 32, 1000).toString() );
  }

}

結果はこのように。

C:\scratch>java Ping google.co.jp
SUCCESS from=72.14.203.104 size=32 rtt=31 ttl=48

C:\scratch>java Ping 1.2.3.4
IP_REQ_TIMED_OUT from=1.2.3.4

[java]

javaでping - ICMP.java

2009/09/25

icmp.dllをロード。

//[ICMP.java]
package jnasamples;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.Structure.ByValue;
import com.sun.jna.Structure.ByReference;
import com.sun.jna.Pointer;

public interface Icmp extends Library {
  Icmp INSTANCE = (Icmp)Native.loadLibrary("icmp",Icmp.class);
  public static class IpAddr extends Structure {
    public byte[] bytes = new byte[4];
  }
  public static class IpAddrByVal extends IpAddr implements ByValue {
  }
  public static class IpOptionInformation extends Structure {
    public byte ttl;
    public byte tos;
    public byte flags;
    public byte optionsSize;
    public Pointer optionsData;
  }
  public static class IpOptionInformationByVal 
      extends IpOptionInformation implements ByValue {
  };
  public static class IpOptionInformationByRef
      extends IpOptionInformation implements ByReference {
  };
  public static class IcmpEchoReply extends Structure {
    public IpAddrByVal address;
    public int status;
    public int roundTripTime;
    public short dataSize;
    public short reserved;
    public Pointer data;
    public IpOptionInformationByVal options;
    public IcmpEchoReply(){
    }
    public IcmpEchoReply(Pointer p){
      useMemory(p);
      read();
    }
  }
  public Pointer IcmpCreateFile();
  public boolean IcmpCloseHandle(Pointer hIcmp);
  public int IcmpSendEcho(
    Pointer     hIcmp,
    IpAddrByVal destinationAddress,
    Pointer     requestData,
    short       requestSize,
    IpOptionInformationByRef requestOptions,
    Pointer     replyBuffer,
    int         replySize,
    int         timeout
  );
}

[java]

javaでgethostbyname - MyGetHostByName.java

2009/09/25

gethostbynameを呼んでみる。

//[MyGetHostByName.java]
import java.util.List;
import java.util.ArrayList;
import com.sun.jna.Pointer;

import jnasamples.Winsock2;
import jnasamples.Winsock2.Hostent;
import jnasamples.Winsock2.WSAData;

public class MyGetHostByName {
  
  private static String dumpAddress(byte[] address){
    StringBuilder builder = new StringBuilder();
    for(byte onebyte : address){
      if(builder.length() > 0) builder.append(".");
      builder.append( 0xFF & onebyte );
    }
    return builder.toString();
  }
  
  private static void dumpHostent(Hostent hostent){
    List<String> aliasesList = new ArrayList<String>();
    List<String> addressList = new ArrayList<String>();
    for(Pointer alias : hostent.aliases.getPointerArray(0L)){
      aliasesList.add(alias.getString(0L));
    }
    for(Pointer address : hostent.addressList.getPointerArray(0L)){
      addressList.add(
        dumpAddress(
          address.getByteArray(0L, hostent.length)) );
    }
    System.out.println("-----HOSTENT-----");
    System.out.println("name         : " + hostent.name.getString(0L));
    System.out.println("addrType     : " + hostent.addrType);
    System.out.println("length       : " + hostent.length);
    System.out.println("aliases      : " + aliasesList);
    System.out.println("addressList  : " + addressList);
  }


  public static void main(String[] args){
    WSAData wsadata = new WSAData();
    if( Winsock2.INSTANCE.WSAStartup((short)2, wsadata) != 0 ){
      throw new RuntimeException("WSAStartup failed");
    }
    Hostent hostent = Winsock2.INSTANCE.gethostbyname(args[0]);
    if(hostent == null){
      System.out.println("hostent is null");
    }else{
      dumpHostent(hostent);
    }
    Winsock2.INSTANCE.WSACleanup();
  }
}

結果はこんな感じ。

C:\scratch>java MyGetHostByName google.co.jp
-----HOSTENT-----
name         : google.co.jp
addrType     : 2
length       : 4
aliases      : []
addressList  : [74.125.91.104, 74.125.95.104, 72.14.203.104]

C:\scratch>nslookup google.co.jp
...
Non-authoritative answer:
Name:    google.co.jp
Addresses:  74.125.95.104, 72.14.203.104, 74.125.91.104

[java]

javaでgethostbyname - Winsock2.java

2009/09/25

ws2_32.dllをロード。 java-1.6.0_12, jna-3.1.0, WindowsXPSP3。

//[Winsock2.java]
package jnasamples;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.Pointer;

public interface Winsock2 extends Library {
  Winsock2 INSTANCE = (Winsock2)Native.loadLibrary("ws2_32",Winsock2.class);
  public static class WSAData extends Structure {
    public short version;
    public short highVersion;
    public byte[] description = new byte[256+1];
    public byte[] systemStatus = new byte[256+1];
    public short maxSockets;
    public short maxUdpDg;
    public Pointer vendorInfo;
  }
  public static class Hostent extends Structure {
    public Pointer name;
    public Pointer aliases;
    public short addrType;
    public short length;
    public Pointer addressList;
  }
  int WSAStartup(short versionRequested, WSAData wsadata);
  int WSACleanup();
  Hostent gethostbyname(String name);
}

[java]

(RFYL - index)