mirror of
https://git.FreeBSD.org/src.git
synced 2024-11-25 07:49:18 +00:00
57e22627f9
Update libpcap from 1.9.0 to 1.9.1. MFC after: 2 weeks
466 lines
13 KiB
C
466 lines
13 KiB
C
/*
|
|
* 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 "ftmacros.h"
|
|
|
|
/*
|
|
* sockutils.h may include <crtdbg.h> on Windows, and pcap-int.h will
|
|
* include portability.h, and portability.h, on Windows, expects that
|
|
* <crtdbg.h> has already been included, so include sockutils.h first.
|
|
*/
|
|
#include "sockutils.h"
|
|
#include "pcap-int.h" // for the details of the pcap_t structure
|
|
#include "pcap-rpcap.h"
|
|
#include "rpcap-protocol.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
|
|
|
|
/* String identifier to be used in the pcap_findalldevs_ex() */
|
|
#define PCAP_TEXT_SOURCE_FILE "File"
|
|
#define PCAP_TEXT_SOURCE_FILE_LEN (sizeof PCAP_TEXT_SOURCE_FILE - 1)
|
|
/* String identifier to be used in the pcap_findalldevs_ex() */
|
|
#define PCAP_TEXT_SOURCE_ADAPTER "Network adapter"
|
|
#define PCAP_TEXT_SOURCE_ADAPTER_LEN (sizeof "Network adapter" - 1)
|
|
|
|
/* String identifier to be used in the pcap_findalldevs_ex() */
|
|
#define PCAP_TEXT_SOURCE_ON_LOCAL_HOST "on local host"
|
|
#define PCAP_TEXT_SOURCE_ON_LOCAL_HOST_LEN (sizeof PCAP_TEXT_SOURCE_ON_LOCAL_HOST + 1)
|
|
|
|
/****************************************************
|
|
* *
|
|
* Function bodies *
|
|
* *
|
|
****************************************************/
|
|
|
|
int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf)
|
|
{
|
|
int type;
|
|
char name[PCAP_BUF_SIZE], path[PCAP_BUF_SIZE], filename[PCAP_BUF_SIZE];
|
|
size_t pathlen;
|
|
size_t stringlen;
|
|
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 *lastdev; /* Last device in the pcap_if_t list */
|
|
pcap_if_t *dev; /* Device we're adding to the pcap_if_t list */
|
|
|
|
/* List starts out empty. */
|
|
(*alldevs) = NULL;
|
|
lastdev = NULL;
|
|
|
|
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;
|
|
|
|
switch (type)
|
|
{
|
|
case PCAP_SRC_IFLOCAL:
|
|
if (pcap_parsesrcstr(source, &type, NULL, 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)
|
|
{
|
|
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
|
|
"No interfaces found! Make sure libpcap/Npcap 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)
|
|
{
|
|
char *localdesc, *desc;
|
|
|
|
/* 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);
|
|
|
|
/* Make a copy of the new device identifier */
|
|
dev->name = strdup(tmpstring);
|
|
if (dev->name == NULL)
|
|
{
|
|
pcap_fmt_errmsg_for_errno(errbuf,
|
|
PCAP_ERRBUF_SIZE, errno,
|
|
"malloc() failed");
|
|
pcap_freealldevs(*alldevs);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Create the description.
|
|
*/
|
|
if ((dev->description == NULL) || (dev->description[0] == 0))
|
|
localdesc = dev->name;
|
|
else
|
|
localdesc = dev->description;
|
|
if (pcap_asprintf(&desc, "%s '%s' %s",
|
|
PCAP_TEXT_SOURCE_ADAPTER, localdesc,
|
|
PCAP_TEXT_SOURCE_ON_LOCAL_HOST) == -1)
|
|
{
|
|
pcap_fmt_errmsg_for_errno(errbuf,
|
|
PCAP_ERRBUF_SIZE, errno,
|
|
"malloc() failed");
|
|
pcap_freealldevs(*alldevs);
|
|
return -1;
|
|
}
|
|
|
|
/* Now overwrite the description */
|
|
free(dev->description);
|
|
dev->description = desc;
|
|
|
|
dev = dev->next;
|
|
}
|
|
|
|
return 0;
|
|
|
|
case PCAP_SRC_FILE:
|
|
{
|
|
#ifdef _WIN32
|
|
WIN32_FIND_DATA filedata;
|
|
HANDLE filehandle;
|
|
#else
|
|
struct dirent *filedata;
|
|
DIR *unixdir;
|
|
#endif
|
|
|
|
if (pcap_parsesrcstr(source, &type, NULL, NULL, 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);
|
|
pathlen = strlen(path);
|
|
|
|
#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
|
|
|
|
/* Add all files we find to the list. */
|
|
do
|
|
{
|
|
#ifdef _WIN32
|
|
/* Skip the file if the pathname won't fit in the buffer */
|
|
if (pathlen + strlen(filedata.cFileName) >= sizeof(filename))
|
|
continue;
|
|
pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata.cFileName);
|
|
#else
|
|
if (pathlen + strlen(filedata->d_name) >= sizeof(filename))
|
|
continue;
|
|
pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata->d_name);
|
|
#endif
|
|
|
|
fp = pcap_open_offline(filename, errbuf);
|
|
|
|
if (fp)
|
|
{
|
|
/* allocate the main structure */
|
|
dev = (pcap_if_t *)malloc(sizeof(pcap_if_t));
|
|
if (dev == NULL)
|
|
{
|
|
pcap_fmt_errmsg_for_errno(errbuf,
|
|
PCAP_ERRBUF_SIZE, errno,
|
|
"malloc() failed");
|
|
pcap_freealldevs(*alldevs);
|
|
return -1;
|
|
}
|
|
|
|
/* Initialize the structure to 'zero' */
|
|
memset(dev, 0, sizeof(pcap_if_t));
|
|
|
|
/* Append it to the list. */
|
|
if (lastdev == NULL)
|
|
{
|
|
/*
|
|
* List is empty, so it's also
|
|
* the first device.
|
|
*/
|
|
*alldevs = dev;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Append after the last device.
|
|
*/
|
|
lastdev->next = dev;
|
|
}
|
|
/* It's now the last device. */
|
|
lastdev = dev;
|
|
|
|
/* Create the new source identifier */
|
|
if (pcap_createsrcstr(tmpstring, PCAP_SRC_FILE, NULL, NULL, filename, errbuf) == -1)
|
|
{
|
|
pcap_freealldevs(*alldevs);
|
|
return -1;
|
|
}
|
|
|
|
dev->name = strdup(tmpstring);
|
|
if (dev->name == NULL)
|
|
{
|
|
pcap_fmt_errmsg_for_errno(errbuf,
|
|
PCAP_ERRBUF_SIZE, errno,
|
|
"malloc() failed");
|
|
pcap_freealldevs(*alldevs);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Create the description.
|
|
*/
|
|
if (pcap_asprintf(&dev->description,
|
|
"%s '%s' %s", PCAP_TEXT_SOURCE_FILE,
|
|
filename, PCAP_TEXT_SOURCE_ON_LOCAL_HOST) == -1)
|
|
{
|
|
pcap_fmt_errmsg_for_errno(errbuf,
|
|
PCAP_ERRBUF_SIZE, errno,
|
|
"malloc() failed");
|
|
pcap_freealldevs(*alldevs);
|
|
return -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;
|
|
}
|
|
|
|
case PCAP_SRC_IFREMOTE:
|
|
return pcap_findalldevs_ex_remote(source, auth, alldevs, errbuf);
|
|
|
|
default:
|
|
pcap_strlcpy(errbuf, "Source type not supported", PCAP_ERRBUF_SIZE);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf)
|
|
{
|
|
char name[PCAP_BUF_SIZE];
|
|
int type;
|
|
pcap_t *fp;
|
|
int status;
|
|
|
|
/*
|
|
* A null device name is equivalent to the "any" device -
|
|
* which might not be supported on this platform, but
|
|
* this means that you'll get a "not supported" error
|
|
* rather than, say, a crash when we try to dereference
|
|
* the null pointer.
|
|
*/
|
|
if (source == NULL)
|
|
source = "any";
|
|
|
|
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) and,
|
|
* if it's file or local, the name of the file or capture device.
|
|
*/
|
|
if (pcap_parsesrcstr(source, &type, NULL, NULL, name, errbuf) == -1)
|
|
return NULL;
|
|
|
|
switch (type)
|
|
{
|
|
case PCAP_SRC_FILE:
|
|
return pcap_open_offline(name, errbuf);
|
|
|
|
case PCAP_SRC_IFLOCAL:
|
|
fp = pcap_create(name, errbuf);
|
|
break;
|
|
|
|
case PCAP_SRC_IFREMOTE:
|
|
/*
|
|
* Although we already have host, port and iface, we prefer
|
|
* to pass only 'source' to pcap_open_rpcap(), so that it
|
|
* has to call pcap_parsesrcstr() again.
|
|
* This is less optimized, but much clearer.
|
|
*/
|
|
return pcap_open_rpcap(source, snaplen, flags, read_timeout, auth, errbuf);
|
|
|
|
default:
|
|
pcap_strlcpy(errbuf, "Source type not supported", PCAP_ERRBUF_SIZE);
|
|
return NULL;
|
|
}
|
|
|
|
if (fp == NULL)
|
|
return (NULL);
|
|
status = pcap_set_snaplen(fp, snaplen);
|
|
if (status < 0)
|
|
goto fail;
|
|
if (flags & PCAP_OPENFLAG_PROMISCUOUS)
|
|
{
|
|
status = pcap_set_promisc(fp, 1);
|
|
if (status < 0)
|
|
goto fail;
|
|
}
|
|
if (flags & PCAP_OPENFLAG_MAX_RESPONSIVENESS)
|
|
{
|
|
status = pcap_set_immediate_mode(fp, 1);
|
|
if (status < 0)
|
|
goto fail;
|
|
}
|
|
#ifdef _WIN32
|
|
/*
|
|
* This flag is supported on Windows only.
|
|
* XXX - is there a way to support it with
|
|
* the capture mechanisms on UN*X? It's not
|
|
* exactly a "set direction" operation; I
|
|
* think it means "do not capture packets
|
|
* injected with pcap_sendpacket() or
|
|
* pcap_inject()".
|
|
*/
|
|
/* disable loopback capture if requested */
|
|
if (flags & PCAP_OPENFLAG_NOCAPTURE_LOCAL)
|
|
fp->opt.nocapture_local = 1;
|
|
#endif /* _WIN32 */
|
|
status = pcap_set_timeout(fp, read_timeout);
|
|
if (status < 0)
|
|
goto fail;
|
|
status = pcap_activate(fp);
|
|
if (status < 0)
|
|
goto fail;
|
|
return fp;
|
|
|
|
fail:
|
|
if (status == PCAP_ERROR)
|
|
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
|
|
name, fp->errbuf);
|
|
else if (status == PCAP_ERROR_NO_SUCH_DEVICE ||
|
|
status == PCAP_ERROR_PERM_DENIED ||
|
|
status == PCAP_ERROR_PROMISC_PERM_DENIED)
|
|
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%s)",
|
|
name, pcap_statustostr(status), fp->errbuf);
|
|
else
|
|
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
|
|
name, pcap_statustostr(status));
|
|
pcap_close(fp);
|
|
return NULL;
|
|
}
|
|
|
|
struct pcap_samp *pcap_setsampling(pcap_t *p)
|
|
{
|
|
return &p->rmt_samp;
|
|
}
|