📄 sockutils.c
字号:
SOCK_ASSERT("I'm currently discarding data\n", 1);
return 0;
}
/*!
\brief Checks that one host (identified by the sockaddr_storage structure) belongs to an 'allowed list'.
This function is useful after an accept() call in order to check if the connecting
host is allowed to connect to me. To do that, we have a buffer that keeps the list of the
allowed host; this function checks the sockaddr_storage structure of the connecting host
against this host list, and it returns '0' is the host is included in this list.
\param hostlist: pointer to a string that contains the list of the allowed host.
\param sep: a string that keeps the separators used between the hosts (for example the
space character) in the host list.
\param from: a sockaddr_storage structure, as it is returned by the accept() call.
\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 It returns:
- '1' if the host list is empty
- '0' if the host belongs to the host list (and therefore it is allowed to connect)
- '-1' in case the host does not belong to the host list (and therefore it is not allowed to connect
- '-2' in case or error. The error message is returned in the 'errbuf' variable.
*/
int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf, int errbuflen)
{
// checks if the connecting host is among the ones allowed
if ( (hostlist) && (hostlist[0]) )
{
char *token; // temp, needed to separate items into the hostlist
struct addrinfo *addrinfo, *ai_next;
char *temphostlist;
temphostlist= (char *) malloc (strlen(hostlist) + 1);
if (temphostlist == NULL)
{
sock_geterror("sock_check_hostlist(), malloc() failed", errbuf, errbuflen);
return -2;
}
// The problem is that strtok modifies the original variable by putting '0' at the end of each token
// So, we have to create a new temporary string in which the original content is kept
strcpy(temphostlist, hostlist);
token= strtok(temphostlist, sep);
// it avoids a warning in the compilation ('addrinfo used but not initialized')
addrinfo = NULL;
while( token != NULL )
{
struct addrinfo hints;
int retval;
addrinfo = NULL;
memset(&hints, 0, sizeof (struct addrinfo) );
hints.ai_family = PF_UNSPEC;
hints.ai_socktype= SOCK_STREAM;
retval = getaddrinfo(token, "0", &hints, &addrinfo);
if (retval != 0)
{
if (errbuf)
{
snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval));
errbuf[errbuflen - 1]= 0;
}
SOCK_ASSERT(errbuf, 1);
// Get next token
token = strtok( NULL, sep);
continue;
}
// ai_next is required to preserve the content of addrinfo, in order to deallocate it properly
ai_next= addrinfo;
while(ai_next)
{
if (sock_cmpaddr(from, (struct sockaddr_storage *) ai_next->ai_addr) == 0)
{
free(temphostlist);
return 0;
}
// If we are here, it means that the current address does not matches
// Let's try with the next one in the header chain
ai_next= ai_next->ai_next;
}
freeaddrinfo(addrinfo);
addrinfo= NULL;
// Get next token
token = strtok( NULL, sep);
}
if (addrinfo)
{
freeaddrinfo(addrinfo);
addrinfo= NULL;
}
if (errbuf)
{
snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused.");
errbuf[errbuflen - 1]= 0;
}
free(temphostlist);
return -1;
}
// No hostlist, so we have to return 'empty list'
return 1;
}
/*!
\brief Compares two addresses contained into two sockaddr_storage structures.
This function is useful to compare two addresses, given their internal representation,
i.e. an sockaddr_storage structure.
The two structures do not need to be sockaddr_storage; you can have both 'sockaddr_in' and
sockaddr_in6, properly acsted in order to be compliant to the function interface.
This function will return '0' if the two addresses matches, '-1' if not.
\param first: a sockaddr_storage structure, (for example the one that is returned by an
accept() call), containing the first address to compare.
\param second: a sockaddr_storage structure containing the second address to compare.
\return '0' if the addresses are equal, '-1' if they are different.
*/
int sock_cmpaddr(struct sockaddr_storage *first, struct sockaddr_storage *second)
{
if (first->ss_family == second->ss_family)
{
if (first->ss_family == AF_INET)
{
if (memcmp( &(((struct sockaddr_in *) first)->sin_addr),
&(((struct sockaddr_in *) second)->sin_addr),
sizeof(struct in_addr) ) == 0)
return 0;
}
else // address family is AF_INET6
{
if (memcmp( &(((struct sockaddr_in6 *) first)->sin6_addr),
&(((struct sockaddr_in6 *) second)->sin6_addr),
sizeof(struct in6_addr) ) == 0)
return 0;
}
}
return -1;
}
/*!
\brief It gets the address/port the system picked for this socket (on connected sockets).
It is used to return the addess and port the server picked for our socket on the local machine.
It works only on:
- connected sockets
- server sockets
On unconnected client sockets it does not work because the system dynamically chooses a port
only when the socket calls a send() call.
\param sock: the connected socket currently opened.
\param address: it contains the address that will be returned by the function. This buffer
must be properly allocated by the user. The address can be either literal or numeric depending
on the value of 'Flags'.
\param addrlen: the length of the 'address' buffer.
\param port: it contains the port that will be returned by the function. This buffer
must be properly allocated by the user.
\param portlen: the length of the 'port' buffer.
\param flags: a set of flags (the ones defined into the getnameinfo() standard socket function)
that determine if the resulting address must be in numeric / literal form, and so on.
\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 It returns '-1' if this function succeedes, '0' otherwise.
The address and port corresponding are returned back in the buffers 'address' and 'port'.
In any case, the returned strings are '0' terminated.
\warning If the socket is using a connectionless protocol, the address may not be available
until I/O occurs on the socket.
*/
int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen)
{
struct sockaddr_storage mysockaddr;
socklen_t sockaddrlen;
sockaddrlen = sizeof(struct sockaddr_storage);
if (getsockname(sock, (struct sockaddr *) &mysockaddr, &sockaddrlen) == -1)
{
sock_geterror("getsockname(): ", errbuf, errbuflen);
return 0;
}
else
{
// Returns the numeric address of the host that triggered the error
return sock_getascii_addrport(&mysockaddr, address, addrlen, port, portlen, flags, errbuf, errbuflen);
}
return 0;
}
/*!
\brief It retrieves two strings containing the address and the port of a given 'sockaddr' variable.
This function is basically an extended version of the inet_ntop(), which does not exist in
WIN32 because the same result can be obtained by using the getnameinfo().
However, differently from inet_ntop(), this function is able to return also literal names
(e.g. 'locahost') dependingly from the 'Flags' parameter.
The function accepts a sockaddr_storage variable (which can be returned by several functions
like bind(), connect(), accept(), and more) and it transforms its content into a 'human'
form. So, for instance, it is able to translate an hex address (stored in bynary form) into
a standard IPv6 address like "::1".
The behaviour of this function depends on the parameters we have in the 'Flags' variable, which
are the ones allowed in the standard getnameinfo() socket function.
\param sockaddr: a 'sockaddr_in' or 'sockaddr_in6' structure containing the address that
need to be translated from network form into the presentation form. This structure must be
zero-ed prior using it, and the address family field must be filled with the proper value.
The user must cast any 'sockaddr_in' or 'sockaddr_in6' structures to 'sockaddr_storage' before
calling this function.
\param address: it contains the address that will be returned by the function. This buffer
must be properly allocated by the user. The address can be either literal or numeric depending
on the value of 'Flags'.
\param addrlen: the length of the 'address' buffer.
\param port: it contains the port that will be returned by the function. This buffer
must be properly allocated by the user.
\param portlen: the length of the 'port' buffer.
\param flags: a set of flags (the ones defined into the getnameinfo() standard socket function)
that determine if the resulting address must be in numeric / literal form, and so on.
\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 It returns '-1' if this function succeedes, '0' otherwise.
The address and port corresponding to the given SockAddr are returned back in the buffers 'address'
and 'port'.
In any case, the returned strings are '0' terminated.
*/
int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen)
{
socklen_t sockaddrlen;
int retval; // Variable that keeps the return value;
retval= -1;
#ifdef WIN32
if (sockaddr->ss_family == AF_INET)
sockaddrlen = sizeof(struct sockaddr_in);
else
sockaddrlen = sizeof(struct sockaddr_in6);
#else
sockaddrlen = sizeof(struct sockaddr_storage);
#endif
if ((flags & NI_NUMERICHOST) == 0) // Check that we want literal names
{
if ( (sockaddr->ss_family == AF_INET6) &&
(memcmp( &((struct sockaddr_in6 *) sockaddr)->sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(struct in6_addr) ) == 0) )
{
if (address)
strncpy(address, SOCKET_NAME_NULL_DAD, addrlen);
return retval;
}
}
if ( getnameinfo((struct sockaddr *) sockaddr, sockaddrlen, address, addrlen, port, portlen, flags) != 0)
{
// If the user wants to receive an error message
if (errbuf)
{
sock_geterror("getnameinfo(): ", errbuf, errbuflen);
errbuf[errbuflen-1]= 0;
}
if (address)
{
strncpy(address, SOCKET_NO_NAME_AVAILABLE, addrlen);
address[addrlen-1]= 0;
}
if (port)
{
strncpy(port, SOCKET_NO_PORT_AVAILABLE, portlen);
port[portlen-1]= 0;
}
retval= 0;
}
return retval;
}
/*!
\brief It translates an address from the 'presentation' form into the 'network' form.
This function basically replaces inet_pton(), which does not exist in WIN32 because
the same result can be obtained by using the getaddrinfo().
An addictional advantage is that 'Address' can be both a numeric address (e.g. '127.0.0.1',
like in inet_pton() ) and a literal name (e.g. 'localhost').
This function does the reverse job of sock_getascii_addrport().
\param address: a zero-terminated string which contains the name you have to
translate. The name can be either literal (e.g. 'localhost') or numeric (e.g. '::1').
\param sockaddr: a user-allocated sockaddr_storage structure which will contains the
'network' form of the requested address.
\param addr_family: a constant which can assume the following values:
- 'AF_INET' if we want to ping an IPv4 host
- 'AF_INET6' if we want to ping an IPv6 host
- 'AF_UNSPEC' if we do not have preferences about the protocol used to ping the host
\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 '-1' if the translation succeded, '-2' if there was some non critical error, '0'
otherwise. In case it fails, the content of the SockAddr variable remains unchanged.
A 'non critical error' can occur in case the 'Address' is a literal name, which can be mapped
to several network addresses (e.g. 'foo.bar.com' => '10.2.2.2' and '10.2.2.3'). In this case
the content of the SockAddr parameter will be the address corresponding to the first mapping.
\warning The sockaddr_storage structure MUST be allocated by the user.
*/
int sock_present2network(const char *address, struct sockaddr_storage *sockaddr, int addr_family, char *errbuf, int errbuflen)
{
int retval;
struct addrinfo *addrinfo;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints) );
hints.ai_family= addr_family;
if ( (retval= sock_initaddress(address, "22222" /* fake port */, &hints, &addrinfo, errbuf, errbuflen)) == -1 )
return 0;
if (addrinfo->ai_family == PF_INET)
memcpy(sockaddr, addrinfo->ai_addr, sizeof(struct sockaddr_in) );
else
memcpy(sockaddr, addrinfo->ai_addr, sizeof(struct sockaddr_in6) );
if (addrinfo->ai_next != NULL)
{
freeaddrinfo(addrinfo);
if (errbuf)
{
snprintf(errbuf, errbuflen, "More than one socket requested; using the first one returned");
errbuf[errbuflen - 1]= 0;
}
return -2;
}
freeaddrinfo(addrinfo);
return -1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -