1335640Shselasky/* 2335640Shselasky * Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy) 3335640Shselasky * Copyright (c) 2005 - 2008 CACE Technologies, Davis (California) 4335640Shselasky * All rights reserved. 5335640Shselasky * 6335640Shselasky * Redistribution and use in source and binary forms, with or without 7335640Shselasky * modification, are permitted provided that the following conditions 8335640Shselasky * are met: 9335640Shselasky * 10335640Shselasky * 1. Redistributions of source code must retain the above copyright 11335640Shselasky * notice, this list of conditions and the following disclaimer. 12335640Shselasky * 2. Redistributions in binary form must reproduce the above copyright 13335640Shselasky * notice, this list of conditions and the following disclaimer in the 14335640Shselasky * documentation and/or other materials provided with the distribution. 15335640Shselasky * 3. Neither the name of the Politecnico di Torino, CACE Technologies 16335640Shselasky * nor the names of its contributors may be used to endorse or promote 17335640Shselasky * products derived from this software without specific prior written 18335640Shselasky * permission. 19335640Shselasky * 20335640Shselasky * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21335640Shselasky * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22335640Shselasky * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23335640Shselasky * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24335640Shselasky * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25335640Shselasky * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26335640Shselasky * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27335640Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28335640Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29335640Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30335640Shselasky * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31335640Shselasky * 32335640Shselasky */ 33335640Shselasky 34335640Shselasky#ifdef HAVE_CONFIG_H 35335640Shselasky#include <config.h> 36335640Shselasky#endif 37335640Shselasky 38356341Scy#include "ftmacros.h" 39356341Scy 40335640Shselasky/* 41335640Shselasky * sockutils.h may include <crtdbg.h> on Windows, and pcap-int.h will 42335640Shselasky * include portability.h, and portability.h, on Windows, expects that 43335640Shselasky * <crtdbg.h> has already been included, so include sockutils.h first. 44335640Shselasky */ 45335640Shselasky#include "sockutils.h" 46335640Shselasky#include "pcap-int.h" // for the details of the pcap_t structure 47335640Shselasky#include "pcap-rpcap.h" 48335640Shselasky#include "rpcap-protocol.h" 49335640Shselasky#include <errno.h> // for the errno variable 50335640Shselasky#include <stdlib.h> // for malloc(), free(), ... 51335640Shselasky#include <string.h> // for strstr, etc 52335640Shselasky 53335640Shselasky#ifndef _WIN32 54335640Shselasky#include <dirent.h> // for readdir 55335640Shselasky#endif 56335640Shselasky 57335640Shselasky/* String identifier to be used in the pcap_findalldevs_ex() */ 58335640Shselasky#define PCAP_TEXT_SOURCE_FILE "File" 59356341Scy#define PCAP_TEXT_SOURCE_FILE_LEN (sizeof PCAP_TEXT_SOURCE_FILE - 1) 60335640Shselasky/* String identifier to be used in the pcap_findalldevs_ex() */ 61335640Shselasky#define PCAP_TEXT_SOURCE_ADAPTER "Network adapter" 62356341Scy#define PCAP_TEXT_SOURCE_ADAPTER_LEN (sizeof "Network adapter" - 1) 63335640Shselasky 64335640Shselasky/* String identifier to be used in the pcap_findalldevs_ex() */ 65335640Shselasky#define PCAP_TEXT_SOURCE_ON_LOCAL_HOST "on local host" 66356341Scy#define PCAP_TEXT_SOURCE_ON_LOCAL_HOST_LEN (sizeof PCAP_TEXT_SOURCE_ON_LOCAL_HOST + 1) 67335640Shselasky 68335640Shselasky/**************************************************** 69335640Shselasky * * 70335640Shselasky * Function bodies * 71335640Shselasky * * 72335640Shselasky ****************************************************/ 73335640Shselasky 74356341Scyint pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf) 75335640Shselasky{ 76335640Shselasky int type; 77335640Shselasky char name[PCAP_BUF_SIZE], path[PCAP_BUF_SIZE], filename[PCAP_BUF_SIZE]; 78356341Scy size_t pathlen; 79356341Scy size_t stringlen; 80335640Shselasky pcap_t *fp; 81335640Shselasky char tmpstring[PCAP_BUF_SIZE + 1]; /* Needed to convert names and descriptions from 'old' syntax to the 'new' one */ 82335640Shselasky pcap_if_t *lastdev; /* Last device in the pcap_if_t list */ 83335640Shselasky pcap_if_t *dev; /* Device we're adding to the pcap_if_t list */ 84335640Shselasky 85335640Shselasky /* List starts out empty. */ 86335640Shselasky (*alldevs) = NULL; 87335640Shselasky lastdev = NULL; 88335640Shselasky 89335640Shselasky if (strlen(source) > PCAP_BUF_SIZE) 90335640Shselasky { 91335640Shselasky pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); 92335640Shselasky return -1; 93335640Shselasky } 94335640Shselasky 95335640Shselasky /* 96335640Shselasky * Determine the type of the source (file, local, remote) 97335640Shselasky * There are some differences if pcap_findalldevs_ex() is called to list files and remote adapters. 98335640Shselasky * In the first case, the name of the directory we have to look into must be present (therefore 99335640Shselasky * the 'name' parameter of the pcap_parsesrcstr() is present). 100335640Shselasky * In the second case, the name of the adapter is not required (we need just the host). So, we have 101335640Shselasky * to use a first time this function to get the source type, and a second time to get the appropriate 102335640Shselasky * info, which depends on the source type. 103335640Shselasky */ 104335640Shselasky if (pcap_parsesrcstr(source, &type, NULL, NULL, NULL, errbuf) == -1) 105335640Shselasky return -1; 106335640Shselasky 107335640Shselasky switch (type) 108335640Shselasky { 109335640Shselasky case PCAP_SRC_IFLOCAL: 110335640Shselasky if (pcap_parsesrcstr(source, &type, NULL, NULL, NULL, errbuf) == -1) 111335640Shselasky return -1; 112335640Shselasky 113335640Shselasky /* Initialize temporary string */ 114335640Shselasky tmpstring[PCAP_BUF_SIZE] = 0; 115335640Shselasky 116335640Shselasky /* The user wants to retrieve adapters from a local host */ 117335640Shselasky if (pcap_findalldevs(alldevs, errbuf) == -1) 118335640Shselasky return -1; 119335640Shselasky 120335640Shselasky if (*alldevs == NULL) 121335640Shselasky { 122335640Shselasky pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, 123356341Scy "No interfaces found! Make sure libpcap/Npcap is properly installed" 124335640Shselasky " on the local machine."); 125335640Shselasky return -1; 126335640Shselasky } 127335640Shselasky 128335640Shselasky /* Scan all the interfaces and modify name and description */ 129335640Shselasky /* This is a trick in order to avoid the re-implementation of the pcap_findalldevs here */ 130335640Shselasky dev = *alldevs; 131335640Shselasky while (dev) 132335640Shselasky { 133356341Scy char *localdesc, *desc; 134356341Scy 135335640Shselasky /* Create the new device identifier */ 136335640Shselasky if (pcap_createsrcstr(tmpstring, PCAP_SRC_IFLOCAL, NULL, NULL, dev->name, errbuf) == -1) 137335640Shselasky return -1; 138335640Shselasky 139335640Shselasky /* Delete the old pointer */ 140335640Shselasky free(dev->name); 141335640Shselasky 142335640Shselasky /* Make a copy of the new device identifier */ 143335640Shselasky dev->name = strdup(tmpstring); 144335640Shselasky if (dev->name == NULL) 145335640Shselasky { 146335640Shselasky pcap_fmt_errmsg_for_errno(errbuf, 147335640Shselasky PCAP_ERRBUF_SIZE, errno, 148335640Shselasky "malloc() failed"); 149335640Shselasky pcap_freealldevs(*alldevs); 150335640Shselasky return -1; 151335640Shselasky } 152335640Shselasky 153356341Scy /* 154356341Scy * Create the description. 155356341Scy */ 156335640Shselasky if ((dev->description == NULL) || (dev->description[0] == 0)) 157356341Scy localdesc = dev->name; 158335640Shselasky else 159356341Scy localdesc = dev->description; 160356341Scy if (pcap_asprintf(&desc, "%s '%s' %s", 161356341Scy PCAP_TEXT_SOURCE_ADAPTER, localdesc, 162356341Scy PCAP_TEXT_SOURCE_ON_LOCAL_HOST) == -1) 163335640Shselasky { 164335640Shselasky pcap_fmt_errmsg_for_errno(errbuf, 165335640Shselasky PCAP_ERRBUF_SIZE, errno, 166335640Shselasky "malloc() failed"); 167335640Shselasky pcap_freealldevs(*alldevs); 168335640Shselasky return -1; 169335640Shselasky } 170335640Shselasky 171356341Scy /* Now overwrite the description */ 172356341Scy free(dev->description); 173356341Scy dev->description = desc; 174356341Scy 175335640Shselasky dev = dev->next; 176335640Shselasky } 177335640Shselasky 178335640Shselasky return 0; 179335640Shselasky 180335640Shselasky case PCAP_SRC_FILE: 181335640Shselasky { 182335640Shselasky#ifdef _WIN32 183335640Shselasky WIN32_FIND_DATA filedata; 184335640Shselasky HANDLE filehandle; 185335640Shselasky#else 186335640Shselasky struct dirent *filedata; 187335640Shselasky DIR *unixdir; 188335640Shselasky#endif 189335640Shselasky 190335640Shselasky if (pcap_parsesrcstr(source, &type, NULL, NULL, name, errbuf) == -1) 191335640Shselasky return -1; 192335640Shselasky 193335640Shselasky /* Check that the filename is correct */ 194335640Shselasky stringlen = strlen(name); 195335640Shselasky 196335640Shselasky /* The directory must end with '\' in Win32 and '/' in UNIX */ 197335640Shselasky#ifdef _WIN32 198335640Shselasky#define ENDING_CHAR '\\' 199335640Shselasky#else 200335640Shselasky#define ENDING_CHAR '/' 201335640Shselasky#endif 202335640Shselasky 203335640Shselasky if (name[stringlen - 1] != ENDING_CHAR) 204335640Shselasky { 205335640Shselasky name[stringlen] = ENDING_CHAR; 206335640Shselasky name[stringlen + 1] = 0; 207335640Shselasky 208335640Shselasky stringlen++; 209335640Shselasky } 210335640Shselasky 211335640Shselasky /* Save the path for future reference */ 212335640Shselasky pcap_snprintf(path, sizeof(path), "%s", name); 213356341Scy pathlen = strlen(path); 214335640Shselasky 215335640Shselasky#ifdef _WIN32 216335640Shselasky /* To perform directory listing, Win32 must have an 'asterisk' as ending char */ 217335640Shselasky if (name[stringlen - 1] != '*') 218335640Shselasky { 219335640Shselasky name[stringlen] = '*'; 220335640Shselasky name[stringlen + 1] = 0; 221335640Shselasky } 222335640Shselasky 223335640Shselasky filehandle = FindFirstFile(name, &filedata); 224335640Shselasky 225335640Shselasky if (filehandle == INVALID_HANDLE_VALUE) 226335640Shselasky { 227335640Shselasky pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); 228335640Shselasky return -1; 229335640Shselasky } 230335640Shselasky 231335640Shselasky#else 232335640Shselasky /* opening the folder */ 233335640Shselasky unixdir= opendir(path); 234335640Shselasky 235335640Shselasky /* get the first file into it */ 236335640Shselasky filedata= readdir(unixdir); 237335640Shselasky 238335640Shselasky if (filedata == NULL) 239335640Shselasky { 240335640Shselasky pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); 241335640Shselasky return -1; 242335640Shselasky } 243335640Shselasky#endif 244335640Shselasky 245335640Shselasky /* Add all files we find to the list. */ 246335640Shselasky do 247335640Shselasky { 248335640Shselasky#ifdef _WIN32 249356341Scy /* Skip the file if the pathname won't fit in the buffer */ 250356341Scy if (pathlen + strlen(filedata.cFileName) >= sizeof(filename)) 251356341Scy continue; 252335640Shselasky pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata.cFileName); 253335640Shselasky#else 254356341Scy if (pathlen + strlen(filedata->d_name) >= sizeof(filename)) 255356341Scy continue; 256335640Shselasky pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata->d_name); 257335640Shselasky#endif 258335640Shselasky 259335640Shselasky fp = pcap_open_offline(filename, errbuf); 260335640Shselasky 261335640Shselasky if (fp) 262335640Shselasky { 263335640Shselasky /* allocate the main structure */ 264335640Shselasky dev = (pcap_if_t *)malloc(sizeof(pcap_if_t)); 265335640Shselasky if (dev == NULL) 266335640Shselasky { 267335640Shselasky pcap_fmt_errmsg_for_errno(errbuf, 268335640Shselasky PCAP_ERRBUF_SIZE, errno, 269335640Shselasky "malloc() failed"); 270335640Shselasky pcap_freealldevs(*alldevs); 271335640Shselasky return -1; 272335640Shselasky } 273335640Shselasky 274335640Shselasky /* Initialize the structure to 'zero' */ 275335640Shselasky memset(dev, 0, sizeof(pcap_if_t)); 276335640Shselasky 277335640Shselasky /* Append it to the list. */ 278335640Shselasky if (lastdev == NULL) 279335640Shselasky { 280335640Shselasky /* 281335640Shselasky * List is empty, so it's also 282335640Shselasky * the first device. 283335640Shselasky */ 284335640Shselasky *alldevs = dev; 285335640Shselasky } 286335640Shselasky else 287335640Shselasky { 288335640Shselasky /* 289335640Shselasky * Append after the last device. 290335640Shselasky */ 291335640Shselasky lastdev->next = dev; 292335640Shselasky } 293335640Shselasky /* It's now the last device. */ 294335640Shselasky lastdev = dev; 295335640Shselasky 296335640Shselasky /* Create the new source identifier */ 297335640Shselasky if (pcap_createsrcstr(tmpstring, PCAP_SRC_FILE, NULL, NULL, filename, errbuf) == -1) 298335640Shselasky { 299335640Shselasky pcap_freealldevs(*alldevs); 300335640Shselasky return -1; 301335640Shselasky } 302335640Shselasky 303356341Scy dev->name = strdup(tmpstring); 304335640Shselasky if (dev->name == NULL) 305335640Shselasky { 306335640Shselasky pcap_fmt_errmsg_for_errno(errbuf, 307335640Shselasky PCAP_ERRBUF_SIZE, errno, 308335640Shselasky "malloc() failed"); 309335640Shselasky pcap_freealldevs(*alldevs); 310335640Shselasky return -1; 311335640Shselasky } 312335640Shselasky 313356341Scy /* 314356341Scy * Create the description. 315356341Scy */ 316356341Scy if (pcap_asprintf(&dev->description, 317356341Scy "%s '%s' %s", PCAP_TEXT_SOURCE_FILE, 318356341Scy filename, PCAP_TEXT_SOURCE_ON_LOCAL_HOST) == -1) 319335640Shselasky { 320335640Shselasky pcap_fmt_errmsg_for_errno(errbuf, 321335640Shselasky PCAP_ERRBUF_SIZE, errno, 322335640Shselasky "malloc() failed"); 323335640Shselasky pcap_freealldevs(*alldevs); 324335640Shselasky return -1; 325335640Shselasky } 326335640Shselasky 327335640Shselasky pcap_close(fp); 328335640Shselasky } 329335640Shselasky } 330335640Shselasky#ifdef _WIN32 331335640Shselasky while (FindNextFile(filehandle, &filedata) != 0); 332335640Shselasky#else 333335640Shselasky while ( (filedata= readdir(unixdir)) != NULL); 334335640Shselasky#endif 335335640Shselasky 336335640Shselasky 337335640Shselasky#ifdef _WIN32 338335640Shselasky /* Close the search handle. */ 339335640Shselasky FindClose(filehandle); 340335640Shselasky#endif 341335640Shselasky 342335640Shselasky return 0; 343335640Shselasky } 344335640Shselasky 345335640Shselasky case PCAP_SRC_IFREMOTE: 346335640Shselasky return pcap_findalldevs_ex_remote(source, auth, alldevs, errbuf); 347335640Shselasky 348335640Shselasky default: 349356341Scy pcap_strlcpy(errbuf, "Source type not supported", PCAP_ERRBUF_SIZE); 350335640Shselasky return -1; 351335640Shselasky } 352335640Shselasky} 353335640Shselasky 354335640Shselaskypcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf) 355335640Shselasky{ 356335640Shselasky char name[PCAP_BUF_SIZE]; 357335640Shselasky int type; 358335640Shselasky pcap_t *fp; 359335640Shselasky int status; 360335640Shselasky 361356341Scy /* 362356341Scy * A null device name is equivalent to the "any" device - 363356341Scy * which might not be supported on this platform, but 364356341Scy * this means that you'll get a "not supported" error 365356341Scy * rather than, say, a crash when we try to dereference 366356341Scy * the null pointer. 367356341Scy */ 368356341Scy if (source == NULL) 369356341Scy source = "any"; 370356341Scy 371335640Shselasky if (strlen(source) > PCAP_BUF_SIZE) 372335640Shselasky { 373335640Shselasky pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); 374335640Shselasky return NULL; 375335640Shselasky } 376335640Shselasky 377335640Shselasky /* 378335640Shselasky * Determine the type of the source (file, local, remote) and, 379335640Shselasky * if it's file or local, the name of the file or capture device. 380335640Shselasky */ 381335640Shselasky if (pcap_parsesrcstr(source, &type, NULL, NULL, name, errbuf) == -1) 382335640Shselasky return NULL; 383335640Shselasky 384335640Shselasky switch (type) 385335640Shselasky { 386335640Shselasky case PCAP_SRC_FILE: 387335640Shselasky return pcap_open_offline(name, errbuf); 388335640Shselasky 389335640Shselasky case PCAP_SRC_IFLOCAL: 390335640Shselasky fp = pcap_create(name, errbuf); 391335640Shselasky break; 392335640Shselasky 393335640Shselasky case PCAP_SRC_IFREMOTE: 394335640Shselasky /* 395335640Shselasky * Although we already have host, port and iface, we prefer 396335640Shselasky * to pass only 'source' to pcap_open_rpcap(), so that it 397335640Shselasky * has to call pcap_parsesrcstr() again. 398335640Shselasky * This is less optimized, but much clearer. 399335640Shselasky */ 400335640Shselasky return pcap_open_rpcap(source, snaplen, flags, read_timeout, auth, errbuf); 401335640Shselasky 402335640Shselasky default: 403356341Scy pcap_strlcpy(errbuf, "Source type not supported", PCAP_ERRBUF_SIZE); 404335640Shselasky return NULL; 405335640Shselasky } 406335640Shselasky 407335640Shselasky if (fp == NULL) 408335640Shselasky return (NULL); 409335640Shselasky status = pcap_set_snaplen(fp, snaplen); 410335640Shselasky if (status < 0) 411335640Shselasky goto fail; 412335640Shselasky if (flags & PCAP_OPENFLAG_PROMISCUOUS) 413335640Shselasky { 414335640Shselasky status = pcap_set_promisc(fp, 1); 415335640Shselasky if (status < 0) 416335640Shselasky goto fail; 417335640Shselasky } 418335640Shselasky if (flags & PCAP_OPENFLAG_MAX_RESPONSIVENESS) 419335640Shselasky { 420335640Shselasky status = pcap_set_immediate_mode(fp, 1); 421335640Shselasky if (status < 0) 422335640Shselasky goto fail; 423335640Shselasky } 424335640Shselasky#ifdef _WIN32 425335640Shselasky /* 426335640Shselasky * This flag is supported on Windows only. 427335640Shselasky * XXX - is there a way to support it with 428335640Shselasky * the capture mechanisms on UN*X? It's not 429335640Shselasky * exactly a "set direction" operation; I 430335640Shselasky * think it means "do not capture packets 431335640Shselasky * injected with pcap_sendpacket() or 432335640Shselasky * pcap_inject()". 433335640Shselasky */ 434335640Shselasky /* disable loopback capture if requested */ 435335640Shselasky if (flags & PCAP_OPENFLAG_NOCAPTURE_LOCAL) 436335640Shselasky fp->opt.nocapture_local = 1; 437335640Shselasky#endif /* _WIN32 */ 438335640Shselasky status = pcap_set_timeout(fp, read_timeout); 439335640Shselasky if (status < 0) 440335640Shselasky goto fail; 441335640Shselasky status = pcap_activate(fp); 442335640Shselasky if (status < 0) 443335640Shselasky goto fail; 444335640Shselasky return fp; 445335640Shselasky 446335640Shselaskyfail: 447335640Shselasky if (status == PCAP_ERROR) 448335640Shselasky pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 449335640Shselasky name, fp->errbuf); 450335640Shselasky else if (status == PCAP_ERROR_NO_SUCH_DEVICE || 451335640Shselasky status == PCAP_ERROR_PERM_DENIED || 452335640Shselasky status == PCAP_ERROR_PROMISC_PERM_DENIED) 453335640Shselasky pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%s)", 454335640Shselasky name, pcap_statustostr(status), fp->errbuf); 455335640Shselasky else 456335640Shselasky pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 457335640Shselasky name, pcap_statustostr(status)); 458335640Shselasky pcap_close(fp); 459335640Shselasky return NULL; 460335640Shselasky} 461335640Shselasky 462335640Shselaskystruct pcap_samp *pcap_setsampling(pcap_t *p) 463335640Shselasky{ 464335640Shselasky return &p->rmt_samp; 465335640Shselasky} 466