1#
2# Try to find libpcap.
3#
4# To tell this module where to look, a user may set the environment variable
5# PCAP_ROOT to point cmake to the *root* of a directory with include and
6# lib subdirectories for pcap.dll (e.g WpdPack or npcap-sdk).
7# Alternatively, PCAP_ROOT may also be set from cmake command line or GUI
8# (e.g cmake -DPCAP_ROOT=C:\path\to\pcap [...])
9#
10
11if(WIN32)
12  #
13  # Building for Windows.
14  #
15  # libpcap isn't set up to install .pc files or pcap-config on Windows,
16  # and it's not clear that either of them would work without a lot
17  # of additional effort.  WinPcap doesn't supply them, and neither
18  # does Npcap.
19  #
20  # So just search for them directly.  Look for both pcap and wpcap.
21  # Don't bother looking for static libraries; unlike most UN*Xes
22  # (with the exception of AIX), where different extensions are used
23  # for shared and static, Windows uses .lib both for import libraries
24  # for DLLs and for static libraries.
25  #
26  # We don't directly set PCAP_INCLUDE_DIRS or PCAP_LIBRARIES, as
27  # they're not supposed to be cache entries, and find_path() and
28  # find_library() set cache entries.
29  #
30  find_path(PCAP_INCLUDE_DIR pcap.h)
31
32  # The 64-bit Packet.lib is located under /x64
33  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
34    #
35    # For the WinPcap and Npcap SDKs, the Lib subdirectory of the top-level
36    # directory contains 32-bit libraries; the 64-bit libraries are in the
37    # Lib/x64 directory.
38    #
39    # The only way to *FORCE* CMake to look in the Lib/x64 directory
40    # without searching in the Lib directory first appears to be to set
41    # CMAKE_LIBRARY_ARCHITECTURE to "x64".
42    #
43    set(CMAKE_LIBRARY_ARCHITECTURE "x64")
44  endif()
45  find_library(PCAP_LIBRARY NAMES pcap wpcap)
46
47  #
48  # Do the standard arg processing, including failing if it's a
49  # required package.
50  #
51  include(FindPackageHandleStandardArgs)
52  find_package_handle_standard_args(PCAP
53    DEFAULT_MSG
54    PCAP_INCLUDE_DIR
55    PCAP_LIBRARY
56  )
57  mark_as_advanced(
58    PCAP_INCLUDE_DIR
59    PCAP_LIBRARY
60  )
61  if(PCAP_FOUND)
62    set(PCAP_LIBRARIES ${PCAP_LIBRARY})
63    set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR})
64  endif()
65else(WIN32)
66  #
67  # Building for UN*X.
68  #
69  # See whether we were handed a QUIET argument, so we can pass it on
70  # to pkg_search_module.  Do *NOT* pass on the REQUIRED argument,
71  # because, if pkg-config isn't found, or it is but it has no .pc
72  # files for libpcap, that is *not* necessarily an indication that
73  # libpcap isn't available - not all systems ship pkg-config, and
74  # libpcap didn't have .pc files until libpcap 1.9.0.
75  #
76  if(PCAP_FIND_QUIETLY)
77    set(_quiet "QUIET")
78  endif()
79
80  #
81  # First, try pkg-config.
82  # Before doing so, set the PKG_CONFIG_PATH environment variable
83  # to include all the directories in CMAKE_PREFIX_PATH.
84  #
85  # *If* we were to require CMake 3.1 or later on UN*X,
86  # pkg_search_module() would do this for us, but, for now,
87  # we're not doing that, in case somebody's building with
88  # CMake on some "long-term support" version, predating
89  # CMake 3.1, of an OS that supplies an earlier
90  # version as a package.
91  #
92  # If we ever set a minimum of 3.1 or later on UN*X, we should
93  # remove the environment variable changes.
94  #
95  # This is based on code in the CMake 3.12.4 FindPkgConfig.cmake,
96  # which is "Distributed under the OSI-approved BSD 3-Clause License."
97  #
98  find_package(PkgConfig)
99
100  #
101  # Get the current PKG_CONFIG_PATH setting.
102  #
103  set(_pkg_config_path "$ENV{PKG_CONFIG_PATH}")
104
105  #
106  # Save it, so we can restore it after we run pkg-config.
107  #
108  set(_saved_pkg_config_path "${_pkg_config_path}")
109
110  if(NOT "${CMAKE_PREFIX_PATH}" STREQUAL "")
111    #
112    # Convert it to a CMake-style path, before we add additional
113    # values to it.
114    #
115    if(NOT "${_pkg_config_path}" STREQUAL "")
116      file(TO_CMAKE_PATH "${_pkg_config_path}" _pkg_config_path)
117    endif()
118
119    #
120    # Turn CMAKE_PREFIX_PATH into a list of extra paths to add
121    # to _pkg_config_path.
122    #
123    set(_extra_paths "")
124    list(APPEND _extra_paths ${CMAKE_PREFIX_PATH})
125
126    # Create a list of the possible pkgconfig subfolder (depending on
127    # the system
128    set(_lib_dirs)
129    if(NOT DEFINED CMAKE_SYSTEM_NAME
130        OR (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU)$"
131            AND NOT CMAKE_CROSSCOMPILING))
132      if(EXISTS "/etc/debian_version") # is this a debian system ?
133        if(CMAKE_LIBRARY_ARCHITECTURE)
134          list(APPEND _lib_dirs "lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig")
135        endif()
136      else()
137        # not debian, check the FIND_LIBRARY_USE_LIB32_PATHS and FIND_LIBRARY_USE_LIB64_PATHS properties
138        get_property(uselib32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS)
139        if(uselib32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
140          list(APPEND _lib_dirs "lib32/pkgconfig")
141        endif()
142        get_property(uselib64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS)
143        if(uselib64 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
144          list(APPEND _lib_dirs "lib64/pkgconfig")
145        endif()
146        get_property(uselibx32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIBX32_PATHS)
147        if(uselibx32 AND CMAKE_INTERNAL_PLATFORM_ABI STREQUAL "ELF X32")
148          list(APPEND _lib_dirs "libx32/pkgconfig")
149        endif()
150      endif()
151    endif()
152    if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND NOT CMAKE_CROSSCOMPILING)
153      list(APPEND _lib_dirs "libdata/pkgconfig")
154    endif()
155    list(APPEND _lib_dirs "lib/pkgconfig")
156    list(APPEND _lib_dirs "share/pkgconfig")
157
158    # Check if directories exist and eventually append them to the
159    # pkgconfig path list
160    foreach(_prefix_dir ${_extra_paths})
161      foreach(_lib_dir ${_lib_dirs})
162        if(EXISTS "${_prefix_dir}/${_lib_dir}")
163          list(APPEND _pkg_config_path "${_prefix_dir}/${_lib_dir}")
164          list(REMOVE_DUPLICATES _pkg_config_path)
165        endif()
166      endforeach()
167    endforeach()
168
169    if(NOT "${_pkg_config_path}" STREQUAL "")
170      # remove empty values from the list
171      list(REMOVE_ITEM _pkg_config_path "")
172      file(TO_NATIVE_PATH "${_pkg_config_path}" _pkg_config_path)
173      if(UNIX)
174        string(REPLACE ";" ":" _pkg_config_path "${_pkg_config_path}")
175        string(REPLACE "\\ " " " _pkg_config_path "${_pkg_config_path}")
176      endif()
177      set(ENV{PKG_CONFIG_PATH} "${_pkg_config_path}")
178    endif()
179  endif()
180  pkg_search_module(CONFIG_PCAP ${_quiet} libpcap)
181  set(ENV{PKG_CONFIG_PATH} "${_saved_pkg_config_path}")
182
183  if(NOT CONFIG_PCAP_FOUND)
184    #
185    # That didn't work.  Try pcap-config.
186    #
187    find_program(PCAP_CONFIG pcap-config)
188    if(PCAP_CONFIG)
189      #
190      # We have pcap-config; use it.
191      #
192      if(NOT "${_quiet}" STREQUAL "QUIET")
193        message(STATUS "Found pcap-config")
194      endif()
195
196      #
197      # If this is a vendor-supplied pcap-config, which we define as
198      # being "a pcap-config in /usr/bin or /usr/ccs/bin" (the latter
199      # is for Solaris and Sun/Oracle Studio), there are some issues.
200      # Work around them.
201      #
202      if("${PCAP_CONFIG}" STREQUAL /usr/bin/pcap-config OR
203         "${PCAP_CONFIG}" STREQUAL /usr/ccs/bin/pcap-config)
204        #
205        # It's vendor-supplied.
206        #
207        if(APPLE)
208          #
209          # This is macOS or another Darwin-based OS.
210          #
211          # That means that /usr/bin/pcap-config it may provide
212          # -I/usr/local/include with --cflags and -L/usr/local/lib
213          # with --libs; if there's no pcap installed under /usr/local,
214          # that will cause the build to fail, and if there is a pcap
215          # installed there, you'll get that pcap even if you don't
216          # want it.  Remember that, so we ignore those values.
217          #
218          set(_broken_apple_pcap_config TRUE)
219        elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND CMAKE_SYSTEM_VERSION MATCHES "5[.][0-9.]*")
220          #
221          # This is Solaris 2 or later, i.e. SunOS 5.x.
222          #
223          # At least on Solaris 11; there's /usr/bin/pcap-config, which
224          # reports -L/usr/lib with --libs, causing the 32-bit libraries
225          # to be found, and there's /usr/bin/{64bitarch}/pcap-config,
226          # where {64bitarch} is a name for the 64-bit version of the
227          # instruction set, which reports -L /usr/lib/{64bitarch},
228          # causing the 64-bit libraries to be found.
229          #
230          # So if we're building 64-bit targets, we replace PCAP_CONFIG
231          # with /usr/bin/{64bitarch}; we get {64bitarch} as the
232          # output of "isainfo -n".
233          #
234          if(CMAKE_SIZEOF_VOID_P EQUAL 8)
235            execute_process(COMMAND "isainfo" "-n"
236              RESULT_VARIABLE ISAINFO_RESULT
237              OUTPUT_VARIABLE ISAINFO_OUTPUT
238              OUTPUT_STRIP_TRAILING_WHITESPACE
239            )
240            if(ISAINFO_RESULT EQUAL 0)
241              #
242              # Success - change PCAP_CONFIG.
243              #
244              string(REPLACE "/bin/" "/bin/${ISAINFO_OUTPUT}/" PCAP_CONFIG "${PCAP_CONFIG}")
245            endif()
246          endif()
247        endif()
248      endif()
249
250      #
251      # Now get the include directories.
252      #
253      execute_process(COMMAND "${PCAP_CONFIG}" "--cflags"
254        RESULT_VARIABLE PCAP_CONFIG_RESULT
255        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
256        OUTPUT_STRIP_TRAILING_WHITESPACE
257      )
258      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
259        message(FATAL_ERROR "pcap-config --cflags failed")
260      endif()
261      separate_arguments(CFLAGS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
262      set(CONFIG_PCAP_INCLUDE_DIRS "")
263      foreach(_arg IN LISTS CFLAGS_LIST)
264        if(_arg MATCHES "^-I")
265          #
266          # Extract the directory by removing the -I.
267          #
268          string(REGEX REPLACE "-I" "" _dir ${_arg})
269          #
270          # Work around macOS (and probably other Darwin) brokenness,
271          # by not adding /usr/local/include if it's from the broken
272          # Apple pcap-config.
273          #
274          if(NOT _broken_apple_pcap_config OR
275             NOT "${_dir}" STREQUAL /usr/local/include)
276            # Add it to CONFIG_PCAP_INCLUDE_DIRS
277            list(APPEND CONFIG_PCAP_INCLUDE_DIRS ${_dir})
278          endif()
279        endif()
280      endforeach()
281
282      #
283      # Now, get the library directories and libraries for dynamic linking.
284      #
285      execute_process(COMMAND "${PCAP_CONFIG}" "--libs"
286        RESULT_VARIABLE PCAP_CONFIG_RESULT
287        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
288        OUTPUT_STRIP_TRAILING_WHITESPACE
289      )
290      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
291        message(FATAL_ERROR "pcap-config --libs failed")
292      endif()
293      separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
294      set(CONFIG_PCAP_LIBRARY_DIRS "")
295      set(CONFIG_PCAP_LIBRARIES "")
296      foreach(_arg IN LISTS LIBS_LIST)
297        if(_arg MATCHES "^-L")
298          #
299          # Extract the directory by removing the -L.
300          #
301          string(REGEX REPLACE "-L" "" _dir ${_arg})
302          #
303          # Work around macOS (and probably other Darwin) brokenness,
304          # by not adding /usr/local/lib if it's from the broken
305          # Apple pcap-config.
306          #
307          if(NOT _broken_apple_pcap_config OR
308             NOT "${_dir}" STREQUAL /usr/local/lib)
309            # Add this directory to CONFIG_PCAP_LIBRARY_DIRS
310            list(APPEND CONFIG_PCAP_LIBRARY_DIRS ${_dir})
311          endif()
312        elseif(_arg MATCHES "^-l")
313          string(REGEX REPLACE "-l" "" _lib ${_arg})
314          list(APPEND CONFIG_PCAP_LIBRARIES ${_lib})
315        endif()
316      endforeach()
317
318      #
319      # Now, get the library directories and libraries for static linking.
320      #
321      execute_process(COMMAND "${PCAP_CONFIG}" "--libs" "--static"
322        RESULT_VARIABLE PCAP_CONFIG_RESULT
323        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
324      )
325      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
326        message(FATAL_ERROR "pcap-config --libs --static failed")
327      endif()
328      separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
329      set(CONFIG_PCAP_STATIC_LIBRARY_DIRS "")
330      set(CONFIG_PCAP_STATIC_LIBRARIES "")
331      foreach(_arg IN LISTS LIBS_LIST)
332        if(_arg MATCHES "^-L")
333          #
334          # Extract the directory by removing the -L.
335          #
336          string(REGEX REPLACE "-L" "" _dir ${_arg})
337          #
338          # Work around macOS (and probably other Darwin) brokenness,
339          # by not adding /usr/local/lib if it's from the broken
340          # Apple pcap-config.
341          #
342          if(NOT _broken_apple_pcap_config OR
343             NOT "${_dir}" STREQUAL /usr/local/lib)
344            # Add this directory to CONFIG_PCAP_STATIC_LIBRARY_DIRS
345            list(APPEND CONFIG_PCAP_STATIC_LIBRARY_DIRS ${_dir})
346          endif()
347        elseif(_arg MATCHES "^-l")
348          string(REGEX REPLACE "-l" "" _lib ${_arg})
349          #
350          # Try to find that library, so we get its full path, as
351          # we do with dynamic libraries.
352          #
353          list(APPEND CONFIG_PCAP_STATIC_LIBRARIES ${_lib})
354        endif()
355      endforeach()
356
357      #
358      # We've set CONFIG_PCAP_INCLUDE_DIRS, CONFIG_PCAP_LIBRARIES, and
359      # CONFIG_PCAP_STATIC_LIBRARIES above; set CONFIG_PCAP_FOUND.
360      #
361      set(CONFIG_PCAP_FOUND YES)
362    endif()
363  endif()
364
365  #
366  # If CONFIG_PCAP_FOUND is set, we have information from pkg-config and
367  # pcap-config; we need to convert library names to library full paths.
368  #
369  # If it's not set, we have to look for the libpcap headers and library
370  # ourselves.
371  #
372  if(CONFIG_PCAP_FOUND)
373    #
374    # Use CONFIG_PCAP_INCLUDE_DIRS as the value for PCAP_INCLUDE_DIRS.
375    #
376    set(PCAP_INCLUDE_DIRS "${CONFIG_PCAP_INCLUDE_DIRS}")
377
378    #
379    # CMake *really* doesn't like the notion of specifying
380    # "here are the directories in which to look for libraries"
381    # except in find_library() calls; it *really* prefers using
382    # full paths to library files, rather than library names.
383    #
384    foreach(_lib IN LISTS CONFIG_PCAP_LIBRARIES)
385      find_library(_libfullpath ${_lib} HINTS ${CONFIG_PCAP_LIBRARY_DIRS})
386      list(APPEND PCAP_LIBRARIES ${_libfullpath})
387      #
388      # Remove that from the cache; we're using it as a local variable,
389      # but find_library insists on making it a cache variable.
390      #
391      unset(_libfullpath CACHE)
392   endforeach()
393
394    #
395    # Now do the same for the static libraries.
396    #
397    set(SAVED_CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES}")
398    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
399    foreach(_lib IN LISTS CONFIG_PCAP_STATIC_LIBRARIES)
400      find_library(_libfullpath ${_lib} HINTS ${CONFIG_PCAP_LIBRARY_DIRS})
401      list(APPEND PCAP_STATIC_LIBRARIES ${_libfullpath})
402      #
403      # Remove that from the cache; we're using it as a local variable,
404      # but find_library insists on making it a cache variable.
405      #
406      unset(_libfullpath CACHE)
407    endforeach()
408    set(CMAKE_FIND_LIBRARY_SUFFIXES "${SAVED_CMAKE_FIND_LIBRARY_SUFFIXES}")
409
410    #
411    # We found libpcap using pkg-config or pcap-config.
412    #
413    set(PCAP_FOUND YES)
414  else(CONFIG_PCAP_FOUND)
415    #
416    # We didn't have pkg-config, or we did but it didn't have .pc files
417    # for libpcap, and we don't have pkg-config, so we have to look for
418    # the headers and libraries ourself.
419    #
420    # We don't directly set PCAP_INCLUDE_DIRS or PCAP_LIBRARIES, as
421    # they're not supposed to be cache entries, and find_path() and
422    # find_library() set cache entries.
423    #
424    # Try to find the header file.
425    #
426    find_path(PCAP_INCLUDE_DIR pcap.h)
427
428    #
429    # Try to find the library
430    #
431    find_library(PCAP_LIBRARY pcap)
432
433    # Try to find the static library (XXX - what about AIX?)
434    set(SAVED_CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES}")
435    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
436    find_library(PCAP_STATIC_LIBRARY pcap)
437    set(CMAKE_FIND_LIBRARY_SUFFIXES "${SAVED_CMAKE_FIND_LIBRARY_SUFFIXES}")
438
439    #
440    # This will fail if REQUIRED is set and PCAP_INCLUDE_DIR or
441    # PCAP_LIBRARY aren't set.
442    #
443    include(FindPackageHandleStandardArgs)
444    find_package_handle_standard_args(PCAP
445      DEFAULT_MSG
446      PCAP_INCLUDE_DIR
447      PCAP_LIBRARY
448    )
449
450    mark_as_advanced(
451      PCAP_INCLUDE_DIR
452      PCAP_LIBRARY
453      PCAP_STATIC_LIBRARY
454    )
455
456    if(PCAP_FOUND)
457      set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR})
458      set(PCAP_LIBRARIES ${PCAP_LIBRARY})
459      set(PCAP_STATIC_LIBRARIES ${PCAP_STATIC_LIBRARY})
460    endif(PCAP_FOUND)
461  endif(CONFIG_PCAP_FOUND)
462endif(WIN32)
463