« | August 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | | | | | | |
| 公告 |
戒除浮躁,读好书,交益友 |
Blog信息 |
blog名称:邢红瑞的blog 日志总数:523 评论数量:1142 留言数量:0 访问次数:9691274 建立时间:2004年12月20日 |

| |
[jvm]未公开的mustang核心秘密(四):jdk中实现的rawsocket 原创空间, 软件技术, 电脑与网络
邢红瑞 发表于 2007/6/23 16:07:21 |
以前面试被问道这样一个问题,你用过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 JNICALLJava_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 jbooleanping4(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;} |
|
回复:未公开的mustang核心秘密(四):jdk中实现的rawsocket 原创空间, 软件技术, 电脑与网络
坏男孩(游客)发表评论于2007/6/28 15:23:04 |
|
» 1 »
|