📄 sockutils.c
字号:
/*
* Copyright (c) 2002 - 2003
* NetGroup, Politecnico di Torino (Italy)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Politecnico di Torino nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sockutils.h"
#include <string.h> // for strerror
#include <errno.h> // for the errno variable
#include <stdio.h> // for the stderr file
/*!
\file sockutils.c
The goal of this file it to provide a common set of primitives for socket manipulation.
Although the socket interface defined in the RFC 2553 (and its updates) is excellent, several
minor issues are still hidden when we want to operate on several operating systems.
These calls do not want to provide a better socket interface; vice versa, they want to
provide a set of calls that is portable among several operating systems, hiding their
differences.
*/
// WinSock Initialization
#ifdef WIN32
#define WINSOCK_MAJOR_VERSION 2 /*!< Ask for Winsock 2.2 */
#define WINSOCK_MINOR_VERSION 2 /*!< Ask for Winsock 2.2 */
int sockcount= 0; /*!< Variable that allows calling the WSAStartup() only one time */
#endif
// Some minor differences between UNIX and Win32
#ifdef WIN32
#define SHUT_WR SD_SEND /*!< The control code for shutdown() is different in Win32 */
#define snprintf _snprintf /*!< The snprintf is called _snprintf() in Win32 */
#endif
/****************************************************
* *
* Locally defined functions *
* *
****************************************************/
int sock_ismcastaddr(const struct sockaddr *saddr);
/*
\brief Global variable; needed to keep the message due to an error that we want to discard.
This can happen, for instance, because we already have an error message and we want to keep
the first one.
*/
char fakeerrbuf[SOCK_ERRBUF_SIZE + 1];
/****************************************************
* *
* Function bodies *
* *
****************************************************/
/*! \ingroup remote_pri_func
\brief It retrieves the error message after an error occurred in the socket interface.
This function is defined because of the different way errors are returned in UNIX
and Win32. This function provides a consistent way to retrieve the error message
(after a socket error occurred) on all the platforms.
\param caller: a pointer to a user-allocated string which contains a message that has
to be printed *before* the true error message. It could be, for example, 'this error
comes from the recv() call at line 31'.
\param string: a pointer to an user-allocated buffer that will contain the complete
error message. This buffer has to be at least 'size' in length.
\param size: the size of the buffer in which the error message will be copied.
\return No return values. The error message is returned in the 'string' parameter.
*/
void sock_geterror(const char *caller, char *string, int size)
{
#ifdef WIN32
int retval;
int code;
char message[SOCK_ERRBUF_SIZE];
code= GetLastError();
retval= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) message, SOCK_ERRBUF_SIZE, NULL);
if (retval == 0)
{
snprintf(string, size, "%sUnable to get the exact error message", caller);
return;
}
snprintf(string, size, "%s%s (code %d)", caller, message, code);
#else
char *message;
message= strerror(errno);
snprintf(string, size, "%s%s (code %d)", caller, message, errno);
#endif
}
/*! \ingroup remote_pri_func
\brief It initializes sockets.
This function is pretty useless on UNIX, since socket initialization is not required.
However it is required on Win32. In UNIX, this function appears to be completely empty.
\param errbuf: a pointer to a user-allocated buffer (of size SOCK_ERRBUF_SIZE)
that will contain the error message (in case there is one).
\return '0' if everything is fine, '-1' if some errors occurred. The error message is returned
in the 'errbuf' variable.
*/
int sock_init(char *errbuf)
{
#ifdef WIN32
if (sockcount == 0)
{
WSADATA wsaData; // helper variable needed to initialize Winsock
// Ask for Winsock version 2.2.
if ( WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
snprintf(errbuf, SOCK_ERRBUF_SIZE, "Failed to initialize Winsock\n");
WSACleanup();
return -1;
}
}
sockcount++;
#endif
return 0;
}
/*! \ingroup remote_pri_func
\brief It deallocates sockets.
This function is pretty useless on UNIX, since socket deallocation is not required.
However it is required on Win32. In UNIX, this function appears to be completely empty.
\return No error values.
*/
void sock_cleanup()
{
#ifdef WIN32
sockcount--;
if (sockcount == 0)
WSACleanup();
#endif
}
/*! \ingroup remote_pri_func
\brief It checks if the sockaddr variable contains a multicast address.
\return '0' if the address is multicast, '-1' if it is not.
*/
int sock_ismcastaddr(const struct sockaddr *saddr)
{
if (saddr->sa_family == PF_INET)
{
struct sockaddr_in *saddr4 = (struct sockaddr_in *) saddr;
if (IN_MULTICAST(ntohl(saddr4->sin_addr.s_addr))) return 0;
else return -1;
}
else
{
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) saddr;
if (IN6_IS_ADDR_MULTICAST(&saddr6->sin6_addr)) return 0;
else return -1;
}
}
/*! \ingroup remote_pri_func
\brief It initializes a network connection both from the client and the server side.
In case of a client socket, this function calls socket() and connect().
In the meanwhile, it checks for any socket error.
If an error occurs, it writes the error message into 'errbuf'.
In case of a server socket, the function calls socket(), bind() and listen().
In no cases this fucntion will authenticate the user on the remote host. This function
has to be done in the rpcap_sendauth().
\param addrinfo: pointer to an addrinfo variable which will be used to
open the socket and such. This variable is the one returned by the previous call to
sockvalidateaddr().
\param server: '1' if this is a server socket, '0' otherwise.
\param nconn: number of the connections that are allowed to wait into the listen() call.
This value has no meanings in case of a client socket.
\param errbuf: a pointer to a user-allocated buffer (of size SOCK_ERRBUF_SIZE)
that will contain the error message (in case there is one).
\return the socket that has been opened (that has to be used in the following sockets calls)
if everything is fine, '-1' if some errors occurred. The error message is returned
in the 'errbuf' variable.
*/
int sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf)
{
SOCKET sock;
sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
if (sock == -1)
{
sock_geterror("socket(): ", errbuf, SOCK_ERRBUF_SIZE);
return -1;
}
// This is a server socket
if (server)
{
#ifdef BSD
// Force the use of IPv6-only addresses; in BSD you can accept both v4 and v6
// connections if you have a "NULL" pointer as the nodename in the getaddrinfo()
// This behaviour is not clear in the RFC 2553, so each system implements the
// bind() differently from this point of view
if (addrinfo->ai_family == PF_INET6)
{
int on;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (int)) == -1)
{
snprintf(errbuf, SOCK_ERRBUF_SIZE, "setsockopt(IPV6_BINDV6ONLY)");
return -1;
}
}
#endif
// WARNING: if the address is a mcast one, I should place the proper Win32 code here
if (bind(sock, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
{
sock_geterror("bind(): ", errbuf, SOCK_ERRBUF_SIZE);
return -1;
}
if (addrinfo->ai_socktype == SOCK_STREAM)
if (listen(sock, nconn) == -1)
{
sock_geterror("listen(): ", errbuf, SOCK_ERRBUF_SIZE);
return -1;
}
// server side ended
return sock;
}
else // we're the client
{
if (connect(sock, addrinfo->ai_addr, addrinfo->ai_addrlen) == -1)
{
sock_geterror("Is libpcap/WinPcap properly installed on the other host? connect() failed: ", errbuf, SOCK_ERRBUF_SIZE);
closesocket(sock);
return -1;
}
return sock;
}
}
/*! \ingroup remote_pri_func
\brief Closes the present (TCP and UDP) socket connection.
This function sends a shutdown() on the socket in order to disable send() calls
(while recv() ones are still allowed). Then, it closes the socket.
\param sock: the socket identifier of the connection that has to be closed.
\param errbuf: a pointer to a user-allocated buffer (of size SOCK_ERRBUF_SIZE)
that will contain the error message (in case there is one).
\return '0' if everything is fine, '-1' if some errors occurred. The error message is returned
in the 'errbuf' variable.
*/
int sock_close(SOCKET sock, char *errbuf)
{
// SHUT_WR: subsequent calls to the send function are disallowed.
// For TCP sockets, a FIN will be sent after all data is sent and
// acknowledged by the Server.
if (shutdown(sock, SHUT_WR) )
{
sock_geterror("shutdown(): ", errbuf, SOCK_ERRBUF_SIZE);
// close the socket anyway
closesocket(sock);
return -1;
}
closesocket(sock);
return 0;
}
/*! \ingroup remote_pri_func
\brief Checks that the address, port and flags given are valids and it returns an 'addrinfo' stucture.
This function basically calls the getaddrinfo() calls, and it performs a set of sanity checks
to control that everything is fine (e.g. a TCP socket cannot have a mcast address, and such).
If an error occurs, it writes the error message into 'errbuf'.
\param address: a pointer to a user-allocated buffer containing the network address to check.
It could be both a numeric - literal address, and it can be NULL or "" (useful in case of a server
socket which has to bind to all addresses).
\param port: a pointer to a user-allocated buffer containing the network port to use.
\param hints: an addrinfo variable (passed by reference) containing the flags needed to create the
addrinfo structure appropriately.
\param addrinfo: it represents the true returning value. This is a pointer to an addrinfo variable
(passed by reference), which will be allocated by this function and returned back to the caller.
This variable will be used in the next sockets calls.
\param errbuf: a pointer to a user-allocated buffer (of size SOCK_ERRBUF_SIZE)
that will contain the error message (in case there is one).
\return '0' if everything is fine, '-1' if some errors occurred. The error message is returned
in the 'errbuf' variable. The addrinfo variable that has to be used in the following sockets calls is
returned into the addrinfo parameter.
\warning The 'addrinfo' variable has to be deleted by the programmer by calling freeaddrinfo() when
it is no longer needed.
*/
int sock_validaddr(const char *address, const char *port,
struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf)
{
int retval;
retval = getaddrinfo(address, port, hints, addrinfo);
if (retval != 0)
{
// if the getaddrinfo() fails, you have to use gai_strerror(), instead of using the standard
// error routines (WSAGetLastError() in Win32 anderrono in UNIX)
snprintf(errbuf, SOCK_ERRBUF_SIZE, "getaddrinfo() %s", gai_strerror(retval));
return -1;
}
/*!
\warning SOCKET: I should check all the accept() in order to bind to all addresses (in case
addrinfo has more han one pointers, and all connect() to use all addresses (in the case the firs one fails)
*/
// This software only supports PF_INET and PF_INET6.
if (( (*addrinfo)->ai_family != PF_INET) && ( (*addrinfo)->ai_family != PF_INET6))
{
snprintf(errbuf, SOCK_ERRBUF_SIZE, "getaddrinfo(): socket type not supported");
return -1;
}
if ( ( (*addrinfo)->ai_socktype == SOCK_STREAM) && (sock_ismcastaddr( (*addrinfo)->ai_addr) == 0) )
{
snprintf(errbuf, SOCK_ERRBUF_SIZE, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
return -1;
}
return 0;
}
/*! \ingroup remote_pri_func
\brief It sends the amount of data contained into 'buffer' on the given socket.
This function basically calls the send() socket function and it checks that all
the data specified in 'buffer' (of size 'size') will be sent. If an error occurs,
it writes the error message into 'errbuf'.
In case the socket buffer does not have enough space, it loops until all data
has been sent.
\param socket: the connected socket currently opened.
\param buffer: a char pointer to a user-allocated buffer in which data is contained.
\param size: number of bytes that have to be sent.
\param errbuf: a pointer to a user-allocated buffer (of size SOCK_ERRBUF_SIZE)
that will contain the error message (in case there is one).
\return '0' if everything is fine, '-1' if some errors occurred. The error message is returned
in the 'errbuf' variable.
*/
int sock_send(SOCKET socket, const char *buffer, int size, char *errbuf)
{
int nsent;
send:
#ifdef linux
/*
Another pain... in Linux there's this flag
MSG_NOSIGNAL
Requests not to send SIGPIPE on errors on stream
oriented sockets when the other end breaks the con
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -