1

How to reset an accepted socket in application level either with IO::Socket::INET in perl or in C?

There is a programm binding, listening on a TCP port, and accepts a client connection, after that it reads and writes some data. If I simply close or shutdown the socket, TCP layer gracefully terminates (with FIN packet), rather than, I'd generate an RST packet.

bandie
  • 173
  • 1
  • 11
  • AFAIK, there's no way to do so using the socket you've been sending and receiving on. You could always handcraft your own RST packet and send it to your peer, but you'd need to create a separate raw packet socket to send it. Linux also has a `SOCK_DESTROY` function (conditionally included in kernel) that can be used by an appropriately privileged process to close an arbitrary connection, but it's also completely out-of-band from you application socket. Why do you want to terminate with RST anyway? – Gil Hamilton Jul 21 '16 at 16:03
  • 2
    You can try SO_LINGER socket option with zero timeout, [according to some resources](http://stackoverflow.com/questions/3757289/tcp-option-so-linger-zero-when-its-required) it will generate RST packet instead of FIN. – Slava Bacherikov Jul 21 '16 at 16:34
  • @SlavaBacherikov thanks, it works in code: `$socket=IO::Socket::INET->new(Listen=1,ReuseAddr=>1,LocalPort=>...,...); $client=$socket->accept(); $client->sockopt(SO_LINGER, pack('II', 1, 0)); close $client`. curl and wget always handle this condition with return code 56 and 4 respectively. however netcat nondeterministically somewhen detects "Connection refused by peer" but sometimes not. – bandie Jul 22 '16 at 07:27
  • 1
    `close` will produce RST if the receive buffer has data that has not yet been read by your application. – kasperd Sep 16 '17 at 08:55

1 Answers1

2

You didn't specify the exact OS you are using. I found that Linux does have an API call which will reset a TCP connection, I have no idea how portable it is. The way to do it is to use the connect system call on the already connected socket but this time with family AF_UNSPEC.

After you have reset a socket that way it is even possible to connect the socket again with another connect call.

int main(int argc, char** argv)
{
  int fd = socket(AF_INET6, SOCK_STREAM, 0);

  while (1) {
    struct sockaddr_in6 sockaddr = {
      .sin6_family = AF_INET6,
      .sin6_port = htons(80),
      .sin6_flowinfo = 0,
      .sin6_scope_id = 0,
    };
    struct timespec s = {
      .tv_sec = 2,
      .tv_nsec = 0,
    };

    /* Connect to port 80 on localhost */
    inet_pton(AF_INET6, "::1", &sockaddr.sin6_addr.s6_addr);
    connect(fd, (struct sockaddr*)&sockaddr,sizeof(sockaddr));
    nanosleep(&s, NULL);

    /* Reset previously connected socket */
    sockaddr.sin6_family = AF_UNSPEC;
    connect(fd, (struct sockaddr*)&sockaddr,sizeof(sockaddr));
    nanosleep(&s, NULL);
  }
}
kasperd
  • 1,952
  • 1
  • 20
  • 31
  • 2
    great. I tested your `connect AF_UNSPEC` solution also on ipv4 listening scenario, in this case I have to re-connect not the `listen`ing socket but the one returned by `accept`. – bandie Sep 18 '17 at 12:32