1#
2# - Find libpcap
3# Find the native PCAP includes and library
4#
5#  PCAP_INCLUDE_DIRS - where to find pcap.h, etc.
6#  PCAP_LIBRARIES    - List of libraries when using pcap.
7#  PCAP_FOUND        - True if pcap found.
8
9include(FindWSWinLibs)
10FindWSWinLibs("libpcap-*" "PCAP_HINTS")
11
12#
13# First, try pkg-config on platforms other than Windows.
14#
15if(NOT WIN32)
16  find_package(PkgConfig)
17  pkg_search_module(PC_PCAP libpcap)
18endif()
19
20if(NOT PC_PCAP_FOUND AND NOT WIN32)
21  #
22  # That didn't work.  Try to retrieve hints from pcap-config.
23  # Do not use it on Windows as pcap-config is a shell script.
24  #
25  find_program(PCAP_CONFIG pcap-config)
26  if(PCAP_CONFIG)
27    #
28    # We have pcap-config; use it.
29    #
30    # First, get the include directory.
31    #
32    execute_process(COMMAND "${PCAP_CONFIG}" "--cflags"
33      RESULT_VARIABLE PCAP_CONFIG_RESULT
34      OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
35      OUTPUT_STRIP_TRAILING_WHITESPACE
36    )
37    if(NOT PCAP_CONFIG_RESULT EQUAL 0)
38      message(FATAL_ERROR "pcap-config --cflags failed")
39    endif()
40    #
41    # Assumes there's exactly one -I flag in the output
42    # of pcap-config --cflags.  That *should* be the case.
43    # Note that the hint might be bogus, on macOS it could be
44    # -I/usr/local/include even though the header isn't
45    # there (it may be under /usr/include or it may be
46    # buried in the Xcode app bundle).
47    #
48    string(REGEX REPLACE "^-I" "" PCAP_CONFIG_INCLUDE_DIRS "${PCAP_CONFIG_OUTPUT}")
49
50    # Now, get the library search path.
51    execute_process(COMMAND "${PCAP_CONFIG}" "--libs"
52      RESULT_VARIABLE PCAP_CONFIG_RESULT
53      OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
54      OUTPUT_STRIP_TRAILING_WHITESPACE
55    )
56    if(NOT PCAP_CONFIG_RESULT EQUAL 0)
57      message(FATAL_ERROR "pcap-config --libs failed")
58    endif()
59    separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
60    set(PCAP_CONFIG_LIBRARY_DIRS "")
61    foreach(_arg IN LISTS LIBS_LIST)
62      # At most one -L path is expected for -lpcap.
63      if(_arg MATCHES "^-L")
64        string(REGEX REPLACE "^-L" "" _dir ${_arg})
65        list(APPEND PCAP_CONFIG_LIBRARY_DIRS ${_dir})
66      endif()
67    endforeach()
68
69    if(UNIX AND CMAKE_FIND_LIBRARY_SUFFIXES STREQUAL ".a")
70      # Now, get the library directories and libraries for static linking.
71      # (XXX - what about AIX?)
72      execute_process(COMMAND "${PCAP_CONFIG}" "--libs" "--static"
73        RESULT_VARIABLE PCAP_CONFIG_RESULT
74        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
75      )
76      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
77        message(FATAL_ERROR "pcap-config --libs --static failed")
78      endif()
79      separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
80      set(PCAP_CONFIG_STATIC_LIBRARY_DIRS "")
81      set(PCAP_CONFIG_STATIC_LIBRARIES "")
82      foreach(_arg IN LISTS LIBS_LIST)
83        if(_arg MATCHES "^-L")
84          # Add this directory to the library directory hints.
85          string(REGEX REPLACE "^-L" "" _dir ${_arg})
86          list(APPEND PCAP_CONFIG_STATIC_LIBRARY_DIRS ${_dir})
87        elseif(_arg MATCHES "^-l")
88          # Add this library to the requirements for static linking.
89          string(REGEX REPLACE "^-l" "" _lib ${_arg})
90          list(APPEND PCAP_CONFIG_STATIC_LIBRARIES ${_lib})
91        endif()
92      endforeach()
93    endif()
94  endif()
95endif()
96
97#
98# Locate the actual include directory. For pkg-config the
99# PC_PCAP_INCLUDE_DIRS variable could be empty if the default
100# header search path is sufficient to locate the header file.
101# For macOS, the directory returned by pcap-config is wrong, so
102# this will make sure to find a valid path.
103#
104find_path(PCAP_INCLUDE_DIR
105  NAMES
106    pcap/pcap.h
107    pcap.h
108  HINTS
109    ${PC_PCAP_INCLUDE_DIRS}
110    ${PCAP_CONFIG_INCLUDE_DIRS}
111    "${PCAP_HINTS}/Include"
112)
113
114# On Windows we load wpcap.dll explicitly and probe its functions in
115# capture\capture-wpcap.c. We don't want to link with pcap.lib since
116# that would bring in the non-capturing (null) pcap.dll from the vcpkg
117# library.
118if(WIN32)
119  set(_pkg_required_vars PCAP_INCLUDE_DIR)
120else()
121  find_library(PCAP_LIBRARY
122    NAMES
123      pcap
124    HINTS
125      ${PC_PCAP_LIBRARY_DIRS}
126      ${PCAP_CONFIG_LIBRARY_DIRS}
127  )
128  set(_pkg_required_vars PCAP_LIBRARY PCAP_INCLUDE_DIR)
129endif()
130
131if(UNIX AND CMAKE_FIND_LIBRARY_SUFFIXES STREQUAL ".a")
132  # Try to find the static library (XXX - what about AIX?)
133  if(PC_PCAP_FOUND)
134    set(_pcap_static_libraries ${PC_PCAP_STATIC_LIBRARIES})
135  elseif(PCAP_CONFIG)
136    set(_pcap_static_libraries ${PCAP_CONFIG_STATIC_LIBRARIES})
137  else()
138    #
139    # No pkg-config nor pcap-config found, hope that this single library is
140    # sufficient for static linking.
141    #
142    set(_pcap_static_libraries pcap)
143  endif()
144
145  set(PCAP_STATIC_LIBRARIES "")
146  foreach(_lib IN LISTS _pcap_static_libraries)
147    #
148    # Try to find that library, so we get its full path, as
149    # we do with dynamic libraries.
150    #
151    string(MAKE_C_IDENTIFIER "PCAP_STATIC_${_lib}_LIBRARY" _libvar)
152    find_library(${_libvar} ${_lib}
153      HINTS
154      ${PC_PCAP_STATIC_LIBRARY_DIRS}
155      ${PCAP_CONFIG_STATIC_LIBRARY_DIRS}
156    )
157    set(_libpath ${${_libvar}})
158    if(_libpath)
159      list(APPEND PCAP_STATIC_LIBRARIES ${_libpath})
160    endif()
161  endforeach()
162endif()
163
164include(FindPackageHandleStandardArgs)
165find_package_handle_standard_args(PCAP DEFAULT_MSG ${_pkg_required_vars})
166mark_as_advanced(${_pkg_required_vars})
167
168if(PCAP_FOUND)
169  set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR})
170  if(UNIX AND CMAKE_FIND_LIBRARY_SUFFIXES STREQUAL ".a")
171    # Link with static libpcap and its transitive dependencies.
172    set(PCAP_LIBRARIES ${PCAP_STATIC_LIBRARIES})
173  else()
174    set(PCAP_LIBRARIES ${PCAP_LIBRARY})
175  endif()
176
177  #Functions
178  include( CMakePushCheckState )
179  include( CheckFunctionExists )
180  include( CheckVariableExists )
181
182  cmake_push_check_state()
183  set( CMAKE_REQUIRED_INCLUDES ${PCAP_INCLUDE_DIRS} )
184  set( CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARIES} )
185
186  include(CheckSymbolExists)
187
188  if(WIN32)
189    #
190    # Prepopulate some values. WinPcap 3.1 and later, and Npcap, have these
191    # in their SDK, and compilation checks on Windows can be slow.  We check
192    # whether they're present at run time, when we load wpcap.dll, and work
193    # around their absence or report an error.
194    #
195    set(HAVE_PCAP_FREECODE TRUE)
196    set(HAVE_PCAP_CREATE TRUE)
197    set(HAVE_PCAP_FREE_DATALINKS TRUE)
198    set(HAVE_PCAP_OPEN TRUE)
199    set(HAVE_PCAP_SETSAMPLING TRUE)
200    set(HAVE_PCAP_SET_TSTAMP_PRECISION TRUE)
201    set(HAVE_PCAP_SET_TSTAMP_TYPE TRUE)
202  else(WIN32)
203    #
204    # Make sure we have at least libpcap 0.8, because we we require at
205    # least libpcap 0.8's APIs.
206    #
207    # We check whether pcap_lib_version is defined in the pcap header,
208    # using it as a proxy for all the 0.8 API's.  if not, we fail.
209    #
210    check_symbol_exists( pcap_lib_version ${PCAP_INCLUDE_DIR}/pcap.h HAVE_PCAP_LIB_VERSION )
211    if( NOT HAVE_PCAP_LIB_VERSION )
212      message(FATAL_ERROR "You need libpcap 0.8 or later")
213    endif( NOT HAVE_PCAP_LIB_VERSION )
214
215    check_function_exists( "pcap_freecode" HAVE_PCAP_FREECODE )
216    check_function_exists( "pcap_create" HAVE_PCAP_CREATE )
217    check_function_exists( "pcap_free_datalinks" HAVE_PCAP_FREE_DATALINKS )
218    check_function_exists( "pcap_open" HAVE_PCAP_OPEN )
219    if( HAVE_PCAP_OPEN )
220      #
221      # XXX - this *should* be checked for independently of checking
222      # for pcap_open(), as you might have pcap_setsampling() without
223      # remote capture support.
224      #
225      # However, 1) the sampling options are treated as remote options
226      # in the GUI and and 2) having pcap_setsampling() doesn't mean
227      # you have sampling support.  libpcap needs a way to indicate
228      # whether a given device supports sampling, and the GUI should
229      # be changed to decouple them.
230      #
231      # (Actually, libpcap needs a general mechanism to offer options
232      # for particular devices, and Wireshark needs to use that
233      # mechanism.  The former is a work in progress.)
234      #
235      # (Note: another work in progress is support for remote
236      # capturing using pcap_create()/pcap_activate(), which we
237      # also need to support once it's available.)
238      #
239      check_function_exists( "pcap_setsampling" HAVE_PCAP_SETSAMPLING )
240    endif( HAVE_PCAP_OPEN )
241  endif(WIN32)
242
243  if( HAVE_PCAP_CREATE )
244    #
245    # If we have pcap_create(), we have pcap_set_buffer_size(), and
246    # can set the capture buffer size.
247    #
248    # Otherwise, if this is Windows, we have pcap_setbuff(), and can
249    # set the capture buffer size.
250    #
251    set( CAN_SET_CAPTURE_BUFFER_SIZE TRUE )
252  endif()
253  check_function_exists( "pcap_set_tstamp_precision" HAVE_PCAP_SET_TSTAMP_PRECISION )
254  check_function_exists( "pcap_set_tstamp_type" HAVE_PCAP_SET_TSTAMP_TYPE )
255  # Remote pcap checks
256  if( HAVE_PCAP_OPEN )
257    set( HAVE_PCAP_REMOTE 1 )
258  endif()
259
260  cmake_pop_check_state()
261endif()
262
263if(PCAP_FOUND AND NOT TARGET pcap::pcap)
264  if(WIN32)
265    add_library(pcap::pcap INTERFACE IMPORTED)
266    set_target_properties(pcap::pcap PROPERTIES
267      INTERFACE_INCLUDE_DIRECTORIES "${PCAP_INCLUDE_DIRS}"
268    )
269  else()
270    add_library(pcap::pcap UNKNOWN IMPORTED)
271    set_target_properties(pcap::pcap PROPERTIES
272      IMPORTED_LOCATION "${PCAP_LIBRARIES}"
273      INTERFACE_INCLUDE_DIRECTORIES "${PCAP_INCLUDE_DIRS}"
274    )
275  endif()
276endif()
277