smbclient-add-rap_netserverenum3-support [plain text]
Index: samba/source/libsmb/clirap.c
===================================================================
--- samba/source/libsmb/clirap.c.orig
+++ samba/source/libsmb/clirap.c
@@ -3,7 +3,9 @@
client RAP calls
Copyright (C) Andrew Tridgell 1994-1998
Copyright (C) Gerald (Jerry) Carter 2004
-
+
+ Copyright (C) 2007 Apple Inc. All rights reserved.
+
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
@@ -216,56 +218,125 @@ BOOL cli_NetServerEnum(struct cli_state
char *p;
pstring param;
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;
- pstrcpy_base(p,"WrLehDz", param);
- p = skip_string(param,sizeof(param),p);
-
- pstrcpy_base(p,"B16BBDz", param);
+ /*
+ * 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;
- 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 (last_entry) {
+ pstrcpy_base(p,"WrLehDOz", param);
+ } else {
+ pstrcpy_base(p,"WrLehDz", param);
+ }
- len = push_ascii(p, workgroup, sizeof(pstring)-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);
+ pstrcpy_base(p,"B16BBDz", 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;
+
+ /* We have more data, tell the server were to continue from */
+ if (last_entry) {
+ len = push_ascii(p, last_entry,
+ sizeof(pstring) - PTR_DIFF(p,param) - 1,
+ STR_TERMINATE|STR_UPPER);
+ } else {
+ len = push_ascii(p, workgroup,
+ sizeof(pstring) - PTR_DIFF(p,param) - 1,
+ STR_TERMINATE|STR_UPPER);
+ }
+
+ if (len == (size_t)-1) {
+ return false;
+ }
+
+ p += len;
+
+ 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 */
+ res = -1; /* error happen break out of the loop */
+ break;
+ }
+ res = rparam ? SVAL(rparam,0) : -1;
- 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;
-
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;
-
+
+ /*
+ * 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 paraniod. 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) {
- char *sname = p;
- int comment_offset = (IVAL(p,22) & 0xFFFF)-converter;
- const char *cmnt = comment_offset?(rdata+comment_offset):"";
pstring s1, s2;
+ int comment_offset;
+ const char *cmnt;
- if (comment_offset < 0 || comment_offset > (int)rdrcnt) continue;
+ sname = p;
+ comment_offset = (IVAL(p,22) & 0xFFFF) - converter;
+ cmnt = comment_offset ? (rdata + comment_offset) : "";
+
+ if (comment_offset < 0 ||
+ comment_offset > (int)rdrcnt) {
+ continue;
+ }
stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
@@ -273,24 +344,46 @@ BOOL cli_NetServerEnum(struct cli_state
pull_ascii_pstring(s2, cmnt);
fn(s1, stype, s2, state);
}
+
+ /* 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 = strdup(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);
- if (count < 0) {
- errno = cli_errno(cli);
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ } while ((res == ERRmoredata) && (total_cnt > return_cnt));
+
+ /* Do any needed cleanup */
+ if (rparam)
+ SAFE_FREE(rparam);
+ if (rdata)
+ SAFE_FREE(rdata);
+ if (last_entry)
+ SAFE_FREE(last_entry);
+
+ if (res == -1) {
+ errno = cli_errno(cli);
} else {
- if (!count) {
+ 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;
+ errno = ENOENT;
}
}
- return(count > 0);
+ return(return_cnt > 0);
}
/****************************************************************************