libimobiledevice / libusbmuxd

A client library to multiplex connections from and to iOS devices
https://libimobiledevice.org
GNU Lesser General Public License v2.1
572 stars 270 forks source link

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

Open pwn0rz opened 2 months ago

pwn0rz commented 2 months ago

This commit https://github.com/libimobiledevice/libusbmuxd/commit/303ece5fa462713552e0013b48b66a08955a12d4 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.

nikias commented 2 months ago

Oh... the reason for the changes was this: https://github.com/libimobiledevice/libimobiledevice-glue/issues/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.

nikias commented 2 months ago

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?

pwn0rz commented 2 months ago

That looks great 💯