aboutsummaryrefslogblamecommitdiffstats
path: root/lib/snmp/src/misc/snmp_conf.erl
blob: c52b4bb6fb1fc892aaedb6b074f9d058cbcf3846 (plain) (tree)
1
2
3
4


                   
                                                        




















                                                                         

                                                                           

                                                           










                                          
                        
                         
                         
                           
                       
                         



                                      



                              
                                       









                                                                  



                                                       




                               























                                                                           

                                                                   

                                                       

                                                     


                                                                            













                                                                            


                              




                                                                  







                                                                   
                                        














































































                                                                               

        

























                                                                         




                                             
 









































                                                                              
                                  


                                                                       
                          










                                                                          
                                                       
                























































































































































































                                                                            


















                                
 


                         
                                 


                                                         
                                  














                                                          
                            
                                       









                                     


                                       
 

                                              

                                              
  

                                              



                                                                 
               
            

                                        
  

                                              




                                                           
               
            

                                        
  


                                        



































                                                                
 

            



















                               


                                           
                       
                                               
               
                                                
                
                                           
        
 


            
                                                                  
                                           










































                                                                          

                                                  
                                                                 
                                       


                                                                           
                                       

                                                                           
  






                                                  
  
             





                                     


                                                                 
                           
                                                                 
                           

                                                       
                           

                                                       




                           

             




                                        


                                                   
               
            

                                          



                                                       
               




                                                                    

                                          
  


                                       
 



























































                                                                     















                                                    






























                                                                       









                                                        
%% 
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%% 
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% %CopyrightEnd%
%% 
%%----------------------------------------------------------------------
%% Purpose: Basic module for reading and verifying config files
%%----------------------------------------------------------------------
-module(snmp_conf).


%% External exports
%% Avoid warning for local function error/1 clashing with autoimported BIF.
-compile({no_auto_import,[error/1]}).
-export([read_files/2, no_gen/2, no_order/2, no_filter/1]).
-export([read/2, read/3]).

%% Basic (type) check functions
-export([check_mandatory/2,
	 check_integer/1, check_integer/2,
	 
	 check_string/1, check_string/2,

	 check_atom/2, 

	 check_timer/1,

	 all_domains/0, 
	 check_domain/1, 
	 all_tdomains/0, 
	 check_tdomain/1,  
	 mk_tdomain/1, 
	 which_domain/1, 
	 check_ip/1, check_ip/2,
	 check_address/2,
	 check_taddress/2,
	 mk_taddress/2, mk_taddress/3,
	 
	 check_packet_size/1, 

	 check_oid/1,
	 check_imask/1, check_emask/1, 
	 
	 check_mp_model/1, 
	 check_sec_model/1, check_sec_model/2, check_sec_model/3, 
	 check_sec_level/1,

	 all_integer/1
	]).


-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl").
-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl").
-include_lib("snmp/include/SNMPv2-TM.hrl").

-define(VMODULE,"CONF").
-include("snmp_verbosity.hrl").


-define(is_word(P), (((P) band (bnot 65535)) =:= 0)).
-define(is_word(P0, P1), ((((P0) bor (P1)) band (bnot 255)) =:= 0)).

mk_word(B0, B1) -> ((B0) bsl 8) bor (B1).
mk_bytes(W) -> [(W) bsr 8,(W) band 255].

-define(
   is_ipv4_addr(A0, A1, A2, A3),
   ((((A0) bor (A1) bor (A2) bor (A3)) band (bnot 255)) =:= 0)).

-define(
   is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
   ((((A0) bor (A1) bor (A2) bor (A3) bor (A4) bor (A5) bor (A6) bor (A7))
	 band (bnot 65535)) =:= 0)).
-define(
   is_ipv6_addr(
     A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15),
   ((((A0) bor (A1) bor (A2) bor (A3) bor
	  (A4) bor (A5) bor (A6) bor (A7) bor
	  (A8) bor (A9) bor (A10) bor (A11) bor
	  (A12) bor (A13) bor (A14) bor (A15))
	 band (bnot 65535)) =:= 0)).


%%-----------------------------------------------------------------

%% read_files(Dir, Files) -> Configs
%% Dir      - string()   - Full path to the config dir.
%% Files    - [{FileName, Gen, Order, Check, Filter}]
%% FileName - string()   - Name of the config file.
%% Gen      - function/2 - In case of failure when reading the config file, 
%%                         this function is called to either generate a
%%                         default file or issue the error.
%%                         Returns a generated config list corresponding
%%                         to the written file.
%%                         (Dir, Error) -> Configs.
%% Order    - function/2 - An ordering function that is used to process
%%                         the read config entries using lists:sort/2.
%%                         Returns true if arg 1 compares less than or
%%                         equal to arg 2, false otherwise.
%% Check    - function/2 - Check each entry as they are read from the file.
%%                         (Entry, State) ->
%%                             {ok,NewState} | {{ok,NewEntry},NewState} |
%%                             throw(Error)
%%                         State =:= 'undefined' the first time.
%% Filter   - function/1 - Process all the config entries read from the file
%%                         (Configs) -> [config_entry()].
%% Configs  - [config_entry()]
%% config_entry() - term()

read_files(Dir, Files) when is_list(Dir) andalso is_list(Files) ->
    read_files(Dir, Files, []).

read_files(_Dir, [], Res) ->
    lists:reverse(Res);
read_files(Dir, [{FileName, Gen, Order, Check, Filter}|Files], Res)
  when is_list(FileName),
       is_function(Gen),
       is_function(Order),
       is_function(Check),
       is_function(Filter) ->
    ?vdebug("read_files -> entry with~n"
	    "   FileName: ~p", [FileName]),
    File = filename:join(Dir, FileName),
    Confs =
	case file:read_file_info(File) of
	    {ok,_} ->
		read(File, Order, Check);
	    {error, R} ->
		?vlog("failed reading file info for ~s: ~n"
		      "   ~p", [FileName, R]),
		Gen(Dir, R)
	end,
    read_files(Dir, Files, [Filter(Confs)|Res]).
%% %%
%% %% XXX remove
%% %%
%% %% read_files(Dir, Files) -> Configs
%% %% Dir      - string()   - Full path to the config dir.
%% %% Files    - [{Gen, Filter, Check, FileName}]
%% %% Gen      - function/2 - In case of failure when reading the config file,
%% %%                         this function is called to either generate a
%% %%                         default file or issue the error.
%% %% Filter   - function/1 - Filters all the config entries read from the file
%% %% Check    - function/1 - Check each entry as they are read from the file.
%% %% FileName - string()   - Name of the config file.
%% %% Configs  - [config_entry()]
%% %% config_entry() - term()
%%
%% read_files(Dir, [{Gen, Filter, Check, FileName}|Files], Res)
%%   when is_function(Filter) andalso
%%        is_function(Check)  andalso
%%        is_list(FileName) ->
%%     ?vdebug("read_files -> entry with"
%% 	"~n   FileName: ~p", [FileName]),
%%     File = filename:join(Dir, FileName),
%%     case file:read_file_info(File) of
%%         {ok, _} ->
%%             Confs = read(File, Check),
%%             read_files(Dir, Files, [Filter(Confs)|Res]);
%%         {error, R} ->
%% 	    ?vlog("failed reading file info for ~s: "
%% 		  "~n   ~p", [FileName, R]),
%%             Gen(Dir, R),
%%             read_files(Dir, Files, [Filter([])|Res])
%%     end.

no_gen(_Dir, _R) -> [].
no_order(_, _) -> true.
no_filter(X) -> X.



%% Ret. Res | exit(Reason)
read(File, Order, Check) when is_function(Order), is_function(Check) ->
    ?vdebug("read -> entry with~n"
	"   File: ~p", [File]),
    Fd = open_file(File),
    read_fd(File, Order, Check, Fd, 1, []).

read_fd(File, Order, Check, Fd, StartLine, Res) ->
    case do_read(Fd, "", StartLine) of
	{ok, Row, EndLine} ->
	    ?vtrace("read_fd ->~n"
		    "   Row:     ~p~n"
		    "   EndLine: ~p", [Row,EndLine]),
	    read_fd(
	      File, Order, Check, Fd, EndLine,
	      [{StartLine, Row, EndLine}|Res]);
	{error, Error, EndLine} ->
	    ?vtrace("read_fd -> read failure:~n"
		    "   Error: ~p~n"
		    "   EndLine: ~p", [Error,EndLine]),
	    file:close(Fd),
	    error({failed_reading, File, StartLine, EndLine, Error});
	{eof, _EndLine} ->
	    Lines =
		lists:sort(
		  fun ({_, RowA, _}, {_, RowB, _}) ->
			  Order(RowA, RowB)
		  end,
		  lists:reverse(Res)),
	    read_check(File, Check, Lines, undefined, [])
    end.

read_check(_, _, [], _, Res) ->
    lists:reverse(Res);
read_check(File, Check, [{StartLine, Row, EndLine}|Lines], State, Res) ->
    try Check(Row, State) of
	{ok, NewState} ->
	    ?vtrace("read_check -> ok", []),
	    read_check(File, Check, Lines, NewState, [Row|Res]);
	{{ok, NewRow}, NewState} ->
	    ?vtrace("read_check -> ok:~n"
		    "   NewRow: ~p~n", [NewRow]),
	    read_check(File, Check, Lines, NewState, [NewRow|Res])
    catch
	{error, Reason} ->
	    ?vtrace("read_check -> error:~n"
		    "   Reason: ~p", [Reason]),
	    error({failed_check, File, StartLine, EndLine, Reason});
	Class:Reason ->
	    Error = {Class,Reason,erlang:get_stacktrace()},
	    ?vtrace("read_check -> failure:~n"
		    "   Error: ~p", [Error]),
	    error({failed_check, File, StartLine, EndLine, Error})
    end.



%% XXX remove

%% Ret. Res | exit(Reason)
read(File, Check) when is_function(Check) -> 
    ?vdebug("read -> entry with"
	"~n   File: ~p", [File]),

    Fd = open_file(File),

    case loop(Fd, [], Check, 1, File) of
        {error, Reason} ->     
            file:close(Fd),
            error(Reason);
        {ok, Res} ->
            file:close(Fd),
            Res
    end.

open_file(File) ->
    case file:open(File, [read]) of
        {ok, Fd} -> 
            Fd;
        {error, Reason} ->
            error({failed_open, File, Reason})
    end.

loop(Fd, Res, Check, StartLine, File) ->
    case do_read(Fd, "", StartLine) of
        {ok, Row, EndLine} ->
	    ?vtrace("loop -> "
		    "~n   Row:     ~p"
		    "~n   EndLine: ~p", [Row, EndLine]),
            case (catch Check(Row)) of
                ok -> 
		    ?vtrace("loop -> ok", []),
                    loop(Fd, [Row | Res], Check, EndLine, File);
                {ok, NewRow} ->
		    ?vtrace("loop -> ok: "
			    "~n   NewRow: ~p", [NewRow]),
                    loop(Fd, [NewRow | Res], Check, EndLine, File);
                {error, Reason} ->
		    ?vtrace("loop -> check error: "
			    "~n   Reason: ~p", [Reason]),
                    {error, {failed_check, File, StartLine, EndLine, Reason}};
                Error ->
		    ?vtrace("loop -> check failure: "
			    "~n   Error: ~p", [Error]),
                    {error, {failed_check, File, StartLine, EndLine, Error}}
		end;
        {error, Error, EndLine} ->
	    ?vtrace("loop -> read failure: "
		    "~n   Error: ~p", [Error]),
            {error, {failed_reading, File, StartLine, EndLine, Error}};
        {eof, _EndLine} ->
            {ok, Res}
    end.


do_read(Io, Prompt, StartLine) ->
    case io:request(Io, {get_until,Prompt,erl_scan,tokens,[StartLine]}) of
        {ok, Toks, EndLine} ->
            case erl_parse:parse_term(Toks) of
                {ok, Term} ->
                    {ok, Term, EndLine};
                {error, {Line, erl_parse, Error}} ->
                    {error, {parse_error, Error}, Line}
            end;
        Other ->
            Other
    end.


%%-----------------------------------------------------------------


check_mandatory(L, [{Key, Value}|T]) ->
    case lists:keymember(Key, 1, L) of
	true -> 
	    check_mandatory(L, T);
	false when Value == mandatory -> 
	    error({missing_mandatory, Key});
	false ->
	    {value, V} = Value,
	    check_mandatory([{Key, V} | L], T)
    end;
check_mandatory(L, []) -> 
    {ok, L}.


%% ---------

check_integer(I) -> check_integer(I, any).

check_integer(I, any) when is_integer(I) -> ok;
check_integer(I, pos) when is_integer(I), I > 0 -> ok;
check_integer(I, neg) when is_integer(I), I < 0 -> ok;
check_integer(I1, {gt,  I2}) 
  when is_integer(I1) andalso is_integer(I2) andalso (I1 > I2) -> ok;
check_integer(I1, {gte, I2}) 
  when is_integer(I1) andalso is_integer(I2) andalso (I1 >= I2) -> ok;
check_integer(I1, {lt,  I2}) 
  when is_integer(I1) andalso is_integer(I2) andalso (I1 < I2) -> ok;
check_integer(I1, {lte, I2}) 
  when is_integer(I1) andalso is_integer(I2) andalso (I1 =< I2) -> ok;
check_integer(I1, {eq,  I1}) 
  when is_integer(I1) -> ok;
check_integer(I,  {range, L, U}) 
  when (is_integer(I) andalso 
	is_integer(L) andalso 
	is_integer(U) andalso 
	(I >= L) andalso (I =< U)) -> ok;
check_integer(I, _)  -> error({invalid_integer, I}).

check_packet_size(S) ->
    case (catch check_integer(S, {range, 484, 2147483647})) of
	ok ->
	    ok;
	{error, _} ->
	    error({invalid_packet_size, S})
    end.

%% ---------

check_string(X) when is_list(X) -> ok;
check_string(X) -> error({invalid_string, X}).

check_string(X, any) 
  when is_list(X) -> ok;
check_string(X, {gt, Len})   
  when is_list(X) andalso (length(X) > Len) -> ok;
check_string(X, {gt, _Len})  
  when is_list(X) -> error({invalid_length, X});
check_string(X, {gte, Len})  
  when is_list(X) andalso (length(X) >= Len) -> ok;
check_string(X, {gte, _Len}) 
  when is_list(X) -> error({invalid_length, X});
check_string(X, {lt, Len})   
  when is_list(X) andalso (length(X) < Len) -> ok;
check_string(X, {lt, _Len})  
  when is_list(X) -> error({invalid_length, X});
check_string(X, {lte, Len})  
  when is_list(X) andalso (length(X) =< Len) -> ok;
check_string(X, {lte, _Len}) 
  when is_list(X) -> error({invalid_length, X});
check_string(X, Len)       
  when is_list(X) andalso is_integer(Len) andalso (length(X) =:= Len) -> ok;
check_string(X, _Len)      when is_list(X) -> error({invalid_length, X});
check_string(X, _Len)                   -> error({invalid_string, X}).


check_atom(X, Atoms) ->
    case lists:keysearch(X, 1, Atoms) of
        {value, {X, Val}} -> 
	    {ok, Val};
        _ -> 
	    error({invalid_atom, X, Atoms})
    end.


%% ---------

check_mp_model(MPModel) when is_atom(MPModel) ->
    All = [{v1,  ?MP_V1}, {v2c, ?MP_V2C}, {v3,  ?MP_V3}],
    check_atom(MPModel, All);
check_mp_model(?MP_V1) ->
    {ok, ?MP_V1};
check_mp_model(?MP_V2C) ->
    {ok, ?MP_V2C};
check_mp_model(?MP_V3) ->
    {ok, ?MP_V3};
check_mp_model(BadMpModel) ->
    error({invalid_mp_model, BadMpModel}).


%% ---------

check_sec_model(SecModel) when is_atom(SecModel) ->
    check_sec_model(SecModel, []);
check_sec_model(?SEC_ANY) ->
    {ok, ?SEC_ANY};
check_sec_model(?SEC_V1) ->
    {ok, ?SEC_V1};
check_sec_model(?SEC_V2C) ->
    {ok, ?SEC_V2C};
check_sec_model(?SEC_USM) ->
    {ok, ?SEC_USM};
check_sec_model(BadSecModel) ->
    error({invalid_sec_model, BadSecModel}).

check_sec_model(SecModel, Exclude) when is_atom(SecModel) ->
    All = [{any, ?SEC_ANY},
           {v1,  ?SEC_V1},
           {v2c, ?SEC_V2C},
           {usm, ?SEC_USM}],
    Alt = [{X, Y} || {X, Y} <- All, not lists:member(X, Exclude)],
    case (catch check_atom(SecModel, Alt) ) of
	{error, _} ->
	    error({invalid_sec_model, SecModel});
	OK ->
	    OK
    end;
check_sec_model(BadSecModel, _Exclude) ->
    error({invalid_sec_model, BadSecModel}).

check_sec_model(v1, v1, Exclude) ->
    check_sec_model2(v1, ?SEC_V1, Exclude);
check_sec_model(v1, SecModel, _Exclude) ->
    error({invalid_sec_model, v1, SecModel});
check_sec_model(v2c, v2c, Exclude) ->
    check_sec_model2(v2c, ?SEC_V2C, Exclude);
check_sec_model(v2c, SecModel, _Exclude) ->
    error({invalid_sec_model, v2c, SecModel});
check_sec_model(v3, usm, Exclude) ->
    check_sec_model2(v3, ?SEC_USM, Exclude);
check_sec_model(v3, SecModel, _Exclude) ->
    error({invalid_sec_model, v3, SecModel});
check_sec_model(M1, M2, _Exclude) ->
    error({invalid_sec_model, M1, M2}).

check_sec_model2(SecModel, SM, Exclude) ->
    case lists:member(SecModel, Exclude) of
	false ->
	    {ok, SM};
	true ->
	    error({invalid_sec_model, SecModel})
    end.


%% ---------

check_sec_level(SecLevel) when is_atom(SecLevel) ->
    All = [{noAuthNoPriv, ?'SnmpSecurityLevel_noAuthNoPriv'}, 
	   {authNoPriv,   ?'SnmpSecurityLevel_authNoPriv'}, 
	   {authPriv,     ?'SnmpSecurityLevel_authPriv'}], 
    case (catch check_atom(SecLevel, All)) of
	{error, _} ->
	    error({invalid_sec_level, SecLevel});
	OK ->
	    OK
    end;
check_sec_level(?'SnmpSecurityLevel_noAuthNoPriv' = SL) ->
    {ok, SL};
check_sec_level(?'SnmpSecurityLevel_authNoPriv' = SL) ->
    {ok, SL};
check_sec_level(?'SnmpSecurityLevel_authPriv' = SL) ->
    {ok, SL};
check_sec_level(BadSecLevel) ->
    error({invalid_sec_level, BadSecLevel}).


%% ---------
all_tdomains() ->
    [
     ?transportDomainUdpIpv4, 
     ?transportDomainUdpIpv6, 
     ?transportDomainUdpIpv4z, 
     ?transportDomainUdpIpv6z, 
     ?transportDomainTcpIpv4, 
     ?transportDomainTcpIpv6, 
     ?transportDomainTcpIpv4z, 
     ?transportDomainTcpIpv6z, 
     ?transportDomainSctpIpv4, 
     ?transportDomainSctpIpv6, 
     ?transportDomainSctpIpv4z, 
     ?transportDomainSctpIpv6z, 
     ?transportDomainLocal, 
     ?transportDomainUdpDns, 
     ?transportDomainTcpDns,
     ?transportDomainSctpDns
    ].

check_tdomain(TDomain) ->
    SupportedTDomains = 
	[
	 ?snmpUDPDomain, % Legacy
	 ?transportDomainUdpIpv4,
	 ?transportDomainUdpIpv6			 
	],
    AllTDomains = all_tdomains(), 
    case lists:member(TDomain, SupportedTDomains) of
	true ->
	    ok;
	false ->
	    case lists:member(TDomain, AllTDomains) of
		true ->
		    error({unsupported_tdomain, TDomain});
		false ->
		    error({unknown_tdomain, TDomain})
	    end
    end.


%% ---------

mk_tdomain(snmpUDPDomain) ->
    mk_tdomain(transportDomainUdpIpv4);
mk_tdomain(transportDomainUdpIpv4) ->
    ?transportDomainUdpIpv4;
mk_tdomain(transportDomainUdpIpv6) ->
    ?transportDomainUdpIpv6;
mk_tdomain(BadDomain) ->
    error({bad_domain, BadDomain}).


%% ---------

%% XXX remove
%% check_taddress(X) ->
%%    check_taddress(snmpUDPDomain, X).

check_taddress(?snmpUDPDomain, X) ->
    check_taddress(transportDomainUdpIpv4, X);
check_taddress(snmpUDPDomain, X) ->
    check_taddress(transportDomainUdpIpv4, X);
%%
check_taddress(?transportDomainUdpIpv4, X) ->
    check_taddress(transportDomainUdpIpv4, X);
check_taddress(transportDomainUdpIpv4, X) ->
    case X of
	[A0,A1,A2,A3,P0,P1]
	  when ?is_ipv4_addr(A0, A1, A2, A3), ?is_word(P0, P1) ->
	    ok;
	_ ->
	    error({invalid_taddress, X})
    end;
%%
check_taddress(?transportDomainUdpIpv6, X) ->
    check_taddress(transportDomainUdpIpv6, X);
check_taddress(transportDomainUdpIpv6, X) ->
    case X of
	[A0,A1,A2,A3,A4,A5,A6,A7,P0,P1]
	when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
	     ?is_word(P0, P1) ->
	    ok;
	_ ->
	    error({invalid_taddress, X})
    end;
%%
check_taddress(BadDomain, _X) ->
    error({invalid_tdomain, BadDomain}).



%% ---------

check_timer(infinity) ->
    {ok, infinity};
check_timer(T) when is_record(T, snmp_incr_timer) ->
    {ok, T};
check_timer({WaitFor, Factor, Incr, Retry} = T) ->
    case (catch do_check_timer(WaitFor, Factor, Incr, Retry)) of
	ok ->
	    {ok, #snmp_incr_timer{wait_for    = WaitFor,
				  factor      = Factor,
				  incr        = Incr,
				  max_retries = Retry}};
	_Err ->
	    error({invalid_timer, T})
    end;
check_timer(Timeout) ->
    case (catch check_integer(Timeout, {gt, 0})) of
	ok ->
	    {ok, #snmp_incr_timer{wait_for    = Timeout,
				  factor      = 1,
				  incr        = 0,
				  max_retries = 0}};
	_Err ->
	    error({invalid_timer, Timeout})
    end.

do_check_timer(WaitFor, Factor, Incr, Retry) ->
    check_integer(WaitFor, {gt,  0}),
    check_integer(Factor,  {gt,  0}),
    check_integer(Incr,    {gte, 0}),
    check_integer(Retry,   {gte, 0}),
    ok.


%% ---------

all_domains() ->
    [
     transportDomainUdpIpv4, 
     transportDomainUdpIpv6, 
     transportDomainUdpIpv4z, 
     transportDomainUdpIpv6z, 
     transportDomainTcpIpv4, 
     transportDomainTcpIpv6, 
     transportDomainTcpIpv4z, 
     transportDomainTcpIpv6z, 
     transportDomainSctpIpv4, 
     transportDomainSctpIpv6, 
     transportDomainSctpIpv4z, 
     transportDomainSctpIpv6z, 
     transportDomainLocal, 
     transportDomainUdpDns, 
     transportDomainTcpDns,
     transportDomainSctpDns
    ].

check_domain(snmpUDPDomain) -> ok;
check_domain(transportDomainUdpIpv4) -> ok;
check_domain(transportDomainUdpIpv6) -> ok;
check_domain(Domain) ->
    case lists:member(Domain, all_domains()) of
	true ->
	    error({unsupported_domain, Domain});
	false ->
	    error({unknown_domain, Domain})
    end.


%% ---------

%% The values of Domain, Ip and Port has both been checked at this
%% point, so we dont need to do that again.

mk_taddress(snmpUDPDomain, Address) ->
    mk_taddress(transportDomainUdpIpv4, Address);
mk_taddress(transportDomainUdpIpv4, {Ip, Port}) when is_list(Ip) ->
    Ip ++ mk_bytes(Port);
mk_taddress(transportDomainUdpIpv4, {Ip, Port}) when is_tuple(Ip) ->
    tuple_to_list(Ip) ++ mk_bytes(Port);
mk_taddress(transportDomainUdpIpv4, IpPort) when is_list(IpPort) ->
    IpPort; % Should be length 6
mk_taddress(transportDomainUdpIpv6, {Ip, Port}) when is_list(Ip) ->
    Ip ++ mk_bytes(Port);
mk_taddress(transportDomainUdpIpv6, {Ip, Port}) when is_tuple(Ip) ->
    tuple_to_list(Ip) ++ mk_bytes(Port);
mk_taddress(transportDomainUdpIpv6, IpPort) when is_list(IpPort) ->
    case IpPort of
	[A0,A1,A2,A3,A4,A5,A6,A7,P] ->
	    [A0,A1,A2,A3,A4,A5,A6,A7] ++ mk_bytes(P);
	[A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15,P0,P1] ->
	    [mk_word(A0, A1),mk_word(A2, A3),
	     mk_word(A4, A5),mk_word(A6, A7),
	     mk_word(A8, A9),mk_word(A10, A11),
	     mk_word(A12, A13),mk_word(A14, A15),P0,P1];
	_ ->
	    IpPort % Should already be 8 words and 2 bytes hence length 10
    end;
mk_taddress(Domain, Address) when is_atom(Domain) ->
    erlang:error(badarg, [Domain,Address]);
%%
%% These are just for convenience
mk_taddress(?snmpUDPDomain, Address) ->
    mk_taddress(snmpUDPDomain, Address);
mk_taddress(?transportDomainUdpIpv4, Address) ->
    mk_taddress(transportDomainUdpIpv4, Address);
mk_taddress(?transportDomainUdpIpv6, Address) ->
    mk_taddress(transportDomainUdpIpv6, Address);
%% Bad domain
mk_taddress(BadDomain, _) ->
    error({bad_domain, BadDomain}).



%% XXX remove

mk_taddress(snmpUDPDomain, Ip, Port) ->
    mk_taddress(transportDomainUdpIpv4, Ip, Port);
mk_taddress(transportDomainUdpIpv4, Ip, Port) when is_list(Ip) ->
    Ip ++ [Port div 256, Port rem 256];
mk_taddress(transportDomainUdpIpv4 = Domain, Ip, Port) when is_tuple(Ip) ->
    mk_taddress(Domain, tuple_to_list(Ip), Port);
mk_taddress(transportDomainUdpIpv6, Ip, Port) when is_list(Ip) ->
    Ip ++ [Port div 256, Port rem 256];
mk_taddress(transportDomainUdpIpv6 = Domain, Ip, Port) when is_tuple(Ip) ->
    mk_taddress(Domain, tuple_to_list(Ip), Port);
%%
%% These are just for convenience
mk_taddress(?snmpUDPDomain, Ip, Port) ->
    mk_taddress(snmpUDPDomain, Ip, Port);
mk_taddress(?transportDomainUdpIpv4, Ip, Port) ->
    mk_taddress(transportDomainUdpIpv4, Ip, Port);
mk_taddress(?transportDomainUdpIpv6, Ip, Port) ->
    mk_taddress(transportDomainUdpIpv6, Ip, Port);
%%
%% Bad domain
mk_taddress(BadDomain, _Ip, _Port) ->
    error({bad_domain, BadDomain}).

    
%% ---------

%% XXX remove

which_domain([A0,A1,A2,A3]) when ?is_ipv4_addr(A0, A1, A2, A3) ->
    transportDomainUdpIpv4;
which_domain({A0,A1,A2,A3}) when ?is_ipv4_addr(A0, A1, A2, A3) ->
    transportDomainUdpIpv4;
which_domain([A0,A1,A2,A3,A4,A5,A6,A7])
  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7) ->
    transportDomainUdpIpv6;
which_domain({A0, A1, A2, A3, A4, A5, A6, A7})
  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7) ->
    transportDomainUdpIpv6.

    
%% ---------

%% XXX remove

check_ip(X) ->
    check_ip(snmpUDPDomain, X).

check_ip(snmpUDPDomain, X) ->
    check_ip(transportDomainUdpIpv4, X);
check_ip(transportDomainUdpIpv4, X) ->
    case X of
	[A,B,C,D] when ?is_ipv4_addr(A, B, C, D) ->
	    ok;
	_ ->
	    error({invalid_ip_address, X})
    end;
check_ip(transportDomainUdpIpv6, X) ->
    case X of
	[A,B,C,D,E,F,G,H]
	  when ?is_ipv6_addr(A, B, C, D, E, F, G, H) ->
	    ok;
	[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]
	  when ?is_ipv6_addr(
		  A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) ->
	    ok;
	_ ->
	    error({invalid_ip_address, X})
    end;
%%
check_ip(BadDomain, _X) ->
    error({invalid_domain, BadDomain}).


%% ---------

%% Check a configuration term field from a file to see if it
%% can be fed to mk_taddress/2.

check_address(Domain, Address)
  when Domain =:= snmpUDPDomain;
       Domain =:= transportDomainUdpIpv4 ->
    case Address of
	%% Erlang native format
	{{A0, A1, A2, A3}, P}
	  when ?is_ipv4_addr(A0, A1, A2, A3), ?is_word(P) ->
	    ok;
	%% Erlangish format
	{[A0,A1,A2,A3], P}
	  when ?is_ipv4_addr(A0, A1, A2, A3), ?is_word(P) ->
	    ok;
	%% SNMP standards format
	[A0,A1,A2,A3,P0,P1]
	  when ?is_ipv4_addr(A0, A1, A2, A3), ?is_word(P0, P1) ->
	    ok;
	_ ->
	    error({invalid_address, {Domain, Address}})
    end;
check_address(transportDomainUdpIpv6 = Domain, Address) ->
    case Address of
	%% Erlang native format
	{{A0, A1, A2, A3, A4, A5, A6, A7}, P}
	  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
	       ?is_word(P) ->
	    ok;
	%% Erlangish format
	{[A0,A1,A2,A3,A4,A5,A6,A7], P}
	  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
	       ?is_word(P) ->
	    ok;
	%% Erlang friendly list format
	[A0,A1,A2,A3,A4,A5,A6,A7,P]
	  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
	       ?is_word(P) ->
	    ok;
	%% Strange hybrid format with port as bytes
	[A0,A1,A2,A3,A4,A5,A6,A7,P0,P1]
	  when ?is_ipv6_addr(A0, A1, A2, A3, A4, A5, A6, A7),
	       ?is_word(P0, P1) ->
	    ok;
	%% SNMP standards format
	[A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15,P0,P1]
	  when ?is_ipv6_addr(
		  A0, A1, A2, A3, A4, A5, A6, A7,
		  A8, A9, A10, A11, A12, A13, A14, A15),
	       ?is_word(P0, P1) ->
	    ok;
	_ ->
	    error({invalid_address, {Domain, Address}})
    end;
check_address(BadDomain, _) ->
    error({invalid_domain, BadDomain}).



%% ---------

check_oid([E1,E2|_] = X) when E1 * 40 + E2 =< 255 ->
    case all_integer(X) of
	true -> 
	    ok;
	_ -> 
	    error({invalid_object_identifier, X})
    end;
check_oid(X) -> 
    error({invalid_object_identifier, X}).


%% ---------

%% Check a (view) mask in the internal form (all 0 and 1): 
check_imask(null) ->
    {ok, []};
check_imask(IMask) when is_list(IMask) ->
    do_check_imask(IMask), 
    {ok, IMask}.

do_check_imask([0|IMask]) ->
    do_check_imask(IMask);
do_check_imask([1|IMask]) ->
    do_check_imask(IMask);
do_check_imask([X|_]) ->
    error({invalid_internal_mask_element, X}).


%% Check a (view) mask in the external form (according to MIB, 
%% an OCTET STRING of at most length 16). 
check_emask(EMask) when is_list(EMask) andalso (length(EMask) =< 16) ->
    do_check_emask(EMask).

do_check_emask([]) ->
    ok;
do_check_emask([X|EMask]) 
  when is_integer(X) andalso (X >= 16#00) andalso (X =< 16#FF) ->
    do_check_emask(EMask);
do_check_emask([X|_]) ->
    error({invalid_external_mask_element, X}).


%% ---------

all_integer([H|T]) when is_integer(H) -> all_integer(T);
all_integer([_H|_T]) -> false;
all_integer([]) -> true.


%% ---------

error(Reason) ->
    throw({error, Reason}).