libmill icon indicating copy to clipboard operation
libmill copied to clipboard

udpsend error in mac os

Open GeekCookie opened this issue 8 years ago • 10 comments

When invoke udpsend method, the error occurs. error code is 22, means invalid argument.

GeekCookie avatar May 26 '17 08:05 GeekCookie

Can you give a concrete example?

sustrik avatar May 26 '17 10:05 sustrik

I have tested this on macOS 10.13 and found that if you use IPADDR_PREF_IPV6 when creating iplocal in dual-stack environment, sending to IPv4 address would cause errno 22. Changing iplocal to IPADDR_PREF_IPV4 works.

Didn't test with other configuration: IPv4 only environment or sending to IPv6 address.

Here is a simple example:

#include <libmill.h>
#include <stdio.h>

int main() {
	ipaddr local = iplocal(NULL, 0, IPADDR_PREF_IPV6);
	udpsock s = udplisten(local);
	ipaddr addr = ipremote("8.8.8.8", 60000, 0, -1);
	char* data[10] = {0};
	udpsend(s, addr, data, 10);
	printf("%d\n", errno);
}

Program would print out 22 in this case. If you change IPADDR_PREF_IPV6 to IPADDR_PREF_IPV4 in line 5 program would print out 0.

HarukaMa avatar Oct 24 '17 18:10 HarukaMa

It looks like the error is coming from here: https://github.com/sustrik/libmill/blob/master/udp.c#L104

22 means EINVAL

The man page description doesn't seem to apply: https://www.unix.com/man-page/osx/2/sendto/

Try checking whether udplisten succeeds.

sustrik avatar Oct 24 '17 19:10 sustrik

Upon more testing I've found that the above code works on ipv4-only interfaces such as one of my VPN tunnel utun2, but it fails on interfaces with ipv6 address even if it is link local address (fe80::/64).

udplisten succeeded nonetheless.

HarukaMa avatar Oct 25 '17 15:10 HarukaMa

Can you trace the execution to udp.c:104 then check the argument values to sendto() function as well as the result value and errno?

sustrik avatar Oct 25 '17 18:10 sustrik

(gdb) r
Starting program: /Users/MrX/test2
[New Thread 0x1403 of process 43440]
warning: unhandled dyld version (15)

Thread 2 hit Breakpoint 1, main () at test2.c:9
9		udpsend(s, addr, data, 10);
(gdb) s
mill_udpsend_ (s=0x100200060, addr=..., buf=0x7ffeefbff600, len=10) at udp.c:103
103	    struct sockaddr *saddr = (struct sockaddr*) &addr;
(gdb) s
104	    ssize_t ss = sendto(s->fd, buf, len, 0, saddr, saddr->sa_family ==
(gdb) p s->fd
$1 = 3
(gdb) p buf
$2 = (const void *) 0x7ffeefbff600
(gdb) p len
$3 = 10
(gdb) p saddr->sa_family
$4 = 2 '\002'
(gdb) s
106	    int _errno = errno;
(gdb) s
107	    if(mill_fast(ss == (ssize_t)len)) {
(gdb) p ss
$5 = -1
(gdb) p _errno
$6 = 22

Couldn't check errno in gdb so I modified udp.c a little. Seems that saddr->sa_family is AF_INET.

HarukaMa avatar Oct 26 '17 20:10 HarukaMa

sa_family is AF_INET because you pass in IPv4 address (8.8.8.8)

Possibly, the problem is that socket, although using "bind to all interfaces" option only binds to IPv6 interfaces and as such it complains when trying to send to IPv4 address.

sustrik avatar Oct 27 '17 05:10 sustrik

I have tried to bind to dual-stack interfaces by using iplocal("en0", 0, IPADDR_PREF_IPV6); but still the same error. en0 have both ipv4 and ipv6 addresses.

HarukaMa avatar Oct 27 '17 12:10 HarukaMa

What happens if you try to send the packet to an acutal IPv6 address?

sustrik avatar Oct 27 '17 15:10 sustrik

The packet could be sent successfully with no error. The packet was observed on tcpdump.

HarukaMa avatar Oct 27 '17 16:10 HarukaMa