gvanem / Watt-32

Watt-32 TCP/IP library and samples.
https://www.watt-32.net/
18 stars 8 forks source link

`connect()`: Allow non-TCP sockets to rebind `remote_addr` #84

Closed jwt27 closed 1 year ago

jwt27 commented 1 year ago

Noticed this while working on #80. For non-SOCK_STREAM sockets, it should be possible to rebind the destination address by calling connect() again.

https://man7.org/linux/man-pages/man2/connect.2.html

Some protocol sockets (e.g., UNIX domain stream sockets) may successfully connect() only once.

Some protocol sockets (e.g., datagram sockets in the UNIX and Internet domains) may use connect() multiple times to change their association.

Not tested, but it should work since I just copied the code from transmit.c.

jwt27 commented 1 year ago

Weird error on CI. Why is grab_localport undefined on Watcom model=large but not in other configurations? I'm not even 100% sure if grab_localport is needed here though.

gvanem commented 1 year ago

Watcom/Large-model does not have grab_localport() since it's inside a USE_BSD_API section in ports.c. Just add a do-nothing version for this:

--- a/src/ports.c
+++ b/src/ports.c
@@ -143,6 +143,13 @@ int grab_localport (WORD port)
   FD_SET (port, lport_inuse);
   return (rc);
 }
+
+#else
+int grab_localport (WORD port)
+{
+  ARGUSED (port);
+  return (-1);
+}
 #endif
jwt27 commented 1 year ago

Ah that makes sense, thanks. But do we need grab_localport() there in the native API? I think only if a program uses both native and BSD APIs (probably rare).

gvanem commented 1 year ago

But do we need grab_localport() there in the native API?

Probably not.

jwt27 commented 1 year ago

But do we need grab_localport() there in the native API?

Probably not.

I'll remove it then.

Also I realized, it wouldn't be too difficult now to implement non-blocking ARP lookups for UDP sockets. Might be worth looking into.

gvanem commented 1 year ago

BTW, these Cygwin warnings:

In function 'gethostbyaddr6_internal',
    inlined from 'gethostbyaddr6' at gethost6.c:434:7:
../inc/netinet/in.h:444:10: warning: '__builtin_memcmp_eq' reading 32 bytes from a region of size 16 [-Wstringop-overflow=]
  444 |         (memcmp ((void*)(a), (void*)(b), sizeof(struct in6_addr)) == 0)
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

has been bugging me. I fail to understand what the issue here. No warnings like this for MinGW (!).

jwt27 commented 1 year ago

I think I see it:

  struct in6_addr {
         union {
           u_char  _S6_u8[16];
           u_short _S6_u16[8];
           u_long  _S6_u32[4];
         } _S6_un;
       };

sizeof(u_long) will be 8 bytes on x86_64. Better option there is to use <stdint.h> types.

gvanem commented 1 year ago

sizeof(u_long) will be 8 bytes on x86_64.

Ah, yes. Effin LP64 platform.

gvanem commented 1 year ago

it wouldn't be too difficult now to implement non-blocking ARP lookups for UDP sockets.

ARP should be pretty fast. Later perhaps. I want to merge this first if you're comfortable with it.

jwt27 commented 1 year ago

it wouldn't be too difficult now to implement non-blocking ARP lookups for UDP sockets.

ARP should be pretty fast. Later perhaps. I want to merge this first if you're comfortable with it.

Sure, but I haven't done much testing yet, and I don't have any "real-world" UDP code to try with. Any suggestions?

gvanem commented 1 year ago

Any suggestions?

C-Ares perhaps (all about UDP you know). I made this djgpp Makefile.dj for it long ago. And with these patches: c-ares.diff.txt

Here bin\adig -s1.1.1.1 -t mx apple.com gives:

id: 25561
flags: qr rd ra
opcode: QUERY
rcode: NOERROR
Questions:
        apple.com      .                MX
Answers:
        apple.com      .        3600    MX      20      mx-in-rno.apple.com.
        apple.com      .        3600    MX      20      mx-in-mdn.apple.com.
        apple.com      .        3600    MX      10      mx-in.g.apple.com.
        apple.com      .        3600    MX      20      mx-in-hfd.apple.com.
        apple.com      .        3600    MX      20      mx-in-vib.apple.com.

(A Winsock2 version)

jwt27 commented 1 year ago

C-Ares works for me, thanks. I used the current version from git, and configured with --enable-nonblocking to double-check that too.

I've been trying this test case, sending messages with nc -u 10.0.0.x 12345 from different machines. No issues. I think it's safe to merge.

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
  const int fd = socket(PF_INET, SOCK_DGRAM, 0);
  if (fd < 0)
  {
    perror("socket()");
    return 1;
  }

  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = INADDR_ANY;
  addr.sin_port = htons(12345);

  if (bind(fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)))
  {
    perror("bind()");
    goto fail;
  }

  for (unsigned i = 0; i < 10; ++i)
  {
    char buf[128];
    socklen_t addrlen = sizeof (struct sockaddr_in);
    socklen_t n = recvfrom(fd, buf, 128, 0, (struct sockaddr*)&addr, &addrlen);
    if (n < 0)
    {
      perror("recvfrom()");
      goto fail;
    }
    fprintf(stderr, "from %s: ", inet_ntoa(addr.sin_addr));
    fwrite(buf, 1, n, stderr);

    if (connect(fd, (struct sockaddr*)&addr, addrlen))
    {
      perror("connect()");
      goto fail;
    }

    if (send(fd, "hello\n", 7, 0) < 0)
    {
      perror("send()");
      goto fail;
    }
  }

  close(fd);
  return 0;

fail:
  printf("fail");
  close(fd);
  return 1;
}
gvanem commented 1 year ago

No issues. I think it's safe to merge.

Very good. Since here a Watt-32-version\bin\adig.exe -d -s1.1.1.1 -t mx apple.com hangs even after receiving a good response. From wattcp.sk:

00:00:00.008: socket: fam:AF_INET type:DGRAM, proto IPPROTO_UDP, 3
00:00:00.008: ioctlsocket:3, file cmd: FIONBIO 1
00:00:00.008: connect:3, auto-binding, src/dest ports: 1355/53
00:00:00.021: send:3, len=27, 1.1.1.1 (53) / UDP 
00:00:00.037: select_s: n=0-3, rw-, timeout 4.969000s, cnt=1 (1r/0w/0x)
00:00:00.052: recvfrom:3, 2: remote: 1.1.1.1 (53), len=155
00:00:00.068: recvfrom:3, EWOULDBLOCK, len=-1
00:00:00.083: select_s: n=0-3, rw-, timeout 4.922000s, timeout!: 4.937033s
00:00:05.020: send:3, len=27, 1.1.1.1 (53) / UDP 
...

Calling select_s() over and over again. (this is a Win-version since I have no way to run DOS-programs).

Merging and many thanks again!

jwt27 commented 1 year ago

Very good. Since here a Watt-32-version\bin\adig.exe -d -s1.1.1.1 -t mx apple.com hangs even after receiving a good response. From wattcp.sk:

00:00:00.008: socket: fam:AF_INET type:DGRAM, proto IPPROTO_UDP, 3
00:00:00.008: ioctlsocket:3, file cmd: FIONBIO 1
00:00:00.008: connect:3, auto-binding, src/dest ports: 1355/53
00:00:00.021: send:3, len=27, 1.1.1.1 (53) / UDP 
00:00:00.037: select_s: n=0-3, rw-, timeout 4.969000s, cnt=1 (1r/0w/0x)
00:00:00.052: recvfrom:3, 2: remote: 1.1.1.1 (53), len=155
00:00:00.068: recvfrom:3, EWOULDBLOCK, len=-1
00:00:00.083: select_s: n=0-3, rw-, timeout 4.922000s, timeout!: 4.937033s
00:00:05.020: send:3, len=27, 1.1.1.1 (53) / UDP 
...

Calling select_s() over and over again. (this is a Win-version since I have no way to run DOS-programs).

Interesting. I had to reconfigure with CPPFLAGS=-DWATT32 to make -d work. If it's any help, here is my wattcp.sk for the same command.

It doesn't seem to use non-blocking mode (does use still-broken fcntl() on djgpp). But as you can see, the reconnect works now:

1193:02:47.268: socket: fam:AF_INET type:DGRAM, proto IPPROTO_UDP, 8
1193:02:47.268: _fsext_demux: fd 8s, func 7 = "fcntl", cmd 00000003, arg 000006E0
1193:02:47.268: fcntlsocket:8, , F_GETFL: blocking
1193:02:47.268: _fsext_demux: fd 8s, func 7 = "fcntl", cmd 00000006, arg 000006E0
1193:02:47.268: fcntlsocket:8, , F_SETFL: blocking
1193:02:47.268: connect:8, auto-binding, src/dest ports: 1360/53
1193:02:46.627: send:8, len=27, flags 0x80
1193:02:46.627:   connect:8, reconnecting, 1.1.1.1 (53) / UDP 
1193:02:46.628: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.628: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.629: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.629: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.629: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.629: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.630: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.630: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.630: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.631: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.631: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.631: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.632: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.632: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.632: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.632: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.633: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.633: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.633: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.633: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.634: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.634: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
1193:02:46.634: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 00088B80
1193:02:46.635: select_s: n=0-8, rwx, timeout 0.000000s, cnt=2 (1r/1w/0x)
1193:02:46.636: recvfrom:8, 2: remote: 1.1.1.1 (53), len=155
1193:02:46.650: _fsext_demux: fd 8s, func 6 = "close", cmd 00172614, arg 00000000
1193:02:46.650: close_s:8No ETHERS line found in WATTCP.CFG

Local ports still in use:
gvanem commented 1 year ago

I had to reconfigure with CPPFLAGS=-DWATT32

Can you post the CFLAGS used?

jwt27 commented 1 year ago

I had to reconfigure with CPPFLAGS=-DWATT32

Can you post the CFLAGS used?

I had them unset so it used some default, probably -g -O2. edit: from the makefile: CFLAGS = -g0 -O2 -Wno-system-headers

jwt27 commented 1 year ago

It doesn't seem to use non-blocking mode (does use still-broken fcntl() on djgpp).

Aaah, just realized didn't use my patched libc. Now it does set non-blocking mode. Doesn't make any difference though since ARPs still always block on UDP.

1193:02:47.268: socket: fam:AF_INET type:DGRAM, proto IPPROTO_UDP, 8
1193:02:47.271: _fsext_demux: fd 8s, func 7 = "fcntl", cmd 00000003, arg 00000000
1193:02:47.271: fcntlsocket:8, , F_GETFL: blocking
1193:02:47.271: _fsext_demux: fd 8s, func 7 = "fcntl", cmd 00000006, arg 00002000
1193:02:47.271: fcntlsocket:8, , F_SETFL: non-blocking
1193:02:47.271: connect:8, auto-binding, src/dest ports: 1118/53
00:00:00.330: send:8, len=27, flags 0x80
00:00:00.330:   connect:8, reconnecting, 1.1.1.1 (53) / UDP 
00:00:00.332: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.332: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.332: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.332: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.333: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.333: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.333: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.333: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.334: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.334: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.335: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.335: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.335: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.335: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.336: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.336: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.336: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.336: select_s: n=0-8, rwx, timeout 0.000000s, cnt=1 (0r/1w/0x)
00:00:00.337: _fsext_demux: fd 8s, func 5 = "ready", cmd 00000006, arg 0009C400
00:00:00.337: select_s: n=0-8, rwx, timeout 0.000000s, cnt=2 (1r/1w/0x)
00:00:00.338: recvfrom:8, 2: remote: 1.1.1.1 (53), len=155
00:00:00.352: _fsext_demux: fd 8s, func 6 = "close", cmd 00172614, arg 00000000
00:00:00.352: close_s:8No ETHERS line found in WATTCP.CFG

Local ports still in use:

edit: Also, strange timer overflow there.