C++ - 如何从struct sockaddr中获取IP地址和端口

5

我正在尝试使用libmilter开发邮件过滤器。因此,我需要定义一些回调函数。目前,我遇到了connect()函数的问题。其声明如下:

sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, struct sockaddr *hostaddr);

当客户端连接到milter时,将调用此回调函数。现在我想记录主机名、IP和端口。对于主机名来说很容易。但是我无法通过hostaddr使其正常工作。
我在StackOverflow上找到了一些类似的文章,但没有一个适用于我。看起来我必须将结构体转换为sockaddr_in或sockaddr_in6,但我遇到了编译器问题,如“member access into incomplete type 'struct sockaddr_in'”。
这是我尝试过的内容:
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
string ipandport;

switch (hostaddr->sa_family) {
    case AF_INET:
        sin = (struct sockaddr_in *) hostaddr;
        unsigned char *ip = (unsigned char *)&sin->sin_addr.s_addr;
        break;
    case AF_INET6:
        // TODO
        break;
    default:
        ipandport = "unknown";
}

我从未想过获取这样琐碎的信息会如此困难 :-)

这句话与IT技术无关。
4个回答

4

inet_ntop用于将二进制转换为字符串,而不是inet_pton

$ man inet_ntop
...
This  function  converts  the  network address structure src in the af address family into a character string.  The
resulting string is copied to the buffer pointed to by dst, which must be a non-null pointer.  The caller specifies
the number of bytes available in this buffer in the argument size.

例子:

#include <arpa/inet.h>
...
char address[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &my_sockaddr_in.sin_addr, address, sizeof(address));

3

以下是未经编译测试的IPv4示例。IPv6几乎相同,只需调整使用的常量定义(请阅读inet_ntop手册)。

#include <arpa/inet.h>

void print_ipv4(struct sockaddr *s)
{
 struct sockaddr_in *sin = (struct sockaddr_in *)s;
 char ip[INET_ADDRSTRLEN];
 uint16_t port;
 
 inet_ntop (AF_INET, sin->sin_addr, ip, sizeof (ip));
 port = htons (sin->sin_port);
 
 printf ("host %s:%d\n", ip, port);
}

谢谢。在等待答案的时候,我找到了另一种解决方案。 - Christian Rößner
1
应该使用ntohs而不是htons吗? - sorush-r
ntohs()和htons()的作用完全相同。它们要么什么也不做,要么交换高低字节。大多数套接字实现使用宏将一个别名为另一个。 - Stian Skjelstad
1
奇怪的是,我的 inet_pton 原型(如 man-pages 5.02,2019-03-06 中所列)不包括您在那里使用的 sizeof 的第四个参数。 - i336_
1
这是错误的方法。inet_pton将字符串转换为struct sockaddr_in,需要使用inet_ntopstruct sockaddr_in转换为字符串。(请参见我的答案。) - fadedbee

1

我找到了另一个答案。在发帖后,我再次搜索并找到了这个答案,最终对我有效:

sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, struct sockaddr *hostaddr) {
assert(ctx != NULL);
assert(hostaddr != NULL);

string ipandport;

char clienthost[NI_MAXHOST];
char clientport[NI_MAXSERV];
int result = getnameinfo(hostaddr, sizeof(*hostaddr),
                         clienthost, sizeof(clienthost),
                         clientport, sizeof (clientport),
                         NI_NUMERICHOST | NI_NUMERICSERV);

if(result != 0)
    ipandport = "unknown";
else
{
    if (hostaddr->sa_family == AF_INET)
        ipandport = string(clienthost) + ":" + string(clientport);
    else
        ipandport = "[" + string(clienthost) + "]:" + string(clientport);
}

if (hostname == nullptr)
    hostname = const_cast<char *>("unknown");

Client *con = new Client(string(hostname), ipandport);

// Store new client data
smfi_setpriv(ctx, static_cast<void *>(con));

cout << "id=" << con->getId()
        << " connect from " << con->getHostname()
        << " IP and port " << con->getIPandPort() << endl;

return SMFIS_CONTINUE;
}

0
struct sockaddr_in  client, server;
SOCKET s , new_socket;
s = socket(AF_INET , SOCK_STREAM , 0 )

server.sin_family       = AF_INET;
server.sin_addr.s_addr  = INADDR_ANY;
server.sin_port         = htons( 8888 );

bind(s ,(struct sockaddr *)&server , sizeof(server))
//pending connection in queue is 1
listen(s , 1);

new_socket = accept(s , (struct sockaddr *)&client, &c);
printf ("client remote port: %i\n", ntohs(client.sin_port) );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接