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

📄 sockutils.c

📁 Windows XP下的抓包程序实现
💻 C
📖 第 1 页 / 共 3 页
字号:
	}
}




/*!
	\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 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_close(SOCKET sock, char *errbuf, int errbuflen)
{
	// 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, errbuflen);
		// close the socket anyway
		closesocket(sock);
		return -1;
	}

	closesocket(sock);
	return 0;
}






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

	\warning This function requires the 'hints' variable as parameter. The semantic of this variable is the same
	of the one of the corresponding variable used into the standard getaddrinfo() socket function. We suggest
	the programmer to look at that function in order to set the 'hints' variable appropriately.
*/
int sock_initaddress(const char *address, const char *port,
							struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen)
{
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 (errno) in UNIX; WIN32 suggests using the GetLastError() instead.
		if (errbuf)
#ifdef WIN32
			sock_geterror("getaddrinfo(): ", errbuf, errbuflen);
#else
			if (errbuf)
			{
				snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval));
				errbuf[errbuflen - 1]= 0;
			}
#endif
		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
*/

	// This software only supports PF_INET and PF_INET6.
	if (( (*addrinfo)->ai_family != PF_INET) && ( (*addrinfo)->ai_family != PF_INET6))
	{
		if (errbuf)
		{
			snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported");
			errbuf[errbuflen - 1]= 0;
		}
		return -1;
	}

	if ( ( (*addrinfo)->ai_socktype == SOCK_STREAM) && (sock_ismcastaddr( (*addrinfo)->ai_addr) == 0) )
	{
		if (errbuf)
		{
			snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
			errbuf[errbuflen - 1]= 0;
		}

		return -1;
	}

	return 0;
}



/*!
	\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 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_send(SOCKET socket, const char *buffer, int size, char *errbuf, int errbuflen)
{
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 connection.
		The EPIPE error is still returned.
*/
	nsent = send(socket, buffer, size, MSG_NOSIGNAL);
#else
	nsent = send(socket, buffer, size, 0);
#endif

	if (nsent == -1)
	{
		sock_geterror("send(): ", errbuf, errbuflen);
		return -1;
	}

	if (nsent != size)
	{
		size-= nsent;
		buffer+= nsent;
		goto send;
	}

	return 0;
}


/*!
	\brief It copies the amount of data contained into 'buffer' into 'tempbuf'.
	and it checks for buffer overflows.

	This function basically copies 'size' bytes of data contained into 'buffer'
	into 'tempbuf', starting at offset 'offset'. Before that, it checks that the 
	resulting buffer will not be larger	than 'totsize'. Finally, it updates 
	the 'offset' variable in order to point to the first empty location of the buffer.

	In case the function is called with 'checkonly' equal to 1, it does not copy
	the data into the buffer. It only checks for buffer overflows and it updates the
	'offset' variable. This mode can be useful when the buffer already contains the 
	data (maybe because the producer writes directly into the target buffer), so
	only the buffer overflow check has to be made.
	In this case, both 'buffer' and 'tempbuf' can be NULL values.

	This function is useful in case the userland application does not know immediately
	all the data it has to write into the socket. This function provides a way to create
	the "stream" step by step, appendning the new data to the old one. Then, when all the
	data has been bufferized, the application can call the sock_send() function.

	\param buffer: a char pointer to a user-allocated buffer that keeps the data
	that has to be copied.

	\param size: number of bytes that have to be copied.

	\param tempbuf: user-allocated buffer (of size 'totsize') in which data
	has to be copied.

	\param offset: an index into 'tempbuf' which keeps the location of its first
	empty location.

	\param totsize: total size of the buffer in which data is being copied.

	\param checkonly: '1' if we do not want to copy data into the buffer and we
	want just do a buffer ovreflow control, '0' if data has to be copied as well.

	\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. When the function returns, 'tempbuf' will 
	have the new string appended, and 'offset' will keep the length of that buffer.
	In case of 'checkonly == 1', data is not copied, but 'offset' is updated in any case.

	\warning This function assumes that the buffer in which data has to be stored is
	large 'totbuf' bytes.

	\warning In case of 'checkonly', be carefully to call this function *before* copying
	the data into the buffer. Otherwise, the control about the buffer overflow is useless.
*/
int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen)
{

	if ((*offset + size) > totsize)
	{
		if (errbuf)
		{
			snprintf(errbuf, errbuflen, "Not enough space in the temporary send buffer.");
			errbuf[errbuflen - 1]= 0;
		}

		return -1;
	};

	if (!checkonly)
		memcpy(tempbuf + (*offset), buffer, size);

	(*offset)+= size;

	return 0;
}



/*!
	\brief It waits on a connected socket and it manages to receive data.

	This function basically calls the recv() socket function and it checks that no
	error occurred. If that happens, it writes the error message into 'errbuf'.

	This function changes its behaviour according to the 'receiveall' flag: if we
	want to receive exactly 'size' byte, it loops on the recv()	until all the requested 
	data is arrived. Otherwise, it returns the data currently available.

	In case the socket does not have enough data available, it cycles on the recv()
	util the requested data (of size 'size') is arrived.
	In this case, it blocks until the number of bytes read is equal to 'size'.

	\param sock: the connected socket currently opened.

	\param buffer: a char pointer to a user-allocated buffer in which data has to be stored

	\param size: size of the allocated buffer. WARNING: this indicates the number of bytes
	that we are expecting to be read.

	\param receiveall: if '0' (or SOCK_RECEIVEALL_NO), it returns as soon as some data 
	is ready; otherwise, (or SOCK_RECEIVEALL_YES) it waits until 'size' data has been 
	received (in case the socket does not have enough data available).

	\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 number of bytes read if everything is fine, '-1' if some errors occurred.
	The error message is returned in the 'errbuf' variable.
*/
int sock_recv(SOCKET sock, char *buffer, int size, int receiveall, char *errbuf, int errbuflen)
{
int nread;
int totread= 0;
	// We can obtain the same result using the MSG_WAITALL flag
	// However, this is not supported by recv() in Win32

	if (size == 0)
	{
		SOCK_ASSERT("I have been requested to read zero bytes", 1);
		return 0;
	}

again:
	nread= recv(sock, &(buffer[totread]), size - totread, 0);

	if (nread == -1)
	{
		sock_geterror("recv(): ", errbuf, errbuflen);
		return -1;
	}

	if (nread == 0)
	{
		if (errbuf)
		{
			snprintf(errbuf, errbuflen, "The other host terminated the connection.");
			errbuf[errbuflen - 1]= 0;
		}

		return -1;
	}

	// If we want to return as soon as some data has been received, 
	// let's do the job
	if (!receiveall)
		return nread;

	totread+= nread;

	if (totread != size)
		goto again;

	return totread;
}



/*!
	\brief It discards N bytes that are currently waiting to be read on the current socket.

	This function is useful in case we receive a message we cannot undestand (e.g.
	wrong version number when receiving a network packet), so that we have to discard all 
	data before reading a new message.

	This function will read 'size' bytes from the socket and discard them.
	It defines an internal buffer in which data will be copied; however, in case
	this buffer is not large enough, it will cycle in order to read everything as well.

	\param sock: the connected socket currently opened.

	\param size: number of bytes that have to be discarded.

	\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_discard(SOCKET sock, int size, char *errbuf, int errbuflen)
{
#define TEMP_BUF_SIZE 32768

char buffer[TEMP_BUF_SIZE];		// network buffer, to be used when the message is discarded

	// A static allocation avoids the need of a 'malloc()' each time we want to discard a message
	// Our feeling is that a buffer if 32KB is enough for most of the application;
	// in case this is not enough, the "while" loop discards the message by calling the 
	// sockrecv() several times.
	// We do not want to create a bigger variable because this causes the program to exit on
	// some platforms (e.g. BSD)

	while (size > TEMP_BUF_SIZE)
	{
		if (sock_recv(sock, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
			return -1;

		size-= TEMP_BUF_SIZE;
	}

	// If there is still data to be discarded
	// In this case, the data can fit into the temporaty buffer
	if (size)
	{
		if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
			return -1;
	}

⌨️ 快捷键说明

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