summaryrefslogtreecommitdiffstats
path: root/source/libsmb/clirap.c
diff options
context:
space:
mode:
authorJames Peach <jpeach@samba.org>2007-12-09 14:18:54 -0800
committerJames Peach <jpeach@samba.org>2007-12-09 14:18:54 -0800
commit40c26d55736ae08934e18c27168fff10dd15442f (patch)
tree53c55039224392516aac8ffe33ae5f957a455a26 /source/libsmb/clirap.c
parent5c347cb46d85d04bbba7c99dca7ff9628f977d84 (diff)
downloadsamba-40c26d55736ae08934e18c27168fff10dd15442f.tar.gz
samba-40c26d55736ae08934e18c27168fff10dd15442f.tar.xz
samba-40c26d55736ae08934e18c27168fff10dd15442f.zip
Support fetching very long server lists with RAP_NetServerEnum3.
Use the RAP_NetServerEnum3 server list continuation API for retrieving server lists that are too long to fit in a single reply. Patch from George Colley <gcolley@apple.com>.
Diffstat (limited to 'source/libsmb/clirap.c')
-rw-r--r--source/libsmb/clirap.c167
1 files changed, 125 insertions, 42 deletions
diff --git a/source/libsmb/clirap.c b/source/libsmb/clirap.c
index 410b7cb1383..54504f608da 100644
--- a/source/libsmb/clirap.c
+++ b/source/libsmb/clirap.c
@@ -3,6 +3,7 @@
client RAP calls
Copyright (C) Andrew Tridgell 1994-1998
Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) James Peach 2007
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
@@ -244,57 +245,118 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
{
char *rparam = NULL;
char *rdata = NULL;
+ char *rdata_end = NULL;
unsigned int rdrcnt,rprcnt;
char *p;
char param[1024];
int uLevel = 1;
- int count = -1;
size_t len;
+ uint32 func = RAP_NetServerEnum2;
+ char *last_entry = NULL;
+ int total_cnt = 0;
+ int return_cnt = 0;
+ int res;
errno = 0; /* reset */
- /* send a SMBtrans command with api NetServerEnum */
- p = param;
- SSVAL(p,0,0x68); /* api number */
- p += 2;
- strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param));
- p = skip_string(param,sizeof(param),p);
+ /*
+ * This may take more than one transaction, so we should loop until
+ * we no longer get a more data to process or we have all of the
+ * items.
+ */
+ do {
+ /* send a SMBtrans command with api NetServerEnum */
+ p = param;
+ SIVAL(p,0,func); /* api number */
+ p += 2;
+ /* Next time through we need to use the continue api */
+ func = RAP_NetServerEnum3;
+
+ if (last_entry) {
+ strlcpy(p,"WrLehDOz", sizeof(param)-PTR_DIFF(p,param));
+ } else {
+ strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param));
+ }
- strlcpy(p,"B16BBDz", sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param, sizeof(param), p);
+ strlcpy(p,"B16BBDz", sizeof(param)-PTR_DIFF(p,param));
+
+ p = skip_string(param, sizeof(param), p);
+ SSVAL(p,0,uLevel);
+ SSVAL(p,2,CLI_BUFFER_SIZE);
+ p += 4;
+ SIVAL(p,0,stype);
+ p += 4;
+
+ /* If we have more data, tell the server where
+ * to continue from.
+ */
+ len = push_ascii(p,
+ last_entry ? last_entry : workgroup,
+ sizeof(param) - PTR_DIFF(p,param) - 1,
+ STR_TERMINATE|STR_UPPER);
+
+ if (len == (size_t)-1) {
+ return false;
+ }
+ p += len;
- p = skip_string(param,sizeof(param),p);
- SSVAL(p,0,uLevel);
- SSVAL(p,2,CLI_BUFFER_SIZE);
- p += 4;
- SIVAL(p,0,stype);
- p += 4;
+ if (!cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt)) { /* return data, return size */
- len = push_ascii(p, workgroup, sizeof(param)-PTR_DIFF(p,param)-1,
- STR_TERMINATE|STR_UPPER);
- if (len == (size_t)-1) {
- return false;
- }
- p += len;
+ /* break out of the loop on error */
+ res = -1;
+ break;
+ }
- if (cli_api(cli,
- param, PTR_DIFF(p,param), 8, /* params, length, max */
- NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
- &rparam, &rprcnt, /* return params, return size */
- &rdata, &rdrcnt /* return data, return size */
- )) {
- int res = rparam? SVAL(rparam,0) : -1;
- char *rdata_end = rdata + rdrcnt;
+ rdata_end = rdata + rdrcnt;
+ res = rparam ? SVAL(rparam,0) : -1;
if (res == 0 || res == ERRmoredata ||
(res != -1 && cli_errno(cli) == 0)) {
- int i;
+ char *sname = NULL;
+ int i, count;
int converter=SVAL(rparam,2);
- count=SVAL(rparam,4);
+ /* Get the number of items returned in this buffer */
+ count = SVAL(rparam, 4);
+
+ /* The next field contains the number of items left,
+ * including those returned in this buffer. So the
+ * first time through this should contain all of the
+ * entries.
+ */
+ if (total_cnt == 0) {
+ total_cnt = SVAL(rparam, 6);
+ }
+
+ /* Keep track of how many we have read */
+ return_cnt += count;
p = rdata;
- for (i = 0;i < count;i++, p += 26) {
- char *sname;
+ /* The last name in the previous NetServerEnum reply is
+ * sent back to server in the NetServerEnum3 request
+ * (last_entry). The next reply should repeat this entry
+ * as the first element. We have no proof that this is
+ * always true, but from traces that seems to be the
+ * behavior from Window Servers. So first lets do a lot
+ * of checking, just being paranoid. If the string
+ * matches then we already saw this entry so skip it.
+ *
+ * NOTE: sv1_name field must be null terminated and has
+ * a max size of 16 (NetBIOS Name).
+ */
+ if (last_entry && count && p &&
+ (strncmp(last_entry, p, 16) == 0)) {
+ count -= 1; /* Skip this entry */
+ return_cnt = -1; /* Not part of total, so don't count. */
+ p = rdata + 26; /* Skip the whole record */
+ }
+
+ for (i = 0; i < count; i++, p += 26) {
int comment_offset;
const char *cmnt;
const char *p1;
@@ -338,24 +400,45 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
fn(s1, stype, s2, state);
TALLOC_FREE(frame);
}
+
+ /* We are done with the old last entry, so now we can free it */
+ if (last_entry) {
+ SAFE_FREE(last_entry); /* This will set it to null */
+ }
+
+ /* We always make a copy of the last entry if we have one */
+ if (sname) {
+ last_entry = smb_xstrdup(sname);
+ }
+
+ /* If we have more data, but no last entry then error out */
+ if (!last_entry && (res == ERRmoredata)) {
+ errno = EINVAL;
+ res = 0;
+ }
+
}
- }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ } while ((res == ERRmoredata) && (total_cnt > return_cnt));
SAFE_FREE(rparam);
SAFE_FREE(rdata);
+ SAFE_FREE(last_entry);
- if (count < 0) {
- errno = cli_errno(cli);
+ if (res == -1) {
+ errno = cli_errno(cli);
} else {
- if (!count) {
- /* this is a very special case, when the domain master for the
- work group isn't part of the work group itself, there is something
- wild going on */
- errno = ENOENT;
+ if (!return_cnt) {
+ /* this is a very special case, when the domain master for the
+ work group isn't part of the work group itself, there is something
+ wild going on */
+ errno = ENOENT;
+ }
}
- }
- return(count > 0);
+ return(return_cnt > 0);
}
/****************************************************************************