How can I refuse a socket connection in C?
Asked Answered
G

6

22

If I want to accept a connection I call accept, but how can I refuse a connection?

In a working socket echo client I have this if statement. In the echo server, how can I make the echo client reach this printf statement?

...
if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) { 
    printf("Connecting failed\n"); 
    return 1; 
}
...
Guzzle answered 16/5, 2013 at 14:55 Comment(10)
accept and close immediatelyTypewriter
@BartFriederichs: that is what I am doing currently, but is there a way to actually refuse it more explicitly?Guzzle
Based on what would you refuse? I'd say what you want is the job of a firewall. Most applications I know thta work with blacklisting or such would accept and close.Typewriter
Actually I want strictly one connection only on this particular port. Any other connection should ideally fail in a very obvious way.Guzzle
just accept only onceChapel
Set the backlog argument to listen(2).Ashanti
As @BartFriederichs says, the obvious way is close immediately, another solution is to use /etc/host.allow fileGinsburg
Don't accept at all and close the socket. The client will then trigger the timeout error on connect.Ibsen
Possible duplicate of Refusing connection from a hostFiliate
Pretty useful if you want to implement a TCP liveness probe...Prod
L
8

To my knowledge, that isn't how TCP works. The accept(..) call will always return with the client details. There is no way to peek at the connection and selectively refuse.

The way you are doing it now is actually the correct way: accept and then close. In case you have another message structure over and above this layer, you can create a custom "Reject message". This option completely depends on your use case.

In case you are looking for rejecting on the basis of IP address, its not within your apps domain. Its the job of your firewall (As @Bart Friederichs says). That way, the request will not even touch the TCP stack.


Actually I want strictly one connection only on this particular port. Any other connection should ideally fail in a very obvious way.

Do not let the accept call in your control flow. Only when you wait on accept will your program wait for a socket connection, never otherwise.

Lorettelorgnette answered 16/5, 2013 at 15:8 Comment(0)
T
13

To get the behavior you want (only accept one connection at a time, other clients attempting should get a failure), there are two choices.

  • You can close your listen socket after you have accepted a connection. Re-create your listen socket after the accepted connection closes.

  • You can close newly established connections if there is already a connection in progress. If you want the client to see a TCP reset, most TCP stacks will trigger one if you enable the linger option with a timeout of 0.

    struct linger lo = { 1, 0 };
    setsockopt(s, SOL_SOCKET, SO_LINGER, &lo, sizeof(lo));
    close(s);

Thilde answered 16/5, 2013 at 15:20 Comment(2)
Is the first option really used in practice? It might be better to keep the listening socket open, to ensure the resource is "owned" by the program for its use as long as it is running.Graniteware
@CraigMcQueen: I am assuming server ports have been provisioned if this option is used.Thilde
L
8

To my knowledge, that isn't how TCP works. The accept(..) call will always return with the client details. There is no way to peek at the connection and selectively refuse.

The way you are doing it now is actually the correct way: accept and then close. In case you have another message structure over and above this layer, you can create a custom "Reject message". This option completely depends on your use case.

In case you are looking for rejecting on the basis of IP address, its not within your apps domain. Its the job of your firewall (As @Bart Friederichs says). That way, the request will not even touch the TCP stack.


Actually I want strictly one connection only on this particular port. Any other connection should ideally fail in a very obvious way.

Do not let the accept call in your control flow. Only when you wait on accept will your program wait for a socket connection, never otherwise.

Lorettelorgnette answered 16/5, 2013 at 15:8 Comment(0)
A
6

In standard socket APIs on most platforms, there is no way to reject a connection. You must accept() the connection and then close it immediately if you don't want it.

The exception to this rule is the Winsock-specific WSAAccept() function. It provides a callback that allows an application to decide, on a per-connection basis, whether a connection should be accepted, rejected, or kept in the backlog queue.

Afire answered 16/5, 2013 at 20:47 Comment(1)
It is interesting, though, how this rejection by callback works by default. It still ACCEPTS the connection first (maybe to fetch remote client metadata?), so for example telnet waits almost indefinitely for user input before it knows it was forcefully outed. Firefox, OTOH, immediately sends GET, notices the error and reconnects. Anyway, one rather do SO_CONDITIONAL_ACCEPT before listen implementing this.Caco
E
2

It might be helpful

  1. to close the listening server socket after having successfully accept()ing the client connection and
  2. to re-establish it after the client connection has gone due to whatever reason.
Elna answered 16/5, 2013 at 15:12 Comment(0)
B
1

A little late to the party, but you can use WSAAccept() instead of accept(). WSAAccept() has an optional callback function that allows you to take a peek at who's knocking at the door and decide if you want to answer. Your callback function can return the constants CF_ACCEPT, CF_REJECT and CF_DEFER. The first two are obvious. The 3rd one allows you to defer answering in case you need to decide later. Once you've decided for sure you call WSAAccept() again and either accept or reject.

Unfortunately, you have to either accept or reject in order to remove the entry from the front of the listen queue. The reason I say it's unfortunate is there is an obvious difference to the connecting machine between getting rejected and never getting a response (timeout).

If a hacker is using a port scanner they'll know you're listening on the port if you reject. At that point they can start hacking their way in. It would be nice to be able to remove the entry from the front of the queue without ever doing anything with it such that whoever's on the other end doesn't know you're listening on that port.

Brewage answered 5/2, 2019 at 20:19 Comment(3)
It's is so strange that there are no functions for this. I'm creating a web server and was thinking that I should make every banned IP timeout (not open the TCP connection and have my OS free up any info allocated for that incoming connection). But that seems not possible without writing my own OS or network driver? I am thinking that if I never open the connection then the routers will not accept more packets from it as well. Hence I can save some bandwidth in case of DoS.Vendor
A follow up to what I wrote above. Since I'm using Linux I found that the service firewalld can do what I want. I can tell it to "ban" certain IP addresses and to them it will look like I'm not running a server then. And I can interface with this service from my own server :)Vendor
@JoakimL.Christiansen i believe you can de facto do it by opening RAW sockets and issuing TCP RESET frame after getting TCP SYN frame. Actually, i believe that is exactly what your firewall does. Granted, that is a significant boilerplate...Caco
A
0

One more crazy option seems to set the socket into a refuse always mode, see setsockopt call with SO_PAUSE_ACCEPT parameter.

Use this option for listening sockets. When the option is set, the socket responds to all incoming connections with an RST rather than accepting them.

I wonder if notification events would still be passed to the application about the connection attempts then (services like WSAEventSelect( , , FD_ACCEPT) and WSAAsyncSelect( , , FD_ACCEPT)) for making application like "port knocking", where mere attempts matters.

UPD. at least with Win10 FD_ACCEPT notifications indeed cease to come on a "paused" socket. Neither come FD_CONNECT or FD_CLOSE.

Also, it seems the clients then are getting time-outed rather than outright rejected. The paused connection fails in somehow more explicit way (telnet reports the rejection outright, while it waits for any user input to send when faces WSAAccept-based rejection) but after a significant delay (seen in Firefox, telnet always has 1-2 seconds delay of its own).

SysInternals TCPView though does not see any special state on such a listening socket.

UPD2. oops, i forgot to add SO_CONDITIONAL_ACCEPT to socket options in between bind and listen. Now telnet (still slow to launch) does no more wait for user input. Firefox OTOH became much slower to report error (it makes about a dozen of attempts before giving up, and did it faster). Maybe it is related to the listen -> WSAAsyncSelect -> WndProc -> WSAAccept pipeline in the test server.

Automate answered 26/6 at 12:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.