c - 如果服务器正在侦听 v6 套接字且 IPV6_V6 选项设置为 false,如何识别接收到的连接是 v4 还是 v6
问题描述
我创建了一个 v6 套接字,其中套接字选项 ipv6_v6only 设置为 false。我现在能够接受来自 v4 客户端和 v6 客户端的连接。此外,我想向套接字添加一些选项,例如 MD5 哈希,并将地址转换为可呈现的格式,所以我想知道是否有任何方法可以根据以下代码识别接收到的连接是 v4 还是 v6。
谢谢。
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define MAX 80
#define PORT 8085
#define SA struct sockaddr
// Function designed for chat between client and server.
void func(int sockfd)
{
char buff[MAX];
int n;
// infinite loop for chat
for (;;) {
bzero(buff, MAX);
// read the message from client and copy it in buffer
read(sockfd, buff, sizeof(buff));
// print buffer which contains the client contents
printf("From client: %s\t To client : ", buff);
bzero(buff, MAX);
n = 0;
// copy server message in the buffer
while ((buff[n++] = getchar()) != '\n')
;
// and send that buffer to client
write(sockfd, buff, sizeof(buff));
// if msg contains "Exit" then server exit and chat ended.
if (strncmp("exit", buff, 4) == 0) {
printf("Server Exit...\n");
break;
}
}
}
// Driver function
int main()
{
int sockfd, connfd, len;
struct sockaddr_in6 servaddr;
struct sockaddr_in cli;
int ret, flag;
pid_t childpid;
// socket create and verification
sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sockfd == -1) {
printf("socket creation failed...\n");
perror("socket()");
return EXIT_FAILURE;
}
else
printf("Socket successfully created..\n");
/* Set socket to reuse address */
flag = 1;
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
if(ret == -1) {
perror("setsockopt(SO_REUSEADDR)");
return EXIT_FAILURE;
}
/* Set socket to reuse address */
flag = 0;
ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag));
if(ret != 0) {
perror("setsockopt(IPV6_V6ONLY)");
return EXIT_FAILURE;
}
bzero(&servaddr, sizeof(servaddr));
// assign IP, PORT
servaddr.sin6_family = AF_INET6;
servaddr.sin6_addr = in6addr_any;
servaddr.sin6_port = htons(PORT);
// Binding newly created socket to given IP and verification
ret = bind(sockfd, (SA*)&servaddr, sizeof(servaddr));
if (ret == -1) {
printf("socket bind failed...\n");
perror("listen()");
close(sockfd);
return EXIT_FAILURE;
}
else
printf("Socket successfully binded..\n");
// Now server is ready to listen and verification
if ((listen(sockfd, 5)) != 0) {
printf("Listen failed...\n");
return EXIT_FAILURE;
}
else
printf("Server listening..\n");
for (;;) { //infinite loop
len = sizeof(cli);
// Accept the data packet from client and verification
connfd = accept(sockfd, (SA*)&cli, &len);
if (connfd < 0) {
printf("server accept failed...\n");
perror("accept()");
return EXIT_FAILURE;
}
printf("Connection accepted...\n");
***
* Here i want to differentiate if the received connection is v4 or v6 and
* based on that i want to do some operation like converting the client
* address to presentable format, also applying md5 hash to the session
***
if ( AF_INET ) {
char str_addr[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, &(cli.sin_addr), str_addr, sizeof(str_addr));
printf("New connection from: %s:%d ...\n", str_addr, ntohs(cli.sin_port));
} else if ( AF_INET6 ) {
char str_addr[INET6_ADDRSTRLEN] = {0};
inet_ntop(AF_INET6, &(cli.sin6_addr), str_addr, sizeof(str_addr));
printf("New connection from: %s:%d ...\n", str_addr, ntohs(cli.sin_port));
}
if ((childpid = fork()) == 0) { //creating a child process
close(sockfd);
//stop listening for new connections by the main process.
//the child will continue to listen.
//the main process now handles the connected client.
// Function for chatting between client and server
func(connfd);
}
// After chatting close the socket
close(connfd);
}
}
解决方案
如果您使用的是 IPv6 套接字,则本地和远程地址将始终为 type struct sockaddr_in6
,因此您对sockaddr_in
(以及同样sin_port
成员)的使用是错误的。在 v6 侦听套接字上接收 IPv4 连接发生在v4 映射地址上。IN6_IS_ADDR_V4MAPPED
宏 from可netinet/in.h
用于查询sin6_addr
客户端地址(注意 6!)是否是来自网络上的 v4 连接的 v4 映射地址。
然而,正如肖恩在评论中指出的那样:
如果您使用
getnameinfo()
将 sockaddr 转换为人类可读的地址,它会透明地处理所有不同的协议,因此您不必担心它们,顺便说一句
你可能没有理由真正关心。如果您使用现代 API 在sockaddr
对象和字符串之间进行转换,那么一切都会自动运行,您无需检查特定系列sockaddr
结构的成员。
推荐阅读
- kubernetes - Kubernetes kubeflow 扩展不起作用
- python - Tcl_AsyncDelete 错误。无法终止 Tk
- python - 在点之间画一条线以使用 python 进行比较、绘图和分析
- python - QCalendarWidget - 读取选定日期的背景颜色
- opencv - 前景的图像密度
- android - 使用会话从android向服务器POST数据
- string - 当对象创建具有连接时,String 保存在哪里?
- python - 如何将常用选项添加到子命令后*子命令的名称
- mysql - 如何制作一个像 sql 结果一样的数据透视表
- mongodb - 日期增加了一天