1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2019. 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%% Purpose: Basic module for reading and verifying config files
22%%----------------------------------------------------------------------
23-module(snmp_conf).
24
25
26%% External exports
27%% Avoid warning for local function error/1 clashing with autoimported BIF.
28-compile({no_auto_import,[error/1]}).
29-export([read_files/2, no_gen/2, no_order/2, no_filter/1, keyorder/4]).
30-export([read/2, read/3]).
31
32%% Basic (type) check functions
33-export([check_mandatory/2,
34	 check_integer/1, check_integer/2,
35
36	 check_string/1, check_string/2,
37
38	 check_atom/2,
39
40	 check_timer/1,
41
42	 all_domains/0,
43	 check_domain/1,
44	 domain_to_name/1,
45	 all_tdomains/0,
46	 check_tdomain/1,
47	 mk_tdomain/0, mk_tdomain/1,
48	 tdomain_to_family/1, tdomain_to_domain/1,
49	 which_domain/1,
50	 mk_addr_string/1,
51	 check_ip/1, check_ip/2,
52	 check_port/1,
53%%	 ip_port_to_domaddr/2,
54	 check_address/2, check_address/3,
55	 check_taddress/2,
56	 mk_taddress/1, mk_taddress/2,
57
58	 check_packet_size/1,
59
60	 check_oid/1,
61	 check_imask/1, check_emask/1,
62
63	 check_mp_model/1,
64	 check_sec_model/1, check_sec_model/2, check_sec_model/3,
65	 check_sec_level/1,
66
67	 all_integer/1
68	]).
69
70
71-define(SNMP_USE_V3, true).
72-include_lib("snmp/include/snmp_types.hrl").
73-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl").
74-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl").
75-include_lib("snmp/include/SNMPv2-TM.hrl").
76
77-define(VMODULE,"CONF").
78-include("snmp_verbosity.hrl").
79
80
81-define(is_word(P), (((P) band (bnot 65535)) =:= 0)).
82-define(is_word(P0, P1), ((((P0) bor (P1)) band (bnot 255)) =:= 0)).
83
84mk_word(B0, B1) -> ((B0) bsl 8) bor (B1).
85mk_bytes(W) -> [(W) bsr 8,(W) band 255].
86
87-define(
88   is_ipv4_addr(A0, A1, A2, A3),
89   ((((A0) bor (A1) bor (A2) bor (A3)) band (bnot 255)) =:= 0)).
90
91-define(
92   is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
93   ((((A0) bor (A1) bor (A2) bor (A3) bor (A4) bor (A5) bor (A6) bor (A7))
94	 band (bnot 65535)) =:= 0)).
95-define(
96   is_ipv6_addr(
97     A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15),
98   ((((A0) bor (A1) bor (A2) bor (A3) bor
99	  (A4) bor (A5) bor (A6) bor (A7) bor
100	  (A8) bor (A9) bor (A10) bor (A11) bor
101	  (A12) bor (A13) bor (A14) bor (A15))
102	 band (bnot 65535)) =:= 0)).
103
104
105%%-----------------------------------------------------------------
106
107%% read_files(Dir, Files) -> Configs
108%% Dir      - string()   - Full path to the config dir.
109%% Files    - [{FileName, Gen, Order, Check, Filter}]
110%% FileName - string()   - Name of the config file.
111%% Gen      - function/2 - In case of failure when reading the config file,
112%%                         this function is called to either generate a
113%%                         default file or issue the error.
114%%                         Returns a generated config list corresponding
115%%                         to the written file.
116%%                         (Dir, Error) -> Configs.
117%% Order    - function/2 - An ordering function that is used to process
118%%                         the read config entries using lists:sort/2.
119%%                         Returns true if arg 1 compares less than or
120%%                         equal to arg 2, false otherwise.
121%% Check    - function/2 - Check each entry as they are read from the file.
122%%                         (Entry, State) ->
123%%                             {ok,NewState} | {{ok,NewEntry},NewState} |
124%%                             throw(Error)
125%%                         State =:= 'undefined' the first time.
126%% Filter   - function/1 - Process all the config entries read from the file
127%%                         (Configs) -> [config_entry()].
128%% Configs  - [config_entry()]
129%% config_entry() - term()
130
131read_files(Dir, Files) when is_list(Dir) andalso is_list(Files) ->
132    read_files(Dir, Files, []).
133
134read_files(_Dir, [], Res) ->
135    lists:reverse(Res);
136read_files(Dir, [{FileName, Gen, Order, Check, Filter}|Files], Res)
137  when is_list(FileName),
138       is_function(Gen),
139       is_function(Order),
140       is_function(Check),
141       is_function(Filter) ->
142    ?vdebug("read_files -> entry with~n"
143	    "   FileName: ~p", [FileName]),
144    File = filename:join(Dir, FileName),
145    Confs =
146	case file:read_file_info(File) of
147	    {ok,_} ->
148		read(File, Order, Check);
149	    {error, R} ->
150		?vlog("failed reading file info for ~s: ~n"
151		      "   ~p", [FileName, R]),
152		Gen(Dir, R)
153	end,
154    read_files(Dir, Files, [Filter(Confs)|Res]).
155
156
157
158no_gen(_Dir, _R) -> [].
159no_order(_, _) -> true.
160no_filter(X) -> X.
161
162%% Order tuples on element N with Keys first in appearence order.
163%%
164%% An ordering function (A, B) shall return true iff
165%% A is less than or equal to B i.e shall return
166%% false iff A is to be ordered after B.
167
168-spec keyorder(N, A, B, Keys) ->
169                      boolean() when
170      N    :: integer(),
171      A    :: tuple(),
172      B    :: tuple(),
173      Keys :: maybe_improper_list().
174
175keyorder(N, A, B, _) when element(N, A) == element(N, B) ->
176    true;
177keyorder(N, A, B, [Key | _])
178  when tuple_size(A) >= 1, element(N, B) == Key ->
179    false;
180keyorder(N, A, B, [Key | _])
181  when element(N, A) == Key, tuple_size(B) >= 1 ->
182    true;
183keyorder(N, A, B, [_ | Keys]) ->
184    keyorder(N, A, B, Keys);
185keyorder(_, A, B, []) when tuple_size(A) >= 1, tuple_size(B) >= 1 ->
186    %% Do not order other keys
187    true;
188keyorder(N, A, B, sort) ->
189    %% Order other keys according to standard sort order
190    element(N, A) =< element(N, B).
191
192
193read(File, Verify) ->
194    Check = fun (Row, State) -> {Verify(Row), State} end,
195    read(File, fun no_order/2, Check).
196
197%% Ret. Res | exit(Reason)
198read(File, Order, Check) when is_function(Order), is_function(Check) ->
199    ?vdebug("read -> entry with~n"
200	"   File: ~p", [File]),
201    Fd    = open_file(File),
202    Lines = read_fd(File, Order, Check, Fd, 1, []),
203    file:close(Fd),
204    Lines.
205
206read_fd(File, Order, Check, Fd, StartLine, Res) ->
207    case do_read(Fd, "", StartLine) of
208	{ok, Row, EndLine} ->
209	    ?vtrace("read_fd ->~n"
210		    "   Row:     ~p~n"
211		    "   EndLine: ~p", [Row,EndLine]),
212	    read_fd(
213	      File, Order, Check, Fd, EndLine,
214	      [{StartLine, Row, EndLine}|Res]);
215	{error, Error, EndLine} ->
216	    ?vtrace("read_fd -> read failure:~n"
217		    "   Error: ~p~n"
218		    "   EndLine: ~p", [Error,EndLine]),
219	    file:close(Fd),
220	    error({failed_reading, File, StartLine, EndLine, Error});
221	{eof, _EndLine} ->
222	    Lines =
223		lists:sort(
224		  fun ({_, RowA, _}, {_, RowB, _}) ->
225			  Order(RowA, RowB)
226		  end,
227		  lists:reverse(Res)),
228	    ?vtrace("read_fd to read_check ->~n"
229		    "   Lines: ~p", [Lines]),
230	    read_check(File, Check, Lines, undefined, [])
231    end.
232
233read_check(_, _, [], _, Res) ->
234    lists:reverse(Res);
235read_check(File, Check, [{StartLine, Row, EndLine}|Lines], State, Res) ->
236    try Check(Row, State) of
237	{Rows, NewState} when is_list(Rows) ->
238	    ?vtrace("read_check -> ok:~n"
239		    "   Rows: ~p~n", [Rows]),
240	    read_check(File, Check, Lines, NewState, Rows ++ Res);
241	{ok, NewState} ->
242	    ?vtrace("read_check -> ok", []),
243	    read_check(File, Check, Lines, NewState, [Row | Res]);
244	{{ok, NewRow}, NewState} ->
245	    ?vtrace("read_check -> ok:~n"
246		    "   NewRow: ~p~n", [NewRow]),
247	    read_check(File, Check, Lines, NewState, [NewRow | Res])
248    catch
249	throw:{error, Reason} ->
250	    ?vtrace("read_check -> error:"
251		    "~n   Reason: ~p", [Reason]),
252	    error({failed_check, File, StartLine, EndLine, Reason});
253	C:E:S ->
254	    ?vtrace("read_check -> failure:"
255                    "~n   Class: ~p"
256		    "~n   Error: ~p"
257		    "~n   Stack: ~p", [C, E, S]),
258	    error({failed_check, File, StartLine, EndLine, {C, E, S}})
259    end.
260
261open_file(File) ->
262    case file:open(File, [read]) of
263	{ok, Fd} ->
264	    Fd;
265	{error, Reason} ->
266	    error({failed_open, File, Reason})
267    end.
268
269do_read(Io, Prompt, StartLine) ->
270    case io:request(Io, {get_until,Prompt,erl_scan,tokens,[StartLine]}) of
271	{ok, Toks, EndLine} ->
272	    case erl_parse:parse_term(Toks) of
273		{ok, Term} ->
274		    {ok, Term, EndLine};
275		{error, {Line, erl_parse, Error}} ->
276		    {error, {parse_error, Error}, Line}
277	    end;
278	Other ->
279	    Other
280    end.
281
282
283
284%%-----------------------------------------------------------------
285
286
287check_mandatory(L, [{Key, Value}|T]) ->
288    case lists:keymember(Key, 1, L) of
289	true ->
290	    check_mandatory(L, T);
291	false when Value == mandatory ->
292	    error({missing_mandatory, Key});
293	false ->
294	    {value, V} = Value,
295	    check_mandatory([{Key, V} | L], T)
296    end;
297check_mandatory(L, []) ->
298    {ok, L}.
299
300
301%% ---------
302
303check_integer(I) -> check_integer(I, any).
304
305check_integer(I, any) when is_integer(I) -> ok;
306check_integer(I, pos) when is_integer(I), I > 0 -> ok;
307check_integer(I, neg) when is_integer(I), I < 0 -> ok;
308check_integer(I1, {gt,  I2})
309  when is_integer(I1) andalso is_integer(I2) andalso (I1 > I2) -> ok;
310check_integer(I1, {gte, I2})
311  when is_integer(I1) andalso is_integer(I2) andalso (I1 >= I2) -> ok;
312check_integer(I1, {lt,  I2})
313  when is_integer(I1) andalso is_integer(I2) andalso (I1 < I2) -> ok;
314check_integer(I1, {lte, I2})
315  when is_integer(I1) andalso is_integer(I2) andalso (I1 =< I2) -> ok;
316check_integer(I1, {eq,  I1})
317  when is_integer(I1) -> ok;
318check_integer(I,  {range, L, U})
319  when (is_integer(I) andalso
320	is_integer(L) andalso
321	is_integer(U) andalso
322	(I >= L) andalso (I =< U)) -> ok;
323check_integer(I, _)  -> error({invalid_integer, I}).
324
325check_packet_size(S) ->
326    case (catch check_integer(S, {range, 484, 2147483647})) of
327	ok ->
328	    ok;
329	{error, _} ->
330	    error({invalid_packet_size, S})
331    end.
332
333%% ---------
334
335check_string(X) when is_list(X) -> ok;
336check_string(X) -> error({invalid_string, X}).
337
338check_string(X, any)
339  when is_list(X) -> ok;
340check_string(X, {gt, Len})
341  when is_list(X) andalso (length(X) > Len) -> ok;
342check_string(X, {gt, _Len})
343  when is_list(X) -> error({invalid_length, X});
344check_string(X, {gte, Len})
345  when is_list(X) andalso (length(X) >= Len) -> ok;
346check_string(X, {gte, _Len})
347  when is_list(X) -> error({invalid_length, X});
348check_string(X, {lt, Len})
349  when is_list(X) andalso (length(X) < Len) -> ok;
350check_string(X, {lt, _Len})
351  when is_list(X) -> error({invalid_length, X});
352check_string(X, {lte, Len})
353  when is_list(X) andalso (length(X) =< Len) -> ok;
354check_string(X, {lte, _Len})
355  when is_list(X) -> error({invalid_length, X});
356check_string(X, Len)
357  when is_list(X) andalso is_integer(Len) andalso (length(X) =:= Len) -> ok;
358check_string(X, _Len)      when is_list(X) -> error({invalid_length, X});
359check_string(X, _Len)                   -> error({invalid_string, X}).
360
361
362check_atom(X, Atoms) ->
363    case lists:keysearch(X, 1, Atoms) of
364        {value, {X, Val}} ->
365	    {ok, Val};
366        _ ->
367	    error({invalid_atom, X, Atoms})
368    end.
369
370
371%% ---------
372
373check_mp_model(MPModel) when is_atom(MPModel) ->
374    All = [{v1,  ?MP_V1}, {v2c, ?MP_V2C}, {v3,  ?MP_V3}],
375    check_atom(MPModel, All);
376check_mp_model(?MP_V1) ->
377    {ok, ?MP_V1};
378check_mp_model(?MP_V2C) ->
379    {ok, ?MP_V2C};
380check_mp_model(?MP_V3) ->
381    {ok, ?MP_V3};
382check_mp_model(BadMpModel) ->
383    error({invalid_mp_model, BadMpModel}).
384
385
386%% ---------
387
388check_sec_model(SecModel) when is_atom(SecModel) ->
389    check_sec_model(SecModel, []);
390check_sec_model(?SEC_ANY) ->
391    {ok, ?SEC_ANY};
392check_sec_model(?SEC_V1) ->
393    {ok, ?SEC_V1};
394check_sec_model(?SEC_V2C) ->
395    {ok, ?SEC_V2C};
396check_sec_model(?SEC_USM) ->
397    {ok, ?SEC_USM};
398check_sec_model(BadSecModel) ->
399    error({invalid_sec_model, BadSecModel}).
400
401check_sec_model(SecModel, Exclude) when is_atom(SecModel) ->
402    All = [{any, ?SEC_ANY},
403           {v1,  ?SEC_V1},
404           {v2c, ?SEC_V2C},
405           {usm, ?SEC_USM}],
406    Alt = [{X, Y} || {X, Y} <- All, not lists:member(X, Exclude)],
407    case (catch check_atom(SecModel, Alt) ) of
408	{error, _} ->
409	    error({invalid_sec_model, SecModel});
410	OK ->
411	    OK
412    end;
413check_sec_model(BadSecModel, _Exclude) ->
414    error({invalid_sec_model, BadSecModel}).
415
416check_sec_model(v1, v1, Exclude) ->
417    check_sec_model2(v1, ?SEC_V1, Exclude);
418check_sec_model(v1, SecModel, _Exclude) ->
419    error({invalid_sec_model, v1, SecModel});
420check_sec_model(v2c, v2c, Exclude) ->
421    check_sec_model2(v2c, ?SEC_V2C, Exclude);
422check_sec_model(v2c, SecModel, _Exclude) ->
423    error({invalid_sec_model, v2c, SecModel});
424check_sec_model(v3, usm, Exclude) ->
425    check_sec_model2(v3, ?SEC_USM, Exclude);
426check_sec_model(v3, SecModel, _Exclude) ->
427    error({invalid_sec_model, v3, SecModel});
428check_sec_model(M1, M2, _Exclude) ->
429    error({invalid_sec_model, M1, M2}).
430
431check_sec_model2(SecModel, SM, Exclude) ->
432    case lists:member(SecModel, Exclude) of
433	false ->
434	    {ok, SM};
435	true ->
436	    error({invalid_sec_model, SecModel})
437    end.
438
439
440%% ---------
441
442check_sec_level(SecLevel) when is_atom(SecLevel) ->
443    All = [{noAuthNoPriv, ?'SnmpSecurityLevel_noAuthNoPriv'},
444	   {authNoPriv,   ?'SnmpSecurityLevel_authNoPriv'},
445	   {authPriv,     ?'SnmpSecurityLevel_authPriv'}],
446    case (catch check_atom(SecLevel, All)) of
447	{error, _} ->
448	    error({invalid_sec_level, SecLevel});
449	OK ->
450	    OK
451    end;
452check_sec_level(?'SnmpSecurityLevel_noAuthNoPriv' = SL) ->
453    {ok, SL};
454check_sec_level(?'SnmpSecurityLevel_authNoPriv' = SL) ->
455    {ok, SL};
456check_sec_level(?'SnmpSecurityLevel_authPriv' = SL) ->
457    {ok, SL};
458check_sec_level(BadSecLevel) ->
459    error({invalid_sec_level, BadSecLevel}).
460
461
462%% ---------
463all_tdomains() ->
464    [
465     ?transportDomainUdpIpv4,
466     ?transportDomainUdpIpv6,
467     ?transportDomainUdpIpv4z,
468     ?transportDomainUdpIpv6z,
469     ?transportDomainTcpIpv4,
470     ?transportDomainTcpIpv6,
471     ?transportDomainTcpIpv4z,
472     ?transportDomainTcpIpv6z,
473     ?transportDomainSctpIpv4,
474     ?transportDomainSctpIpv6,
475     ?transportDomainSctpIpv4z,
476     ?transportDomainSctpIpv6z,
477     ?transportDomainLocal,
478     ?transportDomainUdpDns,
479     ?transportDomainTcpDns,
480     ?transportDomainSctpDns
481    ].
482
483check_tdomain(TDomain) ->
484    SupportedTDomains =
485	[
486	 ?snmpUDPDomain, % Legacy
487	 ?transportDomainUdpIpv4,
488	 ?transportDomainUdpIpv6
489	],
490    AllTDomains = all_tdomains(),
491    case lists:member(TDomain, SupportedTDomains) of
492	true ->
493	    ok;
494	false ->
495	    case lists:member(TDomain, AllTDomains) of
496		true ->
497		    error({unsupported_tdomain, TDomain});
498		false ->
499		    error({unknown_tdomain, TDomain})
500	    end
501    end.
502
503
504%% ---------
505
506mk_tdomain() ->
507    mk_tdomain(snmpUDPDomain).
508
509mk_tdomain(snmpUDPDomain) ->
510    mk_tdomain(transportDomainUdpIpv4);
511mk_tdomain(transportDomainUdpIpv4) ->
512    ?transportDomainUdpIpv4;
513mk_tdomain(transportDomainUdpIpv6) ->
514    ?transportDomainUdpIpv6;
515mk_tdomain(BadDomain) ->
516    error({bad_domain, BadDomain}).
517
518
519%% ---------
520
521tdomain_to_family(snmpUDPDomain) ->
522    inet;
523tdomain_to_family(transportDomainUdpIpv4) ->
524    inet;
525tdomain_to_family(transportDomainUdpIpv6) ->
526    inet6;
527tdomain_to_family(?snmpUDPDomain) ->
528    inet;
529tdomain_to_family(?transportDomainUdpIpv4) ->
530    inet;
531tdomain_to_family(?transportDomainUdpIpv6) ->
532    inet6;
533tdomain_to_family(BadDomain) ->
534    error({bad_domain, BadDomain}).
535
536
537%% ---------
538
539tdomain_to_domain(?snmpUDPDomain) ->
540    snmpUDPDomain;
541tdomain_to_domain(?transportDomainUdpIpv4) ->
542    transportDomainUdpIpv4;
543tdomain_to_domain(?transportDomainUdpIpv6) ->
544    transportDomainUdpIpv6;
545tdomain_to_domain(BadTDomain) ->
546    error({bad_tdomain, BadTDomain}).
547
548
549%% ---------
550
551check_taddress(?snmpUDPDomain, X) ->
552    check_taddress(transportDomainUdpIpv4, X);
553check_taddress(snmpUDPDomain, X) ->
554    check_taddress(transportDomainUdpIpv4, X);
555%%
556check_taddress(?transportDomainUdpIpv4, X) ->
557    check_taddress(transportDomainUdpIpv4, X);
558check_taddress(transportDomainUdpIpv4, X) ->
559    case X of
560	[A0,A1,A2,A3,P0,P1]
561	  when ?is_ipv4_addr(A0, A1, A2, A3), ?is_word(P0, P1) ->
562	    ok;
563	_ ->
564	    error({invalid_taddress, X})
565    end;
566%%
567check_taddress(?transportDomainUdpIpv6, X) ->
568    check_taddress(transportDomainUdpIpv6, X);
569check_taddress(transportDomainUdpIpv6, X) ->
570    case X of
571	[A0,A1,A2,A3,A4,A5,A6,A7,P0,P1]
572	when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
573	     ?is_word(P0, P1) ->
574	    ok;
575	_ ->
576	    error({invalid_taddress, X})
577    end;
578%%
579check_taddress(BadDomain, _X) ->
580    error({invalid_tdomain, BadDomain}).
581
582
583
584%% ---------
585
586check_timer(infinity) ->
587    {ok, infinity};
588check_timer(T) when is_record(T, snmp_incr_timer) ->
589    {ok, T};
590check_timer({WaitFor, Factor, Incr, Retry} = T) ->
591    case (catch do_check_timer(WaitFor, Factor, Incr, Retry)) of
592	ok ->
593	    {ok, #snmp_incr_timer{wait_for    = WaitFor,
594				  factor      = Factor,
595				  incr        = Incr,
596				  max_retries = Retry}};
597	_Err ->
598	    error({invalid_timer, T})
599    end;
600check_timer(Timeout) ->
601    case (catch check_integer(Timeout, {gt, 0})) of
602	ok ->
603	    {ok, #snmp_incr_timer{wait_for    = Timeout,
604				  factor      = 1,
605				  incr        = 0,
606				  max_retries = 0}};
607	_Err ->
608	    error({invalid_timer, Timeout})
609    end.
610
611do_check_timer(WaitFor, Factor, Incr, Retry) ->
612    check_integer(WaitFor, {gt,  0}),
613    check_integer(Factor,  {gt,  0}),
614    check_integer(Incr,    {gte, 0}),
615    check_integer(Retry,   {gte, 0}),
616    ok.
617
618
619%% ---------
620
621all_domains() ->
622    [
623     transportDomainUdpIpv4,
624     transportDomainUdpIpv6,
625     transportDomainUdpIpv4z,
626     transportDomainUdpIpv6z,
627     transportDomainTcpIpv4,
628     transportDomainTcpIpv6,
629     transportDomainTcpIpv4z,
630     transportDomainTcpIpv6z,
631     transportDomainSctpIpv4,
632     transportDomainSctpIpv6,
633     transportDomainSctpIpv4z,
634     transportDomainSctpIpv6z,
635     transportDomainLocal,
636     transportDomainUdpDns,
637     transportDomainTcpDns,
638     transportDomainSctpDns
639    ].
640
641check_domain(snmpUDPDomain) -> ok;
642check_domain(transportDomainUdpIpv4) -> ok;
643check_domain(transportDomainUdpIpv6) -> ok;
644check_domain(Domain) ->
645    case lists:member(Domain, all_domains()) of
646	true ->
647	    error({unsupported_domain, Domain});
648	false ->
649	    error({unknown_domain, Domain})
650    end.
651
652domain_to_name(snmpUDPDomain) ->
653    undefined;
654domain_to_name(transportDomainUdpIpv4) ->
655    udpIpv4;
656domain_to_name(transportDomainUdpIpv6) ->
657    udpIpv6;
658domain_to_name(transportDomainUdpIpv4z) ->
659    udpIpv4z;
660domain_to_name(transportDomainUdpIpv6z) ->
661    udpIpv6z;
662domain_to_name(transportDomainTcpIpv4) ->
663    tcpIpv4;
664domain_to_name(transportDomainTcpIpv6) ->
665    tcpIpv6;
666domain_to_name(transportDomainTcpIpv4z) ->
667    tcpIpv4z;
668domain_to_name(transportDomainTcpIpv6z) ->
669    tcpIpv6z;
670domain_to_name(transportDomainSctpIpv4) ->
671    sctpIpv4;
672domain_to_name(transportDomainSctpIpv6) ->
673    sctpIpv6;
674domain_to_name(transportDomainSctpIpv4z) ->
675    sctpIpv4z;
676domain_to_name(transportDomainSctpIpv6z) ->
677    sctpIpv6z;
678domain_to_name(transportDomainLocal) ->
679    local;
680domain_to_name(transportDomainUdpDns) ->
681    udpDns;
682domain_to_name(transportDomainTcpDns) ->
683    tcpDns;
684domain_to_name(transportDomainSctpDns) ->
685    sctpDns;
686domain_to_name(BadDomain) ->
687    error({bad_domain, BadDomain}).
688
689%% ---------
690
691mk_taddress(Address) ->
692    mk_taddress(snmpUDPDomain, Address).
693
694%% The values of Domain, Ip and Port has both been checked at this
695%% point, so we dont need to do that again, but this function is
696%% also used on incoming packets from net_if so a little
697%% check that net_if does not supply bad arguments is in order.
698%%
699%% These are just for convenience
700mk_taddress(?snmpUDPDomain, Address) ->
701    mk_taddress(snmpUDPDomain, Address);
702mk_taddress(?transportDomainUdpIpv4, Address) ->
703    mk_taddress(transportDomainUdpIpv4, Address);
704mk_taddress(?transportDomainUdpIpv6, Address) ->
705    mk_taddress(transportDomainUdpIpv6, Address);
706%%
707mk_taddress(snmpUDPDomain, Address) -> % Legacy
708    mk_taddress(transportDomainUdpIpv4, Address);
709mk_taddress(transportDomainUdpIpv4 = Domain, Address) ->
710    case Address of
711	[] -> % Empty mask
712	    [];
713	{Ip, Port} when tuple_size(Ip) =:= 4, is_integer(Port) ->
714	    tuple_to_list(Ip) ++ mk_bytes(Port);
715	_ ->
716	    erlang:error(badarg, [Domain,Address])
717    end;
718mk_taddress(transportDomainUdpIpv6 = Domain, Address) ->
719    case Address of
720	[] -> % Empty mask
721	    [];
722	{{A, B, C, D, E, F, G, H}, Port} ->
723	    [A bsr 8, A band 255,
724	     B bsr 8, B band 255,
725	     C bsr 8, C band 255,
726	     D bsr 8, D band 255,
727	     E bsr 8, E band 255,
728	     F bsr 8, F band 255,
729	     G bsr 8, G band 255,
730	     H bsr 8, H band 255,
731	     Port bsr 8, Port band 255];
732	_ ->
733	    erlang:error(badarg, [Domain,Address])
734    end;
735%% Bad domain
736mk_taddress(BadDomain, _) ->
737    error({bad_domain, BadDomain}).
738
739
740%% ---------
741
742%% XXX remove, when net_if handles one socket per transport domain
743
744which_domain([A0,A1,A2,A3])
745  when ?is_ipv4_addr(A0, A1, A2, A3) ->
746    transportDomainUdpIpv4;
747which_domain({A0, A1, A2, A3})
748  when ?is_ipv4_addr(A0, A1, A2, A3) ->
749    transportDomainUdpIpv4;
750which_domain([A0,A1,A2,A3,A4,A5,A6,A7])
751  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7) ->
752    transportDomainUdpIpv6;
753which_domain([A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15])
754  when ?is_ipv6_addr(
755	  A0, A1, A2, A3, A4, A5, A6, A7,
756	  A8, A9, A10, A11, A12, A13, A14, A15) ->
757    transportDomainUdpIpv6;
758which_domain({A0, A1, A2, A3, A4, A5, A6, A7})
759  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7) ->
760    transportDomainUdpIpv6.
761
762%% ---------
763
764mk_addr_string({Domain, Addr}) when is_atom(Domain) ->
765    %% XXX There is only code for IP domains here
766    case check_address_ip(Domain, Addr) of
767	false ->
768	    case check_address_ip_port(Domain, Addr) of
769		false ->
770		    error({bad_address, {Domain, Addr}});
771		true ->
772		    {IP, Port} = Addr,
773		    mk_addr_string_ntoa(Domain, IP, Port);
774		{IP, Port} ->
775		    mk_addr_string_ntoa(Domain, IP, Port)
776	    end;
777	true ->
778	    mk_addr_string_ntoa(Domain, Addr);
779	IP ->
780	    mk_addr_string_ntoa(Domain, IP)
781    end;
782mk_addr_string({_IP, Port} = Addr) when is_integer(Port) ->
783    mk_addr_string({snmpUDPDomain, Addr});
784mk_addr_string(Strange) ->
785    lists:flatten(io_lib:format("~w", [Strange])).
786
787
788mk_addr_string_ntoa({_, _, _, _} = IP) ->
789    inet:ntoa(IP);
790mk_addr_string_ntoa(IP) ->
791    lists:flatten(io_lib:format("[~s]", [inet:ntoa(IP)])).
792
793mk_addr_string_ntoa(Domain, IP) ->
794    case domain_to_name(Domain) of
795	undefined ->
796	    mk_addr_string_ntoa(IP);
797	Name ->
798	    lists:flatten(
799	      io_lib:format("~w://~s", [Name, mk_addr_string_ntoa(IP)]))
800    end.
801
802mk_addr_string_ntoa(Domain, IP, Port) ->
803    lists:flatten(
804      io_lib:format(
805	"~s:~w", [mk_addr_string_ntoa(Domain, IP), Port])).
806
807%% ---------
808
809check_ip(X) ->
810    check_ip(snmpUDPDomain, X).
811
812check_ip(Domain, IP) ->
813    %% XXX There is only code for IP domains here
814    case check_address_ip(Domain, IP) of
815	false ->
816	    error({bad_address, {Domain, IP}});
817	true ->
818	    ok;
819	FixedIP ->
820	    {ok, FixedIP}
821    end.
822
823
824%% ---------
825
826check_port(Port) when ?is_word(Port) ->
827    ok;
828check_port(Port) ->
829    error({bad_port, Port}).
830
831%% ip_port_to_domaddr(IP, Port) when ?is_word(Port) ->
832%%     %% XXX There is only code for IP domains here
833%%     case check_address_ip(transportDomainUdpIpv4, IP) of
834%% 	false ->
835%% 	    case check_address_ip(transportDomainUdpIpv6, IP) of
836%% 		false ->
837%% 		    error({bad_address, {transportDomainUdpIpv4, {IP, Port}}});
838%% 		true ->
839%% 		    {transportDomainUdpIpv6, {IP, Port}};
840%% 		FixedIP ->
841%% 		    {transportDomainUdpIpv6, {FixedIP, Port}}
842%% 	    end;
843%% 	true ->
844%% 	    {transportDomainUdpIpv4, {IP, Port}};
845%% 	FixedIP ->
846%% 	    {transportDomainUdpIpv4, {FixedIP, Port}}
847%%     end;
848%% ip_port_to_domaddr(IP, Port) ->
849%%     error({bad_address, {transportDomainUdpIpv4, {IP, Port}}}).
850
851%% Check a configuration term field from a file to see if it
852%% can be fixed to be fed to mk_taddress/2.
853
854check_address(Domain, Address, DefaultPort) ->
855    %% If Address does not contain Port or contains Port =:= 0
856    %% create an address containing DefaultPort
857    %%
858    %% XXX There is only code for IP domains here
859    case check_address_ip(Domain, Address) of
860	false ->
861	    case check_address_ip_port(Domain, Address) of
862		false ->
863		    error({bad_address, {Domain, Address}});
864		true ->
865		    case Address of
866			{IP, 0} ->
867			    {ok, {IP, DefaultPort}};
868			_ ->
869			    ok
870		    end;
871		{FixedIP, 0} ->
872		    {ok, {FixedIP, DefaultPort}};
873		FixedAddress ->
874		    {ok, FixedAddress}
875	    end;
876	true ->
877	    {ok, {Address, DefaultPort}};
878	FixedIP ->
879	    {ok, {FixedIP, DefaultPort}}
880    end.
881
882check_address(Domain, Address) ->
883    %% Address has to contain Port
884    %%
885    %% XXX There is only code for IP domains here
886    case check_address_ip_port(Domain, Address) of
887	false ->
888	    error({bad_address, {Domain, Address}});
889	true ->
890	    ok;
891	FixedAddress ->
892	    {ok, FixedAddress}
893    end.
894
895%% -> IP
896check_address_ip(Domain, Address)
897  when Domain =:= snmpUDPDomain;
898       Domain =:= transportDomainUdpIpv4 ->
899    case Address of
900	%% Erlang native format
901	{A0, A1, A2, A3}
902	  when ?is_ipv4_addr(A0, A1, A2, A3) ->
903	    true;
904	%% Erlangish format
905	[A0,A1,A2,A3]
906	  when ?is_ipv4_addr(A0, A1, A2, A3) ->
907	    {A0, A1, A2, A3};
908	_ ->
909	    false
910    end;
911check_address_ip(transportDomainUdpIpv6, Address) ->
912    case Address of
913	%% Erlang native format
914	{A0, A1, A2, A3, A4, A5, A6, A7}
915	  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7) ->
916	    true;
917	%% Erlangish format
918	[A0,A1,A2,A3,A4,A5,A6,A7]
919	  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7) ->
920	    {A0, A1, A2, A3, A4, A5, A6, A7};
921	%% SNMP standards format
922	[A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15]
923	  when ?is_ipv6_addr(
924		  A0, A1, A2, A3, A4, A5, A6, A7,
925		  A8, A9, A10, A11, A12, A13, A14, A15) ->
926	    {mk_word(A0, A1), mk_word(A2, A3),
927	     mk_word(A4, A5), mk_word(A6, A7),
928	     mk_word(A8, A9), mk_word(A10, A11),
929	     mk_word(A12, A13), mk_word(A14, A15)};
930	_ ->
931	    false
932    end;
933check_address_ip(BadDomain, _) ->
934    error({bad_domain, BadDomain}).
935
936%% -> {IP, Port}
937check_address_ip_port(Domain, Address)
938  when Domain =:= snmpUDPDomain;
939       Domain =:= transportDomainUdpIpv4 ->
940    case Address of
941	{IP, Port} when ?is_word(Port) ->
942	    case check_address_ip(Domain, IP) of
943		false ->
944		    false;
945		true ->
946		    true;
947		FixedIP ->
948		    {FixedIP, Port}
949	    end;
950	%% SNMP standards format
951	[A0,A1,A2,A3,P0,P1]
952	  when ?is_ipv4_addr(A0, A1, A2, A3), ?is_word(P0, P1) ->
953	    {{A0, A1, A2, A3}, mk_word(P0, P1)};
954	_ ->
955	    false
956    end;
957check_address_ip_port(transportDomainUdpIpv6 = Domain, Address) ->
958    case Address of
959	{IP, Port} when ?is_word(Port) ->
960	    case check_address_ip(Domain, IP) of
961		false ->
962		    false;
963		true ->
964		    true;
965		FixedIP ->
966		    {FixedIP, Port}
967	    end;
968	%% Erlang friendly list format
969	[A0,A1,A2,A3,A4,A5,A6,A7,P]
970	  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
971	       ?is_word(P) ->
972	    {{A0, A1, A2, A3, A4, A5, A6, A7}, P};
973	%% Strange hybrid format with port as bytes
974	[A0,A1,A2,A3,A4,A5,A6,A7,P0,P1]
975	  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
976	       ?is_word(P0, P1) ->
977	    {{A0, A1, A2, A3, A4, A5, A6, A7}, mk_word(P0, P1)};
978	%% SNMP standards format
979	[A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15,P0,P1]
980	  when ?is_ipv6_addr(
981		  A0, A1, A2, A3, A4, A5, A6, A7,
982		  A8, A9, A10, A11, A12, A13, A14, A15),
983	       ?is_word(P0, P1) ->
984	    {{mk_word(A0, A1), mk_word(A2, A3),
985	      mk_word(A4, A5), mk_word(A6, A7),
986	      mk_word(A8, A9), mk_word(A10, A11),
987	      mk_word(A12, A13), mk_word(A14, A15)},
988	     mk_word(P0, P1)};
989	_ ->
990	    false
991    end;
992check_address_ip_port(BadDomain, _) ->
993    error({bad_domain, BadDomain}).
994
995
996
997%% ---------
998
999check_oid([E1,E2|_] = X) when E1 * 40 + E2 =< 255 ->
1000    case all_integer(X) of
1001	true ->
1002	    ok;
1003	_ ->
1004	    error({invalid_object_identifier, X})
1005    end;
1006check_oid(X) ->
1007    error({invalid_object_identifier, X}).
1008
1009
1010%% ---------
1011
1012%% Check a (view) mask in the internal form (all 0 and 1):
1013check_imask(null) ->
1014    {ok, []};
1015check_imask(IMask) when is_list(IMask) ->
1016    do_check_imask(IMask),
1017    {ok, IMask}.
1018
1019do_check_imask([]) ->
1020    ok;
1021do_check_imask([0|IMask]) ->
1022    do_check_imask(IMask);
1023do_check_imask([1|IMask]) ->
1024    do_check_imask(IMask);
1025do_check_imask([X|_]) ->
1026    error({invalid_internal_mask_element, X}).
1027
1028
1029%% Check a (view) mask in the external form (according to MIB,
1030%% an OCTET STRING of at most length 16).
1031check_emask(EMask) when is_list(EMask) andalso (length(EMask) =< 16) ->
1032    do_check_emask(EMask).
1033
1034do_check_emask([]) ->
1035    ok;
1036do_check_emask([X|EMask])
1037  when is_integer(X) andalso (X >= 16#00) andalso (X =< 16#FF) ->
1038    do_check_emask(EMask);
1039do_check_emask([X|_]) ->
1040    error({invalid_external_mask_element, X}).
1041
1042
1043%% ---------
1044
1045all_integer([H|T]) when is_integer(H) -> all_integer(T);
1046all_integer([_H|_T]) -> false;
1047all_integer([]) -> true.
1048
1049
1050%% ---------
1051
1052error(Reason) ->
1053    throw({error, Reason}).
1054
1055