您的位置:首页 > 编程语言 > Java开发

未公开的mustang核心秘密(四):jdk中实现的rawsocket

2007-07-23 10:19 429 查看
以前面试被问道这样一个问题,你用过rawsocket吗,我回答用过,做ICMP,实现非TCPIP协议以外的协议。
ICMP是非IP协议,java实现了TCPIP协议,没有实现ICMP协议,JDK1.5发生了变化,InetAddress提供boolean isReachable(int timeout)
 测试是否可以达到该地址。实现尽最大努力试图到达主机,但防火墙和服务器配置可能阻塞请求,使其在某些特定的端口可以访问时处于不可到达状态。如果可以获得权限,则典型实现将使用 ICMP ECHO REQUEST;否则它将试图在目标主机的端口 7 (Echo) 上建立 TCP 连接。
超时值(以毫秒为单位)指示尝试应该使用的最大时间量。如果在获取应答前操作超时了,则视为主机不可到达。负值将导致抛出 IllegalArgumentException。
 //测试是否可以到达主机
        public boolean IsReach(String host){
                boolean www=false;
                boolean ww=true;
                try {
                        InetAddress address = InetAddress.getByName(host);
                        for(int i=0;i<1000;i++){
                          www =  address.isReachable(500);
                           if(!www) {ww=www;System.out.println(ww);continue;};
                          
                        }
                } catch (UnknownHostException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                return ww;
               
        }
jdk实现了ICMP协议,关注ipv4,v6就不管了。
Inet4AddressImpl.c文件
/*
 * Class:     java_net_Inet4AddressImpl
 * Method:    isReachable0
 * Signature: ([bI[bI)Z
 */
JNIEXPORT jboolean JNICALL
Java_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,
        jbyteArray addrArray,
        jint timeout,
        jbyteArray ifArray,
        jint ttl) {
    jint addr;
    jbyte caddr[4];
    jint fd;
    struct sockaddr_in him;
    struct sockaddr_in* netif = NULL;
    struct sockaddr_in inf;
    int len = 0;
    WSAEVENT hEvent;
    int connect_rv = -1;
    int sz;

    /**
     * Convert IP address from byte array to integer
     */
    sz = (*env)->GetArrayLength(env, addrArray);
    if (sz != 4) {
      return JNI_FALSE;
    }
    memset((char *) &him, 0, sizeof(him));
    (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
    addr = ((caddr[0]<<24) & 0xff000000);
    addr |= ((caddr[1] <<16) & 0xff0000);
    addr |= ((caddr[2] <<8) & 0xff00);
    addr |= (caddr[3] & 0xff);
    addr = htonl(addr);
    /**
     * Socket address
     */
    him.sin_addr.s_addr = addr;
    him.sin_family = AF_INET;
    len = sizeof(him);

    /**
     * If a network interface was specified, let's convert its address
     * as well.
     */
    if (!(IS_NULL(ifArray))) {
      (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
      addr = ((caddr[0]<<24) & 0xff000000);
      addr |= ((caddr[1] <<16) & 0xff0000);
      addr |= ((caddr[2] <<8) & 0xff00);
      addr |= (caddr[3] & 0xff);
      addr = htonl(addr);
      inf.sin_addr.s_addr = addr;
      inf.sin_family = AF_INET;
      inf.sin_port = 0;
      netif = &inf;
    }

#if 0
    /*
     * Windows implementation of ICMP & RAW sockets is too unreliable for now.
     * Therefore it's best not to try it at all and rely only on TCP
     * We may revisit and enable this code in the future.
     */

    /*
     * Let's try to create a RAW socket to send ICMP packets
     * This usually requires "root" privileges, so it's likely to fail.
     */
    fd = NET_Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (fd != -1) {
      /*
       * It didn't fail, so we can use ICMP_ECHO requests.
       */
 return ping4(env, fd, &him, timeout, netif, ttl);
    }
#endif

    /*
     * Can't create a raw socket, so let's try a TCP socket
     */
    fd = NET_Socket(AF_INET, SOCK_STREAM, 0);
    if (fd == JVM_IO_ERR) {
 /* note: if you run out of fds, you may not be able to load
  * the exception class, and get a NoClassDefFoundError
  * instead.
  */
 NET_ThrowNew(env, WSAGetLastError(), "Can't create socket");
 return JNI_FALSE;
    }
    if (ttl > 0) {
      setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl));
    }
    /*
     * A network interface was specified, so let's bind to it.
     */
    if (netif != NULL) {
      if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
 NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");
 closesocket(fd);
 return JNI_FALSE;
      }
    }

    /*
     * Make the socket non blocking so we can use select/poll.
     */
    hEvent = WSACreateEvent();
    WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);

    /* no need to use NET_Connect as non-blocking */
    him.sin_port = htons(7); /* Echo */
    connect_rv = connect(fd, (struct sockaddr *)&him, len);

    /**
     * connection established or refused immediately, either way it means
     * we were able to reach the host!
     */
    if (connect_rv == 0 || WSAGetLastError() == WSAECONNREFUSED) {
 WSACloseEvent(hEvent);
 closesocket(fd);
 return JNI_TRUE;
    } else {
 int optlen;

        switch (WSAGetLastError()) {
        case WSAEHOSTUNREACH: /* Host Unreachable */
        case WSAENETUNREACH: /* Network Unreachable */
        case WSAENETDOWN: /* Network is down */
        case WSAEPFNOSUPPORT: /* Protocol Family unsupported */
   WSACloseEvent(hEvent);
   closesocket(fd);  
   return JNI_FALSE;
        }

 if (WSAGetLastError() != WSAEWOULDBLOCK) {
     NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
      "connect failed");
     WSACloseEvent(hEvent);
     closesocket(fd);
     return JNI_FALSE;
 }

 timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);

 /* has connection been established */

 if (timeout >= 0) {
   optlen = sizeof(connect_rv);
   if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
        &optlen) <0) {
     connect_rv = WSAGetLastError();
   }

   if (connect_rv == 0 || connect_rv == WSAECONNREFUSED) {
     WSACloseEvent(hEvent);
     closesocket(fd);
     return JNI_TRUE;
   }
 }
    }
    WSACloseEvent(hEvent);
    closesocket(fd);
    return JNI_FALSE;
}
注意中间的#if 0这是c程序写屏蔽代码的最好方法。也就是说,jdk根本没有使用ICMP协议来判断主机是否存在,这是可以理解,因为一个ping发过去,稍有经验的黑客,就可以判断出主机的操作系统,为以后扫描做准备。一般来说,很少有人在主机打开echo端口,java这个函数做的有些多余。
jdk没有调用的ping代码
/**
 * ping implementation.
 * Send a ICMP_ECHO_REQUEST packet every second until either the timeout
 * expires or a answer is received.
 * Returns true is an ECHO_REPLY is received, otherwise, false.
 */
static jboolean
ping4(JNIEnv *env, jint fd, struct sockaddr_in* him, jint timeout,
      struct sockaddr_in* netif, jint ttl) {
    jint size;
    jint n, len, hlen1, icmplen;
    char sendbuf[1500];
    char recvbuf[1500];
    struct icmp *icmp;
    struct ip *ip;
    WSAEVENT hEvent;
    struct sockaddr sa_recv;
    jint tmout2;
    u_short pid, seq=1;
    int read_rv = 0;

    pid = (u_short) getpid();
    size = 60*1024;
    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char *) &size, sizeof(size));
    /**
     * A TTL was specified, let's set the socket option.
     */
    if (ttl > 0) {
      setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *) &ttl, sizeof(ttl));
    }

    /**
     * A network interface was specified, let's bind to it.
     */
    if (netif != NULL) {
      if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
 NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");
 closesocket(fd);
 return JNI_FALSE;
      }
    }

    /**
     * Let's make the socket non blocking
     */
    hEvent = WSACreateEvent();
    WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);

    /**
     * send 1 ICMP REQUEST every second until either we get a valid reply
     * or the timeout expired.
     */
    do {
      /**
       * construct the ICMP header
       */
      memset(sendbuf, 0, 1500);
      icmp = (struct icmp *) sendbuf;
      icmp->icmp_type = ICMP_ECHO;
      icmp->icmp_code = 0;
      icmp->icmp_id = htons(pid);
      icmp->icmp_seq = htons(seq);
      seq++;
      /**
       * checksum has to be set to zero before we can calculate the
       * real checksum!
       */
      icmp->icmp_cksum = 0;
      icmp->icmp_cksum = in_cksum((u_short *)icmp, 64);
      /**
       * Ping!
       */
      n = sendto(fd, sendbuf, 64, 0, (struct sockaddr *)him,
   sizeof(struct sockaddr));
      if (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK) {
 NET_ThrowNew(env, WSAGetLastError(), "Can't send ICMP packet");
 closesocket(fd);
 WSACloseEvent(hEvent);
 return JNI_FALSE;
      }

      /*
       * wait for 1 second at most
       */
      tmout2 = timeout > 1000 ? 1000 : timeout;
      do {
 tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
 if (tmout2 >= 0) {
   len = sizeof(sa_recv);
   n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, &sa_recv, &len);
   ip = (struct ip*) recvbuf;
   hlen1 = (ip->ip_hl) << 2;
   icmp = (struct icmp *) (recvbuf + hlen1);
   icmplen = n - hlen1;
   /**
    * Is that a proper ICMP reply?
    */
   if (icmplen >= 8 && icmp->icmp_type == ICMP_ECHOREPLY &&
       ntohs(icmp->icmp_id) == pid) {
     closesocket(fd);
     WSACloseEvent(hEvent);
     return JNI_TRUE;
   }
 }
      } while (tmout2 > 0);
      timeout -= 1000;
    } while (timeout > 0);
    closesocket(fd);
    WSACloseEvent(hEvent);
    return JNI_FALSE;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息