diff options
113 files changed, 5356 insertions, 596 deletions
diff --git a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c index 1147549135..beee1b735f 100644 --- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c +++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c @@ -19,8 +19,12 @@ #ifdef __WIN32__ #include <windows.h> #endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif #include "erl_driver.h" +static void stop(ErlDrvData drv_data); static ErlDrvData start(ErlDrvPort port, char *command); static void output(ErlDrvData drv_data, @@ -31,7 +35,7 @@ static void ready_async(ErlDrvData drv_data, static ErlDrvEntry otp_9302_drv_entry = { NULL /* init */, start, - NULL /* stop */, + stop, output, NULL /* ready_input */, NULL /* ready_output */, @@ -53,42 +57,121 @@ static ErlDrvEntry otp_9302_drv_entry = { NULL /* handle_monitor */ }; +typedef struct Otp9302AsyncData_ Otp9302AsyncData; + +typedef struct { + ErlDrvMutex *mtx; + Otp9302AsyncData *start; + Otp9302AsyncData *end; +} Otp9302MsgQ; + +typedef struct { + ErlDrvPort port; + int smp; + Otp9302MsgQ msgq; +} Otp9302Data; + +struct Otp9302AsyncData_ { + Otp9302AsyncData *next; + ErlDrvPort port; + int smp; + int refc; + int block; + struct { + ErlDrvTermData port; + ErlDrvTermData receiver; + ErlDrvTermData msg; + } term_data; + Otp9302MsgQ *msgq; +}; + + DRIVER_INIT(otp_9302_drv) { return &otp_9302_drv_entry; } +static void stop(ErlDrvData drv_data) +{ + Otp9302Data *data = (Otp9302Data *) drv_data; + if (!data->smp) + erl_drv_mutex_destroy(data->msgq.mtx); + driver_free(data); +} + static ErlDrvData start(ErlDrvPort port, char *command) { - return (ErlDrvData) port; -} + Otp9302Data *data; + ErlDrvSysInfo sys_info; -typedef struct { - ErlDrvPort port; - ErlDrvTermData receiver; - int block; - int cancel; - int eoj; -} Otp9302AsyncData; + data = driver_alloc(sizeof(Otp9302Data)); + if (!data) + return ERL_DRV_ERROR_GENERAL; -static void async_invoke(void *data) + data->port = port; + + driver_system_info(&sys_info, sizeof(ErlDrvSysInfo)); + data->smp = sys_info.smp_support; + + if (!data->smp) { + data->msgq.start = NULL; + data->msgq.end = NULL; + data->msgq.mtx = erl_drv_mutex_create(""); + if (!data->msgq.mtx) { + driver_free(data); + return ERL_DRV_ERROR_GENERAL; + } + } + + return (ErlDrvData) data; +} + +static void send_reply(Otp9302AsyncData *adata) { - Otp9302AsyncData *adata = (Otp9302AsyncData *) data; - char *what = (adata->block - ? "block" - : (adata->cancel - ? "cancel" - : (adata->eoj - ? "end_of_jobs" - : "job"))); ErlDrvTermData spec[] = { - ERL_DRV_PORT, driver_mk_port(adata->port), - ERL_DRV_ATOM, driver_mk_atom(what), + ERL_DRV_PORT, adata->term_data.port, + ERL_DRV_ATOM, adata->term_data.msg, ERL_DRV_TUPLE, 2 }; - driver_send_term(adata->port, adata->receiver, + driver_send_term(adata->port, adata->term_data.receiver, spec, sizeof(spec)/sizeof(spec[0])); +} + +static void enqueue_reply(Otp9302AsyncData *adata) +{ + Otp9302MsgQ *msgq = adata->msgq; + adata->next = NULL; + adata->refc++; + erl_drv_mutex_lock(msgq->mtx); + if (msgq->end) + msgq->end->next = adata; + else + msgq->end = msgq->start = adata; + msgq->end = adata; + erl_drv_mutex_unlock(msgq->mtx); +} + +static void dequeue_replies(Otp9302AsyncData *adata) +{ + Otp9302MsgQ *msgq = adata->msgq; + erl_drv_mutex_lock(msgq->mtx); + if (--adata->refc == 0) + driver_free(adata); + while (msgq->start) { + send_reply(msgq->start); + adata = msgq->start; + msgq->start = msgq->start->next; + if (--adata->refc == 0) + driver_free(adata); + } + msgq->start = msgq->end = NULL; + erl_drv_mutex_unlock(msgq->mtx); +} + +static void async_invoke(void *data) +{ + Otp9302AsyncData *adata = (Otp9302AsyncData *) data; if (adata->block) { #ifdef __WIN32__ Sleep((DWORD) 2000); @@ -96,14 +179,30 @@ static void async_invoke(void *data) sleep(2); #endif } + if (adata->smp) + send_reply(adata); + else + enqueue_reply(adata); +} + +static void ready_async(ErlDrvData drv_data, + ErlDrvThreadData thread_data) +{ + Otp9302AsyncData *adata = (Otp9302AsyncData *) thread_data; + if (adata->smp) + driver_free(adata); + else + dequeue_replies(adata); } static void output(ErlDrvData drv_data, char *buf, int len) { - ErlDrvPort port = (ErlDrvPort) drv_data; - ErlDrvTermData caller = driver_caller(port); - unsigned int key = (unsigned int) port; + Otp9302Data *data = (Otp9302Data *) drv_data; + ErlDrvTermData td_port = driver_mk_port(data->port); + ErlDrvTermData td_receiver = driver_caller(data->port); + ErlDrvTermData td_job = driver_mk_atom("job"); + unsigned int key = (unsigned int) data->port; long id[5]; Otp9302AsyncData *ad[5]; int i; @@ -113,25 +212,21 @@ static void output(ErlDrvData drv_data, if (!ad[i]) abort(); - ad[i]->port = port; - ad[i]->receiver = caller; + ad[i]->smp = data->smp; + ad[i]->port = data->port; ad[i]->block = 0; - ad[i]->eoj = 0; - ad[i]->cancel = 0; + ad[i]->refc = 1; + ad[i]->term_data.port = td_port; + ad[i]->term_data.receiver = td_receiver; + ad[i]->term_data.msg = td_job; + ad[i]->msgq = &data->msgq; } ad[0]->block = 1; - ad[2]->cancel = 1; - ad[4]->eoj = 1; + ad[0]->term_data.msg = driver_mk_atom("block"); + ad[2]->term_data.msg = driver_mk_atom("cancel"); + ad[4]->term_data.msg = driver_mk_atom("end_of_jobs"); for (i = 0; i < sizeof(id)/sizeof(id[0]); i++) - id[i] = driver_async(port, &key, async_invoke, ad[i], driver_free); + id[i] = driver_async(data->port, &key, async_invoke, ad[i], driver_free); if (id[2] > 0) driver_async_cancel(id[2]); } - -static void ready_async(ErlDrvData drv_data, - ErlDrvThreadData thread_data) -{ - if ((void *) thread_data) - driver_free((void *) thread_data); -} - diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 72c890503d..6889ec0b34 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -780,42 +780,43 @@ no_nonlocal_register(suite) -> no_nonlocal_register(doc) -> ["Ensure that we cannot register throug a nonlocal connection"]; no_nonlocal_register(Config) when is_list(Config) -> + ?line case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of + {SSH,Name} when is_list(Name), is_list(SSH) -> + do_no_nonlocal_register(Config,Name); + {false,_} -> + {skip, "No ssh command found to create proxy"}; + _ -> + {skip, "No ssh_proxy_host configured in ts.config"} + end. +do_no_nonlocal_register(Config,SSHHost) when is_list(Config) -> ?line ok = epmdrun(), - ?line {ok,Ifs} = inet:getiflist(), - ?line Addr0 = [ inet:ifget(I, [addr]) || I <- Ifs ], - ?line Addr1 = [ A || {ok,[{addr,A}]} <- Addr0], - ?line Addr = lists:filter(fun({127,_,_,_}) -> - false; - (_) -> - true - end,Addr1), - %% Now we should have all non loopback interface addresses, - %% none should accept a alive2 registration. - ?line Res = lists:map(fun(Ad={A1,A2,A3,A4}) -> - try - Name = "gurka_"++ - integer_to_list(A1)++"_"++ - integer_to_list(A2)++"_"++ - integer_to_list(A3)++"_"++ - integer_to_list(A4), - Bname = list_to_binary(Name), - NameS = byte_size(Bname), - ?line Bin= <<$x:8,4747:16,$M:8,0:8,5:16, - 5:16,NameS:16,Bname/binary, - 0:16>>, - ?line S = size(Bin), - {ok, E} = connect(Ad), - gen_tcp:send(E,[<<S:16>>,Bin]), - closed = recv(E,1), - gen_tcp:close(E), - true - catch - _:_ -> - false - end - end, Addr), - erlang:display(Res), - ?line true = alltrue(Res), + ?line ProxyPort = proxy_port(), + ?line ok = ssh_proxy(SSHHost,ProxyPort), + Res = try + ?line Name = "gurka_" + %++ + %integer_to_list(A1)++"_"++ + %integer_to_list(A2)++"_"++ + %integer_to_list(A3)++"_"++ + %integer_to_list(A4) + , + ?line Bname = list_to_binary(Name), + ?line NameS = byte_size(Bname), + ?line Bin= <<$x:8,4747:16,$M:8,0:8,5:16, + 5:16,NameS:16,Bname/binary, + 0:16>>, + ?line S = size(Bin), + ?line {ok, E} = connect("localhost",ProxyPort,passive), + ?line gen_tcp:send(E,[<<S:16>>,Bin]), + ?line closed = recv(E,1), + ?line gen_tcp:close(E), + true + catch + _:_ -> + false + end, + %erlang:display(Res), + true = Res, ok. no_nonlocal_kill(suite) -> @@ -823,35 +824,34 @@ no_nonlocal_kill(suite) -> no_nonlocal_kill(doc) -> ["Ensure that we cannot kill through nonlocal connection"]; no_nonlocal_kill(Config) when is_list(Config) -> + ?line case {os:find_executable("ssh"),ct:get_config(ssh_proxy_host)} of + {SSH,Name} when is_list(Name), is_list(SSH) -> + do_no_nonlocal_kill(Config,Name); + {false,_} -> + {skip, "No ssh command found to create proxy"}; + _ -> + {skip, "No ssh_proxy_host configured in ts.config"} + end. +do_no_nonlocal_kill(Config,SSHHost) when is_list(Config) -> ?line ok = epmdrun(), - ?line {ok,Ifs} = inet:getiflist(), - ?line Addr0 = [ inet:ifget(I, [addr]) || I <- Ifs ], - ?line Addr1 = [ A || {ok,[{addr,A}]} <- Addr0], - ?line Addr = lists:filter(fun({127,_,_,_}) -> - false; - (_) -> - true - end,Addr1), - %% Now we should have all non loopback interface addresses, - %% none should accept a alive2 registration. - ?line Res = lists:map(fun(Ad) -> - try - {ok, E} = connect(Ad), - M = [?EPMD_KILL_REQ], - send(E, [size16(M), M]), - closed = recv(E,2), - gen_tcp:close(E), - sleep(?MEDIUM_PAUSE), - {ok, E2} = connect(Ad), - gen_tcp:close(E2), - true - catch - _:_ -> - false - end - end, Addr), - erlang:display(Res), - ?line true = alltrue(Res), + ?line ProxyPort = proxy_port(), + ?line ok = ssh_proxy(SSHHost,ProxyPort), + Res = try + {ok, E} = connect("localhost",ProxyPort,passive), + M = [?EPMD_KILL_REQ], + send(E, [size16(M), M]), + closed = recv(E,2), + gen_tcp:close(E), + sleep(?MEDIUM_PAUSE), + {ok, E2} = connect("localhost",ProxyPort,passive), + gen_tcp:close(E2), + true + catch + _:_ -> + false + end, + %erlang:display(Res), + true = Res, ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% no_live_killing(doc) -> @@ -896,6 +896,19 @@ cleanup() -> true end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Start an ssh channel to simulate remote access + +proxy_port() -> + ?PORT+1. + +ssh_proxy(SSHHost,ProxyPort) -> + ?line Host = lists:nth(2,string:tokens(atom_to_list(node()),"@")), + % Requires proxy to be a unix host with the command 'read' accessible + ?line osrun("ssh -L "++integer_to_list(ProxyPort)++":"++Host++":" + ++integer_to_list(?PORT)++" "++SSHHost++" read"). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Normal debug start of epmd diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml index 95941fefdd..2318ccb6d2 100644 --- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml +++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosEventChannelAdmin_­ConsumerAdmin</title> - <shorttitle>..._ConsumerAdmin</shorttitle> + <title>CosEventChannelAdmin_ConsumerAdmin</title> <prepared></prepared> <responsible></responsible> <docno></docno> diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml index 51f9f11613..4495fd4450 100644 --- a/lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml +++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosEventChannelAdmin_­EventChannel</title> - <shorttitle>..._EventChannel</shorttitle> + <title>CosEventChannelAdmin_EventChannel</title> <prepared></prepared> <responsible></responsible> <docno></docno> diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml index 9690c9406d..4cd20ad185 100644 --- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml +++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosEventChannelAdmin_­ProxyPullConsumer</title> - <shorttitle>..._ProxyPullConsumer</shorttitle> + <title>CosEventChannelAdmin_ProxyPullConsumer</title> <prepared></prepared> <responsible></responsible> <docno></docno> diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml index fb17c450f4..830c06a87c 100644 --- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml +++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosEventChannelAdmin_­ProxyPullSupplier</title> - <shorttitle>..._ProxyPullSupplier</shorttitle> + <title>CosEventChannelAdmin_ProxyPullSupplier</title> <prepared></prepared> <responsible></responsible> <docno></docno> diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml index 21e6cfce6f..2c451acd9c 100644 --- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml +++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosEventChannelAdmin_­ProxyPushConsumer</title> - <shorttitle>..._ProxyPushConsumer</shorttitle> + <title>CosEventChannelAdmin_ProxyPushConsumer</title> <prepared></prepared> <responsible></responsible> <docno></docno> diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml index be2dfcafbe..9030c0e735 100644 --- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml +++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosEventChannelAdmin_­ProxyPushSupplier</title> - <shorttitle>..._ProxyPushSupplier</shorttitle> + <title>CosEventChannelAdmin_ProxyPushSupplier</title> <prepared></prepared> <responsible></responsible> <docno></docno> diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml index ca301bb860..e68b0854d8 100644 --- a/lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml +++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosEventChannelAdmin_­SupplierAdmin</title> - <shorttitle>..._SupplierAdmin</shorttitle> + <title>CosEventChannelAdmin_SupplierAdmin</title> <prepared></prepared> <responsible></responsible> <docno></docno> diff --git a/lib/cosEvent/doc/src/notes.xml b/lib/cosEvent/doc/src/notes.xml index 1a5c8afa17..1da5399755 100644 --- a/lib/cosEvent/doc/src/notes.xml +++ b/lib/cosEvent/doc/src/notes.xml @@ -22,8 +22,8 @@ </legalnotice> <title>cosEvent Release Notes</title> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <prepared></prepared> + <responsible></responsible> <docno></docno> <approved></approved> <checked></checked> @@ -33,6 +33,23 @@ </header> <section> + <title>cosEvent 2.1.11</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Removed superfluous usage of shy in the documentation since it can cause problem if + a buggy tool is used.</p> + <p> + Own Id: OTP-9319 Aux Id:</p> + </item> + </list> + </section> + </section> + + <section> <title>cosEvent 2.1.10</title> <section> diff --git a/lib/cosEvent/vsn.mk b/lib/cosEvent/vsn.mk index 38999db5fa..85d3cf552b 100644 --- a/lib/cosEvent/vsn.mk +++ b/lib/cosEvent/vsn.mk @@ -1,3 +1,3 @@ -COSEVENT_VSN = 2.1.10 +COSEVENT_VSN = 2.1.11 diff --git a/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomain.xml b/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomain.xml index cf1cdab966..4941f8652c 100644 --- a/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomain.xml +++ b/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomain.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2009</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,8 +21,7 @@ </legalnotice> - <title>CosEventDomainAdmin_­EventDomain</title> - <shorttitle>..._EventDomain</shorttitle> + <title>CosEventDomainAdmin_EventDomain</title> <prepared></prepared> <docno></docno> <approved>Niclas Eklund</approved> diff --git a/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml b/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml index ea605f23a0..5eff7038d9 100644 --- a/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml +++ b/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosEventDomainAdmin_­EventDomainFactory</title> - <shorttitle>..._EventChannel</shorttitle> + <title>CosEventDomainAdmin_EventDomainFactory</title> <prepared>Niclas Eklund</prepared> <responsible>Niclas Eklund</responsible> <docno></docno> diff --git a/lib/cosEventDomain/doc/src/notes.xml b/lib/cosEventDomain/doc/src/notes.xml index 522dcea829..585761ce65 100644 --- a/lib/cosEventDomain/doc/src/notes.xml +++ b/lib/cosEventDomain/doc/src/notes.xml @@ -32,6 +32,23 @@ </header> <section> + <title>cosEventDomain 1.1.11</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Removed superfluous usage of shy in the documentation since it can cause problem if + a buggy tool is used.</p> + <p> + Own Id: OTP-9319 Aux Id:</p> + </item> + </list> + </section> + </section> + + <section> <title>cosEventDomain 1.1.10</title> <section> diff --git a/lib/cosEventDomain/vsn.mk b/lib/cosEventDomain/vsn.mk index f4a77ab7a8..7df47cef2e 100644 --- a/lib/cosEventDomain/vsn.mk +++ b/lib/cosEventDomain/vsn.mk @@ -1,3 +1,3 @@ -COSEVENTDOMAIN_VSN = 1.1.10 +COSEVENTDOMAIN_VSN = 1.1.11 diff --git a/lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml b/lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml index af9141b205..7c68768aa9 100644 --- a/lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml +++ b/lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosFileTransfer_­Directory</title> - <shorttitle>..._Directory</shorttitle> + <title>CosFileTransfer_Directory</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml b/lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml index bef7cb882f..8b317049e2 100644 --- a/lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml +++ b/lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml @@ -23,11 +23,10 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosFileTransfer_­File</title> - <shorttitle>..._File</shorttitle> + <title>CosFileTransfer_File</title> <prepared></prepared> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-11-09</date> <rev>PA1</rev> diff --git a/lib/cosFileTransfer/doc/src/CosFileTransfer_FileIterator.xml b/lib/cosFileTransfer/doc/src/CosFileTransfer_FileIterator.xml index c848a9830d..80cbd1f448 100644 --- a/lib/cosFileTransfer/doc/src/CosFileTransfer_FileIterator.xml +++ b/lib/cosFileTransfer/doc/src/CosFileTransfer_FileIterator.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,8 +21,7 @@ </legalnotice> - <title>CosFileTransfer_­FileIterator</title> - <shorttitle>..._FileIterator</shorttitle> + <title>CosFileTransfer_FileIterator</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosFileTransfer/doc/src/CosFileTransfer_FileTransferSession.xml b/lib/cosFileTransfer/doc/src/CosFileTransfer_FileTransferSession.xml index 5f4542058b..5ac2c61c92 100644 --- a/lib/cosFileTransfer/doc/src/CosFileTransfer_FileTransferSession.xml +++ b/lib/cosFileTransfer/doc/src/CosFileTransfer_FileTransferSession.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,8 +21,7 @@ </legalnotice> - <title>CosFileTransfer_­FileTransferSession</title> - <shorttitle>..._FileTransferSession</shorttitle> + <title>CosFileTransfer_FileTransferSession</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml b/lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml index 8aa02b2153..7bb6e8d356 100644 --- a/lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml +++ b/lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosFileTransfer_­VirtualFileSystem</title> - <shorttitle>..._VirtualFileSystem</shorttitle> + <title>CosFileTransfer_VirtualFileSystem</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosFileTransfer/doc/src/notes.xml b/lib/cosFileTransfer/doc/src/notes.xml index 48d0c04236..53c207db2f 100644 --- a/lib/cosFileTransfer/doc/src/notes.xml +++ b/lib/cosFileTransfer/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2010</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -31,6 +31,23 @@ </header> <section> + <title>cosFileTransfer 1.1.11</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Removed superfluous usage of shy in the documentation since it can cause problem if + a buggy tool is used.</p> + <p> + Own Id: OTP-9319 Aux Id:</p> + </item> + </list> + </section> + </section> + + <section> <title>cosFileTransfer 1.1.10</title> <section> diff --git a/lib/cosFileTransfer/vsn.mk b/lib/cosFileTransfer/vsn.mk index ef8ee53c5e..9d68ab2720 100644 --- a/lib/cosFileTransfer/vsn.mk +++ b/lib/cosFileTransfer/vsn.mk @@ -1 +1 @@ -COSFILETRANSFER_VSN = 1.1.10 +COSFILETRANSFER_VSN = 1.1.11 diff --git a/lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml b/lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml index 57015b3621..46c3921b66 100644 --- a/lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml +++ b/lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml @@ -23,10 +23,9 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotification_­AdminPropertiesAdmin</title> - <shorttitle>..._AdminPropertiesAdmin</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotification_AdminPropertiesAdmin</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> <approved>Niclas Eklund</approved> <checked></checked> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml index 671f68d482..96ccdf1d29 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­ConsumerAdmin</title> - <shorttitle>..._ConsumerAdmin</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_ConsumerAdmin</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_EventChannel.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_EventChannel.xml index b6af2e2ca3..1682cf9968 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_EventChannel.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_EventChannel.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,8 +21,7 @@ </legalnotice> - <title>CosNotifyChannelAdmin_­EventChannel</title> - <shorttitle>..._EventChannel</shorttitle> + <title>CosNotifyChannelAdmin_EventChannel</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_EventChannelFactory.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_EventChannelFactory.xml index 01976954e7..64e0e4dad8 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_EventChannelFactory.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_EventChannelFactory.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,8 +21,7 @@ </legalnotice> - <title>CosNotifyChannelAdmin_­EventChannelFactory</title> - <shorttitle>..._EventChannelFactory</shorttitle> + <title>CosNotifyChannelAdmin_EventChannelFactory</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml index 8bc182a50c..7ba74547bb 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­ProxyConsumer</title> - <shorttitle>..._ProxyConsumer</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_ProxyConsumer</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml index 43818e5238..d8344e004a 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­ProxyPullConsumer</title> - <shorttitle>..._ProxyPullConsumer</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_ProxyPullConsumer</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml index 4c0aac7ae6..cc2c17a3ca 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­ProxyPullSupplier</title> - <shorttitle>..._ProxyPullSupplier</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_ProxyPullSupplier</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml index 697d00ea51..30ba264f74 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml @@ -23,10 +23,9 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­ProxyPushConsumer</title> - <shorttitle>..._ProxyPushConsumer</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_ProxyPushConsumer</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> <approved>Niclas Eklund</approved> <checked></checked> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml index f6fc3a0f7b..d5079a5ae7 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­ProxyPushSupplier</title> - <shorttitle>..._ProxyPushSupplier</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_ProxyPushSupplier</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml index 81d4de929a..bdd9213a8b 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­ProxySupplier</title> - <shorttitle>..._ProxySupplier</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_ProxySupplier</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml index 4084fd443b..86796a2643 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­SequenceProxyPullConsumer</title> - <shorttitle>..._SequenceProxyPullConsumer</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_SequenceProxyPullConsumer</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml index 16b093b9aa..c30217362a 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­SequenceProxyPullSupplier</title> - <shorttitle>..._SequenceProxyPullSupplier</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_SequenceProxyPullSupplier</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushConsumer.xml index 964d212715..3f3e187486 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushConsumer.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushConsumer.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,15 +21,14 @@ </legalnotice> - <title>CosNotifyChannelAdmin_­SequenceProxyPushConsumer</title> - <shorttitle>..._SequenceProxyPushConsumer</shorttitle> + <title>CosNotifyChannelAdmin_SequenceProxyPushConsumer</title> <prepared></prepared> <docno></docno> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> </header> - <module>CosNotifyChannelAdmin_­SequenceProxyPushConsumer</module> + <module>CosNotifyChannelAdmin_SequenceProxyPushConsumer</module> <modulesummary>This module implements the OMG CosNotifyChannelAdmin::SequenceProxyPushConsumer interface.</modulesummary> <description> <p>To get access to the record definitions for the structures use: <br></br> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml index f8ce2072e1..f85f33de01 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml @@ -23,17 +23,16 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­SequenceProxyPushSupplier</title> - <shorttitle>..._SequenceProxyPushSupplier</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_SequenceProxyPushSupplier</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> </header> - <module>CosNotifyChannelAdmin_­SequenceProxyPushSupplier</module> + <module>CosNotifyChannelAdmin_SequenceProxyPushSupplier</module> <modulesummary>This module implements the OMG CosNotifyChannelAdmin::SequenceProxyPushSupplier interface.</modulesummary> <description> <p>To get access to the record definitions for the structures use: <br></br> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml index 0623d2891b..09546d9584 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml @@ -23,17 +23,16 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­StructuredProxyPullConsumer</title> - <shorttitle>..._StructuredProxyPullConsumer</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_StructuredProxyPullConsumer</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> </header> - <module>CosNotifyChannelAdmin_­StructuredProxyPullConsumer</module> + <module>CosNotifyChannelAdmin_StructuredProxyPullConsumer</module> <modulesummary>This module implements the OMG CosNotifyChannelAdmin::StructuredProxyPullConsumer interface.</modulesummary> <description> <p>To get access to the record definitions for the structures use: <br></br> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml index 0f0bb5d985..d171851014 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml @@ -23,17 +23,16 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­StructuredProxyPullSupplier</title> - <shorttitle>..._StructuredProxyPullSupplier</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_StructuredProxyPullSupplier</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> </header> - <module>CosNotifyChannelAdmin_­StructuredProxyPullSupplier</module> + <module>CosNotifyChannelAdmin_StructuredProxyPullSupplier</module> <modulesummary>This module implements the OMG CosNotifyChannelAdmin::StructuredProxyPullSupplier interface.</modulesummary> <description> <p>To get access to the record definitions for the structures use: <br></br> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml index 7b7a60723e..a055a0ab36 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml @@ -23,17 +23,16 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­StructuredProxyPushConsumer</title> - <shorttitle>..._StructuredProxyPushConsumer</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_StructuredProxyPushConsumer</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> </header> - <module>CosNotifyChannelAdmin_­StructuredProxyPushConsumer</module> + <module>CosNotifyChannelAdmin_StructuredProxyPushConsumer</module> <modulesummary>This module implements the OMG CosNotifyChannelAdmin::StructuredProxyPushConsumer interface.</modulesummary> <description> <p>To get access to the record definitions for the structures use: <br></br> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml index ab0a260a4b..f03322b819 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml @@ -23,17 +23,16 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­StructuredProxyPushSupplier</title> - <shorttitle>..._StructuredProxyPushSupplier</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_StructuredProxyPushSupplier</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> </header> - <module>CosNotifyChannelAdmin_­StructuredProxyPushSupplier</module> + <module>CosNotifyChannelAdmin_StructuredProxyPushSupplier</module> <modulesummary>This module implements the OMG CosNotifyChannelAdmin::StructuredProxyPushSupplier interface.</modulesummary> <description> <p>To get access to the record definitions for the structures use: <br></br> diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml index a567463f7d..5c7408040d 100644 --- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml +++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml @@ -23,10 +23,9 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyChannelAdmin_­SupplierAdmin</title> - <shorttitle>..._SupplierAdmin</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyChannelAdmin_SupplierAdmin</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> <approved>Niclas Eklund</approved> <checked></checked> diff --git a/lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml b/lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml index dd8ef713e8..97b15d958a 100644 --- a/lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml +++ b/lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosNotifyComm_­NotifySubscribe</title> - <shorttitle>..._NotifySubscribe</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosNotifyComm_NotifySubscribe</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-02-01</date> <rev>1.0</rev> diff --git a/lib/cosNotification/doc/src/ch_BNF.xml b/lib/cosNotification/doc/src/ch_BNF.xml index 73e91e3cac..f658f606d3 100644 --- a/lib/cosNotification/doc/src/ch_BNF.xml +++ b/lib/cosNotification/doc/src/ch_BNF.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -294,7 +294,7 @@ FilterID = 'CosNotifyChannelAdmin_ConsumerAdmin': </row> <row> <cell align="left" valign="middle"><c>in</c></cell> - <cell align="left" valign="middle"><c>"'Erlang' in $.FunctionalLanguages­StringSeq"</c></cell> + <cell align="left" valign="middle"><c>"'Erlang' in $.FunctionalLanguagesStringSeq"</c></cell> <cell align="left" valign="middle">Returns <c>TRUE</c>if a given element is found in the given sequence. The element must be of a simple type and the same as the sequence is defined to contain.</cell> </row> <row> @@ -394,17 +394,17 @@ Figure 1: The structure of a structured event.</icaption> </row> <row> <cell align="left" valign="middle">type_name</cell> - <cell align="left" valign="middle">"$.header.fixed_header.event_­type.type_name == 'Type'"</cell> + <cell align="left" valign="middle">"$.header.fixed_header.event_type.type_name == 'Type'"</cell> <cell align="left" valign="middle">"$type_name == 'Type'"</cell> </row> <row> <cell align="left" valign="middle">domain_name</cell> - <cell align="left" valign="middle">"$.header.fixed_header.event_­type.domain_name == 'Domain'"</cell> + <cell align="left" valign="middle">"$.header.fixed_header.event_type.domain_name == 'Domain'"</cell> <cell align="left" valign="middle">"$domain_name == 'Domain'"</cell> </row> <row> <cell align="left" valign="middle">event_name</cell> - <cell align="left" valign="middle">"$.header.fixed_header.event_­name == 'Event'"</cell> + <cell align="left" valign="middle">"$.header.fixed_header.event_name == 'Event'"</cell> <cell align="left" valign="middle">"$event_name == 'Event'"</cell> </row> <tcaption>Table 2: Fixed Header Constraint Examples</tcaption> diff --git a/lib/cosNotification/doc/src/notes.xml b/lib/cosNotification/doc/src/notes.xml index 125e25e67e..a54230c9f7 100644 --- a/lib/cosNotification/doc/src/notes.xml +++ b/lib/cosNotification/doc/src/notes.xml @@ -31,6 +31,21 @@ <file>notes.xml</file> </header> + <section><title>cosNotification 1.1.17</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Removed superfluous usage of shy in the documentation since it can cause problem if + a buggy tool is used.</p> + <p> + Own Id: OTP-9319 Aux Id:</p> + </item> + </list> + </section> + </section> + <section><title>cosNotification 1.1.16</title> <section><title>Improvements and New Features</title> diff --git a/lib/cosNotification/vsn.mk b/lib/cosNotification/vsn.mk index 6613385579..b32919a2ef 100644 --- a/lib/cosNotification/vsn.mk +++ b/lib/cosNotification/vsn.mk @@ -1,2 +1,2 @@ -COSNOTIFICATION_VSN = 1.1.16 +COSNOTIFICATION_VSN = 1.1.17 diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertiesIterator.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertiesIterator.xml index 75c7cb38cb..623e121715 100644 --- a/lib/cosProperty/doc/src/CosPropertyService_PropertiesIterator.xml +++ b/lib/cosProperty/doc/src/CosPropertyService_PropertiesIterator.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,8 +21,7 @@ </legalnotice> - <title>CosPropertyService_­PropertiesIterator</title> - <shorttitle>..._PropertiesIterator</shorttitle> + <title>CosPropertyService_PropertiesIterator</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml index 1710769661..9cb5e8f489 100644 --- a/lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml +++ b/lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosPropertyService_­PropertyNamesIterator</title> - <shorttitle>..._PropertyNamesIterator</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosPropertyService_PropertyNamesIterator</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-07-25</date> <rev>1.0</rev> diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml index 2c1671bf77..f9a7c9ca97 100644 --- a/lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml +++ b/lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosPropertyService_­PropertySet</title> - <shorttitle>..._PropertySet</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosPropertyService_PropertySet</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-07-25</date> <rev>1.0</rev> diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertySetDef.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertySetDef.xml index 7684998428..2dbfd05ba8 100644 --- a/lib/cosProperty/doc/src/CosPropertyService_PropertySetDef.xml +++ b/lib/cosProperty/doc/src/CosPropertyService_PropertySetDef.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,8 +21,7 @@ </legalnotice> - <title>CosPropertyService_­PropertySetDef</title> - <shorttitle>..._PropertySetDef</shorttitle> + <title>CosPropertyService_PropertySetDef</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml index 67aa579e6a..a009e70f68 100644 --- a/lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml +++ b/lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml @@ -23,10 +23,9 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosPropertyService_­PropertySetDefFactory</title> - <shorttitle>..._PropertySetDefFactory</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosPropertyService_PropertySetDefFactory</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> <approved>Niclas Eklund</approved> <checked></checked> diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml index 3fb4822948..62ee2bda4f 100644 --- a/lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml +++ b/lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosPropertyService_­PropertySetFactory</title> - <shorttitle>..._PropertySetFactory</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosPropertyService_PropertySetFactory</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>2000-07-25</date> <rev>1.0</rev> diff --git a/lib/cosProperty/doc/src/notes.xml b/lib/cosProperty/doc/src/notes.xml index 540fdce762..85b2119e9d 100644 --- a/lib/cosProperty/doc/src/notes.xml +++ b/lib/cosProperty/doc/src/notes.xml @@ -22,7 +22,7 @@ </legalnotice> <title>cosProperty Release Notes</title> - <prepared>Niclas Eklund</prepared> + <prepared></prepared> <docno></docno> <approved></approved> <checked></checked> @@ -32,6 +32,23 @@ </header> <section> + <title>cosProperty 1.1.14</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Removed superfluous usage of shy in the documentation since it can cause problem if + a buggy tool is used.</p> + <p> + Own Id: OTP-9319 Aux Id:</p> + </item> + </list> + </section> + </section> + + <section> <title>cosProperty 1.1.13</title> <section> diff --git a/lib/cosProperty/vsn.mk b/lib/cosProperty/vsn.mk index deb1eb0450..ecc4a2746c 100644 --- a/lib/cosProperty/vsn.mk +++ b/lib/cosProperty/vsn.mk @@ -1,2 +1,2 @@ -COSPROPERTY_VSN = 1.1.13 +COSPROPERTY_VSN = 1.1.14 diff --git a/lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml b/lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml index 4b2e57642a..2adf318674 100644 --- a/lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml +++ b/lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml @@ -23,10 +23,9 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosTimerEvent_­TimerEventHandler</title> - <shorttitle>..._TimerEventHandler</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosTimerEvent_TimerEventHandler</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> <approved>Niclas Eklund</approved> <checked></checked> diff --git a/lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml b/lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml index fb3fe747e5..80f5fd1466 100644 --- a/lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml +++ b/lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml @@ -23,8 +23,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosTimerEvent_­TimerEventService</title> - <shorttitle>..._TimerEventService</shorttitle> + <title>CosTimerEvent_TimerEventService</title> <prepared>Niclas Eklund</prepared> <responsible>Niclas Eklund</responsible> <docno></docno> diff --git a/lib/cosTime/doc/src/notes.xml b/lib/cosTime/doc/src/notes.xml index 718ca23bc5..3698e4813e 100644 --- a/lib/cosTime/doc/src/notes.xml +++ b/lib/cosTime/doc/src/notes.xml @@ -33,6 +33,23 @@ </header> <section> + <title>cosTime 1.1.11</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Removed superfluous usage of shy in the documentation since it can cause problem if + a buggy tool is used.</p> + <p> + Own Id: OTP-9319 Aux Id:</p> + </item> + </list> + </section> + </section> + + <section> <title>cosTime 1.1.10</title> <section> diff --git a/lib/cosTime/vsn.mk b/lib/cosTime/vsn.mk index ebc5aff1cc..4d982f3013 100644 --- a/lib/cosTime/vsn.mk +++ b/lib/cosTime/vsn.mk @@ -1,2 +1,2 @@ -COSTIME_VSN = 1.1.10 +COSTIME_VSN = 1.1.11 diff --git a/lib/cosTransactions/doc/src/CosTransactions_RecoveryCoordinator.xml b/lib/cosTransactions/doc/src/CosTransactions_RecoveryCoordinator.xml index 4b870f4b90..0222f3be86 100644 --- a/lib/cosTransactions/doc/src/CosTransactions_RecoveryCoordinator.xml +++ b/lib/cosTransactions/doc/src/CosTransactions_RecoveryCoordinator.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1999</year><year>2009</year> + <year>1999</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,12 +21,11 @@ </legalnotice> - <title>CosTransactions_­RecoveryCoordinator</title> - <shorttitle>..._RecoveryCoordinator</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosTransactions_RecoveryCoordinator</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>1999-04-12</date> <rev>PA1</rev> diff --git a/lib/cosTransactions/doc/src/CosTransactions_SubtransactionAwareResource.xml b/lib/cosTransactions/doc/src/CosTransactions_SubtransactionAwareResource.xml index 2c7b6b5215..5878b41360 100644 --- a/lib/cosTransactions/doc/src/CosTransactions_SubtransactionAwareResource.xml +++ b/lib/cosTransactions/doc/src/CosTransactions_SubtransactionAwareResource.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1999</year><year>2009</year> + <year>1999</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -21,8 +21,7 @@ </legalnotice> - <title>CosTransactions_­SubtransactionAwareResource</title> - <shorttitle>..._SubtransactionAwareResource</shorttitle> + <title>CosTransactions_SubtransactionAwareResource</title> <prepared></prepared> <docno></docno> <checked></checked> diff --git a/lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml b/lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml index 162e6e8cd1..7e93aa2964 100644 --- a/lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml +++ b/lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml @@ -23,12 +23,11 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>CosTransactions_­TransactionFactory</title> - <shorttitle>..._TransactionFactory</shorttitle> - <prepared>Niclas Eklund</prepared> - <responsible>Niclas Eklund</responsible> + <title>CosTransactions_TransactionFactory</title> + <prepared></prepared> + <responsible></responsible> <docno></docno> - <approved>Niclas Eklund</approved> + <approved></approved> <checked></checked> <date>1999-04-12</date> <rev>PA1</rev> diff --git a/lib/cosTransactions/doc/src/notes.xml b/lib/cosTransactions/doc/src/notes.xml index 7586e3c13f..29addf424d 100644 --- a/lib/cosTransactions/doc/src/notes.xml +++ b/lib/cosTransactions/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1999</year><year>2010</year> + <year>1999</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,6 +33,22 @@ </header> <section> + <title>cosTransactions 1.2.11</title> + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Removed superfluous usage of shy in the documentation since it can cause problem if + a buggy tool is used.</p> + <p> + Own Id: OTP-9319 Aux Id:</p> + </item> + </list> + </section> + </section> + + <section> <title>cosTransactions 1.2.10</title> <section> <title>Improvements and New Features</title> diff --git a/lib/cosTransactions/vsn.mk b/lib/cosTransactions/vsn.mk index 82e46f51dd..3960c58c5b 100644 --- a/lib/cosTransactions/vsn.mk +++ b/lib/cosTransactions/vsn.mk @@ -1 +1 @@ -COSTRANSACTIONS_VSN = 1.2.10 +COSTRANSACTIONS_VSN = 1.2.11 diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 3ebf62d87c..c781ccb302 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -50,6 +50,8 @@ #include <openssl/rc2.h> #include <openssl/blowfish.h> #include <openssl/rand.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> #ifdef VALGRIND # include <valgrind/memcheck.h> @@ -128,11 +130,15 @@ static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -199,12 +205,18 @@ static ErlNifFunc nif_funcs[] = { {"md4_final", 1, md4_final}, {"md5_mac_n", 3, md5_mac_n}, {"sha_mac_n", 3, sha_mac_n}, + {"hmac_init", 2, hmac_init}, + {"hmac_update", 2, hmac_update}, + {"hmac_final", 1, hmac_final}, + {"hmac_final_n", 2, hmac_final}, {"des_cbc_crypt", 4, des_cbc_crypt}, {"des_ecb_crypt", 3, des_ecb_crypt}, {"des_ede3_cbc_crypt", 6, des_ede3_cbc_crypt}, {"aes_cfb_128_crypt", 4, aes_cfb_128_crypt}, {"aes_ctr_encrypt", 3, aes_ctr_encrypt}, {"aes_ctr_decrypt", 3, aes_ctr_encrypt}, + {"aes_ctr_stream_encrypt", 2, aes_ctr_stream_encrypt}, + {"aes_ctr_stream_decrypt", 2, aes_ctr_stream_encrypt}, {"rand_bytes", 1, rand_bytes_1}, {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif}, {"rand_bytes", 3, rand_bytes_3}, @@ -255,6 +267,7 @@ static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_sha; static ERL_NIF_TERM atom_md5; +static ERL_NIF_TERM atom_ripemd160; static ERL_NIF_TERM atom_error; static ERL_NIF_TERM atom_rsa_pkcs1_padding; static ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding; @@ -324,6 +337,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_false = enif_make_atom(env,"false"); atom_sha = enif_make_atom(env,"sha"); atom_md5 = enif_make_atom(env,"md5"); + atom_ripemd160 = enif_make_atom(env,"ripemd160"); atom_error = enif_make_atom(env,"error"); atom_rsa_pkcs1_padding = enif_make_atom(env,"rsa_pkcs1_padding"); atom_rsa_pkcs1_oaep_padding = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); @@ -581,6 +595,84 @@ static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return ret; } +static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key) */ + ErlNifBinary key; + ERL_NIF_TERM ret; + unsigned char * ctx_buf; + const EVP_MD *md; + + if (argv[0] == atom_sha) md = EVP_sha1(); + else if (argv[0] == atom_md5) md = EVP_md5(); + else if (argv[0] == atom_ripemd160) md = EVP_ripemd160(); + else goto badarg; + + if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) { + badarg: + return enif_make_badarg(env); + } + + ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret); + HMAC_CTX_init((HMAC_CTX *) ctx_buf); + HMAC_Init((HMAC_CTX *) ctx_buf, key.data, key.size, md); + + return ret; +} + +static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + ErlNifBinary context, data; + ERL_NIF_TERM ret; + unsigned char * ctx_buf; + + if (!enif_inspect_binary(env, argv[0], &context) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || context.size != sizeof(HMAC_CTX)) { + return enif_make_badarg(env); + } + + ctx_buf = enif_make_new_binary(env, sizeof(HMAC_CTX), &ret); + memcpy(ctx_buf, context.data, context.size); + HMAC_Update((HMAC_CTX *)ctx_buf, data.data, data.size); + + return ret; +} + +static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) or (Context, HashLen) */ + ErlNifBinary context; + ERL_NIF_TERM ret; + HMAC_CTX ctx; + unsigned char mac_buf[EVP_MAX_MD_SIZE]; + unsigned char * mac_bin; + unsigned int req_len = 0; + unsigned int mac_len; + + if (!enif_inspect_binary(env, argv[0], &context)) { + return enif_make_badarg(env); + } + if (argc == 2 && !enif_get_uint(env, argv[1], &req_len)) { + return enif_make_badarg(env); + } + + if (context.size != sizeof(ctx)) { + return enif_make_badarg(env); + } + memcpy(&ctx, context.data, context.size); + + HMAC_Final(&ctx, mac_buf, &mac_len); + HMAC_CTX_cleanup(&ctx); + + if (argc == 2 && req_len < mac_len) { + // Only truncate to req_len bytes if asked. + mac_len = req_len; + } + mac_bin = enif_make_new_binary(env, mac_len, &ret); + memcpy(mac_bin, mac_buf, mac_len); + + return ret; +} + static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Key, Ivec, Text, IsEncrypt) */ ErlNifBinary key, ivec, text; @@ -695,6 +787,46 @@ static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM return ret; } +/* Initializes state for ctr streaming (de)encryption +*/ +static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* ({Key, IVec, ECount, Num}, Data) */ + ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin; + AES_KEY aes_key; + unsigned int num; + ERL_NIF_TERM ret, num2_term, cipher_term, ivec2_term, ecount2_term, new_state_term; + int state_arity; + const ERL_NIF_TERM *state_term; + unsigned char * ivec2_buf; + unsigned char * ecount2_buf; + + if (!enif_get_tuple(env, argv[0], &state_arity, &state_term) + || state_arity != 4 + || !enif_inspect_iolist_as_binary(env, state_term[0], &key_bin) + || AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key) != 0 + || !enif_inspect_binary(env, state_term[1], &ivec_bin) || ivec_bin.size != 16 + || !enif_inspect_binary(env, state_term[2], &ecount_bin) || ecount_bin.size != AES_BLOCK_SIZE + || !enif_get_uint(env, state_term[3], &num) + || !enif_inspect_iolist_as_binary(env, argv[1], &text_bin)) { + return enif_make_badarg(env); + } + + ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term); + ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term); + + memcpy(ivec2_buf, ivec_bin.data, 16); + memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size); + + AES_ctr128_encrypt((unsigned char *) text_bin.data, + enif_make_new_binary(env, text_bin.size, &cipher_term), + text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num); + + num2_term = enif_make_uint(env, num); + new_state_term = enif_make_tuple4(env, state_term[0], ivec2_term, ecount2_term, num2_term); + ret = enif_make_tuple2(env, new_state_term, cipher_term); + return ret; +} + static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Bytes) */ unsigned bytes; diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 1ccea6df79..179ba4498c 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -282,6 +282,57 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </desc> </func> <func> + <name>hmac_init(Type, Key) -> Context</name> + <fsummary></fsummary> + <type> + <v>Type = sha | md5 | ripemd160</v> + <v>Key = iolist() | binary()</v> + <v>Context = binary()</v> + </type> + <desc> + <p>Initializes the context for streaming HMAC operations. <c>Type</c> determines + which hash function to use in the HMAC operation. <c>Key</c> is the authentication + key. The key can be any length.</p> + </desc> + </func> + <func> + <name>hmac_update(Context, Data) -> NewContext</name> + <fsummary></fsummary> + <type> + <v>Context = NewContext = binary()</v> + <v>Data = iolist() | binary()</v> + </type> + <desc> + <p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> + must have been generated using an HMAC init function (such as + <seealso marker="#hmac_init/2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c> + must be passed into the next call to <c>hmac_update</c>.</p> + </desc> + </func> + <func> + <name>hmac_final(Context) -> Mac</name> + <fsummary></fsummary> + <type> + <v>Context = Mac = binary()</v> + </type> + <desc> + <p>Finalizes the HMAC operation referenced by <c>Context</c>. The size of the resultant MAC is + determined by the type of hash function used to generate it.</p> + </desc> + </func> + <func> + <name>hmac_final_n(Context, HashLen) -> Mac</name> + <fsummary></fsummary> + <type> + <v>Context = Mac = binary()</v> + <v>HashLen = non_neg_integer()</v> + </type> + <desc> + <p>Finalizes the HMAC operation referenced by <c>Context</c>. <c>HashLen</c> must be greater than + zero. <c>Mac</c> will be a binary with at most <c>HashLen</c> bytes. Note that if HashLen is greater than the actual number of bytes returned from the underlying hash, the returned hash will have fewer than <c>HashLen</c> bytes.</p> + </desc> + </func> + <func> <name>sha_mac(Key, Data) -> Mac</name> <fsummary>Compute an <c>MD5 MAC</c>message authentification code</fsummary> <type> @@ -589,6 +640,55 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </desc> </func> <func> + <name>aes_ctr_stream_init(Key, IVec) -> State</name> + <fsummary></fsummary> + <type> + <v>State = { K, I, E, C }</v> + <v>Key = K = iolist()</v> + <v>IVec = I = E = binary()</v> + <v>C = integer()</v> + </type> + <desc> + <p>Initializes the state for use in streaming AES encryption using Counter mode (CTR). + <c>Key</c> is the AES key and must be either 128, 192, or 256 bts long. <c>IVec</c> is + an arbitrary initializing vector of 128 bits (16 bytes). This state is for use with + <seealso marker="#aes_ctr_stream_encrypt/2">aes_ctr_stream_encrypt</seealso> and + <seealso marker="#aes_ctr_stream_decrypt/2">aes_ctr_stream_decrypt</seealso>.</p> + </desc> + </func> + <func> + <name>aes_ctr_stream_encrypt(State, Text) -> { NewState, Cipher}</name> + <fsummary></fsummary> + <type> + <v>Text = iolist() | binary()</v> + <v>Cipher = binary()</v> + </type> + <desc> + <p>Encrypts <c>Text</c> according to AES in Counter mode (CTR). This function can be + used to encrypt a stream of text using a series of calls instead of requiring all + text to be in memory. <c>Text</c> can be any number of bytes. State is initialized using + <seealso marker="#aes_ctr_stream_init/2">aes_ctr_stream_init</seealso>. <c>NewState</c> is the new streaming + encryption state that must be passed to the next call to <c>aes_ctr_stream_encrypt</c>. + <c>Cipher</c> is the encrypted cipher text.</p> + </desc> + </func> + <func> + <name>aes_ctr_stream_decrypt(State, Cipher) -> { NewState, Text }</name> + <fsummary></fsummary> + <type> + <v>Cipher = iolist() | binary()</v> + <v>Text = binary()</v> + </type> + <desc> + <p>Decrypts <c>Cipher</c> according to AES in Counter mode (CTR). This function can be + used to decrypt a stream of ciphertext using a series of calls instead of requiring all + ciphertext to be in memory. <c>Cipher</c> can be any number of bytes. State is initialized using + <seealso marker="#aes_ctr_stream_init/2">aes_ctr_stream_init</seealso>. <c>NewState</c> is the new streaming + encryption state that must be passed to the next call to <c>aes_ctr_stream_encrypt</c>. + <c>Text</c> is the decrypted data.</p> + </desc> + </func> + <func> <name>erlint(Mpint) -> N</name> <name>mpint(N) -> Mpint</name> <fsummary>Convert between binary multi-precision integer and erlang big integer</fsummary> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index cc7b3acc9c..c35dfcebab 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -28,6 +28,7 @@ %-export([sha256/1, sha256_init/0, sha256_update/2, sha256_final/1]). %-export([sha512/1, sha512_init/0, sha512_update/2, sha512_final/1]). -export([md5_mac/2, md5_mac_96/2, sha_mac/2, sha_mac_96/2]). +-export([hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). -export([des_cbc_encrypt/3, des_cbc_decrypt/3, des_cbc_ivec/1]). -export([des_ecb_encrypt/2, des_ecb_decrypt/2]). -export([des3_cbc_encrypt/5, des3_cbc_decrypt/5]). @@ -53,6 +54,7 @@ -export([aes_cbc_256_encrypt/3, aes_cbc_256_decrypt/3]). -export([aes_cbc_ivec/1]). -export([aes_ctr_encrypt/3, aes_ctr_decrypt/3]). +-export([aes_ctr_stream_init/2, aes_ctr_stream_encrypt/2, aes_ctr_stream_decrypt/2]). -export([dh_generate_parameters/2, dh_check/1]). %% Testing see below @@ -64,6 +66,7 @@ %% sha512, sha512_init, sha512_update, sha512_final, md5_mac, md5_mac_96, sha_mac, sha_mac_96, + sha_mac_init, sha_mac_update, sha_mac_final, des_cbc_encrypt, des_cbc_decrypt, des_ecb_encrypt, des_ecb_decrypt, des_ede3_cbc_encrypt, des_ede3_cbc_decrypt, @@ -85,6 +88,7 @@ %% idea_cbc_encrypt, idea_cbc_decrypt, aes_cbc_256_encrypt, aes_cbc_256_decrypt, aes_ctr_encrypt, aes_ctr_decrypt, + aes_ctr_stream_init, aes_ctr_stream_encrypt, aes_ctr_stream_decrypt, info_lib]). -type rsa_digest_type() :: 'md5' | 'sha'. @@ -217,6 +221,19 @@ sha_final(_Context) -> ?nif_stub. %% %% +%% HMAC (multiple hash options) +%% +-spec hmac_init(atom(), iodata()) -> binary(). +-spec hmac_update(binary(), iodata()) -> binary(). +-spec hmac_final(binary()) -> binary(). +-spec hmac_final_n(binary(), integer()) -> binary(). + +hmac_init(_Type, _Key) -> ?nif_stub. +hmac_update(_Context, _Data) -> ? nif_stub. +hmac_final(_Context) -> ? nif_stub. +hmac_final_n(_Context, _HashLen) -> ? nif_stub. + +%% %% MD5_MAC %% -spec md5_mac(iodata(), iodata()) -> binary(). @@ -243,7 +260,7 @@ sha_mac_96(Key, Data) -> sha_mac_n(Key,Data,12). sha_mac_n(_Key,_Data,_MacSz) -> ?nif_stub. - + %% %% CRYPTO FUNCTIONS %% @@ -579,6 +596,22 @@ aes_ctr_encrypt(_Key, _IVec, _Data) -> ?nif_stub. aes_ctr_decrypt(_Key, _IVec, _Cipher) -> ?nif_stub. %% +%% AES - in counter mode (CTR) with state maintained for multi-call streaming +%% +-type ctr_state() :: { iodata(), binary(), binary(), integer() }. + +-spec aes_ctr_stream_init(iodata(), binary()) -> ctr_state(). +-spec aes_ctr_stream_encrypt(ctr_state(), binary()) -> + { ctr_state(), binary() }. +-spec aes_ctr_stream_decrypt(ctr_state(), binary()) -> + { ctr_state(), binary() }. + +aes_ctr_stream_init(Key, IVec) -> + {Key, IVec, << 0:128 >>, 0}. +aes_ctr_stream_encrypt({_Key, _IVec, _ECount, _Num}=_State, _Data) -> ?nif_stub. +aes_ctr_stream_decrypt({_Key, _IVec, _ECount, _Num}=_State, _Cipher) -> ?nif_stub. + +%% %% XOR - xor to iolists and return a binary %% NB doesn't check that they are the same size, just concatenates %% them and sends them to the driver diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 854a8b4485..283aadb6ea 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -31,6 +31,11 @@ md4_update/1, sha/1, sha_update/1, + hmac_update_sha/1, + hmac_update_sha_n/1, + hmac_update_md5/1, + hmac_update_md5_io/1, + hmac_update_md5_n/1, sha256/1, sha256_update/1, sha512/1, @@ -44,6 +49,7 @@ aes_cbc/1, aes_cbc_iter/1, aes_ctr/1, + aes_ctr_stream/1, mod_exp_test/1, rand_uniform_test/1, strong_rand_test/1, @@ -67,9 +73,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [link_test, md5, md5_update, md4, md4_update, md5_mac, md5_mac_io, sha, sha_update, + hmac_update_sha, hmac_update_sha_n, hmac_update_md5_n, hmac_update_md5_io, hmac_update_md5, %% sha256, sha256_update, sha512,sha512_update, des_cbc, aes_cfb, aes_cbc, - aes_cbc_iter, aes_ctr, des_cbc_iter, des_ecb, + aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb, rand_uniform_test, strong_rand_test, rsa_verify_test, dsa_verify_test, rsa_sign_test, dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test, @@ -284,6 +291,101 @@ sha(Config) when is_list(Config) -> hexstr2bin("84983E441C3BD26EBAAE4AA1F95129E5E54670F1")). +%% +hmac_update_sha_n(doc) -> + ["Request a larger-than-allowed SHA1 HMAC using hmac_init, hmac_update, and hmac_final_n. " + "Expected values for examples are generated using crypto:sha_mac." ]; +hmac_update_sha_n(suite) -> + []; +hmac_update_sha_n(Config) when is_list(Config) -> + ?line Key = hexstr2bin("00010203101112132021222330313233" + "04050607141516172425262734353637" + "08090a0b18191a1b28292a2b38393a3b" + "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"), + ?line Data = "Sampl", + ?line Data2 = "e #1", + ?line Ctx = crypto:hmac_init(sha, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), + ?line Mac = crypto:hmac_final_n(Ctx3, 1024), + ?line Exp = crypto:sha_mac(Key, lists:flatten([Data, Data2])), + ?line m(Exp, Mac), + ?line m(size(Exp), size(Mac)). + + +hmac_update_sha(doc) -> + ["Generate an SHA1 HMAC using hmac_init, hmac_update, and hmac_final. " + "Expected values for examples are generated using crypto:sha_mac." ]; +hmac_update_sha(suite) -> + []; +hmac_update_sha(Config) when is_list(Config) -> + ?line Key = hexstr2bin("00010203101112132021222330313233" + "04050607141516172425262734353637" + "08090a0b18191a1b28292a2b38393a3b" + "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"), + ?line Data = "Sampl", + ?line Data2 = "e #1", + ?line Ctx = crypto:hmac_init(sha, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), + ?line Mac = crypto:hmac_final(Ctx3), + ?line Exp = crypto:sha_mac(Key, lists:flatten([Data, Data2])), + ?line m(Exp, Mac). + +hmac_update_md5(doc) -> + ["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. " + "Expected values for examples are generated using crypto:md5_mac." ]; +hmac_update_md5(suite) -> + []; +hmac_update_md5(Config) when is_list(Config) -> + % ?line Key2 = ["A fine speach", "by a fine man!"], + Key2 = "A fine speach by a fine man!", + ?line Long1 = "Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal.", + ?line Long2 = "Now we are engaged in a great civil war, testing whether that nation, or any nation, so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this.", + ?line Long3 = "But, in a larger sense, we can not dedicate, we can not consecrate, we can not hallow this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us-that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion—that we here highly resolve that these dead shall not have died in vain-that this nation, under God, shall have a new birth of freedom-and that government of the people, by the people, for the people, shall not perish from the earth.", + ?line CtxA = crypto:hmac_init(md5, Key2), + ?line CtxB = crypto:hmac_update(CtxA, Long1), + ?line CtxC = crypto:hmac_update(CtxB, Long2), + ?line CtxD = crypto:hmac_update(CtxC, Long3), + ?line Mac2 = crypto:hmac_final(CtxD), + ?line Exp2 = crypto:md5_mac(Key2, lists:flatten([Long1, Long2, Long3])), + ?line m(Exp2, Mac2). + + +hmac_update_md5_io(doc) -> + ["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. " + "Expected values for examples are generated using crypto:md5_mac." ]; +hmac_update_md5_io(suite) -> + []; +hmac_update_md5_io(Config) when is_list(Config) -> + ?line Key = ["A fine speach", "by a fine man!"], + ?line Data = "Sampl", + ?line Data2 = "e #1", + ?line Ctx = crypto:hmac_init(md5, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), + ?line Mac = crypto:hmac_final(Ctx3), + ?line Exp = crypto:md5_mac(Key, lists:flatten([Data, Data2])), + ?line m(Exp, Mac). + + +hmac_update_md5_n(doc) -> + ["Generate a shortened MD5 HMAC using hmac_init, hmac_update, and hmac_final. " + "Expected values for examples are generated using crypto:md5_mac." ]; +hmac_update_md5_n(suite) -> + []; +hmac_update_md5_n(Config) when is_list(Config) -> + ?line Key = ["A fine speach", "by a fine man!"], + ?line Data = "Sampl", + ?line Data2 = "e #1", + ?line Ctx = crypto:hmac_init(md5, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), + ?line Mac = crypto:hmac_final_n(Ctx3, 12), + ?line Exp = crypto:md5_mac_96(Key, lists:flatten([Data, Data2])), + ?line m(Exp, Mac). + + %% %% sha_update(doc) -> @@ -673,6 +775,77 @@ aes_ctr_do(Key,{IVec, Plain, Cipher}) -> ?line m(C, hexstr2bin(Cipher)), ?line m(P, crypto:aes_ctr_decrypt(Key, I, C)). +aes_ctr_stream(doc) -> "CTR Streaming"; +aes_ctr_stream(Config) when is_list(Config) -> + %% Sample data from NIST Spec.Publ. 800-38A + %% F.5.1 CTR-AES128.Encrypt + Key128 = hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), + Samples128 = [{"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", % Input Block + ["6bc1bee22e409f", "96e93d7e117393172a"], % Plaintext + ["874d6191b620e3261bef6864990db6ce"]}, % Ciphertext + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff00", + ["ae2d8a57", "1e03ac9c", "9eb76fac", "45af8e51"], + ["9806f66b7970fdff","8617187bb9fffdff"]}, + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff01", + ["30c81c46a35c", "e411e5fbc119", "1a0a52ef"], + ["5ae4df3e","dbd5d3","5e5b4f0902","0db03eab"]}, + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff02", + ["f69f2445df4f9b17ad2b417be66c3710"], + ["1e031dda2fbe","03d1792170a0","f3009cee"]}], + lists:foreach(fun(S) -> aes_ctr_stream_do(Key128,S) end, Samples128), + + %% F.5.3 CTR-AES192.Encrypt + Key192 = hexstr2bin("8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"), + Samples192 = [{"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", % Input Block + ["6bc1bee22e409f96e93d7e117393172a"], % Plaintext + ["1abc9324","17521c","a24f2b04","59fe7e6e0b"]}, % Ciphertext + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff00", + ["ae2d8a57", "1e03ac9c9eb76fac", "45af8e51"], + ["090339ec0aa6faefd5ccc2c6f4ce8e94"]}, + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff01", + ["30c81c46a35ce411", "e5fbc1191a0a52ef"], + ["1e36b26bd1","ebc670d1bd1d","665620abf7"]}, + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff02", + ["f69f2445", "df4f9b17ad", "2b417be6", "6c3710"], + ["4f78a7f6d2980958","5a97daec58c6b050"]}], + lists:foreach(fun(S) -> aes_ctr_stream_do(Key192,S) end, Samples192), + + %% F.5.5 CTR-AES256.Encrypt + Key256 = hexstr2bin("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + Samples256 = [{"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", % Input Block + ["6bc1bee22e409f96", "e93d7e117393172a"], % Plaintext + ["601ec313775789", "a5b7a7f504bbf3d228"]}, % Ciphertext + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff00", + ["ae2d8a571e03ac9c9eb76fac45af8e51"], + ["f443e3ca","4d62b59aca84","e990cacaf5c5"]}, + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff01", + ["30c81c46","a35ce411","e5fbc119","1a0a52ef"], + ["2b0930daa23de94ce87017ba2d84988d"]}, + {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff02", + ["f69f2445df4f","9b17ad2b41","7be66c3710"], + ["dfc9c5","8db67aada6","13c2dd08","457941a6"]}], + lists:foreach(fun(S) -> aes_ctr_stream_do(Key256,S) end, Samples256). + + +aes_ctr_stream_do(Key,{IVec, PlainList, CipherList}) -> + ?line I = hexstr2bin(IVec), + ?line S = crypto:aes_ctr_stream_init(Key, I), + ?line C = aes_ctr_stream_do_iter( + S, PlainList, [], + fun(S2,P) -> crypto:aes_ctr_stream_encrypt(S2, P) end), + ?line m(C, hexstr2bin(lists:flatten(CipherList))), + ?line P = aes_ctr_stream_do_iter( + S, CipherList, [], + fun(S2,C2) -> crypto:aes_ctr_stream_decrypt(S2, C2) end), + ?line m(P, hexstr2bin(lists:flatten(PlainList))). + +aes_ctr_stream_do_iter(_State, [], Acc, _CipherFun) -> + iolist_to_binary(lists:reverse(Acc)); +aes_ctr_stream_do_iter(State, [Plain|Rest], Acc, CipherFun) -> + ?line P = hexstr2bin(Plain), + ?line {S2, C} = CipherFun(State, P), + aes_ctr_stream_do_iter(S2, Rest, [C | Acc], CipherFun). + %% %% mod_exp_test(doc) -> @@ -1127,7 +1300,8 @@ worker_loop(0, _) -> worker_loop(N, Config) -> Funcs = { md5, md5_update, md5_mac, md5_mac_io, sha, sha_update, des_cbc, aes_cfb, aes_cbc, des_cbc_iter, rand_uniform_test, strong_rand_test, - rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test }, + rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test, + hmac_update_md5, hmac_update_sha, aes_ctr_stream }, F = element(random:uniform(size(Funcs)),Funcs), %%io:format("worker ~p calling ~p\n",[self(),F]), diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index 740c68d8fa..e754aabc44 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 2.0.2.2 +CRYPTO_VSN = 2.0.3 diff --git a/lib/diameter/doc/src/.gitignore b/lib/diameter/doc/src/.gitignore new file mode 100644 index 0000000000..feeb378fd8 --- /dev/null +++ b/lib/diameter/doc/src/.gitignore @@ -0,0 +1,2 @@ + +/depend.mk diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile index f2a91a88b7..1453138cb6 100644 --- a/lib/diameter/doc/src/Makefile +++ b/lib/diameter/doc/src/Makefile @@ -35,8 +35,10 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # ---------------------------------------------------- include files.mk +XML_REF_FILES = $(XML_REF1_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES) + XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) \ - $(XML_REF1_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES) \ + $(XML_REF_FILES) \ $(XML_PART_FILES) $(XML_CHAPTER_FILES) INTERNAL_HTML_FILES = $(TECHNICAL_DESCR_FILES:%.xml=$(HTMLDIR)/%.html) @@ -49,9 +51,7 @@ HTML_FILES = $(HTML_APP_FILES) $(HTML_EXTRA_FILES) $(HTML_PART_FILES) INFO_FILE = ../../info -HTML_REF_FILES = $(XML_REF1_FILES:%.xml=$(HTMLDIR)/%.html) \ - $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \ - $(XML_REF4_FILES:%.xml=$(HTMLDIR)/%.html) +HTML_REF_FILES = $(XML_REF_FILES:%.xml=$(HTMLDIR)/%.html) HTML_CHAPTER_FILES = $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) @@ -98,6 +98,7 @@ html: gifs $(HTML_REF_MAN_FILE) clean clean_docs: clean_pdf clean_html clean_man rm -f errs core *~ + rm -f depend.mk clean_pdf: rm -f $(PDFDIR)/* @@ -118,7 +119,9 @@ $(INDEX_TARGET): $(INDEX_SRC) $(APP_FILE) s/%UP_ONE_LEVEL%/..\/..\/..\/doc\/index.html/; \ s/%OFF_PRINT%/pdf\/diameter-$(VSN).pdf/' $< > $@ -depend debug opt: +depend: depend.mk + +debug opt: info: @echo "->Makefile<-" @@ -181,17 +184,13 @@ release_docs_spec: $(LOCAL)docs release_spec: -$(HTMLDIR)/diameter_app.html: diameter_app.xml -$(HTMLDIR)/diameter_compile.html: diameter_compile.xml -$(HTMLDIR)/diameter_debug.html: diameter_debug.xml -$(HTMLDIR)/diameter_dict.html: diameter_dict.xml -$(HTMLDIR)/diameter_intro.html: diameter_intro.xml -$(HTMLDIR)/diameter_run.html: diameter_run.xml -$(HTMLDIR)/diameter.html: diameter.xml -$(HTMLDIR)/diameter_tcp.html: diameter_tcp.xml -$(HTMLDIR)/diameter_transport.html: diameter_transport.xml -$(HTMLDIR)/diameter_soc.html: diameter_soc.xml -$(HTMLDIR)/diameter_sctp.html: diameter_sctp.xml +depend.mk: depend.sed $(XML_REF_FILES) $(XML_CHAPTER_FILES) Makefile + (for f in $(XML_REF_FILES) $(XML_CHAPTER_FILES); do \ + sed -f $< $$f | sed "s@%FILE%@`basename $$f .xml`@g"; \ + done) \ + > $@ + +-include depend.mk .PHONY: clean clean_html clean_man clean_pdf \ depend debug opt info \ diff --git a/lib/diameter/doc/src/depend.sed b/lib/diameter/doc/src/depend.sed new file mode 100644 index 0000000000..5973c4586e --- /dev/null +++ b/lib/diameter/doc/src/depend.sed @@ -0,0 +1,34 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2011. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% + +# +# Generate dependencies for html output. The output is further +# massaged in Makefile. +# + +/^<com>\([^<]*\)<\/com>/b rf +/^<module>\([^<]*\)<\/module>/b rf + +/^<chapter>/!d + +s@@$(HTMLDIR)/%FILE%.html: %FILE%.xml@ +q + +:rf +s@@$(HTMLDIR)/\1.html: %FILE%.xml@ +q diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 9774183a2a..36b6cbf0cf 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -415,6 +415,12 @@ eval(F) -> Evaluating an evaluable() <c>E</c> on an argument list <c>A</c> is meant in the sense of <c>eval([E|A])</c>.</p> +<p> +Beware of using local funs (that is, fun expressions not of the +form <c>fun Module:Name/Arity</c>) in situations in which the fun is +not short-lived and code is to be upgraded at runtime since any +processes retaining such a fun will have a reference to old code.</p> + <marker id="peer_filter"/> </item> @@ -712,7 +718,7 @@ transport.</p> <p> Unrecognized options are silently ignored but are returned unmodified by <seealso -marker="#service_info_1">service_info/1,2</seealso> and can be referred to +marker="#service_info">service_info/2</seealso> and can be referred to in predicate functions passed to <seealso marker="#remove_transport">remove_transport/2</seealso>.</p> @@ -739,7 +745,9 @@ marker="#remove_transport">remove_transport/2</seealso>.</p> </type> <desc> <p> -Add transport capability to a service. +Add transport capability to a service.</p> + +<p> The service will start a transport process(es) in order to establish a connection with the peer, either by connecting to the peer (<c>connect</c>) or by accepting incoming connection requests @@ -761,10 +769,8 @@ handling of DWR/DWA and required by RFC 3539 as well as for DPR/DPA.</p> <p> The returned reference uniquely identifies the transport within the scope of the service. -Not that the function returns before a transport connection has been -established.</p> - -<p> +Note that the function returns before a transport connection has been +established. It is not an error to add a transport to a service that has not yet been configured: a service can be started after configuring transports.</p> @@ -776,7 +782,7 @@ transports.</p> <!-- ===================================================================== --> <func> -<name>call(SvcName, App, Request, Options) -> Answer | {error, Reason}</name> +<name>call(SvcName, App, Request, Options) -> ok | Answer | {error, Reason}</name> <fsummary>Send a Diameter request message.</fsummary> <type> <v>SvcName = service_name()</v> @@ -794,15 +800,19 @@ Send a Diameter request message and possibly return the answer or error.</p> defined and callbacks to the corresponding callback module will follow as described below and in <seealso marker="diameter_app">diameter_app(3)</seealso>. -The call returns either when an answer message is received from the -peer or an error occurs, unless the <c>detach</c> option has been -specified. -If <c>detach</c> is not specified then the form of an <c>Answer</c> is -as returned from a <seealso -marker="diameter_app#handle_answer">handle_answer/4</seealso> or +Unless the <c>detach</c> option has been specified to cause an earlier +return, the call returns either when an answer message is received +from the peer or an error occurs. +In the case of an answer, the return value is as returned by a <seealso +marker="diameter_app#handle_answer">handle_answer/4</seealso> +callback. +In the case of an error, whether or not the error is returned directly +by diameter or from a <seealso marker="diameter_app#handle_error">handle_error/4</seealso> -callback.</p> +callback depends on whether or not the outgoing request is +successfully encoded for transmission from the peer, the cases being +documented below.</p> <p> If there are no suitable peers, or if @@ -888,7 +898,8 @@ attempt to send the request over the transport.</p> <p> Note that <c>{error, encode}</c> is the only return value which -guarantees that the request has not been sent over the transport.</p> +guarantees that the request has <em>not</em> been sent over the +transport.</p> <marker id="origin_state_id"/> </desc> @@ -902,9 +913,11 @@ guarantees that the request has not been sent over the transport.</p> <desc> <p> Return a reasonable value for use as Origin-State-Id in -outgoing messages. -The value returned is the number of seconds since 19680120T031408Z -(the first value that can be encoded as a Time()) +outgoing messages.</p> + +<p> +The value returned is the number of seconds since 19680120T031408Z, +the first value that can be encoded as a Diameter Time(), at the time the diameter application was started.</p> <marker id="remove_transport"/> @@ -959,25 +972,7 @@ DPA or timeout.</p> <!-- TODO: document the timeout value, possibly make configurable. --> -<marker id="service_info_1"/> -</desc> -</func> - -<!-- ===================================================================== --> - -<func> -<name>service_info(SvcName) -> Info</name> -<fsummary>Return information about a started service.</fsummary> -<type> -<v>SvcName = service_name()</v> -<v>Info = [{Item, Value}]</v> -</type> -<desc> -<p> -Return information about a started service. -Equivalent to <c>service_info(SvcName, all)</c>.</p> - -<marker id="service_info_2"/> +<marker id="service_info"/> </desc> </func> @@ -992,7 +987,7 @@ Equivalent to <c>service_info(SvcName, all)</c>.</p> </type> <desc> <p> -Return specific information about a started service.</p> +Return information about a started service.</p> <marker id="services"/> </desc> @@ -1018,17 +1013,36 @@ Return the list of started services.</p> <func> <name>session_id(Ident) -> OctetString()</name> -<fsummary>Return a value for a Session-Id AVP</fsummary> +<fsummary>Return a value for a Session-Id AVP.</fsummary> <type> <v>Ident = DiameterIdentity()</v> </type> <desc> <p> -Return a value for a Session-Id AVP. +Return a value for a Session-Id AVP.</p> + +<p> The value has the form required by section 8.8 of RFC 3588. Ident should be the Origin-Host of the peer from which the message containing the returned value will be sent.</p> +<marker id="start"/> +</desc> +</func> + +<!-- ===================================================================== --> +<func> +<name>start() -> ok | {error, Reason}</name> +<fsummary>Start the diameter application.</fsummary> +<desc> +<p> +Start the diameter application.</p> + +<p> +The diameter application must be started before starting a service. +In a production system this will typically be accomplished by a boot +file, not by calling <c>start/0</c> explicitly.</p> + <marker id="start_service"/> </desc> </func> @@ -1036,7 +1050,7 @@ the message containing the returned value will be sent.</p> <!-- ===================================================================== --> <func> <name>start_service(SvcName, Options) -> ok | {error, Reason}</name> -<fsummary>Start a Diameter service</fsummary> +<fsummary>Start a Diameter service.</fsummary> <type> <v>SvcName = service_name()</v> <v>Options = [service_opt()]</v> @@ -1044,7 +1058,9 @@ the message containing the returned value will be sent.</p> </type> <desc> <p> -Start a diameter service. +Start a diameter service.</p> + +<p> A service defines a locally-implemented Diameter peer, specifying the capabilities of the peer to be used during capabilities exchange and the Diameter applications that it supports. @@ -1057,8 +1073,23 @@ marker="#add_transport">add_transport/2</seealso>.</p> <!-- ===================================================================== --> <func> +<name>stop() -> ok | {error, Reason}</name> +<fsummary>Stop the diameter application.</fsummary> +<desc> +<p> +Stop the diameter application.</p> + +<p> +</p> + +<marker id="stop_service"/> +</desc> +</func> + +<!-- ===================================================================== --> +<func> <name>stop_service(SvcName) -> ok | {error, Reason}</name> -<fsummary>Stops a Diameter service.</fsummary> +<fsummary>Stop a Diameter service.</fsummary> <type> <v>SvcName = service_name()</v> <v>Reason = term()</v> @@ -1075,7 +1106,7 @@ Stop a diameter service.</p> <func> <name>subscribe(SvcName) -> true</name> -<fsummary>Subscribe to event messages from a service.</fsummary> +<fsummary>Subscribe to event messages.</fsummary> <type> <v>SvcName = service_name()</v> </type> @@ -1095,7 +1126,7 @@ that does not yet exist.</p> <func> <name>unsubscribe(SvcName) -> true</name> -<fsummary></fsummary> +<fsummary>Unsubscribe to event messages.</fsummary> <type> <v>SvcName = service_name()</v> </type> diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml index c2fecce768..fc359b9d1d 100644 --- a/lib/diameter/doc/src/diameter_app.xml +++ b/lib/diameter/doc/src/diameter_app.xml @@ -47,23 +47,12 @@ configures one of more Diameter applications, each of whose configuration specifies a callback that handles messages specific to its application. The messages and AVPs of the Diameter application are defined in a -specification file whose format is documented in +dictionary file whose format is documented in <seealso marker="diameter_dict">diameter_dict(4)</seealso> while the callback module is documented here. The callback module implements the Diameter application-specific functionality of a service.</p> -<note> -<p> -The arities of the callback functions below assume no extra arguments. -All functions will also be passed any extra arguments configured with -the callback module itself when calling <seealso -marker="diameter#start_service">diameter:start_service/2</seealso> -and, except for peer_up, peer_down and handle_request, any extra -arguments passed to <seealso -marker="diameter#call">diameter:call/4</seealso>.</p> -</note> - <p> A callback module must export all of the functions documented below. The functions themselves are of three distinct flavours:</p> @@ -72,8 +61,8 @@ The functions themselves are of three distinct flavours:</p> <item> <p> <seealso marker="#peer_up">peer_up/3</seealso> and -<seealso marker="#peer_down">peer_down/3</seealso> signal the attainment -or loss of communicativity with a Diameter peer.</p> +<seealso marker="#peer_down">peer_down/3</seealso> signal the +attainment or loss of connectivity with a Diameter peer.</p> </item> <item> @@ -98,6 +87,17 @@ is called in response to an incoming Diameter request message.</p> </description> +<note> +<p> +The arities given for the the callback functions here assume no extra +arguments. +All functions will also be passed any extra arguments configured with +the callback module itself when calling <seealso +marker="diameter#start_service">diameter:start_service/2</seealso> +and, for the call-specific callbacks, any extra arguments passed to +<seealso marker="diameter#call">diameter:call/4</seealso>.</p> +</note> + <!-- ===================================================================== --> <!-- ===================================================================== --> @@ -131,8 +131,8 @@ a message as defined in a dictionary file is encoded as a record with one field for each component AVP. Equivalently, a message can also be encoded as a list whose head is the atom-valued message name (the record name minus any -prefix in the dictionary file) and whose tail is a list of -<c>{FieldName, FieldValue}</c> pairs.</p> +prefix specified in the relevant dictionary file) and whose tail is a +list of <c>{FieldName, FieldValue}</c> pairs.</p> <p> A third representation allows a message to be specified as a list @@ -144,7 +144,8 @@ as directed by the return value of a callback. It differs from the other other two in that it bypasses the checks for messages that do not agree with their definitions in the dictionary in -question: messages are sent exactly as specified.</p> +question (since relays agents must handle arbitrary request): messages +are sent exactly as specified.</p> </item> @@ -153,8 +154,8 @@ question: messages are sent exactly as specified.</p> <p> A container for incoming and outgoing Diameters message that's passed through encode/decode and transport. -Defined in diameter.hrl. -Fields should not be altered except as documented.</p> +Fields of a packet() record should not be set in return values except +as documented.</p> </item> <tag><c>peer_ref() = term()</c></tag> @@ -236,18 +237,18 @@ the callback module in question has been configured.</p> <desc> <p> Invoked when a transport connection has been lost following a previous -call to <seealso marker="peer_up">peer_up/3</seealso>.</p> +call to <seealso marker="#peer_up">peer_up/3</seealso>.</p> <marker id="pick_peer"/> </desc> </func> <func> -<name>Mod:pick_peer(Cands, Reserved, SvcName, State) +<name>Mod:pick_peer(Candidates, Reserved, SvcName, State) -> {ok, Peer} | {Peer, NewState} | false</name> <fsummary>Select a target peer for an outgoing request.</fsummary> <type> -<v>Cands = [Peer]</v> +<v>Candidates = [peer()]</v> <v>Peer = peer() | false</v> <v>SvcName = service_name()</v> <v>State = NewState = state()</v> @@ -266,11 +267,9 @@ marker="diameter#start_service">diameter:start_service/2</seealso>.</p> <p> The candidate peers list will only include those which are selected by any <c>filter</c> option specified in the call to -<seealso marker="diameter#call">diameter:call/4</seealso>.</p> -<!-- -The local candidates are those whose transport process is executing on -the local Erlang node, the remote list those that are available on -other nodes.</p> --> +<seealso marker="diameter#call">diameter:call/4</seealso>, and only +those which have indicated support for the Diameter application in +question.</p> <p> The return values <c>false</c> and <c>{false, State}</c> are @@ -326,13 +325,14 @@ A returned packet() should set the request to be encoded in its to pass information to the transport module. Extra arguments passed to <seealso marker="diameter#call">diameter:call/4</seealso> can be used to -communicate transport data to the callback.</p> - -<p> -Any returned packet() can set the <c>header</c> field to a +communicate transport data to the callback. +A returned packet() can also set the <c>header</c> field to a <c>diameter_header</c> record in order to specify values that should -be preserved in the outgoing request. -A specified <c>message_length</c> is ignored.</p> +be preserved in the outgoing request, although this should typically +not be necessary and allows the callback to set header values +inappropriately. +A returned <c>length</c>, <c>cmd_code</c> or <c>application_id</c> is +ignored.</p> <p> Returning <c>{discard, Reason}</c> causes the request to be aborted @@ -361,7 +361,7 @@ Invoked to return a request for encoding and retransmission. Has the same role as <seealso marker="#prepare_request">prepare_request/3</seealso> in the case that a peer connection is lost an an alternate peer selected but the -Packet passed to <c>prepare_retransmit/3</c> is as returned by +argument packet() is as returned by the initial <c>prepare_request/3</c>.</p> <p> @@ -391,10 +391,12 @@ discarded}</c>.</p> Invoked when an answer message is received from a peer. The return value is returned from the call to <seealso marker="diameter#call">diameter:call/4</seealso> for which the -callback takes place.</p> +callback takes place unless the <c>detach</c> option was +specified.</p> <p> -The decoded answer record is in the <c>msg</c> field of <c>Packet</c>, +The decoded answer record is in the <c>msg</c> field of the argument +packet(), the undecoded binary in the <c>packet</c> field. <c>Request</c> is the outgoing request message as was returned from <seealso marker="#prepare_request">prepare_request/3</seealso> or @@ -414,8 +416,10 @@ By default, an incoming answer message that cannot be successfully decoded causes the request process in question to fail, causing the relevant call to <seealso marker="diameter#call">diameter:call/4</seealso> -to return <c>{error, failure}</c>. -There is no <c>handle_error/4</c> callback in this case. +to return <c>{error, failure} (unless the <c>detach</c> option was +specified)</c>. +In particular, there is no <c>handle_error/4</c> callback in this +case. Application configuration may change this behaviour as described for <seealso marker="diameter#start_service">diameter:start_service/2</seealso>.</p> @@ -440,7 +444,8 @@ Invoked when an error occurs before an answer message is received from a peer in response to an outgoing request. The return value is returned from the call to <seealso marker="diameter#call">diameter:call/4</seealso> for which the -callback takes place.</p> +callback takes place (unless the <c>detach</c> option was +specified).</p> <p> Reason <c>timeout</c> indicates that an answer message has not been @@ -449,8 +454,7 @@ Reason <c>failover</c> indicates that the transport connection to the peer to which the request has been sent has been lost but that not alternate node was available, possibly because a <seealso marker="#pick_peer">pick_peer/4</seealso> -callback returned false. -</p> +callback returned false.</p> <marker id="handle_request"/> </desc> @@ -463,40 +467,33 @@ callback returned false. <v>Packet = packet()</v> <v>SvcName = term()</v> <v>Peer = peer()</v> -<v>Action = Reply | NoReply | Relay | {eval, Action, ContF}</v> +<v>Action = Reply | {relay, Opts} | discard | {eval, Action, ContF}</v> <v>Reply = {reply, message()} - | {protocol_error, ResultCode}</v> -<v>NoReply = discard</v> -<v>Relay = {relay, Opts}</v> -<v>Opts = list()</v> + | {protocol_error, 3000..3999}</v> +<v>Opts = diameter:call_opts()</v> <v>ContF = diameter:evaluable()</v> -<v>ResultCode = 3000..3999</v> </type> <desc> <p> -Invoked when a request message is received from a peer.</p> - -<p> +Invoked when a request message is received from a peer. The application in which the callback takes place (that is, the callback module as configured with <seealso marker="diameter#start_service">diameter:start_service/2</seealso>) is determined by the Application Identifier in the header of the -incoming Diameter request message, the selected module being the one +incoming request message, the selected module being the one whose corresponding <seealso marker="diameter_dict#MESSAGE_RECORDS">dictionary</seealso> declares -itself as defining the application in question, or the RFC 3588 relay -application if the specific application is unsupported but the relay -application has been advertised.</p> +itself as defining either the application in question or the Relay +application.</p> <p> -The packet() in which the incoming request is communicated has the -following signature.</p> +The argument packet() has the following signature.</p> <code> #diameter_packet{header = #diameter_header{}, avps = [#diameter_avp{}], msg = record() | undefined, - errors = [integer() | {integer(), #diameter_avp{}}], + errors = ['Unsigned32'() | {'Unsigned32'(), #diameter_avp{}}], bin = binary(), transport_data = term()} </code> @@ -509,12 +506,16 @@ in <seealso marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>.</p> <p> -The <c>errors</c> field specifies any non-protocol errors that were -encountered in decoding the request and can be returned in a -<c>reply</c> tuple to have diameter set the Result-Code and Failed-AVP -AVP's appropriately. -The list is empty if the request has been received in the relay -application.</p> +The <c>errors</c> field specifies any Result-Code's identifying errors +that were encountered in decoding the request. +In this case diameter will set both Result-Code and +Failed-AVP AVP's in a returned +answer message() before sending it to the peer: +the returned message() need only set any other required AVP's. +Note that the errors detected by diameter are all of the 5xxx series +(Permanent Failures). +The <c>errors</c> list is empty if the request has been received in +the relay application.</p> <p> The <c>transport_data</c> field contains an arbitrary term passed into @@ -525,34 +526,59 @@ sent back to the transport process unless another value is explicitly specified.</p> <p> -The semantics of each of the possible return values are as follows. -(TODO: more.)</p> +The semantics of each of the possible return values are as follows.</p> <taglist> -<tag><c>{reply, Answer}</c></tag> +<tag><c>{reply, message()}</c></tag> <item> <p> Send the specified answer message to the peer.</p> </item> -<tag><c>{relay, Opts}</c></tag> +<tag><c>{protocol_error, 3000..3999}</c></tag> <item> <p> -Relay a request to another peer.</p> -</item> - -<tag><c>{protocol_error, ResultCode}</c></tag> -<item> +Send an answer message to the peer containing the specified +protocol error. +Equivalent to</p> +<code> +{reply, ['answer-message' | Avps] +</code> <p> -Send an answer message to the peer containing the specified 3xxx -protocol error.</p> +where <c>Avps</c> sets the Origin-Host, Origin-Realm, the specified +Result-Code and (if the request sent one) Session-Id AVP's.</p> <p> -RFC 3588 mandates that only answers with a 3xxx series +Note that RFC 3588 mandates that only answers with a 3xxx series Result-Code (protocol errors) may set the E bit. -Returning a non-3xxx value in a <c>{protocol_error, ResultCode}</c> -tuple will cause the request process in question to fail.</p> +Returning a non-3xxx value in a <c>protocol_error</c> tuple +will cause the request process in question to fail.</p> +</item> + +<tag><c>{relay, Opts}</c></tag> +<item> +<p> +Relay a request to another peer. +The appropriate Route-Record AVP will be added to the relayed request +by diameter and <seealso marker="#pick_peer">pick_peer/4</seealso> +and <seealso marker="#prepare_request">prepare_request/3</seealso> +callback will take place just as if <seealso +marker="diameter#call">diameter:call/4</seealso> had been called +explicitly. +However, returning a <c>relay</c> tuple also causes the End-to-End +Identifier to be preserved in the header of the relayed request as +required by RFC 3588.</p> + +<p> +The returned <c>Opts</c> should not specify <c>detach</c> and +the <seealso marker="#handle_answer">handle_answer/4</seealso> +callback following from a relayed request must return its first +argument, the <c>diameter_packet</c> record containing the answer +message. +Note that the <c>extra</c> option can be specified to supply arguments +that can distinguish the relay case from others if so desired, +although the form of the request message may be sufficient.</p> </item> <tag><c>discard</c></tag> @@ -565,14 +591,14 @@ Discard the request.</p> <item> <p> Handle the request as if <c>Action</c> has been returned and then -evaluate the evaluable() <c>ContF</c> in the request process.</p> +evaluate <c>ContF</c> in the request process.</p> </item> </taglist> <p> Note that diameter will respond to protocol errors in an incoming -request without invoking the a <c>handle_request/3</c> callback.</p> +request without invoking <c>handle_request/3</c>.</p> </desc> </func> diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml index 5bc3cab9e4..a87f59bad5 100644 --- a/lib/diameter/doc/src/diameter_dict.xml +++ b/lib/diameter/doc/src/diameter_dict.xml @@ -48,37 +48,41 @@ to encode and decode its messages and AVP's. The dictionary module is in turn generated from a file that defines these messages and AVP's. The format of such a file is defined in -<seealso marker="#FILE_FORMAT">FILE FORMAT</seealso> below.</p> +<seealso marker="#FILE_FORMAT">FILE FORMAT</seealso> below. +Users add support for their specific applications by creating +dictionary files, compiling them to Erlang modules using +<seealso marker="diameterc">diameterc</seealso> and configuring the +resulting dictionaries modules on a service.</p> <p> -The codec generation also results in an hrl that defines records +The codec generation also results in a hrl file that defines records for the messages and grouped AVP's defined for the application, these -records being what a user of the diameter application sends and -receives. +records being what a user of the diameter application sends and receives. +(Modulo other available formats as discussed in <seealso +marker="diameter_app">diameter_app(3)</seealso>.) These records and the underlying Erlang data types corresponding to Diameter data formats are discussed in <seealso marker="#MESSAGE_RECORDS">MESSAGE RECORDS</seealso> and <seealso -marker="#DATA_TYPES">DATA TYPES</seealso> respectively.</p> - -<!-- TODO: Need some reserved dictionary for agents that shouldn't --> -<!-- know about specific applications. --> - -<p> -The diameter application defines the base application of RFC 3588 in -the file diameter_gen_base_rfc3588.dia, and -this is the only application that diameter itself has any specific -knowledge of. -Other applications are callback modules configured for an application -as far as diameter is concerned.</p> - -<p> -A generated hrl also contains defines for the values of defined for +marker="#DATA_TYPES">DATA TYPES</seealso> respectively. +The generated hrl also contains defines for the possible values of AVPs of type Enumerated.</p> <p> -See <seealso marker="diameter_compile">diameterc</seealso> for a -utility that transforms dictionary files into codec modules needed -at runtime.</p> +The diameter application includes three dictionary modules +corresponding to applications defined in section 2.4 of RFC 3588: +<c>diameter_gen_base_rfc3588</c> for the Diameter Common Messages +application with application identifier 0, +<c>diameter_gen_accounting</c> for the Diameter Base Accounting +application with application identifier 3 and +<c>diameter_gen_relay</c>the Relay application with application +identifier 0xFFFFFFFF. +The Common Message and Relay applications are the only applications +that diameter itself has any specific knowledge of. +The Common Message application is used for messages that diameter +itself handles: CER/CEA, DWR/DWA and DPR/DPA. +The Relay application is given special treatment with regard to +encode/decode since the messages and AVP's it handles are not specifically +defined.</p> <marker id="FILE_FORMAT"/> </description> @@ -89,7 +93,7 @@ at runtime.</p> <title>FILE FORMAT</title> <p> -A specification file consists of distinct sections. +A dictionary file consists of distinct sections. Each section starts with a line consisting of a tag followed by zero or more arguments. Each section ends at the the start of the next section or end of file. @@ -223,7 +227,7 @@ The section content is empty.</p> <p> Can occur 0 or more times (with different values of Mod) but all dictionaries should typically inherit RFC3588 AVPs from -diameter_gen_base_rfc3588.</p> +<c>diameter_gen_base_rfc3588</c>.</p> <p> Example:</p> @@ -248,7 +252,7 @@ M and P characters indicating the flags to be set on an outgoing AVP or a single - (minus) character if none are to be set. Type identifies either an AVP Data Format as defined in <seealso -marker="DATA_TYPES">DATA TYPES</seealso> below or a +marker="#DATA_TYPES">DATA TYPES</seealso> below or a type as defined by a <c>@custom_types</c> tag.</p> <p> @@ -263,7 +267,8 @@ Requested-Information 353 Enumerated V <p> Note that the P flag has been deprecated by the Diameter Maintenance -and Extensions Working Group of the IETF.</p> +and Extensions Working Group of the IETF: diameter will set the P flag +to 0 as mandated by the current draft standard.</p> </item> @@ -446,7 +451,7 @@ as values of the types defined here. Values are passed to <seealso marker="diameter#call">diameter:call/4</seealso> in a request record when sending a request, returned in a resulting -answer record and passed to a diameter_app(3) <seealso +answer record and passed to a <seealso marker="diameter_app#handle_request">handle_request</seealso> callback upon reception of an incoming request.</p> @@ -476,8 +481,8 @@ Grouped() = record() <p> On encode, an OctetString() can be specified as an iolist(), excessively large floats (in absolute value) are equivalent to -infinity or '-infinity' and excessively large integers result in -encode failure. +<c>infinity</c> or <c>'-infinity'</c> and excessively large integers +result in encode failure. The records for grouped AVPs are as discussed in the previous section.</p> @@ -583,7 +588,7 @@ QoSFilterRule() = OctetString() </code> <p> -Values of these types are not parsed by diameter.</p> +Values of these types are not currently parsed by diameter.</p> </section> @@ -594,7 +599,9 @@ Values of these types are not parsed by diameter.</p> <title>SEE ALSO</title> <p> -<seealso marker="diameter_util">diameterc(1)</seealso></p> +<seealso marker="diameterc">diameterc(1)</seealso>, +<seealso marker="diameter">diameter(3)</seealso>, +<seealso marker="diameter_app">diameter_app(3)</seealso></p> </section> diff --git a/lib/diameter/doc/src/diameter_examples.xml b/lib/diameter/doc/src/diameter_examples.xml index 344b237866..966d1f1eee 100644 --- a/lib/diameter/doc/src/diameter_examples.xml +++ b/lib/diameter/doc/src/diameter_examples.xml @@ -36,5 +36,10 @@ under the License. <!-- ===================================================================== --> +<p> +To be written. +Example code can be found in the diameter application's +<c>examples</c> subdirectory.</p> + </chapter> diff --git a/lib/diameter/doc/src/diameter_intro.xml b/lib/diameter/doc/src/diameter_intro.xml index 0009b2b77d..ef08002a8b 100644 --- a/lib/diameter/doc/src/diameter_intro.xml +++ b/lib/diameter/doc/src/diameter_intro.xml @@ -36,10 +36,60 @@ under the License. <p> The diameter application is an implementation of the Diameter protocol as defined by RFC 3588. -It supports arbitrary Diameter applications by allowing a client to -specify the commands and AVP's to be supported and has support for -implementing all roles defined in the RFC: client, server and agent. -</p> +It supports arbitrary Diameter applications by way of a +<em>dictionary</em> interface that allows messages and AVP's to be +defined and input into diameter as configuration. +It has support for all roles defined in the RFC: client, server and +agent. +This chapter provides a short overview of the application.</p> + +<p> +A Diameter peer is implemented by configuring a <em>service</em> and +one or more <em>transports</em> using the interface module +<seealso marker="diameter">diameter</seealso>. +The service configuration defines the Diameter applications to be +supported by the peer and, typically, the capabilities that it should +send to remote peers at capabilities exchange upon the establishment +of transport connections. +A transport is configured on a service and provides protocol-specific +send/receive functionality by way of a transport interface defined by +diameter and implemented by a transport module. +The diameter application provides two transport modules: <seealso +marker="diameter_tcp">diameter_tcp</seealso> and <seealso +marker="diameter_sctp">diameter_sctp</seealso> for transport over TCP +(using <c>gen_tcp</c>) and SCTP (using <c>gen_sctp</c>) respectively. +Other transports can be provided by any module that implements +diameter's <seealso marker="diameter_transport">transport +interface</seealso>.</p> + +<p> +While a service typically implements a single Diameter peer (as +identified by an Origin-Host AVP), transports can themselves be +associated with capabilities AVP's so that a single service be used to +implement more than one Diameter peer.</p> + +<p> +Each Diameter application defined on a service is configured with a +callback module that implements the <seealso +marker="diameter_app">application interface</seealso> through which +diameter communicates the connectivity of remote peers, requests peer +selection for outgoing requests, and communicates the reception of +incoming Diameter request and answer messages. +An application using diameter implements these application callback +modules to provide the functionality of the Diameter peer(s) it +implements.</p> + +<p> +Each Diameter application is also configured with one or more +dictionary modules +that provide encode/decode functionality for outgoing/incoming +Diameter messages. +A module is generated from a <seealso +marker="diameter_dict">specification file</seealso> using the <seealso +marker="diameterc">diameterc</seealso> utility. +Dictionaries for the RFC 3588 Diameter Common Messages, Base +Accounting and Relay applications are provided by the diameter +application.</p> </chapter> diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml index be1bb2c56e..37cc871e75 100644 --- a/lib/diameter/doc/src/diameter_transport.xml +++ b/lib/diameter/doc/src/diameter_transport.xml @@ -33,7 +33,7 @@ under the License. </header> <module>diameter_transport</module> -<modulesummary>Diameter transport behaviour.</modulesummary> +<modulesummary>Diameter transport interface.</modulesummary> <description> diff --git a/lib/diameter/doc/src/diameter_using.xml b/lib/diameter/doc/src/diameter_using.xml index 737a0a3941..809b76bdf3 100644 --- a/lib/diameter/doc/src/diameter_using.xml +++ b/lib/diameter/doc/src/diameter_using.xml @@ -35,6 +35,9 @@ under the License. </header> +<p> +To be written.</p> + <!-- ===================================================================== --> </chapter> diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml index 8fdb88749e..eafddd7d1e 100644 --- a/lib/diameter/doc/src/notes.xml +++ b/lib/diameter/doc/src/notes.xml @@ -40,8 +40,65 @@ first.</p> <title>diameter 0.9</title> <p> -First OTP release.</p> +Initial release of the diameter application.</p> +<p> +Known issues or limitations:</p> + +<list> + +<item> +<p> +Some agent-related functionality is not entirely complete. +In particular, support for proxy agents, that advertise specific +Diameter applications but otherwise relay messages in much the same +way as relay agents (for which a <seealso +marker="diameter_app#handle_request">handle_request/3</seealso> +callback can return a <c>relay</c> tuple), will be completed in an +upcoming release. +There may also be more explicit support for redirect agents, although +redirect behaviour can be implemented with the current +functionality.</p> + +</item> + +<item> +<p> +There is some asymmetry in the treatment of messages sent as +<c>diameter_header/avp</c> records and those sent in the "normal" +fashion, and not all of this is documented. +This is related to the previous point since this form of sending a +message was introduced specifically to handle relay agent behaviour +using the same callback interface as for client/server behaviour.</p> +</item> + +<item> +<p> +The User's Guide is currently quite thin. +The introductory chapter followed by the examples (in the application +<c>examples</c> subdirectory) may be sufficient +for those having some familiarity with the Diameter protocol but the +intention is to provide more introductory text. +The reference documentation is quite complete, although some points +could likely be expanded upon.</p> +</item> + +<item> +<p> +The function <seealso +marker="diameter#service_info">diameter:service_info/2</seealso> +can be used to retrieve information about a started service +(statistics, information about connected peers, etc) but +this is not yet documented and both the input and output may change +in the next release.</p> +</item> + + +</list> + +<p> +See <seealso marker="diameter_soc">Standards Compliance</seealso> for +standards-related issues.</p> </section> </chapter> diff --git a/lib/diameter/examples/server_cb.erl b/lib/diameter/examples/server_cb.erl index b8705aedfc..43b8e24b5c 100644 --- a/lib/diameter/examples/server_cb.erl +++ b/lib/diameter/examples/server_cb.erl @@ -74,11 +74,8 @@ handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps}) {reply, answer(RT, Id, OH, OR)}; %% ... or one that wasn't. 3xxx errors are answered by diameter itself -%% but these are non-3xxx errors for which we must contruct a reply. -%% Returning a packet with the non-[] errors field will cause -%% diameter to add the appropriate result code and Failed-AVPs avps. -%% We just have to return the relevant answer record with any required -%% avps. +%% but these are 5xxx errors for which we must contruct a reply. +%% diameter will set Result-Code and Failed-AVP's. handle_request(#diameter_packet{msg = Req} = Pkt, _SvcName, {_, Caps}) when is_record(Req, diameter_base_RAR) -> #diameter_caps{origin_host = {OH,_}, @@ -91,7 +88,7 @@ handle_request(#diameter_packet{msg = Req} = Pkt, _SvcName, {_, Caps}) 'Origin-Realm' = OR, 'Session-Id' = Id}, - {reply, Pkt#diameter_packet{msg = Ans}}; + {reply, Ans}; %% Should really reply to other base messages that we don't support %% but simply discard them instead. diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl index 8bd1ad1e51..0fa7fd406f 100644 --- a/lib/diameter/include/diameter.hrl +++ b/lib/diameter/include/diameter.hrl @@ -110,7 +110,7 @@ %% The diameter service and diameter_apps records are only passed %% through the transport interface when starting a transport process, %% although typically a transport implementation will (and probably -%% should) only be interested diameter_service.host_ip_address. +%% should) only be interested host_ip_address. -record(diameter_service, {pid, diff --git a/lib/diameter/src/app/.gitignore b/lib/diameter/src/app/.gitignore index 1310a0da6b..d388e61877 100644 --- a/lib/diameter/src/app/.gitignore +++ b/lib/diameter/src/app/.gitignore @@ -2,4 +2,5 @@ /diameter_gen_*.erl /diameter_gen_*.hrl /depend.mk +/diameter.mk diff --git a/lib/diameter/src/app/Makefile b/lib/diameter/src/app/Makefile index 8985ca4911..6de220d282 100644 --- a/lib/diameter/src/app/Makefile +++ b/lib/diameter/src/app/Makefile @@ -187,7 +187,7 @@ depend: depend.mk # has already been made since it's currently not smart enough to not # force a rebuild of those beams dependent on generated hrls, and this # is a no-no at make release. -depend.mk: depend.sed $(MODULES:%=%.erl) modules.mk Makefile +depend.mk: depend.sed $(MODULES:%=%.erl) Makefile (for f in $(MODULES); do \ sed -f $< $$f.erl | sed "s@/@/$$f@"; \ done) \ diff --git a/lib/diameter/src/app/diameter.erl b/lib/diameter/src/app/diameter.erl index 5f2ab82475..2f721421d8 100644 --- a/lib/diameter/src/app/diameter.erl +++ b/lib/diameter/src/app/diameter.erl @@ -35,7 +35,6 @@ %% Information. -export([services/0, - service_info/1, service_info/2]). %% Start/stop the application. In a "real" application this should @@ -44,12 +43,6 @@ -export([start/0, stop/0]). -%% Backwards compatibility. --export([add_connector/2, - add_listener/2, - remove_connector/2, - remove_listener/2]). - -include("diameter_internal.hrl"). -include("diameter_types.hrl"). @@ -109,7 +102,7 @@ services() -> [Name || {Name, _} <- diameter_service:services()]. %%% -------------------------------------------------------------------------- -%%% service_info/[12] +%%% service_info/2 %%% -------------------------------------------------------------------------- -spec service_info(service_name(), atom() | [atom()]) @@ -118,9 +111,6 @@ services() -> service_info(SvcName, Option) -> diameter_service:info(SvcName, Option). -service_info(SvcName) -> - service_info(SvcName, all). - %%% -------------------------------------------------------------------------- %%% add_transport/3 %%% -------------------------------------------------------------------------- @@ -133,12 +123,6 @@ add_transport(SvcName, {T, Opts} = Cfg) when is_list(Opts), (T == connect orelse T == listen) -> diameter_config:add_transport(SvcName, Cfg). -add_listener(SvcName, Opts) -> - add_transport(SvcName, {listen, Opts}). - -add_connector(SvcName, Opts) -> - add_transport(SvcName, {connect, Opts}). - %%% -------------------------------------------------------------------------- %%% remove_transport/2 %%% -------------------------------------------------------------------------- @@ -149,12 +133,6 @@ add_connector(SvcName, Opts) -> remove_transport(SvcName, Pred) -> diameter_config:remove_transport(SvcName, Pred). -remove_listener(SvcName, Pred) -> - remove_transport(SvcName, {listen, Pred}). - -remove_connector(SvcName, Pred) -> - remove_transport(SvcName, {connect, Pred}). - %%% -------------------------------------------------------------------------- %%% # subscribe(SvcName) %%% diff --git a/lib/diameter/src/app/diameter_service.erl b/lib/diameter/src/app/diameter_service.erl index 82a8d7a994..63b0649dc4 100644 --- a/lib/diameter/src/app/diameter_service.erl +++ b/lib/diameter/src/app/diameter_service.erl @@ -1278,18 +1278,15 @@ send_request({TPid, Caps, App}, Msg, Opts, Caller, SvcName) -> %% make_packet/1 %% %% Turn an outgoing request as passed to call/4 into a diameter_packet -%% record in preparation for a prepare_request callback. There are two -%% cases: a diameter_packet as argument when we're calling call/4 -%% ourselves in order to relay a request or a bare message in case the -%% call came by way of diameter:call/4. +%% record in preparation for a prepare_request callback. make_packet(Bin) when is_binary(Bin) -> #diameter_packet{header = diameter_codec:decode_header(Bin), bin = Bin}; -make_packet(#diameter_packet{msg = [#diameter_header{} | _]} = Pkt) -> - Pkt; +make_packet(#diameter_packet{msg = [#diameter_header{} = Hdr | Avps]} = Pkt) -> + Pkt#diameter_packet{msg = [make_header(Hdr) | Avps]}; make_packet(#diameter_packet{header = Hdr} = Pkt) -> Pkt#diameter_packet{header = make_header(Hdr)}; @@ -1955,6 +1952,7 @@ is_loop(Code, Vid, OH, Avps) -> %% %% Send a locally originating reply. +%% No errors or a diameter_header/avp list. reply(Msg, Dict, TPid, #diameter_packet{errors = Es, transport_data = TD} = ReqPkt) @@ -1964,11 +1962,7 @@ reply(Msg, Dict, TPid, #diameter_packet{errors = Es, incr(send, Pkt, Dict, TPid), %% count result codes in sent answers send(TPid, Pkt#diameter_packet{transport_data = TD}); -%% Simplify the handling of error cases by accepting a list consisting -%% of an answer record followed by failed AVPs to be packed into a -%% Failed-AVP field, either directly or into an AVP field. Only if -%% the message is a tuple-list or record however, not a list -%% with a list of #diameter_header{} and #diameter_avp{}. +%% Or not: set Result-Code and Failed-AVP AVP's. reply(Msg, Dict, TPid, #diameter_packet{errors = [H|_] = Es} = Pkt) -> reply(rc(Msg, rc(H), [A || {_,A} <- Es], Dict), Dict, diff --git a/lib/diameter/src/compiler/Makefile b/lib/diameter/src/compiler/Makefile index 8512eb515a..3ab76064ac 100644 --- a/lib/diameter/src/compiler/Makefile +++ b/lib/diameter/src/compiler/Makefile @@ -130,7 +130,7 @@ force: depend: depend.mk # Generate dependencies makefile. -depend.mk: ../app/depend.sed $(ERL_FILES) modules.mk Makefile +depend.mk: ../app/depend.sed $(ERL_FILES) Makefile for f in $(MODULES); do \ sed -f $< $$f.erl | sed "s@/@/$$f@"; \ done \ diff --git a/lib/diameter/src/transport/Makefile b/lib/diameter/src/transport/Makefile index 5dc1772796..4b53100fd2 100644 --- a/lib/diameter/src/transport/Makefile +++ b/lib/diameter/src/transport/Makefile @@ -130,7 +130,7 @@ force: depend: depend.mk # Generate dependencies makefile. -depend.mk: ../app/depend.sed $(ERL_FILES) modules.mk Makefile +depend.mk: ../app/depend.sed $(ERL_FILES) Makefile for f in $(MODULES); do \ sed -f $< $$f.erl | sed "s@/@/$$f@"; \ done \ diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 2f73394c4e..fdab2eb02b 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -360,41 +360,50 @@ make_del_dir(Config) when is_list(Config) -> ?line {error, eexist} = ?FILE_MODULE:make_dir(NewDir), ?line ok = ?FILE_MODULE:del_dir(NewDir), ?line {error, enoent} = ?FILE_MODULE:del_dir(NewDir), - - %% Check that we get an error when trying to create... - %% a deep directory - ?line NewDir2 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_mk-dir/foo"), - ?line {error, enoent} = ?FILE_MODULE:make_dir(NewDir2), - %% a nameless directory - ?line {error, enoent} = ?FILE_MODULE:make_dir(""), - %% a directory with illegal name - ?line {error, badarg} = ?FILE_MODULE:make_dir({1,2,3}), - - %% a directory with illegal name, even if it's a (bad) list - ?line {error, badarg} = ?FILE_MODULE:make_dir([1,2,3,{}]), - - %% Maybe this isn't an error, exactly, but worth mentioning anyway: - %% ok = ?FILE_MODULE:make_dir([$f,$o,$o,0,$b,$a,$r])), - %% The above line works, and created a directory "./foo" - %% More elegant would maybe have been to fail, or to really create - %% a directory, but with a name that incorporates the "bar" part of - %% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same - %% dir. But this would slow it down. - - %% Try deleting some bad directories - %% Deleting the parent directory to the current, sounds dangerous, huh? - %% Don't worry ;-) the parent directory should never be empty, right? - case ?FILE_MODULE:del_dir('..') of - {error, eexist} -> ok; - {error, einval} -> ok %FreeBSD + % Make sure we are not in a directory directly under test_server + % as that would result in eacess errors when trying to delere '..', + % because there are processes having that directory as current. + ?line ok = ?FILE_MODULE:make_dir(NewDir), + ?line {ok,CurrentDir} = file:get_cwd(), + ?line ok = ?FILE_MODULE:set_cwd(NewDir), + try + %% Check that we get an error when trying to create... + %% a deep directory + ?line NewDir2 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_mk-dir-noexist/foo"), + ?line {error, enoent} = ?FILE_MODULE:make_dir(NewDir2), + %% a nameless directory + ?line {error, enoent} = ?FILE_MODULE:make_dir(""), + %% a directory with illegal name + ?line {error, badarg} = ?FILE_MODULE:make_dir({1,2,3}), + + %% a directory with illegal name, even if it's a (bad) list + ?line {error, badarg} = ?FILE_MODULE:make_dir([1,2,3,{}]), + + %% Maybe this isn't an error, exactly, but worth mentioning anyway: + %% ok = ?FILE_MODULE:make_dir([$f,$o,$o,0,$b,$a,$r])), + %% The above line works, and created a directory "./foo" + %% More elegant would maybe have been to fail, or to really create + %% a directory, but with a name that incorporates the "bar" part of + %% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same + %% dir. But this would slow it down. + + %% Try deleting some bad directories + %% Deleting the parent directory to the current, sounds dangerous, huh? + %% Don't worry ;-) the parent directory should never be empty, right? + ?line case ?FILE_MODULE:del_dir('..') of + {error, eexist} -> ok; + {error, einval} -> ok %FreeBSD + end, + ?line {error, enoent} = ?FILE_MODULE:del_dir(""), + ?line {error, badarg} = ?FILE_MODULE:del_dir([3,2,1,{}]), + + ?line [] = flush(), + ?line test_server:timetrap_cancel(Dog) + after + ?FILE_MODULE:set_cwd(CurrentDir) end, - ?line {error, enoent} = ?FILE_MODULE:del_dir(""), - ?line {error, badarg} = ?FILE_MODULE:del_dir([3,2,1,{}]), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), ok. cur_dir_0(suite) -> []; diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index a04ea3cdcd..00eda6292f 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -250,39 +250,49 @@ make_del_dir(Config, Handle, Suffix) -> ?line ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]), ?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [NewDir]), - %% Check that we get an error when trying to create... - %% a deep directory - ?line NewDir2 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_mk-dir/foo"), - ?line {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [NewDir2]), - %% a nameless directory - ?line {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [""]), - %% a directory with illegal name - ?line {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, ['mk-dir']), - - %% a directory with illegal name, even if it's a (bad) list - ?line {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, [[1,2,3,{}]]), - - %% Maybe this isn't an error, exactly, but worth mentioning anyway: - %% ok = ?PRIM_FILE:make_dir([$f,$o,$o,0,$b,$a,$r])), - %% The above line works, and created a directory "./foo" - %% More elegant would maybe have been to fail, or to really create - %% a directory, but with a name that incorporates the "bar" part of - %% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same - %% dir. But this would slow it down. - - %% Try deleting some bad directories - %% Deleting the parent directory to the current, sounds dangerous, huh? - %% Don't worry ;-) the parent directory should never be empty, right? - case ?PRIM_FILE_call(del_dir, Handle, [".."]) of - {error, eexist} -> ok; - {error, einval} -> ok %FreeBSD + % Make sure we are not in a directory directly under test_server + % as that would result in eacess errors when trying to delere '..', + % because there are processes having that directory as current. + ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), + ?line {ok, CurrentDir} = ?PRIM_FILE_call(get_cwd, Handle, []), + ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]), + try + %% Check that we get an error when trying to create... + %% a deep directory + ?line NewDir2 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_mk-dir-noexist/foo"), + ?line {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [NewDir2]), + %% a nameless directory + ?line {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [""]), + %% a directory with illegal name + ?line {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, ['mk-dir']), + + %% a directory with illegal name, even if it's a (bad) list + ?line {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, [[1,2,3,{}]]), + + %% Maybe this isn't an error, exactly, but worth mentioning anyway: + %% ok = ?PRIM_FILE:make_dir([$f,$o,$o,0,$b,$a,$r])), + %% The above line works, and created a directory "./foo" + %% More elegant would maybe have been to fail, or to really create + %% a directory, but with a name that incorporates the "bar" part of + %% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same + %% dir. But this would slow it down. + + %% Try deleting some bad directories + %% Deleting the parent directory to the current, sounds dangerous, huh? + %% Don't worry ;-) the parent directory should never be empty, right? + ?line case ?PRIM_FILE_call(del_dir, Handle, [".."]) of + {error, eexist} -> ok; + {error, einval} -> ok %FreeBSD + end, + ?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [""]), + ?line {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]]), + + ?line test_server:timetrap_cancel(Dog) + after + ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [CurrentDir]) end, - ?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [""]), - ?line {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]]), - - ?line test_server:timetrap_cancel(Dog), ok. cur_dir_0a(suite) -> []; diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 4f546a37ed..71f3941577 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -29,6 +29,20 @@ <file>notes.xml</file> </header> +<section><title>Ssh 2.0.7</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + An unexpected message would crash the ssh_connection_handler and close + the connection. Now an error message is generated instead.</p> + <p> + Own Id: OTP-9273</p> + </item> + </list> + </section> +</section> + <section><title>Ssh 2.0.6</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 37f24e2463..974145836c 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -19,10 +19,12 @@ {"%VSN%", [ + {"2.0.6", [{load_module, ssh_userreg, soft_purge, soft_purge, []}]}, {"2.0.5", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, {load_module, ssh_connection_handler, soft_purge, soft_purge, [ssh_userreg]}]} ], [ + {"2.0.6", [{load_module, ssh_userreg, soft_purge, soft_purge, []}]}, {"2.0.5", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, {load_module, ssh_connection_handler, soft_purge, soft_purge, [ssh_userreg]}]} ] diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 3193be2510..00b30e5434 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -569,7 +569,19 @@ handle_info({CloseTag, _Socket}, _StateName, #state{transport_close_tag = CloseTag, %%manager = Pid, ssh_params = #ssh{role = _Role, opts = _Opts}} = State) -> %%ok = ssh_connection_manager:delivered(Pid), - {stop, normal, State}. + {stop, normal, State}; +handle_info(UnexpectedMessage, StateName, #state{ssh_params = SshParams} = State) -> + Msg = lists:flatten(io_lib:format( + "Unexpected message '~p' received in state '~p'\n" + "Role: ~p\n" + "Peer: ~p\n" + "Local Address: ~p\n", [UnexpectedMessage, StateName, + SshParams#ssh.role, SshParams#ssh.peer, + proplists:get_value(address, SshParams#ssh.opts)])), + error_logger:info_report(Msg), + {next_state, StateName, State}. + + %%-------------------------------------------------------------------- %% Function: terminate(Reason, StateName, State) -> void() %% Description:This function is called by a gen_fsm when it is about diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile new file mode 100644 index 0000000000..5a2a6de24a --- /dev/null +++ b/lib/ssh/test/Makefile @@ -0,0 +1,121 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN=$(GS_VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + ssh_test_lib \ + ssh_SUITE \ + ssh_basic_SUITE \ + ssh_to_openssh_SUITE \ + ssh_sftp_SUITE \ + ssh_sftpd_SUITE \ + ssh_sftpd_erlclient_SUITE + +HRL_FILES_NEEDED_IN_TEST= \ + $(ERL_TOP)/lib/ssh/src/ssh.hrl \ + $(ERL_TOP)/lib/ssh/src/ssh_xfer.hrl + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +DATA_DIRS = $(MODULES:%=%_data) + +INCLUDES = -I$(ERL_TOP)/lib/test_server/include \ + -I$(ERL_TOP)/lib/ssh/src \ + +EMAKEFILE=Emakefile +MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile) + +ifeq ($(MAKE_EMAKE),) +BUILDTARGET = $(TARGET_FILES) +RELTEST_FILES = $(INETS_SPECS) $(SOURCE) +else +BUILDTARGET = emakebuild +RELTEST_FILES = $(EMAKEFILE) $(INETS_SPECS) $(SOURCE) +endif + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/ssh_test + +# ---------------------------------------------------- +# FLAGS +# The path to the test_server ebin dir is needed when +# running the target "targets". +# ---------------------------------------------------- +ERL_COMPILE_FLAGS += -pa ../../../internal_tools/test_server/ebin \ + $(INCLUDES) + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- +tests debug opt: $(BUILDTARGET) + +targets: $(TARGET_FILES) + +.PHONY: emakebuild + +emakebuild: $(EMAKEFILE) + +$(EMAKEFILE): + $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' | grep -v Warning > $(EMAKEFILE) + $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) | grep -v Warning >> $(EMAKEFILE) + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) + rm -f core +docs: + +info: + @echo "TARGET_FILES = $(TARGET_FILES)" + @echo "DATA_DIRS = $(DATA_DIRS)" + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: opt + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) + $(INSTALL_DATA) ssh.spec ssh.cover $(RELSYSDIR) + $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) $(RELSYSDIR) + chmod -f -R u+w $(RELSYSDIR) + @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + +release_docs_spec: diff --git a/lib/ssh/test/ssh.cover b/lib/ssh/test/ssh.cover new file mode 100644 index 0000000000..a4221fbbbe --- /dev/null +++ b/lib/ssh/test/ssh.cover @@ -0,0 +1,2 @@ +{incl_app,ssh,details}. + diff --git a/lib/ssh/test/ssh.spec b/lib/ssh/test/ssh.spec new file mode 100644 index 0000000000..8de0fe44e4 --- /dev/null +++ b/lib/ssh/test/ssh.spec @@ -0,0 +1,7 @@ +{suites,"../ssh_test",all}. +{skip_cases,"../ssh_test",ssh_ssh_SUITE, + [ssh], + "Current implementation is timingdependent and\nhence will succeed/fail on a whim"}. +{skip_cases,"../ssh_test",ssh_ssh_SUITE, + [ssh_compressed], + "Current implementation is timingdependent hence will succeed/fail on a whim"}. diff --git a/lib/ssh/test/ssh.spec.vxworks b/lib/ssh/test/ssh.spec.vxworks new file mode 100644 index 0000000000..81f665283c --- /dev/null +++ b/lib/ssh/test/ssh.spec.vxworks @@ -0,0 +1,3 @@ +{topcase, {dir, "../ssh_test"}}. +{require_nodenames, 1}. +%{skip, {M, F, "Not yet implemented"}}. diff --git a/lib/ssh/test/ssh_SUITE.erl b/lib/ssh/test/ssh_SUITE.erl new file mode 100644 index 0000000000..953c9080f9 --- /dev/null +++ b/lib/ssh/test/ssh_SUITE.erl @@ -0,0 +1,72 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%%---------------------------------------------------------------- +%%% Purpose:ssh application test suite. +%%%----------------------------------------------------------------- +-module(ssh_SUITE). +-include_lib("common_test/include/ct.hrl"). +-include("test_server_line.hrl"). + +% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). +-define(application, ssh). + +% Test server specific exports +-export([all/0,groups/0,init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +% Test cases must be exported. +-export([app_test/1]). +-define(cases, [app_test]). + +%% +%% all/1 +%% +all() -> + [app_test]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(_Case, Config) -> + Dog=test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. +% +% Test cases starts here. +% +app_test(suite) -> + []; +app_test(doc) -> + ["Application consistency test."]; +app_test(Config) when is_list(Config) -> + ?t:app_test(?application), + ok. diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl new file mode 100644 index 0000000000..5ea0d98980 --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -0,0 +1,389 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssh_basic_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include("test_server_line.hrl"). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-define(NEWLINE, <<"\r\n">>). + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initialization before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + case catch crypto:start() of + ok -> + Dir = ?config(priv_dir, Config), + {ok, _} = ssh_test_lib:get_id_keys(Dir), + ssh_test_lib:make_dsa_files(Config), + Config; + _Else -> + {skip, "Crypto could not be started!"} + end. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(Config) -> + Dir = ?config(priv_dir, Config), + crypto:stop(), + ssh_test_lib:remove_id_keys(Dir), + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initialization before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initialization before each test case +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + ssh_test_lib:known_hosts(backup), + ssh:start(), + Config. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ssh:stop(), + ssh_test_lib:known_hosts(restore), + ok. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all() -> + [exec, exec_compressed, shell, daemon_already_started, + server_password_option, server_userpassword_option, + known_hosts]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% Test cases starts here. +%%-------------------------------------------------------------------- +sign_and_verify_rsa(doc) -> + ["Test api function ssh:sign_data and ssh:verify_data"]; + +sign_and_verify_rsa(suite) -> + []; +sign_and_verify_rsa(Config) when is_list(Config) -> + Data = ssh:sign_data(<<"correct data">>, "ssh-rsa"), + ok = ssh:verify_data(<<"correct data">>, Data, "ssh-rsa"), + {error,invalid_signature} = ssh:verify_data(<<"incorrect data">>, Data,"ssh-rsa"). + + +exec(doc) -> + ["Test api function ssh_connection:exec"]; + +exec(suite) -> + []; + +exec(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = ?config(data_dir, Config), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId0, + "1+1.", infinity), + Data0 = {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"2\n">>}}, + case ssh_test_lib:receive_exec_result(Data0) of + expected -> + ok; + Other0 -> + test_server:fail(Other0) + end, + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0), + + %% Test that it is possible to start a new channel and + %% run an other exec on the same connection. + {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId1, + "2+2.", infinity), + Data1 = {ssh_cm, ConnectionRef, {data, ChannelId1, 0, <<"4\n">>}}, + case ssh_test_lib:receive_exec_result(Data1) of + expected -> + ok; + Other1 -> + test_server:fail(Other1) + end, + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +exec_compressed(doc) -> + ["Test that compression option works"]; + +exec_compressed(suite) -> + []; + +exec_compressed(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = ?config(data_dir, Config), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {compression, zlib}, + {failfun, fun ssh_test_lib:failfun/2}]), + + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId, + "1+1.", infinity), + Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"2\n">>}}, + case ssh_test_lib:receive_exec_result(Data) of + expected -> + ok; + Other -> + test_server:fail(Other) + end, + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- + +shell(doc) -> + ["Test that ssh:shell/2 works"]; + +shell(suite) -> + []; + +shell(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = ?config(data_dir, Config), + {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + test_server:sleep(500), + + IO = ssh_test_lib:start_io_server(), + Shell = ssh_test_lib:start_shell(Port, IO), + receive + ErlShellStart -> + test_server:format("Erlang shell start: ~p~n", [ErlShellStart]) + end, + receive + ErlPrompt0 -> + test_server:format("Erlang prompt: ~p~n", [ErlPrompt0]) + end, + IO ! {input, self(), "1+1.\r\n"}, + receive + Echo0 -> + test_server:format("Echo: ~p ~n", [Echo0]) + end, + receive + ?NEWLINE -> + ok + end, + receive + Result0 = <<"2">> -> + test_server:format("Result: ~p~n", [Result0]) + end, + receive + ?NEWLINE -> + ok + end, + receive + ErlPrompt1 -> + test_server:format("Erlang prompt: ~p~n", [ErlPrompt1]) + end, + exit(Shell, kill), + %% Does not seem to work in the testserver! + %% IO ! {input, self(), "q().\r\n"}, + %% receive + %% ?NEWLINE -> + %% ok + %% end, + %% receive + %% Echo1 -> + %% test_server:format("Echo: ~p ~n", [Echo1]) + %% end, + %% receive + %% ?NEWLINE -> + %% ok + %% end, + %% receive + %% Result1 -> + %% test_server:format("Result: ~p~n", [Result1]) + %% end, + receive + {'EXIT', Shell, killed} -> + ok + end. + +%%-------------------------------------------------------------------- +daemon_already_started(doc) -> + ["Test that get correct error message if you try to start a daemon", + "on an adress that already runs a daemon see also seq10667" ]; + +daemon_already_started(suite) -> + []; + +daemon_already_started(Config) when is_list(Config) -> + SystemDir = ?config(data_dir, Config), + {Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + {error, eaddrinuse} = ssh_test_lib:daemon(Port, [{system_dir, SystemDir}, + {failfun, + fun ssh_test_lib:failfun/2}]), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +server_password_option(doc) -> + ["validate to server that uses the 'password' option"]; +server_password_option(suite) -> + []; +server_password_option(Config) when is_list(Config) -> + UserDir = ?config(data_dir, Config), % to make sure we don't use + SysDir = ?config(data_dir, Config), % public-key-auth + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {password, "morot"}]), + + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + {error, Reason} = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "vego"}, + {password, "foo"}, + {user_interaction, false}, + {user_dir, UserDir}]), + + test_server:format("Test of wrong password: Error msg: ~p ~n", [Reason]), + + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- + +server_userpassword_option(doc) -> + ["validate to server that uses the 'password' option"]; +server_userpassword_option(suite) -> + []; +server_userpassword_option(Config) when is_list(Config) -> + UserDir = ?config(data_dir, Config), % to make sure we don't use + SysDir = ?config(data_dir, Config), % public-key-auth + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_passwords, [{"vego", "morot"}]}]), + + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "vego"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + ssh:close(ConnectionRef), + + {error, Reason0} = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + + test_server:format("Test of user foo that does not exist. " + "Error msg: ~p ~n", [Reason0]), + + {error, Reason1} = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "vego"}, + {password, "foo"}, + {user_interaction, false}, + {user_dir, UserDir}]), + test_server:format("Test of wrong Password. " + "Error msg: ~p ~n", [Reason1]), + + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +known_hosts(doc) -> + ["check that known_hosts is updated correctly"]; +known_hosts(suite) -> + []; +known_hosts(Config) when is_list(Config) -> + SystemDir = ?config(data_dir, Config), + UserDir = ?config(priv_dir, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + + KnownHosts = filename:join(UserDir, "known_hosts"), + file:delete(KnownHosts), + {error, enoent} = file:read_file(KnownHosts), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{user_dir, UserDir}, + {user_interaction, false}, + silently_accept_hosts]), + {ok, _Channel} = ssh_connection:session_channel(ConnectionRef, infinity), + ok = ssh:close(ConnectionRef), + {ok, Binary} = file:read_file(KnownHosts), + Lines = string:tokens(binary_to_list(Binary), "\n"), + [Line] = Lines, + [HostAndIp, Alg, _KeyData] = string:tokens(Line, " "), + [Host, _Ip] = string:tokens(HostAndIp, ","), + "ssh-" ++ _ = Alg, + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl new file mode 100644 index 0000000000..c96b6de3ea --- /dev/null +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -0,0 +1,543 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(ssh_sftp_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include("test_server_line.hrl"). + +-include_lib("kernel/include/file.hrl"). + +% Default timetrap timeout +-define(default_timeout, ?t:minutes(1)). + +-define(SFPD_PORT, 9999). +-define(USER, "Alladin"). +-define(PASSWD, "Sesame"). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + case {catch crypto:start(),catch ssh:start()} of + {ok,ok} -> + Dir = ?config(priv_dir, Config), + {ok, _} = ssh_test_lib:get_id_keys(Dir), + ssh_test_lib:make_dsa_files(Config), + Config; + {ok,_} -> + {skip,"Could not start ssh!"}; + {_,ok} -> + {skip,"Could not start crypto!"}; + {_,_} -> + {skip,"Could not start crypto and ssh!"} + end. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(Config) -> + crypto:stop(), + Dir = ?config(priv_dir, Config), + ssh_test_lib:remove_id_keys(Dir), + Config. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initiation before each test case +%%-------------------------------------------------------------------- +init_per_testcase(_Case, Config) -> + prep(Config), + TmpConfig0 = lists:keydelete(watchdog, 1, Config), + TmpConfig = lists:keydelete(sftp, 1, TmpConfig0), + Dog = test_server:timetrap(?default_timeout), + Dir = ?config(priv_dir, Config), + SysDir = ?config(data_dir, Config), + Host = ssh_test_lib:hostname(), + + Sftp = case (catch ssh_sftp:start_channel(Host, + [{user_dir, Dir}, + {user_interaction, false}, + {silently_accept_hosts, true}])) of + {ok, ChannelPid, Connection} -> + {ChannelPid, Connection}; + _Error -> + {_Sftpd, _Host, _Port} = + ssh_test_lib:daemon(Host, ?SFPD_PORT, + [{system_dir, SysDir}, + {user_passwords, + [{?USER, ?PASSWD}]}, + {failfun, + fun ssh_test_lib:failfun/2}]), + Result = (catch ssh_sftp:start_channel(Host, ?SFPD_PORT, + [{user, ?USER}, + {password, ?PASSWD}, + {user_interaction, false}, + {silently_accept_hosts, true}])), + {ok, ChannelPid, Connection} = Result, + {ChannelPid, Connection} + end, + + [{sftp, Sftp}, {watchdog, Dog} | TmpConfig]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_Case, Config) -> + {Sftp, Connection} = ?config(sftp, Config), + ssh_sftp:stop_channel(Sftp), + ssh:close(Connection), + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all() -> + [open_close_file, open_close_dir, read_file, read_dir, + write_file, rename_file, mk_rm_dir, remove_file, links, + retrieve_attributes, set_attributes, async_read, + async_write, position, pos_read, pos_write]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% Test cases starts here. +%%-------------------------------------------------------------------- +open_close_file(doc) -> + ["Test API functions open/3 and close/2"]; +open_close_file(suite) -> + []; +open_close_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + + {Sftp, _} = ?config(sftp, Config), + + ok = open_close_file(Sftp, FileName, [read]), + ok = open_close_file(Sftp, FileName, [write]), + ok = open_close_file(Sftp, FileName, [write, creat]), + ok = open_close_file(Sftp, FileName, [write, trunc]), + ok = open_close_file(Sftp, FileName, [append]), + ok = open_close_file(Sftp, FileName, [read, binary]), + + ok. + +open_close_file(Server, File, Mode) -> + {ok, Handle} = ssh_sftp:open(Server, File, Mode), + ok = ssh_sftp:close(Server, Handle), + ok. + + +%%-------------------------------------------------------------------- +open_close_dir(doc) -> + ["Test API functions opendir/2 and close/2"]; +open_close_dir(suite) -> + []; +open_close_dir(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + {Sftp, _} = ?config(sftp, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + + {ok, Handle} = ssh_sftp:opendir(Sftp, PrivDir), + ok = ssh_sftp:close(Sftp, Handle), + {error, _} = ssh_sftp:opendir(Sftp, FileName), + + ok. +%%-------------------------------------------------------------------- +read_file(doc) -> + ["Test API funtion read_file/2"]; +read_file(suite) -> + []; +read_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + + {Sftp, _} = ?config(sftp, Config), + + {ok, Data} = ssh_sftp:read_file(Sftp, FileName), + + {ok, Data} = file:read_file(FileName), + + ok. +%%-------------------------------------------------------------------- +read_dir(doc) -> + ["Test API function list_dir/2"]; +read_dir(suite) -> + []; +read_dir(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + {Sftp, _} = ?config(sftp, Config), + {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), + test_server:format("sftp list dir: ~p~n", [Files]), + ok. + +%%-------------------------------------------------------------------- +write_file(doc) -> + ["Test API function write_file/2"]; +write_file(suite) -> + []; +write_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + + {Sftp, _} = ?config(sftp, Config), + + Data = list_to_binary("Hej hopp!"), + + ssh_sftp:write_file(Sftp, FileName, [Data]), + + {ok, Data} = file:read_file(FileName), + + ok. + +%%-------------------------------------------------------------------- +remove_file(doc) -> + ["Test API function delete/2"]; +remove_file(suite) -> + []; +remove_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + + {Sftp, _} = ?config(sftp, Config), + + {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), + + true = lists:member(filename:basename(FileName), Files), + + ok = ssh_sftp:delete(Sftp, FileName), + + {ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir), + + false = lists:member(filename:basename(FileName), NewFiles), + + {error, _} = ssh_sftp:delete(Sftp, FileName), + + ok. + +%%-------------------------------------------------------------------- +rename_file(doc) -> + ["Test API function rename_file/2"]; +rename_file(suite) -> + []; +rename_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + NewFileName = filename:join(PrivDir, "test.txt"), + + {Sftp, _} = ?config(sftp, Config), + + {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), + + test_server:format("FileName: ~p, Files: ~p~n", [FileName, Files]), + + true = lists:member(filename:basename(FileName), Files), + false = lists:member(filename:basename(NewFileName), Files), + + ok = ssh_sftp:rename(Sftp, FileName, NewFileName), + + {ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir), + + test_server:format("FileName: ~p, Files: ~p~n", [FileName, NewFiles]), + + false = lists:member(filename:basename(FileName), NewFiles), + true = lists:member(filename:basename(NewFileName), NewFiles), + + ok. + +%%-------------------------------------------------------------------- +mk_rm_dir(doc) -> + ["Test API functions make_dir/2, del_dir/2"]; +mk_rm_dir(suite) -> + []; +mk_rm_dir(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + {Sftp, _} = ?config(sftp, Config), + DirName = filename:join(PrivDir, "test"), + + ok = ssh_sftp:make_dir(Sftp, DirName), + ok = ssh_sftp:del_dir(Sftp, DirName), + + NewDirName = filename:join(PrivDir, "foo/bar"), + + {error, _} = ssh_sftp:make_dir(Sftp, NewDirName), + {error, _} = ssh_sftp:del_dir(Sftp, PrivDir), + + ok. + +%%-------------------------------------------------------------------- +links(doc) -> + ["Tests API function make_symlink/3"]; +links(suite) -> + []; +links(Config) when is_list(Config) -> + case test_server:os_type() of + {win32, _} -> + {skip, "Links are not fully supported by windows"}; + _ -> + {Sftp, _} = ?config(sftp, Config), + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + LinkFileName = filename:join(PrivDir, "link_test.txt"), + + ok = ssh_sftp:make_symlink(Sftp, FileName, LinkFileName), + {ok, FileName} = ssh_sftp:read_link(Sftp, LinkFileName), + ok + end. + +%%-------------------------------------------------------------------- +retrieve_attributes(doc) -> + ["Test API function read_file_info/3"]; +retrieve_attributes(suite) -> + []; +retrieve_attributes(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + {Sftp, _} = ?config(sftp, Config), + + {ok, FileInfo} = ssh_sftp:read_file_info(Sftp, FileName), + + {ok, NewFileInfo} = file:read_file_info(FileName), + + %% TODO comparison. There are some differences now is that ok? + test_server:format("SFTP: ~p FILE: ~p~n", [FileInfo, NewFileInfo]), + ok. + +%%-------------------------------------------------------------------- +set_attributes(doc) -> + ["Test API function write_file_info/3"]; +set_attributes(suite) -> + []; +set_attributes(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + {Sftp, _} = ?config(sftp, Config), + + {ok,Fd} = file:open(FileName, write), + io:put_chars(Fd,"foo"), + + ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#400}), + {error, eacces} = file:write_file(FileName, "hello again"), + ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#600}), + ok = file:write_file(FileName, "hello again"), + + ok. + +%%-------------------------------------------------------------------- + +async_read(doc) -> + ["Test API aread/3"]; +async_read(suite) -> + []; +async_read(Config) when is_list(Config) -> + {Sftp, _} = ?config(sftp, Config), + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "sftp.txt"), + {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]), + {async, Ref} = ssh_sftp:aread(Sftp, Handle, 20), + + receive + {async_reply, Ref, {ok, Data}} -> + test_server:format("Data: ~p~n", [Data]), + ok; + Msg -> + test_server:fail(Msg) + end, + ok. +%%-------------------------------------------------------------------- +async_write(doc) -> + ["Test API awrite/3"]; +async_write(suite) -> + []; +async_write(Config) when is_list(Config) -> + {Sftp, _} = ?config(sftp, Config), + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + {ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]), + Data = list_to_binary("foobar"), + {async, Ref} = ssh_sftp:awrite(Sftp, Handle, Data), + + receive + {async_reply, Ref, ok} -> + {ok, Data} = file:read_file(FileName); + Msg -> + test_server:fail(Msg) + end, + ok. + +%%-------------------------------------------------------------------- + +position(doc) -> + ["Test API functions position/3"]; +position(suite) -> + []; +position(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + {Sftp, _} = ?config(sftp, Config), + + Data = list_to_binary("1234567890"), + ssh_sftp:write_file(Sftp, FileName, [Data]), + + {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]), + + {ok, 3} = ssh_sftp:position(Sftp, Handle, {bof, 3}), + {ok, "4"} = ssh_sftp:read(Sftp, Handle, 1), + + {ok, 10} = ssh_sftp:position(Sftp, Handle, eof), + eof = ssh_sftp:read(Sftp, Handle, 1), + + {ok, 6} = ssh_sftp:position(Sftp, Handle, {bof, 6}), + {ok, "7"} = ssh_sftp:read(Sftp, Handle, 1), + + {ok, 9} = ssh_sftp:position(Sftp, Handle, {cur, 2}), + {ok, "0"} = ssh_sftp:read(Sftp, Handle, 1), + + {ok, 0} = ssh_sftp:position(Sftp, Handle, bof), + {ok, "1"} = ssh_sftp:read(Sftp, Handle, 1), + + {ok, 1} = ssh_sftp:position(Sftp, Handle, cur), + {ok, "2"} = ssh_sftp:read(Sftp, Handle, 1), + + ok. + +%%-------------------------------------------------------------------- +pos_read(doc) -> + ["Test API functions pread/3 and apread/3"]; +pos_read(suite) -> + []; +pos_read(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + {Sftp, _} = ?config(sftp, Config), + Data = list_to_binary("Hej hopp!"), + ssh_sftp:write_file(Sftp, FileName, [Data]), + + {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]), + + {async, Ref} = ssh_sftp:apread(Sftp, Handle, {bof, 5}, 4), + + NewData = "opp!", + + receive + {async_reply, Ref, {ok, NewData}} -> + ok; + Msg -> + test_server:fail(Msg) + end, + + NewData1 = "hopp", + + {ok, NewData1} = ssh_sftp:pread(Sftp, Handle, {bof, 4}, 4), + + ok. +%%-------------------------------------------------------------------- +pos_write(doc) -> + ["Test API functions pwrite/4 and apwrite/4"]; +pos_write(suite) -> + []; +pos_write(Config) when is_list(Config) -> + + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + {Sftp, _} = ?config(sftp, Config), + + {ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]), + + Data = list_to_binary("Bye,"), + ssh_sftp:write_file(Sftp, FileName, [Data]), + + NewData = list_to_binary(" see you tomorrow"), + {async, Ref} = ssh_sftp:apwrite(Sftp, Handle, {bof, 4}, NewData), + receive + {async_reply, Ref, ok} -> + ok; + Msg -> + test_server:fail(Msg) + end, + + ok = ssh_sftp:pwrite(Sftp, Handle, eof, list_to_binary("!")), + + NewData1 = list_to_binary("Bye, see you tomorrow!"), + {ok, NewData1} = ssh_sftp:read_file(Sftp, FileName), + + ok. + +%% Internal functions +%%-------------------------------------------------------------------- +prep(Config) -> + PrivDir = ?config(priv_dir, Config), + TestFile = filename:join(PrivDir, "sftp.txt"), + TestFile1 = filename:join(PrivDir, "test.txt"), + TestLink = filename:join(PrivDir, "link_test.txt"), + + file:delete(TestFile), + file:delete(TestFile1), + file:delete(TestLink), + + %% Initial config + DataDir = ?config(data_dir, Config), + FileName = filename:join(DataDir, "sftp.txt"), + file:copy(FileName, TestFile), + Mode = 8#00400 bor 8#00200 bor 8#00040, % read & write owner, read group + {ok, FileInfo} = file:read_file_info(TestFile), + ok = file:write_file_info(TestFile, + FileInfo#file_info{mode = Mode}). diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/id_rsa b/lib/ssh/test/ssh_sftp_SUITE_data/id_rsa new file mode 100644 index 0000000000..7e3f885f5d --- /dev/null +++ b/lib/ssh/test/ssh_sftp_SUITE_data/id_rsa @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDLKYTdRnGzphcN+pF8UuI3sYB7rxZUHbOT87K3vh8XOLkDOsS3 +8VREtNS8Wb3uYXsRtyDoUvrLIDnyllOfJSDupWLr4ibckUZd/nhFAaC6WryVmH6k +GlQLLp9KU+vcn2DwYeo14gbwHYDB3pmv4CWAlnO1m/BkX4aLz1zC314OkQIBIwKB +gD/Z2UzboBPjvhpWEHeHw3CW3zzQoJ4X9pw2peH57IOkHOPCA0/A3/hWFvleCH4e +owWRU3w3ViKVGYbBh/7RJ5rllN+ENUmVn536srJTxLKUtvb5jRGj3W6EWgAGHSUB +hm83Kt9Lb5hprL7dPrNGvSseBm/LQSfBQ4vUUyiVRKGPAkEA/rPxWoLdBBP+FZtE +fGzz9izPM6Fe6o8ZGNZIlRBProOhgEvvIqdgzQWObgLVVrw+M/YApPpiYS3PEmWj +b2b+jwJBAMwyYeL6coKTl8swDu8HvLnshgUFJFTtHhOTXsKtXQNI1b24xhUrB3Sb +X8fmoByyRNRpOfvg4Jdqi3Z6KfIcsN8CQQDEfC83McBw3DkJWoVKCugVrYnmACSm +USH9N5cT6AL0VupNB2C0VTwL37cEaJXyc/V4ipLIaWHV8CNl9qKmZWVJAkEAurG4 +lQI8zyfbPW3EgsU+1d+QeZ5NGnJkpC73jWtNudwxIn0M4CdXRgpmMxwAGjyWs5No +Nr75OfsDKn5SPHIAywJAKrtONlOizgDiG3EvAXZlwFtOb+HkQ7lrFwczrQu9m7yi +brSAcnTrLKI6CrR33b/QJLvb9C/HTEZojFABGq8M7A== +-----END RSA PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/id_rsa.pub b/lib/ssh/test/ssh_sftp_SUITE_data/id_rsa.pub new file mode 100644 index 0000000000..77f57de4af --- /dev/null +++ b/lib/ssh/test/ssh_sftp_SUITE_data/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAyymE3UZxs6YXDfqRfFLiN7GAe68WVB2zk/Oyt74fFzi5AzrEt/FURLTUvFm97mF7Ebcg6FL6yyA58pZTnyUg7qVi6+Im3JFGXf54RQGgulq8lZh+pBpUCy6fSlPr3J9g8GHqNeIG8B2Awd6Zr+AlgJZztZvwZF+Gi89cwt9eDpE= jakob@balin diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp.txt b/lib/ssh/test/ssh_sftp_SUITE_data/sftp.txt new file mode 100644 index 0000000000..2a878ae255 --- /dev/null +++ b/lib/ssh/test/ssh_sftp_SUITE_data/sftp.txt @@ -0,0 +1,252 @@ +There are 5 KeySyms per KeyCode; KeyCodes range from 8 to 254. + + KeyCode Keysym (Keysym) ... + Value Value (Name) ... + + 8 + 9 + 10 + 11 0x0041 (A) + 12 0x0042 (B) + 13 0x0043 (C) + 14 0x0044 (D) + 15 0x0065 (e) 0x0045 (E) 0x20ac (EuroSign) + 16 0x0046 (F) + 17 0x0047 (G) + 18 0x0048 (H) + 19 0x0049 (I) + 20 0x004a (J) + 21 0x004b (K) + 22 0x004c (L) + 23 0x004d (M) + 24 0x004e (N) + 25 0x004f (O) + 26 0x0050 (P) + 27 0x0051 (Q) + 28 0x0052 (R) + 29 0x0053 (S) + 30 0x0054 (T) + 31 0x0055 (U) + 32 0x0056 (V) + 33 0x0057 (W) + 34 0x0058 (X) + 35 0x0059 (Y) + 36 0x005a (Z) + 37 0x0031 (1) 0x0021 (exclam) + 38 0x0032 (2) 0x0022 (quotedbl) 0x0040 (at) + 39 0x0033 (3) 0x0023 (numbersign) 0x00a3 (sterling) + 40 0x0034 (4) 0x00a4 (currency) 0x0024 (dollar) + 41 0x0035 (5) 0x0025 (percent) + 42 0x0036 (6) 0x0026 (ampersand) + 43 0x0037 (7) 0x002f (slash) 0x007b (braceleft) + 44 0x0038 (8) 0x0028 (parenleft) 0x005b (bracketleft) + 45 0x0039 (9) 0x0029 (parenright) 0x005d (bracketright) + 46 0x0030 (0) 0x003d (equal) 0x007d (braceright) + 47 0xff0d (Return) + 48 0xff1b (Escape) + 49 0xff08 (BackSpace) + 50 0xff09 (Tab) + 51 0x0020 (space) + 52 0x002b (plus) 0x003f (question) 0x005c (backslash) + 53 0x1005ff03 (SunFA_Acute) 0x1005ff00 (SunFA_Grave) + 54 0x00c5 (Aring) + 55 0x1005ff04 (SunFA_Diaeresis) 0x005e (asciicircum) 0x007e (asciitilde) + 56 + 57 0x0027 (apostrophe) 0x002a (asterisk) 0x0060 (grave) + 58 0x00d6 (Odiaeresis) + 59 0x00c4 (Adiaeresis) + 60 0x00a7 (section) 0x00bd (onehalf) + 61 0x002c (comma) 0x003b (semicolon) + 62 0x002e (period) 0x003a (colon) + 63 0x002d (minus) 0x005f (underscore) + 64 0xffe5 (Caps_Lock) + 65 0xffbe (F1) + 66 0xffbf (F2) + 67 0xffc0 (F3) + 68 0xffc1 (F4) + 69 0xffc2 (F5) + 70 0xffc3 (F6) + 71 0xffc4 (F7) + 72 0xffc5 (F8) + 73 0xffc6 (F9) + 74 0xffc7 (F10) + 75 0x1005ff10 (SunF36) + 76 0x1005ff11 (SunF37) + 77 0xffd3 (F22) 0xffd3 (F22) 0xff61 (Print) 0x1005ff60 (SunSys_Req) + 78 0xffd4 (F23) 0xffd4 (F23) 0xff14 (Scroll_Lock) + 79 0xffd2 (F21) 0xffd2 (F21) 0xff13 (Pause) 0xff6b (Break) + 80 0xff63 (Insert) + 81 0xff50 (Home) + 82 0xff55 (Prior) + 83 0xffff (Delete) + 84 0xff57 (End) + 85 0xff56 (Next) + 86 0xff53 (Right) + 87 0xff51 (Left) + 88 0xff54 (Down) + 89 0xff52 (Up) + 90 0xff7f (Num_Lock) + 91 0xffd6 (F25) 0xffd6 (F25) 0xffaf (KP_Divide) + 92 0xffd7 (F26) 0xffd7 (F26) 0xffaa (KP_Multiply) + 93 0xffd5 (F24) 0xffd5 (F24) 0xffad (KP_Subtract) + 94 0xffab (KP_Add) + 95 0xff8d (KP_Enter) + 96 0xffde (F33) 0xffde (F33) 0xffb1 (KP_1) 0xff57 (End) + 97 0xff54 (Down) 0xffdf (F34) 0xffb2 (KP_2) + 98 0xffe0 (F35) 0xffe0 (F35) 0xffb3 (KP_3) 0xff56 (Next) + 99 0xff51 (Left) 0xffdb (F30) 0xffb4 (KP_4) + 100 0xffdc (F31) 0xffdc (F31) 0xffb5 (KP_5) + 101 0xff53 (Right) 0xffdd (F32) 0xffb6 (KP_6) + 102 0xffd8 (F27) 0xffd8 (F27) 0xffb7 (KP_7) 0xff50 (Home) + 103 0xff52 (Up) 0xffd9 (F28) 0xffb8 (KP_8) + 104 0xffda (F29) 0xffda (F29) 0xffb9 (KP_9) 0xff55 (Prior) + 105 0xff9e (KP_Insert) 0xff9e (KP_Insert) 0xffb0 (KP_0) + 106 0xffff (Delete) 0xffff (Delete) 0xffac (KP_Separator) + 107 0x003c (less) 0x003e (greater) 0x007c (bar) + 108 0xff20 (Multi_key) + 109 0x1005ff76 (SunPowerSwitch) 0x1005ff7d (SunPowerSwitchShift) + 110 + 111 + 112 + 113 + 114 + 115 + 116 + 117 + 118 + 119 + 120 + 121 + 122 + 123 0xffce (F17) 0xffce (F17) 0x1005ff73 (SunOpen) + 124 0xff6a (Help) + 125 0xffca (F13) 0xffca (F13) 0x1005ff70 (SunProps) + 126 0xffcc (F15) 0xffcc (F15) 0x1005ff71 (SunFront) + 127 0xffc8 (F11) 0xffc8 (F11) 0xff69 (Cancel) + 128 0xffc9 (F12) 0xffc9 (F12) 0xff66 (Redo) + 129 0xffcb (F14) 0xffcb (F14) 0xff65 (Undo) + 130 0xffd1 (F20) 0xffd1 (F20) 0x1005ff75 (SunCut) + 131 0xffcd (F16) 0xffcd (F16) 0x1005ff72 (SunCopy) + 132 0xffcf (F18) 0xffcf (F18) 0x1005ff74 (SunPaste) + 133 0xffd0 (F19) 0xffd0 (F19) 0xff68 (Find) + 134 0x1005ff78 (SunAudioMute) 0x1005ff7a (SunVideoDegauss) + 135 0x1005ff79 (SunAudioRaiseVolume) 0x1005ff7c (SunVideoRaiseBrightness) + 136 0x1005ff77 (SunAudioLowerVolume) 0x1005ff7b (SunVideoLowerBrightness) + 137 + 138 + 139 + 140 + 141 + 142 + 143 + 144 + 145 + 146 + 147 + 148 + 149 + 150 + 151 + 152 + 153 + 154 + 155 + 156 + 157 + 158 + 159 + 160 + 161 + 162 + 163 + 164 + 165 + 166 + 167 + 168 + 169 + 170 + 171 + 172 + 173 + 174 + 175 + 176 + 177 + 178 + 179 + 180 + 181 + 182 + 183 + 184 + 185 + 186 + 187 + 188 + 189 + 190 + 191 + 192 + 193 + 194 + 195 + 196 + 197 + 198 + 199 + 200 + 201 + 202 + 203 + 204 + 205 + 206 + 207 + 208 + 209 + 210 + 211 + 212 + 213 + 214 + 215 + 216 + 217 + 218 + 219 + 220 + 221 + 222 + 223 + 224 + 225 + 226 + 227 + 228 + 229 + 230 + 231 0xffe3 (Control_L) + 232 0xffe1 (Shift_L) + 233 0xffe9 (Alt_L) + 234 0xffe7 (Meta_L) + 235 + 236 0xffe2 (Shift_R) + 237 0xff7e (Mode_switch) + 238 0xffe8 (Meta_R) + 239 + 240 + 241 + 242 + 243 + 244 + 245 + 246 + 247 + 248 + 249 + 250 + 251 + 252 + 253 + 254 diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl new file mode 100644 index 0000000000..bfe54a3e75 --- /dev/null +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -0,0 +1,934 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(ssh_sftpd_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include("test_server_line.hrl"). +-include("ssh_xfer.hrl"). +-include("ssh.hrl"). + +-include_lib("kernel/include/file.hrl"). + +-define(SFPD_PORT, 9999). +-define(USER, "Alladin"). +-define(PASSWD, "Sesame"). +-define(XFER_PACKET_SIZE, 32768). +-define(XFER_WINDOW_SIZE, 4*?XFER_PACKET_SIZE). +-define(TIMEOUT, 10000). +-define(REG_ATTERS, <<0,0,0,0,1>>). +-define(UNIX_EPOCH, 62167219200). + +-define(is_set(F, Bits), + ((F) band (Bits)) == (F)). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + case {catch ssh:stop(),catch crypto:start()} of + {ok,ok} -> + ssh_test_lib:make_dsa_files(Config), + Config; + {ok,_} -> + {skip,"Could not start ssh!"}; + {_,ok} -> + {skip,"Could not start crypto!"}; + {_,_} -> + {skip,"Could not start crypto and ssh!"} + end. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + crypto:stop(), + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initiation before each test case +%%-------------------------------------------------------------------- +init_per_testcase(TestCase, Config) -> + ssh:start(), + prep(Config), + SysDir = ?config(data_dir, Config), + {ok, Sftpd} = + ssh_sftpd:listen(?SFPD_PORT, [{system_dir, SysDir}, + {user_passwords,[{?USER, ?PASSWD}]}, + {pwdfun, fun(_,_) -> true end}]), + + Cm = ssh_test_lib:connect(?SFPD_PORT, + [{system_dir, SysDir}, + {user_dir, SysDir}, + {user, ?USER}, {password, ?PASSWD}, + {user_interaction, false}, + {silently_accept_hosts, true}, + {pwdfun, fun(_,_) -> true end}]), + {ok, Channel} = + ssh_connection:session_channel(Cm, ?XFER_WINDOW_SIZE, + ?XFER_PACKET_SIZE, ?TIMEOUT), + + success = ssh_connection:subsystem(Cm, Channel, "sftp", ?TIMEOUT), + + ProtocolVer = case atom_to_list(TestCase) of + "ver3_" ++ _ -> + 3; + _ -> + ?SSH_SFTP_PROTOCOL_VERSION + end, + + Data = <<?UINT32(ProtocolVer)>> , + + Size = 1 + size(Data), + + ssh_connection:send(Cm, Channel, << ?UINT32(Size), + ?SSH_FXP_INIT, Data/binary >>), + + {ok, <<?SSH_FXP_VERSION, ?UINT32(Version), _Ext/binary>>, _} + = reply(Cm, Channel), + + test_server:format("Client: ~p Server ~p~n", [ProtocolVer, Version]), + + [{sftp, {Cm, Channel}}, {sftpd, Sftpd }| Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, Config) -> + ssh_sftpd:stop(?config(sftpd, Config)), + {Cm, Channel} = ?config(sftp, Config), + ssh_connection:close(Cm, Channel), + ssh:close(Cm), + ssh:stop(), + ok. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all() -> + [open_close_file, open_close_dir, read_file, read_dir, + write_file, rename_file, mk_rm_dir, remove_file, + real_path, retrieve_attributes, set_attributes, links, + ver3_rename_OTP_6352, seq10670, sshd_read_file]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% Test cases starts here. +%%-------------------------------------------------------------------- +open_close_file(doc) -> + ["Test SSH_FXP_OPEN and SSH_FXP_CLOSE commands"]; +open_close_file(suite) -> + []; +open_close_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + {Cm, Channel} = ?config(sftp, Config), + ReqId = 0, + + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = + open_file(FileName, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = close(Handle, ReqId, + Cm, Channel), + NewReqId = ReqId + 1, + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_INVALID_HANDLE), _/binary>>, _} = + close(Handle, ReqId, Cm, Channel), + + NewReqId1 = NewReqId + 1, + %% {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), % Ver 6 we have 5 + %% ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} = + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), + ?UINT32(?SSH_FX_FAILURE), _/binary>>, _} = + open_file(PrivDir, Cm, Channel, NewReqId1, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + + ok. + +%%-------------------------------------------------------------------- +open_close_dir(doc) -> + ["Test SSH_FXP_OPENDIR and SSH_FXP_CLOSE commands"]; +open_close_dir(suite) -> + []; +open_close_dir(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + {Cm, Channel} = ?config(sftp, Config), + FileName = filename:join(PrivDir, "test.txt"), + ReqId = 0, + + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = + open_dir(PrivDir, Cm, Channel, ReqId), + + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = close(Handle, ReqId, + Cm, Channel), + + NewReqId = 1, + case open_dir(FileName, Cm, Channel, NewReqId) of + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), + ?UINT32(?SSH_FX_NOT_A_DIRECTORY), _/binary>>, _} -> + %% Only if server is using vsn > 5. + ok; + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), + ?UINT32(?SSH_FX_FAILURE), _/binary>>, _} -> + ok + end. + +%%-------------------------------------------------------------------- +read_file(doc) -> + ["Test SSH_FXP_READ command"]; +read_file(suite) -> + []; +read_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = + open_file(FileName, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + + NewReqId = 1, + + {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length), + Data/binary>>, _} = + read_file(Handle, 100, 0, Cm, Channel, NewReqId), + + {ok, Data} = file:read_file(FileName), + + ok. +%%-------------------------------------------------------------------- +read_dir(doc) -> + ["Test SSH_FXP_READDIR command"]; +read_dir(suite) -> + []; +read_dir(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + {Cm, Channel} = ?config(sftp, Config), + ReqId = 0, + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = + open_dir(PrivDir, Cm, Channel, ReqId), + ok = read_dir(Handle, Cm, Channel, ReqId), + ok. + +%%-------------------------------------------------------------------- +write_file(doc) -> + ["Test SSH_FXP_WRITE command"]; +write_file(suite) -> + []; +write_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = + open_file(FileName, Cm, Channel, ReqId, + ?ACE4_WRITE_DATA bor ?ACE4_WRITE_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + + NewReqId = 1, + Data = list_to_binary("Write file test"), + + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_OK), + _/binary>>, _} + = write_file(Handle, Data, 0, Cm, Channel, NewReqId), + + {ok, Data} = file:read_file(FileName), + + ok. + +%%-------------------------------------------------------------------- +remove_file(doc) -> + ["Test SSH_FXP_REMOVE command"]; +remove_file(suite) -> + []; +remove_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = + remove(FileName, Cm, Channel, ReqId), + + NewReqId = 1, + %% {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), % ver 6 we have 5 + %% ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY ), _/binary>>, _} = + + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), + ?UINT32(?SSH_FX_FAILURE), _/binary>>, _} = + remove(PrivDir, Cm, Channel, NewReqId), + + ok. + +%%-------------------------------------------------------------------- +rename_file(doc) -> + ["Test SSH_FXP_RENAME command"]; +rename_file(suite) -> + []; +rename_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + NewFileName = filename:join(PrivDir, "test1.txt"), + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = + rename(FileName, NewFileName, Cm, Channel, ReqId, 6, 0), + + NewReqId = ReqId + 1, + + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = + rename(NewFileName, FileName, Cm, Channel, NewReqId, 6, + ?SSH_FXP_RENAME_OVERWRITE), + + NewReqId1 = NewReqId + 1, + file:copy(FileName, NewFileName), + + %% No owerwrite + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), + ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS), _/binary>>, _} = + rename(FileName, NewFileName, Cm, Channel, NewReqId1, 6, + ?SSH_FXP_RENAME_NATIVE), + + NewReqId2 = NewReqId1 + 1, + + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId2), + ?UINT32(?SSH_FX_OP_UNSUPPORTED), _/binary>>, _} = + rename(FileName, NewFileName, Cm, Channel, NewReqId2, 6, + ?SSH_FXP_RENAME_ATOMIC), + + ok. + +%%-------------------------------------------------------------------- +mk_rm_dir(doc) -> + ["Test SSH_FXP_MKDIR and SSH_FXP_RMDIR command"]; +mk_rm_dir(suite) -> + []; +mk_rm_dir(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + {Cm, Channel} = ?config(sftp, Config), + DirName = filename:join(PrivDir, "test"), + ReqId = 0, + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), + _/binary>>, _} = mkdir(DirName, Cm, Channel, ReqId), + + NewReqId = 1, + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FAILURE), + _/binary>>, _} = mkdir(DirName, Cm, Channel, NewReqId), + + NewReqId1 = 2, + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), ?UINT32(?SSH_FX_OK), + _/binary>>, _} = rmdir(DirName, Cm, Channel, NewReqId1), + + NewReqId2 = 3, + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId2), ?UINT32(?SSH_FX_NO_SUCH_FILE), + _/binary>>, _} = rmdir(DirName, Cm, Channel, NewReqId2), + + ok. +%%-------------------------------------------------------------------- +real_path(doc) -> + ["Test SSH_FXP_REALPATH command"]; +real_path(suite) -> + []; +real_path(Config) when is_list(Config) -> + case test_server:os_type() of + {win32, _} -> + {skip, "Not a relevant test on windows"}; + _ -> + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + PrivDir = ?config(priv_dir, Config), + TestDir = filename:join(PrivDir, "ssh_test"), + ok = file:make_dir(TestDir), + + OrigPath = filename:join(TestDir, ".."), + + {ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(_), ?UINT32(Len), + Path:Len/binary, _/binary>>, _} + = real_path(OrigPath, Cm, Channel, ReqId), + + RealPath = filename:absname(binary_to_list(Path)), + AbsPrivDir = filename:absname(PrivDir), + + test_server:format("Path: ~p PrivDir: ~p~n", [RealPath, AbsPrivDir]), + + true = RealPath == AbsPrivDir, + + ok + end. + +%%-------------------------------------------------------------------- +links(doc) -> + []; +links(suite) -> + []; +links(Config) when is_list(Config) -> + case test_server:os_type() of + {win32, _} -> + {skip, "Links are not fully supported by windows"}; + _ -> + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + LinkFileName = filename:join(PrivDir, "link_test.txt"), + + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = + create_link(LinkFileName, FileName, Cm, Channel, ReqId), + + NewReqId = 1, + {ok, <<?SSH_FXP_NAME, ?UINT32(NewReqId), ?UINT32(_), ?UINT32(Len), + Path:Len/binary, _/binary>>, _} + = read_link(LinkFileName, Cm, Channel, NewReqId), + + + true = binary_to_list(Path) == FileName, + + test_server:format("Path: ~p~n", [binary_to_list(Path)]), + ok + end. + +%%-------------------------------------------------------------------- +retrieve_attributes(doc) -> + ["Test SSH_FXP_STAT, SSH_FXP_LSTAT AND SSH_FXP_FSTAT commands"]; +retrieve_attributes(suite) -> + []; +retrieve_attributes(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + {ok, FileInfo} = file:read_file_info(FileName), + + AttrValues = + retrive_attributes(FileName, Cm, Channel, ReqId), + + Type = encode_file_type(FileInfo#file_info.type), + Size = FileInfo#file_info.size, + Owner = FileInfo#file_info.uid, + Group = FileInfo#file_info.gid, + Permissions = FileInfo#file_info.mode, + Atime = calendar:datetime_to_gregorian_seconds( + erlang:localtime_to_universaltime(FileInfo#file_info.atime)) + - ?UNIX_EPOCH, + Mtime = calendar:datetime_to_gregorian_seconds( + erlang:localtime_to_universaltime(FileInfo#file_info.mtime)) + - ?UNIX_EPOCH, + Ctime = calendar:datetime_to_gregorian_seconds( + erlang:localtime_to_universaltime(FileInfo#file_info.ctime)) + - ?UNIX_EPOCH, + + lists:foreach(fun(Value) -> + <<?UINT32(Flags), _/binary>> = Value, + true = ?is_set(?SSH_FILEXFER_ATTR_SIZE, + Flags), + true = ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS, + Flags), + true = ?is_set(?SSH_FILEXFER_ATTR_ACCESSTIME, + Flags), + true = ?is_set(?SSH_FILEXFER_ATTR_CREATETIME, + Flags), + true = ?is_set(?SSH_FILEXFER_ATTR_MODIFYTIME, + Flags), + true = ?is_set(?SSH_FILEXFER_ATTR_OWNERGROUP, + Flags), + false = ?is_set(?SSH_FILEXFER_ATTR_ACL, + Flags), + false = ?is_set(?SSH_FILEXFER_ATTR_SUBSECOND_TIMES, + Flags), + false = ?is_set(?SSH_FILEXFER_ATTR_BITS, + Flags), + false = ?is_set(?SSH_FILEXFER_ATTR_EXTENDED, + Flags), + + <<?UINT32(_Flags), ?BYTE(Type), + ?UINT64(Size), + ?UINT32(OwnerLen), BinOwner:OwnerLen/binary, + ?UINT32(GroupLen), BinGroup:GroupLen/binary, + ?UINT32(Permissions), + ?UINT64(Atime), + ?UINT64(Ctime), + ?UINT64(Mtime)>> = Value, + + Owner = list_to_integer(binary_to_list(BinOwner)), + Group = list_to_integer(binary_to_list(BinGroup)) + end, AttrValues), + + ok. +%%-------------------------------------------------------------------- +set_attributes(doc) -> + ["Test SSH_FXP_SETSTAT AND SSH_FXP_FSETSTAT commands"]; +set_attributes(suite) -> + []; +set_attributes(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + {ok, FileInfo} = file:read_file_info(FileName), + + OrigPermissions = FileInfo#file_info.mode, + Permissions = 8#400, %% User read-only + + Flags = ?SSH_FILEXFER_ATTR_PERMISSIONS, + + Atters = [?uint32(Flags), ?byte(?SSH_FILEXFER_TYPE_REGULAR), + ?uint32(Permissions)], + + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = + set_attributes_file(FileName, Atters, Cm, Channel, ReqId), + + {ok, NewFileInfo} = file:read_file_info(FileName), + NewPermissions = NewFileInfo#file_info.mode, + + %% Can not test that NewPermissions = Permissions as + %% on Unix platforms, other bits than those listed in the + %% API may be set. + test_server:format("Org: ~p New: ~p~n", [OrigPermissions, NewPermissions]), + true = OrigPermissions =/= NewPermissions, + + test_server:format("Try to open the file"), + NewReqId = 2, + {ok, <<?SSH_FXP_HANDLE, ?UINT32(NewReqId), Handle/binary>>, _} = + open_file(FileName, Cm, Channel, NewReqId, + ?ACE4_READ_DATA bor ?ACE4_WRITE_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + + NewAtters = [?uint32(Flags), ?byte(?SSH_FILEXFER_TYPE_REGULAR), + ?uint32(OrigPermissions)], + + NewReqId1 = 3, + + test_server:format("Set original permissions on the now open file"), + + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = + set_attributes_open_file(Handle, NewAtters, Cm, Channel, NewReqId1), + + {ok, NewFileInfo1} = file:read_file_info(FileName), + OrigPermissions = NewFileInfo1#file_info.mode, + + ok. + +%%-------------------------------------------------------------------- +ver3_rename_OTP_6352(doc) -> + ["Test that ver3 rename message is handled"]; + +ver3_rename_OTP_6352(suite) -> + []; + +ver3_rename_OTP_6352(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + NewFileName = filename:join(PrivDir, "test1.txt"), + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_OK), _/binary>>, _} = + rename(FileName, NewFileName, Cm, Channel, ReqId, 3, 0), + + ok. + +%%-------------------------------------------------------------------- +seq10670(doc) -> + ["Check that realpath works ok"]; + +seq10670(suite) -> + []; + +seq10670(Config) when is_list(Config) -> + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + case test_server:os_type() of + {win32, _} -> + {skip, "Not a relevant test on windows"}; + _ -> + {ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(_), ?UINT32(Len), + Root:Len/binary, _/binary>>, _} + = real_path("/..", Cm, Channel, ReqId), + + <<"/">> = Root, + + {ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(_), ?UINT32(Len), + Path:Len/binary, _/binary>>, _} + = real_path("/usr/bin/../..", Cm, Channel, ReqId), + + Root = Path + end. + +%% Internal functions +%%-------------------------------------------------------------------- +prep(Config) -> + PrivDir = ?config(priv_dir, Config), + TestFile = filename:join(PrivDir, "test.txt"), + TestFile1 = filename:join(PrivDir, "test1.txt"), + + file:delete(TestFile), + file:delete(TestFile1), + + %% Initial config + DataDir = ?config(data_dir, Config), + FileName = filename:join(DataDir, "test.txt"), + file:copy(FileName, TestFile), + Mode = 8#00400 bor 8#00200 bor 8#00040, % read & write owner, read group + {ok, FileInfo} = file:read_file_info(TestFile), + ok = file:write_file_info(TestFile, + FileInfo#file_info{mode = Mode}). + +reply(Cm, Channel) -> + reply(Cm, Channel,<<>>). + +reply(Cm, Channel, RBuf) -> + receive + {ssh_cm, Cm, {data, Channel, 0, Data}} -> + case <<RBuf/binary, Data/binary>> of + <<?UINT32(Len),Reply:Len/binary,Rest/binary>> -> + {ok, Reply, Rest}; + RBuf2 -> + reply(Cm, Channel, RBuf2) + end; + {ssh_cm, Cm, {eof, Channel}} -> + eof; + {ssh_cm, Cm, {closed, Channel}} -> + closed; + {ssh_cm, Cm, Msg} -> + test_server:fail(Msg) + end. + + +open_file(File, Cm, Channel, ReqId, Access, Flags) -> + + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(File)), + ?uint32(Access), + ?uint32(Flags), + ?REG_ATTERS]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_OPEN, Data/binary>>), + reply(Cm, Channel). + + + +close(Handle, ReqId, Cm , Channel) -> + Data = list_to_binary([?uint32(ReqId), Handle]), + + Size = 1 + size(Data), + + ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_CLOSE, + Data/binary>>), + + reply(Cm, Channel). + + + +open_dir(Dir, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(Dir))]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_OPENDIR, Data/binary>>), + reply(Cm, Channel). + + +rename(OldName, NewName, Cm, Channel, ReqId, Version, Flags) -> + Data = + case Version of + 3 -> + list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(OldName)), + ?binary(list_to_binary(NewName))]); + _ -> + list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(OldName)), + ?binary(list_to_binary(NewName)), + ?uint32(Flags)]) + end, + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_RENAME, Data/binary>>), + reply(Cm, Channel). + + +mkdir(Dir, Cm, Channel, ReqId)-> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(Dir)), + ?REG_ATTERS]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_MKDIR, Data/binary>>), + reply(Cm, Channel). + + +rmdir(Dir, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(Dir))]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_RMDIR, Data/binary>>), + reply(Cm, Channel). + +remove(File, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(File))]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_REMOVE, Data/binary>>), + reply(Cm, Channel). + + +read_dir(Handle, Cm, Channel, ReqId) -> + + Data = list_to_binary([?uint32(ReqId), Handle]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_READDIR, Data/binary>>), + case reply(Cm, Channel) of + {ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(Count), + ?UINT32(Len), Listing:Len/binary, _/binary>>, _} -> + test_server:format("Count: ~p Listing: ~p~n", + [Count, binary_to_list(Listing)]), + read_dir(Handle, Cm, Channel, ReqId); + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_EOF), _/binary>>, _} -> + ok + end. + +read_file(Handle, MaxLength, OffSet, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), Handle, + ?uint64(OffSet), + ?uint32(MaxLength)]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_READ, Data/binary>>), + reply(Cm, Channel). + + +write_file(Handle, FileData, OffSet, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), Handle, + ?uint64(OffSet), + ?binary(FileData)]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_WRITE, Data/binary>>), + reply(Cm, Channel). + + +real_path(OrigPath, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(OrigPath))]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_REALPATH, Data/binary>>), + reply(Cm, Channel). + +create_link(LinkPath, Path, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(LinkPath)), + ?binary(list_to_binary(Path))]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_SYMLINK, Data/binary>>), + reply(Cm, Channel). + + +read_link(Link, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(Link))]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_READLINK, Data/binary>>), + reply(Cm, Channel). + +retrive_attributes_file(FilePath, Flags, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(FilePath)), + ?uint32(Flags)]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_STAT, Data/binary>>), + reply(Cm, Channel). + +retrive_attributes_file_or_link(FilePath, Flags, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(FilePath)), + ?uint32(Flags)]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_LSTAT, Data/binary>>), + reply(Cm, Channel). + +retrive_attributes_open_file(Handle, Flags, Cm, Channel, ReqId) -> + + Data = list_to_binary([?uint32(ReqId), + Handle, + ?uint32(Flags)]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_FSTAT, Data/binary>>), + reply(Cm, Channel). + +retrive_attributes(FileName, Cm, Channel, ReqId) -> + + Attr = ?SSH_FILEXFER_ATTR_SIZE, + + {ok, <<?SSH_FXP_ATTRS, ?UINT32(ReqId), Value/binary>>, _} + = retrive_attributes_file(FileName, Attr, + Cm, Channel, ReqId), + + NewReqId = ReqId + 1, + {ok, <<?SSH_FXP_ATTRS, ?UINT32(NewReqId), Value1/binary>>, _} + = retrive_attributes_file_or_link(FileName, + Attr, Cm, Channel, NewReqId), + + NewReqId1 = NewReqId + 1, + {ok, <<?SSH_FXP_HANDLE, ?UINT32(NewReqId1), Handle/binary>>, _} = + open_file(FileName, Cm, Channel, NewReqId1, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + + NewReqId2 = NewReqId1 + 1, + {ok, <<?SSH_FXP_ATTRS, ?UINT32(NewReqId2), Value2/binary>>, _} + = retrive_attributes_open_file(Handle, Attr, Cm, Channel, NewReqId2), + + [Value, Value1, Value2]. + +set_attributes_file(FilePath, Atters, Cm, Channel, ReqId) -> + Data = list_to_binary([?uint32(ReqId), + ?binary(list_to_binary(FilePath)), + Atters]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_SETSTAT, Data/binary>>), + reply(Cm, Channel). + + +set_attributes_open_file(Handle, Atters, Cm, Channel, ReqId) -> + + Data = list_to_binary([?uint32(ReqId), + Handle, + Atters]), + Size = 1 + size(Data), + ssh_connection:send(Cm, Channel, <<?UINT32(Size), + ?SSH_FXP_FSETSTAT, Data/binary>>), + reply(Cm, Channel). + + +encode_file_type(Type) -> + case Type of + regular -> ?SSH_FILEXFER_TYPE_REGULAR; + directory -> ?SSH_FILEXFER_TYPE_DIRECTORY; + symlink -> ?SSH_FILEXFER_TYPE_SYMLINK; + special -> ?SSH_FILEXFER_TYPE_SPECIAL; + unknown -> ?SSH_FILEXFER_TYPE_UNKNOWN; + other -> ?SSH_FILEXFER_TYPE_UNKNOWN; + socket -> ?SSH_FILEXFER_TYPE_SOCKET; + char_device -> ?SSH_FILEXFER_TYPE_CHAR_DEVICE; + block_device -> ?SSH_FILEXFER_TYPE_BLOCK_DEVICE; + fifo -> ?SSH_FILEXFER_TYPE_FIFO; + undefined -> ?SSH_FILEXFER_TYPE_UNKNOWN + end. + +%%-------------------------------------------------------------------- +sshd_read_file(doc) -> + ["Test SSH_FXP_READ command, using sshd-server"]; +sshd_read_file(suite) -> + []; +sshd_read_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(PrivDir, "test.txt"), + + ReqId = 0, + {Cm, Channel} = ?config(sftp, Config), + + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = + open_file(FileName, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + + NewReqId = 1, + + {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length), + Data/binary>>, _} = + read_file(Handle, 100, 0, Cm, Channel, NewReqId), + + {ok, Data} = file:read_file(FileName), + + ok. diff --git a/lib/ssh/test/ssh_sftpd_SUITE_data/test.txt b/lib/ssh/test/ssh_sftpd_SUITE_data/test.txt new file mode 100644 index 0000000000..681bff80a0 --- /dev/null +++ b/lib/ssh/test/ssh_sftpd_SUITE_data/test.txt @@ -0,0 +1 @@ +Sftp test file.
\ No newline at end of file diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl new file mode 100644 index 0000000000..2209af05d5 --- /dev/null +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -0,0 +1,328 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(ssh_sftpd_erlclient_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include("test_server_line.hrl"). + +-include_lib("kernel/include/file.hrl"). + +-define(SSHD_PORT, 9999). +-define(USER, "Alladin"). +-define(PASSWD, "Sesame"). +-define(SSH_MAX_PACKET_SIZE, 32768). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + catch ssh:stop(), + case catch crypto:start() of + ok -> + DataDir = ?config(data_dir, Config), + FileAlt = filename:join(DataDir, "ssh_sftpd_file_alt.erl"), + c:c(FileAlt), + FileName = filename:join(DataDir, "test.txt"), + {ok, FileInfo} = file:read_file_info(FileName), + ok = file:write_file_info(FileName, + FileInfo#file_info{mode = 8#400}), + ssh_test_lib:make_dsa_files(Config), + Config; + _Else -> + {skip,"Could not start ssh!"} + end. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + crypto:stop(), + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initiation before each test case +%%-------------------------------------------------------------------- +init_per_testcase(TestCase, Config) -> + ssh:start(), + DataDir = ?config(data_dir, Config), + + Options = + case atom_to_list(TestCase) of + "file_cb" ++ _ -> + Spec = + ssh_sftpd:subsystem_spec([{file_handler, + ssh_sftpd_file_alt}]), + [{user_passwords,[{?USER, ?PASSWD}]}, + {pwdfun, fun(_,_) -> true end}, + {system_dir, DataDir}, + {user_dir, DataDir}, + {subsystems, [Spec]}]; + "root_dir" -> + Privdir = ?config(priv_dir, Config), + Root = filename:join(Privdir, root), + file:make_dir(Root), + Spec = ssh_sftpd:subsystem_spec([{root,Root}]), + [{user_passwords,[{?USER, ?PASSWD}]}, + {pwdfun, fun(_,_) -> true end}, + {system_dir, DataDir}, + {user_dir, DataDir}, + {subsystems, [Spec]}]; + "list_dir_limited" -> + Spec = + ssh_sftpd:subsystem_spec([{max_files,1}]), + [{user_passwords,[{?USER, ?PASSWD}]}, + {pwdfun, fun(_,_) -> true end}, + {system_dir, DataDir}, + {user_dir, DataDir}, + {subsystems, [Spec]}]; + + _ -> + [{user_passwords,[{?USER, ?PASSWD}]}, + {pwdfun, fun(_,_) -> true end}, + {user_dir, DataDir}, + {system_dir, DataDir}] + end, + + {Sftpd, Host, _Port} = ssh_test_lib:daemon(any, ?SSHD_PORT, Options), + + {ok, ChannelPid, Connection} = + ssh_sftp:start_channel(Host, ?SSHD_PORT, + [{silently_accept_hosts, true}, + {user, ?USER}, {password, ?PASSWD}, + {pwdfun, fun(_,_) -> true end}, + {system_dir, DataDir}, + {user_dir, DataDir}, + {timeout, 30000}]), + TmpConfig = lists:keydelete(sftp, 1, Config), + NewConfig = lists:keydelete(sftpd, 1, TmpConfig), + [{sftp, {ChannelPid, Connection}}, {sftpd, Sftpd} | NewConfig]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, Config) -> + catch ssh_sftpd:stop(?config(sftpd, Config)), + {Sftp, Connection} = ?config(sftp, Config), + catch ssh_sftp:stop_channel(Sftp), + catch ssh:close(Connection), + ssh:stop(), + ok. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all() -> + [close_file_OTP_6350, quit_OTP_6349, file_cb_OTP_6356, + root_dir, list_dir_limited]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% Test cases starts here. +%%-------------------------------------------------------------------- +close_file_OTP_6350(doc) -> + ["Test that sftpd closes its fildescriptors after compleating the " + "transfer"]; + +close_file_OTP_6350(suite) -> + []; + +close_file_OTP_6350(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + FileName = filename:join(DataDir, "test.txt"), + + {Sftp, _} = ?config(sftp, Config), + + NumOfPorts = length(erlang:ports()), + + test_server:format("Number of open ports: ~p~n", [NumOfPorts]), + + {ok, <<_/binary>>} = ssh_sftp:read_file(Sftp, FileName), + + NumOfPorts = length(erlang:ports()), + + test_server:format("Number of open ports: ~p~n", + [length(erlang:ports())]), + + ok. + +%%-------------------------------------------------------------------- + +quit_OTP_6349(doc) -> + [" When the sftp client ends the session the " + "server will now behave correctly and not leave the " + "client hanging."]; + +quit_OTP_6349(suite) -> + []; + +quit_OTP_6349(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + FileName = filename:join(DataDir, "test.txt"), + + {Sftp, _} = ?config(sftp, Config), + + {ok, <<_/binary>>} = ssh_sftp:read_file(Sftp, FileName), + + ok = ssh_sftp:stop_channel(Sftp), + + Host = ssh_test_lib:hostname(), + + timer:sleep(5000), + {ok, NewSftp, _Conn} = ssh_sftp:start_channel(Host, ?SSHD_PORT, + [{silently_accept_hosts, true}, + {pwdfun, fun(_,_) -> true end}, + {system_dir, DataDir}, + {user_dir, DataDir}, + {user, ?USER}, {password, ?PASSWD}]), + + {ok, <<_/binary>>} = ssh_sftp:read_file(NewSftp, FileName), + + ok = ssh_sftp:stop_channel(NewSftp), + ok. + +%%-------------------------------------------------------------------- + +file_cb_OTP_6356(doc) -> + ["Test that it is possible to change the callback module for" + " the sftpds filehandling."]; + +file_cb_OTP_6356(suite) -> + []; + +file_cb_OTP_6356(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + FileName = filename:join(DataDir, "test.txt"), + + register(sftpd_file_alt_tester, self()), + + {Sftp, _} = ?config(sftp, Config), + + {ok, Bin} = ssh_sftp:read_file(Sftp, FileName), + alt_file_handler_check(alt_open), + alt_file_handler_check(alt_read_file_info), + alt_file_handler_check(alt_position), + alt_file_handler_check(alt_read), + alt_file_handler_check(alt_position), + alt_file_handler_check(alt_read), + alt_file_handler_check(alt_close), + + + NewFileName = filename:join(PrivDir, "test.txt"), + ok = ssh_sftp:write_file(Sftp, NewFileName, Bin), + alt_file_handler_check(alt_open), + alt_file_handler_check(alt_read_file_info), + alt_file_handler_check(alt_position), + alt_file_handler_check(alt_write), + alt_file_handler_check(alt_close), + + ReFileName = filename:join(PrivDir, "test1.txt"), + ok = ssh_sftp:rename(Sftp, NewFileName, ReFileName), + alt_file_handler_check(alt_rename), + + ok = ssh_sftp:delete(Sftp, ReFileName), + alt_file_handler_check(alt_delete), + + NewDir = filename:join(PrivDir, "testdir"), + ok = ssh_sftp:make_dir(Sftp, NewDir), + alt_file_handler_check(alt_make_dir), + + ok = ssh_sftp:del_dir(Sftp, NewDir), + alt_file_handler_check(alt_read_link_info), + alt_file_handler_check(alt_write_file_info), + alt_file_handler_check(alt_del_dir), + ok. + +root_dir(doc) -> + [""]; +root_dir(suite) -> + []; +root_dir(Config) when is_list(Config) -> + {Sftp, _} = ?config(sftp, Config), + FileName = "test.txt", + Bin = <<"Test file for root dir option">>, + ok = ssh_sftp:write_file(Sftp, FileName, Bin), + {ok, Bin} = ssh_sftp:read_file(Sftp, FileName), + {ok, Listing} = + ssh_sftp:list_dir(Sftp, "."), + test_server:format("Listing: ~p~n", [Listing]), + ok. + +list_dir_limited(doc) -> + [""]; +list_dir_limited(suite) -> + []; +list_dir_limited(Config) when is_list(Config) -> + {Sftp, _} = ?config(sftp, Config), + {ok, Listing} = + ssh_sftp:list_dir(Sftp, "."), + test_server:format("Listing: ~p~n", [Listing]), + ok. + +alt_file_handler_check(Msg) -> + receive + Msg -> + ok; + Other -> + test_server:fail({Msg, Other}) + after 10000 -> + test_server:fail("Not alt file handler") + end. diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl new file mode 100644 index 0000000000..9e119c4929 --- /dev/null +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl @@ -0,0 +1,100 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% + +%%% Description: Dummy Callback module for ssh_sftpd to test +%%% the possibility to switch file handling implementation. + +-module(ssh_sftpd_file_alt). + +-behaviour(ssh_sftpd_file_api). + +%% API +-export([close/2, delete/2, del_dir/2, get_cwd/1, is_dir/2, list_dir/2, + make_dir/2, make_symlink/3, open/3, position/3, read/3, + read_file_info/2, read_link/2, read_link_info/2, rename/3, + write/3, write_file_info/3]). + +close(IoDevice, State) -> + sftpd_file_alt_tester ! alt_close, + {file:close(IoDevice), State}. + +delete(Path, State) -> + sftpd_file_alt_tester ! alt_delete, + {file:delete(Path), State}. + +del_dir(Path, State) -> + sftpd_file_alt_tester ! alt_del_dir, + {file:del_dir(Path), State}. + +get_cwd(State) -> + {file:get_cwd(), State}. + +is_dir(AbsPath, State) -> + sftpd_file_alt_tester ! alt_is_dir, + {filelib:is_dir(AbsPath), State}. + +list_dir(AbsPath, State) -> + sftpd_file_alt_tester ! alt_list_dir, + {file:list_dir(AbsPath), State}. + +make_dir(Dir, State) -> + sftpd_file_alt_tester ! alt_make_dir, + {file:make_dir(Dir), State}. + +make_symlink(Path2, Path, State) -> + sftpd_file_alt_tester ! alt_make_symlink, + {file:make_symlink(Path2, Path), State}. + +open(Path, Flags, State) -> + sftpd_file_alt_tester ! alt_open, + {file:open(Path, Flags), State}. + +position(IoDevice, Offs, State) -> + sftpd_file_alt_tester ! alt_position, + {file:position(IoDevice, Offs), State}. + +read(IoDevice, Len, State) -> + sftpd_file_alt_tester ! alt_read, + {file:read(IoDevice, Len), State}. + +read_link(Path, State) -> + sftpd_file_alt_tester ! alt_read_link, + {file:read_link(Path), State}. + +read_link_info(Path, State) -> + sftpd_file_alt_tester ! alt_read_link_info, + {file:read_link_info(Path), State}. + +read_file_info(Path, State) -> + sftpd_file_alt_tester ! alt_read_file_info, + {file:read_file_info(Path), State}. + +rename(Path, Path2, State) -> + sftpd_file_alt_tester ! alt_rename, + {file:rename(Path, Path2), State}. + +write(IoDevice, Data, State) -> + sftpd_file_alt_tester ! alt_write, + {file:write(IoDevice, Data), State}. + +write_file_info(Path,Info, State) -> + sftpd_file_alt_tester ! alt_write_file_info, + {file:write_file_info(Path, Info), State}. diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/test.txt b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/test.txt new file mode 100644 index 0000000000..681bff80a0 --- /dev/null +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/test.txt @@ -0,0 +1 @@ +Sftp test file.
\ No newline at end of file diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl new file mode 100644 index 0000000000..425fae22c1 --- /dev/null +++ b/lib/ssh/test/ssh_test_lib.erl @@ -0,0 +1,684 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +-module(ssh_test_lib). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("public_key/include/public_key.hrl"). +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +-define(TIMEOUT, 50000). + +connect(Options) -> + connect(hostname(), inet_port(), Options). + +connect(Port, Options) when is_integer(Port) -> + connect(hostname(), Port, Options); +connect(any, Options) -> + connect(hostname(), inet_port(), Options); +connect(Host, Options) -> + connect(Host, inet_port(), Options). + +connect(any, Port, Options) -> + connect(hostname(), Port, Options); +connect(Host, Port, Options) -> + case ssh:connect(Host, Port, Options) of + {ok, ConnectionRef} -> + ConnectionRef; + Error -> + Error + end. + +daemon(Options) -> + daemon(any, inet_port(), Options). + +daemon(Port, Options) when is_integer(Port) -> + daemon(any, Port, Options); +daemon(Host, Options) -> + daemon(Host, inet_port(), Options). + +daemon(Host, Port, Options) -> + case ssh:daemon(Host, Port, Options) of + {ok, Pid} when Host == any -> + {Pid, hostname(), Port}; + {ok, Pid} -> + {Pid, Host, Port}; + Error -> + Error + end. + + + + +start_shell(Port, IOServer) -> + spawn_link(?MODULE, init_shell, [Port, IOServer]). + +init_shell(Port, IOServer) -> + Host = hostname(), + UserDir = get_user_dir(), + Options = [{user_interaction, false}, {silently_accept_hosts, + true}] ++ UserDir, + group_leader(IOServer, self()), + loop_shell(Host, Port, Options). + +loop_shell(Host, Port, Options) -> + ssh:shell(Host, Port, Options). + +start_io_server() -> + spawn_link(?MODULE, init_io_server, [self()]). + +init_io_server(TestCase) -> + process_flag(trap_exit, true), + loop_io_server(TestCase, []). + +loop_io_server(TestCase, Buff0) -> + receive + {input, TestCase, Line} -> + %io:format("~p~n",[{input, TestCase, Line}]), + loop_io_server(TestCase, Buff0 ++ [Line]); + {io_request, From, ReplyAs, Request} -> + %io:format("request -> ~p~n",[Request]), + {ok, Reply, Buff} = io_request(Request, TestCase, From, + ReplyAs, Buff0), + %io:format("reply -> ~p~n",[Reply]), + io_reply(From, ReplyAs, Reply), + loop_io_server(TestCase, Buff); + {'EXIT',_, _} -> + erlang:display('EXIT'), + ok + end. + +io_request({put_chars, Chars}, TestCase, _, _, Buff) -> + reply(TestCase, Chars), + {ok, ok, Buff}; +io_request({put_chars, Enc, Chars}, TestCase, _, _, Buff) -> + reply(TestCase, unicode:characters_to_binary(Chars,Enc,latin1)), + {ok, ok, Buff}; + +io_request({get_line, _} = Request, _, From, ReplyAs, [] = Buff) -> + erlang:send_after(1000, self(), {io_request, From, ReplyAs, Request}), + {ok, [], Buff}; +io_request({get_line, _Enc, _Prompt} = Request, _, From, ReplyAs, [] = Buff) -> + erlang:send_after(1000, self(), {io_request, From, ReplyAs, Request}), + {ok, [], Buff}; + +io_request({get_line, _Enc,_}, _, _, _, [Line | Buff]) -> + {ok, Line, Buff}. + +io_reply(_, _, []) -> + ok; +io_reply(From, ReplyAs, Reply) -> + From ! {io_reply, ReplyAs, Reply}. + +reply(_, []) -> + ok; +reply(TestCase, Result) -> + TestCase ! Result. + +receive_exec_result(Msg) -> + test_server:format("Expect data! ~p", [Msg]), + receive + Msg -> + test_server:format("1: Collected data ~p", [Msg]), + expected; + Other -> + {unexpected_msg, Other} + end. +receive_exec_end(ConnectionRef, ChannelId) -> + Eof = {ssh_cm, ConnectionRef, {eof, ChannelId}}, + ExitStatus = {ssh_cm, ConnectionRef, {exit_status, ChannelId, 0}}, + Closed = {ssh_cm, ConnectionRef,{closed, ChannelId}}, + case receive_exec_result(ExitStatus) of + {unexpected_msg, Eof} -> %% Open ssh seems to not allways send these messages + %% in the same order! + test_server:format("2: Collected data ~p", [Eof]), + case receive_exec_result(ExitStatus) of + expected -> + expected = receive_exec_result(Closed); + {unexpected_msg, Closed} -> + test_server:format("3: Collected data ~p", [Closed]) + end; + expected -> + test_server:format("4: Collected data ~p", [ExitStatus]), + expected = receive_exec_result(Eof), + expected = receive_exec_result(Closed); + Other -> + test_server:fail({unexpected_msg, Other}) + end. + +receive_exec_result(Data, ConnectionRef, ChannelId) -> + Eof = {ssh_cm, ConnectionRef, {eof, ChannelId}}, + Closed = {ssh_cm, ConnectionRef,{closed, ChannelId}}, + expected = receive_exec_result(Data), + expected = receive_exec_result(Eof), + expected = receive_exec_result(Closed). + + +inet_port()-> + {ok, Socket} = gen_tcp:listen(0, [{reuseaddr, true}]), + {ok, Port} = inet:port(Socket), + gen_tcp:close(Socket), + Port. + + +%% copy private keys to given dir from ~/.ssh +get_id_keys(DstDir) -> + SrcDir = filename:join(os:getenv("HOME"), ".ssh"), + RsaOk = copyfile(SrcDir, DstDir, "id_rsa"), + DsaOk = copyfile(SrcDir, DstDir, "id_dsa"), + case {RsaOk, DsaOk} of + {{ok, _}, {ok, _}} -> {ok, both}; + {{ok, _}, _} -> {ok, rsa}; + {_, {ok, _}} -> {ok, dsa}; + {Error, _} -> Error + end. + +remove_id_keys(Dir) -> + file:delete(filename:join(Dir, "id_rsa")), + file:delete(filename:join(Dir, "id_dsa")). + +copyfile(SrcDir, DstDir, Fn) -> + file:copy(filename:join(SrcDir, Fn), + filename:join(DstDir, Fn)). + +failfun(_User, {authmethod,none}) -> + ok; +failfun(User, Reason) -> + error_logger:format("~p failed XXX to login: ~p~n", [User, Reason]). + +hostname() -> + {ok,Host} = inet:gethostname(), + Host. + +known_hosts(BR) -> + KnownHosts = ssh_file:file_name(user, "known_hosts", []), + B = KnownHosts ++ "xxx", + case BR of + backup -> + file:rename(KnownHosts, B); + restore -> + file:delete(KnownHosts), + file:rename(B, KnownHosts) + end. + + +get_user_dir() -> + case os:type() of + {win32, _} -> + [{user_dir, filename:join([os:getenv("HOME"), ".ssh"])}]; + _ -> + [] + end. + + +make_dsa_cert_files(Config) -> + make_dsa_cert_files("", Config). + +make_dsa_cert_files(RoleStr, Config) -> + + CaInfo = {CaCert, _} = make_cert([{key, dsa}]), + {Cert, CertKey} = make_cert([{key, dsa}, {issuer, CaInfo}]), + CaCertFile = filename:join([?config(data_dir, Config), + RoleStr, "dsa_cacerts.pem"]), + CertFile = filename:join([?config(data_dir, Config), + RoleStr, "dsa_cert.pem"]), + KeyFile = filename:join([?config(data_dir, Config), + RoleStr, "dsa_key.pem"]), + + der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]), + der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]), + der_to_pem(KeyFile, [CertKey]), + {CaCertFile, CertFile, KeyFile}. + +make_dsa_files(Config) -> + make_dsa_files(Config, rfc4716_public_key). +make_dsa_files(Config, Type) -> + {DSA, EncodedKey} = ssh_test_lib:gen_dsa(128, 20), + PKey = DSA#'DSAPrivateKey'.y, + P = DSA#'DSAPrivateKey'.p, + Q = DSA#'DSAPrivateKey'.q, + G = DSA#'DSAPrivateKey'.g, + Dss = #'Dss-Parms'{p=P, q=Q, g=G}, + {ok, Hostname} = inet:gethostname(), + {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet), + IP = lists:concat([A, ".", B, ".", C, ".", D]), + Attributes = [], % Could be [{comment,"user@" ++ Hostname}], + HostNames = [{hostnames,[IP, IP]}], + PublicKey = [{{PKey, Dss}, Attributes}], + KnownHosts = [{{PKey, Dss}, HostNames}], + + KnownHostsEnc = public_key:ssh_encode(KnownHosts, known_hosts), + KnownHosts = public_key:ssh_decode(KnownHostsEnc, known_hosts), + + PublicKeyEnc = public_key:ssh_encode(PublicKey, Type), +% PublicKey = public_key:ssh_decode(PublicKeyEnc, Type), + + SystemTmpDir = ?config(data_dir, Config), + filelib:ensure_dir(SystemTmpDir), + file:make_dir(SystemTmpDir), + + DSAFile = filename:join(SystemTmpDir, "ssh_host_dsa_key.pub"), + file:delete(DSAFile), + + DSAPrivateFile = filename:join(SystemTmpDir, "ssh_host_dsa_key"), + file:delete(DSAPrivateFile), + + KHFile = filename:join(SystemTmpDir, "known_hosts"), + file:delete(KHFile), + + PemBin = public_key:pem_encode([EncodedKey]), + + file:write_file(DSAFile, PublicKeyEnc), + file:write_file(KHFile, KnownHostsEnc), + file:write_file(DSAPrivateFile, PemBin), + ok. + +%%-------------------------------------------------------------------- +%% Create and return a der encoded certificate +%% Option Default +%% ------------------------------------------------------- +%% digest sha1 +%% validity {date(), date() + week()} +%% version 3 +%% subject [] list of the following content +%% {name, Name} +%% {email, Email} +%% {city, City} +%% {state, State} +%% {org, Org} +%% {org_unit, OrgUnit} +%% {country, Country} +%% {serial, Serial} +%% {title, Title} +%% {dnQualifer, DnQ} +%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created) +%% (obs IssuerKey migth be {Key, Password} +%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key +%% +%% +%% (OBS: The generated keys are for testing only) +%% make_cert([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()} +%%-------------------------------------------------------------------- +make_cert(Opts) -> + SubjectPrivateKey = get_key(Opts), + {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts), + Cert = public_key:pkix_sign(TBSCert, IssuerKey), + true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok + {Cert, encode_key(SubjectPrivateKey)}. + +%%-------------------------------------------------------------------- +%% Writes cert files in Dir with FileName and FileName ++ Suffix +%% write_cert(::string(), ::string(), {Cert,Key}) -> ok +%%-------------------------------------------------------------------- +write_cert(Dir, FileName, Suffix, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) -> + ok = der_to_pem(filename:join(Dir, FileName), + [{'Certificate', Cert, not_encrypted}]), + ok = der_to_pem(filename:join(Dir, FileName ++ Suffix), [Key]). + +%%-------------------------------------------------------------------- +%% Creates a rsa key (OBS: for testing only) +%% the size are in bytes +%% gen_rsa(::integer()) -> {::atom(), ::binary(), ::opaque()} +%%-------------------------------------------------------------------- +gen_rsa(Size) when is_integer(Size) -> + Key = gen_rsa2(Size), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- +%% Creates a dsa key (OBS: for testing only) +%% the sizes are in bytes +%% gen_dsa(::integer()) -> {::atom(), ::binary(), ::opaque()} +%%-------------------------------------------------------------------- +gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> + Key = gen_dsa2(LSize, NSize), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- +%% Verifies cert signatures +%% verify_signature(::binary(), ::tuple()) -> ::boolean() +%%-------------------------------------------------------------------- +verify_signature(DerEncodedCert, DerKey, _KeyParams) -> + Key = decode_key(DerKey), + case Key of + #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} -> + public_key:pkix_verify(DerEncodedCert, + #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}); + #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> + public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_key(Opts) -> + case proplists:get_value(key, Opts) of + undefined -> make_key(rsa, Opts); + rsa -> make_key(rsa, Opts); + dsa -> make_key(dsa, Opts); + Key -> + Password = proplists:get_value(password, Opts, no_passwd), + decode_key(Key, Password) + end. + +decode_key({Key, Pw}) -> + decode_key(Key, Pw); +decode_key(Key) -> + decode_key(Key, no_passwd). + + +decode_key(#'RSAPublicKey'{} = Key,_) -> + Key; +decode_key(#'RSAPrivateKey'{} = Key,_) -> + Key; +decode_key(#'DSAPrivateKey'{} = Key,_) -> + Key; +decode_key(PemEntry = {_,_,_}, Pw) -> + public_key:pem_entry_decode(PemEntry, Pw); +decode_key(PemBin, Pw) -> + [KeyInfo] = public_key:pem_decode(PemBin), + decode_key(KeyInfo, Pw). + +encode_key(Key = #'RSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), + {'RSAPrivateKey', list_to_binary(Der), not_encrypted}; +encode_key(Key = #'DSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), + {'DSAPrivateKey', list_to_binary(Der), not_encrypted}. + +make_tbs(SubjectKey, Opts) -> + Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), + + IssuerProp = proplists:get_value(issuer, Opts, true), + {Issuer, IssuerKey} = issuer(IssuerProp, Opts, SubjectKey), + + {Algo, Parameters} = sign_algorithm(IssuerKey, Opts), + + SignAlgo = #'SignatureAlgorithm'{algorithm = Algo, + parameters = Parameters}, + Subject = case IssuerProp of + true -> %% Is a Root Ca + Issuer; + _ -> + subject(proplists:get_value(subject, Opts),false) + end, + + {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1, + signature = SignAlgo, + issuer = Issuer, + validity = validity(Opts), + subject = Subject, + subjectPublicKeyInfo = publickey(SubjectKey), + version = Version, + extensions = extensions(Opts) + }, IssuerKey}. + +issuer(true, Opts, SubjectKey) -> + %% Self signed + {subject(proplists:get_value(subject, Opts), true), SubjectKey}; +issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) -> + {issuer_der(Issuer), decode_key(IssuerKey)}; +issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) -> + {ok, [{cert, Cert, _}|_]} = pem_to_der(File), + {issuer_der(Cert), decode_key(IssuerKey)}. + +issuer_der(Issuer) -> + Decoded = public_key:pkix_decode_cert(Issuer, otp), + #'OTPCertificate'{tbsCertificate=Tbs} = Decoded, + #'OTPTBSCertificate'{subject=Subject} = Tbs, + Subject. + +subject(undefined, IsRootCA) -> + User = if IsRootCA -> "RootCA"; true -> os:getenv("USER") end, + Opts = [{email, User ++ "@erlang.org"}, + {name, User}, + {city, "Stockholm"}, + {country, "SE"}, + {org, "erlang"}, + {org_unit, "testing dep"}], + subject(Opts); +subject(Opts, _) -> + subject(Opts). + +subject(SubjectOpts) when is_list(SubjectOpts) -> + Encode = fun(Opt) -> + {Type,Value} = subject_enc(Opt), + [#'AttributeTypeAndValue'{type=Type, value=Value}] + end, + {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}. + +%% Fill in the blanks +subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}}; +subject_enc({email, Email}) -> {?'id-emailAddress', Email}; +subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}}; +subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}}; +subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}}; +subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}}; +subject_enc({country, Country}) -> {?'id-at-countryName', Country}; +subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial}; +subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}}; +subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ}; +subject_enc(Other) -> Other. + + +extensions(Opts) -> + case proplists:get_value(extensions, Opts, []) of + false -> + asn1_NOVALUE; + Exts -> + lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)]) + end. + +default_extensions(Exts) -> + Def = [{key_usage,undefined}, + {subject_altname, undefined}, + {issuer_altname, undefined}, + {basic_constraints, default}, + {name_constraints, undefined}, + {policy_constraints, undefined}, + {ext_key_usage, undefined}, + {inhibit_any, undefined}, + {auth_key_id, undefined}, + {subject_key_id, undefined}, + {policy_mapping, undefined}], + Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end, + Exts ++ lists:foldl(Filter, Def, Exts). + +extension({_, undefined}) -> []; +extension({basic_constraints, Data}) -> + case Data of + default -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true}, + critical=true}; + false -> + []; + Len when is_integer(Len) -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len}, + critical=true}; + _ -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = Data} + end; +extension({Id, Data, Critical}) -> + #'Extension'{extnID = Id, extnValue = Data, critical = Critical}. + + +publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> + Public = #'RSAPublicKey'{modulus=N, publicExponent=E}, + Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = Public}; +publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', + parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}. + +validity(Opts) -> + DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1), + DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7), + {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}), + Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end, + #'Validity'{notBefore={generalTime, Format(DefFrom)}, + notAfter ={generalTime, Format(DefTo)}}. + +sign_algorithm(#'RSAPrivateKey'{}, Opts) -> + Type = case proplists:get_value(digest, Opts, sha1) of + sha1 -> ?'sha1WithRSAEncryption'; + sha512 -> ?'sha512WithRSAEncryption'; + sha384 -> ?'sha384WithRSAEncryption'; + sha256 -> ?'sha256WithRSAEncryption'; + md5 -> ?'md5WithRSAEncryption'; + md2 -> ?'md2WithRSAEncryption' + end, + {Type, 'NULL'}; +sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> + {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}. + +make_key(rsa, _Opts) -> + %% (OBS: for testing only) + gen_rsa2(64); +make_key(dsa, _Opts) -> + gen_dsa2(128, 20). %% Bytes i.e. {1024, 160} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RSA key generation (OBS: for testing only) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53, + 47,43,41,37,31,29,23,19,17,13,11,7,5,3]). + +gen_rsa2(Size) -> + P = prime(Size), + Q = prime(Size), + N = P*Q, + Tot = (P - 1) * (Q - 1), + [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES), + {D1,D2} = extended_gcd(E, Tot), + D = erlang:max(D1,D2), + case D < E of + true -> + gen_rsa2(Size); + false -> + {Co1,Co2} = extended_gcd(Q, P), + Co = erlang:max(Co1,Co2), + #'RSAPrivateKey'{version = 'two-prime', + modulus = N, + publicExponent = E, + privateExponent = D, + prime1 = P, + prime2 = Q, + exponent1 = D rem (P-1), + exponent2 = D rem (Q-1), + coefficient = Co + } + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% DSA key generation (OBS: for testing only) +%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm +%% and the fips_186-3.pdf +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_dsa2(LSize, NSize) -> + Q = prime(NSize), %% Choose N-bit prime Q + X0 = prime(LSize), + P0 = prime((LSize div 2) +1), + + %% Choose L-bit prime modulus P such that p-1 is a multiple of q. + case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of + error -> + gen_dsa2(LSize, NSize); + P -> + G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. + %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used. + + X = prime(20), %% Choose x by some random method, where 0 < x < q. + Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. + + #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} + end. + +%% See fips_186-3.pdf +dsa_search(T, P0, Q, Iter) when Iter > 0 -> + P = 2*T*Q*P0 + 1, + case is_prime(crypto:mpint(P), 50) of + true -> P; + false -> dsa_search(T+1, P0, Q, Iter-1) + end; +dsa_search(_,_,_,_) -> + error. + + +%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +prime(ByteSize) -> + Rand = odd_rand(ByteSize), + crypto:erlint(prime_odd(Rand, 0)). + +prime_odd(Rand, N) -> + case is_prime(Rand, 50) of + true -> + Rand; + false -> + NotPrime = crypto:erlint(Rand), + prime_odd(crypto:mpint(NotPrime+2), N+1) + end. + +%% see http://en.wikipedia.org/wiki/Fermat_primality_test +is_prime(_, 0) -> true; +is_prime(Candidate, Test) -> + CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate), + case crypto:mod_exp(CoPrime, Candidate, Candidate) of + CoPrime -> is_prime(Candidate, Test-1); + _ -> false + end. + +odd_rand(Size) -> + Min = 1 bsl (Size*8-1), + Max = (1 bsl (Size*8))-1, + odd_rand(crypto:mpint(Min), crypto:mpint(Max)). + +odd_rand(Min,Max) -> + Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max), + BitSkip = (Sz+4)*8-1, + case Rand of + Odd = <<_:BitSkip, 1:1>> -> Odd; + Even = <<_:BitSkip, 0:1>> -> + crypto:mpint(crypto:erlint(Even)+1) + end. + +extended_gcd(A, B) -> + case A rem B of + 0 -> + {0, 1}; + N -> + {X, Y} = extended_gcd(B, N), + {Y, X-Y*(A div B)} + end. + +pem_to_der(File) -> + {ok, PemBin} = file:read_file(File), + public_key:pem_decode(PemBin). + +der_to_pem(File, Entries) -> + PemBin = public_key:pem_encode(Entries), + file:write_file(File, PemBin). diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl new file mode 100644 index 0000000000..f959d50484 --- /dev/null +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -0,0 +1,458 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(ssh_to_openssh_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include("test_server_line.hrl"). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-define(TIMEOUT, 50000). +-define(SSH_DEFAULT_PORT, 22). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initialization before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + case catch crypto:start() of + ok -> + ssh_test_lib:make_dsa_files(Config), + Config; + _Else -> + {skip,"Could not start crypto!"} + end. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + crypto:stop(), + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initialization before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initialization before each test case +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + ssh:start(), + Config. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ssh:stop(), + ok. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all() -> + case os:find_executable("ssh") of + false -> + {skip, "openSSH not installed on host"}; + _ -> + [erlang_shell_client_openssh_server, + erlang_client_openssh_server_exec, + erlang_client_openssh_server_exec_compressed, + erlang_server_openssh_client_exec, + erlang_server_openssh_client_exec_compressed, + erlang_client_openssh_server_setenv, + erlang_client_openssh_server_publickey_rsa, + erlang_client_openssh_server_publickey_dsa, + erlang_server_openssh_client_pulic_key_dsa, + erlang_client_openssh_server_password] + end. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% TEST cases starts here. +%%-------------------------------------------------------------------- +erlang_shell_client_openssh_server(doc) -> + ["Test that ssh:shell/2 works"]; + +erlang_shell_client_openssh_server(suite) -> + []; + +erlang_shell_client_openssh_server(Config) when is_list(Config) -> + process_flag(trap_exit, true), + IO = ssh_test_lib:start_io_server(), + Shell = ssh_test_lib:start_shell(?SSH_DEFAULT_PORT, IO), + IO ! {input, self(), "echo Hej\n"}, + receive_hej(), + IO ! {input, self(), "exit\n"}, + receive + <<"logout">> -> + receive + <<"Connection closed">> -> + ok + end; + Other0 -> + test_server:fail({unexpected_msg, Other0}) + end, + receive + {'EXIT', Shell, normal} -> + ok; + Other1 -> + test_server:fail({unexpected_msg, Other1}) + end. + +%-------------------------------------------------------------------- +erlang_client_openssh_server_exec(doc) -> + ["Test api function ssh_connection:exec"]; + +erlang_client_openssh_server_exec(suite) -> + []; + +erlang_client_openssh_server_exec(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId0, + "echo testing", infinity), + Data0 = {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}}, + case ssh_test_lib:receive_exec_result(Data0) of + expected -> + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0); + {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} + = ExitStatus0} -> + test_server:format("0: Collected data ~p", [ExitStatus0]), + ssh_test_lib:receive_exec_result(Data0, + ConnectionRef, ChannelId0); + Other0 -> + test_server:fail(Other0) + end, + + {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId1, + "echo testing1", infinity), + Data1 = {ssh_cm, ConnectionRef, {data, ChannelId1, 0, <<"testing1\n">>}}, + case ssh_test_lib:receive_exec_result(Data1) of + expected -> + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1); + {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId1, 0}} + = ExitStatus1} -> + test_server:format("0: Collected data ~p", [ExitStatus1]), + ssh_test_lib:receive_exec_result(Data1, + ConnectionRef, ChannelId1); + Other1 -> + test_server:fail(Other1) + end. + +%%-------------------------------------------------------------------- +erlang_client_openssh_server_exec_compressed(doc) -> + ["Test that compression option works"]; + +erlang_client_openssh_server_exec_compressed(suite) -> + []; + +erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}, + {compression, zlib}]), + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId, + "echo testing", infinity), + Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}}, + case ssh_test_lib:receive_exec_result(Data) of + expected -> + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId); + {unexpected_msg,{ssh_cm, ConnectionRef, + {exit_status, ChannelId, 0}} = ExitStatus} -> + test_server:format("0: Collected data ~p", [ExitStatus]), + ssh_test_lib:receive_exec_result(Data, ConnectionRef, ChannelId); + Other -> + test_server:fail(Other) + end. + +%%-------------------------------------------------------------------- +erlang_server_openssh_client_exec(doc) -> + ["Test that exec command works."]; + +erlang_server_openssh_client_exec(suite) -> + []; + +erlang_server_openssh_client_exec(Config) when is_list(Config) -> + SystemDir = ?config(data_dir, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + + + test_server:sleep(500), + + Cmd = "ssh -p " ++ integer_to_list(Port) ++ + " -o StrictHostKeyChecking=no "++ Host ++ " 1+1.", + SshPort = open_port({spawn, Cmd}, [binary]), + + receive + {SshPort,{data, <<"2\n">>}} -> + ok + after ?TIMEOUT -> + test_server:fail("Did not receive answer") + + end, + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +erlang_server_openssh_client_exec_compressed(doc) -> + ["Test that exec command works."]; + +erlang_server_openssh_client_exec_compressed(suite) -> + []; + +erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) -> + SystemDir = ?config(data_dir, Config), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {compression, zlib}, + {failfun, fun ssh_test_lib:failfun/2}]), + + test_server:sleep(500), + + Cmd = "ssh -p " ++ integer_to_list(Port) ++ + " -o StrictHostKeyChecking=no -C "++ Host ++ " 1+1.", + SshPort = open_port({spawn, Cmd}, [binary]), + + receive + {SshPort,{data, <<"2\n">>}} -> + ok + after ?TIMEOUT -> + test_server:fail("Did not receive answer") + + end, + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +erlang_client_openssh_server_setenv(doc) -> + ["Test api function ssh_connection:setenv"]; + +erlang_client_openssh_server_setenv(suite) -> + []; + +erlang_client_openssh_server_setenv(Config) when is_list(Config) -> + ConnectionRef = + ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId} = + ssh_connection:session_channel(ConnectionRef, infinity), + Env = case ssh_connection:setenv(ConnectionRef, ChannelId, + "ENV_TEST", "testing_setenv", + infinity) of + success -> + <<"tesing_setenv\n">>; + failure -> + <<"\n">> + end, + success = ssh_connection:exec(ConnectionRef, ChannelId, + "echo $ENV_TEST", infinity), + Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, Env}}, + case ssh_test_lib:receive_exec_result(Data) of + expected -> + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId); + {unexpected_msg,{ssh_cm, ConnectionRef, + {data,0,1, UnxpectedData}}} -> + %% Some os may return things as + %% ENV_TEST: Undefined variable.\n" + test_server:format("UnxpectedData: ~p", [UnxpectedData]), + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId); + {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId, 0}} + = ExitStatus} -> + test_server:format("0: Collected data ~p", [ExitStatus]), + ssh_test_lib:receive_exec_result(Data, + ConnectionRef, ChannelId); + Other -> + test_server:fail(Other) + end. + +%%-------------------------------------------------------------------- + +%% setenv not meaningfull on erlang ssh daemon! + +%%-------------------------------------------------------------------- +erlang_client_openssh_server_publickey_rsa(doc) -> + ["Validate using rsa publickey."]; +erlang_client_openssh_server_publickey_rsa(suite) -> + []; +erlang_client_openssh_server_publickey_rsa(Config) when is_list(Config) -> + {ok,[[Home]]} = init:get_argument(home), + SrcDir = filename:join(Home, ".ssh"), + UserDir = ?config(priv_dir, Config), + case ssh_test_lib:copyfile(SrcDir, UserDir, "id_rsa") of + {ok, _} -> + ConnectionRef = + ssh_test_lib:connect(?SSH_DEFAULT_PORT, + [{user_dir, UserDir}, + {public_key_alg, ssh_rsa}, + {user_interaction, false}, + silently_accept_hosts]), + {ok, Channel} = + ssh_connection:session_channel(ConnectionRef, infinity), + ok = ssh_connection:close(ConnectionRef, Channel), + ok = ssh:close(ConnectionRef), + ok = file:delete(filename:join(UserDir, "id_rsa")); + {error, enoent} -> + {skip, "no ~/.ssh/id_rsa"} + end. + +%%-------------------------------------------------------------------- +erlang_client_openssh_server_publickey_dsa(doc) -> + ["Validate using dsa publickey."]; +erlang_client_openssh_server_publickey_dsa(suite) -> + []; +erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) -> + {ok,[[Home]]} = init:get_argument(home), + SrcDir = filename:join(Home, ".ssh"), + UserDir = ?config(priv_dir, Config), + case ssh_test_lib:copyfile(SrcDir, UserDir, "id_dsa") of + {ok, _} -> + ConnectionRef = + ssh_test_lib:connect(?SSH_DEFAULT_PORT, + [{user_dir, UserDir}, + {public_key_alg, ssh_dsa}, + {user_interaction, false}, + silently_accept_hosts]), + {ok, Channel} = + ssh_connection:session_channel(ConnectionRef, infinity), + ok = ssh_connection:close(ConnectionRef, Channel), + ok = ssh:close(ConnectionRef), + ok = file:delete(filename:join(UserDir, "id_dsa")); + {error, enoent} -> + {skip, "no ~/.ssh/id_dsa"} + end. + +%%-------------------------------------------------------------------- +erlang_server_openssh_client_pulic_key_dsa(doc) -> + ["Validate using dsa publickey."]; + +erlang_server_openssh_client_pulic_key_dsa(suite) -> + []; + +erlang_server_openssh_client_pulic_key_dsa(Config) when is_list(Config) -> + SystemDir = ?config(data_dir, Config), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {public_key_alg, ssh_dsa}, + {failfun, fun ssh_test_lib:failfun/2}]), + + test_server:sleep(500), + + Cmd = "ssh -p " ++ integer_to_list(Port) ++ + " -o StrictHostKeyChecking=no "++ Host ++ " 1+1.", + SshPort = open_port({spawn, Cmd}, [binary]), + + receive + {SshPort,{data, <<"2\n">>}} -> + ok + after ?TIMEOUT -> + test_server:fail("Did not receive answer") + + end, + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +erlang_client_openssh_server_password(doc) -> + ["Test client password option"]; + +erlang_client_openssh_server_password(suite) -> + []; + +erlang_client_openssh_server_password(Config) when is_list(Config) -> + %% to make sure we don't public-key-auth + UserDir = ?config(data_dir, Config), + {error, Reason0} = + ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + + test_server:format("Test of user foo that does not exist. " + "Error msg: ~p~n", [Reason0]), + + User = string:strip(os:cmd("whoami"), right, $\n), + + case length(string:tokens(User, " ")) of + 1 -> + {error, Reason1} = + ssh_test_lib:connect(?SSH_DEFAULT_PORT, + [{silently_accept_hosts, true}, + {user, User}, + {password, "foo"}, + {user_interaction, false}, + {user_dir, UserDir}]), + test_server:format("Test of wrong Pasword. " + "Error msg: ~p~n", [Reason1]); + _ -> + test_server:format("Whoami failed reason: ~n", []) + end. + +%%-------------------------------------------------------------------- +% +%% Not possible to send password with openssh without user interaction +%% +%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +receive_hej() -> + receive + <<"Hej\n">> = Hej-> + test_server:format("Expected result: ~p~n", [Hej]); + Info -> + test_server:format("Extra info: ~p~n", [Info]), + receive_hej() + end. diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index d0861b3ddc..d79038df29 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 2.0.6 +SSH_VSN = 2.0.7 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index 8c9e08ac3a..18867cfb68 100644 --- a/lib/stdlib/doc/src/re.xml +++ b/lib/stdlib/doc/src/re.xml @@ -94,7 +94,7 @@ <type> <v>Regexp = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> <v>Options = [ Option ]</v> - <v>Option = unicode | anchored | caseless | dollar_endonly | dotall | extended | firstline | multiline | no_auto_capture | dupnames | ungreedy | {newline, NLSpec}| bsr_anycrlf | bsr_unicode</v> + <v>Option = <seealso marker="#type-compile_option">compile_option()</seealso></v> <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v> <v>MP = <seealso marker="#type-mp">mp()</seealso></v> <v>ErrSpec = {ErrString, Position}</v> @@ -170,7 +170,7 @@ This option makes it possible to include comments inside complicated patterns. N <fsummary>Match a subject against regular expression and capture subpatterns</fsummary> <type> <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> - <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> + <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata()</v> <v>Captured = [ CaptureData ]</v> <v>CaptureData = {integer(),integer()}</v> </type> diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index 02dbe60741..e08258a535 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -46,7 +46,7 @@ split(Subject,RE) -> -spec split(Subject, RE, Options) -> SplitList when Subject :: iodata() | unicode:charlist(), - RE :: mp() | iodata(), + RE :: mp() | iodata() | unicode:charlist(), Options :: [ Option ], Option :: anchored | global | notbol | noteol | notempty | {offset, non_neg_integer()} | {newline, nl_spec()} @@ -238,7 +238,7 @@ replace(Subject,RE,Replacement) -> -spec replace(Subject, RE, Replacement, Options) -> iodata() | unicode:charlist() when Subject :: iodata() | unicode:charlist(), - RE :: mp() | iodata(), + RE :: mp() | iodata() | unicode:charlist(), Replacement :: iodata() | unicode:charlist(), Options :: [Option], Option :: anchored | global | notbol | noteol | notempty diff --git a/lib/wx/src/Makefile b/lib/wx/src/Makefile index 3cc668375f..46bc06271c 100644 --- a/lib/wx/src/Makefile +++ b/lib/wx/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2010. All Rights Reserved. +# Copyright Ericsson AB 2008-2011. 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 @@ -29,8 +29,7 @@ ERL_COMPILE_FLAGS += -I$(ERLINC) +warn_unused_vars ARCHIVE = wx-$(VSN).ez -ErlMods = \ - wx \ +ErlMods = wx \ wx_object \ wxe_master \ wxe_server \ @@ -44,7 +43,7 @@ GEN_FILES = $(wildcard gen/wx*.erl) \ gen/glu.erl \ gen/gl.erl -GEN_MODS = $(GEN_FILES:gen/%.erl= %,\n ) +GEN_MODS = $(GEN_FILES:gen/%.erl=%,) GEN_HRL = \ $(EGEN)/wxe_debug.hrl \ diff --git a/lib/wx/src/wx.appup.src b/lib/wx/src/wx.appup.src index c02edd2afb..1102af612e 100644 --- a/lib/wx/src/wx.appup.src +++ b/lib/wx/src/wx.appup.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2011. 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 @@ -18,5 +18,6 @@ %% %CopyrightEnd% {"%VSN%", + [ ], [ ] }. |