inet_ntoa函数陷阱

本文通过一个示例程序揭示了inet_ntoa函数在处理IP地址时的一个奇怪现象,并探讨了C语言中不同字符串处理方式的特点。
今天编程时遇到一个很诡异的问题,是关于inet_ntoa函数的。
先看一个测试程序

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main(int argc, char* argv[])
{
    struct in_addr addr1,addr2;
    unsigned long l1,l2;
    l1= inet_addr("192.168.0.74");
    l2 = inet_addr("211.100.21.179");
    memcpy(&addr1, &l1, 4);
    memcpy(&addr2, &l2, 4);
    printf("%s : %s\n", inet_ntoa(addr1), inet_ntoa(addr2)); //注意这一句的运行结果

    printf("%s\n", inet_ntoa(addr1));
    printf("%s\n", inet_ntoa(addr2));
    return 0;
}


inet_ntoa函数就是将ip无符号整形转换成ip字符串,函数返回的是指向ip字符串的指针。上面的程序看上去应该没什么问题,我们看一下运行结果:

[root@localhost ~]# gcc test.c -o iptest
[root@localhost ~]# ./iptest 
192.168.0.74 : 192.168.0.74(竟然是这个结果)
192.168.0.74
211.100.21.179

为什么会出现这样的结果?我们先把该问题搁置一边,说点别的。
在C语言中,自动变量在堆栈中分配内存,当包含自动变量的函数或代码块退出时,它们所占用的内存便会被回收,为了能够在包含自动变量的函数或代码块退出时,仍然能够访问这些自动变量的内存,可以如下几种方案:
1)返回一个指向字符串常量的指针。如
char *func(){ return "only works for simple strings";}
仅适用于简单的情形。
2)使用全局声明的数组
这种方法是最简单的,但是缺点也是显而易见,因为任何人都有可能修改这个数组内容。
3)使用静态数组。
char *func()
{
   static char buffer[20];
   ...
   return buffer;
}
这样做的缺点是该函数下一次调用时会覆盖这个数组的内容,所以调用者必须备份数组里的内容。
4)显示分配内存,保存返回的值
char *func()
{
   char *s=malloc(100);
   ...
   return s;
}
这样做的缺点是,调用者很容易忘记释放内存,从而造成内存泄露。

好了,言归正传。inet_ntoa函数的输入参数是u_int型的ip地址,返回的却是指向ip字符串的指针,很明显,ip字符串所占的内存是在函数内部分配的,而我们并不需要释放该内存,所以,它分配的内存是静态的,也就是说下一次调用该函数时会覆盖这个数组的内容,所以就会出现上述的结果(printf里面的可变参数的求值是从右到左的)。为了解决这个问题,我们只需备份数组里的内容即可。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/ethernet.h> #include <netinet/in.h> #include <net/if.h> #include <arpa/inet.h> #include <linux/if_packet.h> // 对于sockaddr_ll结构,仅Linux支持 #include"arp.h" #include <linux/if_arp.h> #define INITIAL_SIZE 1024 // 创建原始套接字 int create_arp_socket() { int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); if (sock < 0) { perror("socket"); exit(EXIT_FAILURE); } return sock; } // 构建ARP请求报文 void build_arp_packet(struct ether_arp *arp, uint32_t target_ip) { arp->ar_hrd = htons(ARPHRD_ETHER); arp->ar_pro = htons(ETHERTYPE_IP); arp->ar_hln = ETH_ALEN; arp->ar_pln = sizeof(in_addr_t); arp->ar_op = htons(ARPOP_REQUEST); // 设置源MAC(本机MAC) get_local_mac(arp->arp_sha); // 设置源IP(本机IP) struct in_addr local_ip; get_local_ip(&local_ip); memcpy(arp->arp_spa, &local_ip, sizeof(local_ip)); // 设置目标MAC(广播) memset(arp->arp_tha, 0xFF, ETH_ALEN); // 设置目标IP memcpy(arp->arp_tpa, &target_ip, sizeof(target_ip)); } // ARP扫描线程 void* scan_thread(void *arg) { arp_scanner *scanner = (arp_scanner*)arg; int sock = create_arp_socket(); while (scanner->config.enable_scan) { pthread_mutex_lock(&scanner->lock); config_t cfg = scanner->config; pthread_mutex_unlock(&scanner->lock); // 遍历IP范围 for (uint32_t ip = cfg.start_ip; ip <= cfg.end_ip; ip++) { struct ether_arp arp_req; build_arp_packet(&arp_req, ip); // 发送ARP请求 send_arp(sock, &arp_req); // 按间隔等待 usleep(cfg.packet_interval * 1000); } // 等待扫描周期 sleep(cfg.scan_cycle); } close(sock); return NULL; } // ARP响应处理 void process_arp_response(arp_scanner *scanner, struct ether_arp *arp) { uint32_t ip; memcpy(&ip, arp->arp_spa, sizeof(ip)); pthread_mutex_lock(&scanner->lock); // 查找或创建条目 arp_entry *entry = find_or_create_entry(scanner, ip); memcpy(entry->mac, arp->arp_sha, ETH_ALEN); entry->last_update = time(NULL); entry->expire_time = entry->last_update + scanner->config.entry_ttl; pthread_mutex_unlock(&scanner->lock); } // 超时清理线程 void* cleanup_thread(void *arg) { arp_scanner *scanner = (arp_scanner*)arg; while (1) { sleep(5); // 每5秒检查一次 pthread_mutex_lock(&scanner->lock); time_t now = time(NULL); for (int i = 0; i < scanner->entry_count; ) { if (scanner->entries[i].expire_time < now) { // 移除超时条目 memmove(&scanner->entries[i], &scanner->entries[i+1], (scanner->entry_count - i - 1) * sizeof(arp_entry)); scanner->entry_count--; } else { i++; } } pthread_mutex_unlock(&scanner->lock); } return NULL; } // 动态调整内存 void resize_entries(arp_scanner *scanner, int new_size) { arp_entry *new_entries = realloc(scanner->entries, new_size * sizeof(arp_entry)); if (new_entries) { scanner->entries = new_entries; scanner->max_entries = new_size; } } void update_config(arp_scanner *scanner, config_t new_config) { pthread_mutex_lock(&scanner->lock); // 验证IP范围有效性 if (ntohl(new_config.end_ip) < ntohl(new_config.start_ip)) { fprintf(stderr, "Invalid IP range\n"); } else { scanner->config = new_config; } pthread_mutex_unlock(&scanner->lock); } // 配置 config_t default_config = { .enable_scan = 1, .scan_cycle = 300, // 5分钟 .packet_interval = 100, // 100毫秒 .entry_ttl = 1800, // 30分钟 .start_ip=1, .end_ip=1 }; int main() { default_config.start_ip = inet_addr("192.168.1.1"); default_config.end_ip = inet_addr("192.168.1.254"); arp_scanner scanner = { .entries = malloc(INITIAL_SIZE * sizeof(arp_entry)), .entry_count = 0, .max_entries = INITIAL_SIZE, .config = default_config }; pthread_mutex_init(&scanner.lock, NULL); // 启动扫描线程 pthread_t scan_tid, cleanup_tid; pthread_create(&scan_tid, NULL, scan_thread, &scanner); pthread_create(&cleanup_tid, NULL, cleanup_thread, &scanner); // 启动ARP响应接收 int arp_sock = create_arp_socket(); while (1) { struct ether_arp arp_resp; if (recv_arp(arp_sock, &arp_resp) > 0) { process_arp_response(&scanner, &arp_resp); } } // 清理资源 pthread_join(scan_tid, NULL); pthread_join(cleanup_tid, NULL); free(scanner.entries); close(arp_sock); return 0; } 这个代码编译了会报这些错误,给我解决gcc -o arp arp.c -lthread arp.c: In function ‘build_arp_packet’: arp.c:37:5: warning: implicit declaration of function ‘get_local_mac’ [-Wimplicit-function-declaration] get_local_mac(arp->arp_sha); ^ arp.c:41:5: warning: implicit declaration of function ‘get_local_ip’ [-Wimplicit-function-declaration] get_local_ip(&local_ip); ^ arp.c: In function ‘scan_thread’: arp.c:67:13: warning: implicit declaration of function ‘send_arp’ [-Wimplicit-function-declaration] send_arp(sock, &arp_req); ^ arp.c: In function ‘process_arp_response’: arp.c:88:24: warning: implicit declaration of function ‘find_or_create_entry’ [-Wimplicit-function-declaration] arp_entry *entry = find_or_create_entry(scanner, ip); ^ arp.c:88:24: warning: initialization makes pointer from integer without a cast [-Wint-conversion] arp.c: In function ‘main’: arp.c:173:13: warning: implicit declaration of function ‘recv_arp’ [-Wimplicit-function-declaration] if (recv_arp(arp_sock, &arp_resp) > 0) { ^ /usr/bin/ld: cannot find -lthread collect2: error: ld returned 1 exit status
最新发布
08-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值