⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sockutils.c

📁 Windows XP下的抓包程序实现
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 * 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.
 * 
 */



/*!
	\file sockutils.c

	The goal of this file is 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 in supporting several operating systems.

	These calls do not want to provide a better socket interface; vice versa, they intend to
	provide a set of calls that is portable among several operating systems, hiding their
	differences.
*/


#include "sockutils.h"
#include <string.h>	// for strerror()
#include <errno.h>	// for the errno variable
#include <stdio.h>	// for the stderr file
#include <stdlib.h>	// for malloc() and free()






// 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


//! Size of the buffer that has to keep error messages
#define SOCK_ERRBUF_SIZE 1024


	// Constants; used in order to keep strings here
#define SOCKET_NO_NAME_AVAILABLE "No name available"
#define SOCKET_NO_PORT_AVAILABLE "No port available"
#define SOCKET_NAME_NULL_DAD "Null address (possibly DAD Phase)"




/****************************************************
 *                                                  *
 * Locally defined functions                        *
 *                                                  *
 ****************************************************/

int sock_ismcastaddr(const struct sockaddr *saddr);





/****************************************************
 *                                                  *
 * Function bodies                                  *
 *                                                  *
 ****************************************************/


/*!
	\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'. It may be NULL.
	
	\param errbuf: a pointer to an user-allocated buffer that will contain the complete
	error message. This buffer has to be at least 'errbuflen' in length.
	It can be NULL; in this case the error cannot be printed.

	\param errbuflen: length of the buffer that will contains the error. The error message cannot be
	larger than 'errbuflen - 1' because the last char is reserved for the string terminator.

	\return No return values. The error message is returned in the 'string' parameter.
*/
void sock_geterror(const char *caller, char *errbuf, int errbuflen)
{
#ifdef WIN32
	int retval;
	int code;
	TCHAR message[SOCK_ERRBUF_SIZE];	/* It will be char (if we're using ascii) or wchar_t (if we're using unicode) */
	
		code= GetLastError();
	
		retval= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
	                  FORMAT_MESSAGE_MAX_WIDTH_MASK,
	                  NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
	                  message, sizeof(message) / sizeof(TCHAR), NULL);
	
		if (retval == 0)
		{
			if (errbuf)
			{
				if ( (caller) && (*caller) )
					snprintf(errbuf, errbuflen, "%sUnable to get the exact error message", caller);
				else
					snprintf(errbuf, errbuflen, "Unable to get the exact error message");

				errbuf[errbuflen - 1]= 0;
			}

			return;
		}
	
		if (errbuf)
		{
			if ( (caller) && (*caller) )
				snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, code);
			else
				snprintf(errbuf, errbuflen, "%s (code %d)", message, code);

			errbuf[errbuflen - 1]= 0;
		}


#else
	char *message;
	
		message= strerror(errno);

		if (errbuf)
		{
			if ( (caller) && (*caller) )
				snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, errno);
			else
				snprintf(errbuf, errbuflen, "%s (code %d)", message, errno);

			errbuf[errbuflen - 1]= 0;
		}

#endif
}



/*!
	\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 an user-allocated buffer that will contain the complete
	error message. This buffer has to be at least 'errbuflen' in length.
	It can be NULL; in this case the error cannot be printed.

	\param errbuflen: length of the buffer that will contains the error. The error message cannot be
	larger than 'errbuflen - 1' because the last char is reserved for the string terminator.

	\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, int errbuflen)
{
#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)
		{
			if (errbuf)
			{
				snprintf(errbuf, errbuflen, "Failed to initialize Winsock\n");
				errbuf[errbuflen - 1]= 0;
			}

			WSACleanup();

			return -1;
		}
	}

	sockcount++;
#endif

	return 0;
}



/*!
	\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
}



/*!
	\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;
	}
}



/*!
	\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().

	This function is usually preceeded by the sock_initaddress().

	\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
	sock_initaddress().

	\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 an user-allocated buffer that will contain the complete
	error message. This buffer has to be at least 'errbuflen' in length.
	It can be NULL; in this case the error cannot be printed.

	\param errbuflen: length of the buffer that will contains the error. The error message cannot be
	larger than 'errbuflen - 1' because the last char is reserved for the string terminator.

	\return the socket that has been opened (that has to be used in the following sockets calls)
	if everything is fine, '0' if some errors occurred. The error message is returned
	in the 'errbuf' variable.
*/
SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen)
{
SOCKET sock;

	sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
	if (sock == -1)
	{
		sock_geterror("socket(): ", errbuf, errbuflen);
		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)
			{
				if (errbuf)
				{
					snprintf(errbuf, errbuflen, "setsockopt(IPV6_BINDV6ONLY)");
					errbuf[errbuflen - 1]= 0;
				}
				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, errbuflen);
			return -1;
		}

		if (addrinfo->ai_socktype == SOCK_STREAM)
			if (listen(sock, nconn) == -1)
			{
				sock_geterror("listen(): ", errbuf, errbuflen);
				return -1;
			}

		// server side ended
		return sock;
	}
	else	// we're the client
	{
	struct addrinfo *tempaddrinfo;
	char *errbufptr;
	int bufspaceleft;

		tempaddrinfo= addrinfo;
		errbufptr= errbuf;
		bufspaceleft= errbuflen;
		*errbufptr= 0;


		// We have to loop though all the addinfo returned.
		// For instance, we can have both IPv6 and IPv4 addresses, but the service we're trying
		// to connect to is unavailable in IPv6, so we have to try in IPv4 as well
		while (tempaddrinfo)
		{
			
			if (connect(sock, tempaddrinfo->ai_addr, tempaddrinfo->ai_addrlen) == -1)
			{
			int msglen;
			char TmpBuffer[100];
			char SocketErrorMessage[SOCK_ERRBUF_SIZE];

				// We have to retrieve the error message before any other socket call completes, otherwise
				// the error message is lost
				sock_geterror(NULL, SocketErrorMessage, sizeof(SocketErrorMessage) );

				// Returns the numeric address of the host that triggered the error
				sock_getascii_addrport( (struct sockaddr_storage *) tempaddrinfo->ai_addr, TmpBuffer, sizeof(TmpBuffer), NULL, 0, NI_NUMERICHOST, TmpBuffer, sizeof(TmpBuffer) );

				snprintf(errbufptr, bufspaceleft, "Is the server properly installed on %s?  connect() failed: %s", TmpBuffer, SocketErrorMessage);

				// In case more then one 'connect' fails, we manage to keep all the error messages
				msglen= strlen(errbufptr);

				errbufptr[msglen]= ' ';
				errbufptr[msglen + 1]= 0;

				bufspaceleft= bufspaceleft - (msglen + 1);
				errbufptr+= (msglen + 1);

				tempaddrinfo= tempaddrinfo->ai_next;
			}
			else
				break;
		}

		// Check how we exit from the previous loop
		// If tempaddrinfo is equal to NULL, it means that all the connect() failed.
		if (tempaddrinfo == NULL) 
		{
			closesocket(sock);
			return -1;
		}
		else
			return sock;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -