1.背景

在运行JBoss程序时,出现如下错误。

Caused by: java.net.UnknownHostException: bogon: bogon: 未知的名称或服务at java.net.InetAddress.getLocalHost(InetAddress.java:1506)at org.jboss.ws.common.management.AbstractServerConfig.setWebServiceHost(AbstractServerConfig.java:133)at org.jboss.as.webservices.config.ServerConfigImpl.setWebServiceHost(ServerConfigImpl.java:98)at org.jboss.as.webservices.dmr.WSSubsystemAdd.createServerConfig(WSSubsystemAdd.java:103)... 12 moreCaused by: java.net.UnknownHostException: bogon: 未知的名称或服务at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method)at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:929)at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1324)at java.net.InetAddress.getLocalHost(InetAddress.java:1501)... 15 more

最终定位原因是因为无法通过主机名,解析出IP地址所致,将/etc/hosts中增加了主机名与IP地址的映射后问题解决。但是发现解决的太容易,有些问题和原理没有想透彻。

bogon是什么含义?在哪里定义的?

java的java.net.InetAddress.getLocalHost为何与bogon扯上关系?

2.分析过程

在分析之前先了解一下Linux命令 ipcalc。ipcalc是RedHat提供的一个开源软件,其提供了一种计算主机IP信息的简单方法。比如可以通过IP地址与子网掩码计算出网络地址、广播地址等等,也可以通过IP地址反向获取主机名。

[yeqiyu@CentOS7-229 ~]$ ipcalc --help
Usage: ipcalc [OPTION...]-c, --check         Validate IP address for specified address family-4, --ipv4          IPv4 address family (default)-6, --ipv6          IPv6 address family-b, --broadcast     Display calculated broadcast address-h, --hostname      Show hostname determined via DNS-m, --netmask       Display default netmask for IP (class A, B, or C)-n, --network       Display network address-p, --prefix        Display network prefix-s, --silent        Don't ever display error messagesHelp options:-?, --help          Show this help message--usage             Display brief usage message

在不同状态使用ipcalc命令,查看效果。可以看到,已经出现了HOSTNAME=bogon关键信息。

#hosts中未进行主机名与IP的映射
[yeqiyu@CentOS7-229 ~]$ ipcalc -h 192.168.130.229
HOSTNAME=bogon#hosts中进行了主机名与IP的映射
[yeqiyu@CentOS7-229 ~]$ ipcalc -h 192.168.130.229
HOSTNAME=vcentos7-229

因此,可以推测,当本地DNS未设置时,会使用公共DNS,但公共DNS无法将IP地址反向解析为主机名时,会使用默认值bogon。我们换个方式佐证一下。

更换同版本的Linux环境

[yeqiyu@s168 ~]$ nslookup 192.168.129.168
168.129.168.192.in-addr.arpa    name = bogon.Authoritative answers can be found from:

更换Windows机器查看现象。DNS服务商将IP反向解析为了bogon。

C:\Users\YQY>nslookup 192.168.40.105
服务器:  public1.114dns.com
Address:  114.114.114.114名称:    bogon
Address:  192.168.40.105C:\Users\YQY>nslookup 202.106.0.20
服务器:  public1.114dns.com
Address:  114.114.114.114名称:    gjjline.bta.net.cn
Address:  202.106.0.20

通过不同操作系统、不同工具、不同IP验证,基本可以得出与IP地址的反向解析有关。当目标IP是私有地址或者无法被DNS反向解析时,都会将IP解析为bogon。

3.Bogon是什么

Bogon IP Addresses是一组IP地址,不被互联网地址分配机构(IANA)和RIR(区域互联网注册)分配给任何实体,这个未分配的地址空间称为虚假空间。bogon还包括保留的私有地址和链路本地地址范围(Martian Packets)。

IPv4 Bobon范围

Netblock Description
0.0.0.0/8 "This" network
10.0.0.0/8 Private-use networks
100.64.0.0/10 Carrier-grade NAT
127.0.0.0/8 Loopback
127.0.53.53 Name collision occurrence
169.254.0.0/16 Link local
172.16.0.0/12 Private-use networks
192.0.0.0/24 IETF protocol assignments
192.0.2.0/24 TEST-NET-1
192.168.0.0/16 Private-use networks
198.18.0.0/15 Network interconnect device benchmark testing
198.51.100.0/24 TEST-NET-2
203.0.113.0/24 TEST-NET-3
224.0.0.0/4 Multicast
240.0.0.0/4 Reserved for future use
255.255.255.255/32 Limited broadcast

4.JAVA解析过程分析

上述过程分析了在操作系统层面,什么情况会返回bogon,那么,JAVA层面是如何实现解析的呢?

分析JDK源码,找到类InetAddress,查看方法 getLocalHost(),可以看到其通过impl.getLocalHostName()获取了主机名,然后再通过主机名获取IP地址。根据错误提示,程序已经获得了主机名(bogon),因此关键点要找到getLocalHostName的具体实现。

    /*** Returns the address of the local host. This is achieved by retrieving* the name of the host from the system, then resolving that name into* an {@code InetAddress}.** <P>Note: The resolved address may be cached for a short period of time.* </P>** <p>If there is a security manager, its* {@code checkConnect} method is called* with the local host name and {@code -1}* as its arguments to see if the operation is allowed.* If the operation is not allowed, an InetAddress representing* the loopback address is returned.** @return     the address of the local host.** @exception  UnknownHostException  if the local host name could not*             be resolved into an address.** @see SecurityManager#checkConnect* @see java.net.InetAddress#getByName(java.lang.String)*/public static InetAddress getLocalHost() throws UnknownHostException {SecurityManager security = System.getSecurityManager();try {String local = impl.getLocalHostName();if (security != null) {security.checkConnect(local, -1);}if (local.equals("localhost")) {return impl.loopbackAddress();}InetAddress ret = null;synchronized (cacheLock) {long now = System.currentTimeMillis();if (cachedLocalHost != null) {if ((now - cacheTime) < maxCacheTime) // Less than 5s old?ret = cachedLocalHost;elsecachedLocalHost = null;}// we are calling getAddressesFromNameService directly// to avoid getting localHost from cacheif (ret == null) {InetAddress[] localAddrs;try {localAddrs =InetAddress.getAddressesFromNameService(local, null);} catch (UnknownHostException uhe) {// Rethrow with a more informative error message.UnknownHostException uhe2 =new UnknownHostException(local + ": " +uhe.getMessage());uhe2.initCause(uhe);throw uhe2;}cachedLocalHost = localAddrs[0];cacheTime = now;ret = localAddrs[0];}}return ret;} catch (java.lang.SecurityException e) {return impl.loopbackAddress();}}

getLocalHostName实现了接口InetAddressImpl的getLocalHostName方法,而Inet4AddressImpl又是InetAddressImpl的一个实现,可以找到该方法。

public native String getLocalHostName() throws UnknownHostException;

关键native表示该方法由C语言实现,找到源码。


JNIEXPORT jstring JNICALL
Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {char hostname[NI_MAXHOST+1];hostname[0] = '\0';if (JVM_GetHostName(hostname, sizeof(hostname))) {//如果获取失败,则设置默认值localhost/* Something went wrong, maybe networking is not setup? */strcpy(hostname, "localhost");} else {struct addrinfo hints, *res;int error;hostname[NI_MAXHOST] = '\0'; //设置默认结束符memset(&hints, 0, sizeof(hints));hints.ai_flags = AI_CANONNAME; //这个参数很重要,要求返回主机规范名称,非别名hints.ai_family = AF_INET;error = getaddrinfo(hostname, NULL, &hints, &res); //这里hostname会重新改写if (error == 0) {/* host is known to name service */getnameinfo(res->ai_addr,res->ai_addrlen,hostname,NI_MAXHOST,NULL,0,NI_NAMEREQD);/* if getnameinfo fails hostname is still the valuefrom gethostname */freeaddrinfo(res);}}return (*env)->NewStringUTF(env, hostname);
}

可以看到关键函数JVM_GetHostName和getaddrinfo,对变量hostname做了操作。参照JDK的源码,写一个Demo,来看下在哪里发生了变化。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#define NI_MAXHOST 1025
int main() {char hostname[NI_MAXHOST+1] = {0};gethostname(hostname, NI_MAXHOST);printf("hostname_1=%s\n", hostname);struct addrinfo hints, *res;int error;memset(&hints, 0, sizeof(hints));hints.ai_flags = AI_CANONNAME;hints.ai_family = AF_INET;error = getaddrinfo(hostname, NULL, &hints, &res);printf("hostname_2=%s\n", hostname);if (error == 0) {/* host is known to name service */getnameinfo(res->ai_addr,res->ai_addrlen,hostname,NI_MAXHOST,NULL,0,NI_NAMEREQD);/* if getnameinfo fails hostname is still the valuefrom gethostname */freeaddrinfo(res);}printf("hostname_3=%s\n", hostname);
}

编译

gcc gethostname_demo.c -o gethostname_demo

执行

[root@CentOS7-229 HostnameDemo_lib]# ./gethostname_demo
hostname_1=CentOS7-229
hostname_2=CentOS7-229
hostname_3=bogon

可以看到,经过getaddrinfo函数之后,hostname变成了bogon

此时,我们修改host文件,补充主机名到IP的解析,然后再用demo查看,可以获取主机名了。

[root@CentOS7-229 HostnameDemo_lib]# cat /etc/hosts
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
192.168.130.229 CentOS7-229
[root@CentOS7-229 HostnameDemo_lib]# ./gethostname_demo
hostname_1=CentOS7-229
hostname_2=CentOS7-229
hostname_3=CentOS7-229

关键点在于getnameinfo函数,如果在没有进行主机名和IP映射时,getnameinfo做了哪些事情?

#include <netdb.h>
#include <sys/socket.h>int getnameinfo(const struct sockaddr* restrict addr, socklen_t addrlen,char* restrict host, socklen_t hostlen,char* restrict serv, socklen_t servlen, int flags);
   是getaddrinfo的互补函数,以一个套接字地址为参数,返回描述其中的主机的一个字符串和描述其中的服务的另一个字符串。那么问题来了,明明已经通过gethostname和getaddrinfo函数拿到了主机名,又要再通过getnameinfo重新走一遍hosts、reslov.conf,将bogon ip反向解析为bogon名称,这么做的原因是什么呢?当前的能力还无法理解,暂且记录下来,后续再补充。

5.jboss与bogon

当jboss.bind.address=127.0.0.1或者192.168.130.229时,虽然会出现如下警告,但并不会出现ERROR级别错误。

21:59:44,867 WARN  [com.arjuna.ats.arjuna] (MSC service thread 1-8) ARJUNA012210: Unable to use InetAddress.getLocalHost() to resolve address.

在JBOSS启动时,jboss.bind.address=0.0.0.0,且不在hosts中映射,两个条件同时都满足,才会出现UnknownHostException: bogon: bogon: 未知的名称或服务。由JAVA实现的JBOSS又是怎么和bogon关联起来的呢?

这需要研究一下jboss.bind.address的实现机制。初步推测是如果设置具体IP地址,则直接监听这个指定的IP即可。如果设置为0.0.0.0,则需要监听本机上的所有IP,所以会调用getlocalhost的方法,来获取本机所有IP,于是便出现了步骤4的现象。

6.总结

借着这个问题,重新温习了Linux下DNS解析过程,将JAVA DNS内部实现原理进行了基本的学习,在Linux下的C开发与使用有了进一步了解。虽留有遗憾,仍然收获满满。

7.参考

ipcalc命令 – 简单的IP地址计算器 – Linux命令大全(手册)

Bogon IP addresses, ipv4 and ipv6 bogon IP Ranges

networking - Java DNS resolution hangs forever - Stack Overflow

getnameinfo(3) - Linux manual page

getaddrinfo(3) - Linux manual page

https://www.ibm.com/docs/en/zos/2.3.0?topic=functions-getnameinfo-get-name-information

Jdk8 DNS解析 - Loull - 博客园

jboss一个bogon引发的思考相关推荐

  1. Spring之LoadTimeWeaver——一个需求引发的思考---转

    原文地址:http://www.myexception.cn/software-architecture-design/602651.html Spring之LoadTimeWeaver--一个需求引 ...

  2. cuda必须装在c盘吗_软件安装到C盘会影响计算机运行速度吗?一个问题引发的思考...

    本文共1542字,预计阅读时间4-5分钟,若忙请暂时收藏! 从1939年第一台电子计算机样机运转那一刻,计算机作为20世纪最伟大的发明之一,在短短几十年内,极大程度影响了人类的社会活动和生产活动,伴随 ...

  3. 一个洗车店引发的思考:给谁发优惠券好呢?

    文章目录 一个洗车店的故事 解决方案 参考文献 注:本文是论文Unit Selection Based on Counterfactual Logic的一个笔记 一个洗车店的故事 从前有个洗车店,洗车 ...

  4. 一个“笑话”引发的思考

    抱歉引用了"馒头"的经典格式 . 技术是枯燥的,如果我们玩技术的再不自娱自乐一下,那可真没救儿了. 言归正传,"笑话"是这样的: 当美国人第一次成功发射载人卫星 ...

  5. 一个截图引发的思考——实现APP区域截图及图片对比

    前言 许久没有更新博客,整理下学习与思考的笔记. Uiaotomator2有两种截图方法,一种是整个截图,一种是元素截图,但是没有坐标截图,本篇文章将带领探索如何实现坐标截图. 初始代码 以下探索都建 ...

  6. 由一个问题引发的思考

    线程的合理使用能够提升程序的处理性能,主要有两个方面,第一个是能够利用多核cpu以及超线程技术来实现线程的并行执行:第二个是线程的异步化执行相比于同步执行来说,异步执行能够很好的优化程序的处理性能提升 ...

  7. 一个小程序引发的思考

    既然是一个小程序引发的思考,那么我们就先看看这个小程序,看看他有何神奇之处: namespace ConsoleApplication1 {class Program{static void Main ...

  8. 一个分组查询引发的思考

    一个分组查询引发的思考 我们在看项目代码或者SQL语句时, 往往会看到很多非常复杂的业务或者SQL 那么问题来了. 复杂SQL是如何写成的? 下面通过一个数据展示的需求来体会到复杂的SQL是如何书写的 ...

  9. 一个HBase查询问题引发的思考,作为HBase使用者这个问题你知道答案吗?

    前言 讲解HBase事务的文章很多,这里就不过多赘述了,大家应该都知道是通过MVCC实现的.但是今天这篇文章的背景是一个同事和我讨论一个问题引发的,这个问题使我重新梳理下这块内容并作为记录和大家分享. ...

最新文章

  1. 烂泥:haproxy学习之手机规则匹配
  2. android教育平板,调查称iPad在教育领域占优势 Android平板为零
  3. 【学亮说】Java实现单例模式的8种方式(你真的搞懂单例模式了吗?)
  4. 【SQLite】简单的基本使用步骤
  5. php5.2 json,php5.2以上版本json_encode兼容性
  6. Blazor编辑表单状态控件
  7. java sendredirect 参数_使用response.sendRedirect()传递隐藏参数
  8. Linux下history命令用法
  9. linux平台上不同类型的压缩文件的压缩与解压
  10. 网安学习日志(5)流量分析基础
  11. python 文件题目练习
  12. 塑料划分PP PE PS PA ABS PVC
  13. DOM操作简易年历案例
  14. Docker 自动化部署
  15. ubuntu 刷新频率 如何查看_Ubuntu 7.04救命啊!屏幕刷新频率只有50HZ眼不行啦!显示器是CRT...
  16. 祝早道日语七周年快乐!
  17. Pandas基础-利用python进行数据分析
  18. 第五章 ERP计划的时间概念
  19. 【计算机视觉】全景相机标定(MATLAB/opencv)
  20. 《Unity3D人工智能编程精粹》笔记

热门文章

  1. flutter 常见问题
  2. applepay默认卡怎么设置(苹果手表设置Apple Apy默认卡片)
  3. 6位中国民间艺术家在美国纽约法拉盛图书馆庆猪年
  4. error LNK1104: 无法打开文件“MSCOREE.lib”
  5. android手机 u盘 启动不起来,u盘无法启动怎么办 u盘无法启动解决方法【详解】...
  6. 老主板使用Clover引导到NVME SSD
  7. python控制windows 任务计划程序 获取具体单一任务
  8. Android自定义View,跟随手指滑动效果
  9. Windows卸载JDK和安装JDK
  10. Android JPush(极光推送)的使用教程