libimobiledevice/libusbmuxd

socket_create only binds to IPv6 any instead of IPv4 and IPv6 loopback

Opened this issue · 3 comments

This commit 303ece5 uses socket_create to bind and listen address, however by default it seems to bind to IPv6 address only on my mac device.

IPv4 loopback

$ iproxy -s '127.0.0.1' 2222:22

$ sudo lsof -P -i tcp:2222
COMMAND  PID  USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
iproxy  4759  user    3u  IPv4 0x920c56b46d641c07      0t0  TCP localhost:2222 (LISTEN)

# ssh ok

IPv6 loopback

$ iproxy -s '::1' 2222:22

$ sudo lsof -P -i tcp:2222
COMMAND  PID  USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
iproxy  4898  user    3u  IPv6 0x920c56b935c599b7      0t0  TCP localhost:2222 (LISTEN)

# ssh ok

Default: IPv6 any

$ iproxy 2222:22

$ sudo lsof -P -i tcp:2222
COMMAND  PID  USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
iproxy  4923  user    4u  IPv6 0x920c56b935c599b7      0t0  TCP *:2222 (LISTEN

$ ssh root@127.0.0.1 -p 2222
ssh: connect to host 127.0.0.1 port 2222: Connection refused

$ ssh root@localhost -p 2222
# success

Env

$ sw_vers
ProductName:        macOS
ProductVersion:        14.4.1
BuildVersion:        23E224

According to the code at https://github.com/libimobiledevice/libimobiledevice-glue/blob/fde8946a3988790fd5d3f01fc0a1fd43609ab1d1/src/socket.c#L534

On my device, I will get 2 address which are [::]:2222 and 0.0.0.0:2222 by calling getaddrinfo, however due to the break, the second address will not be bind, which does not match the help (127.0.0.1) / commit (ipv4+ipv6) description.

Oh... the reason for the changes was this: libimobiledevice/libimobiledevice-glue#40
I guess I misunderstood (and subsequently failed to properly test) what NULL would mean for getaddrinfo, and assumed we'd get a single socket capable of listening to both IPv4 and IPv6, but I was wrong apparently... Now the break obviously prevents creating further sockets, and that makes perfect sense, since the function create_socket returns a single socket and I don't see a way to change it. However, I can just change the code in iproxy to the old behavior, creating two listening sockets.

Ok I know how to fix this, but I am not sure if this is the best way to do it. In https://github.com/libimobiledevice/libimobiledevice-glue/blob/fde8946a3988790fd5d3f01fc0a1fd43609ab1d1/src/socket.c#L517
I am setting IPV6_V6ONLY to 1. Setting this to NO would end up creating a single socket that would also be reachable with IPv4.
I tried this:

diff --git a/src/socket.c b/src/socket.c
index d4dedd1..3375e5b 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -463,6 +463,7 @@ int socket_create(const char* addr, uint16_t port)
 {
        int sfd = -1;
        int yes = 1;
+       int no = 0;
        struct addrinfo hints;
        struct addrinfo *result, *rp;
        char portstr[8];
@@ -514,7 +515,7 @@ int socket_create(const char* addr, uint16_t port)

 #if defined(AF_INET6) && defined(IPV6_V6ONLY)
                if (rp->ai_family == AF_INET6) {
-                       if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&yes, sizeof(int)) == -1) {
+                       if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (addr) ? (void*)&yes : (void*)&no, sizeof(int)) == -1) {
                                perror("setsockopt() IPV6_V6ONLY");
                        }
                }

And it will make it work. It would set IPV6_V6ONLY to 0 only if addr is NULL but 1 in any other case. Not sure if it would be needed in any other case?

That looks great 💯