/* * Copyright (C) 1996-2017 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ // Author: Jens-S. V?ckler // // File: socket.hh // Sun May 3 1998 // // (c) 1998 Lehrgebiet Rechnernetze und Verteilte Systeme // Universit?t Hannover, Germany // // Books: W. Richard Steven, "Advanced Programming in the UNIX Environment", // Addison-Wesley, 1992. // // Permission to use, copy, modify, distribute, and sell this software // and its documentation for any purpose is hereby granted without fee, // provided that (i) the above copyright notices and this permission // notice appear in all copies of the software and related documentation, // and (ii) the names of the Lehrgebiet Rechnernetze und Verteilte // Systeme and the University of Hannover may not be used in any // advertising or publicity relating to the software without the // specific, prior written permission of Lehrgebiet Rechnernetze und // Verteilte Systeme and the University of Hannover. // // THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, // EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY // WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. // // IN NO EVENT SHALL THE LEHRGEBIET RECHNERNETZE UND VERTEILTE SYSTEME OR // THE UNIVERSITY OF HANNOVER BE LIABLE FOR ANY SPECIAL, INCIDENTAL, // INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT // ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, // ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS // SOFTWARE. // // Revision 1.3 1999/01/19 11:00:50 voeckler // Linux glibc2 fixes for socket size parameters. // // Revision 1.2 1998/08/27 15:23:39 voeckler // added TCP_NODELAY options at several places. // // Revision 1.1 1998/08/13 21:52:55 voeckler // Initial revision // #include "squid.h" #include "socket.hh" #include #include #include #include #include #include #include "convert.hh" int setSocketBuffers( int sockfd, int size ) // purpose: set socket buffers for both directions to the specified size // paramtr: sockfd (IN): socket file descriptor // size (IN): new socket buffer size // returns: -1 on setsockopt() errors, 0 otherwise // warning: prints error message on stderr, errno will be changed { if ( setsockopt( sockfd, SOL_SOCKET, SO_RCVBUF, (char*) &size, sizeof(size) ) == -1 ) { perror( "setsockopt( SO_RCVBUF )" ); return -1; } if ( setsockopt( sockfd, SOL_SOCKET, SO_SNDBUF, (char*) &size, sizeof(size) ) == -1 ) { perror( "setsockopt( SO_SNDBUF )" ); return -1; } return 0; } int getSocketNoDelay( int sockfd ) // purpose: get state of the TCP_NODELAY of the socket // paramtr: sockfd (IN): socket descriptor // returns: 1, if TCP_NODELAY is set, // 0, if TCP_NODELAY is not set, // -1, if an error occurred (e.g. datagram socket) { int delay = 0; socklen_t len = sizeof(delay); if ( getsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &delay, &len ) == -1 ) { perror( "# getsockopt( TCP_NODELAY ) failed" ); return -1; } else return ( delay ? 1 : 0 ); } int setSocketNoDelay( int sockfd, bool nodelay ) // purpose: get state of the TCP_NODELAY of the socket // paramtr: sockfd (IN): socket descriptor // nodelay (IN): true, if TCP_NODELAY is to be set, false otherwise. // returns: 0, if everything worked out o.k. // -1, if an error occurred (e.g. datagram socket) { const int delay = 1; if ( setsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY, (const char*) &delay, sizeof(delay) ) == -1 ) { perror( "setsockopt( TCP_NODELAY ) failed" ); return -1; } else return 0; } int commonCode( int& sockfd, bool nodelay, int sendBufferSize, int recvBufferSize ) // purpose: common code in server sockets and client sockets // paramtr: sockfd (IO): socket filedescriptor // nodelay (IN): true=set TCP_NODELAY option. // sendBufferSize (IN): don't set (use sys defaults) if < 0 // recvBufferSize (IN): don't set (use sys defaults) if < 0 // returns: 0 == if everthing went ok, -1 otherwise // warning: sockfd will be closed, if -1 is returned! { // set TCP_NODELAY option, if that is wanted. // The socket API default is unset. if ( nodelay ) { const int delay = 1; if ( setsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY, (const char*) &delay, sizeof(delay) ) == -1 ) { perror( "setsockopt( TCP_NODELAY ) failed" ); close(sockfd); return -1; } } // set the socket send buffer size explicitly, or use the system default if ( sendBufferSize >= 0 ) { if ( setsockopt( sockfd, SOL_SOCKET, SO_SNDBUF, (char*) &sendBufferSize, sizeof(sendBufferSize) ) == -1 ) { perror( "setsockopt( SO_SNDBUF )" ); close(sockfd); return -1; } } // set the socket recv buffer size explicitly, or use the system default if ( recvBufferSize >= 0 ) { if ( setsockopt( sockfd, SOL_SOCKET, SO_RCVBUF, (char*) &recvBufferSize, sizeof(recvBufferSize) ) == -1 ) { perror( "setsockopt( SO_RCVBUF )" ); close(sockfd); return -1; } } return 0; } int connectTo( struct in_addr host, unsigned short port, bool nodelay, int sendBufferSize, int recvBufferSize ) // purpose: connect to a server as a client // paramtr: host (IN): address describing the server // port (IN): port to connect at the server // nodelay (IN): true=set TCP_NODELAY option. // sendBufferSize (IN): don't set (use sys defaults) if < 0 // recvBufferSize (IN): don't set (use sys defaults) if < 0 // returns: >=0 is the descriptor of the opened, connected socket, // -1 is an indication of an error (errno may have been reset). { int sockfd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( sockfd == -1 ) { perror( "socket() failed" ); return -1; } if ( commonCode( sockfd, nodelay, sendBufferSize, recvBufferSize ) == -1 ) return -1; struct sockaddr_in server; memset( &server, 0, sizeof(server) ); server.sin_family = AF_INET; server.sin_addr = host; server.sin_port = port; if ( connect( sockfd, (struct sockaddr*) &server, sizeof(server) ) == -1 ) { perror( "connect() failure" ); close(sockfd); return -1; } return sockfd; } int serverSocket( struct in_addr host, unsigned short port, int backlog, bool reuse, bool nodelay, int sendBufferSize, int recvBufferSize ) // purpose: open a server socket for listening // paramtr: host (IN): host to bind locally to, use INADDRY_ANY for * // port (IN): port to bind to, use 0 for system assigned // backlog (IN): listen backlog queue length // reuse (IN): set SO_REUSEADDR option - default usefully // nodelay (IN): true=set TCP_NODELAY option. // SETTING TCP_NODELAY ON A SERVER SOCKET DOES NOT MAKE SENSE! // sendBufferSize (IN): don't set (use sys defaults) if < 0 // recvBufferSize (IN): don't set (use sys defaults) if < 0 // returns: opened listening fd, or -1 on error. // warning: error message will be printed on stderr and errno reset. { int sockfd = socket( AF_INET, SOCK_STREAM, 0 ); if ( sockfd == -1 ) { perror( "socket" ); return -1; } if ( reuse ) { int opt = 1; if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, (char*) &opt, sizeof(int) ) == -1) { perror( "setsockopt( SO_REUSEADDR )" ); close( sockfd ); return -1; } } if ( commonCode( sockfd, nodelay, sendBufferSize, recvBufferSize ) == -1 ) return -1; struct sockaddr_in server; memset( &server, 0, sizeof(server) ); server.sin_family = AF_INET; server.sin_port = port; server.sin_addr = host; if ( bind( sockfd, (SA*) &server, sizeof(server) ) == -1 ) { SockAddress socket; fprintf( stderr, "bind(%s): %s\n", my_sock_ntoa(server,socket), strerror(errno) ); close(sockfd); return -1; } if ( listen( sockfd, backlog ) == -1 ) { perror( "listen" ); close(sockfd); return -1; } return sockfd; }