I am using the :inet.peername
function. Most of the time it returns {:ok, {ip, port}}
but sometimes it fails with {:error, :einval}
.
Under which conditions do the failure happens? Can I do something to avoid them?
I have looked at the implementation of the function, it is C code. but while I have a good understanding of C the function is rather complex and not intelligible for me.
The code of Erlang is using a port:
case INET_REQ_PEER: { /* get peername */
char tbuf[sizeof(inet_address)];
inet_address peer;
inet_address* ptr;
unsigned int sz;
DEBUGF(("inet_ctl(%ld): PEER\r\n", (long)desc->port));
if (!(desc->state & INET_F_ACTIVE))
return ctl_error(ENOTCONN, rbuf, rsize);
if ((ptr = desc->peer_ptr) != NULL) {
sz = desc->peer_addr_len;
}
else {
ptr = &peer;
sz = sizeof(peer);
sys_memzero((char *) &peer, sz);
if (IS_SOCKET_ERROR
(sock_peer
(desc->s, (struct sockaddr*)ptr, &sz)))
return ctl_error(sock_errno(), rbuf, rsize);
}
if (inet_get_address(tbuf, ptr, &sz) < 0)
return ctl_error(EINVAL, rbuf, rsize);
return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize);
}
static int inet_get_address(char* dst, inet_address* src, unsigned int* len)
{
/* Compare the code with inet_address_to_erlang() */
int family;
short port;
family = src->sa.sa_family;
if ((family == AF_INET) && (*len >= sizeof(struct sockaddr_in))) {
dst[0] = INET_AF_INET;
port = sock_ntohs(src->sai.sin_port);
put_int16(port, dst+1);
sys_memcpy(dst+3, (char*)&src->sai.sin_addr, sizeof(struct in_addr));
*len = 3 + sizeof(struct in_addr);
return 0;
}
#if defined(HAVE_IN6) && defined(AF_INET6)
else if ((family == AF_INET6) && (*len >= sizeof(struct sockaddr_in6))) {
dst[0] = INET_AF_INET6;
port = sock_ntohs(src->sai6.sin6_port);
put_int16(port, dst+1);
sys_memcpy(dst+3, (char*)&src->sai6.sin6_addr,sizeof(struct in6_addr));
*len = 3 + sizeof(struct in6_addr);
return 0;
}
#endif
#ifdef HAVE_SYS_UN_H
else if (family == AF_UNIX) {
size_t n, m;
if (*len < offsetof(struct sockaddr_un, sun_path)) return -1;
n = *len - offsetof(struct sockaddr_un, sun_path);
if (255 < n) return -1;
m = my_strnlen(src->sal.sun_path, n);
#ifdef __linux__
/* Assume that the address is a zero terminated string,
* except when the first byte is \0 i.e the string length is 0,
* then use the reported length instead.
* This fix handles Linux's nonportable
* abstract socket address extension.
*/
if (m == 0) m = n;
#endif
dst[0] = INET_AF_LOCAL;
dst[1] = (char) ((unsigned char) m);
sys_memcpy(dst+2, src->sal.sun_path, m);
*len = 1 + 1 + m;
return 0;
}
#endif
else if (family == AF_UNSPEC) {
dst[0] = INET_AF_UNSPEC;
*len = 1;
}
else {
dst[0] = INET_AF_UNDEFINED;
*len = 1;
}
return -1;
}