views:

383

answers:

4

I'm writing a simple tcp server application using sockets. As far as I know I can obtain the client's ip address and port after calling accept().

Now lets assume I have a banlist and I want to ban some ip addresses from my server. Is there a better way than accepting the connection and then dropping it?

Is there a way to get the client's ip and port before accepting the connection? If we have accept() why don't we have something like refuse()? Is there a way to refuse the connection or simply ignore connection attempt from a host?

+3  A: 

On windows: look at WSAAccept, may be it is what you need:

SOCKET WSAAccept(
  __in     SOCKET s,
  __out    struct sockaddr *addr,
  __inout  LPINT addrlen,
  __in     LPCONDITIONPROC lpfnCondition,
  __in     DWORD dwCallbackData
);

lpfnCondition -- the address of an optional, application-specified condition function that will make an accept/reject decision based on the caller information passed in as parameters, and optionally create or join a socket group by assigning an appropriate value to the result parameter g of this function. If this parameter is NULL, then no condition function is called.

For linux solution look at: GNU Common C++ -- TCPSocket class, it has onAccept(), and reject() methods.

virtual bool TCPSocket::onAccept    (    const InetHostAddress &     ia,
     tpport_t   port
    )   [inline, protected, virtual]

A method to call in a derived TCPSocket class that is acting as a server when a connection request is being accepted.

The server can implement protocol specific rules to exclude the remote socket from being accepted by returning false. The Peek method can also be used for this purpose.


However, you can just close socket after accept if pre-condition is false :)

Mihail
If there is no other solution, I will accept and close the socket immediately like opening a door to ask who are you and close it if you are banned... But I don't want to open that door at all. I want to make banned users think that the server machine is simply down.
Calmarius
What you want is more elegant, however in development we need balance beetween features gratefulness and time/money spent on it. Like in XP said -- do it in the simpliest way, later you can come and refactor it.
Mihail
+6  A: 

The TCP implementation normally completes the TCP 3-way handshake before the user process even has access to the connection, and the accept() function merely gets the next connection off the queue. So it is too late to pretend that the server is down. This works the same way for regular TCP data; the TCP implementation does not wait for the application to actually recv() the data before a TCP ACK is sent. This keeps the other side from needlessly retransmitting packets that were received correctly, and allows the throughput to remain high, even when the application is bogged down with other things. In the case of new connections (SYN packets), this also allows the kernel to protect itself (and the application) from SYN flood attacks.

Although not portable, many platforms provide some sort of firewall capability that will allow filtering incoming connections based on IP address/port. However that is usually configured system-wide and not by an individual application.

mark4o
So if I listen on a port the connection will be established in background regardless I want or not?
Calmarius
Calling listen() tells the OS that you want any connection that can be successfully established on your port. So unless you filter the packet at a lower level (firewall rule), it will respond to any connection request to this port. If you decide that you don't want a particular connection you can always close() it, which is what some of the other answers here will do.
mark4o
+1  A: 

TCP Wrapper has become very widespread on UNIX-like platforms. Yes, the connection is only checked after accept; however, if you're doing this anyways, you might as well use the same configuration that everybody else uses.

In essence, your program is either run with tcpd or modified to use libwrap directly, like so:

#include <tcpd.h>
connfd = accept(listenfd, (struct sockaddr *)&addr, &addrlen);
if (!hosts_ctl("my_program", STRING_UNKNOWN, inet_ntoa(addr.sin_addr), STRING_UNKNOWN)) {
    fprintf(stderr, "Client %s connection disallowed\n", inet_ntoa(addr.sin_addr));
    close(connfd);
}

Then you configure /etc/hosts.allow and /etc/hosts.deny according to your desires, e.g.

# /etc/hosts.allow
my_program: 127.0.0.1 10. 192.168.

# /etc/hosts.deny
my_program: ALL

This will cause my_program to reject all connection attempts from non-local addresses, but can be changed without touching my_program at all.

ephemient
A: 

the concept of not accepting connection based on IP address will not work if you are behing NAT.