1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2021. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(snmp_test_mgr).
22
23
24%%----------------------------------------------------------------------
25%% This module implements a simple SNMP manager for Erlang. Its used
26%% during by the agent test suite.
27%%----------------------------------------------------------------------
28
29%% c(snmp_test_mgr).
30%% snmp_test_mgr:start().
31%% snmp_test_mgr:g([[sysContact,0]]).
32
33%% snmp_test_mgr:start([{engine_id, "mbjk's engine"}, v3, {agent, "clip"}, {mibs, ["../mibs/SNMPv2-MIB"]}]).
34
35%% snmp_test_mgr:start([{engine_id, "agentEngine"}, {user, "iwl_test"}, {dir, "mgr_conf"}, {sec_level, authPriv}, v3, {agent, "clip"}]).
36
37%% User interface
38-export([start_link/1, start/1, stop/0,
39	 d/0, discovery/0,
40	 g/1, s/1, gn/1, gn/0, r/0, gb/3, rpl/1,
41	 send_bytes/1,
42	 expect/2,expect/3,expect/4,expect/6,get_response/2,
43	 receive_response/0,
44	 purify_oid/1,
45	 oid_to_name/1, name_to_oid/1]).
46
47%% Internal exports
48-export([get_oid_from_varbind/1,
49	 var_and_value_to_varbind/2, flatten_oid/2, make_vb/1]).
50-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
51
52-include_lib("snmp/include/snmp_types.hrl").
53-include_lib("snmp/include/STANDARD-MIB.hrl").
54-include("snmp_test_lib.hrl").
55-include_lib("snmp/src/app/snmp_internal.hrl").
56
57-record(state, {dbg         = true,
58                quiet,
59                parent,
60                timeout     = 3500,
61                print_traps = true,
62                mini_mib,
63                packet_server,
64                last_sent_pdu,
65                last_received_pdu}).
66
67-define(SERVER, ?MODULE).
68-define(PACK_SERV, snmp_test_mgr_misc).
69
70start_link(Options) ->
71    gen_server:start_link({local, ?SERVER}, ?MODULE, {Options, self()}, []).
72
73start(Options) ->
74    gen_server:start({local, ?SERVER}, ?MODULE, {Options, self()}, []).
75
76stop() ->
77    call(stop).
78
79d() ->
80    discovery().
81
82discovery() ->
83    call(discovery).
84
85g(Oids) ->
86    cast({get, Oids}).
87
88%% VarsAndValues is: {PlainOid, o|s|i, Value} (unknown mibs) | {Oid, Value}
89s(VarsAndValues) ->
90    cast({set, VarsAndValues}).
91
92gn(Oids) when is_list(Oids) ->
93    cast({get_next, Oids});
94gn(N) when is_integer(N) ->
95    cast({iter_get_next, N}).
96gn() ->
97    cast(iter_get_next).
98
99r() ->
100    cast(resend_pdu).
101
102gb(NonRepeaters, MaxRepetitions, Oids) ->
103    cast({bulk, {NonRepeaters, MaxRepetitions, Oids}}).
104
105rpl(RespPdu) ->
106    cast({response, RespPdu}).
107
108send_bytes(Bytes) ->
109    cast({send_bytes, Bytes}).
110
111purify_oid(Oid) ->
112    call({purify_oid, Oid}, 5000).
113
114oid_to_name(Oid) ->
115    call({oid_to_name, Oid}, 5000).
116
117name_to_oid(Name) ->
118    call({name_to_oid, Name}, 5000).
119
120
121%%----------------------------------------------------------------------
122%% Purpose: For writing test sequences
123%% Args: Y=any (varbinds) | trap | timeout | VarBinds | ErrStatus
124%% Returns: ok|{error, Id, Reason}
125%%----------------------------------------------------------------------
126expect(Id,Y) -> echo_errors(expect_impl(Id,Y)).
127expect(Id,v2trap,VBs) -> echo_errors(expect_impl(Id,v2trap,VBs));
128expect(Id,report,VBs) -> echo_errors(expect_impl(Id,report,VBs));
129expect(Id,{inform, Reply},VBs) ->
130    echo_errors(expect_impl(Id,{inform,Reply},VBs)).
131expect(Id,Err,Idx,VBs) -> echo_errors(expect_impl(Id,Err,Idx,VBs)).
132expect(Id,trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
133    echo_errors(expect_impl(Id,trap,Enterp,Generic,
134			    Specific,ExpectedVarbinds)).
135
136%%-----------------------------------------------------------------
137%% Purpose: For writing test sequences
138%%-----------------------------------------------------------------
139get_response(Id, Vars) -> echo_errors(get_response_impl(Id, Vars)).
140
141%%----------------------------------------------------------------------
142%% Receives a response from the agent.
143%% Returns: a PDU or {error, Reason}.
144%% It doesn't receive traps though.
145%%----------------------------------------------------------------------
146receive_response() ->
147    receive_response(get_timeout()).
148
149receive_response(Timeout) ->
150    d("await response within ~w ms",[Timeout]),
151    receive
152	{snmp_pdu, PDU} when is_record(PDU, pdu) ->
153	    d("received PDU: ~n\t~p",[PDU]),
154	    PDU
155    after Timeout ->
156	    d("response timeout",[]),
157	    {error, timeout}
158    end.
159
160
161get_timeout() ->
162    case get(receive_response_timeout) of
163	Int when is_integer(Int) and (Int > 0) ->
164	    Int;
165	_ ->
166	    get_timeout(os:type())
167    end.
168
169get_timeout(_)       -> 10000. % Trying to improve test results % 3500.
170
171
172%%----------------------------------------------------------------------
173%% Receives a trap from the agent.
174%% Returns: TrapPdu|{error, Reason}
175%%----------------------------------------------------------------------
176receive_trap(Timeout) ->
177    d("await trap within ~w ms",[Timeout]),
178    receive
179	{snmp_pdu, PDU} when is_record(PDU, trappdu) ->
180	    d("received trap-PDU: ~n\t~p",[PDU]),
181	    PDU
182    after Timeout ->
183	    d("trap timeout",[]),
184	    {error, timeout}
185    end.
186
187%%----------------------------------------------------------------------
188%% Options: List of
189%%  {agent_udp, UDPPort},  {agent, Agent}
190%%  Optional:
191%%  {community, String ("public" is default}, quiet,
192%%  {mibs, List of Filenames}, {trap_udp, UDPPort (default 5000)},
193%%----------------------------------------------------------------------
194init({Options, CallerPid}) ->
195    %% This causes "verbosity printouts" to print (from level debug)
196    %% in the modules we "borrow" from the agent code (burr,,,)
197    %% With "our" name (mgr).
198    put(sname,     mgr),
199    put(verbosity, debug),
200    %% Make use of the "test name" print "feature"
201    put(tname,     "MGR"),
202    ?SNMP_RAND_SEED(),
203    case (catch is_options_ok(Options)) of
204	true ->
205	    put(debug, get_value(debug, Options, false)),
206	    d("init -> extract options"),
207 	    PacksDbg    = get_value(packet_server_debug, Options, false),
208	    ?IPRINT("init -> PacksDbg: ~p", [PacksDbg]),
209	    RecBufSz    = get_value(recbuf,            Options, 1024),
210	    ?IPRINT("init -> RecBufSz: ~p", [RecBufSz]),
211	    Mibs        = get_value(mibs,              Options, []),
212	    ?IPRINT("init -> Mibs: "
213                    "~n   ~tp", [Mibs]),
214	    Udp         = get_value(agent_udp,         Options, 4000),
215	    ?IPRINT("init -> Udp: ~p", [Udp]),
216	    User        = get_value(user,              Options, "initial"),
217	    ?IPRINT("init -> User: ~p", [User]),
218	    EngineId    = get_value(engine_id,         Options, "agentEngine"),
219	    ?IPRINT("init -> EngineId: ~p", [EngineId]),
220	    CtxEngineId = get_value(context_engine_id, Options, EngineId),
221	    ?IPRINT("init -> CtxEngineId: ~p", [CtxEngineId]),
222	    TrapUdp     = get_value(trap_udp,          Options, 5000),
223	    ?IPRINT("init -> TrapUdp: ~p", [TrapUdp]),
224	    Dir         = get_value(dir,               Options, "."),
225	    ?IPRINT("init -> Dir: ~p", [Dir]),
226	    SecLevel    = get_value(sec_level,         Options, noAuthNoPriv),
227	    ?IPRINT("init -> SecLevel: ~p", [SecLevel]),
228	    MiniMIB     = snmp_mini_mib:create(Mibs),
229	    d("init -> MiniMIB: "
230              "~n   ~p", [MiniMIB]),
231	    Version     = case lists:member(v2, Options) of
232			      true -> 'version-2';
233			      false ->
234				  case lists:member(v3, Options) of
235				      true -> 'version-3';
236				      false -> 'version-1'
237				  end
238			  end,
239	    ?IPRINT("init -> Version: ~p", [Version]),
240	    Com = case Version of
241		      'version-3' ->
242			  get_value(context, Options, "");
243		      _ ->
244			  get_value(community, Options, "public")
245		  end,
246	    ?IPRINT("init -> Com: ~p", [Com]),
247	    VsnHdrD =
248		{Com, User, EngineId, CtxEngineId, mk_seclevel(SecLevel)},
249	    ?IPRINT("init -> VsnHdrD: ~p", [VsnHdrD]),
250	    IpFamily = get_value(ipfamily, Options, inet),
251	    ?IPRINT("init -> IpFamily: ~p", [IpFamily]),
252	    AgIp = case snmp_misc:assq(agent, Options) of
253		       {value, Addr} when is_tuple(Addr) andalso
254                                          (size(Addr) =:= 4) andalso
255                                          (IpFamily =:= inet) ->
256                           ?IPRINT("init -> Addr: ~p", [Addr]),
257			   Addr;
258		       {value, Addr} when is_tuple(Addr) andalso
259                                          (size(Addr) =:= 8) andalso
260                                          (IpFamily =:= inet6) ->
261                           ?IPRINT("init -> Addr: ~p", [Addr]),
262			   Addr;
263		       {value, Host} when is_list(Host) ->
264                           ?IPRINT("init -> Host: ~p", [Host]),
265			   {ok, Ip} = ?LIB:which_host_ip(Host, IpFamily),
266			   Ip
267		   end,
268	    ?IPRINT("init -> AgIp: ~p", [AgIp]),
269	    Quiet = lists:member(quiet, Options),
270	    ?IPRINT("init -> Quiet: ~p", [Quiet]),
271	    PackServ =
272		start_packet_server(
273		  Quiet, Options, CallerPid, AgIp, Udp, TrapUdp,
274		  VsnHdrD, Version, Dir, RecBufSz, PacksDbg, IpFamily),
275	    d("init -> packet server: ~p",[PackServ]),
276	    State = #state{parent        = CallerPid,
277			   quiet         = Quiet,
278			   mini_mib      = MiniMIB,
279			   packet_server = PackServ},
280	    d("init -> done",[]),
281	    {ok, State};
282
283	{error, Reason} ->
284	    {stop,Reason}
285    end.
286
287start_packet_server(false, _Options, _CallerPid, AgIp, Udp, TrapUdp,
288		    VsnHdrD, Version, Dir, RecBufSz, PacksDbg, IpFamily) ->
289    d("start_packet_server -> entry", []),
290    ?PACK_SERV:start_link_packet(
291       {msg, self()}, AgIp, Udp, TrapUdp,
292       VsnHdrD, Version, Dir, RecBufSz, PacksDbg, IpFamily);
293start_packet_server(true, Options, CallerPid, AgIp, Udp, TrapUdp,
294		    VsnHdrD, Version, Dir, RecBufSz, PacksDbg, IpFamily) ->
295    Type =  get_value(receive_type, Options, pdu),
296    d("start_packet_server -> entry with"
297      "~n   CallerPid: ~p"
298      "~n   when"
299      "~n   Type:      ~p",[CallerPid, Type]),
300    ?PACK_SERV:start_link_packet(
301       {Type, CallerPid}, AgIp, Udp, TrapUdp,
302       VsnHdrD, Version, Dir, RecBufSz, PacksDbg, IpFamily).
303
304is_options_ok([{mibs,List}|Opts]) when is_list(List) ->
305    is_options_ok(Opts);
306is_options_ok([quiet|Opts])  ->
307    is_options_ok(Opts);
308is_options_ok([{agent,_}|Opts]) ->
309    is_options_ok(Opts);
310is_options_ok([{ipfamily,IpFamily}|Opts])
311  when IpFamily =:= inet;
312       IpFamily =:= inet6 ->
313    is_options_ok(Opts);
314is_options_ok([{agent_udp,Int}|Opts]) when is_integer(Int) ->
315    is_options_ok(Opts);
316is_options_ok([{agent_udp, {IntR, IntT}}|Opts]) when is_integer(IntR) andalso
317                                                     is_integer(IntT) ->
318    is_options_ok(Opts);
319is_options_ok([{trap_udp,Int}|Opts]) when is_integer(Int) ->
320    is_options_ok(Opts);
321is_options_ok([{community,List}|Opts]) when is_list(List) ->
322    is_options_ok(Opts);
323is_options_ok([{dir,List}|Opts]) when is_list(List) ->
324    is_options_ok(Opts);
325is_options_ok([{sec_level,noAuthNoPriv}|Opts]) ->
326    is_options_ok(Opts);
327is_options_ok([{sec_level,authNoPriv}|Opts]) ->
328    is_options_ok(Opts);
329is_options_ok([{sec_level,authPriv}|Opts]) ->
330    is_options_ok(Opts);
331is_options_ok([{context,List}|Opts]) when is_list(List) ->
332    is_options_ok(Opts);
333is_options_ok([{user,List}|Opts]) when is_list(List) ->
334    is_options_ok(Opts);
335is_options_ok([{engine_id,List}|Opts]) when is_list(List) ->
336    is_options_ok(Opts);
337is_options_ok([{context_engine_id,List}|Opts]) when is_list(List) ->
338    is_options_ok(Opts);
339is_options_ok([v1|Opts]) ->
340    is_options_ok(Opts);
341is_options_ok([v2|Opts]) ->
342    is_options_ok(Opts);
343is_options_ok([v3|Opts]) ->
344    is_options_ok(Opts);
345is_options_ok([{debug,Bool}|Opts]) ->
346    case is_bool(Bool) of
347	ok ->
348	    is_options_ok(Opts);
349	error ->
350	    {error, {bad_option, debug, Bool}}
351    end;
352is_options_ok([{packet_server_debug,Bool}|Opts]) ->
353    case is_bool(Bool) of
354	ok ->
355	    is_options_ok(Opts);
356	error ->
357	    {error, {bad_option, packet_server_debug, Bool}}
358    end;
359is_options_ok([{recbuf,Sz}|Opts]) when (0 < Sz) and (Sz =< 65535) ->
360    is_options_ok(Opts);
361is_options_ok([InvOpt|_]) ->
362    {error,{invalid_option,InvOpt}};
363is_options_ok([]) -> true.
364
365is_bool(true)  -> ok;
366is_bool(false) -> ok;
367is_bool(_)     -> error.
368
369mk_seclevel(noAuthNoPriv) -> 0;
370mk_seclevel(authNoPriv) -> 1;
371mk_seclevel(authPriv) -> 3.
372
373
374handle_call({purify_oid, Oid}, _From, #state{mini_mib = MiniMib} = State) ->
375    d("handle_call -> purify_oid for ~p",[Oid]),
376    Reply = (catch purify_oid(Oid, MiniMib)),
377    {reply, Reply, State};
378
379handle_call({find_pure_oid, XOid}, _From, State) ->
380    d("handle_call -> find_pure_oid for ~p",[XOid]),
381    {reply, catch flatten_oid(XOid, State#state.mini_mib), State};
382
383handle_call({oid_to_name, Oid}, _From, State) ->
384    d("handle_call -> oid_to_name for Oid: ~p",[Oid]),
385    Reply =
386	case lists:keysearch(Oid, 1, State#state.mini_mib) of
387	    {value, {_Oid, Name, _Type}} ->
388		{ok, Name};
389	    false ->
390		{error, {no_such_oid, Oid}}
391	end,
392    {reply, Reply, State};
393
394handle_call({name_to_oid, Name}, _From, State) ->
395    d("handle_call -> name_to_oid for Name: ~p",[Name]),
396    Reply =
397	case lists:keysearch(Name, 2, State#state.mini_mib) of
398	    {value, {Oid, _Name, _Type}} ->
399		{ok, Oid};
400	    false ->
401		{error, {no_such_name, Name}}
402	end,
403    {reply, Reply, State};
404
405handle_call(stop, _From, #state{mini_mib = MiniMIB} = State) ->
406    d("handle_call -> stop request",[]),
407    snmp_mini_mib:delete(MiniMIB),
408    {stop, normal, ok, State#state{mini_mib = undefined}};
409
410handle_call(discovery, _From, State) ->
411    d("handle_call -> discovery",[]),
412    {Reply, NewState} = execute_discovery(State),
413    {reply, Reply, NewState}.
414
415
416handle_cast({get, Oids}, State) ->
417    d("handle_cast -> get request for ~p", [Oids]),
418    {noreply, execute_request(get, Oids, State)};
419
420handle_cast({set, VariablesAndValues}, State) ->
421    d("handle_cast -> set request for ~p", [VariablesAndValues]),
422    {noreply, execute_request(set, VariablesAndValues, State)};
423
424handle_cast({get_next, Oids}, State) ->
425    d("handle_cast -> get-next request for ~p", [Oids]),
426    {noreply, execute_request(get_next, Oids, State)};
427
428handle_cast(iter_get_next, State)
429  when is_record(State#state.last_received_pdu, pdu) ->
430    d("handle_cast -> iter_get_next request", []),
431    PrevPDU = State#state.last_received_pdu,
432    Oids    = [get_oid_from_varbind(Vb) || Vb <- PrevPDU#pdu.varbinds],
433    {noreply, execute_request(get_next, Oids, State)};
434
435handle_cast(iter_get_next, State) ->
436    ?EPRINT("[Iterated get-next] No Response PDU to "
437            "start iterating from.", []),
438    {noreply, State};
439
440handle_cast({iter_get_next, N}, State) ->
441    d("handle_cast -> iter_get_next(~p) request",[N]),
442    if
443	is_record(State#state.last_received_pdu, pdu) ->
444	    PDU = get_next_iter_impl(N, State#state.last_received_pdu,
445				     State#state.mini_mib,
446				     State#state.packet_server),
447	    {noreply, State#state{last_received_pdu = PDU}};
448	true ->
449	    ?EPRINT("[Iterated get-next] No Response PDU to "
450                    "start iterating from.", []),
451	    {noreply, State}
452    end;
453
454handle_cast(resend_pdu, #state{last_sent_pdu = PDU} = State) ->
455    d("handle_cast -> resend_pdu request when"
456      "~n   PDU = ~p", [PDU]),
457    send_pdu(PDU#pdu{request_id = make_request_id()},
458	     State#state.mini_mib,
459	     State#state.packet_server),
460    {noreply, State};
461
462handle_cast({bulk, Args}, State) ->
463    d("handle_bulk -> bulk request for ~p", [Args]),
464    {noreply, execute_request(bulk, Args, State)};
465
466handle_cast({response, RespPdu}, State) ->
467    d("handle_cast -> response request with "
468      "~n   ~p", [RespPdu]),
469    ?PACK_SERV:send_pdu(RespPdu, State#state.packet_server),
470    {noreply, State};
471
472handle_cast({send_bytes, Bytes}, State) ->
473    d("handle_cast -> send-bytes request for ~p bytes", [sizeOf(Bytes)]),
474    ?PACK_SERV:send_bytes(Bytes, State#state.packet_server),
475    {noreply, State};
476
477handle_cast(Msg, State) ->
478    d("handle_cast -> unknown message: "
479      "~n   ~p", [Msg]),
480    {noreply, State}.
481
482
483handle_info({snmp_msg, Msg, Ip, Udp}, State) ->
484    io:format("* Got PDU: ~s", [?PACK_SERV:format_hdr(Msg)]),
485    PDU = ?PACK_SERV:get_pdu(Msg),
486    echo_pdu(PDU, State#state.mini_mib),
487    case PDU#pdu.type of
488	'inform-request' ->
489	    %% Generate a response...
490	    RespPDU = PDU#pdu{type = 'get-response',
491			      error_status = noError,
492			      error_index = 0},
493	    RespMsg = ?PACK_SERV:set_pdu(Msg, RespPDU),
494	    ?PACK_SERV:send_msg(RespMsg, State#state.packet_server, Ip, Udp);
495	_Else ->
496	    ok
497    end,
498    {noreply, State#state{last_received_pdu = PDU}};
499
500handle_info({'EXIT', Pid, Reason}, #state{packet_server = Pid} = State) ->
501    error_logger:error_msg("Received unexpected exit signal from Packet Server ~p: "
502			   "~n   ~p", [Pid, Reason]),
503    {stop, State#state{packet_server = undefined}};
504
505handle_info(Info, State) ->
506    d("handle_info -> unknown info: "
507      "~n   ~p", [Info]),
508    {noreply, State}.
509
510
511terminate(Reason, #state{packet_server = Pid} = _State) when is_pid(Pid) ->
512    d("terminate -> with Reason: ~n\t~p", [Reason]),
513    ?PACK_SERV:stop(Pid);
514terminate(Reason, _State) ->
515    d("terminate -> with Reason: ~n\t~p",[Reason]),
516    ok.
517
518
519%%----------------------------------------------------------------------
520%% Returns: A new State
521%%----------------------------------------------------------------------
522execute_discovery(State) ->
523    Pdu   = make_discovery_pdu(),
524    Reply = ?PACK_SERV:send_discovery_pdu(Pdu, State#state.packet_server),
525    {Reply, State#state{last_sent_pdu = Pdu}}.
526
527
528execute_request(Operation, Data, State) ->
529    case (catch make_pdu(Operation, Data, State#state.mini_mib)) of
530	{error, {Format, Data2}} ->
531	    report_error(State, Format, Data2),
532	    State;
533	{error, _Reason} ->
534	    State;
535	PDU when is_record(PDU, pdu) ->
536	    send_pdu(PDU, State#state.mini_mib, State#state.packet_server),
537	    State#state{last_sent_pdu = PDU}
538    end.
539
540report_error(#state{quiet = true, parent = Pid}, Format, Args) ->
541    Reason = lists:flatten(io_lib:format(Format, Args)),
542    Pid ! {oid_error, Reason};
543report_error(_, Format, Args) ->
544    ?EPRINT(Format, Args).
545
546
547get_oid_from_varbind(#varbind{oid = Oid}) -> Oid.
548
549send_pdu(PDU, _MiniMIB, PackServ) ->
550    ?PACK_SERV:send_pdu(PDU, PackServ).
551
552%%----------------------------------------------------------------------
553%% Purpose: Unnesting of oids like [myTable, 3, 4, "hej", 45] to
554%%          [1,2,3,3,4,104,101,106,45]
555%%----------------------------------------------------------------------
556
557purify_oid([A|T], MiniMib) when is_atom(A) ->
558    Oid2 =
559	case snmp_mini_mib:oid(MiniMib, A) of
560	    false ->
561		throw({error, {unknown_aliasname, A}});
562	    Oid ->
563		lists:flatten([Oid|T])
564	end,
565    {ok, verify_pure_oid(Oid2)};
566purify_oid(L, _) when is_list(L) ->
567    {ok, verify_pure_oid(lists:flatten(L))};
568purify_oid(X, _) ->
569    {error, {invalid_oid, X}}.
570
571verify_pure_oid([]) ->
572    [];
573verify_pure_oid([H | T]) when is_integer(H) and (H >= 0) ->
574    [H | verify_pure_oid(T)];
575verify_pure_oid([H | _]) ->
576    throw({error, {not_pure_oid, H}}).
577
578flatten_oid(XOid, DB)  ->
579    Oid = case XOid of
580	       [A|T] when is_atom(A) ->
581		   [remove_atom(A, DB)|T];
582	       L when is_list(L) ->
583		   XOid;
584	       Shit ->
585		   throw({error,
586			  {"Invalid oid, not a list of integers: ~w", [Shit]}})
587	   end,
588    check_is_pure_oid(lists:flatten(Oid)).
589
590remove_atom(AliasName, DB) when is_atom(AliasName) ->
591    case snmp_mini_mib:oid(DB, AliasName) of
592	false ->
593	    throw({error, {"Unknown aliasname in oid: ~w", [AliasName]}});
594	Oid ->
595	    Oid
596    end;
597remove_atom(X, _DB) ->
598    X.
599
600%%----------------------------------------------------------------------
601%% Throws if not a list of integers
602%%----------------------------------------------------------------------
603check_is_pure_oid([]) -> [];
604check_is_pure_oid([X | T]) when is_integer(X) and (X >= 0) ->
605    [X | check_is_pure_oid(T)];
606check_is_pure_oid([X | _T]) ->
607    throw({error, {"Invalid oid, it contains a non-integer: ~w", [X]}}).
608
609get_next_iter_impl(0, PrevPDU, _MiniMIB, _PackServ) ->
610    PrevPDU;
611get_next_iter_impl(N, PrevPDU, MiniMIB, PackServ) ->
612    Oids = [get_oid_from_varbind(Vb) || Vb <- PrevPDU#pdu.varbinds],
613    PDU  = make_pdu(get_next, Oids, MiniMIB),
614    send_pdu(PDU, MiniMIB, PackServ),
615    case receive_response() of
616	{error, timeout} ->
617	    io:format("(timeout)~n"),
618	    get_next_iter_impl(N, PrevPDU, MiniMIB, PackServ);
619	{error, _Reason} ->
620	    PrevPDU;
621	RPDU when is_record(RPDU, pdu) ->
622	    io:format("(~w)", [N]),
623	    echo_pdu(RPDU, MiniMIB),
624	    get_next_iter_impl(N-1, RPDU, MiniMIB, PackServ)
625    end.
626
627%%--------------------------------------------------
628%% Used to resend a PDU. Takes the old PDU and
629%% generates a fresh one (with a new requestID).
630%%--------------------------------------------------
631
632make_pdu(set, VarsAndValues, MiniMIB) ->
633    VBs = [var_and_value_to_varbind(VAV, MiniMIB) || VAV <- VarsAndValues],
634    make_pdu_impl(set, VBs);
635make_pdu(bulk, {NonRepeaters, MaxRepetitions, Oids}, MiniMIB) ->
636    Foids = [flatten_oid(Oid, MiniMIB) || Oid <- Oids],
637    #pdu{type = 'get-bulk-request',
638	 request_id   = make_request_id(),
639	 error_status = NonRepeaters,
640	 error_index  = MaxRepetitions,
641	 varbinds     = [make_vb(Oid) || Oid <- Foids]};
642make_pdu(Operation, Oids, MiniMIB) ->
643    Foids = [flatten_oid(Oid, MiniMIB) || Oid <- Oids],
644    make_pdu_impl(Operation, Foids).
645
646make_pdu_impl(get, Oids) ->
647    #pdu{type         = 'get-request',
648	 request_id   = make_request_id(),
649	 error_status = noError,
650	 error_index  = 0,
651	 varbinds     = [make_vb(Oid) || Oid <- Oids]};
652
653make_pdu_impl(get_next, Oids) ->
654    #pdu{type         = 'get-next-request',
655	 request_id   = make_request_id(),
656	 error_status = noError,
657	 error_index  = 0,
658	 varbinds     = [make_vb(Oid) || Oid <- Oids]};
659
660make_pdu_impl(set, Varbinds) ->
661    #pdu{type         = 'set-request',
662	 request_id   = make_request_id(),
663	 error_status = noError,
664	 error_index  = 0,
665	 varbinds     = Varbinds}.
666
667make_discovery_pdu() ->
668    make_pdu_impl(get, []).
669
670var_and_value_to_varbind({Oid, Type, Value}, MiniMIB) ->
671    Oid2 = flatten_oid(Oid, MiniMIB),
672    #varbind{oid          = Oid2,
673	     variabletype = char_to_type(Type),
674	     value        = Value};
675var_and_value_to_varbind({XOid, Value}, MiniMIB) ->
676    Oid = flatten_oid(XOid, MiniMIB),
677    #varbind{oid          = Oid,
678	     variabletype = snmp_mini_mib:type(MiniMIB, Oid),
679	     value        = Value}.
680
681char_to_type(o) ->
682    'OBJECT IDENTIFIER';
683char_to_type(i) ->
684    'INTEGER';
685char_to_type(u) ->
686    'Unsigned32';
687char_to_type(g) -> % Gauge, Gauge32
688    'Unsigned32';
689char_to_type(s) ->
690    'OCTET STRING'.
691
692make_vb(Oid) ->
693    #varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}.
694
695make_request_id() ->
696    snmp_test_mgr_counter_server:increment(mgr_request_id, 1, 1, 2147483647).
697
698echo_pdu(PDU, MiniMIB) ->
699    io:format("~s", [snmp_misc:format_pdu(PDU, MiniMIB)]).
700
701
702%%----------------------------------------------------------------------
703%% Test Sequence
704%%----------------------------------------------------------------------
705echo_errors({error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}})->
706    ?EPRINT("*** Unexpected Behaviour *** Id: ~w"
707            "~n   Expected: " ++ ExpectedFormat ++
708            "~n   Got:      " ++ Format ++ "~n",
709            [Id] ++ ExpectedData ++ Data),
710    {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}};
711echo_errors(ok) -> ok;
712echo_errors({ok, _} = OK) -> OK.
713
714get_response_impl(Id, ExpVars) ->
715    ?IPRINT("await response ~w with"
716            "~n   Expected Varbinds: ~p",
717            [Id, ExpVars]),
718    PureVars = find_pure_oids2(ExpVars),
719    case receive_response() of
720	#pdu{type         = 'get-response',
721	     error_status = noError,
722	     error_index  = 0,
723	     varbinds     = VBs} ->
724            ?IPRINT("received expected response pdu (~w) - match vars"
725                    "~n   Expected VBs: ~p"
726                    "~n   Received VBs: ~p",
727                    [Id, PureVars, VBs]),
728	    match_vars(Id, PureVars, VBs, []);
729
730	#pdu{type         = Type2,
731	     request_id   = ReqId,
732	     error_status = Err2,
733	     error_index  = Index2} ->
734            ?EPRINT("received unexpected response pdu: ~w, ~w, ~w"
735                    "~n   Received Error: ~p"
736                    "~n   Received Index: ~p",
737                    [Type2, Id, ReqId, Err2, Index2]),
738	    {error,
739	     Id,
740	     {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
741	      ['get-response', noError, 0, ReqId]},
742	     {"Type: ~w, ErrStat: ~w, Idx: ~w",
743	      [Type2, Err2, Index2]}};
744
745	{error, Reason} ->
746            %% We did not get the message we wanted,
747            %% but what did we get?
748            ?EPRINT("unexpected receive pdu error: ~w"
749                    "~n   Reason:  ~p"
750                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
751	    format_reason(Id, Reason)
752    end.
753
754
755
756%%----------------------------------------------------------------------
757%% Returns: ok | {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}}
758%%----------------------------------------------------------------------
759expect_impl(Id, any) ->
760    ?IPRINT("await ~w pdu (any)", [Id]),
761    case receive_response() of
762	PDU when is_record(PDU, pdu) ->
763            ?IPRINT("received expected pdu (~w)", [Id]),
764            ok;
765	{error, Reason} ->
766            %% We did not get the message we wanted,
767            %% but what did we get?
768            ?EPRINT("unexpected receive error: ~w"
769                    "~n   Reason:  ~p"
770                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
771            format_reason(Id, Reason)
772    end;
773
774expect_impl(Id, return) ->
775    ?IPRINT("await ~w pdu", [Id]),
776    case receive_response() of
777	PDU when is_record(PDU, pdu) ->
778            ?IPRINT("received expected pdu (~w)", [Id]),
779            {ok, PDU};
780	{error, Reason} ->
781            %% We did not get the message we wanted,
782            %% but what did we get?
783            ?EPRINT("unexpected receive error: ~w"
784                    "~n   Reason:  ~p"
785                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
786            format_reason(Id, Reason)
787    end;
788
789expect_impl(Id, trap) ->
790    ?IPRINT("await ~w trap", [Id]),
791    case receive_trap(3500) of
792	PDU when is_record(PDU, trappdu) ->
793            ?IPRINT("received expected trap (~w)", [Id]),
794            ok;
795	{error, Reason} ->
796            %% We did not get the message we wanted,
797            %% but what did we get?
798            ?EPRINT("unexpected receive error: ~w"
799                    "~n   Reason:  ~p"
800                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
801            format_reason(Id, Reason)
802    end;
803
804expect_impl(Id, timeout) ->
805    ?IPRINT("await ~w nothing", [Id]),
806    receive
807	X ->
808            ?EPRINT("received unexpected message: ~w"
809                    "~n   ~p",
810                    [Id, X]),
811	    {error, Id, {"Timeout", []}, {"Message ~w",  [X]}}
812    after 3500 ->
813	    ok
814    end;
815
816expect_impl(Id, Err) when is_atom(Err) ->
817    ?IPRINT("await ~w with"
818            "~n   Err: ~p",
819            [Id, Err]),
820    case receive_response() of
821	#pdu{error_status = Err} ->
822            ?IPRINT("received pdu with expected error status (~w, ~w)",
823                    [Id, Err]),
824	    ok;
825
826	#pdu{type         = Type2,
827	     request_id   = ReqId,
828	     error_status = Err2} ->
829            ?EPRINT("received pdu with unexpected error status: ~w, ~w, ~w"
830                    "~n   Expected Error: ~p"
831                    "~n   Received Error: ~p",
832                    [Type2, Id, ReqId, Err, Err2]),
833	    {error, Id, {"ErrorStatus: ~w, RequestId: ~w", [Err,ReqId]},
834	     {"ErrorStatus: ~w", [Err2]}};
835
836	{error, Reason} ->
837            %% We did not get the message we wanted,
838            %% but what did we get?
839            ?EPRINT("unexpected receive error: ~w"
840                    "~n   Reason:  ~p"
841                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
842	    format_reason(Id, Reason)
843    end;
844
845expect_impl(Id, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
846    ?IPRINT("await ~w with"
847            "~n   ExpectedVarbinds: ~p",
848            [Id, ExpectedVarbinds]),
849    PureVars = find_pure_oids(ExpectedVarbinds),
850    case receive_response() of
851	#pdu{type         = 'get-response',
852	     error_status = noError,
853	     error_index  = 0,
854	     varbinds     = VBs} ->
855            ?IPRINT("received expected response pdu (~w) - check varbinds"
856                    "~n   Expected VBs: ~p"
857                    "~n   Received VBs: ~p",
858                    [Id, PureVars, VBs]),
859	    check_vars(Id, PureVars, VBs);
860
861	#pdu{type         = Type2,
862	     request_id   = ReqId,
863	     error_status = Err2,
864	     error_index  = Index2} ->
865            ?EPRINT("received unexpected pdu: ~w, ~w, ~w"
866                    "~n   Received Error: ~p"
867                    "~n   Received Index: ~p",
868                     [Type2, Id, ReqId, Err2, Index2]),
869	    {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
870			 ['get-response', noError, 0, ReqId]},
871	     {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
872
873	{error, Reason} ->
874            %% We did not get the message we wanted,
875            %% but what did we get?
876            ?EPRINT("unexpected receive error: ~w"
877                    "~n   Reason:  ~p"
878                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
879	    format_reason(Id, Reason)
880    end.
881
882expect_impl(Id, v2trap, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
883    ?IPRINT("await v2 trap ~w with"
884            "~n   ExpectedVarbinds: ~p",
885            [Id, ExpectedVarbinds]),
886    PureVars = find_pure_oids(ExpectedVarbinds),
887    case receive_response() of
888	#pdu{type         = 'snmpv2-trap',
889	     error_status = noError,
890	     error_index  = 0,
891	     varbinds     = VBs} ->
892            ?IPRINT("received expected v2 trap (~w) - check varbinds"
893                    "~n   Expected VBs: ~p"
894                    "~n   Received VBs: ~p",
895                    [Id, PureVars, VBs]),
896	    check_vars(Id, PureVars, VBs);
897
898	#pdu{type         = Type2,
899	     request_id   = ReqId,
900	     error_status = Err2,
901	     error_index  = Index2} ->
902            ?EPRINT("received unexpected pdu: ~w, ~w, ~w"
903                    "~n   Received Error: ~p"
904                    "~n   Received Index: ~p",
905                    [Type2, Id, ReqId, Err2, Index2]),
906	    {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
907			 ['snmpv2-trap', noError, 0, ReqId]},
908	     {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
909
910	{error, Reason} ->
911            %% We did not get the message we wanted,
912            %% but what did we get?
913            ?EPRINT("unexpected receive error: ~w"
914                    "~n   Reason:  ~p"
915                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
916	    format_reason(Id, Reason)
917    end;
918
919expect_impl(Id, report, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
920    ?IPRINT("await report ~w with"
921            "~n   ExpectedVarbinds: ~p",
922            [Id, ExpectedVarbinds]),
923    PureVBs = find_pure_oids(ExpectedVarbinds),
924    case receive_response() of
925	#pdu{type         = 'report',
926	     error_status = noError,
927	     error_index  = 0,
928	     varbinds     = VBs} ->
929            ?IPRINT("received expected report (~w) - check varbinds"
930                    "~n   Expected VBs: ~p"
931                    "~n   Received VBs: ~p",
932                    [Id, PureVBs, VBs]),
933	    check_vars(Id, PureVBs, VBs);
934
935	#pdu{type         = Type2,
936	     request_id   = ReqId,
937	     error_status = Err2,
938	     error_index  = Index2} ->
939            ?EPRINT("received unexpected pdu: ~w, ~w, ~w"
940                    "~n   Received Error: ~p"
941                    "~n   Received Index: ~p",
942                    [Type2, Id, ReqId, Err2, Index2]),
943	    {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
944			 [report, noError, 0, ReqId]},
945	     {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
946
947	{error, Reason} ->
948            %% We did not get the message we wanted,
949            %% but what did we get?
950            ?EPRINT("unexpected receive error: ~w"
951                    "~n   Reason:  ~p"
952                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
953	    format_reason(Id, Reason)
954    end;
955
956expect_impl(Id, {inform, Reply}, ExpectedVarbinds)
957  when is_list(ExpectedVarbinds) ->
958    ?IPRINT("await inform ~w with"
959            "~n   Reply:            ~p"
960            "~n   ExpectedVarbinds: ~p",
961            [Id, Reply, ExpectedVarbinds]),
962    PureVBs = find_pure_oids(ExpectedVarbinds),
963    Resp    = receive_response(),
964    case Resp of
965	#pdu{type         = 'inform-request',
966	     error_status = noError,
967	     error_index  = 0,
968	     varbinds     = VBs} ->
969            ?IPRINT("received inform (~w) - check varbinds"
970                    "~n   Expected VBs: ~p"
971                    "~n   Received VBs: ~p",
972                    [Id, PureVBs, VBs]),
973	    case check_vars(Id, PureVBs, VBs) of
974		ok when (Reply == true) ->
975                    ?IPRINT("varbinds ok (~w) - send ok inform response", [Id]),
976		    RespPDU = Resp#pdu{type = 'get-response',
977				       error_status = noError,
978				       error_index = 0},
979		    ?MODULE:rpl(RespPDU),
980		    ok;
981		ok when (element(1, Reply) == error) ->
982                    ?IPRINT("varbinds ok (~w) - send error inform response", [Id]),
983		    {error, Status, Index} = Reply,
984		    RespPDU = Resp#pdu{type = 'get-response',
985				       error_status = Status,
986				       error_index = Index},
987		    ?MODULE:rpl(RespPDU),
988		    ok;
989		ok when (Reply == false) ->
990                    ?IPRINT("varbinds ok (~w) - don't send inform response", [Id]),
991		    ok;
992		Else ->
993                    ?EPRINT("unexpected (inform) varbinds (~w):"
994                            "~n      VBs: ~p"
995                            "~n      ~p", [Id, VBs, Else]),
996		    Else
997	    end;
998
999	#pdu{type         = Type2,
1000	     request_id   = ReqId,
1001	     error_status = Err2,
1002	     error_index  = Index2} ->
1003            ?EPRINT("received unexpected pdu: ~w, ~w, ~w"
1004                    "~n   Received Error: ~p"
1005                    "~n   Received Index: ~p",
1006                    [Type2, Id, ReqId, Err2, Index2]),
1007	    {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
1008			 ['inform-request', noError, 0, ReqId]},
1009	     {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
1010
1011	{error, Reason} ->
1012            %% We did not get the message we wanted,
1013            %% but what did we get?
1014            ?EPRINT("unexpected receive error: ~w"
1015                    "~n   Reason:  ~p"
1016                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
1017	    format_reason(Id, Reason)
1018    end.
1019
1020expect_impl(Id, Err, Index, any = _ExpectedVarbinds) ->
1021    ?IPRINT("await response ~w with"
1022            "~n   Err:              ~p"
1023            "~n   Index:            ~p"
1024            "~n   ExpectedVarbinds: ~p",
1025            [Id, Err, Index, _ExpectedVarbinds]),
1026    case receive_response() of
1027	#pdu{type         = 'get-response',
1028	     error_status = Err,
1029	     error_index  = Index} ->
1030            ?IPRINT("received expected response pdu (~w, ~w, ~w)",
1031                    [Id, Err, Index]),
1032	    ok;
1033
1034	#pdu{type         = 'get-response',
1035             error_status = Err} when (Index == any) ->
1036            ?IPRINT("received expected response pdu (~w, ~w)",
1037                    [Id, Err]),
1038	    ok;
1039
1040	#pdu{type         = 'get-response',
1041	     request_id   = ReqId,
1042	     error_status = Err,
1043	     error_index  = Idx} when is_list(Index) ->
1044	    case lists:member(Idx, Index) of
1045		true ->
1046                    ?IPRINT("received expected response pdu (~w, ~w, ~w)",
1047                            [Id, Err, Idx]),
1048		    ok;
1049		false ->
1050                    ?EPRINT("received response pdu with unexpected index (~w, ~w):"
1051                            "~n   Expected Index: ~p"
1052                            "~n   Received Index: ~p",
1053                            [Id, Err, Index, Idx]),
1054		    {error, Id, {"ErrStat: ~w, Idx: ~w, RequestId: ~w",
1055				 [Err, Index, ReqId]},
1056		     {"ErrStat: ~w, Idx: ~w", [Err, Idx]}}
1057	    end;
1058
1059	#pdu{type         = Type2,
1060	     request_id   = ReqId,
1061	     error_status = Err2,
1062	     error_index  = Index2} ->
1063            ?EPRINT("received unexpected response pdu: ~w, ~w, ~w"
1064                    "~n   Expected Error: ~p"
1065                    "~n   Received Error: ~p"
1066                    "~n   Expected Index: ~p"
1067                    "~n   Received Index: ~p",
1068                    [Type2, Id, ReqId, Err, Err2, Index, Index2]),
1069	    {error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
1070			 ['get-response', Err, Index, ReqId]},
1071	     {"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
1072
1073	{error, Reason} ->
1074            %% We did not get the message we wanted,
1075            %% but what did we get?
1076            ?EPRINT("unexpected (receive) response: "
1077                    "~n   Reason:  ~p"
1078                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
1079	    format_reason(Id, Reason)
1080    end;
1081
1082expect_impl(Id, Err, Index, ExpectedVarbinds) ->
1083    ?IPRINT("await response ~w with"
1084            "~n   Err:              ~p"
1085            "~n   Index:            ~p"
1086            "~n   ExpectedVarbinds: ~p",
1087            [Id, Err, Index, ExpectedVarbinds]),
1088    PureVBs = find_pure_oids(ExpectedVarbinds),
1089    case receive_response() of
1090	#pdu{type         = 'get-response',
1091	     error_status = Err,
1092	     error_index  = Index,
1093	     varbinds     = VBs} ->
1094            ?IPRINT("received expected response pdu (~w, ~w, ~w) - check varbinds"
1095                    "~n   Expected VBs: ~p"
1096                    "~n   Received VBs: ~p",
1097                    [Id, Err, Index, PureVBs, VBs]),
1098	    check_vars(Id, PureVBs, VBs);
1099
1100	#pdu{type         = 'get-response',
1101	     error_status = Err,
1102	     varbinds     = VBs} when (Index == any) ->
1103            ?IPRINT("received expected response pdu (~w, ~w) - check varbinds"
1104                    "~n   Expected VBs: ~p"
1105                    "~n   Received VBs: ~p",
1106                    [Id, Err, PureVBs, VBs]),
1107	    check_vars(Id, PureVBs, VBs);
1108
1109	#pdu{type         = 'get-response',
1110	     request_id   = ReqId,
1111	     error_status = Err,
1112	     error_index  = Idx,
1113	     varbinds     = VBs} when is_list(Index) ->
1114	    case lists:member(Idx, Index) of
1115		true ->
1116                    ?IPRINT("received expected pdu (~w, ~w, ~w) - check varbinds"
1117                            "~n   Expected VBs: ~p"
1118                            "~n   Received VBs: ~p",
1119                            [Id, Err, Idx, PureVBs, VBs]),
1120		    check_vars(Id, PureVBs, VBs);
1121		false ->
1122                    ?EPRINT("received response pdu with unexpected index (~w, ~w):"
1123                            "~n   Expected Index: ~p"
1124                            "~n   Received Index: ~p"
1125                            "~n   Expected VBs:   ~p"
1126                            "~n   Received VBs:   ~p",
1127                            [Id, Err, Index, Idx, PureVBs, VBs]),
1128		    {error,Id,
1129		     {"ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
1130		      [Err,Index,PureVBs,ReqId]},
1131		     {"ErrStat: ~w, Idx: ~w, Varbinds: ~w",
1132		      [Err,Idx,VBs]}}
1133	    end;
1134
1135	#pdu{type         = Type2,
1136	     request_id   = ReqId,
1137	     error_status = Err2,
1138	     error_index  = Index2,
1139	     varbinds     = VBs} ->
1140            ?EPRINT("received unexpected response pdu: ~w, ~w, ~w"
1141                    "~n   Expected Error: ~p"
1142                    "~n   Received Error: ~p"
1143                    "~n   Expected Index: ~p"
1144                    "~n   Received Index: ~p"
1145                    "~n   Expected VBs:   ~p"
1146                    "~n   Received VBs:   ~p",
1147                    [Type2, Id, ReqId,
1148                     Err, Err2, Index, Index2, PureVBs, VBs]),
1149	    {error,Id,
1150	     {"Type: ~w, ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
1151	      ['get-response',Err,Index,PureVBs,ReqId]},
1152	     {"Type: ~w, ErrStat: ~w Idx: ~w Varbinds: ~w",
1153	      [Type2,Err2,Index2,VBs]}};
1154
1155	{error, Reason} ->
1156            %% We did not get the message we wanted,
1157            %% but what did we get?
1158            ?EPRINT("unexpected receive pdu error: ~w"
1159                    "~n   Reason:  ~p"
1160                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
1161	    format_reason(Id, Reason)
1162    end.
1163
1164expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
1165    ?IPRINT("await trap pdu ~w with"
1166            "~n   Enterprise:       ~p"
1167            "~n   Generic:          ~p"
1168            "~n   Specific:         ~p"
1169            "~n   ExpectedVarbinds: ~p",
1170            [Id, Enterp, Generic, Specific, ExpectedVarbinds]),
1171    PureE   = find_pure_oid(Enterp),
1172    PureVBs = find_pure_oids(ExpectedVarbinds),
1173    case receive_trap(3500) of
1174	#trappdu{enterprise    = PureE,
1175		 generic_trap  = Generic,
1176		 specific_trap = Specific,
1177		 varbinds      = VBs} ->
1178            ?IPRINT("received expected trap pdu - check varbinds"
1179                    "~n   Expected VBs: ~p"
1180                    "~n   Received VBs: ~p",
1181                    [PureVBs, VBs]),
1182	    check_vars(Id, PureVBs, VBs);
1183
1184	#trappdu{enterprise    = Ent2,
1185		 generic_trap  = G2,
1186		 specific_trap = Spec2,
1187		 varbinds      = VBs} ->
1188            ?EPRINT("received unexpected trap pdu: ~w"
1189                    "~n   Expected Enterprise: ~p"
1190                    "~n   Received Enterprise: ~p"
1191                    "~n   Expected Generic:    ~p"
1192                    "~n   Received Generic:    ~p"
1193                    "~n   Expected Specific:   ~p"
1194                    "~n   Received Specific:   ~p"
1195                    "~n   Expected VBs:        ~p"
1196                    "~n   Received VBs:        ~p",
1197                    [Id,
1198                     PureE, Ent2,
1199                     Generic, G2,
1200                     Specific, Spec2,
1201                     PureVBs, VBs]),
1202	    {error, Id,
1203	     {"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w",
1204	      [PureE, Generic, Specific, ExpectedVarbinds]},
1205	     {"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w",
1206	      [Ent2, G2, Spec2, VBs]}};
1207
1208	{error, Reason} ->
1209            %% We did not get the message we wanted,
1210            %% but what did we get?
1211            ?EPRINT("unexpected receive trap pdu error: ~w"
1212                    "~n   Reason:  ~p"
1213                    "~n   Msg Que: ~p", [Id, Reason, ?MQUEUE()]),
1214	    format_reason(Id, Reason)
1215    end.
1216
1217format_reason(Id, Reason) ->
1218    {error, Id, {"?", []}, {"~w", [Reason]}}.
1219
1220
1221%%----------------------------------------------------------------------
1222%% Args: Id, ExpectedVarbinds, GotVarbinds
1223%% Returns: ok
1224%% Fails: if not ok
1225%%----------------------------------------------------------------------
1226check_vars(_Id,[], []) ->
1227    ok;
1228check_vars(Id,Vars, []) ->
1229    {error, Id, {"More Varbinds (~w)", [Vars]}, {"Too few", []}};
1230check_vars(Id,[], Varbinds) ->
1231    {error,Id, {"Fewer Varbinds", []}, {"Too many (~w)", [Varbinds]}};
1232check_vars(Id,[{_XOid, any} | Vars], [#varbind{oid = _Oid} |Vbs]) ->
1233    check_vars(Id,Vars, Vbs);
1234check_vars(Id,[{Oid, Val} | Vars], [#varbind{oid = Oid, value = Val} |Vbs]) ->
1235    check_vars(Id,Vars, Vbs);
1236check_vars(Id,[{Oid, Val} | _], [#varbind{oid = Oid, value = Val2} |_]) ->
1237    {error, Id, {" Varbind: ~w = ~w", [Oid, Val]}, {"Value: ~w", [Val2]}};
1238check_vars(Id,[{Oid, _Val} | _], [#varbind{oid = Oid2, value = _Val2} |_]) ->
1239    {error, Id, {"Oid: ~w", [Oid]}, {"Oid: ~w", [Oid2]}}.
1240
1241match_vars(Id, [Oid|T], [#varbind{oid = Oid, value = Value} | Vbs], Res) ->
1242    match_vars(Id, T, Vbs, [Value | Res]);
1243match_vars(_Id, [], [], Res) ->
1244    {ok, lists:reverse(Res)};
1245match_vars(Id, [Oid | _], [#varbind{oid = Oid2}], _Res) ->
1246    {error, Id, {" Oid: ~w", [Oid]}, {"Oid2: ~w", [Oid2]}};
1247match_vars(Id, Vars, [], _Res) ->
1248    {error, Id, {"More Varbinds (~w)", [Vars]}, {"Too few", []}};
1249match_vars(Id, [], Varbinds, _Res) ->
1250    {error,Id, {"Fewer Varbinds", []}, {"Too many (~w)", [Varbinds]}}.
1251
1252
1253
1254find_pure_oids([]) -> [];
1255find_pure_oids([{XOid, Q}|T]) ->
1256    [{find_pure_oid(XOid), Q} | find_pure_oids(T)].
1257
1258find_pure_oids2([]) -> [];
1259find_pure_oids2([XOid|T]) ->
1260    [find_pure_oid(XOid) | find_pure_oids2(T)].
1261
1262
1263%%----------------------------------------------------------------------
1264%% Returns: Oid
1265%% Fails: malformed oids
1266%%----------------------------------------------------------------------
1267find_pure_oid(XOid) ->
1268    case gen_server:call(?MODULE, {find_pure_oid, XOid}, infinity) of
1269	{error, {Format, Data}} ->
1270	    ok = io:format(Format, Data),
1271	    exit(malformed_oid);
1272	Oid when is_list(Oid) -> Oid
1273    end.
1274
1275get_value(Opt, Opts, Default) ->
1276    case snmp_misc:assq(Opt,Opts) of
1277	{value, C} -> C;
1278	false -> Default
1279    end.
1280
1281
1282%%----------------------------------------------------------------------
1283
1284call(Req) ->
1285    call(Req, infinity).
1286
1287call(Req, To) ->
1288    gen_server:call(?SERVER, Req, To).
1289
1290cast(Msg) ->
1291    gen_server:cast(?SERVER, Msg).
1292
1293
1294%%----------------------------------------------------------------------
1295%% Debug
1296%%----------------------------------------------------------------------
1297
1298sizeOf(L) when is_list(L) ->
1299    length(lists:flatten(L));
1300sizeOf(B) when is_binary(B) ->
1301    size(B).
1302
1303d(F)    -> d(F, []).
1304d(F, A) -> d(get(debug), F, A).
1305
1306d(true, F, A) ->
1307    ?IPRINT(F, A);
1308d(_,_F,_A) ->
1309    ok.
1310
1311