
curl 库 CURLINFO_LOCAL_IP 完全指南获取并安全使用最近连接的本地 IP 地址【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl本篇指南基于 curl 官方 libcurl 手册页 CURLINFO_LOCAL_IP.md讲解如何通过curl_easy_getinfo(CURLINFO_LOCAL_IP)获取当前连接“本地端”的 IP 地址即出站源地址深入剖析其底层通过getsockname()采集的实现机制、字符串内存的生命周期规则不可自行释放、内存会被复用并给出可直接编译运行的 C 示例。读完后你将能够在代理/多网卡/QUIC 场景下正确识别本机实际出口 IP、规避内存误用导致的悬垂指针以及与CURLINFO_PRIMARY_IP、CURLINFO_LOCAL_PORT配合完成完整的连接四元组审计。一、CURLINFO_LOCAL_IP 是什么CURLINFO_LOCAL_IP是 libcurl 提供的信息information类查询项用于获取最近一次已建立连接的本地端sourceIP 地址。它与常被混淆的CURLINFO_PRIMARY_IP是一对查询项含义对应源码字段CURLINFO_PRIMARY_IP最近主连接的远端IP服务器地址data-info.primary.remote_ipCURLINFO_LOCAL_IP最近主连接的本地IP本机出口地址data-info.primary.local_ip从 lib/getinfo.c 中可以看到两者的取值逻辑case CURLINFO_PRIMARY_IP: /* Return the ip address of the most recent (primary) connection */ *param_charp >#include curl/curl.h CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_LOCAL_IP, char **ip);调用约定传入一个char *的指针的指针char **。libcurl 会把指向字符串的指针写入该位置返回的字符串是null 结尾的 IP 地址文本启用了 IPv6 时该字符串可以是 IPv6 地址若查询时该 handle 尚无可用的本地 IP例如从未建连、或协议不支持如 TFTPip指针会被置为 NULL——示例代码中对ip判空正是为此。返回值语义curl_easy_getinfo()返回CURLcodeCURLE_OK0表示成功非零表示出错例如传入了不支持的 info 类型会返回CURLE_UNKNOWN_OPTION见 lib/getinfo.c 的default分支。内存生命周期必须遵守的规则原文档强调了两点它们是使用本接口的核心纪律指针指向的内存会被复用你拿到的是 handle 内部私有缓冲区下一次请求会覆盖它。想长期保存必须立即自行strdup/拷贝。绝不能 free 它这块内存由 libcurl 持有在调用curl_easy_cleanup()时随 handle 自动释放。手动释放会引发 double-free。三、官方示例完整可运行的 C 程序以下为文档 CURLINFO_LOCAL_IP.md 中的 EXAMPLE 原文完整保留int main(void) { char *ip; CURLcode result; CURL *curl curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, https://example.com); /* Perform the transfer */ result curl_easy_perform(curl); /* Check for errors */ if((result CURLE_OK) !curl_easy_getinfo(curl, CURLINFO_LOCAL_IP, ip) ip) { printf(Local IP: %s\n, ip); } /* always cleanup */ curl_easy_cleanup(curl); }逐行说明curl_easy_perform()之后才查询——本地 IP 只在连接实际建立后才存在查询必须晚于传输或至少在连接完成之后条件!curl_easy_getinfo(...) ip同时防御了两种情况查询本身失败返回非 0、以及ip为 NULL无可用本地地址最后curl_easy_cleanup()统一释放 handle 及其内部字符串印证“无需也不得手动释放”的规则。编译方式gcc main.c -lcurl -o main链接 libcurl 库需要本机已安装 libcurl 开发头文件与库。四、源码级原理本地 IP 是如何被采集的1. 采集点socket 过滤器中的set_local_ip()libcurl 将 socket 层逻辑抽象为 cfilter连接过滤器体系。本地 IP 的真正来源是 lib/cf-socket.c 中的set_local_ip()static void set_local_ip(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx cf-ctx; ctx-ip.local_ip[0] 0; ctx-ip.local_port 0; #ifdef HAVE_GETSOCKNAME if((ctx-sock ! CURL_SOCKET_BAD) !(data-conn-scheme-protocol CURLPROTO_TFTP)) { /* TFTP does not connect, so it cannot get the IP like this */ struct Curl_sockaddr_storage ssloc; curl_socklen_t slen sizeof(struct Curl_sockaddr_storage); memset(ssloc, 0, sizeof(ssloc)); if(getsockname(ctx-sock, (struct sockaddr *)ssloc, slen)) { ... } else { CURLcode result sockaddr2string((struct sockaddr *)ssloc, slen, ctx-ip.local_ip, ctx-ip.local_port); ... } } #endif }要点底层依赖 POSIXgetsockname(2)读取已连接 socket 的本地端点含源 IP 与本地端口再经sockaddr2string()转成文本存入ctx-ip.local_ip/ctx-ip.local_port因此只有建立真实套接字连接后才有值——这也解释了为何文档要求“查询的是 most recent connection”的信息TFTP 被显式排除TFTP 走 UDP 且 libcurl 对其不做常规 connect 语义源码注释写明 “TFTP does not connect, so it cannot get the IP like this”所以 TFTP 传输后CURLINFO_LOCAL_IP可能为 NULL。set_local_ip()在 lib/cf-socket.c 中被多处调用如 accept 处理、连接完成路径等保证 TCP 与 QUIC 等传输建立时都会刷新该值。2. 数据结构struct ip_quadruple本地/远端地址与端口统一存放在 lib/urldata.h 定义的“IP 四元组”中struct ip_quadruple { char remote_ip[MAX_IPADR_LEN]; char local_ip[MAX_IPADR_LEN]; uint16_t remote_port; uint16_t local_port; uint8_t transport; /* TRNSPRT_TCP / UDP / QUIC / UNIX ... */ };transport字段标记该连接属于 TCP、UDP、QUIC 还是 Unix socket宏CUR_IP_QUAD_HAS_PORTS()据此判断端口信息是否有效。这说明CURLINFO_LOCAL_IP的设计是传输无关的与文档声明的 TCP/QUIC 适用性一致。3. 写入 handle 信息区连接就绪后四元组整体复制到 easy handle 的信息区。lib/cf-socket.c 中data-info.primary ctx-ip;此后curl_easy_getinfo(curl, CURLINFO_LOCAL_IP, ip)直接从data-info.primary.local_ip返回指针lib/getinfo.c与第二节分析的“内存复用、不可 free”完全吻合——它指向的就是 handle 结构内部的固定缓冲区。4. 同一数据的另一消费方pre-request 回调除了 getinfolocal_ip还在连接前回调中被使用。lib/multi.c 中调用用户注册的CURLOPT_FPREREQ回调时会传入data-info.primary.local_ipprereq_rc >char *lip, *rip; long lport, rport; if(!curl_easy_getinfo(curl, CURLINFO_LOCAL_IP, lip) !curl_easy_getinfo(curl, CURLINFO_LOCAL_PORT, lport) !curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP, rip) !curl_easy_getinfo(curl, CURLINFO_PRIMARY_PORT, rport) lip rip) { printf(%s:%ld - %s:%ld\n, lip, lport, rip, rport); }注意与CURLINFO_LOCAL_IP相同的内存纪律对CURLINFO_PRIMARY_IP同样适用——都是 handle 内部私有内存用完即拷切勿 free。六、使用要点与常见陷阱查询时机必须在连接建立之后通常curl_easy_perform()返回后查询未连接或连接失败时可能得到 NULL。IPv6 字面量启用 IPv6 并发建连Happy Eyeballs时返回串可能是 IPv6 文本若要在日志中拼接端口建议按[%s]:%ld格式处理。内存复用同一个 handle 连续发起多个请求复用 handle 的循环时上一次查询返回的指针内容会被新请求覆盖——如需留痕立即拷贝。不得释放返回指针由 libcurl 管理随curl_easy_cleanup()释放手动free()属未定义行为。NULL 可能合法无连接、TFTP 等场景下ip为 NULL代码必须判空官方示例已示范。仅反映“最近主连接”若发生重定向导致多个连接得到的是最后一次主连接的本地端信息。七、小结CURLINFO_LOCAL_IP是 libcurl 连接诊断三件套本地 IP/本地端口/远端 IP之一API 面curl_easy_getinfo(CURL *handle, CURLINFO_LOCAL_IP, char **ip)7.21.0 起可用适用于 TCP 与 QUIC实现面由 lib/cf-socket.c 的set_local_ip()基于getsockname()采集经 lib/urldata.h 的struct ip_quadruple承载最终在 lib/getinfo.c 暴露纪律面字符串可被复用、绝不可 free、使用前判空、需要时立即拷贝。遵循以上规则即可在代理、多出口、双栈并发等复杂网络环境中可靠地把“本机实际出口地址”这一关键信息纳入日志与监控体系。【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考