1. Overview
In my last blog topic, I realize a network sniffer in Ubuntu, here I rewrite the code in Windows XP, and add a new function to find all network adapter.
2. Developing enviroment
- windows xp
- minGW 5.1.6
- gcc 3.4.5
- winPcap 4.1.1
- Erlang/OTP R13B03
3. nif.erl
%%% nif sniffer -module(nif). -on_load(on_load/0). -export([lookup/0, opendevice/1, capture/0, loop/1]). on_load() -> ok = erlang:load_nif("./nif", 0), true. lookup() -> error. opendevice(_Interface) -> error. capture() -> error. loop(0) -> ok; loop(Count) -> Pkt = capture(), io:format("~p~n", [Pkt]), loop(Count-1).
4. nif.h
#include "erl_nif.h" #include "stdio.h" #include "pcap.h" #include "string.h" #include "ctype.h" #ifndef NIF_H #define NIF_H #ifdef __cplusplus extern "C" { #endif static ERL_NIF_TERM opendevice(ErlNifEnv* env, ERL_NIF_TERM device); static ERL_NIF_TERM capture(ErlNifEnv* env); static ERL_NIF_TERM lookup(ErlNifEnv* env); #ifdef __cplusplus } #endif #endif
5. nif.c
/* This file used to create a Erlang NIF which sniffer network packets. */ #include "nif.h" pcap_t *devHandler = NULL; static int my_enif_get_string(ErlNifEnv *env, ERL_NIF_TERM list, char* buf) { ERL_NIF_TERM cell, head, tail; int val; while (enif_get_list_cell(env, list, &head, &tail)) { if (!enif_get_int(env, head, &val)) return 1; *buf = (char)val; buf++; list = tail; } *buf = '\0'; return 0; } static ERL_NIF_TERM lookup(ErlNifEnv* env) { int i = 0; char errbuf[PCAP_ERRBUF_SIZE], str[1024]; pcap_if_t *alldevs; pcap_if_t *d; if (pcap_findalldevs_ex("rpcap://", NULL /* auth is not needed */, &alldevs, errbuf) == -1) return enif_make_string(env, errbuf); for(d= alldevs; d != NULL; d= d->next) { strcat(str, d->name); strcat(str, "|||"); /* strcat(str, "\t\t"); if (d->description) strcat(str, d->description); else strcat(str,"\n"); */ } pcap_freealldevs(alldevs); return enif_make_string(env, str); } static ERL_NIF_TERM opendevice(ErlNifEnv* env, ERL_NIF_TERM device) { char dev[64]; char errbuf[PCAP_ERRBUF_SIZE]; //memset(errbuf, 0, PCAP_ERRBUF_SIZE); my_enif_get_string(env, device, dev); /* return enif_make_string(env, dev); */ /* Parms: dev,snaplen,promisc,timeout_ms,errbuf * to_ms=0 means wait enough packet to arrive. */ devHandler = pcap_open_live(dev, 65535, 1, 0, errbuf); if(devHandler != NULL) return enif_make_atom(env, "ok"); else return enif_make_string(env, errbuf); } static ERL_NIF_TERM capture(ErlNifEnv* env) { int i; struct pcap_pkthdr pkthdr; const u_char *packet = NULL; ErlNifBinary bin; packet = pcap_next(devHandler, &pkthdr); if(packet != NULL) { enif_alloc_binary(env, pkthdr.len, &bin); for(i = 0; i < pkthdr.len; i++) { bin.data[i] = packet[i]; } } else { bin.size = sizeof("NULL"); bin.data = "NULL"; } return enif_make_binary(env, &bin); } static ErlNifFunc nif_funcs[] = { {"lookup", 0, lookup}, {"capture", 0, capture}, {"opendevice", 1, opendevice} }; ERL_NIF_INIT(nif,nif_funcs,NULL,NULL,NULL,NULL)
6. Build the code
- Insatll minGW, and re-set %PATH%, %C_INCLUDE_PATH%, %LIBRARY_PATH%.
- Copy the ERTS WinPcap include and lib folder to minGW folder.
- Copy wpcap.lib to source folder.
- in windows [cmd] enviroment, execute following
gcc -shared -o nif.dll nif.c wpcap.lib
7. Test the code
Erlang R13B03 (erts-5.7.4) [smp:2:2] [rq:2] [async-threads:0] Eshell V5.7.4 (abort with ^G) (first@ETxumingyong)1> cd("sniffer_nif/win32"). D:/workspace/sniffer_nif/win32 ok (first@ETxumingyong)2> c(nif). {ok,nif} (first@ETxumingyong)3> nif:lookup(). "rpcap://\\Device\\NPF_GenericDialupAdapter||| rpcap://\\Device\\NPF_{CB6CFA59-46DE-4172-BBB1-85C85E654848} |||rpcap://\\Device\\NPF_{B9A5FCD5-1424-4F42-B680-E73A89CFF638}|||" (first@ETxumingyong)4> nif:opendevice("rpcap://\\Device\\NPF_{CB6CFA59-46DE-4172-BBB1-85C85E654848}"). ok -------------------------------------------------------------- <<255,255,255,255,255,255,0,27,185,223,71,97,8,0,69,0,0,78,188,123,0,0,128,17, 13,12,10,184,45,169,10,184,45,255,0,137,0,137,0,58,111,159,134,52,1,16,0,1,0, 0,0,0,0,0,32,69,75,69,74,69,66,69,79,69,76,69,80,69,79,69,72,69,67,69,66,69, 79,67,65,67,65,67,65,67,65,67,65,0,0,32,0,1>> <<255,255,255,255,255,255,0,35,77,220,61,143,8,0,69,0,0,78,19,248,0,0,64,17, 245,76,10,184,45,236,10,184,45,255,0,137,0,137,0,58,191,137,128,20,1,16,0,1, 0,0,0,0,0,0,32,69,70,69,73,70,65,70,65,67,65,67,65,67,65,67,65,67,65,67,65, 67,65,67,65,67,65,67,65,67,65,66,76,0,0,32,0,1>> <<1,128,194,0,0,0,0,2,125,41,137,65,0,38,66,66,3,0,0,0,0,0,128,0,0,2,125,41, 137,64,0,0,0,0,128,0,0,2,125,41,137,64,10,13,0,0,20,0,2,0,15,0,0,0,0,0,0,0,0, 0>> <<255,255,255,255,255,255,0,35,84,32,248,213,8,6,0,1,8,0,6,4,0,1,0,35,84,32, 248,213,10,184,45,230,0,0,0,0,0,0,10,184,45,136,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0>> 。。。
8. Note
- I tried the visuall c++ 6.0, failed, the cl tool are so old.
- After call [nif:lookup() ] function, there are 3 net adapter with register style output, very strange.