#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define LOGD printf
#define LOGE printf
#define LOGW printf
#define PUBLICS_DNS "114.114.114.114"
#define DNS_LEN (16 + 1)
#define BUFF_LEN256 256
#define BUFF_LEN1024 1024
#define DNS_MAX 25
#define DNS_PORT 53
#define DNS_CONF_FILE "/etc/resolv.conf"
#define BEGIN_TIMESTAMP() \
struct timespec startTime, endTime; \
char tmBuf[512] = {0}; \
int32_t err; \
uint64_t startTm = 0, endTm, diffTime; \
errno = 0; \
memset(&startTime, 0, sizeof(startTime)); \
err = clock_gettime(CLOCK_REALTIME, &startTime); \
if (err != 0) { \
(void)strerror_r(errno, tmBuf, sizeof(tmBuf)); \
LOGE("clock_gettime error, err:%d, reason:%s", err, tmBuf); \
} else { \
startTm = (uint64_t)(startTime.tv_sec * 1000) + (uint64_t)(startTime.tv_nsec / (1000 * 1000)); \
}
#define END_TIMESTAMP() \
errno = 0; \
memset(&endTime, 0, sizeof(endTime)); \
err = clock_gettime(CLOCK_REALTIME, &endTime); \
if (err != 0) { \
memset(tmBuf, 0, sizeof(tmBuf)); \
(void)strerror_r(errno, tmBuf, sizeof(tmBuf)); \
LOGE("clock_gettime error, err:%d, reason:%s", err, tmBuf); \
} else { \
endTm = (uint64_t)(endTime.tv_sec * 1000) + (uint64_t)(endTime.tv_nsec / (1000 * 1000)); \
if (endTm >= startTm && startTm != 0) { \
diffTime = endTm - startTm; \
if (diffTime > 0) \
LOGD("[%s] blocking time:%lums\n", __FUNCTION__, diffTime); \
} \
}
typedef struct {
uint16_t id;
uint16_t flags;
uint16_t numq;
uint16_t numAnswerRR;
uint16_t numAuthRR;
uint16_t numAddRR;
} DnsHeader;
typedef struct {
uint16_t type;
uint16_t classes;
} DnsQryFmt;
void PrintDataByHEX(const char *title, uint8_t *data, uint32_t len)
{
if ((title == NULL) || (data == NULL) || (len == 0)) {
LOGE("input param error!");
return;
}
int i;
uint8_t *tmpData = data;
char *p = NULL;
uint32_t dataLen = len;
char buf[128 + 1] = {0};
uint8_t tmpValue;
while (dataLen > 0) {
p = buf;
for (i = 0; ((i < 16) && (dataLen > 0)); i++) {
tmpValue = (*tmpData >> 4) & 0x0f;
if (tmpValue <= 9) {
*p++ = tmpValue + '0';
} else {
*p++ = tmpValue - 10 + 'a';
}
tmpValue = (*tmpData & 0x0f);
if (tmpValue <= 9) {
*p++ = tmpValue + '0';
} else {
*p++ = tmpValue - 10 + 'a';
}
*p++ = ' ';
tmpData++;
if (dataLen > 0) {
dataLen--;
}
}
LOGD("[%s][%d]:%s\n", title, len, buf);
memset(buf, 0, sizeof(buf));
}
}
static char* StrTrim(char *str)
{
char *p = NULL;
char *q = NULL;
p = str;
q = str + strlen(str) - 1;
while(isspace(*p))
p++;
while(isspace(*q))
q--;
*(q + 1) = '\0';
return p;
}
static void PacketQueryNameData(char *buf, uint32_t len)
{
char *p = buf;
int32_t i = 0;
while (p < (buf + len)) {
if (*p == '.') {
*(p - i - 1) = i;
i = 0;
} else {
i++;
}
p++;
}
*(p - i - 1) = i;
}
void PrintErrnoInfo(const char *title, int32_t errnum)
{
char tmpBuf[BUFF_LEN256] = {0};
(void)strerror_r(errnum, tmpBuf, sizeof(tmpBuf));
LOGE("%s->errno:%d reson:%s\n", title, errnum, tmpBuf);
}
static bool GetDnsServer(uint8_t *cnt, char *dns)
{
char tmp[BUFF_LEN256];
FILE *fp = NULL;
fp = fopen(DNS_CONF_FILE, "r");
if (fp == NULL) {
LOGE("fopen %s failed\n", DNS_CONF_FILE);
return false;
}
while (!feof(fp)) {
bzero(tmp, sizeof(tmp));
fgets(tmp, sizeof(tmp), fp);
char *pos = strstr(tmp, "nameserver");
if (pos == NULL) {
continue;
}
strcpy(dns, StrTrim(pos + strlen("nameserver")));
LOGD("dns%hhu:%s\n", *cnt, dns);
if (*cnt < DNS_MAX) {
(*cnt)++;
}
dns += DNS_LEN;
}
fclose(fp);
return true;
}
int32_t UserGetHostByName(int32_t domain, const char *name, const char *usrDns, char *ip, uint32_t ipLen)
{
int32_t cliFd = -1;
char dns[DNS_MAX][DNS_LEN];
uint8_t cnt = 0;
char buf[BUFF_LEN1024];
int32_t i = 0;
if (usrDns != NULL) {
cnt = 1;
strcpy(dns[0], usrDns);
} else if ((usrDns == NULL ) && !GetDnsServer(&cnt, dns[0])) {
return -1;
}
struct sockaddr_in svrAddr;
struct sockaddr_in6 svrAddr6;
while (cnt-- > 0) {
if (domain == AF_INET) {
bzero(&svrAddr, sizeof(svrAddr));
svrAddr.sin_family = AF_INET;
svrAddr.sin_port = htons(DNS_PORT);
LOGD("==>dns[%d]:%s\n", i, dns[i]);
inet_pton(AF_INET, dns[i], &svrAddr.sin_addr);
LOGD("svrAddr.sin_addr:%#x\n", svrAddr.sin_addr.s_addr);
} else if (domain == AF_INET6) {
bzero(&svrAddr6, sizeof(svrAddr6));
inet_pton(AF_INET6, dns[i], &svrAddr6.sin6_addr);
} else {
LOGE("invalid domain:%d\n", domain);
return -1;
}
i++;
cliFd = socket(domain, SOCK_DGRAM, 0);
if (cliFd <= 0) {
LOGE("socket error:%d\n", cliFd);
continue;
}
bzero(buf, sizeof(buf));
DnsHeader *dnsHeader = (DnsHeader *)buf;
dnsHeader->id = (uint16_t)1;
dnsHeader->flags = htons(0x0100);
dnsHeader->numq = htons(1);
dnsHeader->numAnswerRR = 0;
strcpy(buf + sizeof(DnsHeader) + 1, name);
PacketQueryNameData(buf + sizeof(DnsHeader) + 1, strlen(name));
DnsQryFmt *dnsQry = (DnsQryFmt *)(buf + sizeof(DnsHeader) + 2 + strlen(name));
dnsQry->classes = htons(1);
dnsQry->type = htons(1);
ssize_t rcvLen = -1;
BEGIN_TIMESTAMP();
if (domain == AF_INET) {
errno = 0;
struct timeval tmOut;
tmOut.tv_sec = 2;
tmOut.tv_usec = 0;
if (setsockopt(cliFd, SOL_SOCKET, SO_RCVTIMEO, &tmOut, sizeof(tmOut)) != 0) {
PrintErrnoInfo("setsockopt", errno);
continue;
}
socklen_t sockLen = sizeof(struct sockaddr_in);
if (sendto(cliFd, buf, sizeof(DnsHeader) + strlen(name) + 2 + sizeof(DnsQryFmt), 0, (struct sockaddr *)&svrAddr, sockLen) <= 0) {
PrintErrnoInfo("sendto", errno);
continue;
}
PrintDataByHEX("SENDTO", (uint8_t *)buf, sizeof(DnsHeader) + strlen(name) + 2 + sizeof(DnsQryFmt));
rcvLen = recvfrom(cliFd, buf, sizeof(buf), 0, (struct sockaddr *)&svrAddr, &sockLen);
if (rcvLen <= 0) {
PrintErrnoInfo("recvfrom", errno);
continue;
}
PrintDataByHEX("RECVFROM", (uint8_t *)buf, rcvLen);
} else {
LOGW("not support\n");
}
END_TIMESTAMP();
LOGD("dnsHeader->numAnswerRR:%u\n", dnsHeader->numAnswerRR);
if (dnsHeader->numAnswerRR == 0) {
LOGE("dns query error\n");
continue;
}
snprintf(ip, ipLen - 1,"%hhu.%hhu.%hhu.%hhu", (uint8_t)*(buf + rcvLen - 4), (uint8_t)*(buf + rcvLen - 3), (uint8_t)*(buf + rcvLen - 2), (uint8_t)*(buf + rcvLen - 1));
LOGD("dns query success:%s=>%s\n", name, ip);
close(cliFd);
break;
}
return 0;
}
int32_t main(int32_t argcs, char *argv[])
{
char ip[BUFF_LEN256] = {0};
if (argcs == 2) {
UserGetHostByName(AF_INET, "www.baidu.com", argv[1], ip, sizeof(ip));
} else {
UserGetHostByName(AF_INET, "www.baidu.com", NULL, ip, sizeof(ip));
}
return 0;
}