From 3a6f761eb061d93837038f0aa75c2ffcb0ba3639 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 12 Jul 2004 16:35:48 +0000 Subject: r1470: Get the smb_trans2 structure out of the rap_cli_call struct. Initial attempt at RAP server infrastructure. Look at rap_server.c for the dummy functions that are supposed to implement the core functionality. ipc_rap.c contains all the data shuffling. _rap_shareenum and _rap_serverenum2 in ipc_rap.c are (I think) regular enough to be auto-generated. I did not test all the corner cases yet, but nevertheless I would like some comments on the general style. Volker P.S: samba-3 smbclient now doesn't freak out anymore, although the results are not entirely correct :-) (This used to be commit 08140cc1a838b4eaa23c897b280a46c95b7ef3e0) --- source4/include/rap.h | 2 +- source4/ntvfs/config.mk | 5 +- source4/ntvfs/ipc/ipc_rap.c | 463 +++++++++++++++++++++++++++++++++++++++++ source4/ntvfs/ipc/rap_server.c | 54 +++++ source4/ntvfs/ipc/vfs_ipc.c | 3 + source4/smb_server/trans2.c | 2 +- source4/torture/rap/rap.c | 35 ++-- source4/torture/torture.c | 1 + 8 files changed, 546 insertions(+), 19 deletions(-) create mode 100644 source4/ntvfs/ipc/ipc_rap.c create mode 100644 source4/ntvfs/ipc/rap_server.c (limited to 'source4') diff --git a/source4/include/rap.h b/source4/include/rap.h index 4fee8818db..c184be6483 100644 --- a/source4/include/rap.h +++ b/source4/include/rap.h @@ -71,7 +71,7 @@ struct rap_NetServerEnum2 { uint16 level; uint16 bufsize; uint32 servertype; - char *domain; + const char *domain; } in; struct { diff --git a/source4/ntvfs/config.mk b/source4/ntvfs/config.mk index 0feb380824..25dd51b487 100644 --- a/source4/ntvfs/config.mk +++ b/source4/ntvfs/config.mk @@ -32,7 +32,10 @@ INIT_OBJ_FILES = \ # Start MODULE ntvfs_ipc [MODULE::ntvfs_ipc] INIT_OBJ_FILES = \ - ntvfs/ipc/vfs_ipc.o + ntvfs/ipc/vfs_ipc.o \ + ntvfs/ipc/ipc_rap.o \ + ntvfs/ipc/rap_server.o + # End MODULE ntvfs_ipc ################################################ diff --git a/source4/ntvfs/ipc/ipc_rap.c b/source4/ntvfs/ipc/ipc_rap.c new file mode 100644 index 0000000000..d2cd0b38d5 --- /dev/null +++ b/source4/ntvfs/ipc/ipc_rap.c @@ -0,0 +1,463 @@ +/* + Unix SMB/CIFS implementation. + RAP handlers + + Copyright (C) Volker Lendecke 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#define NERR_Success 0 +#define NERR_badpass 86 +#define NERR_notsupported 50 + +struct rap_string_heap { + TALLOC_CTX *mem_ctx; + int offset; + int num_strings; + const char **strings; +}; + +struct rap_heap_save { + int offset, num_strings; +}; + +static void rap_heap_save(struct rap_string_heap *heap, + struct rap_heap_save *save) +{ + save->offset = heap->offset; + save->num_strings = heap->num_strings; +} + +static void rap_heap_restore(struct rap_string_heap *heap, + struct rap_heap_save *save) +{ + heap->offset = save->offset; + heap->num_strings = save->num_strings; +} + +struct rap_call { + TALLOC_CTX *mem_ctx; + uint16 callno; + const char *paramdesc; + const char *datadesc; + + uint16 status; + uint16 convert; + + uint16 rcv_paramlen, rcv_datalen; + + struct ndr_push *ndr_push_param; + struct ndr_push *ndr_push_data; + struct rap_string_heap *heap; + + struct ndr_pull *ndr_pull_param; + struct ndr_pull *ndr_pull_data; +}; + +#define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM); + +static struct rap_call *new_rap_srv_call(TALLOC_CTX *mem_ctx, + struct smb_trans2 *trans) +{ + struct rap_call *call; + + call = talloc_p(mem_ctx, struct rap_call); + + if (call == NULL) + return NULL; + + ZERO_STRUCTP(call); + + call->mem_ctx = mem_ctx; + + call->ndr_pull_param = ndr_pull_init_blob(&trans->in.params, mem_ctx); + call->ndr_pull_param->flags = RAPNDR_FLAGS; + + call->ndr_pull_data = ndr_pull_init_blob(&trans->in.data, mem_ctx); + call->ndr_pull_data->flags = RAPNDR_FLAGS; + + call->heap = talloc_p(mem_ctx, struct rap_string_heap); + + if (call->heap == NULL) + return NULL; + + ZERO_STRUCTP(call->heap); + + call->heap->mem_ctx = mem_ctx; + + return call; +} + +static NTSTATUS rap_srv_pull_word(struct rap_call *call, uint16 *result) +{ + if (*call->paramdesc++ != 'W') + return NT_STATUS_INVALID_PARAMETER; + + return ndr_pull_uint16(call->ndr_pull_param, result); +} + +static NTSTATUS rap_srv_pull_dword(struct rap_call *call, uint32 *result) +{ + if (*call->paramdesc++ != 'D') + return NT_STATUS_INVALID_PARAMETER; + + return ndr_pull_uint32(call->ndr_pull_param, result); +} + +static NTSTATUS rap_srv_pull_string(struct rap_call *call, const char **result) +{ + char paramdesc = *call->paramdesc++; + + if (paramdesc == 'O') { + *result = NULL; + return NT_STATUS_OK; + } + + if (paramdesc != 'z') + return NT_STATUS_INVALID_PARAMETER; + + return ndr_pull_string(call->ndr_pull_param, NDR_SCALARS, result); +} + +static NTSTATUS rap_srv_pull_bufsize(struct rap_call *call, uint16 *bufsize) +{ + NTSTATUS result; + + if ( (*call->paramdesc++ != 'r') || (*call->paramdesc++ != 'L') ) + return NT_STATUS_INVALID_PARAMETER; + + result = ndr_pull_uint16(call->ndr_pull_param, bufsize); + + if (!NT_STATUS_IS_OK(result)) + return result; + + call->heap->offset = *bufsize; + + return NT_STATUS_OK; +} + +static NTSTATUS rap_srv_pull_expect_multiple(struct rap_call *call) +{ + if ( (*call->paramdesc++ != 'e') || (*call->paramdesc++ != 'h') ) + return NT_STATUS_INVALID_PARAMETER; + + return NT_STATUS_OK; +} + +static NTSTATUS rap_push_string(struct ndr_push *data_push, + struct rap_string_heap *heap, + const char *str) +{ + size_t space; + + if (str == NULL) + str = ""; + + space = strlen(str)+1; + + if (heap->offset < space) + return NT_STATUS_BUFFER_TOO_SMALL; + + heap->offset -= space; + + NDR_CHECK(ndr_push_uint16(data_push, heap->offset)); + NDR_CHECK(ndr_push_uint16(data_push, 0)); + + heap->strings = talloc_realloc(heap->mem_ctx, heap->strings, + sizeof(*heap->strings) * + (heap->num_strings + 1)); + + if (heap->strings == NULL) + return NT_STATUS_NO_MEMORY; + + heap->strings[heap->num_strings] = str; + heap->num_strings += 1; + + return NT_STATUS_OK; +} + +#define NDR_OK(call) do { result = call; \ + if (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) \ + goto buffer_overflow; \ + if (!NT_STATUS_IS_OK(result)) \ + goto done; \ + } while (0) + +static NTSTATUS _rap_netshareenum(struct smbsrv_request *req, + struct rap_call *call) +{ + struct rap_NetShareEnum r; + NTSTATUS result; + + NDR_OK(rap_srv_pull_word(call, &r.in.level)); + NDR_OK(rap_srv_pull_bufsize(call, &r.in.bufsize)); + NDR_OK(rap_srv_pull_expect_multiple(call)); + + switch(r.in.level) { + case 0: + if (strcmp(call->datadesc, "B13") != 0) + return NT_STATUS_INVALID_PARAMETER; + break; + case 1: + if (strcmp(call->datadesc, "B13BWz") != 0) + return NT_STATUS_INVALID_PARAMETER; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + break; + } + + result = rap_netshareenum(req, &r); + + if (!NT_STATUS_IS_OK(result)) + return result; + + for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) { + + int i = r.out.count; + struct ndr_push_save data_save; + struct rap_heap_save heap_save; + + ndr_push_save(call->ndr_push_data, &data_save); + rap_heap_save(call->heap, &heap_save); + + switch(r.in.level) { + case 0: + NDR_OK(ndr_push_bytes(call->ndr_push_data, + r.out.info[i].info0.name, + sizeof(r.out.info[i].info0.name))); + break; + case 1: + NDR_OK(ndr_push_bytes(call->ndr_push_data, + r.out.info[i].info1.name, + sizeof(r.out.info[i].info1.name))); + NDR_OK(ndr_push_uint8(call->ndr_push_data, + r.out.info[i].info1.pad)); + NDR_OK(ndr_push_uint16(call->ndr_push_data, + r.out.info[i].info1.type)); + + NDR_OK(rap_push_string(call->ndr_push_data, + call->heap, + r.out.info[i].info1.comment)); + + break; + } + + if (call->ndr_push_data->offset > call->heap->offset) { + + buffer_overflow: + + ndr_push_restore(call->ndr_push_data, &data_save); + rap_heap_restore(call->heap, &heap_save); + break; + } + } + + call->status = r.out.status; + + NDR_CHECK(ndr_push_uint16(call->ndr_push_param, r.out.count)); + NDR_CHECK(ndr_push_uint16(call->ndr_push_param, r.out.available)); + + result = NT_STATUS_OK; + + done: + return result; +} + +static NTSTATUS _rap_netserverenum2(struct smbsrv_request *req, + struct rap_call *call) +{ + struct rap_NetServerEnum2 r; + NTSTATUS result; + + NDR_OK(rap_srv_pull_word(call, &r.in.level)); + NDR_OK(rap_srv_pull_bufsize(call, &r.in.bufsize)); + NDR_OK(rap_srv_pull_expect_multiple(call)); + NDR_OK(rap_srv_pull_dword(call, &r.in.servertype)); + NDR_OK(rap_srv_pull_string(call, &r.in.domain)); + + switch(r.in.level) { + case 0: + if (strcmp(call->datadesc, "B16") != 0) + return NT_STATUS_INVALID_PARAMETER; + break; + case 1: + if (strcmp(call->datadesc, "B16BBDz") != 0) + return NT_STATUS_INVALID_PARAMETER; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + break; + } + + result = rap_netserverenum2(req, &r); + + if (!NT_STATUS_IS_OK(result)) + return result; + + for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) { + + int i = r.out.count; + struct ndr_push_save data_save; + struct rap_heap_save heap_save; + + ndr_push_save(call->ndr_push_data, &data_save); + rap_heap_save(call->heap, &heap_save); + + switch(r.in.level) { + case 0: + NDR_OK(ndr_push_bytes(call->ndr_push_data, + r.out.info[i].info0.name, + sizeof(r.out.info[i].info0.name))); + break; + case 1: + NDR_OK(ndr_push_bytes(call->ndr_push_data, + r.out.info[i].info1.name, + sizeof(r.out.info[i].info1.name))); + NDR_OK(ndr_push_uint8(call->ndr_push_data, + r.out.info[i].info1.version_major)); + NDR_OK(ndr_push_uint8(call->ndr_push_data, + r.out.info[i].info1.version_minor)); + NDR_OK(ndr_push_uint32(call->ndr_push_data, + r.out.info[i].info1.servertype)); + + NDR_OK(rap_push_string(call->ndr_push_data, + call->heap, + r.out.info[i].info1.comment)); + + break; + } + + if (call->ndr_push_data->offset > call->heap->offset) { + + buffer_overflow: + + ndr_push_restore(call->ndr_push_data, &data_save); + rap_heap_restore(call->heap, &heap_save); + break; + } + } + + call->status = r.out.status; + + NDR_CHECK(ndr_push_uint16(call->ndr_push_param, r.out.count)); + NDR_CHECK(ndr_push_uint16(call->ndr_push_param, r.out.available)); + + result = NT_STATUS_OK; + + done: + return result; +} + +static NTSTATUS api_Unsupported(struct smbsrv_request *req, + struct rap_call *call) +{ + call->status = NERR_notsupported; + call->convert = 0; + return NT_STATUS_OK; +} + +#define RAP_NetShareEnum 0 +#define RAP_NetServerEnum2 104 + +static const struct +{ + const char *name; + int id; + NTSTATUS (*fn)(struct smbsrv_request *req, struct rap_call *call); +} api_commands[] = { + {"NetShareEnum", RAP_NetShareEnum, _rap_netshareenum }, + {"NetServerEnum2", RAP_NetServerEnum2, _rap_netserverenum2 }, + {NULL, -1, api_Unsupported} +}; + +NTSTATUS ipc_rap_call(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + int i; + NTSTATUS result; + struct rap_call *call; + DATA_BLOB result_param, result_data; + struct ndr_push *final_param; + struct ndr_push *final_data; + + call = new_rap_srv_call(req->mem_ctx, trans); + + if (call == NULL) + return NT_STATUS_NO_MEMORY; + + NDR_CHECK(ndr_pull_uint16(call->ndr_pull_param, &call->callno)); + NDR_CHECK(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS, + &call->paramdesc)); + NDR_CHECK(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS, + &call->datadesc)); + + call->ndr_push_param = ndr_push_init_ctx(req->mem_ctx); + call->ndr_push_data = ndr_push_init_ctx(req->mem_ctx); + + if ((call->ndr_push_param == NULL) || (call->ndr_push_data == NULL)) + return NT_STATUS_NO_MEMORY; + + call->ndr_push_param->flags = RAPNDR_FLAGS; + call->ndr_push_data->flags = RAPNDR_FLAGS; + + result = NT_STATUS_NOT_IMPLEMENTED; + + for (i=0; api_commands[i].name != NULL; i++) { + if (api_commands[i].id == call->callno) { + DEBUG(5, ("Running RAP call %s\n", + api_commands[i].name)); + result = api_commands[i].fn(req, call); + break; + } + } + + if (!NT_STATUS_IS_OK(result)) + return result; + + result_param = ndr_push_blob(call->ndr_push_param); + result_data = ndr_push_blob(call->ndr_push_data); + + final_param = ndr_push_init_ctx(req->mem_ctx); + final_data = ndr_push_init_ctx(req->mem_ctx); + + if ((final_param == NULL) || (final_data == NULL)) + return NT_STATUS_NO_MEMORY; + + final_param->flags = RAPNDR_FLAGS; + final_data->flags = RAPNDR_FLAGS; + + NDR_CHECK(ndr_push_uint16(final_param, call->status)); + NDR_CHECK(ndr_push_uint16(final_param, + call->heap->offset - result_data.length)); + NDR_CHECK(ndr_push_bytes(final_param, result_param.data, + result_param.length)); + + NDR_CHECK(ndr_push_bytes(final_data, result_data.data, + result_data.length)); + + for (i=call->heap->num_strings-1; i>=0; i--) + NDR_CHECK(ndr_push_string(final_data, NDR_SCALARS, + call->heap->strings[i])); + + trans->out.setup_count = 0; + trans->out.setup = NULL; + trans->out.params = ndr_push_blob(final_param); + trans->out.data = ndr_push_blob(final_data); + + return result; +} diff --git a/source4/ntvfs/ipc/rap_server.c b/source4/ntvfs/ipc/rap_server.c new file mode 100644 index 0000000000..4a7b2dd91b --- /dev/null +++ b/source4/ntvfs/ipc/rap_server.c @@ -0,0 +1,54 @@ +/* + Unix SMB/CIFS implementation. + RAP handlers + + Copyright (C) Volker Lendecke 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* At this moment these are just dummy functions, but you might get the + * idea. */ + +NTSTATUS rap_netshareenum(struct smbsrv_request *req, + struct rap_NetShareEnum *r) +{ + r->out.status = 0; + r->out.available = 2; + r->out.info = talloc_array_p(req->mem_ctx, + union rap_shareenum_info, 2); + + strncpy(r->out.info[0].info1.name, "C$", 12); + r->out.info[0].info1.pad = 0; + r->out.info[0].info1.type = 0; + r->out.info[0].info1.comment = talloc_strdup(req->mem_ctx, "Bla"); + + strncpy(r->out.info[1].info1.name, "IPC$", 12); + r->out.info[1].info1.pad = 0; + r->out.info[1].info1.type = 1; + r->out.info[1].info1.comment = talloc_strdup(req->mem_ctx, "Blub"); + + return NT_STATUS_OK; +} + +NTSTATUS rap_netserverenum2(struct smbsrv_request *req, + struct rap_NetServerEnum2 *r) +{ + r->out.status = 0; + r->out.available = 0; + return NT_STATUS_OK; +} diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c index f6a0015f1d..73b3de314d 100644 --- a/source4/ntvfs/ipc/vfs_ipc.c +++ b/source4/ntvfs/ipc/vfs_ipc.c @@ -667,6 +667,9 @@ static NTSTATUS ipc_trans(struct smbsrv_request *req, struct smb_trans2 *trans) { NTSTATUS status; + if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN")) + return ipc_rap_call(req, trans); + if (trans->in.setup_count != 2) { return NT_STATUS_INVALID_PARAMETER; } diff --git a/source4/smb_server/trans2.c b/source4/smb_server/trans2.c index 301a0221d0..dcb8bd1d56 100644 --- a/source4/smb_server/trans2.c +++ b/source4/smb_server/trans2.c @@ -1275,7 +1275,7 @@ void reply_trans_generic(struct smbsrv_request *req, uint8_t command) /* parse out the setup words */ trans.in.setup = talloc(req->mem_ctx, trans.in.setup_count * sizeof(uint16_t)); - if (!trans.in.setup) { + if (trans.in.setup_count && !trans.in.setup) { req_reply_error(req, NT_STATUS_NO_MEMORY); return; } diff --git a/source4/torture/rap/rap.c b/source4/torture/rap/rap.c index 65519bcce0..2ed069fd61 100644 --- a/source4/torture/rap/rap.c +++ b/source4/torture/rap/rap.c @@ -26,10 +26,10 @@ struct rap_call { char *paramdesc; const char *datadesc; - struct smb_trans2 trans; - uint16 status; uint16 convert; + + uint16 rcv_paramlen, rcv_datalen; struct ndr_push *ndr_push_param; struct ndr_push *ndr_push_data; @@ -55,7 +55,7 @@ static struct rap_call *new_rap_cli_call(uint16 callno) ZERO_STRUCTP(call); call->callno = callno; - call->trans.in.max_param = 4; /* uint16 error, uint16 "convert" */ + call->rcv_paramlen = 4; call->mem_ctx = mem_ctx; call->ndr_push_param = ndr_push_init_ctx(mem_ctx); @@ -102,14 +102,14 @@ static void rap_cli_push_rcvbuf(struct rap_call *call, int len) rap_cli_push_paramdesc(call, 'r'); rap_cli_push_paramdesc(call, 'L'); ndr_push_uint16(call->ndr_push_param, len); - call->trans.in.max_data = len; + call->rcv_datalen = len; } static void rap_cli_expect_multiple_entries(struct rap_call *call) { rap_cli_push_paramdesc(call, 'e'); rap_cli_push_paramdesc(call, 'h'); - call->trans.in.max_param += 4; /* uint16 entry count, uint16 total */ + call->rcv_paramlen += 4; /* uint16 entry count, uint16 total */ } static void rap_cli_push_string(struct rap_call *call, const char *str) @@ -161,6 +161,7 @@ static NTSTATUS rap_cli_do_call(struct cli_state *cli, TALLOC_CTX *mem_ctx, NTSTATUS result; DATA_BLOB param_blob; struct ndr_push *params; + struct smb_trans2 trans; params = ndr_push_init_ctx(mem_ctx); @@ -169,12 +170,14 @@ static NTSTATUS rap_cli_do_call(struct cli_state *cli, TALLOC_CTX *mem_ctx, params->flags = RAPNDR_FLAGS; - call->trans.in.max_setup = 0; - call->trans.in.flags = 0; - call->trans.in.timeout = 0; - call->trans.in.setup_count = 0; - call->trans.in.setup = NULL; - call->trans.in.trans_name = "\\PIPE\\LANMAN"; + trans.in.max_param = call->rcv_paramlen; + trans.in.max_data = call->rcv_datalen; + trans.in.max_setup = 0; + trans.in.flags = 0; + trans.in.timeout = 0; + trans.in.setup_count = 0; + trans.in.setup = NULL; + trans.in.trans_name = "\\PIPE\\LANMAN"; NDR_CHECK(ndr_push_uint16(params, call->callno)); NDR_CHECK(ndr_push_string(params, NDR_SCALARS, call->paramdesc)); @@ -184,19 +187,19 @@ static NTSTATUS rap_cli_do_call(struct cli_state *cli, TALLOC_CTX *mem_ctx, NDR_CHECK(ndr_push_bytes(params, param_blob.data, param_blob.length)); - call->trans.in.params = ndr_push_blob(params); - call->trans.in.data = data_blob(NULL, 0); + trans.in.params = ndr_push_blob(params); + trans.in.data = data_blob(NULL, 0); - result = smb_raw_trans(cli->tree, call->mem_ctx, &call->trans); + result = smb_raw_trans(cli->tree, call->mem_ctx, &trans); if (!NT_STATUS_IS_OK(result)) return result; - call->ndr_pull_param = ndr_pull_init_blob(&call->trans.out.params, + call->ndr_pull_param = ndr_pull_init_blob(&trans.out.params, call->mem_ctx); call->ndr_pull_param->flags = RAPNDR_FLAGS; - call->ndr_pull_data = ndr_pull_init_blob(&call->trans.out.data, + call->ndr_pull_data = ndr_pull_init_blob(&trans.out.data, call->mem_ctx); call->ndr_pull_data->flags = RAPNDR_FLAGS; diff --git a/source4/torture/torture.c b/source4/torture/torture.c index 0a74c75961..d48d10a504 100644 --- a/source4/torture/torture.c +++ b/source4/torture/torture.c @@ -4195,6 +4195,7 @@ static struct { {"RAW-CONTEXT", torture_raw_context, 0}, {"RAW-RENAME", torture_raw_rename, 0}, {"RAW-SEEK", torture_raw_seek, 0}, + {"RAW-RAP", torture_raw_rap, 0}, {"SCAN-TRANS2", torture_trans2_scan, 0}, {"SCAN-NTTRANS", torture_nttrans_scan, 0}, {"SCAN-ALIASES", torture_trans2_aliases, 0}, -- cgit