gethostbyname 为了获取域名的ip地址,我写了如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 std::string host2ip (const std::string& host) { if (host.empty ()) return {}; std::regex valid_ipv4_re (R"(^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$)" ) ; std::smatch ipv4_match; if (std::regex_match (host, ipv4_match, valid_ipv4_re)) { return host; } struct hostent *server = gethostbyname (host.c_str ()); if (!server) { LOG (ERROR) << "gethostbyname fail, host: " << host; return {}; } char addr[128 ]; inet_ntop (server->h_addrtype, server->h_addr, addr, sizeof (addr)); return addr; }
如果参数是个ipv4地址,则直接返回,否则通过gethostbyname函数查询。
服务运行了一个半月左右,登上机器一看发现有两次coredump(有脚本辅助重启,客户端没感知到),堆栈如下
1 2 3 4 #0 inet_ntop4 (size=128, dst=0x7f864afcc870 "\020", src=0x0) at ./resolv/inet_ntop.c:85 #1 __GI_inet_ntop (af=2, src=0x0, dst=0x7f864afcc870 "\020", size=128) at ./resolv/inet_ntop.c:57 #2 0x000055f656081cab in liph::host2ip(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ()
src参数为null,再查看下gethostbyname函数手册 ,发现是不可重入的。可重入版本是gethostbyname_r,但是POSIX规范中这些函数都被标记为deprecated,我们有更好的选择。
getaddrinfo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <netdb.h> #include <sys/socket.h> #include <sys/types.h> struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr ; char *ai_canonname; struct addrinfo *ai_next ; }; int getaddrinfo (const char *restrict node, const char *restrict service, const struct addrinfo *restrict hints, struct addrinfo **restrict res) ; void freeaddrinfo (struct addrinfo *res) ;const char *gai_strerror (int errcode) ;
替换如下
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 32 33 std::string host2ip (const std::string& host) { struct addrinfo hints; memset (&hints, 0 , sizeof (hints)); hints.ai_family = AF_UNSPEC; struct addrinfo *result = nullptr ; int r = getaddrinfo (host.c_str (), nullptr , &hints, &result); if (r != 0 ) { LOG (ERROR) << "host: " << host; LOG (ERROR) << "getaddrinfo fail: " << gai_strerror (r); return {}; } std::unique_ptr<struct addrinfo, decltype ([](struct addrinfo *ptr) { if (ptr) freeaddrinfo(ptr); }) > res (result) ; if (res) { void *addr = nullptr ; switch (res->ai_family) { case AF_INET: addr = &(reinterpret_cast <struct sockaddr_in *>(res->ai_addr)->sin_addr); break ; case AF_INET6: addr = &(reinterpret_cast <struct sockaddr_in6 *>(res->ai_addr)->sin6_addr); break ; default : return {}; } if (addr) { char buffer[128 ]; inet_ntop (res->ai_family, addr, buffer, sizeof (buffer)); return buffer; } return {}; } return {}; }