diff options
Diffstat (limited to 'lib')
26 files changed, 1353 insertions, 628 deletions
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 70c845bade..14ce3cbe7f 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -480,66 +480,69 @@ apply(Module, Function, [ReplyInfo | Args]) <d>ex: "134.138" or "[FEDC:BA98" (all IP-addresses starting with 134.138 or FEDC:BA98), "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP-address).</d> <v>MaxSessions = integer() </v> <d>Default is <c>2</c>. - Maximum number of persistent connections to a host.</d> + Maximum number of persistent connections to a host.</d> <v>MaxKeepAlive = integer() </v> - <d>Default is <c>5</c>. - Maximum number of outstanding requests on the same connection to - a host.</d> - <v>KeepAliveTimeout = integer() </v> - <d>Default is <c>120000</c> (= 2 min). - If a persistent connection is idle longer than the - <c>keep_alive_timeout</c> the client will close the connection. - The server may also have such a time out but you should - not count on it!</d> + <d>Default is <c>5</c>. + Maximum number of outstanding requests on the same connection to + a host.</d> + <v>KeepAliveTimeout = integer() </v> + <d>Default is <c>120000</c> (= 2 min). + If a persistent connection is idle longer than the + <c>keep_alive_timeout</c> in milliseconds, + the client will close the connection. + The server may also have such a time out but you should + not count on it!</d> <v>MaxPipeline = integer() </v> - <d>Default is <c>2</c>. - Maximum number of outstanding requests on a pipelined connection to a host.</d> - <v>PipelineTimeout = integer() </v> - <d>Default is <c>0</c>, - which will result in pipelining not being used. - If a persistent connection is idle longer than the - <c>pipeline_timeout</c> the client will close the connection. </d> + <d>Default is <c>2</c>. + Maximum number of outstanding requests on a pipelined connection + to a host.</d> + <v>PipelineTimeout = integer() </v> + <d>Default is <c>0</c>, + which will result in pipelining not being used. + If a persistent connection is idle longer than the + <c>pipeline_timeout</c> in milliseconds, + the client will close the connection. </d> <v>CookieMode = enabled | disabled | verify </v> <d>Default is <c>disabled</c>. - If Cookies are enabled all valid cookies will automatically be - saved in the client manager's cookie database. - If the option <c>verify</c> is used the function <c>store_cookies/2</c> - has to be called for the cookies to be saved.</d> - <v>IpFamily = inet | inet6 | inet6fb4 </v> - <d>By default <c>inet</c>. - When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6. - It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>. - The option is here to provide a workaround for buggy ipv6 stacks to ensure that - ipv4 will always work.</d> + If Cookies are enabled all valid cookies will automatically be + saved in the client manager's cookie database. + If the option <c>verify</c> is used the function <c>store_cookies/2</c> + has to be called for the cookies to be saved.</d> + <v>IpFamily = inet | inet6 | inet6fb4 </v> + <d>By default <c>inet</c>. + When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6. + It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>. + The option is here to provide a workaround for buggy ipv6 stacks to ensure that + ipv4 will always work.</d> <v>IpAddress = ip_address() </v> - <d>If the host has several network interfaces, this option specifies which one to use. - See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> + <d>If the host has several network interfaces, this option specifies which one to use. + See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> <v>Port = integer() </v> - <d>Specify which local port number to use. - See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> - <v>VerboseMode = false | verbose | debug | trace </v> - <d>Default is <c>false</c>. - This option is used to switch on (or off) - different levels of erlang trace on the client. - It is a debug feature.</d> + <d>Specify which local port number to use. + See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> + <v>VerboseMode = false | verbose | debug | trace </v> + <d>Default is <c>false</c>. + This option is used to switch on (or off) + different levels of erlang trace on the client. + It is a debug feature.</d> <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> - <p>Sets options to be used for subsequent requests.</p> - <note> - <p>If possible the client will keep its connections - alive and use persistent connections - with or without pipeline depending on configuration - and current circumstances. The HTTP/1.1 specification does not - provide a guideline for how many requests would be - ideal to be sent on a persistent connection, - this very much depends on the - application. Note that a very long queue of requests may cause a - user perceived delay as earlier requests may take a long time - to complete. The HTTP/1.1 specification does suggest a - limit of 2 persistent connections per server, which is the - default value of the <c>max_sessions</c> option. </p> - </note> + <p>Sets options to be used for subsequent requests.</p> + <note> + <p>If possible the client will keep its connections + alive and use persistent connections + with or without pipeline depending on configuration + and current circumstances. The HTTP/1.1 specification does not + provide a guideline for how many requests would be + ideal to be sent on a persistent connection, + this very much depends on the + application. Note that a very long queue of requests may cause a + user perceived delay as earlier requests may take a long time + to complete. The HTTP/1.1 specification does suggest a + limit of 2 persistent connections per server, which is the + default value of the <c>max_sessions</c> option. </p> + </note> <marker id="get_options"></marker> </desc> @@ -648,6 +651,8 @@ apply(Module, Function, [ReplyInfo | Args]) <p>Resets (clears) the cookie database for the specified <c>Profile</c>. If no profile is specified the default profile will be used. </p> + + <marker id="which_cookies"></marker> </desc> </func> @@ -667,6 +672,42 @@ apply(Module, Function, [ReplyInfo | Args]) <p>This function produces a list of the entire cookie database. It is intended for debugging/testing purposes. If no profile is specified the default profile will be used. </p> + + <marker id="which_sessions"></marker> + </desc> + </func> + + <func> + <name>which_sessions() -> session_info()</name> + <name>which_sessions(Profile) -> session_info()</name> + <fsummary>Produces a slightly processed dump of the sessions database.</fsummary> + <type> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + <v>session_info() = {GoodSessions, BadSessions, NonSessions}</v> + <v>GoodSessions = session()</v> + <v>BadSessions = tuple()</v> + <v>NonSessions = term()</v> + </type> + <desc> + <p>This function produces a slightly processed dump of the session + database. It is intended for debugging. + If no profile is specified the default profile will be used. </p> + + <marker id="info"></marker> + </desc> + </func> + + <func> + <name>info() -> list()</name> + <name>info(Profile) -> list()</name> + <fsummary>Produces a list of miscelleneous info</fsummary> + <type> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + </type> + <desc> + <p>This function produces a list of miscelleneous info. + It is intended for debugging. + If no profile is specified the default profile will be used. </p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index dfdeb4016c..3aae1ff70a 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>Inets Release Notes</title> @@ -32,16 +32,93 @@ <file>notes.xml</file> </header> + + <section> + <title>Inets 5.9.1</title> + + <section> + <title>Improvements and New Features</title> + <!-- + <p>-</p> + --> + + <list> + <item> + <p>Better handling of errorI(s) during update of the session + database. </p> + <p>Also added and updated some debugging functions + <seealso marker="httpc#which_sessions">which_sessions/10,1</seealso> + and + <seealso marker="httpc#info">info/0</seealso>. </p> + <p>Own Id: OTP-10093</p> + <p>Aux Id: Seq 12062</p> + </item> - <section><title>Inets 5.9</title> - - <section><title>Improvements and New Features</title> -<!-- - <p>-</p> ---> + <item> + <p>Removed R14B compatible version of (inets-service and + tftp) behaviour definition. </p> + <p>Own Id: OTP-10095</p> + </item> + + <item> + <p>[httpc] Documentation of KeepAlive and Pipeline timeout + options have been improved. </p> + <p>Own Id: OTP-10114</p> + </item> + </list> + + </section> + <section> + <title>Fixed Bugs and Malfunctions</title> + <!-- + <p>-</p> + --> + <list> <item> + <p>[httpc] Cancel request does not work due to incorrect + handler table creation (wrong keypos). </p> + <p>Vyacheslav Vorobyov</p> + <p>Own Id: OTP-10092</p> + </item> + + </list> + + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + + <!-- + <list> + <item> + <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), + has been deprecated. The config option that specified usage of + this version of the ssl app, <c>ossl</c>, has been removed. </p> + <p>Own Id: OTP-9522</p> + </item> + + </list> + --> + + </section> + + </section> <!-- 5.9.1 --> + + + <section> + <title>Inets 5.9</title> + + <section> + <title>Improvements and New Features</title> + <!-- + <p>-</p> + --> + + <list> + <item> <p>[httpd] Make the server header configurable with new config option <seealso marker="httpd#prop_server_tokens">server_tokens</seealso>. @@ -102,11 +179,11 @@ </section> +<!-- <section> <title>Incompatibilities</title> <p>-</p> -<!-- <list> <item> <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), @@ -116,9 +193,9 @@ </item> </list> ---> </section> +--> </section> <!-- 5.9 --> @@ -285,31 +362,6 @@ </section> - <section> - <title>Incompatibilities</title> -<!-- - <p>-</p> ---> - - <list> - <item> - <p>[httpc] Deprecated interface module <c>http</c> has been removed. - It has (long) been replaced by http client interface module - <seealso marker="httpc#">httpc</seealso>. </p> - <p>Own Id: OTP-9359</p> - </item> - - <item> - <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), - has been deprecated. The config option that specified usage of - this version of the ssl app, <c>ossl</c>, has been removed. </p> - <p>Own Id: OTP-9522</p> - </item> - - </list> - - </section> - <section><title>Fixed Bugs and Malfunctions</title> <!-- <p>-</p> @@ -332,6 +384,24 @@ </list> </section> +<!-- + <section> + <title>Incompatibilities</title> + <p>-</p> + + <list> + <item> + <p>[httpc] Deprecated interface module <c>http</c> has been removed. + It has (long) been replaced by http client interface module + <seealso marker="httpc#">httpc</seealso>. </p> + <p>Own Id: OTP-9359</p> + </item> + + </list> + + </section> +--> + </section> <!-- 5.7.2 --> diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index f4802fb96d..b6e7708353 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -39,6 +39,7 @@ cookie_header/1, cookie_header/2, cookie_header/3, which_cookies/0, which_cookies/1, reset_cookies/0, reset_cookies/1, + which_sessions/0, which_sessions/1, stream_next/1, default_profile/0, profile_name/1, profile_name/2, @@ -267,6 +268,7 @@ set_option(Key, Value, Profile) -> %% Reason - term() %% Description: Retrieves the current options. %%------------------------------------------------------------------------- + get_options() -> record_info(fields, options). @@ -373,8 +375,6 @@ cookie_header(Url, Opts, Profile) {error, {not_started, Profile}} end. - - %%-------------------------------------------------------------------------- %% which_cookies() -> [cookie()] @@ -398,6 +398,28 @@ which_cookies(Profile) -> %%-------------------------------------------------------------------------- +%% which_sessions() -> {GoodSession, BadSessions, NonSessions} +%% which_sessions(Profile) -> {GoodSession, BadSessions, NonSessions} +%% +%% Description: Debug function, dumping the sessions database, sorted +%% into three groups (Good-, Bad- and Non-sessions). +%%------------------------------------------------------------------------- +which_sessions() -> + which_sessions(default_profile()). + +which_sessions(Profile) -> + ?hcrt("which sessions", [{profile, Profile}]), + try + begin + httpc_manager:which_sessions(profile_name(Profile)) + end + catch + exit:{noproc, _} -> + {[], [], []} + end. + + +%%-------------------------------------------------------------------------- %% info() -> list() %% info(Profile) -> list() %% diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index b8c34bd99b..6fe05dec80 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1713,7 +1713,32 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) -> catch error:undef -> % This could happen during code upgrade Session2 = erlang:setelement(Pos, Session, Value), - insert_session(Session2, ProfileName) + insert_session(Session2, ProfileName); + T:E -> + error_logger:error_msg("Failed updating session: " + "~n ProfileName: ~p" + "~n SessionId: ~p" + "~n Pos: ~p" + "~n Value: ~p" + "~nwhen" + "~n Session (db) info: ~p" + "~n Session (db): ~p" + "~n Session (record): ~p" + "~n T: ~p" + "~n E: ~p", + [ProfileName, SessionId, Pos, Value, + (catch httpc_manager:which_session_info(ProfileName)), + Session, + (catch httpc_manager:lookup_session(ProfileName, SessionId)), + T, E]), + exit({failed_updating_session, + [{profile, ProfileName}, + {session_id, SessionId}, + {pos, Pos}, + {value, Value}, + {etype, T}, + {error, E}, + {stacktrace, erlang:get_stacktrace()}]}) end. diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index b225b43214..3612b331e7 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -34,8 +34,11 @@ retry_request/2, redirect_request/2, insert_session/2, + lookup_session/2, update_session/4, delete_session/2, + which_sessions/1, + which_session_info/1, set_options/2, get_options/2, store_cookies/3, @@ -59,17 +62,9 @@ options = #options{} }). --record(handler_info, - { - id, % Id of the request: request_id() - starter, % Pid of the handler starter process (temp): pid() - handler, % Pid of the handler process: pid() - from, % From for the request: from() - state % State of the handler: initiating | started | operational | canceled - }). - -define(DELAY, 500). + %%==================================================================== %% Internal Application API %%==================================================================== @@ -195,13 +190,28 @@ insert_session(Session, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: lookup_session(SessionId, ProfileName) -> _ +%% SessionId - term() +%% ProfileName - atom() +%% +%% Description: Looks up a session record in the httpc manager +%% table <ProfileName>__session_db. +%%-------------------------------------------------------------------- + +lookup_session(SessionId, ProfileName) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("lookup session", [{session_id, SessionId}, {profile, ProfileName}]), + ets:lookup(SessionDbName, SessionId). + + +%%-------------------------------------------------------------------- %% Function: update_session(ProfileName, SessionId, Pos, Value) -> _ %% Session - #session{} %% ProfileName - atom() %% %% Description: Update, only one field (Pos) of the session record %% identified by the SessionId, the session information -%% of the httpc manager table <ProfileName>_session_db. +%% of the httpc manager table <ProfileName>__session_db. %% Intended to be called by the httpc request handler process. %%-------------------------------------------------------------------- @@ -216,12 +226,12 @@ update_session(ProfileName, SessionId, Pos, Value) -> %%-------------------------------------------------------------------- -%% Function: delete_session(SessionId, ProfileName) -> _ +%% Function: delete_session(SessionId, ProfileName) -> void() %% SessionId - {{Host, Port}, HandlerPid} %% ProfileName - atom() %% %% Description: Deletes session information from the httpc manager -%% table httpc_manager_session_db_<Profile>. Intended to be called by +%% table <ProfileName>__session_db. Intended to be called by %% the httpc request handler process. %%-------------------------------------------------------------------- @@ -232,6 +242,57 @@ delete_session(SessionId, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: which sessions(ProfileName) -> SessionsInfo +%% ProfileName - atom() +%% SessionsInfo - {GoodSessions, BadSessions, NonSessions} +%% GoodSessions - [#session{}] +%% BadSessions - [tuple()] +%% NonSessions - [term()] +%% +%% Description: Produces a list of all sessions in the session db. +%% Used for debugging and since that is the intent, there is some +%% checking and transforming done, which produces the results. +%%-------------------------------------------------------------------- + +which_sessions(ProfileName) -> + ?hcrt("which_sessions", [{profile, ProfileName}]), + SessionDbName = session_db_name(ProfileName), + which_sessions2(SessionDbName). + +which_sessions2(SessionDbName) -> + Sessions = which_sessions_order(ets:tab2list(SessionDbName)), + GoodSessions = [GoodSession || {good_session, GoodSession} <- Sessions], + BadSessions = [BadSession || {bad_session, BadSession} <- Sessions], + NonSessions = [NonSession || {non_session, NonSession} <- Sessions], + {lists:keysort(#session.id, GoodSessions), + lists:keysort(#session.id, BadSessions), + lists:sort(NonSessions)}. + +which_sessions_order([]) -> + []; +which_sessions_order([Session|Sessions]) when is_record(Session, session) -> + [{good_session, Session} | which_sessions_order(Sessions)]; +which_sessions_order([BadSession|Sessions]) + when is_tuple(BadSession) andalso + (element(1, BadSession) =:= session) -> + [{bad_session, BadSession} | which_sessions_order(Sessions)]; +which_sessions_order([NonSession|Sessions]) -> + [{non_session, NonSession} | which_sessions_order(Sessions)]. + + +%%-------------------------------------------------------------------- +%% Function: which session_info(ProfileName) -> list() +%% +%% Description: Produces a ets table info list of the sessions table +%%-------------------------------------------------------------------- + +which_session_info(ProfileName) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("which_session_info", [{profile, ProfileName}]), + ets:info(SessionDbName). + + +%%-------------------------------------------------------------------- %% Function: set_options(Options, ProfileName) -> ok %% %% Options = [Option] @@ -379,8 +440,7 @@ do_init(ProfileName, CookiesDir) -> %% Create handler db ?hcrt("create handler/request db", []), HandlerDbName = handler_db_name(ProfileName), - ets:new(HandlerDbName, - [protected, set, named_table, {keypos, #handler_info.id}]), + ets:new(HandlerDbName, [protected, set, named_table, {keypos, 1}]), %% Cookie DB ?hcrt("create cookie db", []), @@ -414,9 +474,10 @@ handle_call({request, Request}, _, State) -> {stop, Error, httpc_response:error(Request, Error), State} end; -handle_call({cancel_request, RequestId}, From, State) -> +handle_call({cancel_request, RequestId}, From, + #state{handler_db = HandlerDb} = State) -> ?hcri("cancel_request", [{request_id, RequestId}]), - case ets:lookup(State#state.handler_db, RequestId) of + case ets:lookup(HandlerDb, RequestId) of [] -> %% The request has allready compleated make sure %% it is deliverd to the client process queue so @@ -428,9 +489,9 @@ handle_call({cancel_request, RequestId}, From, State) -> {noreply, State}; [{_, Pid, _}] -> httpc_handler:cancel(RequestId, Pid, From), - {noreply, State#state{cancel = - [{RequestId, Pid, From} | - State#state.cancel]}} + {noreply, + State#state{cancel = + [{RequestId, Pid, From} | State#state.cancel]}} end; handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> @@ -457,8 +518,8 @@ handle_call({which_cookies, Url, Options}, _, handle_call({get_options, OptionItems}, _, #state{options = Options} = State) -> ?hcrv("get options", [{option_items, OptionItems}]), - Reply = [{OptionItem, get_option(OptionItem, Options)} || OptionItem <- - OptionItems], + Reply = [{OptionItem, get_option(OptionItem, Options)} || + OptionItem <- OptionItems], {reply, Reply, State}; handle_call(info, _, State) -> @@ -645,7 +706,7 @@ code_change(_, code_change(_, State, _) -> {ok, State}. -%% This function is to catch everything that calls through the cracks... +%% This function is used to catch everything that falls through the cracks... update_session_table(SessionDB, Transform) -> ets:safe_fixtable(SessionDB, true), update_session_table(SessionDB, ets:first(SessionDB), Transform), @@ -673,40 +734,56 @@ update_session_table(SessionDB, Key, Transform) -> %%-------------------------------------------------------------------- get_manager_info(#state{handler_db = HDB, - cookie_db = CDB} = _State) -> + session_db = SDB, + cookie_db = CDB, + options = Options} = _State) -> HandlerInfo = get_handler_info(HDB), + SessionInfo = which_sessions2(SDB), + OptionsInfo = + [{Item, get_option(Item, Options)} || + Item <- record_info(fields, options)], CookieInfo = httpc_cookie:which_cookies(CDB), - [{handlers, HandlerInfo}, {cookies, CookieInfo}]. + [{handlers, HandlerInfo}, + {sessions, SessionInfo}, + {options, OptionsInfo}, + {cookies, CookieInfo}]. + +sort_handlers(Unsorted) -> + sort_handlers2(lists:keysort(1, Unsorted)). + +sort_handlers2([]) -> + []; +sort_handlers2([{HandlerPid, RequestId}|L]) -> + {Handler, Rest} = sort_handlers2(HandlerPid, [RequestId], L), + [Handler | sort_handlers2(Rest)]. + +sort_handlers2(HandlerPid, Reqs, []) -> + {{HandlerPid, lists:sort(Reqs)}, []}; +sort_handlers2(HandlerPid, Reqs, [{HandlerPid, ReqId}|Rest]) -> + sort_handlers2(HandlerPid, [ReqId|Reqs], Rest); +sort_handlers2(HandlerPid1, Reqs, [{HandlerPid2, _}|_] = Rest) + when HandlerPid1 =/= HandlerPid2 -> + {{HandlerPid1, lists:sort(Reqs)}, Rest}. get_handler_info(Tab) -> - Pattern = #handler_info{handler = '$1', - state = '$2', - _ = '_'}, - Handlers1 = [{Pid, State} || [Pid, State] <- ets:match(Tab, Pattern)], - F = fun({Pid, State} = Elem, Acc) when State =/= canceled -> - case lists:keymember(Pid, 1, Acc) of - true -> - Acc; - false -> - [Elem | Acc] - end; - (_, Acc) -> - Acc - end, - Handlers2 = lists:foldl(F, [], Handlers1), - Handlers3 = [{Pid, State, - case (catch httpc_handler:info(Pid)) of - {'EXIT', _} -> + Pattern = {'$2', '$1', '_'}, + Handlers1 = [{Pid, Id} || [Pid, Id] <- ets:match(Tab, Pattern)], + Handlers2 = sort_handlers(Handlers1), + Handlers3 = [{Pid, Reqs, + try + begin + httpc_handler:info(Pid) + end + catch + _:_ -> %% Why would this crash? %% Only if the process has died, but we don't %% know about it? - []; - Else -> - Else - end} || - {Pid, State} <- Handlers2], + [] + end} || {Pid, Reqs} <- Handlers2], Handlers3. + handle_request(#request{settings = #http_options{version = "HTTP/0.9"}} = Request, State) -> @@ -758,19 +835,21 @@ handle_request(Request, State = #state{options = Options}) -> {reply, {ok, NewRequest#request.id}, State}. -start_handler(Request, State) -> +start_handler(#request{id = Id, + from = From} = Request, + #state{profile_name = ProfileName, + handler_db = HandlerDb, + options = Options}) -> {ok, Pid} = case is_inets_manager() of true -> httpc_handler_sup:start_child([whereis(httpc_handler_sup), - Request, State#state.options, - State#state.profile_name]); + Request, Options, ProfileName]); false -> - httpc_handler:start_link(self(), Request, State#state.options, - State#state.profile_name) + httpc_handler:start_link(self(), Request, Options, ProfileName) end, - ets:insert(State#state.handler_db, {Request#request.id, - Pid, Request#request.from}), + HandlerInfo = {Id, Pid, From}, + ets:insert(HandlerDb, HandlerInfo), erlang:monitor(process, Pid). @@ -827,12 +906,14 @@ select_session(Candidates, Max) -> {ok, HandlerPid} end. -pipeline_or_keep_alive(Request, HandlerPid, State) -> +pipeline_or_keep_alive(#request{id = Id, + from = From} = Request, + HandlerPid, + #state{handler_db = HandlerDb} = State) -> case (catch httpc_handler:send(Request, HandlerPid)) of ok -> - ets:insert(State#state.handler_db, {Request#request.id, - HandlerPid, - Request#request.from}); + HandlerInfo = {Id, HandlerPid, From}, + ets:insert(HandlerDb, HandlerInfo); _ -> % timeout pipelining failed start_handler(Request, State) end. diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index c7029f7b31..2adb2a0fc8 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,14 +18,25 @@ {"%VSN%", [ + {"5.9", + [ + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_manager, soft, soft_purge, soft_purge, []} + ] + }, {"5.8.1", [ + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, http_uri, soft_purge, soft_purge, []}, {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, {load_module, httpc, soft_purge, soft_purge, [http_uri, httpc_manager]}, - {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, {update, inets_sup, soft, soft_purge, soft_purge, []}, @@ -35,36 +46,15 @@ {load_module, httpd_script_env, soft_purge, soft_purge, []}, {load_module, inets, soft_purge, soft_purge, [inets_trace]}, - {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, {update, httpd_sup, soft, soft_purge, soft_purge, []}, {add_module, inets_trace} ] }, - {"5.8", + {"5.8", [ - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, - - {load_module, httpc, soft_purge, soft_purge, - [http_uri, httpc_manager]}, - - {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, - {update, inets_sup, soft, soft_purge, soft_purge, []}, - - {load_module, inets, soft_purge, soft_purge, [inets_trace]}, - - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, []}, - {load_module, httpd_script_env, soft_purge, soft_purge, []}, - - {load_module, ftp, soft_purge, soft_purge, []}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, [http_uri, httpc_handler]}, - {update, httpd_sup, soft, soft_purge, soft_purge, []}, - - {add_module, inets_trace} + {restart_application, inets} ] }, {"5.7.2", @@ -74,14 +64,25 @@ } ], [ + {"5.9", + [ + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_manager, soft, soft_purge, soft_purge, []} + ] + }, {"5.8.1", [ + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, http_uri, soft_purge, soft_purge, []}, {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, {load_module, httpc, soft_purge, soft_purge, [http_uri, httpc_manager]}, - {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, {update, inets_sup, soft, soft_purge, soft_purge, []}, @@ -91,36 +92,15 @@ {load_module, httpd_script_env, soft_purge, soft_purge, []}, {load_module, inets, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, {update, httpd_sup, soft, soft_purge, soft_purge, []}, {remove, {inets_trace, soft_purge, brutal_purge}} ] }, {"5.8", [ - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, - - {load_module, httpc, soft_purge, soft_purge, - [http_uri, httpc_manager]}, - - {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, - {update, inets_sup, soft, soft_purge, soft_purge, []}, - - {load_module, inets, soft_purge, soft_purge, []}, - - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, []}, - {load_module, httpd_script_env, soft_purge, soft_purge, []}, - - {load_module, ftp, soft_purge, soft_purge, []}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, [http_uri, httpc_handler]}, - {update, httpd_sup, soft, soft_purge, soft_purge, []}, - - {remove, {inets_trace, soft_purge, brutal_purge}} + {restart_application, inets} ] }, {"5.7.2", diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk index d24cc0aea3..adef32dc19 100644 --- a/lib/inets/src/inets_app/inets.mk +++ b/lib/inets/src/inets_app/inets.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2011. All Rights Reserved. +# Copyright Ericsson AB 2010-2012. 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 @@ -33,10 +33,6 @@ ifeq ($(WARN_UNUSED_WARS), true) ERL_COMPILE_FLAGS += +warn_unused_vars endif -ifeq ($(shell erl -noshell -eval 'io:format("~4s", [erlang:system_info(otp_release)])' -s init stop), R14B) -INETS_ERL_COMPILE_FLAGS += -D'OTP-R14B-COMPILER' -endif - INETS_APP_VSN_COMPILE_FLAGS = \ +'{parse_transform,sys_pre_attributes}' \ +'{attribute,insert,app_vsn,$(APP_VSN)}' diff --git a/lib/inets/src/inets_app/inets_service.erl b/lib/inets/src/inets_app/inets_service.erl index a057a51e2c..d17fdfe13e 100644 --- a/lib/inets/src/inets_app/inets_service.erl +++ b/lib/inets/src/inets_app/inets_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -20,20 +20,6 @@ -module(inets_service). --ifdef('OTP-R14B-COMPILER'). - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{start_standalone, 1}, - {start_service, 1}, - {stop_service, 1}, - {services, 0}, - {service_info, 1}]; -behaviour_info(_) -> - undefined. - --else. %% Starts service stand-alone %% start_standalone(Config) -> % {ok, Pid} | {error, Reason} @@ -83,4 +69,3 @@ behaviour_info(_) -> -callback service_info(Service :: term()) -> {ok, [{Property :: term(), Value :: term()}]} | {error, Reason :: term()}. --endif. diff --git a/lib/inets/src/tftp/tftp.erl b/lib/inets/src/tftp/tftp.erl index 0d7ae1a89e..1621add246 100644 --- a/lib/inets/src/tftp/tftp.erl +++ b/lib/inets/src/tftp/tftp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -224,20 +224,6 @@ service_info/1 ]). --ifdef('OTP-R14B-COMPILER'). - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{prepare, 6}, - {open, 6}, - {read, 1}, - {write, 2}, - {abort, 3}]; -behaviour_info(_) -> - undefined. - --else. -type peer() :: {PeerType :: inet | inet6, PeerHost :: inet:ip_address(), @@ -280,8 +266,6 @@ behaviour_info(_) -> -callback abort(Code :: error_code(), string(), State :: term()) -> 'ok'. --endif. - -include("tftp.hrl"). diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index a116edef77..1cdd96f0b0 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -761,116 +761,158 @@ http_inets_pipe(Config) when is_list(Config) -> test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), - - httpc:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1"), - {ok, RequestId1} = + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), + + httpc:set_options([{pipeline_timeout, 50000}]), + + p("test_pipeline -> issue (async) request 1" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdA1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - test_server:format("RequestId1: ~p~n", [RequestId1]), - p("test_pipeline -> RequestId1: ~p", [RequestId1]), - - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request 2"), - {ok, RequestId2} = + tsp("RequestIdA1: ~p", [RequestIdA1]), + p("test_pipeline -> RequestIdA1: ~p" + "~n when profile info: ~p", [RequestIdA1, httpc:info()]), + + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request A2, A3 and A4" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdA2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId2: ~p", [RequestId2]), - p("test_pipeline -> RequestId2: ~p", [RequestId2]), - - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = + {ok, RequestIdA3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdA4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdAs => A2: ~p, A3: ~p and A4: ~p", + [RequestIdA2, RequestIdA3, RequestIdA4]), + p("test_pipeline -> RequestIds => A2: ~p, A3: ~p and A4: ~p" + "~n when profile info: ~p", + [RequestIdA2, RequestIdA3, RequestIdA4, httpc:info()]), + + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = httpc:request(get, {URL, []}, [], []), + + p("test_pipeline -> expect reply for (async) request A1, A2, A3 and A4" + "~n when profile info: ~p", [httpc:info()]), + pipeline_await_async_reply([{RequestIdA1, a1, 200}, + {RequestIdA2, a2, 200}, + {RequestIdA3, a3, 200}, + {RequestIdA4, a4, 200}], ?MINS(1)), - p("test_pipeline -> expect reply for (async) request 1 or 2"), - receive - {http, {RequestId1, {{_, 200, _}, _, _}}} -> - p("test_pipeline -> received reply for (async) request 1 - now wait for 2"), - receive - {http, {RequestId2, {{_, 200, _}, _, _}}} -> - p("test_pipeline -> received reply for (async) request 2"), - ok; - {http, Msg1} -> - tsf(Msg1) - end; - {http, {RequestId2, {{_, 200, _}, _, _}}} -> - io:format("test_pipeline -> received reply for (async) request 2 - now wait for 1"), - receive - {http, {RequestId1, {{_, 200, _}, _, _}}} -> - io:format("test_pipeline -> received reply for (async) request 1"), - ok; - {http, Msg2} -> - tsf(Msg2) - end; - {http, Msg3} -> - tsf(Msg3) - after 60000 -> - receive Any1 -> - tsp("received crap after timeout: ~n ~p", [Any1]), - tsf({error, {timeout, Any1}}) - end - end, - - p("test_pipeline -> sleep some"), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request 4"), - {ok, RequestId3} = + p("test_pipeline -> sleep some" + "~n when profile info: ~p", [httpc:info()]), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request B1, B2, B3 and B4" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdB1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId3: ~p", [RequestId3]), - p("test_pipeline -> RequestId3: ~p", [RequestId3]), - - p("test_pipeline -> issue (async) request 5"), - {ok, RequestId4} = + {ok, RequestIdB2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId4: ~p~n", [RequestId4]), - p("test_pipeline -> RequestId4: ~p", [RequestId4]), - - p("test_pipeline -> cancel (async) request 4"), - ok = httpc:cancel_request(RequestId3), - - p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), - receive - {http, {RequestId3, _}} -> - tsf(http_cancel_request_failed) - after 3000 -> - ok - end, - - p("test_pipeline -> expect reply for (async) request 4"), - Body = - receive - {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> - p("test_pipeline -> received reply for (async) request 5"), - tsp("Receive : ~p", [Res]), - BinBody4; - {http, Msg4} -> - tsf(Msg4) - after 60000 -> - receive Any2 -> - tsp("received crap after timeout: ~n ~p", [Any2]), - tsf({error, {timeout, Any2}}) - end - end, + {ok, RequestIdB3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdB4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p", + [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4]), + p("test_pipeline -> RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p" + "~n when profile info: ~p", + [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4, httpc:info()]), + + p("test_pipeline -> cancel (async) request B2" + "~n when profile info: ~p", [httpc:info()]), + ok = httpc:cancel_request(RequestIdB2), + + p("test_pipeline -> " + "expect *no* reply for cancelled (async) request B2 (for 3 secs)" + "~n when profile info: ~p", [httpc:info()]), + receive + {http, {RequestIdB2, _}} -> + tsf(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request B1, B3 and B4" + "~n when profile info: ~p", [httpc:info()]), + Bodies = pipeline_await_async_reply([{RequestIdB1, b1, 200}, + {RequestIdB3, b3, 200}, + {RequestIdB4, b4, 200}], ?MINS(1)), + [{b1, Body}|_] = Bodies, - p("test_pipeline -> check reply for (async) request 5"), + p("test_pipeline -> check reply for (async) request B1" + "~n when profile info: ~p", [httpc:info()]), inets_test_lib:check_body(binary_to_list(Body)), - - p("test_pipeline -> ensure no unexpected incomming"), + + p("test_pipeline -> ensure no unexpected incomming" + "~n when profile info: ~p", [httpc:info()]), receive {http, Any} -> tsf({unexpected_message, Any}) after 500 -> ok end, - - p("test_pipeline -> done"), + + p("test_pipeline -> done" + "~n when profile info: ~p", [httpc:info()]), ok. +pipeline_await_async_reply(ReqIds, Timeout) -> + pipeline_await_async_reply(ReqIds, Timeout, []). + +pipeline_await_async_reply([], _, Acc) -> + lists:keysort(1, Acc); +pipeline_await_async_reply(ReqIds, Timeout, Acc) when Timeout > 0 -> + T1 = inets_test_lib:timestamp(), + p("pipeline_await_async_reply -> await replies" + "~n ReqIds: ~p" + "~n Timeout: ~p", [ReqIds, Timeout]), + receive + {http, {RequestId, {{_, Status, _}, _, Body}}} -> + p("pipeline_await_async_reply -> received reply for" + "~n RequestId: ~p" + "~n Status: ~p", [RequestId, Status]), + case lists:keysearch(RequestId, 1, ReqIds) of + {value, {RequestId, N, Status}} -> + p("pipeline_await_async_reply -> " + "found expected request ~w", [N]), + ReqIds2 = lists:keydelete(RequestId, 1, ReqIds), + NewTimeout = Timeout - (inets_test_lib:timestamp()-T1), + pipeline_await_async_reply(ReqIds2, NewTimeout, + [{N, Body} | Acc]); + {value, {RequestId, N, WrongStatus}} -> + p("pipeline_await_async_reply -> " + "found request ~w with wrong status", [N]), + tsf({reply_with_unexpected_status, + {RequestId, N, WrongStatus}}); + false -> + tsf({unexpected_reply, {RequestId, Status}}) + end; + {http, Msg} -> + tsf({unexpected_reply, Msg}) + after Timeout -> + receive + Any -> + tsp("pipeline_await_async_reply -> " + "received unknown data after timeout: " + "~n ~p", [Any]), + tsf({timeout, {unknown, Any}}) + end + end; +pipeline_await_async_reply(ReqIds, _, Acc) -> + tsp("pipeline_await_async_reply -> " + "timeout: " + "~n ~p" + "~nwhen" + "~n ~p", [ReqIds, Acc]), + tsf({timeout, ReqIds, Acc}). + + + %%------------------------------------------------------------------------- http_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index cb1214b7fb..387263ce58 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -82,19 +82,23 @@ actions(Type, Port, Host, Node) -> [{statuscode, 200}, {version, "HTTP/1.0"}]). + %%------------------------------------------------------------------------- security(ServerRoot, Type, Port, Host, Node) -> - %% io:format(user, "~w:security -> entry with" - %% "~n ServerRoot: ~p" - %% "~n Type: ~p" - %% "~n Port: ~p" - %% "~n Host: ~p" - %% "~n Node: ~p" - %% "~n", [?MODULE, ServerRoot, Type, Port, Host, Node]), - -%% io:format(user, "~w:security -> register~n", [?MODULE]), + tsp("security -> " + "entry with" + "~n ServerRoot: ~p" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n Node: ~p", [ServerRoot, Type, Port, Host, Node]), + + tsp("security -> " + "register - receive security events"), global:register_name(mod_security_test, self()), % Receive events + tsp("security -> " + "sleep"), test_server:sleep(5000), OpenDir = filename:join([ServerRoot, "htdocs", "open"]), @@ -102,133 +106,240 @@ security(ServerRoot, Type, Port, Host, Node) -> %% Test blocking / unblocking of users. %% /open, require user one Aladdin -%% io:format(user, "~w:security -> remove user~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "remove all existing users"), remove_users(Node, ServerRoot, Host, Port, "open"), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'one' - expect 401"), auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "onePassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'two' - expect 401"), auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "two"}, {password, "twoPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'Alladin' - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "Aladdin", "AladdinPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "Aladdin"}, {password, "AladdinPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> add users~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "add user 'one'"), add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []), + + tsp("security -> " + "blocking and unblocking of users - " + "add user 'two'"), add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request 1 for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request 2 for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> await block security event~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "await block security event (two failed attempts)"), receive_security_event({event, user_block, Port, OpenDir, [{user, "one"}]}, Node, Port), -%% io:format(user, "~w:security -> unregister~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "unregister - no more security events"), global:unregister_name(mod_security_test), % No more events. -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with correct password - expect 403"), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 403}]), %% User "one" should be blocked now.. - %% [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "list blocked users - 'one' should be the only one"), case list_blocked_users(Node, Port) of [{"one",_, Port, OpenDir,_}] -> ok; Blocked -> - %% io:format(user, "~w:security -> Blocked: ~p" - %% "~n", [?MODULE, Blocked]), + tsp(" *** unexpected blocked users ***" + "~n Blocked: ~p", [Blocked]), exit({unexpected_blocked, Blocked}) end, - -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port,OpenDir), -%% io:format(user, "~w:security -> unblock user~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "list users blocked for dir '~p' - " + "user 'one' should be the only one", [OpenDir]), + [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir), + + tsp("security -> " + "blocking and unblocking of users - " + "unblock user 'one' for dir '~p'", [OpenDir]), true = unblock_user(Node, "one", Port, OpenDir), - %% User "one" should not be blocked any more.. -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + %% User "one" should not be blocked any more. + + tsp("security -> " + "blocking and unblocking of users - " + "ensure user 'one' is no longer blocked"), [] = list_blocked_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - [] = list_blocked_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with correct password - expect 200"), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 200}]), + + %% Test list_auth_users & auth_timeout -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - ["one"] = list_auth_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "auth request for user 'two' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p' - expect user 'one'", [OpenDir]), ["one"] = list_auth_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "auth request for user 'two' with correct password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p' - expect user 'one'", [OpenDir]), ["one"] = list_auth_users(Node, Port, OpenDir), + %% Wait for successful auth to timeout. + tsp("security -> " + "list-auth-users and auth-timeout - " + "wait for successful auth to timeout"), test_server:sleep(?AUTH_TIMEOUT*1001), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect none"), [] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p'~n - expect none", [OpenDir]), [] = list_auth_users(Node, Port, OpenDir), + %% "two" is blocked. -%% io:format(user, "~w:security -> unblock user~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "unblock user 'two' for dir '~p'", [OpenDir]), true = unblock_user(Node, "two", Port, OpenDir), + + %% Test explicit blocking. Block user 'two'. -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - list blocked users - should be none"), [] = list_blocked_users(Node,Port,OpenDir), -%% io:format(user, "~w:security -> block user~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - " + "block user 'two' for dir '~p'", [OpenDir]), true = block_user(Node, "two", Port, OpenDir, 10), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - " + "auth request for user 'two' with correct password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", - [{statuscode, 401}]). + [{statuscode, 401}]), + tsp("security -> " + "done"). + %%------------------------------------------------------------------------- auth(Type, Port, Host, Node) -> + tsp("auth -> " + "entry with" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n Node: ~p", [Type, Port, Host, Node]), + %% Authentication required! ok = httpd_test_lib:verify_request(Type,Host,Port,Node, "GET /open/ HTTP/1.0\r\n\r\n", @@ -913,13 +1024,11 @@ list_users(Node, Root, _Host, Port, Dir) -> receive_security_event(Event, Node, Port) -> - %% io:format(user, "~w:receive_security_event -> entry with" - %% "~n Event: ~p" - %% "~n Node: ~p" - %% "~n Port: ~p" - %% "~n", [?MODULE, Event, Node, Port]), + tsp("receive_security_event -> await ~w event", [element(2, Event)]), receive Event -> + tsp("receive_security_event -> " + "received expected ~w event", [element(2, Event)]), ok; {'EXIT', _, _} -> receive_security_event(Event, Node, Port) @@ -1027,8 +1136,14 @@ check_lists_members1(L1,L2) -> {error,{lists_not_equal,L1,L2}}. -%% tsp(F) -> -%% inets_test_lib:tsp(F). +%% p(F) -> +%% p(F, []). + +%% p(F, A) -> +%% io:format(user, "~w:" ++ F ++ "~n", [?MODULE|A]). + +tsp(F) -> + inets_test_lib:tsp(F). tsp(F, A) -> inets_test_lib:tsp(F, A). diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index c94be796cd..0f8671b682 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -31,6 +31,7 @@ send/3, close/2]). -export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]). -export([info/4, log/4, debug/4, print/4]). +-export([timestamp/0, formated_timestamp/0]). -export([tsp/1, tsp/2, tsf/1, tss/1]). -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). @@ -530,34 +531,27 @@ connect(ip_comm, Host, Port, Opts, Type) -> "~n Opts: ~p" "~n Type: ~p", [Host, Port, Opts, Type]), - case gen_tcp:connect(Host, Port, Opts) of + case gen_tcp:connect(Host, Port, Opts, timer:seconds(10)) of {ok, Socket} -> tsp("connect success"), {ok, Socket}; - {error, nxdomain} when Type =:= inet6 -> - tsp("connect error nxdomain when" - "~n Opts: ~p", [Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - {error, eafnosupport} when Type =:= inet6 -> - tsp("connect error eafnosupport when" - "~n Opts: ~p", [Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - {error, econnreset} when Type =:= inet6 -> - tsp("connect error econnreset when" - "~n Opts: ~p", [Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - {error, enetunreach} when Type =:= inet6 -> - tsp("connect error eafnosupport when" - "~n Opts: ~p", [Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - {error, econnrefused} when Type =:= inet6 -> - tsp("connect error econnrefused when" - "~n Opts: ~p", [Opts]), + {error, Reason} when ((Type =:= inet6) andalso + ((Reason =:= timeout) orelse + (Reason =:= nxdomain) orelse + (Reason =:= eafnosupport) orelse + (Reason =:= econnreset) orelse + (Reason =:= enetunreach) orelse + (Reason =:= econnrefused) orelse + (Reason =:= ehostunreach))) -> + tsp("connect(ip_comm) -> Connect error: " + "~n Reason: ~p" + "~n Type: ~p" + "~n Opts: ~p", [Reason, Type, Opts]), connect(ip_comm, Host, Port, Opts -- [inet6], inet); Error -> - tsp("connect(ip_conn) -> Fatal connect error: " + tsp("connect(ip_comm) -> Fatal connect error: " "~n Error: ~p" "~nwhen" "~n Host: ~p" @@ -642,6 +636,9 @@ tsf(Reason) -> tss(Time) -> test_server:sleep(Time). +timestamp() -> + http_util:timestamp(). + formated_timestamp() -> format_timestamp( os:timestamp() ). diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 488947c3a1..949eceea7f 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.9 +INETS_VSN = 5.9.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 2d045faa0f..442837d57d 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -34,6 +34,98 @@ <section> + <title>SNMP Development Toolkit 4.22.1</title> + <p>Version 4.22.1 supports code replacement in runtime from/to + version 4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and + 4.21. </p> + + <section> + <title>Improvements and new features</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>[agent] Sematic fixes to SNMP-USER-BASED-SM-MIB. + The semantics allow the <c>usmUserAuthKeyChange</c> and + <c>usmUserPrivKeyChange</c> objects to be written to in the + same set requests that also creates and clones the user. + This was not possible beforehand, causing test tools checking + semantic SNMPv3 behaviour to fail on a lot of test cases. </p> + <p>Furthermore, once the user has been cloned by writing to an + instance of <c>usmUserCloneFrom</c>, further set-operations to + the same object will not return an error, but be no-ops. + Especially, it must be avoided to copy security parameters + again (possibly even from a different user). </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10166</p> + </item> + + <item> + <p>[agent] Errors in <c>vacmAccessTable</c> RowStatus handling. + There are problems with the handling of vacmAccessTableStatus + that cause some SNMP test suites to report errors. + Most notably, erroneous set operations frequently cause "genErr" + errors to be returned. These "genErr" errors are usually caused + by badmatch exceptions coming from + <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c> + if the row does not exist. </p> + <p>The semantics of the RowStatus handling in that table has + been adjusted to be compliant with the RowStatus + textual description of SNPMv2-TC MIB. </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10164</p> + </item> + </list> + + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>[agent] Fix walk over vacmAccessTable. + Fix the get_next implementation of vacmAccessTable to + return all table entries. </p> + <p>The get_next implementation of vacmAccessTable did not return + all available table data. Instead, it only returned the first + column for each row, and all columns for the last row available. </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10165</p> + </item> + + <item> + <p>[manager] + <seealso marker="snmpm#log_to_io">snmpm:log_to_io/6</seealso> + did not use the LogName argument. </p> + <p>Own Id: OTP-10066</p> + </item> + + <item> + <p>Incorrect TimeTicks decode. Also bad handling of + invalid encode (value outside of value range) for both + <c>TimeTicks</c> and <c>Unsigned32</c>. </p> + <p>Own Id: OTP-10132</p> + </item> + + </list> + + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + </section> + + </section> <!-- 4.22.1 --> + + + <section> <title>SNMP Development Toolkit 4.22</title> <p>Version 4.22 supports code replacement in runtime from/to version 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p> diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml index 340f2f1fa7..eec53162a1 100644 --- a/lib/snmp/doc/src/snmp_config.xml +++ b/lib/snmp/doc/src/snmp_config.xml @@ -963,7 +963,8 @@ Manager snmp config: 7b. User name? hobbes 7c. Security name? [hobbes] 7d. Authentication protocol (no/sha/md5)? [no] sha -7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] +7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, \ + 17,18,19,20] 7d. Priv protocol (no/des/aes)? [no] des 7f Priv [des] key (length 0 or 16)? [""] 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 7. Configure an usm user handled by this manager (y/n)? [y] n diff --git a/lib/snmp/doc/src/snmpa_network_interface_filter.xml b/lib/snmp/doc/src/snmpa_network_interface_filter.xml index 10419517dd..84953c5270 100644 --- a/lib/snmp/doc/src/snmpa_network_interface_filter.xml +++ b/lib/snmp/doc/src/snmpa_network_interface_filter.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> @@ -84,7 +84,9 @@ <title>DATA TYPES</title> <code type="none"> port() = integer() > 0 -pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report +pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | + 'set-request' | trap | 'get-bulk-request' | 'inform-request' | + report </code> <marker id="accept_recv"></marker> </section> diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index 9bbb6cdbdb..8ab3be8e18 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -173,16 +173,16 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv </type> <desc> <p>Register the manager entity (=user) responsible for specific - agent(s). </p> + agent(s). </p> <p><c>Module</c> is the callback module (snmpm_user behaviour) which - will be called whenever something happens (detected - agent, incoming reply or incoming trap/notification). - Note that this could have already been done as a - consequence of the node config. (see users.conf).</p> + will be called whenever something happens (detected + agent, incoming reply or incoming trap/notification). + Note that this could have already been done as a + consequence of the node config. (see users.conf).</p> - <p>The argument <c>DefaultAgentConfig</c> is used as default values when - this user register agents.</p> + <p>The argument <c>DefaultAgentConfig</c> is used as default + values when this user register agents.</p> <p>The type of <c>Val</c> depends on <c>Item</c>: </p> <code type="none"><![CDATA[ diff --git a/lib/snmp/doc/src/snmpm_network_interface_filter.xml b/lib/snmp/doc/src/snmpm_network_interface_filter.xml index 5f80cec94e..4dc133dd71 100644 --- a/lib/snmp/doc/src/snmpm_network_interface_filter.xml +++ b/lib/snmp/doc/src/snmpm_network_interface_filter.xml @@ -82,7 +82,9 @@ <title>DATA TYPES</title> <code type="none"> port() = integer() > 0 -pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report | trappdu +pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | + 'set-request' | trap | 'get-bulk-request' | 'inform-request' | + report | trappdu </code> <marker id="accept_recv"></marker> </section> diff --git a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl index 2e801622e7..3c4ba1af66 100644 --- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl +++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl @@ -54,6 +54,7 @@ %% Columns not accessible via SNMP -define(usmUserAuthKey, 14). -define(usmUserPrivKey, 15). +-define(is_cloning, 16). %%%----------------------------------------------------------------- @@ -564,7 +565,9 @@ usmUserTable(set, RowIndex, Cols0) -> {ok, Cols} -> ?vtrace("usmUserTable(set) -> verified" "~n Cols: ~p", [Cols]), - NCols = pre_set(RowIndex, Cols), + % check whether we're cloning. if so, get cloned params and add a few + % defaults that might be needed. + NCols = pre_set(RowIndex, validate_clone_from(RowIndex, Cols)), ?vtrace("usmUserTable(set) -> pre-set: " "~n NCols: ~p", [NCols]), %% NOTE: The NCols parameter is sent to snmp_generic, but not to @@ -730,30 +733,40 @@ validate_is_set_ok(Error, _RowIndex, _Cols) -> Error. do_validate_is_set_ok(RowIndex, Cols) -> - validate_clone_from(RowIndex, Cols), - validate_auth_protocol(RowIndex, Cols), - validate_auth_key_change(RowIndex, Cols), - validate_own_auth_key_change(RowIndex, Cols), - validate_priv_protocol(RowIndex, Cols), - validate_priv_key_change(RowIndex, Cols), - validate_own_priv_key_change(RowIndex, Cols), + NCols = validate_clone_from(RowIndex, Cols), + validate_auth_protocol(RowIndex, NCols), + validate_auth_key_change(RowIndex, NCols), + validate_own_auth_key_change(RowIndex, NCols), + validate_priv_protocol(RowIndex, NCols), + validate_priv_key_change(RowIndex, NCols), + validate_own_priv_key_change(RowIndex, NCols), ok. pre_set(RowIndex, Cols) -> + %% Remove the ?is_cloning member again; it must no longer be + %% present. + Cols0 = key1delete(?is_cloning, Cols), %% Possibly initialize the usmUserSecurityName and privacy keys case snmp_generic:table_row_exists(db(usmUserTable), RowIndex) of - true -> Cols; + true -> Cols0; false -> SecName = get_user_name(RowIndex), - [{?usmUserSecurityName, SecName} | Cols] ++ - [{?usmUserAuthKey, ""}, - {?usmUserPrivKey, ""}] + Cols1 = [{?usmUserSecurityName, SecName} | Cols0], + case proplists:get_value(?is_cloning, Cols) of + true -> + % the row is just being cloned. the cloned user's + % passwords are already present in Cols and must + % not be overwritten. + Cols1; + _ -> + Cols1 ++ [{?usmUserAuthKey, ""}, + {?usmUserPrivKey, ""}] + end end. validate_set({noError, 0}, RowIndex, Cols) -> %% Now, all is_set_ok validation steps have been executed. So %% everything is ready for the set. - set_clone_from(RowIndex, Cols), set_auth_key_change(RowIndex, Cols), set_own_auth_key_change(RowIndex, Cols), set_priv_key_change(RowIndex, Cols), @@ -769,7 +782,7 @@ validate_set(Error, _RowIndex, _Cols) -> %% no further checks. %%----------------------------------------------------------------- validate_clone_from(RowIndex, Cols) -> - case lists:keysearch(?usmUserCloneFrom, 1, Cols) of + case key1search(?usmUserCloneFrom, Cols) of {value, {_Col, RowPointer}} -> RowIndex2 = extract_row(RowPointer), OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), @@ -778,35 +791,63 @@ validate_clone_from(RowIndex, Cols) -> case OldCloneFrom of {value, Val} when Val /= noinit -> %% This means that the cloning is already done... - ok; + no_cloning(Cols); _ -> - %% Otherwise, we must check the CloneFrom value - case snmp_generic:table_get_element(db(usmUserTable), - RowIndex2, - ?usmUserStatus) of - {value, ?'RowStatus_active'} -> ok; - _ -> inconsistentName(?usmUserCloneFrom) - end + %% Otherwise, we must check the CloneFrom value. It + %% must relate to a usmUserEntry that exists and is active. + case snmp_generic:table_get_row(db(usmUserTable), RowIndex2) of + CloneFromRow when is_tuple(CloneFromRow) -> + case element(?usmUserStatus, CloneFromRow) of + ?'RowStatus_active' -> + get_cloned_cols(CloneFromRow, Cols); + _ -> + inconsistentName(?usmUserCloneFrom) + end; + undefined -> + inconsistentName(?usmUserCloneFrom) + end end; false -> - ok + % no ?usmUserCloneFrom specified, don't modify columns + no_cloning(Cols) end. +get_cloned_cols(CloneFromRow, Cols) -> + % initialize cloned columns with data from CloneFromRow + % and overwrite that again with data found in Cols + AuthP = element(?usmUserAuthProtocol, CloneFromRow), + PrivP = element(?usmUserPrivProtocol, CloneFromRow), + AuthK = element(?usmUserAuthKey, CloneFromRow), + PrivK = element(?usmUserPrivKey, CloneFromRow), + ClonedCols = [{?usmUserAuthProtocol, AuthP}, + {?usmUserPrivProtocol, PrivP}, + {?usmUserAuthKey, AuthK}, + {?usmUserPrivKey, PrivK}, + {?is_cloning, true} + ], + Func = fun({Col, _} = Item, NCols) -> + key1store(Col, NCols, Item) + end, + Cols1 = lists:foldl(Func, ClonedCols, Cols), + key1sort(Cols1). + +no_cloning(Cols0) -> + Cols1 = key1delete(?usmUserCloneFrom, Cols0), + key1delete(?is_cloning, Cols1). + validate_auth_protocol(RowIndex, Cols) -> - case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of + case key1search(?usmUserAuthProtocol, Cols) of {value, {_Col, AuthProtocol}} -> - %% Check if the row has been cloned; we can't check the + %% Check if the row is being cloned; we can't check the %% old value of authProtocol, because if the row was %% createAndWaited, the default value would have been %% written (usmNoAuthProtocol). - OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), - RowIndex, - ?usmUserCloneFrom), - case OldCloneFrom of - {value, Val} when Val /= noinit -> - %% This means that the cloning is already done; set is ok - %% if new protocol is usmNoAuthProtocol + IsCloning = proplists:get_value(?is_cloning, Cols, false), + if + not IsCloning -> + %% This means that the row is not being cloned right + %% now; set is ok if new protocol is usmNoAuthProtocol case AuthProtocol of ?usmNoAuthProtocol -> %% Check that the Priv protocl is noPriv @@ -821,7 +862,7 @@ validate_auth_protocol(RowIndex, Cols) -> _ -> wrongValue(?usmUserAuthProtocol) end; - _ -> + true -> %% Otherwise, check that the new protocol is known, %% and that the system we're running supports the %% hash function. @@ -867,7 +908,7 @@ validate_own_priv_key_change(RowIndex, Cols) -> %% Check that the requesting user is the same as the modified user validate_requester(RowIndex, Cols, KeyChangeCol) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, _} -> case get(sec_model) of % Check the securityModel in the request ?SEC_USM -> ok; @@ -890,17 +931,14 @@ validate_requester(RowIndex, Cols, KeyChangeCol) -> end. validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, {_Col, KeyC}} -> %% Check if the row has been cloned; or if it is cloned in %% this set-operation. OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), RowIndex, ?usmUserCloneFrom), - IsClonePresent = case lists:keysearch(?usmUserCloneFrom, 1, Cols) of - {value, _} -> true; - false -> false - end, + IsClonePresent = proplists:get_value(?is_cloning, Cols, false), %% Set is ok if 1) the user already is created, 2) this is %% a new user, which has been cloned, or is about to be %% cloned. @@ -912,7 +950,7 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> %% The user is cloned in this operation ok; _ -> - %% The user doen't exist, or hasn't been cloned, + %% The user doesn't exist, or hasn't been cloned, %% and is not cloned in this operation. inconsistentName(KeyChangeCol) end, @@ -939,17 +977,15 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> end. validate_priv_protocol(RowIndex, Cols) -> - case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of + case key1search(?usmUserPrivProtocol, Cols) of {value, {_Col, PrivProtocol}} -> %% Check if the row has been cloned; we can't check the %% old value of privhProtocol, because if the row was %% createAndWaited, the default value would have been %% written (usmNoPrivProtocol). - OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), - RowIndex, - ?usmUserCloneFrom), - case OldCloneFrom of - {value, Val} when Val /= noinit -> + IsCloning = proplists:get_value(?is_cloning, Cols, false), + if + not IsCloning -> %% This means that the cloning is already done; set is ok %% if new protocol is usmNoPrivProtocol case PrivProtocol of @@ -962,7 +998,7 @@ validate_priv_protocol(RowIndex, Cols) -> _ -> wrongValue(?usmUserPrivProtocol) end; - _ -> + true -> %% Otherwise, check that the new protocol is known, %% and that the system we're running supports the %% crypto function. @@ -1005,31 +1041,6 @@ validate_priv_protocol(RowIndex, Cols) -> end. -set_clone_from(RowIndex, Cols) -> - %% If CloneFrom is modified, do the cloning. - case lists:keysearch(?usmUserCloneFrom, 1, Cols) of - {value, {_Col, RowPointer}} -> - RowIndex2 = extract_row(RowPointer), % won't fail - CloneRow = snmp_generic:table_get_row(db(usmUserTable), RowIndex2, - foi(usmUserTable)), - AuthP = element(?usmUserAuthProtocol, CloneRow), - PrivP = element(?usmUserPrivProtocol, CloneRow), - AuthK = element(?usmUserAuthKey, CloneRow), - PrivK = element(?usmUserPrivKey, CloneRow), - SCols = [{?usmUserAuthProtocol, AuthP}, - {?usmUserPrivProtocol, PrivP}, - {?usmUserAuthKey, AuthK}, - {?usmUserPrivKey, PrivK}], - case snmp_generic:table_set_elements(db(usmUserTable), - RowIndex, - SCols) of - true -> ok; - false -> {commitFailed, ?usmUserCloneFrom} - end; - false -> - ok - end. - set_auth_key_change(RowIndex, Cols) -> set_key_change(RowIndex, Cols, ?usmUserAuthKeyChange, auth). @@ -1043,7 +1054,7 @@ set_own_priv_key_change(RowIndex, Cols) -> set_key_change(RowIndex, Cols, ?usmUserOwnPrivKeyChange, priv). set_key_change(RowIndex, Cols, KeyChangeCol, Type) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, {_Col, KeyChange}} -> KeyCol = case Type of auth -> ?usmUserAuthKey; @@ -1071,11 +1082,11 @@ extract_row([H | T], [H | T2]) -> extract_row(T, T2); extract_row([], [?usmUserSecurityName | T]) -> T; extract_row(_, _) -> wrongValue(?usmUserCloneFrom). -%% Pre: the user exixt +%% Pre: the user exists or is being cloned in this operation get_auth_proto(RowIndex, Cols) -> - %% The protocol can be chanegd by the request too, otherwise, + %% The protocol can be changed by the request too, otherwise, %% check the stored protocol. - case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of + case key1search(?usmUserAuthProtocol, Cols) of {value, {_, Protocol}} -> Protocol; false -> @@ -1090,11 +1101,11 @@ get_auth_proto(RowIndex, Cols) -> end end. -%% Pre: the user exixt +%% Pre: the user exists or is being cloned in this operation get_priv_proto(RowIndex, Cols) -> - %% The protocol can be chanegd by the request too, otherwise, + %% The protocol can be changed by the request too, otherwise, %% check the stored protocol. - case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of + case key1search(?usmUserPrivProtocol, Cols) of {value, {_, Protocol}} -> Protocol; false -> @@ -1232,6 +1243,27 @@ set_sname(_) -> %% Keep it, if already set. error(Reason) -> throw({error, Reason}). + +%%----------------------------------------------------------------- +%% lists key-function(s) wrappers + +-compile({inline,key1delete/2}). +key1delete(Key, List) -> + lists:keydelete(Key, 1, List). + +-compile({inline,key1search/2}). +key1search(Key, List) -> + lists:keysearch(Key, 1, List). + +-compile({inline,key1store/3}). +key1store(Key, List, Elem) -> + lists:keystore(Key, 1, List, Elem). + +-compile({inline,key1sort/1}). +key1sort(List) -> + lists:keysort(1, List). + + %%----------------------------------------------------------------- info_msg(F, A) -> diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index 479a44795f..436f15eb9c 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -565,45 +565,85 @@ vacmAccessTable(is_set_ok, RowIndex, Cols0) -> case (catch verify_vacmAccessTable_cols(Cols0, [])) of {ok, Cols} -> IsValidKey = is_valid_key(RowIndex), - case lists:keysearch(?vacmAccessStatus, 1, Cols) of - %% Ok, if contextMatch is init - {value, {Col, ?'RowStatus_active'}} -> - {ok, Row} = snmpa_vacm:get_row(RowIndex), - case element(?vacmAContextMatch, Row) of - noinit -> {inconsistentValue, Col}; - _ -> {noError, 0} + StatusCol = lists:keyfind(?vacmAccessStatus, 1, Cols), + MaybeRow = snmpa_vacm:get_row(RowIndex), + case {StatusCol, MaybeRow} of + {{Col, ?'RowStatus_active'}, false} -> + %% row does not yet exist => inconsistentValue + %% (see SNMPv2-TC.mib, RowStatus textual convention) + {inconsistentValue, Col}; + {{Col, ?'RowStatus_active'}, {ok, Row}} -> + %% Ok, if contextMatch is init + case element(?vacmAContextMatch, Row) of + noinit -> + %% check whether ContextMatch is being set in + %% the same operation + case proplists:get_value(?vacmAccessContextMatch, + Cols) of + undefined -> + %% no, we can't make this row active yet + {inconsistentValue, Col}; + _ -> + %% ok, activate the row + {noError, 0} + end; + _ -> + {noError, 0} end; - {value, {Col, ?'RowStatus_notInService'}} -> % Ok, if not notReady - {ok, Row} = snmpa_vacm:get_row(RowIndex), + {{Col, ?'RowStatus_notInService'}, false} -> + %% row does not yet exist => inconsistentValue + %% (see SNMPv2-TC.mib, RowStatus textual convention) + {inconsistentValue, Col}; + {{Col, ?'RowStatus_notInService'}, {ok, Row}} -> + %% Ok, if not notReady case element(?vacmAStatus, Row) of - ?'RowStatus_notReady' -> {inconsistentValue, Col}; - _ -> {noError, 0} + ?'RowStatus_notReady' -> + {inconsistentValue, Col}; + _ -> + {noError, 0} end; - {value, {Col, ?'RowStatus_notReady'}} -> % never ok! + {{Col, ?'RowStatus_notReady'}, _} -> + %% never ok! {inconsistentValue, Col}; - {value, {Col, ?'RowStatus_createAndGo'}} -> % ok, if it doesn't exist + {{Col, ?'RowStatus_createAndGo'}, false} -> + %% ok, if it doesn't exist Res = lists:keysearch(?vacmAccessContextMatch, 1, Cols), - case snmpa_vacm:get_row(RowIndex) of - false when (IsValidKey =:= true) andalso - is_tuple(Res) -> {noError, 0}; - false -> {noCreation, Col}; % Bad RowIndex - _ -> {inconsistentValue, Col} + if + IsValidKey =/= true -> + %% bad RowIndex => noCreation + {noCreation, Col}; + is_tuple(Res) -> + %% required field is present => noError + {noError, 0}; + true -> + %% required field is missing => inconsistentValue + {inconsistentValue, Col} end; - {value, {Col, ?'RowStatus_createAndWait'}} -> % ok, if it doesn't exist - case snmpa_vacm:get_row(RowIndex) of - false when (IsValidKey =:= true) -> {noError, 0}; - false -> {noCreation, Col}; % Bad RowIndex - _ -> {inconsistentValue, Col} + {{Col, ?'RowStatus_createAndGo'}, _} -> + %% row already exists => inconsistentValue + {inconsistentValue, Col}; + {{Col, ?'RowStatus_createAndWait'}, false} -> + %% ok, if it doesn't exist + if + IsValidKey =:= true -> + %% RowIndex is valid => noError + {noError, 0}; + true -> + {noCreation, Col} end; - {value, {_Col, ?'RowStatus_destroy'}} -> % always ok! + {{Col, ?'RowStatus_createAndWait'}, _} -> + %% Row already exists => inconsistentValue + {inconsistentValue, Col}; + {value, {_Col, ?'RowStatus_destroy'}} -> + %% always ok! {noError, 0}; - _ -> % otherwise, it's a change; it must exist - case snmpa_vacm:get_row(RowIndex) of - {ok, _} -> - {noError, 0}; - false -> - {inconsistentName, element(1, hd(Cols))} - end + {_, false} -> + %% otherwise, it's a row change; + %% row does not exist => inconsistentName + {inconsistentName, element(1, hd(Cols))}; + _ -> + %% row change and row exists => noError + {noError, 0} end; Error -> Error @@ -734,10 +774,15 @@ do_vacmAccessTable_set(RowIndex, Cols) -> %% Cols are sorted, and all columns are > 3. +do_get_next(_RowIndex, []) -> + % Cols can be empty because we're called for each + % output of split_cols(); and one of that may well + % be empty. + []; do_get_next(RowIndex, Cols) -> case snmpa_vacm:get_next_row(RowIndex) of {NextIndex, Row} -> - F1 = fun(Col) when Col < ?vacmAccessStatus -> + F1 = fun(Col) when Col =< ?vacmAccessStatus -> {[Col | NextIndex], element(Col-3, Row)}; (_) -> endOfTable @@ -745,9 +790,9 @@ do_get_next(RowIndex, Cols) -> lists:map(F1, Cols); false -> case snmpa_vacm:get_next_row([]) of - {_NextIndex, Row} -> + {NextIndex2, Row} -> F2 = fun(Col) when Col < ?vacmAccessStatus -> - {[Col+1 | RowIndex], element(Col-2, Row)}; + {[Col+1 | NextIndex2], element(Col-2, Row)}; (_) -> endOfTable end, diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 8360d88c94..593ddd82bd 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,8 +22,18 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.22", + [ + {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []} + ] + }, {"4.21.7", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -50,6 +60,8 @@ }, {"4.21.6", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -76,6 +88,8 @@ }, {"4.21.5", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -107,6 +121,8 @@ }, {"4.21.4", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -141,6 +157,8 @@ }, {"4.21.3", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -175,6 +193,8 @@ }, {"4.21.2", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -211,6 +231,8 @@ }, {"4.21.1", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -248,6 +270,8 @@ }, {"4.21", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -288,8 +312,19 @@ %% ------D o w n g r a d e --------------------------------------------------- [ + {"4.22", + [ + {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []} + ] + }, {"4.21.7", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_pdus, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -316,6 +351,8 @@ }, {"4.21.6", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -342,6 +379,8 @@ }, {"4.21.5", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -373,6 +412,8 @@ }, {"4.21.4", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -407,6 +448,8 @@ }, {"4.21.3", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -441,6 +484,8 @@ }, {"4.21.2", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -477,6 +522,8 @@ }, {"4.21.1", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, @@ -514,6 +561,8 @@ }, {"4.21", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 89eaee9f80..f590892c66 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -1411,7 +1411,7 @@ log_to_io(LogDir, Mibs, LogName, LogFile) -> log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start). log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> - snmp:log_to_io(LogDir, Mibs, LogFile, Start, Stop). + snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop). change_log_size(NewSize) -> diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl index 0788d86b2d..5b8be90a71 100644 --- a/lib/snmp/src/misc/snmp_pdus.erl +++ b/lib/snmp/src/misc/snmp_pdus.erl @@ -254,68 +254,95 @@ strip(0, _Tail) -> %%---------------------------------------------------------------------- %% Returns:{Type, Value} %%---------------------------------------------------------------------- + +%% OBJECT IDENTIFIER dec_value([6 | Bytes]) -> {Value, Rest} = dec_oid_notag(Bytes), {{'OBJECT IDENTIFIER', Value}, Rest}; dec_value([5,0 | T]) -> {{'NULL', 'NULL'}, T}; + +%% INTEGER dec_value([2 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), {{'INTEGER', Value}, Rest}; + +%% OCTET STRING dec_value([4 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'OCTET STRING', Value}, Rest}; + +%% IpAddress dec_value([64 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'IpAddress', Value}, Rest}; + +%% Counter32 dec_value([65 | Bytes]) -> %% Counter32 is an unsigned 32 but is actually encoded as %% a signed integer 32 (INTEGER). {Value, Rest} = dec_integer_notag(Bytes), Value2 = - if + if + (Value >= 0) andalso (Value =< 16#ffffffff) -> + %% We accept value above 16#7fffffff + %% in order to be backward bug-compatible + Value; + (Value < 0) -> + 16#ffffffff + Value + 1; + true -> + exit({error, {bad_counter32, Value}}) + end, + {{'Counter32', Value2}, Rest}; + +%% Unsigned32 +dec_value([66 | Bytes]) -> + {Value, Rest} = dec_integer_notag(Bytes), + Value2 = + if (Value >= 0) andalso (Value =< 16#ffffffff) -> - %% We accept value above 16#7fffffff - %% in order to be backward bug-compatible Value; (Value < 0) -> 16#ffffffff + Value + 1; true -> - exit({error, {bad_counter32, Value}}) + exit({error, {bad_unsigned32, Value}}) end, - {{'Counter32', Value2}, Rest}; -dec_value([66 | Bytes]) -> - {Value, Rest} = dec_integer_notag(Bytes), - if - (Value >= 0) andalso (Value =< 4294967295) -> - {{'Unsigned32', Value}, Rest}; - true -> - exit({error, {bad_unsigned32, Value}}) - end; + {{'Unsigned32', Value2}, Rest}; + +%% TimeTicks dec_value([67 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), - if - (Value >= 0) andalso (Value =< 4294967295) -> - {{'TimeTicks', Value}, Rest}; + Value2 = + if + (Value >= 0) andalso (Value =< 16#ffffffff) -> + Value; + (Value < 0) -> + 16#ffffffff + Value + 1; true -> exit({error, {bad_timeticks, Value}}) - end; + end, + {{'TimeTicks', Value2}, Rest}; + +%% Opaque dec_value([68 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'Opaque', Value}, Rest}; + +%% Counter64 dec_value([70 | Bytes]) -> %% Counter64 is an unsigned 64 but is actually encoded as %% a signed integer 64. {Value, Rest} = dec_integer_notag(Bytes), Value2 = - if - (Value >= 0) andalso (Value < 16#8000000000000000) -> - Value; - (Value < 0) -> - 18446744073709551615 + Value + 1; - true -> - exit({error, {bad_counter64, Value}}) end, + if + (Value >= 0) andalso (Value < 16#8000000000000000) -> + Value; + (Value < 0) -> + 16#ffffffffffffffff + Value + 1; + true -> + exit({error, {bad_counter64, Value}}) end, {{'Counter64', Value2}, Rest}; + dec_value([128,0|T]) -> {{'NULL', noSuchObject}, T}; dec_value([129,0|T]) -> @@ -629,7 +656,7 @@ enc_VarBind_attributes(#varbind{oid = Oid, variabletype = Type,value = Val}) -> ValueBytes = enc_value(Type, Val), lists:append(OidBytes, ValueBytes). -enc_value('INTEGER',Val) -> +enc_value('INTEGER', Val) -> enc_integer_tag(Val); enc_value('OCTET STRING', Val) -> enc_oct_str_tag(Val); @@ -637,7 +664,7 @@ 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) -> +enc_value('IpAddress', Val) -> Bytes2 = enc_oct_str_notag(Val), Len2 = elength(length(Bytes2)), lists:append([64 | Len2],Bytes2); @@ -668,6 +695,24 @@ enc_value('Counter32', Val) -> Bytes2 = enc_integer_notag(Val2), Len2 = elength(length(Bytes2)), lists:append([65 | Len2],Bytes2); +enc_value('Unsigned32', Val) -> + if + (Val >= 0) andalso (Val =< 4294967295) -> + Bytes2 = enc_integer_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([66 | Len2], Bytes2); + true -> + exit({error, {bad_counter32, Val}}) + end; +enc_value('TimeTicks', Val) -> + if + (Val >= 0) andalso (Val =< 4294967295) -> + Bytes2 = enc_integer_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([67 | Len2], Bytes2); + true -> + exit({error, {bad_timeticks, Val}}) + end; enc_value('Counter64', Val) -> Val2 = if @@ -682,18 +727,7 @@ enc_value('Counter64', Val) -> end, Bytes2 = enc_integer_notag(Val2), Len2 = elength(length(Bytes2)), - lists:append([70 | Len2],Bytes2); -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. + lists:append([70 | Len2],Bytes2). %%---------------------------------------------------------------------- diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 3575dcc970..bfe14bc080 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -346,47 +346,72 @@ end_per_group(_GroupName, Config) -> -init_per_testcase(otp8395 = Case, Config) when is_list(Config) -> +%% ---- Init Per TestCase ---- + +init_per_testcase(Case, Config) when is_list(Config) -> ?DBG("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + p("Agent Info: " + "~n ~p", [snmpa:info()]), + + init_per_testcase1(Case, Config). + +init_per_testcase1(otp8395 = Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [Case, Config]), otp8395({init, init_per_testcase2(Case, Config)}); -init_per_testcase(otp9884 = Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" +init_per_testcase1(otp9884 = Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [Case, Config]), otp9884({init, init_per_testcase2(Case, Config)}); -init_per_testcase(otp_7157_test = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" +init_per_testcase1(otp_7157_test = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(1)), [{watchdog, Dog} | Config ]; -init_per_testcase(v2_inform_i = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" +init_per_testcase1(v2_inform_i = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(10)), [{watchdog, Dog} | Config ]; -init_per_testcase(v3_inform_i = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" +init_per_testcase1(v3_inform_i = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(10)), [{watchdog, Dog} | Config ]; -init_per_testcase(_Case, Config) when is_list(Config) -> +init_per_testcase1(_Case, Config) when is_list(Config) -> ?DBG("init_per_testcase -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(6)), [{watchdog, Dog}| Config ]. -end_per_testcase(otp8395, Config) when is_list(Config) -> + +%% ---- End Per TestCase ---- + +end_per_testcase(Case, Config) when is_list(Config) -> + ?DBG("end_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + p("Agent Info: " + "~n ~p", [snmpa:info()]), + + display_log(Config), + + end_per_testcase1(Case, Config). + +end_per_testcase1(otp8395, Config) when is_list(Config) -> otp8395({fin, Config}); -end_per_testcase(otp9884, Config) when is_list(Config) -> +end_per_testcase1(otp9884, Config) when is_list(Config) -> otp9884({fin, Config}); -end_per_testcase(_Case, Config) when is_list(Config) -> - ?DBG("end_per_testcase -> entry with" +end_per_testcase1(_Case, Config) when is_list(Config) -> + ?DBG("end_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?config(watchdog, Config), @@ -1388,9 +1413,6 @@ simple(Config) when is_list(Config) -> try_test(simple_standard_test), - p("Display log"), - display_log(Config), - p("done"), ok. diff --git a/lib/snmp/test/snmp_pdus_test.erl b/lib/snmp/test/snmp_pdus_test.erl index 07b6d6b657..0d78749bcb 100644 --- a/lib/snmp/test/snmp_pdus_test.erl +++ b/lib/snmp/test/snmp_pdus_test.erl @@ -39,6 +39,7 @@ otp7575/1, otp8563/1, otp9022/1, + otp10132/1, init_per_testcase/2, end_per_testcase/2 ]). @@ -74,16 +75,16 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% Test case definitions %%====================================================================== all() -> -[{group, tickets}]. + [{group, tickets}]. groups() -> - [{tickets, [], [otp7575, otp8563, otp9022]}]. + [{tickets, [], [otp7575, otp8563, otp9022, otp10132]}]. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. @@ -94,7 +95,7 @@ end_per_group(_GroupName, Config) -> %%====================================================================== otp7575(suite) -> []; -otp7575(doc) -> ["OTP-7575"]; +otp7575(doc) -> ["OTP-7575 - Message version"]; otp7575(Config) when is_list(Config) -> io:format("attempt to decode message with valid version~n", []), MsgWithOkVersion = <<48,39,2,1,0,4,6,112,117,98,108,105,99,160,26,2,2,1,49,2,1,0,2,1,0,48,14,48,12,6,8,43,6,1,2,1,1,5,0,5,0>>, @@ -127,48 +128,55 @@ otp7575(Config) when is_list(Config) -> otp8563(suite) -> []; -otp8563(doc) -> ["OTP-8563"]; +otp8563(doc) -> ["OTP-8563 - Counter64"]; otp8563(Config) when is_list(Config) -> Val1 = 16#7fffffffffffffff, - io:format("try encode and decode ~w~n", [Val1]), + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('Counter64', Val1), + io:format(" => ~w~n", [Enc1]), {{'Counter64', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode ~w~n", [Val2]), + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('Counter64', Val2), + io:format(" => ~w~n", [Enc2]), {{'Counter64', Val2}, []} = snmp_pdus:dec_value(Enc2), Val3 = Val2 + 1, - io:format("try encode and decode ~w~n", [Val3]), + io:format("try encode and decode valule 3: ~w (0x~.16b)~n", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('Counter64', Val3), + io:format(" => ~w~n", [Enc3]), {{'Counter64', Val3}, []} = snmp_pdus:dec_value(Enc3), Val4 = 16#fffffffffffffffe, - io:format("try encode and decode ~w~n", [Val4]), + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('Counter64', Val4), + io:format(" => ~w~n", [Enc4]), {{'Counter64', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode ~w~n", [Val5]), + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('Counter64', Val5), + io:format(" => ~w~n", [Enc5]), {{'Counter64', Val5}, []} = snmp_pdus:dec_value(Enc5), Val6 = 16#ffffffffffffffff + 1, - io:format("try and fail to encode ~w~n", [Val6]), + io:format("try and fail to encode value 6: ~w (0x~.16b)~n", [Val6, Val6]), case (catch snmp_pdus:enc_value('Counter64', Val6)) of {'EXIT', {error, {bad_counter64, Val6}}} -> ok; Unexpected6 -> + io:format(" => ~w~n", [Unexpected6]), exit({unexpected_encode_result, Unexpected6, Val6}) end, Val7 = -1, - io:format("try and fail to encode ~w~n", [Val7]), + io:format("try and fail to encode value 7: ~w~n", [Val7]), case (catch snmp_pdus:enc_value('Counter64', Val7)) of {'EXIT', {error, {bad_counter64, Val7}}} -> ok; Unexpected7 -> + io:format(" => ~w~n", [Unexpected7]), exit({unexpected_encode_result, Unexpected7, Val7}) end, @@ -176,51 +184,151 @@ otp8563(Config) when is_list(Config) -> otp9022(suite) -> []; -otp9022(doc) -> ["OTP-9022"]; +otp9022(doc) -> ["OTP-9022 - Counter32"]; otp9022(Config) when is_list(Config) -> - Val1 = 16#7fffffff, - io:format("try encode and decode ~w~n", [Val1]), + Val0 = 2908389204, + io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]), + Enc0 = snmp_pdus:enc_value('Counter32', Val0), + io:format(" => ~w~n", [Enc0]), + {{'Counter32', Val0}, []} = snmp_pdus:dec_value(Enc0), + + Val1 = 0, + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('Counter32', Val1), + io:format(" => ~w~n", [Enc1]), {{'Counter32', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode ~w~n", [Val2]), + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('Counter32', Val2), + io:format(" => ~w~n", [Enc2]), {{'Counter32', Val2}, []} = snmp_pdus:dec_value(Enc2), - Val3 = Val2 + 1, - io:format("try encode and decode ~w~n", [Val3]), + Val3 = 16#7ffffffe, + io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('Counter32', Val3), + io:format(" => ~w~n", [Enc3]), {{'Counter32', Val3}, []} = snmp_pdus:dec_value(Enc3), - Val4 = 16#fffffffe, - io:format("try encode and decode ~w~n", [Val4]), + Val4 = Val3 + 1, + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('Counter32', Val4), + io:format(" => ~w~n", [Enc4]), {{'Counter32', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode ~w~n", [Val5]), + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('Counter32', Val5), + io:format(" => ~w~n", [Enc5]), {{'Counter32', Val5}, []} = snmp_pdus:dec_value(Enc5), - Val6 = 16#ffffffff + 1, - io:format("try and fail to encode ~w~n", [Val6]), - case (catch snmp_pdus:enc_value('Counter32', Val6)) of - {'EXIT', {error, {bad_counter32, Val6}}} -> + Val6 = 16#fffffffe, + io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + Enc6 = snmp_pdus:enc_value('Counter32', Val6), + io:format(" => ~w~n", [Enc6]), + {{'Counter32', Val6}, []} = snmp_pdus:dec_value(Enc6), + + Val7 = Val6 + 1, + io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]), + Enc7 = snmp_pdus:enc_value('Counter32', Val7), + io:format(" => ~w~n", [Enc7]), + {{'Counter32', Val7}, []} = snmp_pdus:dec_value(Enc7), + + Val8 = 16#ffffffff + 1, + io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]), + case (catch snmp_pdus:enc_value('Counter32', Val8)) of + {'EXIT', {error, {bad_counter32, Val8}}} -> ok; - Unexpected6 -> - exit({unexpected_encode_result, Unexpected6, Val6}) + Unexpected8 -> + io:format(" => ~w~n", [Unexpected8]), + exit({unexpected_encode_result, Unexpected8, Val8}) end, - Val7 = -1, - io:format("try and fail to encode ~w~n", [Val7]), - case (catch snmp_pdus:enc_value('Counter32', Val7)) of - {'EXIT', {error, {bad_counter32, Val7}}} -> + Val9 = -1, + io:format("try and fail to encode value 9: ~w~n", [Val9]), + case (catch snmp_pdus:enc_value('Counter32', Val9)) of + {'EXIT', {error, {bad_counter32, Val9}}} -> ok; - Unexpected7 -> - exit({unexpected_encode_result, Unexpected7, Val7}) + Unexpected9 -> + io:format(" => ~w~n", [Unexpected9]), + exit({unexpected_encode_result, Unexpected9, Val9}) + end, + + ok. + + +otp10132(suite) -> []; +otp10132(doc) -> ["OTP-10132 - TimeTicks"]; +otp10132(Config) when is_list(Config) -> + Val0 = 2159001034, + io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]), + Enc0 = snmp_pdus:enc_value('TimeTicks', Val0), + io:format(" => ~w~n", [Enc0]), + {{'TimeTicks', Val0}, []} = snmp_pdus:dec_value(Enc0), + + Val1 = 0, + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), + Enc1 = snmp_pdus:enc_value('TimeTicks', Val1), + io:format(" => ~w~n", [Enc1]), + {{'TimeTicks', Val1}, []} = snmp_pdus:dec_value(Enc1), + + Val2 = Val1 + 1, + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), + Enc2 = snmp_pdus:enc_value('TimeTicks', Val2), + io:format(" => ~w~n", [Enc2]), + {{'TimeTicks', Val2}, []} = snmp_pdus:dec_value(Enc2), + + Val3 = 16#7ffffffe, + io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), + Enc3 = snmp_pdus:enc_value('TimeTicks', Val3), + io:format(" => ~w~n", [Enc3]), + {{'TimeTicks', Val3}, []} = snmp_pdus:dec_value(Enc3), + + Val4 = Val3 + 1, + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), + Enc4 = snmp_pdus:enc_value('TimeTicks', Val4), + io:format(" => ~w~n", [Enc4]), + {{'TimeTicks', Val4}, []} = snmp_pdus:dec_value(Enc4), + + Val5 = Val4 + 1, + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), + Enc5 = snmp_pdus:enc_value('TimeTicks', Val5), + io:format(" => ~w~n", [Enc5]), + {{'TimeTicks', Val5}, []} = snmp_pdus:dec_value(Enc5), + + Val6 = 16#fffffffe, + io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + Enc6 = snmp_pdus:enc_value('TimeTicks', Val6), + io:format(" => ~w~n", [Enc6]), + {{'TimeTicks', Val6}, []} = snmp_pdus:dec_value(Enc6), + + Val7 = Val6 + 1, + io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]), + Enc7 = snmp_pdus:enc_value('TimeTicks', Val7), + io:format(" => ~w~n", [Enc7]), + {{'TimeTicks', Val7}, []} = snmp_pdus:dec_value(Enc7), + + Val8 = Val7 + 1, + io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]), + case (catch snmp_pdus:enc_value('TimeTicks', Val8)) of + {'EXIT', {error, {bad_timeticks, Val8}}} -> + ok; + Unexpected8 -> + io:format(" => ~w~n", [Unexpected8]), + exit({unexpected_encode_result, Unexpected8, Val8}) end, + Val9 = -1, + io:format("try and fail to encode value 9: ~w~n", [Val9]), + case (catch snmp_pdus:enc_value('TimeTicks', Val9)) of + {'EXIT', {error, {bad_timeticks, Val9}}} -> + ok; + Unexpected9 -> + io:format(" => ~w~n", [Unexpected9]), + exit({unexpected_encode_result, Unexpected9, Val9}) + end, + + io:format("done~n", []), ok. diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index 36b9764bc8..b90dbe4eef 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = snmp -SNMP_VSN = 4.22 +SNMP_VSN = 4.22.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)" |