diff options
Diffstat (limited to 'lib/snmp/src/misc/snmp_pdus.erl')
-rw-r--r-- | lib/snmp/src/misc/snmp_pdus.erl | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl new file mode 100644 index 0000000000..6c80fc3876 --- /dev/null +++ b/lib/snmp/src/misc/snmp_pdus.erl @@ -0,0 +1,770 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. 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% +%% + +-module(snmp_pdus). + +-define(SNMP_USE_V3, true). +-include("snmp_types.hrl"). + +-define(VMODULE,"PDUS"). +-include("snmp_verbosity.hrl"). + +%% See RFC1155, RFC1157, RFC1901, RFC1902, RFC1905, RFC2272 + + +%% API +-export([enc_message/1, enc_message_only/1, enc_pdu/1, + enc_varbind/1, + enc_oct_str_tag/1, enc_scoped_pdu/1, + enc_usm_security_parameters/1, + dec_message/1, dec_message_only/1, dec_pdu/1, + dec_scoped_pdu_data/1, dec_scoped_pdu/1, + dec_usm_security_parameters/1, + strip_encrypted_scoped_pdu_data/1, + octet_str_to_bits/1, bits_to_str/1, + get_encoded_length/1]). + +%% Returns the number of octets required to encode Length. +get_encoded_length(Length) -> + length(elength(Length)). + +dec_message([48 | Bytes]) -> + Bytes2 = get_data_bytes(Bytes), + case dec_snmp_version(Bytes2) of + {'version-3', Rest} -> + dec_rest_v3_msg(Rest); + {Vsn, Rest} -> % 1 or 2 + dec_rest_v1_v2_msg(Vsn, Rest) + end. + +dec_message_only([48 | Bytes]) -> + Bytes2 = get_data_bytes(Bytes), + case dec_snmp_version(Bytes2) of + {'version-3', Rest} -> + dec_rest_v3_msg_only(Rest); + {Vsn, Rest} -> % 1 or 2 + dec_rest_v1_v2_msg_only(Vsn, Rest) + end. + +dec_snmp_version(Bytes) -> + case (catch dec_int_tag(Bytes, 10)) of + {error, {bad_integer, BadInt}} -> + exit({bad_version, BadInt}); + {SNMPversion, Rest} when is_integer(SNMPversion) andalso is_list(Rest) -> + {dec_snmp_ver(SNMPversion), Rest}; + {'EXIT', Reason} -> + exit(Reason) + end. + + +dec_snmp_ver(0) -> + 'version-1'; +dec_snmp_ver(1) -> + 'version-2'; +dec_snmp_ver(3) -> + 'version-3'; +dec_snmp_ver(Vsn) -> + exit({bad_version, Vsn}). + +dec_rest_v1_v2_msg(Vsn, Rest1) -> + {Community, Rest2} = dec_oct_str_tag(Rest1), + PDU = dec_pdu(Rest2), + #message{version = Vsn, vsn_hdr = Community, data = PDU}. + +dec_rest_v1_v2_msg_only(Vsn, Rest1) -> + {Community, Rest2} = dec_oct_str_tag(Rest1), + #message{version = Vsn, vsn_hdr = Community, data = Rest2}. + +dec_rest_v3_msg_only([48 | Bytes]) -> % starts with header data sequence + {Size, Tail} = dec_len(Bytes), + {HBytes, Bytes1} = split_at(Tail, Size, []), + %% Decode HeaderData + {MsgID, HBytes1} = dec_int_tag(HBytes), + chk_msg_id(MsgID), + {MsgMaxSize, HBytes2} = dec_int_tag(HBytes1), + chk_msg_max_size(MsgMaxSize), + {MsgFlags, HBytes3} = dec_oct_str_tag(HBytes2), + {MsgSecurityModel, []} = dec_int_tag(HBytes3), + chk_msg_sec_model(MsgSecurityModel), + %% Continue with Message +% {MsgSecurityParameters, Bytes2} = dec_oct_str_tag(Bytes1), + + [4 | Bytes1a] = Bytes1, + {Size1a, Tail1a} = dec_len(Bytes1a), + {MsgSecurityParameters, Bytes2} = split_at(Tail1a, Size1a, []), + + %% [48 , HdrDataLen, HdrData, 4, MsgSecLen, MsgSec, ...] + %% NOTE: HdrDataLen is always so small that one octet is enough to + %% encode its length. + %% MsgSecLen is worse... but for USM, it is small enough for + %% one octet. USM is currently the only secmodel. + %% 1 + 1 + Size + 1 + 1 + Size1a + HdrSize = Size + Size1a + 4, + V3Hdr = #v3_hdr{msgID = MsgID, + msgMaxSize = MsgMaxSize, + msgFlags = MsgFlags, %dec_msg_flags(MsgFlags), + msgSecurityModel = MsgSecurityModel, + msgSecurityParameters = MsgSecurityParameters, + hdr_size = HdrSize}, + #message{version = 'version-3', vsn_hdr = V3Hdr, data = Bytes2}. + +dec_rest_v3_msg(Bytes) -> + Message = dec_rest_v3_msg_only(Bytes), + Data = Message#message.data, + Message#message{data = dec_scoped_pdu_data(Data)}. + +dec_scoped_pdu_data([48 | Bytes]) -> % plaintext + {ScopedPdu, []} = dec_scoped_pdu_notag(Bytes), + ScopedPdu; +dec_scoped_pdu_data([4 | Bytes]) -> % encryptedPDU + {EncryptedPDU, []} = dec_oct_str_notag(Bytes), + EncryptedPDU. + + +dec_scoped_pdu([48 | Bytes]) -> + element(1, dec_scoped_pdu_notag(Bytes)). + +dec_scoped_pdu_notag(Bytes) -> + Bytes1 = get_data_bytes(Bytes), + {ContextEngineID, Bytes2} = dec_oct_str_tag(Bytes1), + {ContextName, Bytes3} = dec_oct_str_tag(Bytes2), + Pdu = dec_pdu(Bytes3), + {#scopedPdu{contextEngineID = ContextEngineID, + contextName = ContextName, + data = Pdu}, + []}. + +dec_pdu_tag(160) -> + 'get-request'; +dec_pdu_tag(161) -> + 'get-next-request'; +dec_pdu_tag(162) -> + 'get-response'; +dec_pdu_tag(163) -> + 'set-request'; +%% 164 SNMPv1 Trap +%% 165 Bulk +dec_pdu_tag(166) -> + 'inform-request'; +dec_pdu_tag(167) -> + 'snmpv2-trap'; +dec_pdu_tag(168) -> + report. + + +dec_pdu([164 | Bytes]) -> % It's a trap + Bytes2 = get_data_bytes(Bytes), + {Enterprise, Rest1} = dec_oid_tag(Bytes2), + {{'IpAddress', AgentAddr}, Rest2} = dec_value(Rest1), + {GenericTrap, Rest3} = dec_int_tag(Rest2), + {SpecificTrap, Rest4} = dec_int_tag(Rest3), + {{'TimeTicks', TimeStamp}, VBBytes} = dec_value(Rest4), + VBs = dec_VBs(VBBytes), + #trappdu{enterprise = Enterprise, agent_addr = AgentAddr, + generic_trap = GenericTrap, specific_trap = SpecificTrap, + time_stamp = TimeStamp, varbinds = VBs}; + +dec_pdu([165 | Bytes]) -> % Bulk + Bytes2 = get_data_bytes(Bytes), + {RequestID, Rest1} = dec_int_tag(Bytes2), + {NonRepeaters, Rest2} = dec_int_tag(Rest1), + {MaxRepetitions,VBbytes} = dec_int_tag(Rest2), + VBs = dec_VBs(VBbytes), + #pdu{type = 'get-bulk-request', request_id = RequestID, + error_status = NonRepeaters, error_index = MaxRepetitions, + varbinds = VBs}; + +dec_pdu([PduTag | Bytes]) -> + Type = dec_pdu_tag(PduTag), + Bytes2 = get_data_bytes(Bytes), + {RequestID, Rest1} = dec_int_tag(Bytes2), + {ErrStat, Rest2} = dec_int_tag(Rest1), + ErrStatus = case lists:keysearch(ErrStat, 2, errMsgs()) of + {value, {ErrStatName, _ErrStat}} -> + ErrStatName; + false -> + ErrStat + end, + {ErrIndex, VarbindsBytes} = dec_int_tag(Rest2), + VBs = dec_VBs(VarbindsBytes), + #pdu{type = Type, request_id = RequestID, error_status = ErrStatus, + error_index = ErrIndex, varbinds = VBs}. + +dec_VBs([48 | Bytes]) -> + Bytes1 = get_data_bytes(Bytes), + dec_individual_VBs(Bytes1, 1, []). + +dec_individual_VBs([], _No, VBs) -> + lists:reverse(VBs); +dec_individual_VBs([48 | Bytes], OrgIndex, AccVBs) -> + {_SizeOfThisVB, Bytes2} = dec_len(Bytes), + {Oid, Rest} = dec_oid_tag(Bytes2), + {{Type, Value}, Rest2} = dec_value(Rest), + % perhaps we should check that we have eaten SizeOfThisVB bytes, but we + % don't consider ourselves to have time for such list traversing stuff. + dec_individual_VBs(Rest2, OrgIndex + 1, [#varbind{oid = Oid, + variabletype = Type, + value = Value, + org_index = OrgIndex} + | AccVBs]). + +dec_usm_security_parameters([48 | Bytes1]) -> + {_Len, Bytes2} = dec_len(Bytes1), + {MsgAuthEngineID, Bytes3} = dec_oct_str_tag(Bytes2), + {MsgAuthEngineBoots, Bytes4} = dec_int_tag(Bytes3), + {MsgAuthEngineTime, Bytes5} = dec_int_tag(Bytes4), + {MsgUserName, Bytes6} = dec_oct_str_tag(Bytes5), + {MsgAuthParams, Bytes7} = dec_oct_str_tag(Bytes6), + {MsgPrivParams, []} = dec_oct_str_tag(Bytes7), + #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID, + msgAuthoritativeEngineBoots = MsgAuthEngineBoots, + msgAuthoritativeEngineTime = MsgAuthEngineTime, + msgUserName = MsgUserName, + msgAuthenticationParameters = MsgAuthParams, + msgPrivacyParameters = MsgPrivParams}. + +strip_encrypted_scoped_pdu_data([48 | Bytes]) -> + {Size, Tail} = dec_len(Bytes), + [48 | elength(Size)] ++ strip(Size, Tail). + +strip(N, [H|T]) when N > 0 -> [H | strip(N-1, T)]; +strip(0, _Tail) -> + []. + + +%%---------------------------------------------------------------------- +%% Returns:{Type, Value} +%%---------------------------------------------------------------------- +dec_value([6 | Bytes]) -> + {Value, Rest} = dec_oid_notag(Bytes), + {{'OBJECT IDENTIFIER', Value}, Rest}; +dec_value([5,0 | T]) -> + {{'NULL', 'NULL'}, T}; +dec_value([2 | Bytes]) -> + {Value, Rest} = dec_integer_notag(Bytes), + {{'INTEGER', Value}, Rest}; +dec_value([4 | Bytes]) -> + {Value, Rest} = dec_oct_str_notag(Bytes), + {{'OCTET STRING', Value}, Rest}; +dec_value([64 | Bytes]) -> + {Value, Rest} = dec_oct_str_notag(Bytes), + {{'IpAddress', Value}, Rest}; +dec_value([65 | Bytes]) -> + {Value, Rest} = dec_integer_notag(Bytes), + if Value >= 0, Value =< 4294967295 -> + {{'Counter32', Value}, Rest}; + true -> + exit({error, {bad_counter32, Value}}) + end; +dec_value([66 | Bytes]) -> + {Value, Rest} = dec_integer_notag(Bytes), + if Value >= 0, Value =< 4294967295 -> + {{'Unsigned32', Value}, Rest}; + true -> + exit({error, {bad_unsigned32, Value}}) + end; +dec_value([67 | Bytes]) -> + {Value, Rest} = dec_integer_notag(Bytes), + if Value >= 0, Value =< 4294967295 -> + {{'TimeTicks', Value}, Rest}; + true -> + exit({error, {bad_timeticks, Value}}) + end; +dec_value([68 | Bytes]) -> + {Value, Rest} = dec_oct_str_notag(Bytes), + {{'Opaque', Value}, Rest}; +dec_value([70 | Bytes]) -> + {Value, Rest} = dec_integer_notag(Bytes), + if Value >= 0, Value =< 18446744073709551615 -> + {{'Counter64', Value}, Rest}; + true -> + exit({error, {bad_counter64, Value}}) + end; +dec_value([128,0|T]) -> + {{'NULL', noSuchObject}, T}; +dec_value([129,0|T]) -> + {{'NULL', noSuchInstance}, T}; +dec_value([130,0|T]) -> + {{'NULL', endOfMibView}, T}. + + +%%---------------------------------------------------------------------- +%% Purpose: Uses the beginning length bytes to return the actual data. +%% If data has the wrong length, the program is exited. +%% Pre: Tag is removed. +%%---------------------------------------------------------------------- +get_data_bytes(Bytes) -> + {Size, Tail} = dec_len(Bytes), + if + length(Tail) =:= Size -> + Tail; + true -> + exit({error, {wrong_length, Bytes}}) + end. + +split_at(L, 0, Acc) -> + {lists:reverse(Acc), L}; +split_at([H|T], N, Acc) -> + split_at(T, N-1, [H|Acc]). + +%%---------------------------------------------------------------------- +%% All decoding routines return: {Data, RestBytes} +%%---------------------------------------------------------------------- + +dec_int_tag([2 | Bytes]) -> + dec_integer_notag(Bytes). +dec_int_tag([2 | Bytes], SizeLimit) -> + dec_integer_notag(Bytes, SizeLimit). + +dec_integer_notag(Ints) -> + dec_integer_notag(Ints, infinity). +dec_integer_notag(Ints, SizeLimit) -> + case dec_len(Ints) of + {Size, Ints2} when SizeLimit =:= infinity -> + do_dec_integer_notag(Size, Ints2); + {Size, Ints2} when (is_integer(SizeLimit) andalso + (Size =< SizeLimit)) -> + do_dec_integer_notag(Size, Ints2); + {BadSize, _BadInts2} -> + throw({error, {bad_integer, {BadSize, SizeLimit}}}) + end. + +do_dec_integer_notag(Size, Ints) -> + if hd(Ints) band 128 == 0 -> %% Positive number + dec_pos_int(Ints, Size, 8 * (Size - 1)); + true -> %% Negative + dec_neg_int(Ints, Size, 8 * (Size - 1)) + end. + + +dec_pos_int(T, 0, _) -> {0, T}; +dec_pos_int([Byte|Tail], Size, Shift) -> + {Int, Rest} = dec_pos_int(Tail, Size - 1, Shift - 8), + {(Byte bsl Shift) bor Int, Rest}. + +dec_neg_int(T, 0, _) -> {0, T}; +dec_neg_int([Byte|Tail], Size, Shift) -> + {Int, Rest} = dec_pos_int(Tail, Size - 1, Shift-8), + {(-128 + (Byte band 127) bsl Shift) bor Int, Rest}. + +dec_oct_str_tag([4 | Bytes]) -> + dec_oct_str_notag(Bytes). + +dec_oct_str_notag(Bytes) -> + {Size, Tail} = dec_len(Bytes), + split_at(Tail, Size, []). + +dec_oid_tag([6 | Bytes]) -> + dec_oid_notag(Bytes). + +dec_oid_notag(Bytes) -> + {Size, [H | Tail]} = dec_len(Bytes), + {Oid, Rest} = dec_oid_elements(Tail, Size - 1, []), + {[H div 40, H rem 40 | Oid], Rest}. + +dec_oid_elements(L, 0, Acc) -> + {lists:reverse(Acc), L}; +dec_oid_elements([Dig|Tail], Size, Acc) when Dig < 128 -> + dec_oid_elements(Tail, Size - 1, [Dig | Acc]); +dec_oid_elements([Dig|Tail], Size, Acc) -> + {Num, Neaten, Tl} = dec_oid_element(Tail,1,Dig band 127), + dec_oid_elements(Tl, Size - Neaten, [Num|Acc]). + +dec_oid_element([Dig|Tail], Neaten, Num) when Dig < 128 -> + {Num*128+Dig,Neaten+1,Tail}; +dec_oid_element([Dig|Tail],Neaten, Num) -> + dec_oid_element(Tail, Neaten+1, Num*128 + (Dig band 127)). + +chk_msg_id(MsgId) when (MsgId >= 0) andalso (MsgId =< 2147483647) -> ok; +chk_msg_id(MsgId) -> exit({bad_msg_id, MsgId}). + +chk_msg_max_size(MMS) when (MMS >= 484) andalso (MMS =< 2147483647) -> ok; +chk_msg_max_size(MMS) -> exit({bad_msg_max_size, MMS}). + +chk_msg_sec_model(MsgSecurityModel) when MsgSecurityModel >= 0, + MsgSecurityModel =< 2147483647 -> ok; +chk_msg_sec_model(MsgSecurityModel) -> + exit({bad_msg_sec_model, MsgSecurityModel}). + +%%---------------------------------------------------------------------- +%% Code copied from the original ASN.1 compiler written by +%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +%% Returns: {Len, Tail} +%%---------------------------------------------------------------------- +dec_len([128|_Tail]) -> + %% indefinite form - not allowed in SNMP + exit({asn1_error, indefinite_length}); + +dec_len([Hd|Tl]) when Hd >= 0 -> + %% definite form + if + Hd < 128 -> % 8th bit is cleared + %% Short form (actually, we can remove this test, since snmp_pdus + %% performs this test _before_ calling this function) + {Hd,Tl}; + true -> + %% Long form + No = Hd band 127, % clear 8th bit + {DigList, Rest} = head(No, Tl), + Size = dec_integer_len(DigList), + {Size, Rest} + end. + +dec_integer_len([D]) -> + D; +dec_integer_len([A,B]) -> + (A bsl 8) bor B; +dec_integer_len([A,B,C]) -> + (A bsl 16) bor (B bsl 8) bor C; +%% More than 3 elements for length => either *very* long packet +%% (which we don't handle), or the length is encoded with more octets +%% than necessary (in which case the first octet must be 0). +dec_integer_len([0 | T]) -> + dec_integer_len(T). + +%%----------------------------------------------------------------- +%% head(N, List) -> {List1, List2} +%% List == List1 ++ List2 +%% length(List1) == N +%%----------------------------------------------------------------- +head(L,List) -> + head(L,List,[]). + +head(0,L,Res) -> + {lists:reverse(Res),L}; + +head(Int,[H|Tail],Res) -> + head(Int-1,Tail,[H|Res]); +head(Int, [], _Res) -> + exit({asn1_error, {bad_length, Int}}). + +%%%---------------------------------------------------------------------- +%%% ENCODING ENCODING ENCODING ENCODING ENCODING ENCODING ENCODING ENCODING +%%%---------------------------------------------------------------------- + +enc_message(#message{version = Ver, vsn_hdr = VsnHdr, data = Data}) -> + VerBytes = enc_version(Ver), + Bytes = + case Ver of + 'version-3' -> + V3HeaderBytes = enc_v3_header(VsnHdr), + DataBytes = enc_scoped_pdu(Data), + V3HeaderBytes ++ DataBytes; + _ -> + ComBytes = enc_community(VsnHdr), + DataBytes = enc_pdu(Data), + ComBytes ++ DataBytes + end, + Bytes2 = VerBytes ++ Bytes, + Len = elength(length(Bytes2)), + [48 | Len] ++ Bytes2. + +enc_message_only(#message{version = Ver, vsn_hdr = VsnHdr, data = DataBytes}) -> + VerBytes = enc_version(Ver), + Bytes = + case Ver of + 'version-3' -> + V3HeaderBytes = enc_v3_header(VsnHdr), + V3HeaderBytes ++ DataBytes; + _ -> + ComBytes = enc_community(VsnHdr), + ComBytes ++ DataBytes + end, + Bytes2 = VerBytes ++ Bytes, + Len = elength(length(Bytes2)), + [48 | Len] ++ Bytes2. + +enc_version('version-1') -> + [2,1,0]; +enc_version('version-2') -> + [2,1,1]; +enc_version('version-3') -> + [2,1,3]. + +enc_community(Com) -> + enc_oct_str_tag(Com). + +enc_v3_header(#v3_hdr{msgID = MsgID, + msgMaxSize = MsgMaxSize, + msgFlags = MsgFlags, + msgSecurityModel = MsgSecurityModel, + msgSecurityParameters = MsgSecurityParameters}) -> + Bytes = lists:append([enc_integer_tag(MsgID), + enc_integer_tag(MsgMaxSize), + enc_oct_str_tag(MsgFlags), + enc_integer_tag(MsgSecurityModel)]), + Len = elength(length(Bytes)), + lists:append([[48 | Len], Bytes, enc_oct_str_tag(MsgSecurityParameters)]). + +enc_scoped_pdu(#scopedPdu{contextEngineID = ContextEngineID, + contextName = ContextName, + data = Data}) -> + Bytes = lists:append([enc_oct_str_tag(ContextEngineID), + enc_oct_str_tag(ContextName), + enc_pdu(Data)]), + Len = elength(length(Bytes)), + [48 | Len] ++ Bytes. + + +enc_pdu(PDU) when PDU#pdu.type =:= 'get-request' -> + enc_pdu(160, PDU); +enc_pdu(PDU) when PDU#pdu.type =:= 'get-next-request' -> + enc_pdu(161, PDU); +enc_pdu(PDU) when PDU#pdu.type =:= 'get-response' -> + enc_pdu(162, PDU); +enc_pdu(PDU) when PDU#pdu.type =:= 'set-request' -> + enc_pdu(163, PDU); +enc_pdu(PDU) when PDU#pdu.type =:= 'get-bulk-request' -> + enc_pdu(165, PDU); +enc_pdu(PDU) when PDU#pdu.type =:= 'inform-request' -> + enc_pdu(166, PDU); +enc_pdu(PDU) when PDU#pdu.type =:= 'snmpv2-trap' -> + enc_pdu(167, PDU); +enc_pdu(PDU) when PDU#pdu.type =:= report -> + enc_pdu(168, PDU); +enc_pdu(TrapPDU) when is_record(TrapPDU, trappdu) -> + enc_Trap(TrapPDU). + + +enc_pdu(Tag,PDU) -> + Bytes2 = enc_pdu2(PDU), + Len2 = elength(length(Bytes2)), + lists:append([Tag | Len2], Bytes2). + +enc_pdu2(#pdu{type = Type, request_id = ReqId, error_index = ErrIndex, + error_status = ErrStat, varbinds = VBs}) -> + ReqBytes = enc_integer_tag(ReqId), + Val = err_val(ErrStat,Type), + ErrStatBytes = enc_integer_tag(Val), + ErrIndexBytes = enc_integer_tag(ErrIndex), + VBsBytes = enc_VarBindList(VBs), + lists:append([ReqBytes, ErrStatBytes, ErrIndexBytes, VBsBytes]). + +enc_usm_security_parameters( + #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID, + msgAuthoritativeEngineBoots = MsgAuthEngineBoots, + msgAuthoritativeEngineTime = MsgAuthEngineTime, + msgUserName = MsgUserName, + msgAuthenticationParameters = MsgAuthParams, + msgPrivacyParameters = MsgPrivParams}) -> + Bytes1 = enc_oct_str_tag(MsgAuthEngineID), + Bytes2 = enc_integer_tag(MsgAuthEngineBoots), + Bytes3 = enc_integer_tag(MsgAuthEngineTime), + Bytes4 = enc_oct_str_tag(MsgUserName), + Bytes5 = enc_oct_str_tag(MsgAuthParams), + Bytes6 = enc_oct_str_tag(MsgPrivParams), + Bytes7 = lists:append([Bytes1, Bytes2, Bytes3, Bytes4, Bytes5, Bytes6]), + Len = elength(length(Bytes7)), + [48 | Len] ++ Bytes7. + +err_val(Int,'get-bulk-request') when is_integer(Int) -> Int; +err_val(ErrStat, _) -> + {value, {_ErrStat, Val}} = lists:keysearch(ErrStat, 1, errMsgs()), + Val. + +errMsgs() -> + [{noError,0},{tooBig,1},{noSuchName,2}, + {badValue,3},{readOnly,4},{genErr,5}, + %% v2 + {noAccess,6},{wrongType,7},{wrongLength,8},{wrongEncoding,9}, + {wrongValue,10},{noCreation,11},{inconsistentValue,12}, + {resourceUnavailable,13},{commitFailed,14},{undoFailed,15}, + {authorizationError,16},{notWritable,17},{inconsistentName,18}]. + +enc_VarBindList(EncodedVBs) when is_integer(hd(EncodedVBs)) -> + Len1 = elength(length(EncodedVBs)), + lists:append([48 | Len1],EncodedVBs); +enc_VarBindList(VBs) -> + Bytes1 = lists:append(lists:map(fun enc_varbind/1, VBs)), + Len1 = elength(length(Bytes1)), + lists:append([48 | Len1],Bytes1). + +enc_varbind(Varbind) -> + Bytes1 = enc_VarBind_attributes(Varbind), + Len1 = elength(length(Bytes1)), + lists:append([48 | Len1],Bytes1). + + +enc_VarBind_attributes(#varbind{oid = Oid, variabletype = Type,value = Val}) -> + OidBytes = enc_oid_tag(Oid), + ValueBytes = enc_value(Type, Val), + lists:append(OidBytes, ValueBytes). + +enc_value('INTEGER',Val) -> + enc_integer_tag(Val); +enc_value('OCTET STRING', Val) -> + enc_oct_str_tag(Val); +enc_value('BITS', Val) -> + enc_oct_str_tag(bits_to_str(Val)); +enc_value('OBJECT IDENTIFIER', Val) -> + enc_oid_tag(Val); +enc_value('IpAddress',Val) -> + Bytes2 = enc_oct_str_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([64 | Len2],Bytes2); +enc_value('Opaque', Val) -> + Bytes2 = enc_oct_str_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([68 | Len2],Bytes2); +enc_value(_Type, noSuchObject) -> + [128,0]; +enc_value(_Type, noSuchInstance) -> + [129,0]; +enc_value(_Type, endOfMibView) -> + [130,0]; +enc_value('NULL', _Val) -> + [5,0]; +enc_value(Type, Val) -> + Bytes2 = enc_integer_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([enc_val_tag(Type,Val) | Len2],Bytes2). + +enc_val_tag('Counter32',Val) when (Val >= 0) andalso (Val =< 4294967295) -> + 65; +enc_val_tag('Unsigned32', Val) when (Val >= 0) andalso (Val =< 4294967295) -> + 66; +enc_val_tag('TimeTicks', Val) when (Val >= 0) andalso (Val =< 4294967295) -> + 67; +enc_val_tag('Counter64', Val) when ((Val >= 0) andalso + (Val =< 18446744073709551615)) -> + 70. + + +%%---------------------------------------------------------------------- +%% Impl according to RFC1906, section 8 +%% For example: the number 1010 0000 (=160) 0100 0001 (=65) is represented as +%% the octet string: 1000 0010, 0000 0101 (=[130,5]) +%%---------------------------------------------------------------------- +bits_to_str(0) -> ""; +bits_to_str(Int) -> + [rev_int8(Int band 255) | bits_to_str(Int div 256)]. + +rev_int8(Val) -> + rev_int(Val,0,1,128). + +rev_int(_Val,Res,256,0) -> Res; +rev_int(Val,Res,OldBit,NewBit) when Val band OldBit =/= 0 -> + rev_int(Val,Res+NewBit,OldBit*2,NewBit div 2); +rev_int(Val,Res,OldBit,NewBit) -> + rev_int(Val,Res,OldBit*2,NewBit div 2). + +octet_str_to_bits(Str) -> + octet_str_to_bits(Str,1). + +octet_str_to_bits("",_) -> 0; +octet_str_to_bits([Byte|Bytes],Mul) -> + Mul*rev_int8(Byte)+octet_str_to_bits(Bytes,Mul*256). + + +enc_Trap(TrapPdu) when is_record(TrapPdu, trappdu) -> + Bytes1 = enc_trap_data(TrapPdu), + Len1 = elength(length(Bytes1)), + lists:append([164 | Len1],Bytes1). + + +enc_trap_data(#trappdu{enterprise = Enterprise, + agent_addr = AgentAddr, + generic_trap = GenericTrap, + specific_trap = SpecificTrap, + time_stamp = TimeStamp, + varbinds = VBs}) -> + L1 = enc_oid_tag(Enterprise), + L2 = enc_value('IpAddress', AgentAddr), + L3 = enc_integer_tag(GenericTrap), + L4 = enc_integer_tag(SpecificTrap), + L5 = enc_value('TimeTicks', TimeStamp), + L6 = enc_VarBindList(VBs), + lists:append([L1,L2,L3,L4,L5,L6]). + +enc_oid_tag([E1,E2|RestOid]) when E1 * 40 + E2 =< 255 -> + Head = 40*E1 + E2, % weird + Res = e_object_elements(RestOid, []), + lists:append([6 | elength(length(Res) + 1)],[Head|Res]). + +e_object_elements([Num | T], Res) -> + e_object_elements(T, lists:append(e_object_element(Num),Res)); + +e_object_elements([], Res) -> lists:reverse(Res). + +%%---------------------------------------------------------------------- +%% The reversed encoding for an oid-element +%%---------------------------------------------------------------------- +e_object_element(Num) when Num > 0 -> + [Last|T] = e_object_element2(Num), + [Last-128|T]; +e_object_element(0) -> [0]. + +e_object_element2(Num) when Num > 0 -> + Byte = (Num rem 128), + [128+Byte | e_object_element2((Num-Byte) div 128)]; +e_object_element2(0) -> []. + +enc_integer_tag(Val) when Val >= 0 -> %% stdcase positive ints + Bytes = eint(Val,[]), + [2 | elength(length(Bytes))] ++ Bytes; + +enc_integer_tag(Val) -> %% It's a negative number + Bytes = enint(Val,[]), + [2 | elength(length(Bytes))] ++ Bytes. + +enc_integer_notag(Val) when Val >= 0 -> %% stdcase positive ints + eint(Val,[]); + +enc_integer_notag(Val) -> %% It's a negative number + enint(Val,[]). + +eint(0, [B|Acc]) when B < 128 -> + [B|Acc]; +eint(N, Acc) -> + eint(N bsr 8, [N band 16#ff| Acc]). + +enint(-1, [B1|T]) when B1 > 127 -> + [B1|T]; +enint(N, Acc) -> + enint(N bsr 8, [N band 16#ff|Acc]). + +enc_oct_str_tag(OStr) when is_list(OStr) -> + lists:append([4|elength(length(OStr))],OStr); +enc_oct_str_tag(OBin) -> + [4 | elength(size(OBin))] ++ binary_to_list(OBin). + + +enc_oct_str_notag(OStr) -> OStr. + +%%----------------------------------------------------------------- +%% Always use definite form +%%----------------------------------------------------------------- +%% Short +elength(L) when L < 127 -> + [L]; + +%% 3 cases of long form +elength(L) when L =< 16#FF -> + [2#10000001,L]; + +elength(L) when L =< 16#FFFF -> + [2#10000010,(L bsr 8),(L band 16#FF)]; + +elength(L) when L =< 16#7FFFFF -> + [2#10000011,(L bsr 16),((L band 16#FF00) bsr 8), (L band 16#FF)]. + + |