diff options
-rw-r--r-- | Win32/Prj/wpcap.vcxproj | 14 | ||||
-rw-r--r-- | Win32/Prj/wpcap.vcxproj.filters | 18 | ||||
-rw-r--r-- | pcap-new.c | 1269 | ||||
-rw-r--r-- | pcap-remote.c | 2142 | ||||
-rw-r--r-- | pcap-remote.h | 474 | ||||
-rw-r--r-- | pcap-win32.c | 10 | ||||
-rw-r--r-- | pcap/pcap.h | 5 | ||||
-rw-r--r-- | remote-ext.h | 447 | ||||
-rw-r--r-- | sockutils.c | 1263 | ||||
-rw-r--r-- | sockutils.h | 248 |
10 files changed, 5884 insertions, 6 deletions
diff --git a/Win32/Prj/wpcap.vcxproj b/Win32/Prj/wpcap.vcxproj index 3eca465f..6aa90767 100644 --- a/Win32/Prj/wpcap.vcxproj +++ b/Win32/Prj/wpcap.vcxproj @@ -96,7 +96,7 @@ <SuppressStartupBanner>true</SuppressStartupBanner> <WarningLevel>Level3</WarningLevel> <AdditionalIncludeDirectories>../../;../../lbl/;../../bpf/;../include/;../../../../common;../../../../dag/include;../../../../dag/drv/windows;../../../Win32-Extensions;./;Win32-Extensions;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>NDEBUG;YY_NEVER_INTERACTIVE;yylval=pcap_lval;_USRDLL;BUILDING_PCAP;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;HAVE_ADDRINFO;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>NDEBUG;YY_NEVER_INTERACTIVE;yylval=pcap_lval;_USRDLL;BUILDING_PCAP;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;HAVE_ADDRINFO;HAVE_REMOTE;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AssemblerListingLocation>.\Release\</AssemblerListingLocation> <ObjectFileName>.\Release\</ObjectFileName> <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName> @@ -123,7 +123,7 @@ win_bison -ppcap_ --yacc --output=..\..\grammar.c --defines ..\..\grammar.y</Com <SuppressStartupBanner>true</SuppressStartupBanner> <WarningLevel>Level3</WarningLevel> <AdditionalIncludeDirectories>../../;../../lbl/;../../bpf/;../include/;../../../../common;../../../../dag/include;../../../../dag/drv/windows;../../../Win32-Extensions;./;Win32-Extensions;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>NDEBUG;YY_NEVER_INTERACTIVE;yylval=pcap_lval;_USRDLL;BUILDING_PCAP;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;HAVE_ADDRINFO;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>NDEBUG;YY_NEVER_INTERACTIVE;yylval=pcap_lval;_USRDLL;BUILDING_PCAP;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;HAVE_ADDRINFO;HAVE_REMOTE;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AssemblerListingLocation>.\Release\</AssemblerListingLocation> <ObjectFileName>.\Release\</ObjectFileName> <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName> @@ -151,7 +151,7 @@ win_bison -ppcap_ --yacc --output=..\..\grammar.c --defines ..\..\grammar.y</Com <MinimalRebuild>true</MinimalRebuild> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> <AdditionalIncludeDirectories>../../;../../lbl/;../../bpf/;../include/;../../../../common;../../../../dag/include;../../../../dag/drv/windows;../../../Win32-Extensions;./;Win32-Extensions;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>_DEBUG;YY_NEVER_INTERACTIVE;yylval=pcap_lval;_USRDLL;BUILDING_PCAP;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;HAVE_ADDRINFO;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_DEBUG;YY_NEVER_INTERACTIVE;yylval=pcap_lval;_USRDLL;BUILDING_PCAP;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;HAVE_ADDRINFO;HAVE_REMOTE;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AssemblerListingLocation>.\Debug\</AssemblerListingLocation> <ObjectFileName>.\Debug\</ObjectFileName> <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName> @@ -179,7 +179,7 @@ win_bison -ppcap_ --yacc --output=..\..\grammar.c --defines ..\..\grammar.y</Com <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <AdditionalIncludeDirectories>../../;../../lbl/;../../bpf/;../include/;../../../../common;../../../../dag/include;../../../../dag/drv/windows;../../../Win32-Extensions;./;Win32-Extensions;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>_DEBUG;YY_NEVER_INTERACTIVE;yylval=pcap_lval;_USRDLL;BUILDING_PCAP;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;HAVE_ADDRINFO;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_DEBUG;YY_NEVER_INTERACTIVE;yylval=pcap_lval;_USRDLL;BUILDING_PCAP;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;HAVE_ADDRINFO;HAVE_REMOTE;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AssemblerListingLocation>.\Debug\</AssemblerListingLocation> <ObjectFileName>.\Debug\</ObjectFileName> <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName> @@ -210,18 +210,24 @@ win_bison -ppcap_ --yacc --output=..\..\grammar.c --defines ..\..\grammar.y</Com <ClCompile Include="..\..\nametoaddr.c" /> <ClCompile Include="..\..\optimize.c" /> <ClCompile Include="..\..\pcap-common.c" /> + <ClCompile Include="..\..\pcap-new.c" /> + <ClCompile Include="..\..\pcap-remote.c" /> <ClCompile Include="..\..\pcap-win32.c" /> <ClCompile Include="..\..\pcap.c" /> <ClCompile Include="..\..\savefile.c" /> <ClCompile Include="..\..\scanner.c" /> <ClCompile Include="..\..\sf-pcap-ng.c" /> <ClCompile Include="..\..\sf-pcap.c" /> + <ClCompile Include="..\..\sockutils.c" /> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\pcap-common.h" /> <ClInclude Include="..\..\pcap-int.h" /> + <ClInclude Include="..\..\pcap-remote.h" /> <ClInclude Include="..\..\pcap-stdinc.h" /> <ClInclude Include="..\..\pcap.h" /> + <ClInclude Include="..\..\remote-ext.h" /> + <ClInclude Include="..\..\sockutils.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/Win32/Prj/wpcap.vcxproj.filters b/Win32/Prj/wpcap.vcxproj.filters index 3dea04c3..3ec20e6e 100644 --- a/Win32/Prj/wpcap.vcxproj.filters +++ b/Win32/Prj/wpcap.vcxproj.filters @@ -55,6 +55,15 @@ <ClCompile Include="..\..\missing\win_snprintf.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\pcap-new.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\pcap-remote.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\sockutils.c"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <Filter Include="Header Files"> @@ -77,5 +86,14 @@ <ClInclude Include="..\..\pcap-int.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\pcap-remote.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\remote-ext.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\sockutils.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project>
\ No newline at end of file diff --git a/pcap-new.c b/pcap-new.c new file mode 100644 index 00000000..bd0ac82e --- /dev/null +++ b/pcap-new.c @@ -0,0 +1,1269 @@ +/* + * Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2008 CACE Technologies, Davis (California) + * 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, CACE Technologies + * 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcap-int.h" // for the details of the pcap_t structure +#include "pcap-remote.h" +#include "sockutils.h" +#include <errno.h> // for the errno variable +#include <stdlib.h> // for malloc(), free(), ... +#include <string.h> // for strstr, etc + +#ifndef WIN32 +#include <dirent.h> // for readdir +#endif + + + +/* Keeps a list of all the opened connections in the active mode. */ +extern struct activehosts *activeHosts; + + +/* + * \brief Keeps the main socket identifier when we want to accept a new remote connection (active mode only). + * See the documentation of pcap_remoteact_accept() and pcap_remoteact_cleanup() for more details. + */ +SOCKET sockmain; + + +/* String identifier to be used in the pcap_findalldevs_ex() */ +#define PCAP_TEXT_SOURCE_FILE "File" +/* String identifier to be used in the pcap_findalldevs_ex() */ +#define PCAP_TEXT_SOURCE_ADAPTER "Network adapter" + +/* String identifier to be used in the pcap_findalldevs_ex() */ +#define PCAP_TEXT_SOURCE_ON_LOCAL_HOST "on local host" +/* String identifier to be used in the pcap_findalldevs_ex() */ +#define PCAP_TEXT_SOURCE_ON_REMOTE_HOST "on remote node" + +/* +* Private data for capturing on WinPcap devices. +*/ +struct pcap_win { + int nonblock; + + int filtering_in_kernel; /* using kernel filter */ + +#ifdef HAVE_DAG_API + int dag_fcs_bits; /* Number of checksum bits from link layer */ +#endif +}; + +/**************************************************** + * * + * Function bodies * + * * + ****************************************************/ + +int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf) +{ + SOCKET sockctrl; /* socket descriptor of the control connection */ + unsigned int nread = 0; /* number of bytes of the payload read from the socket */ + struct addrinfo hints; /* temp variable needed to resolve hostnames into to socket representation */ + struct addrinfo *addrinfo; /* temp variable needed to resolve hostnames into to socket representation */ + struct rpcap_header header; /* structure that keeps the general header of the rpcap protocol */ + int i, j; /* temp variables */ + int naddr; /* temp var needed to avoid problems with IPv6 addresses */ + int retval; /* store the return value of the functions */ + int nif; /* Number of interfaces listed */ + int active = 0; /* 'true' if we the other end-party is in active mode */ + char host[PCAP_BUF_SIZE], port[PCAP_BUF_SIZE], name[PCAP_BUF_SIZE], path[PCAP_BUF_SIZE], filename[PCAP_BUF_SIZE]; + int type; + pcap_t *fp; + char tmpstring[PCAP_BUF_SIZE + 1]; /* Needed to convert names and descriptions from 'old' syntax to the 'new' one */ + pcap_if_t *dev; /* Previous device into the pcap_if_t chain */ + + + if (strlen(source) > PCAP_BUF_SIZE) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); + return -1; + } + + /* + * Determine the type of the source (file, local, remote) + * There are some differences if pcap_findalldevs_ex() is called to list files and remote adapters. + * In the first case, the name of the directory we have to look into must be present (therefore + * the 'name' parameter of the pcap_parsesrcstr() is present). + * In the second case, the name of the adapter is not required (we need just the host). So, we have + * to use a first time this function to get the source type, and a second time to get the appropriate + * info, which depends on the source type. + */ + if (pcap_parsesrcstr(source, &type, NULL, NULL, NULL, errbuf) == -1) + return -1; + + if (type == PCAP_SRC_IFLOCAL) + { + if (pcap_parsesrcstr(source, &type, host, NULL, NULL, errbuf) == -1) + return -1; + + /* Initialize temporary string */ + tmpstring[PCAP_BUF_SIZE] = 0; + + /* The user wants to retrieve adapters from a local host */ + if (pcap_findalldevs(alldevs, errbuf) == -1) + return -1; + + if ((alldevs == NULL) || (*alldevs == NULL)) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "No interfaces found! Make sure libpcap/WinPcap is properly installed" + " on the local machine."); + return -1; + } + + /* Scan all the interfaces and modify name and description */ + /* This is a trick in order to avoid the re-implementation of the pcap_findalldevs here */ + dev = *alldevs; + while (dev) + { + /* Create the new device identifier */ + if (pcap_createsrcstr(tmpstring, PCAP_SRC_IFLOCAL, NULL, NULL, dev->name, errbuf) == -1) + return -1; + + /* Delete the old pointer */ + free(dev->name); + + dev->name = (char *)malloc(strlen(tmpstring) + 1); + + if (dev->name == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + return -1; + } + + /* Copy the new device identifier into the correct memory location */ + strncpy(dev->name, tmpstring, strlen(tmpstring) + 1); + + + /* Create the new device description */ + if ((dev->description == NULL) || (dev->description[0] == 0)) + pcap_snprintf(tmpstring, sizeof(tmpstring) - 1, "%s '%s' %s", PCAP_TEXT_SOURCE_ADAPTER, + dev->name, PCAP_TEXT_SOURCE_ON_LOCAL_HOST); + else + pcap_snprintf(tmpstring, sizeof(tmpstring) - 1, "%s '%s' %s", PCAP_TEXT_SOURCE_ADAPTER, + dev->description, PCAP_TEXT_SOURCE_ON_LOCAL_HOST); + + /* Delete the old pointer */ + free(dev->description); + + dev->description = (char *)malloc(strlen(tmpstring) + 1); + + if (dev->description == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + return -1; + } + + /* Copy the new device description into the correct memory location */ + strncpy(dev->description, tmpstring, strlen(tmpstring) + 1); + + dev = dev->next; + } + + return 0; + } + + (*alldevs) = NULL; + + if (type == PCAP_SRC_FILE) + { + size_t stringlen; +#ifdef WIN32 + WIN32_FIND_DATA filedata; + HANDLE filehandle; +#else + struct dirent *filedata; + DIR *unixdir; +#endif + + if (pcap_parsesrcstr(source, &type, host, port, name, errbuf) == -1) + return -1; + + /* Check that the filename is correct */ + stringlen = strlen(name); + + /* The directory must end with '\' in Win32 and '/' in UNIX */ +#ifdef WIN32 +#define ENDING_CHAR '\\' +#else +#define ENDING_CHAR '/' +#endif + + if (name[stringlen - 1] != ENDING_CHAR) + { + name[stringlen] = ENDING_CHAR; + name[stringlen + 1] = 0; + + stringlen++; + } + + /* Save the path for future reference */ + pcap_snprintf(path, sizeof(path), "%s", name); + +#ifdef WIN32 + /* To perform directory listing, Win32 must have an 'asterisk' as ending char */ + if (name[stringlen - 1] != '*') + { + name[stringlen] = '*'; + name[stringlen + 1] = 0; + } + + filehandle = FindFirstFile(name, &filedata); + + if (filehandle == INVALID_HANDLE_VALUE) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); + return -1; + } + +#else + /* opening the folder */ + unixdir= opendir(path); + + /* get the first file into it */ + filedata= readdir(unixdir); + + if (filedata == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); + return -1; + } +#endif + + do + { + +#ifdef WIN32 + pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata.cFileName); +#else + pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata->d_name); +#endif + + fp = pcap_open_offline(filename, errbuf); + + if (fp) + { + /* allocate the main structure */ + if (*alldevs == NULL) /* This is in case it is the first file */ + { + (*alldevs) = (pcap_if_t *)malloc(sizeof(pcap_if_t)); + dev = (*alldevs); + } + else + { + dev->next = (pcap_if_t *)malloc(sizeof(pcap_if_t)); + dev = dev->next; + } + + /* check that the malloc() didn't fail */ + if (dev == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + return -1; + } + + /* Initialize the structure to 'zero' */ + memset(dev, 0, sizeof(pcap_if_t)); + + /* Create the new source identifier */ + if (pcap_createsrcstr(tmpstring, PCAP_SRC_FILE, NULL, NULL, filename, errbuf) == -1) + return -1; + + stringlen = strlen(tmpstring); + + dev->name = (char *)malloc(stringlen + 1); + if (dev->name == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + return -1; + } + + strncpy(dev->name, tmpstring, stringlen); + + dev->name[stringlen] = 0; + + /* Create the description */ + pcap_snprintf(tmpstring, sizeof(tmpstring) - 1, "%s '%s' %s", PCAP_TEXT_SOURCE_FILE, + filename, PCAP_TEXT_SOURCE_ON_LOCAL_HOST); + + stringlen = strlen(tmpstring); + + dev->description = (char *)malloc(stringlen + 1); + + if (dev->description == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + return -1; + } + + /* Copy the new device description into the correct memory location */ + strncpy(dev->description, tmpstring, stringlen + 1); + + pcap_close(fp); + } + } +#ifdef WIN32 + while (FindNextFile(filehandle, &filedata) != 0); +#else + while ( (filedata= readdir(unixdir)) != NULL); +#endif + + +#ifdef WIN32 + /* Close the search handle. */ + FindClose(filehandle); +#endif + + return 0; + } + + /* If we come here, it is a remote host */ + + /* Retrieve the needed data for getting adapter list */ + if (pcap_parsesrcstr(source, &type, host, port, NULL, errbuf) == -1) + return -1; + + /* Warning: this call can be the first one called by the user. */ + /* For this reason, we have to initialize the WinSock support. */ + if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + + /* Check for active mode */ + if ((retval = rpcap_remoteact_getsock(host, errbuf)) == -1) + return -1; + + if (retval) + { + sockctrl = retval; + active = 1; + } + else /* we're not in active mode; let's opening a new control connection (if needed) */ + { + addrinfo = NULL; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((port == NULL) || (port[0] == 0)) + { + /* the user chose not to specify the port */ + if (sock_initaddress(host, RPCAP_DEFAULT_NETPORT, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + } + else + { + if (sock_initaddress(host, port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + } + + if ((sockctrl = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + /* addrinfo is no longer used */ + freeaddrinfo(addrinfo); + addrinfo = NULL; + + if (rpcap_sendauth(sockctrl, auth, errbuf) == -1) + { + /* Control connection has to be closed only in case the remote machine is in passive mode */ + if (!active) + sock_close(sockctrl, NULL, 0); + return -1; + } + } + + /* RPCAP findalldevs command */ + rpcap_createhdr(&header, RPCAP_MSG_FINDALLIF_REQ, 0, 0); + + if (sock_send(sockctrl, (char *)&header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + if (sock_recv(sockctrl, (char *)&header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + /* Checks if the message is correct */ + retval = rpcap_checkmsg(errbuf, sockctrl, &header, RPCAP_MSG_FINDALLIF_REPLY, RPCAP_MSG_ERROR, 0); + + if (retval != RPCAP_MSG_FINDALLIF_REPLY) /* the message is not the one expected */ + { + switch (retval) + { + case -3: /* Unrecoverable network error */ + case -2: /* The other endpoint send a message that is not allowed here */ + case -1: /* The other endpoint has a version number that is not compatible with our */ + break; + + case RPCAP_MSG_ERROR: /* The other endpoint reported an error */ + break; + + default: + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Internal error"); + break; + }; + } + + if (!active) + sock_close(sockctrl, NULL, 0); + + return -1; + } + + /* read the number of interfaces */ + nif = ntohs(header.value); + + /* loop until all interfaces have been received */ + for (i = 0; i < nif; i++) + { + struct rpcap_findalldevs_if findalldevs_if; + char tmpstring2[PCAP_BUF_SIZE + 1]; /* Needed to convert names and descriptions from 'old' syntax to the 'new' one */ + size_t stringlen; + + tmpstring2[PCAP_BUF_SIZE] = 0; + + /* receive the findalldevs structure from remote host */ + if ((nread += sock_recv(sockctrl, (char *)&findalldevs_if, + sizeof(struct rpcap_findalldevs_if), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + findalldevs_if.namelen = ntohs(findalldevs_if.namelen); + findalldevs_if.desclen = ntohs(findalldevs_if.desclen); + findalldevs_if.naddr = ntohs(findalldevs_if.naddr); + + /* allocate the main structure */ + if (i == 0) + { + (*alldevs) = (pcap_if_t *)malloc(sizeof(pcap_if_t)); + dev = (*alldevs); + } + else + { + dev->next = (pcap_if_t *)malloc(sizeof(pcap_if_t)); + dev = dev->next; + } + + /* check that the malloc() didn't fail */ + if (dev == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + goto error; + } + + /* Initialize the structure to 'zero' */ + memset(dev, 0, sizeof(pcap_if_t)); + + /* allocate mem for name and description */ + if (findalldevs_if.namelen) + { + + if (findalldevs_if.namelen >= sizeof(tmpstring)) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface name too long"); + goto error; + } + + /* Retrieve adapter name */ + if ((nread += sock_recv(sockctrl, tmpstring, findalldevs_if.namelen, SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + tmpstring[findalldevs_if.namelen] = 0; + + /* Create the new device identifier */ + if (pcap_createsrcstr(tmpstring2, PCAP_SRC_IFREMOTE, host, port, tmpstring, errbuf) == -1) + return -1; + + stringlen = strlen(tmpstring2); + + dev->name = (char *)malloc(stringlen + 1); + if (dev->name == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + goto error; + } + + /* Copy the new device name into the correct memory location */ + strncpy(dev->name, tmpstring2, stringlen + 1); + } + + if (findalldevs_if.desclen) + { + if (findalldevs_if.desclen >= sizeof(tmpstring)) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface description too long"); + goto error; + } + + /* Retrieve adapter description */ + if ((nread += sock_recv(sockctrl, tmpstring, findalldevs_if.desclen, SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + tmpstring[findalldevs_if.desclen] = 0; + + + pcap_snprintf(tmpstring2, sizeof(tmpstring2) - 1, "%s '%s' %s %s", PCAP_TEXT_SOURCE_ADAPTER, + tmpstring, PCAP_TEXT_SOURCE_ON_REMOTE_HOST, host); + + stringlen = strlen(tmpstring2); + + dev->description = (char *)malloc(stringlen + 1); + + if (dev->description == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + goto error; + } + + /* Copy the new device description into the correct memory location */ + strncpy(dev->description, tmpstring2, stringlen + 1); + } + + dev->flags = ntohl(findalldevs_if.flags); + + naddr = 0; + /* loop until all addresses have been received */ + for (j = 0; j < findalldevs_if.naddr; j++) + { + struct rpcap_findalldevs_ifaddr ifaddr; + + /* Retrieve the interface addresses */ + if ((nread += sock_recv(sockctrl, (char *)&ifaddr, + sizeof(struct rpcap_findalldevs_ifaddr), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + /* WARNING libpcap bug: the address listing is available only for AF_INET */ + if (ntohs(ifaddr.addr.ss_family) == AF_INET) + { + struct pcap_addr *addr; + + if (naddr == 0) + { + dev->addresses = (struct pcap_addr *) malloc(sizeof(struct pcap_addr)); + addr = dev->addresses; + } + else + { + addr->next = (struct pcap_addr *) malloc(sizeof(struct pcap_addr)); + addr = addr->next; + } + naddr++; + + if (addr == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + goto error; + } + addr->next = NULL; + + if (rpcap_deseraddr((struct sockaddr_storage *) &ifaddr.addr, + (struct sockaddr_storage **) &addr->addr, errbuf) == -1) + goto error; + if (rpcap_deseraddr((struct sockaddr_storage *) &ifaddr.netmask, + (struct sockaddr_storage **) &addr->netmask, errbuf) == -1) + goto error; + if (rpcap_deseraddr((struct sockaddr_storage *) &ifaddr.broadaddr, + (struct sockaddr_storage **) &addr->broadaddr, errbuf) == -1) + goto error; + if (rpcap_deseraddr((struct sockaddr_storage *) &ifaddr.dstaddr, + (struct sockaddr_storage **) &addr->dstaddr, errbuf) == -1) + goto error; + + if ((addr->addr == NULL) && (addr->netmask == NULL) && + (addr->broadaddr == NULL) && (addr->dstaddr == NULL)) + { + free(addr); + addr = NULL; + if (naddr == 1) + naddr = 0; /* the first item of the list had NULL addresses */ + } + } + } + } + + /* Checks if all the data has been read; if not, discard the data in excess */ + if (nread != ntohl(header.plen)) + { + if (sock_discard(sockctrl, ntohl(header.plen) - nread, errbuf, PCAP_ERRBUF_SIZE) == 1) + return -1; + } + + /* Control connection has to be closed only in case the remote machine is in passive mode */ + if (!active) + { + /* DO not send RPCAP_CLOSE, since we did not open a pcap_t; no need to free resources */ + if (sock_close(sockctrl, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + } + + /* To avoid inconsistencies in the number of sock_init() */ + sock_cleanup(); + + return 0; + +error: + /* + * In case there has been an error, I don't want to overwrite it with a new one + * if the following call fails. I want to return always the original error. + * + * Take care: this connection can already be closed when we try to close it. + * This happens because a previous error in the rpcapd, which requested to + * closed the connection. In that case, we already recognized that into the + * rpspck_isheaderok() and we already acknowledged the closing. + * In that sense, this call is useless here (however it is needed in case + * the client generates the error). + * + * Checks if all the data has been read; if not, discard the data in excess + */ + if (nread != ntohl(header.plen)) + { + if (sock_discard(sockctrl, ntohl(header.plen) - nread, NULL, 0) == 1) + return -1; + } + + /* Control connection has to be closed only in case the remote machine is in passive mode */ + if (!active) + sock_close(sockctrl, NULL, 0); + + /* To avoid inconsistencies in the number of sock_init() */ + sock_cleanup(); + + return -1; +} + + +int pcap_createsrcstr(char *source, int type, const char *host, const char *port, const char *name, char *errbuf) +{ + switch (type) + { + case PCAP_SRC_FILE: + { + strncpy(source, PCAP_SRC_FILE_STRING, PCAP_BUF_SIZE); + if ((name) && (*name)) + { + strncat(source, name, PCAP_BUF_SIZE); + return 0; + } + else + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The file name cannot be NULL."); + return -1; + } + } + + case PCAP_SRC_IFREMOTE: + { + strncpy(source, PCAP_SRC_IF_STRING, PCAP_BUF_SIZE); + if ((host) && (*host)) + { + if ((strcspn(host, "aAbBcCdDeEfFgGhHjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ")) == strlen(host)) + { + /* the host name does not contains alphabetic chars. So, it is a numeric address */ + /* In this case we have to include it between square brackets */ + strncat(source, "[", PCAP_BUF_SIZE); + strncat(source, host, PCAP_BUF_SIZE); + strncat(source, "]", PCAP_BUF_SIZE); + } + else + strncat(source, host, PCAP_BUF_SIZE); + + if ((port) && (*port)) + { + strncat(source, ":", PCAP_BUF_SIZE); + strncat(source, port, PCAP_BUF_SIZE); + } + + strncat(source, "/", PCAP_BUF_SIZE); + } + else + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The host name cannot be NULL."); + return -1; + } + + if ((name) && (*name)) + strncat(source, name, PCAP_BUF_SIZE); + + return 0; + } + + case PCAP_SRC_IFLOCAL: + { + strncpy(source, PCAP_SRC_IF_STRING, PCAP_BUF_SIZE); + + if ((name) && (*name)) + strncat(source, name, PCAP_BUF_SIZE); + + return 0; + } + + default: + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The interface type is not valid."); + return -1; + } + } +} + + +int pcap_parsesrcstr(const char *source, int *type, char *host, char *port, char *name, char *errbuf) +{ + char *ptr; + int ntoken; + char tmpname[PCAP_BUF_SIZE]; + char tmphost[PCAP_BUF_SIZE]; + char tmpport[PCAP_BUF_SIZE]; + int tmptype; + + /* Initialization stuff */ + tmpname[0] = 0; + tmphost[0] = 0; + tmpport[0] = 0; + + if (host) + *host = 0; + if (port) + *port = 0; + if (name) + *name = 0; + + /* Look for a 'rpcap://' identifier */ + if ((ptr = strstr(source, PCAP_SRC_IF_STRING)) != NULL) + { + if (strlen(PCAP_SRC_IF_STRING) == strlen(source)) + { + /* The source identifier contains only the 'rpcap://' string. */ + /* So, this is a local capture. */ + *type = PCAP_SRC_IFLOCAL; + return 0; + } + + ptr += strlen(PCAP_SRC_IF_STRING); + + if (strchr(ptr, '[')) /* This is probably a numeric address */ + { + ntoken = sscanf(ptr, "[%[1234567890:.]]:%[^/]/%s", tmphost, tmpport, tmpname); + + if (ntoken == 1) /* probably the port is missing */ + ntoken = sscanf(ptr, "[%[1234567890:.]]/%s", tmphost, tmpname); + + tmptype = PCAP_SRC_IFREMOTE; + } + else + { + ntoken = sscanf(ptr, "%[^/:]:%[^/]/%s", tmphost, tmpport, tmpname); + + if (ntoken == 1) + { + /* + * This can be due to two reasons: + * - we want a remote capture, but the network port is missing + * - we want to do a local capture + * To distinguish between the two, we look for the '/' char + */ + if (strchr(ptr, '/')) + { + /* We're on a remote capture */ + sscanf(ptr, "%[^/]/%s", tmphost, tmpname); + tmptype = PCAP_SRC_IFREMOTE; + } + else + { + /* We're on a local capture */ + if (*ptr) + strncpy(tmpname, ptr, PCAP_BUF_SIZE); + + /* Clean the host name, since it is a remote capture */ + /* NOTE: the host name has been assigned in the previous "ntoken= sscanf(...)" line */ + tmphost[0] = 0; + + tmptype = PCAP_SRC_IFLOCAL; + } + } + else + tmptype = PCAP_SRC_IFREMOTE; + } + + if (host) + strcpy(host, tmphost); + if (port) + strcpy(port, tmpport); + if (type) + *type = tmptype; + + if (name) + { + /* + * If the user wants the host name, but it cannot be located into the source string, return error + * However, if the user is not interested in the interface name (e.g. if we're called by + * pcap_findalldevs_ex(), which does not have interface name, do not return error + */ + if (tmpname[0]) + { + strcpy(name, tmpname); + } + else + { + if (errbuf) + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The interface name has not been specified in the source string."); + + return -1; + } + } + + return 0; + } + + /* Look for a 'file://' identifier */ + if ((ptr = strstr(source, PCAP_SRC_FILE_STRING)) != NULL) + { + ptr += strlen(PCAP_SRC_FILE_STRING); + if (*ptr) + { + if (name) + strncpy(name, ptr, PCAP_BUF_SIZE); + + if (type) + *type = PCAP_SRC_FILE; + + return 0; + } + else + { + if (errbuf) + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The file name has not been specified in the source string."); + + return -1; + } + + } + + /* Backward compatibility; the user didn't use the 'rpcap://, file://' specifiers */ + if ((source) && (*source)) + { + if (name) + strncpy(name, source, PCAP_BUF_SIZE); + + if (type) + *type = PCAP_SRC_IFLOCAL; + + return 0; + } + else + { + if (errbuf) + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The interface name has not been specified in the source string."); + + return -1; + } +}; + + +pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf) +{ + char host[PCAP_BUF_SIZE], port[PCAP_BUF_SIZE], name[PCAP_BUF_SIZE]; + int type; + pcap_t *fp; + int result; + + if (strlen(source) > PCAP_BUF_SIZE) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); + return NULL; + } + + /* determine the type of the source (file, local, remote) */ + if (pcap_parsesrcstr(source, &type, host, port, name, errbuf) == -1) + return NULL; + + + switch (type) + { + case PCAP_SRC_FILE: + fp = pcap_open_offline(name, errbuf); + break; + + case PCAP_SRC_IFREMOTE: + fp = pcap_create(source, errbuf); + if (fp == NULL) + { + return NULL; + } + + /* + * Although we already have host, port and iface, we prefer TO PASS only 'pars' to the + * pcap_open_remote() so that it has to call the pcap_parsesrcstr() again. + * This is less optimized, but much clearer. + */ + + result = pcap_opensource_remote(fp, auth); + + if (result != 0) + { + pcap_close(fp); + return NULL; + } + + struct pcap_md *md; /* structure used when doing a remote live capture */ + md = (struct pcap_md *) ((u_char*)fp->priv + sizeof(struct pcap_win)); + + fp->snapshot = snaplen; + fp->opt.timeout = read_timeout; + md->rmt_flags = flags; + break; + + case PCAP_SRC_IFLOCAL: + + fp = pcap_open_live(name, snaplen, (flags & PCAP_OPENFLAG_PROMISCUOUS), read_timeout, errbuf); + +#ifdef WIN32 + /* + * these flags are supported on Windows only + */ + if (fp != NULL && fp->adapter != NULL) + { + /* disable loopback capture if requested */ + if (flags & PCAP_OPENFLAG_NOCAPTURE_LOCAL) + { + if (!PacketSetLoopbackBehavior(fp->adapter, NPF_DISABLE_LOOPBACK)) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Unable to disable the capture of loopback packets."); + pcap_close(fp); + return NULL; + } + } + + /* set mintocopy to zero if requested */ + if (flags & PCAP_OPENFLAG_MAX_RESPONSIVENESS) + { + if (!PacketSetMinToCopy(fp->adapter, 0)) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Unable to set max responsiveness."); + pcap_close(fp); + return NULL; + } + } + } +#endif /* WIN32 */ + + break; + + default: + strcpy(errbuf, "Source type not supported"); + return NULL; + } + return fp; +} + + +struct pcap_samp *pcap_setsampling(pcap_t *p) +{ + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)p->priv + sizeof(struct pcap_win)); + return &(md->rmt_samp); +} + + +SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf) +{ + /* socket-related variables */ + struct addrinfo hints; /* temporary struct to keep settings needed to open the new socket */ + struct addrinfo *addrinfo; /* keeps the addrinfo chain; required to open a new socket */ + struct sockaddr_storage from; /* generic sockaddr_storage variable */ + socklen_t fromlen; /* keeps the length of the sockaddr_storage variable */ + SOCKET sockctrl; /* keeps the main socket identifier */ + struct activehosts *temp, *prev; /* temp var needed to scan he host list chain */ + + *connectinghost = 0; /* just in case */ + + /* Prepare to open a new server socket */ + memset(&hints, 0, sizeof(struct addrinfo)); + /* WARNING Currently it supports only ONE socket family among ipv4 and IPv6 */ + hints.ai_family = AF_INET; /* PF_UNSPEC to have both IPv4 and IPv6 server */ + hints.ai_flags = AI_PASSIVE; /* Ready to a bind() socket */ + hints.ai_socktype = SOCK_STREAM; + + /* Warning: this call can be the first one called by the user. */ + /* For this reason, we have to initialize the WinSock support. */ + if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + + /* Do the work */ + if ((port == NULL) || (port[0] == 0)) + { + if (sock_initaddress(address, RPCAP_DEFAULT_NETPORT_ACTIVE, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + SOCK_ASSERT(errbuf, 1); + return -2; + } + } + else + { + if (sock_initaddress(address, port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + SOCK_ASSERT(errbuf, 1); + return -2; + } + } + + + if ((sockmain = sock_open(addrinfo, SOCKOPEN_SERVER, 1, errbuf, PCAP_ERRBUF_SIZE)) == -1) + { + SOCK_ASSERT(errbuf, 1); + return -2; + } + + /* Connection creation */ + fromlen = sizeof(struct sockaddr_storage); + + sockctrl = accept(sockmain, (struct sockaddr *) &from, &fromlen); + + /* We're not using sock_close, since we do not want to send a shutdown */ + /* (which is not allowed on a non-connected socket) */ + closesocket(sockmain); + sockmain = 0; + + if (sockctrl == -1) + { + sock_geterror("accept(): ", errbuf, PCAP_ERRBUF_SIZE); + return -2; + } + + /* Get the numeric for of the name of the connecting host */ + if (getnameinfo((struct sockaddr *) &from, fromlen, connectinghost, RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST)) + { + sock_geterror("getnameinfo(): ", errbuf, PCAP_ERRBUF_SIZE); + rpcap_senderror(sockctrl, errbuf, PCAP_ERR_REMOTEACCEPT, NULL); + sock_close(sockctrl, NULL, 0); + return -1; + } + + /* checks if the connecting host is among the ones allowed */ + if (sock_check_hostlist((char *)hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0) + { + rpcap_senderror(sockctrl, errbuf, PCAP_ERR_REMOTEACCEPT, NULL); + sock_close(sockctrl, NULL, 0); + return -1; + } + + /* Send authentication to the remote machine */ + if (rpcap_sendauth(sockctrl, auth, errbuf) == -1) + { + rpcap_senderror(sockctrl, errbuf, PCAP_ERR_REMOTEACCEPT, NULL); + sock_close(sockctrl, NULL, 0); + return -3; + } + + /* Checks that this host does not already have a cntrl connection in place */ + + /* Initialize pointers */ + temp = activeHosts; + prev = NULL; + + while (temp) + { + /* This host already has an active connection in place, so I don't have to update the host list */ + if (sock_cmpaddr(&temp->host, &from) == 0) + return sockctrl; + + prev = temp; + temp = temp->next; + } + + /* The host does not exist in the list; so I have to update the list */ + if (prev) + { + prev->next = (struct activehosts *) malloc(sizeof(struct activehosts)); + temp = prev->next; + } + else + { + activeHosts = (struct activehosts *) malloc(sizeof(struct activehosts)); + temp = activeHosts; + } + + if (temp == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + rpcap_senderror(sockctrl, errbuf, PCAP_ERR_REMOTEACCEPT, NULL); + sock_close(sockctrl, NULL, 0); + return -1; + } + + memcpy(&temp->host, &from, fromlen); + temp->sockctrl = sockctrl; + temp->next = NULL; + + return sockctrl; +} + + +int pcap_remoteact_close(const char *host, char *errbuf) +{ + struct activehosts *temp, *prev; /* temp var needed to scan the host list chain */ + struct addrinfo hints, *addrinfo, *ai_next; /* temp var needed to translate between hostname to its address */ + int retval; + + temp = activeHosts; + prev = NULL; + + /* retrieve the network address corresponding to 'host' */ + addrinfo = NULL; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + retval = getaddrinfo(host, "0", &hints, &addrinfo); + if (retval != 0) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "getaddrinfo() %s", gai_strerror(retval)); + return -1; + } + + while (temp) + { + ai_next = addrinfo; + while (ai_next) + { + if (sock_cmpaddr(&temp->host, (struct sockaddr_storage *) ai_next->ai_addr) == 0) + { + struct rpcap_header header; + + /* Close this connection */ + rpcap_createhdr(&header, RPCAP_MSG_CLOSE, 0, 0); + + /* I don't check for errors, since I'm going to close everything */ + sock_send(temp->sockctrl, (char *)&header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE); + + if (sock_close(temp->sockctrl, errbuf, PCAP_ERRBUF_SIZE)) + { + /* To avoid inconsistencies in the number of sock_init() */ + sock_cleanup(); + + return -1; + } + + if (prev) + prev->next = temp->next; + else + activeHosts = temp->next; + + freeaddrinfo(addrinfo); + + free(temp); + + /* To avoid inconsistencies in the number of sock_init() */ + sock_cleanup(); + + return 0; + } + + ai_next = ai_next->ai_next; + } + prev = temp; + temp = temp->next; + } + + if (addrinfo) + freeaddrinfo(addrinfo); + + /* To avoid inconsistencies in the number of sock_init() */ + sock_cleanup(); + + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The host you want to close the active connection is not known"); + return -1; +} + + +void pcap_remoteact_cleanup() +{ + /* Very dirty, but it works */ + if (sockmain) + { + closesocket(sockmain); + + /* To avoid inconsistencies in the number of sock_init() */ + sock_cleanup(); + } + +} + + +int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf) +{ + struct activehosts *temp; /* temp var needed to scan the host list chain */ + size_t len; + char hoststr[RPCAP_HOSTLIST_SIZE + 1]; + + temp = activeHosts; + + len = 0; + *hostlist = 0; + + while (temp) + { + /*int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen) */ + + /* Get the numeric form of the name of the connecting host */ + if (sock_getascii_addrport((struct sockaddr_storage *) &temp->host, hoststr, + RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST, errbuf, PCAP_ERRBUF_SIZE) != -1) + /* if (getnameinfo( (struct sockaddr *) &temp->host, sizeof (struct sockaddr_storage), hoststr, */ + /* RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST) ) */ + { + /* sock_geterror("getnameinfo(): ", errbuf, PCAP_ERRBUF_SIZE); */ + return -1; + } + + len = len + strlen(hoststr) + 1 /* the separator */; + + if ((size < 0) || (len >= (size_t)size)) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The string you provided is not able to keep " + "the hostnames for all the active connections"); + return -1; + } + + strcat(hostlist, hoststr); + hostlist[len - 1] = sep; + hostlist[len] = 0; + + temp = temp->next; + } + + return 0; +} + diff --git a/pcap-remote.c b/pcap-remote.c new file mode 100644 index 00000000..70d27fb8 --- /dev/null +++ b/pcap-remote.c @@ -0,0 +1,2142 @@ +/* + * Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2008 CACE Technologies, Davis (California) + * 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, CACE Technologies + * 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> /* for strlen(), ... */ +#include <stdlib.h> /* for malloc(), free(), ... */ +#include <stdarg.h> /* for functions with variable number of arguments */ +#include <errno.h> /* for the errno variable */ +#include "pcap.h" +#include "pcap-int.h" +#include "pcap-remote.h" +#include "sockutils.h" + + +/* + * \file pcap-remote.c + * + * This file keeps all the new funtions that are needed for the RPCAP protocol. + * Almost all the pcap functions need to be modified in order to become compatible + * with the RPCAP protocol. However, you can find here only the ones that are completely new. + * + * This file keeps also the functions that are 'private', i.e. are needed by the RPCAP + * protocol but are not exported to the user. + * + * \warning All the RPCAP functions that are allowed to return a buffer containing + * the error description can return max PCAP_ERRBUF_SIZE characters. + * However there is no guarantees that the string will be zero-terminated. + * Best practice is to define the errbuf variable as a char of size 'PCAP_ERRBUF_SIZE+1' + * and to insert manually a NULL character at the end of the buffer. This will + * guarantee that no buffer overflows occur even if we use the printf() to show + * the error on the screen. + */ + + + +#define PCAP_STATS_STANDARD 0 /* Used by pcap_stats_remote to see if we want standard or extended statistics */ +#define PCAP_STATS_EX 1 /* Used by pcap_stats_remote to see if we want standard or extended statistics */ + + + +/* Keeps a list of all the opened connections in the active mode. */ +struct activehosts *activeHosts; + +/* + * Private data for capturing on WinPcap devices. + */ +struct pcap_win { + int nonblock; + + int filtering_in_kernel; /* using kernel filter */ + +#ifdef HAVE_DAG_API + int dag_fcs_bits; /* Number of checksum bits from link layer */ +#endif +}; + +/**************************************************** + * * + * Locally defined functions * + * * + ****************************************************/ +int rpcap_checkver(SOCKET sock, struct rpcap_header *header, char *errbuf); +struct pcap_stat *rpcap_stats_remote(pcap_t *p, struct pcap_stat *ps, int mode); +int pcap_pack_bpffilter(pcap_t *fp, char *sendbuf, int *sendbufidx, struct bpf_program *prog); +int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog); + + +/**************************************************** + * * + * Function bodies * + * * + ****************************************************/ + +/* + * \ingroup remote_pri_func + * + * \brief It traslates (i.e. de-serializes) a 'sockaddr_storage' structure from + * the network byte order to the host byte order. + * + * It accepts a 'sockaddr_storage' structure as it is received from the network and it + * converts it into the host byte order (by means of a set of ntoh() ). + * The function will allocate the 'sockaddrout' variable according to the address family + * in use. In case the address does not belong to the AF_INET nor AF_INET6 families, + * 'sockaddrout' is not allocated and a NULL pointer is returned. + * This usually happens because that address does not exist on the other host, so the + * RPCAP daemon sent a 'sockaddr_storage' structure containing all 'zero' values. + * + * \param sockaddrin: a 'sockaddr_storage' pointer to the variable that has to be + * de-serialized. + * + * \param sockaddrout: a 'sockaddr_storage' pointer to the variable that will contain + * the de-serialized data. The structure returned can be either a 'sockaddr_in' or 'sockaddr_in6'. + * This variable will be allocated automatically inside this function. + * + * \param errbuf: a pointer to a user-allocated buffer (of size PCAP_ERRBUF_SIZE) + * that will contain the error message (in case there is one). + * + * \return '0' if everything is fine, '-1' if some errors occurred. Basically, the error + * can be only the fact that the malloc() failed to allocate memory. + * The error message is returned in the 'errbuf' variable, while the deserialized address + * is returned into the 'sockaddrout' variable. + * + * \warning This function supports only AF_INET and AF_INET6 address families. + * + * \warning The sockaddrout (if not NULL) must be deallocated by the user. + */ +int rpcap_deseraddr(struct sockaddr_storage *sockaddrin, struct sockaddr_storage **sockaddrout, char *errbuf) +{ + /* Warning: we support only AF_INET and AF_INET6 */ + if (ntohs(sockaddrin->ss_family) == AF_INET) + { + struct sockaddr_in *sockaddr; + + sockaddr = (struct sockaddr_in *) sockaddrin; + sockaddr->sin_family = ntohs(sockaddr->sin_family); + sockaddr->sin_port = ntohs(sockaddr->sin_port); + + (*sockaddrout) = (struct sockaddr_storage *) malloc(sizeof(struct sockaddr_in)); + if ((*sockaddrout) == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + return -1; + } + memcpy(*sockaddrout, sockaddr, sizeof(struct sockaddr_in)); + return 0; + } + if (ntohs(sockaddrin->ss_family) == AF_INET6) + { + struct sockaddr_in6 *sockaddr; + + sockaddr = (struct sockaddr_in6 *) sockaddrin; + sockaddr->sin6_family = ntohs(sockaddr->sin6_family); + sockaddr->sin6_port = ntohs(sockaddr->sin6_port); + sockaddr->sin6_flowinfo = ntohl(sockaddr->sin6_flowinfo); + sockaddr->sin6_scope_id = ntohl(sockaddr->sin6_scope_id); + + (*sockaddrout) = (struct sockaddr_storage *) malloc(sizeof(struct sockaddr_in6)); + if ((*sockaddrout) == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc() failed: %s", pcap_strerror(errno)); + return -1; + } + memcpy(*sockaddrout, sockaddr, sizeof(struct sockaddr_in6)); + return 0; + } + + /* It is neither AF_INET nor AF_INET6 */ + *sockaddrout = NULL; + return 0; +} + + + +/* \ingroup remote_pri_func + * + * \brief It reads a packet from the network socket. This does not make use of + * callback (hence the "nocb" string into its name). + * + * This function is called by the several pcap_next_ex() when they detect that + * we have a remote capture and they are the client side. In that case, they need + * to read packets from the socket. + * + * Parameters and return values are exactly the same of the pcap_next_ex(). + * + * \warning By choice, this function does not make use of semaphores. A smarter + * implementation should put a semaphore into the data thread, and a signal will + * be raised as soon as there is data into the socket buffer. + * However this is complicated and it does not bring any advantages when reading + * from the network, in which network delays can be much more important than + * these optimizations. Therefore, we chose the following approach: + * - the 'timeout' chosen by the user is split in two (half on the server side, + * with the usual meaning, and half on the client side) + * - this function checks for packets; if there are no packets, it waits for + * timeout/2 and then it checks again. If packets are still missing, it returns, + * otherwise it reads packets. + */ +int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr **pkt_header, u_char **pkt_data) +{ + struct rpcap_header *header; /* general header according to the RPCAP format */ + struct rpcap_pkthdr *net_pkt_header; /* header of the packet */ + char netbuf[RPCAP_NETBUF_SIZE]; /* size of the network buffer in which the packet is copied, just for UDP */ + unsigned int nread; /* number of bytes (of payload) currently read from the network (referred to the current pkt) */ + int retval; /* generic return value */ + + /* Structures needed for the select() call */ + fd_set rfds; /* set of socket descriptors we have to check */ + struct timeval tv; /* maximum time the select() can block waiting for data */ + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)p->priv + sizeof(struct pcap_win)); + + /* + * Define the read timeout, to be used in the select() + * 'timeout', in pcap_t, is in milliseconds; we have to convert it into sec and microsec + */ + tv.tv_sec = p->opt.timeout / 1000; + tv.tv_usec = (p->opt.timeout - tv.tv_sec * 1000) * 1000; + + /* Watch out sockdata to see if it has input */ + FD_ZERO(&rfds); + + /* + * 'fp->rmt_sockdata' has always to be set before calling the select(), + * since it is cleared by the select() + */ + FD_SET(md->rmt_sockdata, &rfds); + + retval = select(md->rmt_sockdata + 1, &rfds, NULL, NULL, &tv); + if (retval == -1) + { + sock_geterror("select(): ", p->errbuf, PCAP_ERRBUF_SIZE); + return -1; + } + + /* There is no data waiting, so return '0' */ + if (retval == 0) + return 0; + + /* + * data is here; so, let's copy it into the user buffer. + * I'm going to read a new packet; so I reset the number of bytes (payload only) read + */ + nread = 0; + + /* + * We have to define 'header' as a pointer to a larger buffer, + * because in case of UDP we have to read all the message within a single call + */ + header = (struct rpcap_header *) netbuf; + net_pkt_header = (struct rpcap_pkthdr *) (netbuf + sizeof(struct rpcap_header)); + + if (md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) + { + /* Read the entire message from the network */ + if (sock_recv(md->rmt_sockdata, netbuf, RPCAP_NETBUF_SIZE, SOCK_RECEIVEALL_NO, p->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + } + else + { + if (sock_recv(md->rmt_sockdata, netbuf, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + } + + /* Checks if the message is correct */ + retval = rpcap_checkmsg(p->errbuf, md->rmt_sockdata, header, RPCAP_MSG_PACKET, 0); + + if (retval != RPCAP_MSG_PACKET) /* the message is not the one expected */ + { + switch (retval) + { + case -3: /* Unrecoverable network error */ + return -1; /* Do nothing; just exit from here; the error code is already into the errbuf */ + + case -2: /* The other endpoint sent a message that is not allowed here */ + case -1: /* The other endpoint has a version number that is not compatible with our */ + return 0; /* Return 'no packets received' */ + + default: + { + SOCK_ASSERT("Internal error", 1); + return 0; /* Return 'no packets received' */ + }; + } + } + + /* In case of TCP, read the remaining of the packet from the socket */ + if (!(md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP)) + { + /* Read the RPCAP packet header from the network */ + if ((nread = sock_recv(md->rmt_sockdata, (char *)net_pkt_header, + sizeof(struct rpcap_pkthdr), SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE)) == -1) + return -1; + } + + if ((ntohl(net_pkt_header->caplen) + sizeof(struct pcap_pkthdr)) <= ((unsigned)p->bufsize)) + { + /* Initialize returned structures */ + *pkt_header = (struct pcap_pkthdr *) p->buffer; + *pkt_data = (u_char*)p->buffer + sizeof(struct pcap_pkthdr); + + (*pkt_header)->caplen = ntohl(net_pkt_header->caplen); + (*pkt_header)->len = ntohl(net_pkt_header->len); + (*pkt_header)->ts.tv_sec = ntohl(net_pkt_header->timestamp_sec); + (*pkt_header)->ts.tv_usec = ntohl(net_pkt_header->timestamp_usec); + + /* + * I don't update the counter of the packets dropped by the network since we're using TCP, + * therefore no packets are dropped. Just update the number of packets received correctly + */ + md->TotCapt++; + + /* Copies the packet into the data buffer */ + if (md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) + { + unsigned int npkt; + + /* + * In case of UDP the packet has already been read, we have to copy it into 'buffer'. + * Another option should be to declare 'netbuf' as 'static'. However this prevents + * using several pcap instances within the same process (because the static buffer is shared among + * all processes) + */ + memcpy(*pkt_data, netbuf + sizeof(struct rpcap_header) + sizeof(struct rpcap_pkthdr), (*pkt_header)->caplen); + + /* We're using UDP, so we need to update the counter of the packets dropped by the network */ + npkt = ntohl(net_pkt_header->npkt); + + if (md->TotCapt != npkt) + { + md->TotNetDrops += (npkt - md->TotCapt); + md->TotCapt = npkt; + } + + } + else + { + /* In case of TCP, read the remaining of the packet from the socket */ + if ((nread += sock_recv(md->rmt_sockdata, *pkt_data, (*pkt_header)->caplen, SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE)) == -1) + return -1; + + /* Checks if all the data has been read; if not, discard the data in excess */ + /* This check has to be done only on TCP connections */ + if (nread != ntohl(header->plen)) + sock_discard(md->rmt_sockdata, ntohl(header->plen) - nread, NULL, 0); + } + + + /* Packet read successfully */ + return 1; + } + else + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Received a packet that is larger than the internal buffer size."); + return -1; + } + +} + + +/* \ingroup remote_pri_func + * + * \brief It reads a packet from the network socket. + * + * This function is called by the several pcap_read() when they detect that + * we have a remote capture and they are the client side. In that case, they need + * to read packets from the socket. + * + * This function relies on the pcap_read_nocb_remote to deliver packets. The + * difference, here, is that as soon as a packet is read, it is delivered + * to the application by means of a callback function. + * + * Parameters and return values are exactly the same of the pcap_read(). + */ +int pcap_read_remote(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + struct pcap_pkthdr *pkt_header; + u_char *pkt_data; + int n = 0; + + while ((n < cnt) || (cnt < 0)) + { + if (pcap_read_nocb_remote(p, &pkt_header, &pkt_data) == 1) + { + (*callback)(user, pkt_header, pkt_data); + n++; + } + else + return n; + } + return n; +} + + +/* \ingroup remote_pri_func + * + * \brief It sends a CLOSE command to the capture server. + * + * This function is called when the user wants to close a pcap_t adapter. + * In case we're capturing from the network, it sends a command to the other + * peer that says 'ok, let's stop capturing'. + * This function is called automatically when the user calls the pcap_close(). + * + * Parameters and return values are exactly the same of the pcap_close(). + * + * \warning Since we're closing the connection, we do not check for errors. + */ +void pcap_cleanup_remote(pcap_t *fp) +{ + struct rpcap_header header; /* header of the RPCAP packet */ + struct activehosts *temp; /* temp var needed to scan the host list chain, to detect if we're in active mode */ + int active = 0; /* active mode or not? */ + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)fp->priv + sizeof(struct pcap_win)); + + /* detect if we're in active mode */ + temp = activeHosts; + while (temp) + { + if (temp->sockctrl == md->rmt_sockctrl) + { + active = 1; + break; + } + temp = temp->next; + } + + if (!active) + { + rpcap_createhdr(&header, RPCAP_MSG_CLOSE, 0, 0); + + /* I don't check for errors, since I'm going to close everything */ + sock_send(md->rmt_sockctrl, (char *)&header, sizeof(struct rpcap_header), NULL, 0); + } + else + { + rpcap_createhdr(&header, RPCAP_MSG_ENDCAP_REQ, 0, 0); + + /* I don't check for errors, since I'm going to close everything */ + sock_send(md->rmt_sockctrl, (char *)&header, sizeof(struct rpcap_header), NULL, 0); + + /* wait for the answer */ + /* Don't check what we got, since the present libpcap does not uses this pcap_t anymore */ + sock_recv(md->rmt_sockctrl, (char *)&header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, NULL, 0); + + if (ntohl(header.plen) != 0) + sock_discard(md->rmt_sockctrl, ntohl(header.plen), NULL, 0); + } + + if (md->rmt_sockdata) + { + sock_close(md->rmt_sockdata, NULL, 0); + md->rmt_sockdata = 0; + } + + if ((!active) && (md->rmt_sockctrl)) + sock_close(md->rmt_sockctrl, NULL, 0); + + md->rmt_sockctrl = 0; + + if (md->currentfilter) + { + free(md->currentfilter); + md->currentfilter = NULL; + } + + /* To avoid inconsistencies in the number of sock_init() */ + sock_cleanup(); +} + + + +/* \ingroup remote_pri_func + * + * \brief It retrieves network statistics from the other peer. + * + * This function is just a void cointainer, since the work is done by the rpcap_stats_remote(). + * See that funcion for more details. + * + * Parameters and return values are exactly the same of the pcap_stats(). + */ +int pcap_stats_remote(pcap_t *p, struct pcap_stat *ps) +{ + struct pcap_stat *retval; + + retval = rpcap_stats_remote(p, ps, PCAP_STATS_STANDARD); + + if (retval) + return 0; + else + return -1; +} + + + +/* \ingroup remote_pri_func + * + * \brief It retrieves network statistics from the other peer. + * + * This function is just a void cointainer, since the work is done by the rpcap_stats_remote(). + * See that funcion for more details. + * + * Parameters and return values are exactly the same of the pcap_stats_ex(). + */ +struct pcap_stat *pcap_stats_ex_remote(pcap_t *p) +{ + /* '0' (third param) means 'standard pcap_stats()' */ + return (rpcap_stats_remote(p, &(p->stat), PCAP_STATS_EX)); +} + + + +/* \ingroup remote_pri_func + * + * \brief It retrieves network statistics from the other peer. + * + * This function can be called in two modes: + * - PCAP_STATS_STANDARD: if we want just standard statistics (i.e. the pcap_stats() ) + * - PCAP_STATS_EX: if we want extended statistics (i.e. the pcap_stats_ex() ) + * + * This 'mode' parameter is needed because in the standard pcap_stats() the variable that keeps the + * statistics is allocated by the user. Unfortunately, this structure has been extended in order + * to keep new stats. However, if the user has a smaller structure and it passes it to the pcap_stats, + * thid function will try to fill in more data than the size of the structure, so that the application + * goes in memory overflow. + * So, we need to know it we have to copy just the standard fields, or the extended fields as well. + * + * In case we want to copy the extended fields as well, the problem of memory overflow does no + * longer exist because the structure pcap_stat is no longer allocated by the program; + * it is allocated by the library instead. + * + * \param p: the pcap_t structure related to the current instance. + * + * \param ps: a 'pcap_stat' structure, needed for compatibility with pcap_stat(), in which + * the structure is allocated by the user. In case of pcap_stats_ex, this structure and the + * function return value point to the same variable. + * + * \param mode: one of PCAP_STATS_STANDARD or PCAP_STATS_EX. + * + * \return The structure that keeps the statistics, or NULL in case of error. + * The error string is placed in the pcap_t structure. + */ +struct pcap_stat *rpcap_stats_remote(pcap_t *p, struct pcap_stat *ps, int mode) +{ + struct rpcap_header header; /* header of the RPCAP packet */ + struct rpcap_stats netstats; /* statistics sent on the network */ + unsigned int nread = 0; /* number of bytes of the payload read from the socket */ + int retval; /* temp variable which stores functions return value */ + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)p->priv + sizeof(struct pcap_win)); + + /* + * If the capture has still to start, we cannot ask statistics to the other peer, + * so we return a fake number + */ + if (!md->rmt_capstarted) + { + if (mode == PCAP_STATS_STANDARD) + { + ps->ps_drop = 0; + ps->ps_ifdrop = 0; + ps->ps_recv = 0; + } + else + { + ps->ps_capt = 0; + ps->ps_drop = 0; + ps->ps_ifdrop = 0; + ps->ps_netdrop = 0; + ps->ps_recv = 0; + ps->ps_sent = 0; + } + + return ps; + } + + rpcap_createhdr(&header, RPCAP_MSG_STATS_REQ, 0, 0); + + /* Send the PCAP_STATS command */ + if (sock_send(md->rmt_sockctrl, (char *)&header, sizeof(struct rpcap_header), p->errbuf, PCAP_ERRBUF_SIZE)) + goto error; + + /* Receive the RPCAP stats reply message */ + if (sock_recv(md->rmt_sockctrl, (char *)&header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + /* Checks if the message is correct */ + retval = rpcap_checkmsg(p->errbuf, md->rmt_sockctrl, &header, RPCAP_MSG_STATS_REPLY, RPCAP_MSG_ERROR, 0); + + if (retval != RPCAP_MSG_STATS_REPLY) /* the message is not the one expected */ + { + switch (retval) + { + case -3: /* Unrecoverable network error */ + case -2: /* The other endpoint send a message that is not allowed here */ + case -1: /* The other endpoint has a version number that is not compatible with our */ + goto error; + + case RPCAP_MSG_ERROR: /* The other endpoint reported an error */ + /* Update nread, since the rpcap_checkmsg() already purged the buffer */ + nread = ntohl(header.plen); + + /* Do nothing; just exit; the error code is already into the errbuf */ + goto error; + + default: + { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Internal error"); + goto error; + }; + } + } + + if ((nread = sock_recv(md->rmt_sockctrl, (char *)&netstats, sizeof(struct rpcap_stats), SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + if (mode == PCAP_STATS_STANDARD) + { + ps->ps_drop = ntohl(netstats.krnldrop); + ps->ps_ifdrop = ntohl(netstats.ifdrop); + ps->ps_recv = ntohl(netstats.ifrecv); + } + else + { + ps->ps_capt = md->TotCapt; + ps->ps_drop = ntohl(netstats.krnldrop); + ps->ps_ifdrop = ntohl(netstats.ifdrop); + ps->ps_netdrop = md->TotNetDrops; + ps->ps_recv = ntohl(netstats.ifrecv); + ps->ps_sent = ntohl(netstats.svrcapt); + } + + /* Checks if all the data has been read; if not, discard the data in excess */ + if (nread != ntohl(header.plen)) + { + if (sock_discard(md->rmt_sockctrl, ntohl(header.plen) - nread, NULL, 0) == 1) + goto error; + } + + return ps; + +error: + if (nread != ntohl(header.plen)) + sock_discard(md->rmt_sockctrl, ntohl(header.plen) - nread, NULL, 0); + + return NULL; +} + + + + +/* \ingroup remote_pri_func + * + * \brief It opens a remote adapter by opening an RPCAP connection and so on. + * + * This function does basically the job of pcap_open_live() for a remote interface. + * In other words, we have a pcap_read for win32, which reads packets from NPF, + * another for LINUX, and so on. Now, we have a pcap_opensource_remote() as well. + * The difference, here, is the capture thread does not start until the + * pcap_startcapture_remote() is called. + * + * This is because, in remote capture, we cannot start capturing data as soon ad the + * 'open adapter' command is sent. Suppose the remote adapter is already overloaded; + * if we start a capture (which, by default, has a NULL filter) the new traffic can + * saturate the network. + * + * Instead, we want to "open" the adapter, then send a "start capture" command only + * when we're ready to start the capture. + * This funtion does this job: it sends a "open adapter" command (according to the + * RPCAP protocol), but it does not start the capture. + * + * Since the other libpcap functions do not share this way of life, we have to make + * some dirty things in order to make everyting working. + * + * \param fp: A pointer to a pcap_t structure that has been previously created with + * \ref pcap_create(). + * \param source: see pcap_open(). + * \param auth: see pcap_open(). + * + * \return 0 in case of success, -1 otherwise. In case of success, the pcap_t pointer in fp can be + * used as a parameter to the following calls (pcap_compile() and so on). In case of + * problems, fp->errbuf contains a text explanation of error. + * + * \warning In case we call the pcap_compile() and the capture is not started, the filter + * will be saved into the pcap_t structure, and it will be sent to the other host later + * (when the pcap_startcapture_remote() is called). + */ +int pcap_opensource_remote(pcap_t *fp, struct pcap_rmtauth *auth) +{ + char host[PCAP_BUF_SIZE], ctrlport[PCAP_BUF_SIZE], iface[PCAP_BUF_SIZE]; + + char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data to be sent is buffered */ + int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ + unsigned int nread = 0; /* number of bytes of the payload read from the socket */ + int retval; /* store the return value of the functions */ + int active = 0; /* '1' if we're in active mode */ + + /* socket-related variables */ + struct addrinfo hints; /* temp, needed to open a socket connection */ + struct addrinfo *addrinfo; /* temp, needed to open a socket connection */ + SOCKET sockctrl = 0; /* socket descriptor of the control connection */ + + /* RPCAP-related variables */ + struct rpcap_header header; /* header of the RPCAP packet */ + struct rpcap_openreply openreply; /* open reply message */ + + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)fp->priv + sizeof(struct pcap_win)); + + + /* + * determine the type of the source (NULL, file, local, remote) + * You must have a valid source string even if we're in active mode, because otherwise + * the call to the following function will fail. + */ + if (pcap_parsesrcstr(fp->opt.device, &retval, host, ctrlport, iface, fp->errbuf) == -1) + return -1; + + if (retval != PCAP_SRC_IFREMOTE) + { + pcap_snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "This function is able to open only remote interfaces"); + return -1; + } + + addrinfo = NULL; + + /* + * Warning: this call can be the first one called by the user. + * For this reason, we have to initialize the WinSock support. + */ + if (sock_init(fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + + retval = rpcap_remoteact_getsock(host, fp->errbuf); + + if (retval == -1) + return -1; + + /* The capturing machine is in active mode */ + if (retval) + { + sockctrl = retval; + active = 1; + } + else + { + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((ctrlport == NULL) || (ctrlport[0] == 0)) + { + /* the user chose not to specify the port */ + if (sock_initaddress(host, RPCAP_DEFAULT_NETPORT, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + } + else + { + /* the user chose not to specify the port */ + if (sock_initaddress(host, ctrlport, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + } + + if ((sockctrl = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, fp->errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + freeaddrinfo(addrinfo); + addrinfo = NULL; + + if (rpcap_sendauth(sockctrl, auth, fp->errbuf) == -1) + goto error; + } + + /* + * Now it's time to start playing with the RPCAP protocol + * RPCAP open command: create the request message + */ + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf, PCAP_ERRBUF_SIZE)) + goto error; + + rpcap_createhdr((struct rpcap_header *) sendbuf, RPCAP_MSG_OPEN_REQ, 0, strlen(iface)); + + if (sock_bufferize(iface, strlen(iface), sendbuf, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, fp->errbuf, PCAP_ERRBUF_SIZE)) + goto error; + + if (sock_send(sockctrl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE)) + goto error; + + /* Receive the RPCAP open reply message */ + if (sock_recv(sockctrl, (char *)&header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + /* Checks if the message is correct */ + retval = rpcap_checkmsg(fp->errbuf, sockctrl, &header, RPCAP_MSG_OPEN_REPLY, RPCAP_MSG_ERROR, 0); + + if (retval != RPCAP_MSG_OPEN_REPLY) /* the message is not the one expected */ + { + switch (retval) + { + case -3: /* Unrecoverable network error */ + case -2: /* The other endpoint send a message that is not allowed here */ + case -1: /* The other endpoint has a version number that is not compatible with our */ + goto error; + + case RPCAP_MSG_ERROR: /* The other endpoint reported an error */ + /* Update nread, since the rpcap_checkmsg() already purged the buffer */ + nread = ntohl(header.plen); + /* Do nothing; just exit; the error code is already into the errbuf */ + goto error; + + default: + { + pcap_snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Internal error"); + goto error; + }; + } + } + + + if ((nread += sock_recv(sockctrl, (char *)&openreply, sizeof(struct rpcap_openreply), SOCK_RECEIVEALL_YES, fp->errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + /* Set proper fields into the pcap_t struct */ + fp->linktype = ntohl(openreply.linktype); + fp->tzoff = ntohl(openreply.tzoff); + md->rmt_sockctrl = sockctrl; + md->rmt_clientside = 1; + + + /* This code is duplicated from the end of this function */ + fp->read_op = pcap_read_remote; + fp->setfilter_op = pcap_setfilter_remote; + fp->getnonblock_op = NULL; /* This is not implemented in remote capture */ + fp->setnonblock_op = NULL; /* This is not implemented in remote capture */ + fp->stats_op = pcap_stats_remote; + fp->cleanup_op = pcap_cleanup_remote; + + /* Checks if all the data has been read; if not, discard the data in excess */ + if (nread != ntohl(header.plen)) + { + if (sock_discard(sockctrl, ntohl(header.plen) - nread, NULL, 0) == 1) + goto error; + } + return 0; + +error: + /* + * When the connection has been established, we have to close it. So, at the + * beginning of this function, if an error occur we return immediately with + * a return NULL; when the connection is established, we have to come here + * ('goto error;') in order to close everything properly. + * + * Checks if all the data has been read; if not, discard the data in excess + */ + if (nread != ntohl(header.plen)) + sock_discard(sockctrl, ntohl(header.plen) - nread, NULL, 0); + + if (addrinfo) + freeaddrinfo(addrinfo); + + if (!active) + sock_close(sockctrl, NULL, 0); + + return -1; +} + + +/* \ingroup remote_pri_func + * + * \brief It starts a remote capture. + * + * This function is requires since the RPCAP protocol decouples the 'open' from the + * 'start capture' functions. + * This function takes all the parameters needed (which have been stored into the pcap_t structure) + * and sends them to the server. + * If everything is fine, it creates a new child thread that reads data from the network + * and puts data it into the user buffer. + * The pcap_read() will read data from the user buffer, as usual. + * + * The remote capture acts like a new "kernel", which puts packets directly into + * the buffer pointed by pcap_t. + * In fact, this function does not rely on a kernel that reads packets and put them + * into the user buffer; it has to do that on its own. + * + * \param fp: the pcap_t descriptor of the device currently open. + * + * \return '0' if everything is fine, '-1' otherwise. The error message (if one) + * is returned into the 'errbuf' field of the pcap_t structure. + */ +int pcap_startcapture_remote(pcap_t *fp) +{ + char sendbuf[RPCAP_NETBUF_SIZE];/* temporary buffer in which data to be sent is buffered */ + int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ + char portdata[PCAP_BUF_SIZE]; /* temp variable needed to keep the network port for the the data connection */ + unsigned int nread = 0; /* number of bytes of the payload read from the socket */ + int retval; /* store the return value of the functions */ + int active = 0; /* '1' if we're in active mode */ + struct activehosts *temp; /* temp var needed to scan the host list chain, to detect if we're in active mode */ + char host[INET6_ADDRSTRLEN + 1];/* numeric name of the other host */ + + /* socket-related variables*/ + struct addrinfo hints; /* temp, needed to open a socket connection */ + struct addrinfo *addrinfo; /* temp, needed to open a socket connection */ + SOCKET sockdata = 0; /* socket descriptor of the data connection */ + struct sockaddr_storage saddr; /* temp, needed to retrieve the network data port chosen on the local machine */ + socklen_t saddrlen; /* temp, needed to retrieve the network data port chosen on the local machine */ + int ai_family; /* temp, keeps the address family used by the control connection */ + + /* RPCAP-related variables*/ + struct rpcap_header header; /* header of the RPCAP packet */ + struct rpcap_startcapreq *startcapreq; /* start capture request message */ + struct rpcap_startcapreply startcapreply; /* start capture reply message */ + + /* Variables related to the buffer setting */ + int res, itemp; + int sockbufsize = 0; + + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)fp->priv + sizeof(struct pcap_win)); + + /* + * Let's check if sampling has been required. + * If so, let's set it first + */ + if (pcap_setsampling_remote(fp) != 0) + return -1; + + + /* detect if we're in active mode */ + temp = activeHosts; + while (temp) + { + if (temp->sockctrl == md->rmt_sockctrl) + { + active = 1; + break; + } + temp = temp->next; + } + + addrinfo = NULL; + + /* + * Gets the complete sockaddr structure used in the ctrl connection + * This is needed to get the address family of the control socket + * Tip: I cannot save the ai_family of the ctrl sock in the pcap_t struct, + * since the ctrl socket can already be open in case of active mode; + * so I would have to call getpeername() anyway + */ + saddrlen = sizeof(struct sockaddr_storage); + if (getpeername(md->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) + { + sock_geterror("getsockname(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + goto error; + } + ai_family = ((struct sockaddr_storage *) &saddr)->ss_family; + + /* Get the numeric address of the remote host we are connected to */ + if (getnameinfo((struct sockaddr *) &saddr, saddrlen, host, + sizeof(host), NULL, 0, NI_NUMERICHOST)) + { + sock_geterror("getnameinfo(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + goto error; + } + + /* + * Data connection is opened by the server toward the client if: + * - we're using TCP, and the user wants us to be in active mode + * - we're using UDP + */ + if ((active) || (md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP)) + { + /* + * We have to create a new socket to receive packets + * We have to do that immediately, since we have to tell the other + * end which network port we picked up + */ + memset(&hints, 0, sizeof(struct addrinfo)); + /* TEMP addrinfo is NULL in case of active */ + hints.ai_family = ai_family; /* Use the same address family of the control socket */ + hints.ai_socktype = (md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; /* Data connection is opened by the server toward the client */ + + /* Let's the server pick up a free network port for us */ + if (sock_initaddress(NULL, "0", &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + if ((sockdata = sock_open(addrinfo, SOCKOPEN_SERVER, + 1 /* max 1 connection in queue */, fp->errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + /* addrinfo is no longer used */ + freeaddrinfo(addrinfo); + addrinfo = NULL; + + /* get the complete sockaddr structure used in the data connection */ + saddrlen = sizeof(struct sockaddr_storage); + if (getsockname(sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1) + { + sock_geterror("getsockname(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + goto error; + } + + /* Get the local port the system picked up */ + if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL, + 0, portdata, sizeof(portdata), NI_NUMERICSERV)) + { + sock_geterror("getnameinfo(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + goto error; + } + } + + /* + * Now it's time to start playing with the RPCAP protocol + * RPCAP start capture command: create the request message + */ + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf, PCAP_ERRBUF_SIZE)) + goto error; + + rpcap_createhdr((struct rpcap_header *) sendbuf, RPCAP_MSG_STARTCAP_REQ, 0, + sizeof(struct rpcap_startcapreq) + sizeof(struct rpcap_filter) + fp->fcode.bf_len * sizeof(struct rpcap_filterbpf_insn)); + + /* Fill the structure needed to open an adapter remotely */ + startcapreq = (struct rpcap_startcapreq *) &sendbuf[sendbufidx]; + + if (sock_bufferize(NULL, sizeof(struct rpcap_startcapreq), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf, PCAP_ERRBUF_SIZE)) + goto error; + + memset(startcapreq, 0, sizeof(struct rpcap_startcapreq)); + + /* By default, apply half the timeout on one side, half of the other */ + fp->opt.timeout = fp->opt.timeout / 2; + startcapreq->read_timeout = htonl(fp->opt.timeout); + + /* portdata on the openreq is meaningful only if we're in active mode */ + if ((active) || (md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP)) + { + sscanf(portdata, "%d", (int *)&(startcapreq->portdata)); /* cast to avoid a compiler warning */ + startcapreq->portdata = htons(startcapreq->portdata); + } + + startcapreq->snaplen = htonl(fp->snapshot); + startcapreq->flags = 0; + + if (md->rmt_flags & PCAP_OPENFLAG_PROMISCUOUS) + startcapreq->flags |= RPCAP_STARTCAPREQ_FLAG_PROMISC; + if (md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) + startcapreq->flags |= RPCAP_STARTCAPREQ_FLAG_DGRAM; + if (active) + startcapreq->flags |= RPCAP_STARTCAPREQ_FLAG_SERVEROPEN; + + startcapreq->flags = htons(startcapreq->flags); + + /* Pack the capture filter */ + if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, &fp->fcode)) + goto error; + + if (sock_send(md->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE)) + goto error; + + + /* Receive the RPCAP start capture reply message */ + if (sock_recv(md->rmt_sockctrl, (char *)&header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + /* Checks if the message is correct */ + retval = rpcap_checkmsg(fp->errbuf, md->rmt_sockctrl, &header, RPCAP_MSG_STARTCAP_REPLY, RPCAP_MSG_ERROR, 0); + + if (retval != RPCAP_MSG_STARTCAP_REPLY) /* the message is not the one expected */ + { + switch (retval) + { + case -3: /* Unrecoverable network error */ + case -2: /* The other endpoint send a message that is not allowed here */ + case -1: /* The other endpoint has a version number that is not compatible with our */ + goto error; + + case RPCAP_MSG_ERROR: /* The other endpoint reported an error */ + /* Update nread, since the rpcap_checkmsg() already purged the buffer */ + nread = ntohl(header.plen); + /* Do nothing; just exit; the error code is already into the errbuf */ + goto error; + + default: + { + pcap_snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Internal error"); + goto error; + }; + } + } + + + if ((nread += sock_recv(md->rmt_sockctrl, (char *)&startcapreply, + sizeof(struct rpcap_startcapreply), SOCK_RECEIVEALL_YES, fp->errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + /* + * In case of UDP data stream, the connection is always opened by the daemon + * So, this case is already covered by the code above. + * Now, we have still to handle TCP connections, because: + * - if we're in active mode, we have to wait for a remote connection + * - if we're in passive more, we have to start a connection + * + * We have to do he job in two steps because in case we're opening a TCP connection, we have + * to tell the port we're using to the remote side; in case we're accepting a TCP + * connection, we have to wait this info from the remote side. + */ + + if (!(md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP)) + { + if (!active) + { + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = ai_family; /* Use the same address family of the control socket */ + hints.ai_socktype = (md->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) ? SOCK_DGRAM : SOCK_STREAM; + sprintf(portdata, "%d", ntohs(startcapreply.portdata)); + + /* Let's the server pick up a free network port for us */ + if (sock_initaddress(host, portdata, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + if ((sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, fp->errbuf, PCAP_ERRBUF_SIZE)) == -1) + goto error; + + /* addrinfo is no longer used */ + freeaddrinfo(addrinfo); + addrinfo = NULL; + } + else + { + SOCKET socktemp; /* We need another socket, since we're going to accept() a connection */ + + /* Connection creation */ + saddrlen = sizeof(struct sockaddr_storage); + + socktemp = accept(sockdata, (struct sockaddr *) &saddr, &saddrlen); + + if (socktemp == -1) + { + sock_geterror("accept(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + goto error; + } + + /* Now that I accepted the connection, the server socket is no longer needed */ + sock_close(sockdata, fp->errbuf, PCAP_ERRBUF_SIZE); + sockdata = socktemp; + } + } + + /* Let's save the socket of the data connection */ + md->rmt_sockdata = sockdata; + + /* Allocates WinPcap/libpcap user buffer, which is a socket buffer in case of a remote capture */ + /* It has the same size of the one used on the other side of the connection */ + fp->bufsize = ntohl(startcapreply.bufsize); + + /* Let's get the actual size of the socket buffer */ + itemp = sizeof(sockbufsize); + + res = getsockopt(sockdata, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, &itemp); + if (res == -1) + { + sock_geterror("pcap_startcapture_remote()", fp->errbuf, PCAP_ERRBUF_SIZE); + SOCK_ASSERT(fp->errbuf, 1); + } + + /* + * Warning: on some kernels (e.g. Linux), the size of the user buffer does not take + * into account the pcap_header and such, and it is set equal to the snaplen. + * In my view, this is wrong (the meaning of the bufsize became a bit strange). + * So, here bufsize is the whole size of the user buffer. + * In case the bufsize returned is too small, let's adjust it accordingly. + */ + if (fp->bufsize <= fp->snapshot) + fp->bufsize += sizeof(struct pcap_pkthdr); + + /* if the current socket buffer is smaller than the desired one */ + if (sockbufsize < fp->bufsize) + { + /* Loop until the buffer size is OK or the original socket buffer size is larger than this one */ + while (1) + { + res = setsockopt(sockdata, SOL_SOCKET, SO_RCVBUF, (char *)&(fp->bufsize), sizeof(fp->bufsize)); + + if (res == 0) + break; + + /* + * If something goes wrong, half the buffer size (checking that it does not become smaller than + * the current one) + */ + fp->bufsize /= 2; + + if (sockbufsize >= fp->bufsize) + { + fp->bufsize = sockbufsize; + break; + } + } + } + + /* + * Let's allocate the packet; this is required in order to put the packet somewhere when + * extracting data from the socket + * Since buffering has already been done in the socket buffer, here we need just a buffer, + * whose size is equal to the pcap header plus the snapshot length + */ + fp->bufsize = fp->snapshot + sizeof(struct pcap_pkthdr); + + fp->buffer = (u_char *)malloc(fp->bufsize); + if (fp->buffer == NULL) + { + pcap_snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + goto error; + } + + + /* Checks if all the data has been read; if not, discard the data in excess */ + if (nread != ntohl(header.plen)) + { + if (sock_discard(md->rmt_sockctrl, ntohl(header.plen) - nread, NULL, 0) == 1) + goto error; + } + + /* + * In case the user does not want to capture RPCAP packets, let's update the filter + * We have to update it here (instead of sending it into the 'StartCapture' message + * because when we generate the 'start capture' we do not know (yet) all the ports + * we're currently using. + */ + if (md->rmt_flags & PCAP_OPENFLAG_NOCAPTURE_RPCAP) + { + struct bpf_program fcode; + + if (pcap_createfilter_norpcappkt(fp, &fcode) == -1) + goto error; + + /* We cannot use 'pcap_setfilter_remote' because formally the capture has not been started yet */ + /* (the 'fp->rmt_capstarted' variable will be updated some lines below) */ + if (pcap_updatefilter_remote(fp, &fcode) == -1) + goto error; + + pcap_freecode(&fcode); + } + + md->rmt_capstarted = 1; + return 0; + +error: + /* + * When the connection has been established, we have to close it. So, at the + * beginning of this function, if an error occur we return immediately with + * a return NULL; when the connection is established, we have to come here + * ('goto error;') in order to close everything properly. + * + * Checks if all the data has been read; if not, discard the data in excess + */ + if (nread != ntohl(header.plen)) + sock_discard(md->rmt_sockctrl, ntohl(header.plen) - nread, NULL, 0); + + if ((sockdata) && (sockdata != -1)) /* we can be here because sockdata said 'error' */ + sock_close(sockdata, NULL, 0); + + if (!active) + sock_close(md->rmt_sockctrl, NULL, 0); + + /* + * We do not have to call pcap_close() here, because this function is always called + * by the user in case something bad happens + */ + // if (fp) + // { + // pcap_close(fp); + // fp= NULL; + // } + + return -1; +} + + +/* + * \brief Takes a bpf program and sends it to the other host. + * + * This function can be called in two cases: + * - the pcap_startcapture() is called (we have to send the filter along with + * the 'start capture' command) + * - we want to udpate the filter during a capture (i.e. the pcap_setfilter() + * is called when the capture is still on) + * + * This function serializes the filter into the sending buffer ('sendbuf', passed + * as a parameter) and return back. It does not send anything on the network. + * + * \param fp: the pcap_t descriptor of the device currently opened. + * + * \param sendbuf: the buffer on which the serialized data has to copied. + * + * \param sendbufidx: it is used to return the abounf of bytes copied into the buffer. + * + * \param prog: the bpf program we have to copy. + * + * \return '0' if everything is fine, '-1' otherwise. The error message (if one) + * is returned into the 'errbuf' field of the pcap_t structure. + */ +int pcap_pack_bpffilter(pcap_t *fp, char *sendbuf, int *sendbufidx, struct bpf_program *prog) +{ + struct rpcap_filter *filter; + struct rpcap_filterbpf_insn *insn; + struct bpf_insn *bf_insn; + struct bpf_program fake_prog; /* To be used just in case the user forgot to set a filter */ + unsigned int i; + + + if (prog->bf_len == 0) /* No filters have been specified; so, let's apply a "fake" filter */ + { + if (pcap_compile(fp, &fake_prog, NULL /* buffer */, 1, 0) == -1) + return -1; + + prog = &fake_prog; + } + + filter = (struct rpcap_filter *) sendbuf; + + if (sock_bufferize(NULL, sizeof(struct rpcap_filter), NULL, sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + filter->filtertype = htons(RPCAP_UPDATEFILTER_BPF); + filter->nitems = htonl((int32)prog->bf_len); + + if (sock_bufferize(NULL, prog->bf_len * sizeof(struct rpcap_filterbpf_insn), + NULL, sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + insn = (struct rpcap_filterbpf_insn *) (filter + 1); + bf_insn = prog->bf_insns; + + for (i = 0; i < prog->bf_len; i++) + { + insn->code = htons(bf_insn->code); + insn->jf = bf_insn->jf; + insn->jt = bf_insn->jt; + insn->k = htonl(bf_insn->k); + + insn++; + bf_insn++; + } + + return 0; +} + + +/* \ingroup remote_pri_func + * + * \brief Update a filter on a remote host. + * + * This function is called when the user wants to update a filter. + * In case we're capturing from the network, it sends the filter to the other peer. + * This function is *not* called automatically when the user calls the pcap_setfilter(). + * There will be two cases: + * - the capture is already on: in this case, pcap_setfilter() calls pcap_updatefilter_remote() + * - the capture has not started yet: in this case, pcap_setfilter() stores the filter into + * the pcap_t structure, and then the filter is sent with the pcap_startcap(). + * + * Parameters and return values are exactly the same of the pcap_setfilter(). + * + * \warning This function *does not* clear the packet currently into the buffers. Therefore, + * the user has to expect to receive some packets that are related to the previous filter. + * If you want to discard all the packets before applying a new filter, you have to close + * the current capture session and start a new one. + */ +int pcap_updatefilter_remote(pcap_t *fp, struct bpf_program *prog) +{ + int retval; /* general variable used to keep the return value of other functions */ + char sendbuf[RPCAP_NETBUF_SIZE];/* temporary buffer in which data to be sent is buffered */ + int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ + struct rpcap_header header; /* To keep the reply message */ + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)fp->priv + sizeof(struct pcap_win)); + + + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, fp->errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + rpcap_createhdr((struct rpcap_header *) sendbuf, RPCAP_MSG_UPDATEFILTER_REQ, 0, + sizeof(struct rpcap_filter) + prog->bf_len * sizeof(struct rpcap_filterbpf_insn)); + + if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, prog)) + return -1; + + if (sock_send(md->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + /* Waits for the answer */ + if (sock_recv(md->rmt_sockctrl, (char *)&header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + + /* Checks if the message is correct */ + retval = rpcap_checkmsg(fp->errbuf, md->rmt_sockctrl, &header, RPCAP_MSG_UPDATEFILTER_REPLY, 0); + + if (retval != RPCAP_MSG_UPDATEFILTER_REPLY) /* the message is not the one expected */ + { + switch (retval) + { + case -3: /* Unrecoverable network error */ + case -2: /* The other endpoint sent a message that is not allowed here */ + case -1: /* The other endpoint has a version number that is not compatible with our */ + /* Do nothing; just exit from here; the error code is already into the errbuf */ + return -1; + + default: + { + SOCK_ASSERT("Internal error", 0); + return -1; + }; + } + } + + if (ntohl(header.plen) != 0) /* the message has an unexpected size */ + { + if (sock_discard(md->rmt_sockctrl, ntohl(header.plen), fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + } + + return 0; +} + + +/* + * \ingroup remote_pri_func + * + * \brief Send a filter to a remote host. + * + * This function is called when the user wants to set a filter. + * In case we're capturing from the network, it sends the filter to the other peer. + * This function is called automatically when the user calls the pcap_setfilter(). + * + * Parameters and return values are exactly the same of the pcap_setfilter(). + */ +int pcap_setfilter_remote(pcap_t *fp, struct bpf_program *prog) +{ + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)fp->priv + sizeof(struct pcap_win)); + + if (!md->rmt_capstarted) + { + /* copy filter into the pcap_t structure */ + if (install_bpf_program(fp, prog) == -1) + return -1; + return 0; + } + + /* we have to update a filter during run-time */ + if (pcap_updatefilter_remote(fp, prog)) + return -1; + + return 0; +} + + +/* + * \ingroup remote_pri_func + * + * \brief Update the current filter in order not to capture rpcap packets. + * + * This function is called *only* when the user wants exclude RPCAP packets + * related to the current session from the captured packets. + * + * \return '0' if everything is fine, '-1' otherwise. The error message (if one) + * is returned into the 'errbuf' field of the pcap_t structure. + */ +int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog) +{ + int RetVal = 0; + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)fp->priv + sizeof(struct pcap_win)); + + /* We do not want to capture our RPCAP traffic. So, let's update the filter */ + if (md->rmt_flags & PCAP_OPENFLAG_NOCAPTURE_RPCAP) + { + struct sockaddr_storage saddr; /* temp, needed to retrieve the network data port chosen on the local machine */ + socklen_t saddrlen; /* temp, needed to retrieve the network data port chosen on the local machine */ + char myaddress[128]; + char myctrlport[128]; + char mydataport[128]; + char peeraddress[128]; + char peerctrlport[128]; + char *newfilter; + const int newstringsize = 1024; + size_t currentfiltersize; + + /* Get the name/port of the other peer */ + saddrlen = sizeof(struct sockaddr_storage); + if (getpeername(md->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) + { + sock_geterror("getpeername(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + return -1; + } + + if (getnameinfo((struct sockaddr *) &saddr, saddrlen, peeraddress, + sizeof(peeraddress), peerctrlport, sizeof(peerctrlport), NI_NUMERICHOST | NI_NUMERICSERV)) + { + sock_geterror("getnameinfo(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + return -1; + } + + /* We cannot check the data port, because this is available only in case of TCP sockets */ + /* Get the name/port of the current host */ + if (getsockname(md->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) + { + sock_geterror("getsockname(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + return -1; + } + + /* Get the local port the system picked up */ + if (getnameinfo((struct sockaddr *) &saddr, saddrlen, myaddress, + sizeof(myaddress), myctrlport, sizeof(myctrlport), NI_NUMERICHOST | NI_NUMERICSERV)) + { + sock_geterror("getnameinfo(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + return -1; + } + + /* Let's now check the data port */ + if (getsockname(md->rmt_sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1) + { + sock_geterror("getsockname(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + return -1; + } + + /* Get the local port the system picked up */ + if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL, 0, mydataport, sizeof(mydataport), NI_NUMERICSERV)) + { + sock_geterror("getnameinfo(): ", fp->errbuf, PCAP_ERRBUF_SIZE); + return -1; + } + + currentfiltersize = strlen(md->currentfilter); + + newfilter = (char *)malloc(currentfiltersize + newstringsize + 1); + + if (currentfiltersize) + { + pcap_snprintf(newfilter, currentfiltersize + newstringsize, + "(%s) and not (host %s and host %s and port %s and port %s) and not (host %s and host %s and port %s)", + md->currentfilter, myaddress, peeraddress, myctrlport, peerctrlport, myaddress, peeraddress, mydataport); + } + else + { + pcap_snprintf(newfilter, currentfiltersize + newstringsize, + "not (host %s and host %s and port %s and port %s) and not (host %s and host %s and port %s)", + myaddress, peeraddress, myctrlport, peerctrlport, myaddress, peeraddress, mydataport); + } + + newfilter[currentfiltersize + newstringsize] = 0; + + /* This is only an hack to make the pcap_compile() working properly */ + md->rmt_clientside = 0; + + if (pcap_compile(fp, prog, newfilter, 1, 0) == -1) + RetVal = -1; + + /* This is only an hack to make the pcap_compile() working properly */ + md->rmt_clientside = 1; + + free(newfilter); + } + + return RetVal; +} + +/* + * \ingroup remote_pri_func + * + * \brief Set sampling parameters in the remote host. + * + * This function is called when the user wants to set activate sampling on the remote host. + * + * Sampling parameters are defined into the 'pcap_t' structure. + * + * \param p: the pcap_t descriptor of the device currently opened. + * + * \return '0' if everything is OK, '-1' is something goes wrong. The error message is returned + * in the 'errbuf' member of the pcap_t structure. + */ +int pcap_setsampling_remote(pcap_t *p) +{ + int retval; /* general variable used to keep the return value of other functions */ + char sendbuf[RPCAP_NETBUF_SIZE];/* temporary buffer in which data to be sent is buffered */ + int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ + struct rpcap_header header; /* To keep the reply message */ + struct rpcap_sampling *sampling_pars; /* Structure that is needed to send sampling parameters to the remote host */ + struct pcap_md *md; /* structure used when doing a remote live capture */ + + md = (struct pcap_md *) ((u_char*)p->priv + sizeof(struct pcap_win)); + + /* If no samping is requested, return 'ok' */ + if (md->rmt_samp.method == PCAP_SAMP_NOSAMP) + return 0; + + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, p->errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + rpcap_createhdr((struct rpcap_header *) sendbuf, RPCAP_MSG_SETSAMPLING_REQ, 0, sizeof(struct rpcap_sampling)); + + /* Fill the structure needed to open an adapter remotely */ + sampling_pars = (struct rpcap_sampling *) &sendbuf[sendbufidx]; + + if (sock_bufferize(NULL, sizeof(struct rpcap_sampling), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, p->errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + memset(sampling_pars, 0, sizeof(struct rpcap_sampling)); + + sampling_pars->method = md->rmt_samp.method; + sampling_pars->value = htonl(md->rmt_samp.value); + + if (sock_send(md->rmt_sockctrl, sendbuf, sendbufidx, p->errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + /* Waits for the answer */ + if (sock_recv(md->rmt_sockctrl, (char *)&header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, p->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + + /* Checks if the message is correct */ + retval = rpcap_checkmsg(p->errbuf, md->rmt_sockctrl, &header, RPCAP_MSG_SETSAMPLING_REPLY, 0); + + if (retval != RPCAP_MSG_SETSAMPLING_REPLY) /* the message is not the one expected */ + { + switch (retval) + { + case -3: /* Unrecoverable network error */ + case -2: /* The other endpoint sent a message that is not allowed here */ + case -1: /* The other endpoint has a version number that is not compatible with our */ + case RPCAP_MSG_ERROR: + /* Do nothing; just exit from here; the error code is already into the errbuf */ + return -1; + + default: + { + SOCK_ASSERT("Internal error", 0); + return -1; + }; + } + } + + if (ntohl(header.plen) != 0) /* the message has an unexpected size */ + { + if (sock_discard(md->rmt_sockctrl, ntohl(header.plen), p->errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + } + + return 0; + +} + + +/********************************************************* + * * + * Miscellaneous functions * + * * + *********************************************************/ + + +/* \ingroup remote_pri_func + * \brief It sends a RPCAP error to the other peer. + * + * This function has to be called when the main program detects an error. This function + * will send on the other peer the 'buffer' specified by the user. + * This function *does not* request a RPCAP CLOSE connection. A CLOSE command must be sent + * explicitly by the program, since we do not know it the error can be recovered in some + * way or it is a non-recoverable one. + * + * \param sock: the socket we are currently using. + * + * \param error: an user-allocated (and '0' terminated) buffer that contains the error + * description that has to be transmitted on the other peer. The error message cannot + * be longer than PCAP_ERRBUF_SIZE. + * + * \param errcode: a integer which tells the other party the type of error we had; + * currently is is not too much used. + * + * \param errbuf: a pointer to a user-allocated buffer (of size PCAP_ERRBUF_SIZE) + * that will contain the error message (in case there is one). It could be network problem. + * + * \return '0' if everything is fine, '-1' if some errors occurred. The error message is returned + * in the 'errbuf' variable. + */ +int rpcap_senderror(SOCKET sock, char *error, unsigned short errcode, char *errbuf) +{ + char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data to be sent is buffered */ + int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ + uint16 length; + + length = (uint16)strlen(error); + + if (length > PCAP_ERRBUF_SIZE) + length = PCAP_ERRBUF_SIZE; + + rpcap_createhdr((struct rpcap_header *) sendbuf, RPCAP_MSG_ERROR, errcode, length); + + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + if (sock_bufferize(error, length, sendbuf, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + if (sock_send(sock, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + return 0; +} + + +/* \ingroup remote_pri_func + * \brief Sends the authentication message. + * + * It sends the authentication parameters on the control socket. + * This function is required in order to open the connection with the other end party. + * + * \param sock: the socket we are currently using. + * + * \param auth: authentication parameters that have to be sent. + * + * \param errbuf: a pointer to a user-allocated buffer (of size PCAP_ERRBUF_SIZE) + * that will contain the error message (in case there is one). It could be network problem + * of the fact that the authorization failed. + * + * \return '0' if everything is fine, '-1' if some errors occurred. The error message is returned + * in the 'errbuf' variable. + * The error message could be also 'the authentication failed'. + */ +int rpcap_sendauth(SOCKET sock, struct pcap_rmtauth *auth, char *errbuf) +{ + char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data that has to be sent is buffered */ + int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ + uint16 length; /* length of the payload of this message */ + struct rpcap_auth *rpauth; + uint16 auth_type; + struct rpcap_header header; + int retval; /* temp variable which stores functions return value */ + + if (auth) + { + auth_type = auth->type; + + switch (auth->type) + { + case RPCAP_RMTAUTH_NULL: + length = sizeof(struct rpcap_auth); + break; + + case RPCAP_RMTAUTH_PWD: + length = sizeof(struct rpcap_auth); + if (auth->username) length += strlen(auth->username); + if (auth->password) length += strlen(auth->password); + break; + + default: + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication type not recognized."); + return -1; + } + } + else + { + auth_type = RPCAP_RMTAUTH_NULL; + length = sizeof(struct rpcap_auth); + } + + + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + rpcap_createhdr((struct rpcap_header *) sendbuf, RPCAP_MSG_AUTH_REQ, 0, length); + + rpauth = (struct rpcap_auth *) &sendbuf[sendbufidx]; + + if (sock_bufferize(NULL, sizeof(struct rpcap_auth), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + memset(rpauth, 0, sizeof(struct rpcap_auth)); + + rpauth->type = htons(auth_type); + + if (auth_type == RPCAP_RMTAUTH_PWD) + { + + if (auth->username) + rpauth->slen1 = strlen(auth->username); + else + rpauth->slen1 = 0; + + if (sock_bufferize(auth->username, rpauth->slen1, sendbuf, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + if (auth->password) + rpauth->slen2 = strlen(auth->password); + else + rpauth->slen2 = 0; + + if (sock_bufferize(auth->password, rpauth->slen2, sendbuf, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + rpauth->slen1 = htons(rpauth->slen1); + rpauth->slen2 = htons(rpauth->slen2); + } + + if (sock_send(sock, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE)) + return -1; + + if (sock_recv(sock, (char *)&header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE) == -1) + return -1; + + retval = rpcap_checkmsg(errbuf, sock, &header, RPCAP_MSG_AUTH_REPLY, RPCAP_MSG_ERROR, 0); + + if (retval != RPCAP_MSG_AUTH_REPLY) /* the message is not the one expected */ + { + switch (retval) + { + case -3: /* Unrecoverable network error */ + case -2: /* The other endpoint sent a message that is not allowed here */ + case -1: /* The other endpoint has a version number that is not compatible with our */ + /* Do nothing; just exit from here; the error code is already into the errbuf */ + return -1; + + case RPCAP_MSG_ERROR: + { + return -1; + }; + + default: + { + SOCK_ASSERT("Internal error", 0); + return -1; + }; + } + } + + if (ntohl(header.plen)) + { + if (sock_discard(sock, ntohl(header.plen), errbuf, PCAP_ERRBUF_SIZE)) + return -1; + } + + return 0; +} + + + +/* \ingroup remote_pri_func + * \brief Creates a structure of type rpcap_header. + * + * This function is provided just because the creation of an rpcap header is quite a common + * task. It accepts all the values that appears into an rpcap_header, and it puts them in + * place using the proper hton() calls. + * + * \param header: a pointer to a user-allocated buffer which will contain the serialized + * header, ready to be sent on the network. + * + * \param type: a value (in the host by order) which will be placed into the header.type + * field and that represents the type of the current message. + * + * \param value: a value (in the host by order) which will be placed into the header.value + * field and that has a message-dependent meaning. + * + * \param length: a value (in the host by order) which will be placed into the header.length + * field and that represents the payload length of the message. + * + * \return Nothing. The serialized header is returned into the 'header' variable. + */ +void rpcap_createhdr(struct rpcap_header *header, uint8 type, uint16 value, uint32 length) +{ + memset(header, 0, sizeof(struct rpcap_header)); + + header->ver = RPCAP_VERSION; + header->type = type; + header->value = htons(value); + header->plen = htonl(length); +} + + + +/* ingroup remote_pri_func + * \brief Checks if the header of the received message is correct. + * + * This function is a way to easily check if the message received, in a certain + * state of the RPCAP protocol Finite State Machine, is valid. This function accepts, + * as a parameter, the list of message types that are allowed in a certain situation, + * and it returns the one which occurs. + * + * \param errbuf: a pointer to a user-allocated buffer (of size PCAP_ERRBUF_SIZE) + * that will contain the error message (in case there is one). It could be either problem + * occurred inside this function (e.g. a network problem in case it tries to send an + * error on the other peer and the send() call fails), an error message which has been + * sent to us from the other party, or a version error (the message receive has a version + * number that is incompatible with our). + * + * \param sock: the socket that has to be used to receive data. This function can + * read data from socket in case the version contained into the message is not compatible + * with our. In that case, all the message is purged from the socket, so that the following + * recv() calls will return a new message. + * + * \param header: a pointer to and 'rpcap_header' structure that keeps the data received from + * the network (still in network byte order) and that has to be checked. + * + * \param first: this function has a variable number of parameters. From this point on, + * all the messages that are valid in this context must be passed as parameters. + * The message type list must be terminated with a '0' value, the null message type, + * which means 'no more types to check'. The RPCAP protocol does not define anything with + * message type equal to zero, so there is no ambiguity in using this value as a list terminator. + * + * \return The message type of the message that has been detected. In case of errors (e.g. the + * header contains a type that is not listed among the allowed types), this function will + * return the following codes: + * - (-1) if the version is incompatible. + * - (-2) if the code is not among the one listed into the parameters list + * - (-3) if a network error (connection reset, ...) + * - RPCAP_MSG_ERROR if the message is an error message (it follow that the RPCAP_MSG_ERROR + * could not be present in the allowed message-types list, because this function checks + * for errors anyway) + * + * In case either the version is incompatible or nothing matches (i.e. it returns '-1' or '-2'), + * it discards the message body (i.e. it reads the remaining part of the message from the + * network and it discards it) so that the application is ready to receive a new message. + */ +int rpcap_checkmsg(char *errbuf, SOCKET sock, struct rpcap_header *header, uint8 first, ...) +{ + va_list ap; + uint8 type; + int32 len; + + va_start(ap, first); + + /* Check if the present version of the protocol can handle this message */ + if (rpcap_checkver(sock, header, errbuf)) + { + SOCK_ASSERT(errbuf, 1); + + va_end(ap); + return -1; + } + + type = first; + + while (type != 0) + { + /* + * The message matches with one of the types listed + * There is no need of conversions since both values are uint8 + * + * Check if the other side reported an error. + * If yes, it retrieves it and it returns it back to the caller + */ + if (header->type == RPCAP_MSG_ERROR) + { + len = ntohl(header->plen); + + if (len >= PCAP_ERRBUF_SIZE) + { + if (sock_recv(sock, errbuf, PCAP_ERRBUF_SIZE - 1, SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE)) + return -3; + + sock_discard(sock, len - (PCAP_ERRBUF_SIZE - 1), NULL, 0); + + /* Put '\0' at the end of the string */ + errbuf[PCAP_ERRBUF_SIZE - 1] = 0; + } + else + { + if (sock_recv(sock, errbuf, len, SOCK_RECEIVEALL_YES, errbuf, PCAP_ERRBUF_SIZE) == -1) + return -3; + + /* Put '\0' at the end of the string */ + errbuf[len] = 0; + } + + + va_end(ap); + return header->type; + } + + if (header->type == type) + { + va_end(ap); + return header->type; + } + + /* get next argument */ + type = va_arg(ap, int); + } + + /* we already have an error, so please discard this one */ + sock_discard(sock, ntohl(header->plen), NULL, 0); + + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The other endpoint sent a message that is not allowed here."); + SOCK_ASSERT(errbuf, 1); + + va_end(ap); + return -2; +} + + + +/* \ingroup remote_pri_func + * \brief Checks if the version contained into the message is compatible with + * the one handled by this implementation. + * + * Right now, this function does not have any sophisticated task: if the versions + * are different, it returns -1 and it discards the message. + * It is expected that in the future this message will become more complex. + * + * \param sock: the socket that has to be used to receive data. This function can + * read data from socket in case the version contained into the message is not compatible + * with our. In that case, all the message is purged from the socket, so that the following + * recv() calls will return a new (clean) message. + * + * \param header: a pointer to and 'rpcap_header' structure that keeps the data received from + * the network (still in network byte order) and that has to be checked. + * + * \param errbuf: a pointer to a user-allocated buffer (of size PCAP_ERRBUF_SIZE) + * that will contain the error message (in case there is one). The error message is + * "incompatible version". + * + * \return '0' if everything is fine, '-1' if some errors occurred. The error message is returned + * in the 'errbuf' variable. + */ +int rpcap_checkver(SOCKET sock, struct rpcap_header *header, char *errbuf) +{ + /* + * This is a sample function. + * + * In the real world, you have to check at the type code, + * and decide accordingly. + */ + + if (header->ver != RPCAP_VERSION) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Incompatible version number: message discarded."); + + /* we already have an error, so please discard this one */ + sock_discard(sock, ntohl(header->plen), NULL, 0); + return -1; + } + + return 0; +} + + +/* \ingroup remote_pri_func + * + * \brief It returns the socket currently used for this active connection (active mode only). + * + * This function is just for internal use; it returns the socket ID of the active connection + * currently opened. + * + * \param host: a string that keeps the host name of the host for which we want to + * get the socket ID for that active connection. + * + * \param errbuf: a pointer to a user-allocated buffer (of size PCAP_ERRBUF_SIZE) + * that will contain the error message (in case there is one). + * + * \return the socket identifier if everything is fine, '0' if this host is not in the active + * host list. It returns '-1' in case of error. The error message is returned into the errbuf variable. + * + * \warning Win32: be carefully not to assign the returning value of this call to a SOCKET + * directly. It should lead to wrong results, since Win32 sockets are unsigned int; therefore + * a negative value could not be handled correctly. + */ +int rpcap_remoteact_getsock(const char *host, char *errbuf) +{ + struct activehosts *temp; /* temp var needed to scan the host list chain */ + struct addrinfo hints, *addrinfo, *ai_next; /* temp var needed to translate between hostname to its address */ + int retval; + + /* retrieve the network address corresponding to 'host' */ + addrinfo = NULL; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + retval = getaddrinfo(host, "0", &hints, &addrinfo); + if (retval != 0) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "getaddrinfo() %s", gai_strerror(retval)); + return -1; + } + + temp = activeHosts; + + while (temp) + { + ai_next = addrinfo; + while (ai_next) + { + if (sock_cmpaddr(&temp->host, (struct sockaddr_storage *) ai_next->ai_addr) == 0) + return (temp->sockctrl); + + ai_next = ai_next->ai_next; + } + temp = temp->next; + } + + if (addrinfo) + freeaddrinfo(addrinfo); + + /* The host you want to get the socket ID does not have an active connection */ + return 0; +} + + + + + diff --git a/pcap-remote.h b/pcap-remote.h new file mode 100644 index 00000000..23138609 --- /dev/null +++ b/pcap-remote.h @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2008 CACE Technologies, Davis (California) + * 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, CACE Technologies + * 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. + * + */ + +#ifndef __PCAP_REMOTE_H__ +#define __PCAP_REMOTE_H__ + + +#include "pcap.h" +#include "sockutils.h" /* Needed for some structures (like SOCKET, sockaddr_in) which are used here */ + + +/* + * \file pcap-remote.h + * + * This file keeps all the new definitions and typedefs that are exported to the user and + * that are needed for the RPCAP protocol. + * + * \warning All the RPCAP functions that are allowed to return a buffer containing + * the error description can return max PCAP_ERRBUF_SIZE characters. + * However there is no guarantees that the string will be zero-terminated. + * Best practice is to define the errbuf variable as a char of size 'PCAP_ERRBUF_SIZE+1' + * and to insert manually the termination char at the end of the buffer. This will + * guarantee that no buffer overflows occur even if we use the printf() to show + * the error on the screen. + * + * \warning This file declares some typedefs that MUST be of a specific size. + * These definitions (i.e. typedefs) could need to be changed on other platforms than + * Intel IA32. + * + * \warning This file defines some structures that are used to transfer data on the network. + * Be careful that you compiler MUST not insert padding into these structures + * for better alignment. + * These structures have been created in order to be correctly aligned to a 32 bits + * boundary, but be careful in any case. + */ + + + + + + + + +/********************************************************* + * * + * General definitions / typedefs for the RPCAP protocol * + * * + *********************************************************/ + +/* All the following structures and typedef belongs to the Private Documentation */ +/* + * \addtogroup remote_pri_struct + * \{ + */ + +#define RPCAP_DEFAULT_NETPORT "2002" /* Default port on which the RPCAP daemon is waiting for connections. */ +/* Default port on which the client workstation is waiting for connections in case of active mode. */ +#define RPCAP_DEFAULT_NETPORT_ACTIVE "2003" +#define RPCAP_DEFAULT_NETADDR "" /* Default network address on which the RPCAP daemon binds to. */ +#define RPCAP_VERSION 0 /* Present version of the RPCAP protocol (0 = Experimental). */ +#define RPCAP_TIMEOUT_INIT 90 /* Initial timeout for RPCAP connections (default: 90 sec) */ +#define RPCAP_TIMEOUT_RUNTIME 180 /* Run-time timeout for RPCAP connections (default: 3 min) */ +#define RPCAP_ACTIVE_WAIT 30 /* Waiting time between two attempts to open a connection, in active mode (default: 30 sec) */ +#define RPCAP_SUSPEND_WRONGAUTH 1 /* If the authentication is wrong, stops 1 sec before accepting a new auth message */ + +/* + * \brief Buffer used by socket functions to send-receive packets. + * In case you plan to have messages larger than this value, you have to increase it. + */ +#define RPCAP_NETBUF_SIZE 64000 + + +/* + * \brief Separators used for the host list. + * + * It is used: + * - by the rpcapd daemon, when you types a list of allowed connecting hosts + * - by the rpcap in active mode, when the client waits for incoming connections from other hosts + */ +#define RPCAP_HOSTLIST_SEP " ,;\n\r" + + + + +/* WARNING: These could need to be changed on other platforms */ +typedef unsigned char uint8; /* Provides an 8-bits unsigned integer */ +typedef unsigned short uint16; /* Provides a 16-bits unsigned integer */ +typedef unsigned int uint32; /* Provides a 32-bits unsigned integer */ +typedef int int32; /* Provides a 32-bits integer */ + + + + +/* + * \brief Keeps a list of all the opened connections in the active mode. + * + * This structure defines a linked list of items that are needed to keep the info required to + * manage the active mode. + * In other words, when a new connection in active mode starts, this structure is updated so that + * it reflects the list of active mode connections currently opened. + * This structure is required by findalldevs() and open_remote() to see if they have to open a new + * control connection toward the host, or they already have a control connection in place. + */ +struct activehosts +{ + struct sockaddr_storage host; + SOCKET sockctrl; + struct activehosts *next; +}; + + +/********************************************************* + * * + * Protocol messages formats * + * * + *********************************************************/ +/* WARNING Take care you compiler does not insert padding for better alignments into these structs */ + + +/* Common header for all the RPCAP messages */ +struct rpcap_header +{ + uint8 ver; /* RPCAP version number */ + uint8 type; /* RPCAP message type (error, findalldevs, ...) */ + uint16 value; /* Message-dependent value (not always used) */ + uint32 plen; /* Length of the payload of this RPCAP message */ +}; + + +/* Format of the message for the interface description (findalldevs command) */ +struct rpcap_findalldevs_if +{ + uint16 namelen; /* Length of the interface name */ + uint16 desclen; /* Length of the interface description */ + uint32 flags; /* Interface flags */ + uint16 naddr; /* Number of addresses */ + uint16 dummy; /* Must be zero */ +}; + + +/* Format of the message for the address listing (findalldevs command) */ +struct rpcap_findalldevs_ifaddr +{ + struct sockaddr_storage addr; /* Network address */ + struct sockaddr_storage netmask; /* Netmask for that address */ + struct sockaddr_storage broadaddr; /* Broadcast address for that address */ + struct sockaddr_storage dstaddr; /* P2P destination address for that address */ +}; + + + +/* + * \brief Format of the message of the connection opening reply (open command). + * + * This structure transfers over the network some of the values useful on the client side. + */ +struct rpcap_openreply +{ + int32 linktype; /* Link type */ + int32 tzoff; /* Timezone offset */ +}; + + + +/* Format of the message that starts a remote capture (startcap command) */ +struct rpcap_startcapreq +{ + uint32 snaplen; /* Length of the snapshot (number of bytes to capture for each packet) */ + uint32 read_timeout; /* Read timeout in milliseconds */ + uint16 flags; /* Flags (see RPCAP_STARTCAPREQ_FLAG_xxx) */ + uint16 portdata; /* Network port on which the client is waiting at (if 'serveropen') */ +}; + + +/* Format of the reply message that devoted to start a remote capture (startcap reply command) */ +struct rpcap_startcapreply +{ + int32 bufsize; /* Size of the user buffer allocated by WinPcap; it can be different from the one we chose */ + uint16 portdata; /* Network port on which the server is waiting at (passive mode only) */ + uint16 dummy; /* Must be zero */ +}; + + +/* + * \brief Format of the header which encapsulates captured packets when transmitted on the network. + * + * This message requires the general header as well, since we want to be able to exchange + * more information across the network in the future (for example statistics, and kind like that). + */ +struct rpcap_pkthdr +{ + uint32 timestamp_sec; /* 'struct timeval' compatible, it represents the 'tv_sec' field */ + uint32 timestamp_usec; /* 'struct timeval' compatible, it represents the 'tv_usec' field */ + uint32 caplen; /* Length of portion present in the capture */ + uint32 len; /* Real length this packet (off wire) */ + uint32 npkt; /* Ordinal number of the packet (i.e. the first one captured has '1', the second one '2', etc) */ +}; + + +/* General header used for the pcap_setfilter() command; keeps just the number of BPF instructions */ +struct rpcap_filter +{ + uint16 filtertype; /* type of the filter transferred (BPF instructions, ...) */ + uint16 dummy; /* Must be zero */ + uint32 nitems; /* Number of items contained into the filter (e.g. BPF instructions for BPF filters) */ +}; + + +/* Structure that keeps a single BPF instuction; it is repeated 'ninsn' times according to the 'rpcap_filterbpf' header */ +struct rpcap_filterbpf_insn +{ + uint16 code; /* opcode of the instruction */ + uint8 jt; /* relative offset to jump to in case of 'true' */ + uint8 jf; /* relative offset to jump to in case of 'false' */ + int32 k; /* instruction-dependent value */ +}; + + +/* Structure that keeps the data required for the authentication on the remote host */ +struct rpcap_auth +{ + uint16 type; /* Authentication type */ + uint16 dummy; /* Must be zero */ + uint16 slen1; /* Length of the first authentication item (e.g. username) */ + uint16 slen2; /* Length of the second authentication item (e.g. password) */ +}; + + +/* Structure that keeps the statistics about the number of packets captured, dropped, etc. */ +struct rpcap_stats +{ + uint32 ifrecv; /* Packets received by the kernel filter (i.e. pcap_stats.ps_recv) */ + uint32 ifdrop; /* Packets dropped by the network interface (e.g. not enough buffers) (i.e. pcap_stats.ps_ifdrop) */ + uint32 krnldrop; /* Packets dropped by the kernel filter (i.e. pcap_stats.ps_drop) */ + uint32 svrcapt; /* Packets captured by the RPCAP daemon and sent on the network */ +}; + + +/* Structure that is needed to set sampling parameters */ +struct rpcap_sampling +{ + uint8 method; /* Sampling method */ + uint8 dummy1; /* Must be zero */ + uint16 dummy2; /* Must be zero */ + uint32 value; /* Parameter related to the sampling method */ +}; + + +/* + * Private data for doing a live capture. + */ +struct pcap_md { + struct pcap_stat stat; + /* XXX */ + int use_bpf; /* using kernel filter */ + u_long TotPkts; /* can't overflow for 79 hrs on ether */ + u_long TotAccepted; /* count accepted by filter */ + u_long TotDrops; /* count of dropped packets */ + long TotMissed; /* missed by i/f during this run */ + long OrigMissed; /* missed by i/f before this run */ + char *device; /* device name */ + int timeout; /* timeout for buffering */ + int must_clear; /* stuff we must clear when we close */ + struct pcap *next; /* list of open pcaps that need stuff cleared on close */ +#ifdef linux + int sock_packet; /* using Linux 2.0 compatible interface */ + int cooked; /* using SOCK_DGRAM rather than SOCK_RAW */ + int ifindex; /* interface index of device we're bound to */ + int lo_ifindex; /* interface index of the loopback device */ + u_int packets_read; /* count of packets read with recvfrom() */ + bpf_u_int32 oldmode; /* mode to restore when turning monitor mode off */ + u_int tp_version; /* version of tpacket_hdr for mmaped ring */ + u_int tp_hdrlen; /* hdrlen of tpacket_hdr for mmaped ring */ +#endif /* linux */ + +#ifdef HAVE_DAG_API +#ifdef HAVE_DAG_STREAMS_API + u_char *dag_mem_bottom;/* DAG card current memory bottom pointer */ + u_char *dag_mem_top; /* DAG card current memory top pointer */ +#else /* HAVE_DAG_STREAMS_API */ + void *dag_mem_base; /* DAG card memory base address */ + u_int dag_mem_bottom; /* DAG card current memory bottom offset */ + u_int dag_mem_top; /* DAG card current memory top offset */ +#endif /* HAVE_DAG_STREAMS_API */ + int dag_fcs_bits; /* Number of checksum bits from link layer */ + int dag_offset_flags; /* Flags to pass to dag_offset(). */ + int dag_stream; /* DAG stream number */ + int dag_timeout; /* timeout specified to pcap_open_live. + * Same as in linux above, introduce + * generally? + */ +#endif /* HAVE_DAG_API */ +#ifdef HAVE_ZEROCOPY_BPF + /* + * Zero-copy read buffer -- for zero-copy BPF. 'buffer' above will + * alternative between these two actual mmap'd buffers as required. + * As there is a header on the front size of the mmap'd buffer, only + * some of the buffer is exposed to libpcap as a whole via bufsize; + * zbufsize is the true size. zbuffer tracks the current zbuf + * associated with buffer so that it can be used to decide which the + * next buffer to read will be. + */ + u_char *zbuf1, *zbuf2, *zbuffer; + u_int zbufsize; + u_int zerocopy; + u_int interrupted; + struct timespec firstsel; + /* + * If there's currently a buffer being actively processed, then it is + * referenced here; 'buffer' is also pointed at it, but offset by the + * size of the header. + */ + struct bpf_zbuf_header *bzh; +#endif /* HAVE_ZEROCOPY_BPF */ + + + +#ifdef HAVE_REMOTE + /* + * There is really a mess with previous variables, and it seems to me that they are not used + * (they are used in pcap_pf.c only). I think we have to start using them. + * The meaning is the following: + * + * - TotPkts: the amount of packets received by the bpf filter, *before* applying the filter + * - TotAccepted: the amount of packets that satisfies the filter + * - TotDrops: the amount of packet that were dropped into the kernel buffer because of lack of space + * - TotMissed: the amount of packets that were dropped by the physical interface; it is basically + * the value of the hardware counter into the card. This number is never put to zero, so this number + * takes into account the *total* number of interface drops starting from the interface power-on. + * - OrigMissed: the amount of packets that were dropped by the interface *when the capture begins*. + * This value is used to detect the number of packets dropped by the interface *during the present + * capture*, so that (ps_ifdrops= TotMissed - OrigMissed). + */ + unsigned int TotNetDrops; /* keeps the number of packets that have been dropped by the network */ + /* + * \brief It keeps the number of packets that have been received by the application. + * + * Packets dropped by the kernel buffer are not counted in this variable. The variable is always + * equal to (TotAccepted - TotDrops), except for the case of remote capture, in which we have also + * packets in flight, i.e. that have been transmitted by the remote host, but that have not been + * received (yet) from the client. In this case, (TotAccepted - TotDrops - TotNetDrops) gives a + * wrong result, since this number does not corresponds always to the number of packet received by + * the application. For this reason, in the remote capture we need another variable that takes + * into account of the number of packets actually received by the application. + */ + unsigned int TotCapt; + + /*! \brief '1' if we're the network client; needed by several functions (like pcap_setfilter() ) to know if + they have to use the socket or they have to open the local adapter. */ + int rmt_clientside; + + SOCKET rmt_sockctrl; //!< socket ID of the socket used for the control connection + SOCKET rmt_sockdata; //!< socket ID of the socket used for the data connection + int rmt_flags; //!< we have to save flags, since they are passed by the pcap_open_live(), but they are used by the pcap_startcapture() + int rmt_capstarted; //!< 'true' if the capture is already started (needed to knoe if we have to call the pcap_startcapture() + struct pcap_samp rmt_samp; //!< Keeps the parameters related to the sampling process. + char *currentfilter; //!< Pointer to a buffer (allocated at run-time) that stores the current filter. Needed when flag PCAP_OPENFLAG_NOCAPTURE_RPCAP is turned on. +#endif /* HAVE_REMOTE */ + +}; + + +/* Messages field coding */ +#define RPCAP_MSG_ERROR 1 /* Message that keeps an error notification */ +#define RPCAP_MSG_FINDALLIF_REQ 2 /* Request to list all the remote interfaces */ +#define RPCAP_MSG_OPEN_REQ 3 /* Request to open a remote device */ +#define RPCAP_MSG_STARTCAP_REQ 4 /* Request to start a capture on a remote device */ +#define RPCAP_MSG_UPDATEFILTER_REQ 5 /* Send a compiled filter into the remote device */ +#define RPCAP_MSG_CLOSE 6 /* Close the connection with the remote peer */ +#define RPCAP_MSG_PACKET 7 /* This is a 'data' message, which carries a network packet */ +#define RPCAP_MSG_AUTH_REQ 8 /* Message that keeps the authentication parameters */ +#define RPCAP_MSG_STATS_REQ 9 /* It requires to have network statistics */ +#define RPCAP_MSG_ENDCAP_REQ 10 /* Stops the current capture, keeping the device open */ +#define RPCAP_MSG_SETSAMPLING_REQ 11 /* Set sampling parameters */ + +#define RPCAP_MSG_FINDALLIF_REPLY (128+RPCAP_MSG_FINDALLIF_REQ) /* Keeps the list of all the remote interfaces */ +#define RPCAP_MSG_OPEN_REPLY (128+RPCAP_MSG_OPEN_REQ) /* The remote device has been opened correctly */ +#define RPCAP_MSG_STARTCAP_REPLY (128+RPCAP_MSG_STARTCAP_REQ) /* The capture is starting correctly */ +#define RPCAP_MSG_UPDATEFILTER_REPLY (128+RPCAP_MSG_UPDATEFILTER_REQ) /* The filter has been applied correctly on the remote device */ +#define RPCAP_MSG_AUTH_REPLY (128+RPCAP_MSG_AUTH_REQ) /* Sends a message that says 'ok, authorization successful' */ +#define RPCAP_MSG_STATS_REPLY (128+RPCAP_MSG_STATS_REQ) /* Message that keeps the network statistics */ +#define RPCAP_MSG_ENDCAP_REPLY (128+RPCAP_MSG_ENDCAP_REQ) /* Confirms that the capture stopped successfully */ +#define RPCAP_MSG_SETSAMPLING_REPLY (128+RPCAP_MSG_SETSAMPLING_REQ) /* Confirms that the capture stopped successfully */ + +#define RPCAP_STARTCAPREQ_FLAG_PROMISC 1 /* Enables promiscuous mode (default: disabled) */ +#define RPCAP_STARTCAPREQ_FLAG_DGRAM 2 /* Use a datagram (i.e. UDP) connection for the data stream (default: use TCP)*/ +#define RPCAP_STARTCAPREQ_FLAG_SERVEROPEN 4 /* The server has to open the data connection toward the client */ +#define RPCAP_STARTCAPREQ_FLAG_INBOUND 8 /* Capture only inbound packets (take care: the flag has no effects with promiscuous enabled) */ +#define RPCAP_STARTCAPREQ_FLAG_OUTBOUND 16 /* Capture only outbound packets (take care: the flag has no effects with promiscuous enabled) */ + +#define RPCAP_UPDATEFILTER_BPF 1 /* This code tells us that the filter is encoded with the BPF/NPF syntax */ + + +/* Network error codes */ +#define PCAP_ERR_NETW 1 /* Network error */ +#define PCAP_ERR_INITTIMEOUT 2 /* The RPCAP initial timeout has expired */ +#define PCAP_ERR_AUTH 3 /* Generic authentication error */ +#define PCAP_ERR_FINDALLIF 4 /* Generic findalldevs error */ +#define PCAP_ERR_NOREMOTEIF 5 /* The findalldevs was ok, but the remote end had no interfaces to list */ +#define PCAP_ERR_OPEN 6 /* Generic pcap_open error */ +#define PCAP_ERR_UPDATEFILTER 7 /* Generic updatefilter error */ +#define PCAP_ERR_GETSTATS 8 /* Generic pcap_stats error */ +#define PCAP_ERR_READEX 9 /* Generic pcap_next_ex error */ +#define PCAP_ERR_HOSTNOAUTH 10 /* The host is not authorized to connect to this server */ +#define PCAP_ERR_REMOTEACCEPT 11 /* Generic pcap_remoteaccept error */ +#define PCAP_ERR_STARTCAPTURE 12 /* Generic pcap_startcapture error */ +#define PCAP_ERR_ENDCAPTURE 13 /* Generic pcap_endcapture error */ +#define PCAP_ERR_RUNTIMETIMEOUT 14 /* The RPCAP run-time timeout has expired */ +#define PCAP_ERR_SETSAMPLING 15 /* Error during the settings of sampling parameters */ +#define PCAP_ERR_WRONGMSG 16 /* The other end endpoint sent a message which has not been recognized */ +#define PCAP_ERR_WRONGVER 17 /* The other end endpoint has a version number that is not compatible with our */ +/* + * \} + * // end of private documentation + */ + + +/********************************************************* + * * + * Exported function prototypes * + * * + *********************************************************/ +int pcap_opensource_remote(pcap_t *p, struct pcap_rmtauth *auth); +int pcap_startcapture_remote(pcap_t *fp); + +int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr **pkt_header, u_char **pkt_data); +int pcap_read_remote(pcap_t *p, int cnt, pcap_handler callback, u_char *user); +int pcap_updatefilter_remote(pcap_t *fp, struct bpf_program *prog); +int pcap_setfilter_remote(pcap_t *fp, struct bpf_program *prog); +int pcap_stats_remote(pcap_t *p, struct pcap_stat *ps); +int pcap_setsampling_remote(pcap_t *p); +struct pcap_stat *pcap_stats_ex_remote(pcap_t *p); +void pcap_cleanup_remote(pcap_t *p); + +void rpcap_createhdr(struct rpcap_header *header, uint8 type, uint16 value, uint32 length); +int rpcap_deseraddr(struct sockaddr_storage *sockaddrin, struct sockaddr_storage **sockaddrout, char *errbuf); +int rpcap_checkmsg(char *errbuf, SOCKET sock, struct rpcap_header *header, uint8 first, ...); +int rpcap_senderror(SOCKET sock, char *error, unsigned short errcode, char *errbuf); +int rpcap_sendauth(SOCKET sock, struct pcap_rmtauth *auth, char *errbuf); + +int rpcap_remoteact_getsock(const char *host, char *errbuf); + +#endif + diff --git a/pcap-win32.c b/pcap-win32.c index 40e9f079..77cddcf9 100644 --- a/pcap-win32.c +++ b/pcap-win32.c @@ -53,6 +53,9 @@ int* _errno(); #define errno (*_errno()) #endif /* __MINGW32__ */ +#ifdef HAVE_REMOTE +#include "pcap-remote.h" +#endif /* HAVE_REMOTE */ static int pcap_setfilter_win32_npf(pcap_t *, struct bpf_program *); static int pcap_setfilter_win32_dag(pcap_t *, struct bpf_program *); @@ -1065,8 +1068,11 @@ pcap_t * pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - - p = pcap_create_common(ebuf, sizeof (struct pcap_win)); +#ifdef HAVE_REMOTE + p = pcap_create_common(ebuf, sizeof(struct pcap_win) + sizeof(struct pcap_md)); +#else + p = pcap_create_common(ebuf, sizeof(struct pcap_win))); +#endif /* HAVE_REMOTE */ if (p == NULL) return (NULL); diff --git a/pcap/pcap.h b/pcap/pcap.h index b5d50b8e..7f92a379 100644 --- a/pcap/pcap.h +++ b/pcap/pcap.h @@ -526,6 +526,11 @@ PCAP_API void bpf_dump(const struct bpf_program *, int); #endif /* _WIN32/MSDOS/UN*X */ +#ifdef HAVE_REMOTE + /* Includes most of the public stuff that is needed for the remote capture */ + #include <remote-ext.h> +#endif /* HAVE_REMOTE */ + #ifdef __cplusplus } #endif diff --git a/remote-ext.h b/remote-ext.h new file mode 100644 index 00000000..d854ee28 --- /dev/null +++ b/remote-ext.h @@ -0,0 +1,447 @@ +/* + * 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. + * + */ + + +#ifndef __REMOTE_EXT_H__ +#define __REMOTE_EXT_H__ + + +#ifndef HAVE_REMOTE +#error Please do not include this file directly. Just define HAVE_REMOTE and then include pcap.h +#endif + +/*// Definition for Microsoft Visual Studio */ +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * \file remote-ext.h + * + * The goal of this file it to include most of the new definitions that should be + * placed into the pcap.h file. + * + * It includes all new definitions (structures and functions like pcap_open(). + * Some of the functions are not really a remote feature, but, right now, + * they are placed here. + */ + + + +/*// All this stuff is public */ +/* + * \addtogroup remote_struct + * \{ + */ + + + + +/* + * \brief Defines the maximum buffer size in which address, port, interface names are kept. + * + * In case the adapter name or such is larger than this value, it is truncated. + * This is not used by the user; however it must be aware that an hostname / interface + * name longer than this value will be truncated. + */ +#define PCAP_BUF_SIZE 1024 + + +/* + * \addtogroup remote_source_ID + * \{ + */ + + +/* + * \brief Internal representation of the type of source in use (file, + * remote/local interface). + * + * This indicates a file, i.e. the user want to open a capture from a local file. + */ +#define PCAP_SRC_FILE 2 +/* + * \brief Internal representation of the type of source in use (file, + * remote/local interface). + * + * This indicates a local interface, i.e. the user want to open a capture from + * a local interface. This does not involve the RPCAP protocol. + */ +#define PCAP_SRC_IFLOCAL 3 +/* + * \brief Internal representation of the type of source in use (file, + * remote/local interface). + * + * This indicates a remote interface, i.e. the user want to open a capture from + * an interface on a remote host. This does involve the RPCAP protocol. + */ +#define PCAP_SRC_IFREMOTE 4 + +/* + * \} + */ + + + +/* \addtogroup remote_source_string + * + * The formats allowed by the pcap_open() are the following: + * - file://path_and_filename [opens a local file] + * - rpcap://devicename [opens the selected device devices available on the local host, without using the RPCAP protocol] + * - rpcap://host/devicename [opens the selected device available on a remote host] + * - rpcap://host:port/devicename [opens the selected device available on a remote host, using a non-standard port for RPCAP] + * - adaptername [to open a local adapter; kept for compability, but it is strongly discouraged] + * - (NULL) [to open the first local adapter; kept for compability, but it is strongly discouraged] + * + * The formats allowed by the pcap_findalldevs_ex() are the following: + * - file://folder/ [lists all the files in the given folder] + * - rpcap:// [lists all local adapters] + * - rpcap://host:port/ [lists the devices available on a remote host] + * + * Referring to the 'host' and 'port' parameters, they can be either numeric or literal. Since + * IPv6 is fully supported, these are the allowed formats: + * + * - host (literal): e.g. host.foo.bar + * - host (numeric IPv4): e.g. 10.11.12.13 + * - host (numeric IPv4, IPv6 style): e.g. [10.11.12.13] + * - host (numeric IPv6): e.g. [1:2:3::4] + * - port: can be either numeric (e.g. '80') or literal (e.g. 'http') + * + * Here you find some allowed examples: + * - rpcap://host.foo.bar/devicename [everything literal, no port number] + * - rpcap://host.foo.bar:1234/devicename [everything literal, with port number] + * - rpcap://10.11.12.13/devicename [IPv4 numeric, no port number] + * - rpcap://10.11.12.13:1234/devicename [IPv4 numeric, with port number] + * - rpcap://[10.11.12.13]:1234/devicename [IPv4 numeric with IPv6 format, with port number] + * - rpcap://[1:2:3::4]/devicename [IPv6 numeric, no port number] + * - rpcap://[1:2:3::4]:1234/devicename [IPv6 numeric, with port number] + * - rpcap://[1:2:3::4]:http/devicename [IPv6 numeric, with literal port number] + * + * \{ + */ + + +/* + * \brief String that will be used to determine the type of source in use (file, + * remote/local interface). + * + * This string will be prepended to the interface name in order to create a string + * that contains all the information required to open the source. + * + * This string indicates that the user wants to open a capture from a local file. + */ +#define PCAP_SRC_FILE_STRING "file://" +/* + * \brief String that will be used to determine the type of source in use (file, + * remote/local interface). + * + * This string will be prepended to the interface name in order to create a string + * that contains all the information required to open the source. + * + * This string indicates that the user wants to open a capture from a network interface. + * This string does not necessarily involve the use of the RPCAP protocol. If the + * interface required resides on the local host, the RPCAP protocol is not involved + * and the local functions are used. + */ +#define PCAP_SRC_IF_STRING "rpcap://" + +/* + * \} + */ + + + + + +/* + * \addtogroup remote_open_flags + * \{ + */ + +/* + * \brief Defines if the adapter has to go in promiscuous mode. + * + * It is '1' if you have to open the adapter in promiscuous mode, '0' otherwise. + * Note that even if this parameter is false, the interface could well be in promiscuous + * mode for some other reason (for example because another capture process with + * promiscuous mode enabled is currently using that interface). + * On on Linux systems with 2.2 or later kernels (that have the "any" device), this + * flag does not work on the "any" device; if an argument of "any" is supplied, + * the 'promisc' flag is ignored. + */ +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +/* + * \brief Defines if the data transfer (in case of a remote + * capture) has to be done with UDP protocol. + * + * If it is '1' if you want a UDP data connection, '0' if you want + * a TCP data connection; control connection is always TCP-based. + * A UDP connection is much lighter, but it does not guarantee that all + * the captured packets arrive to the client workstation. Moreover, + * it could be harmful in case of network congestion. + * This flag is meaningless if the source is not a remote interface. + * In that case, it is simply ignored. + */ +#define PCAP_OPENFLAG_DATATX_UDP 2 + + +/* + * \brief Defines if the remote probe will capture its own generated traffic. + * + * In case the remote probe uses the same interface to capture traffic and to send + * data back to the caller, the captured traffic includes the RPCAP traffic as well. + * If this flag is turned on, the RPCAP traffic is excluded from the capture, so that + * the trace returned back to the collector is does not include this traffic. + */ +#define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4 + +/* + * \brief Defines if the local adapter will capture its own generated traffic. + * + * This flag tells the underlying capture driver to drop the packets that were sent by itself. + * This is useful when building applications like bridges, that should ignore the traffic + * they just sent. + */ +#define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8 + +/* + * \brief This flag configures the adapter for maximum responsiveness. + * + * In presence of a large value for nbytes, WinPcap waits for the arrival of several packets before + * copying the data to the user. This guarantees a low number of system calls, i.e. lower processor usage, + * i.e. better performance, which is good for applications like sniffers. If the user sets the + * PCAP_OPENFLAG_MAX_RESPONSIVENESS flag, the capture driver will copy the packets as soon as the application + * is ready to receive them. This is suggested for real time applications (like, for example, a bridge) + * that need the best responsiveness. + */ +#define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16 + +/* + * \} + */ + + +/* + * \addtogroup remote_samp_methods + * \{ + */ + +/* + *\brief No sampling has to be done on the current capture. + * + * In this case, no sampling algorithms are applied to the current capture. + */ +#define PCAP_SAMP_NOSAMP 0 + +/* + * \brief It defines that only 1 out of N packets must be returned to the user. + * + * In this case, the 'value' field of the 'pcap_samp' structure indicates the + * number of packets (minus 1) that must be discarded before one packet got accepted. + * In other words, if 'value = 10', the first packet is returned to the caller, while + * the following 9 are discarded. + */ +#define PCAP_SAMP_1_EVERY_N 1 + +/* + * \brief It defines that we have to return 1 packet every N milliseconds. + * + * In this case, the 'value' field of the 'pcap_samp' structure indicates the 'waiting + * time' in milliseconds before one packet got accepted. + * In other words, if 'value = 10', the first packet is returned to the caller; the next + * returned one will be the first packet that arrives when 10ms have elapsed. + */ +#define PCAP_SAMP_FIRST_AFTER_N_MS 2 + +/* + * \} + */ + + +/* + * \addtogroup remote_auth_methods + * \{ + */ + +/* + * \brief It defines the NULL authentication. + * + * This value has to be used within the 'type' member of the pcap_rmtauth structure. + * The 'NULL' authentication has to be equal to 'zero', so that old applications + * can just put every field of struct pcap_rmtauth to zero, and it does work. + */ +#define RPCAP_RMTAUTH_NULL 0 +/* + * \brief It defines the username/password authentication. + * + * With this type of authentication, the RPCAP protocol will use the username/ + * password provided to authenticate the user on the remote machine. If the + * authentication is successful (and the user has the right to open network devices) + * the RPCAP connection will continue; otherwise it will be dropped. + * + * This value has to be used within the 'type' member of the pcap_rmtauth structure. + */ +#define RPCAP_RMTAUTH_PWD 1 + +/* + * \} + */ + + + + +/* + * \brief This structure keeps the information needed to autheticate + * the user on a remote machine. + * + * The remote machine can either grant or refuse the access according + * to the information provided. + * In case the NULL authentication is required, both 'username' and + * 'password' can be NULL pointers. + * + * This structure is meaningless if the source is not a remote interface; + * in that case, the functions which requires such a structure can accept + * a NULL pointer as well. + */ +struct pcap_rmtauth +{ + /* + * \brief Type of the authentication required. + * + * In order to provide maximum flexibility, we can support different types + * of authentication based on the value of this 'type' variable. The currently + * supported authentication methods are defined into the + * \link remote_auth_methods Remote Authentication Methods Section\endlink. + */ + int type; + /* + * \brief Zero-terminated string containing the username that has to be + * used on the remote machine for authentication. + * + * This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication + * and it can be NULL. + */ + char *username; + /* + * \brief Zero-terminated string containing the password that has to be + * used on the remote machine for authentication. + * + * This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication + * and it can be NULL. + */ + char *password; +}; + + +/* + * \brief This structure defines the information related to sampling. + * + * In case the sampling is requested, the capturing device should read + * only a subset of the packets coming from the source. The returned packets depend + * on the sampling parameters. + * + * \warning The sampling process is applied <strong>after</strong> the filtering process. + * In other words, packets are filtered first, then the sampling process selects a + * subset of the 'filtered' packets and it returns them to the caller. + */ +struct pcap_samp +{ + /* + * Method used for sampling. Currently, the supported methods are listed in the + * \link remote_samp_methods Sampling Methods Section\endlink. + */ + int method; + + /* + * This value depends on the sampling method defined. For its meaning, please check + * at the \link remote_samp_methods Sampling Methods Section\endlink. + */ + int value; +}; + + + + +// Maximum length of an host name (needed for the RPCAP active mode) +#define RPCAP_HOSTLIST_SIZE 1024 + + +/* + * \} + */ // end of public documentation + + +// Exported functions + + + +/* + * \name New WinPcap functions + * + * This section lists the new functions that are able to help considerably in writing + * WinPcap programs because of their easiness of use. + */ +// \{ +PCAP_API pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf); +PCAP_API int pcap_createsrcstr(char *source, int type, const char *host, const char *port, const char *name, char *errbuf); +PCAP_API int pcap_parsesrcstr(const char *source, int *type, char *host, char *port, char *name, char *errbuf); +PCAP_API int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf); +PCAP_API struct pcap_samp *pcap_setsampling(pcap_t *p); + +// \} +// End of new WinPcap functions + + + +/* + * \name Remote Capture functions + */ +// \{ +PCAP_API SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf); +PCAP_API int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf); +PCAP_API int pcap_remoteact_close(const char *host, char *errbuf); +PCAP_API void pcap_remoteact_cleanup(); +// \} +// End of remote capture functions + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/sockutils.c b/sockutils.c new file mode 100644 index 00000000..2466da7a --- /dev/null +++ b/sockutils.c @@ -0,0 +1,1263 @@ +/* + * 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 behavior 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; + size_t 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) + { + size_t 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; + } +} + + + + +/* + * \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' structure. + * + * 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, appending 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 behavior 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() + * until 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 understand (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 temporary buffer + */ + if (size) + { + if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) + return -1; + } + + 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 address 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 succeeds, '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. 'localhost') dependently 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 binary form) into + * a standard IPv6 address like "::1". + * + * The behavior 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 succeeds, '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 additional 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 succeeded, '-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; +} + + diff --git a/sockutils.h b/sockutils.h new file mode 100644 index 00000000..40f0b930 --- /dev/null +++ b/sockutils.h @@ -0,0 +1,248 @@ +/* + * 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. + * + */ + + +#ifndef __SOCKUTILS_H__ +#define __SOCKUTILS_H__ + + +#if _MSC_VER > 1000 +#pragma once +#endif + + +#ifdef WIN32 +/* Prevents a compiler warning in case this was already defined (to avoid that */ +/* windows.h includes winsock.h */ +#ifdef _WINSOCKAPI_ +#undef _WINSOCKAPI_ +#endif +/* Need windef.h for defines used in winsock2.h under MingW32 */ +#ifdef __MINGW32__ +#include <windef.h> +#endif +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <stdio.h> +#include <string.h> /* for memset() */ +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> /* DNS lookup */ +#include <unistd.h> /* close() */ +#include <errno.h> /* errno() */ +#include <netinet/in.h> /* for sockaddr_in, in BSD at least */ +#include <arpa/inet.h> +#include <net/if.h> +#endif + + +/* + * MingW headers include this definition, but only for Windows XP and above. + * MSDN states that this function is available for most versions on Windows. + */ +#if ((defined(__MINGW32__)) && (_WIN32_WINNT < 0x0501)) +int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD, + char*,DWORD,int); +#endif + + +/* + * \defgroup SockUtils Cross-platform socket utilities (IPv4-IPv6) + */ + + +/* + * \addtogroup SockUtils + * \{ + */ + + + +/* + * \defgroup ExportedStruct Exported Structures and Definitions + */ + +/* + * \addtogroup ExportedStruct + * \{ + */ + + + + +/* Some minor differences between UNIX and Win32 */ +#ifdef WIN32 +/* + * \brief In Win32, sockets use unsigned integers; in UNIX, they use signed integer. + * + * So, we define a generic SOCKET in order to be cross-platform compatible. + */ +/* #define SOCKET unsigned int */ +#else +/* + * \brief In Win32, sockets use unsigned integers; in UNIX, they use signed integer. + * + * So, we define a generic SOCKET in order to be cross-platform compatible. + */ + +#define SOCKET int + +/* + * \brief In Win32, the close() call cannot be used for socket. + * + * So, we define a generic closesocket() call in order to be cross-platform compatible. + */ +#define closesocket(a) close(a) +#endif + + + + + +/* + * \brief DEBUG facility: it prints an error message on the screen (stderr) + * + * This macro prints the error on the standard error stream (stderr); + * if we are working in debug mode (i.e. there is no NDEBUG defined) and we are in + * Microsoft Visual C++, the error message will appear on the MSVC console as well. + * + * When NDEBUG is defined, this macro is empty. + * + * \param msg: the message you want to print. + * + * \param expr: 'false' if you want to abort the program, 'true' it you want + * to print the message and continue. + * + * \return No return values. + */ +#ifdef NDEBUG +#define SOCK_ASSERT(msg, expr) ((void)0) +#else +#include <assert.h> +#if (defined(WIN32) && defined(_MSC_VER)) +#include <crtdbg.h> /* for _CrtDbgReport */ +/* Use MessageBox(NULL, msg, "warning", MB_OK)' instead of the other calls if you want to debug a Win32 service */ +/* Remember to activate the 'allow service to interact with desktop' flag of the service */ +#define SOCK_ASSERT(msg, expr) { _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%s\n", msg); fprintf(stderr, "%s\n", msg); assert(expr); } +#else +#define SOCK_ASSERT(msg, expr) { fprintf(stderr, "%s\n", msg); assert(expr); } +#endif +#endif + + + + +/**************************************************** + * * + * Exported functions / definitions * + * * + ****************************************************/ + +/* 'checkonly' flag, into the rpsock_bufferize() */ +#define SOCKBUF_CHECKONLY 1 +/* no 'checkonly' flag, into the rpsock_bufferize() */ +#define SOCKBUF_BUFFERIZE 0 + +/* no 'server' flag; it opens a client socket */ +#define SOCKOPEN_CLIENT 0 +/* 'server' flag; it opens a server socket */ +#define SOCKOPEN_SERVER 1 + +/* Changes the behaviour of the sock_recv(); it does not wait to receive all data */ +#define SOCK_RECEIVEALL_NO 0 +/* Changes the behaviour of the sock_recv(); it waits to receive all data */ +#define SOCK_RECEIVEALL_YES 1 + + +/* + * \} + */ + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* + * \defgroup ExportedFunc Exported Functions + */ + +/* + * \addtogroup ExportedFunc + * \{ + */ + + +int sock_init(char *errbuf, int errbuflen); +void sock_cleanup(); +/* It is 'public' because there are calls (like accept() ) which are not managed from inside the sockutils files */ +void sock_geterror(const char *caller, char *errbuf, int errbufsize); +int sock_initaddress(const char *address, const char *port, +struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen); +int sock_recv(SOCKET socket, char *buffer, int size, int receiveall, char *errbuf, int errbuflen); +SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen); +int sock_close(SOCKET sock, char *errbuf, int errbuflen); + +int sock_send(SOCKET socket, const char *buffer, int size, char *errbuf, int errbuflen); +int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen); +int sock_discard(SOCKET socket, int size, char *errbuf, int errbuflen); +int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf, int errbuflen); +int sock_cmpaddr(struct sockaddr_storage *first, struct sockaddr_storage *second); + +int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen); + +int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen); +int sock_present2network(const char *address, struct sockaddr_storage *sockaddr, int addr_family, char *errbuf, int errbuflen); + + +#ifdef __cplusplus +} +#endif + + +/* + * \} + */ + + + +/* + * \} + */ + + + +#endif |