From 549d61b02c0dd1b2e4169ff1b1d7b8afb2a8cf94 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 5 Feb 2010 16:55:15 +0100 Subject: [PATCH 1/5] s3:smbd: rename api_RNetServerEnum => api_RNetServerEnum2 metze (cherry picked from commit dc58672c6588a1715698721153b35ed2d594bc67) --- source3/smbd/lanman.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index 95b21c9..01f3299 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -1360,7 +1360,7 @@ static bool srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2) extracted from lists saved by nmbd on the local host. ****************************************************************************/ -static bool api_RNetServerEnum(connection_struct *conn, uint16 vuid, +static bool api_RNetServerEnum2(connection_struct *conn, uint16 vuid, char *param, int tpscnt, char *data, int tdscnt, int mdrcnt, int mprcnt, char **rdata, @@ -1505,7 +1505,7 @@ static bool api_RNetServerEnum(connection_struct *conn, uint16 vuid, SAFE_FREE(servers); - DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n", + DEBUG(3,("NetServerEnum2 domain = %s uLevel=%d counted=%d total=%d\n", domain,uLevel,counted,counted+missed)); return True; @@ -4631,7 +4631,7 @@ static const struct { {"WPrintDestGetInfo", RAP_WPrintDestGetInfo, api_WPrintDestGetInfo}, {"NetRemoteTOD", RAP_NetRemoteTOD, api_NetRemoteTOD}, {"WPrintQueuePurge", RAP_WPrintQPurge, api_WPrintQueueCtrl}, - {"NetServerEnum", RAP_NetServerEnum2, api_RNetServerEnum}, /* anon OK */ + {"NetServerEnum2", RAP_NetServerEnum2, api_RNetServerEnum2}, /* anon OK */ {"WAccessGetUserPerms",RAP_WAccessGetUserPerms,api_WAccessGetUserPerms}, {"SetUserPassword", RAP_WUserPasswordSet2, api_SetUserPassword}, {"WWkstaUserLogon", RAP_WWkstaUserLogon, api_WWkstaUserLogon}, -- 1.6.3.3 From b01ad8f78eab930697d94a3f8abb5928b41a9d21 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 8 Feb 2010 18:45:18 +0100 Subject: [PATCH 2/5] s3:smbd: add/improve some DEBUG messages in api_RNetServerEnum2() metze (cherry picked from commit 495ac4616654c9e62e14031b7439aff21e42ec91) --- source3/smbd/lanman.c | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index 01f3299..bd67739 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -1429,6 +1429,8 @@ static bool api_RNetServerEnum2(connection_struct *conn, uint16 vuid, fstrcpy(domain, lp_workgroup()); } + DEBUG(4, ("domain [%s]\n", domain)); + if (lp_browse_list()) { total = get_server_info(servertype,&servers,domain); } @@ -1451,8 +1453,8 @@ static bool api_RNetServerEnum2(connection_struct *conn, uint16 vuid, } lastname = s->name; data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0); - DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n", - s->name, s->type, s->comment, s->domain)); + DEBUG(4,("fill_srv_info[%d] %20s %8x %25s %15s\n", + i, s->name, s->type, s->comment, s->domain)); if (data_len <= buf_len) { counted++; @@ -1487,8 +1489,8 @@ static bool api_RNetServerEnum2(connection_struct *conn, uint16 vuid, } lastname = s->name; fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata); - DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n", - s->name, s->type, s->comment, s->domain)); + DEBUG(4,("fill_srv_info[%d] %20s %8x %25s %15s\n", + i, s->name, s->type, s->comment, s->domain)); count2--; } } -- 1.6.3.3 From b710fab9ace179185b15409bcc5d4994f7ee8ca2 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 10 Dec 2009 14:35:24 +1100 Subject: [PATCH 3/5] util: added binsearch.h for binary array searches This was moved from the schema_query code. It will now be used in more than one place, so best to make it a library macro. I think there are quite a few places that could benefit from this. (cherry picked from commit 71943e8858943718affb6a3c0ded2127f07057f0) Signed-off-by: Stefan Metzmacher (cherry picked from commit 448b8f35d7a7cff73d35304673302178f593c9d0) Signed-off-by: Stefan Metzmacher (cherry picked from commit 6b24639c08b764eb0205c63674e80b303b2be2ac) Signed-off-by: Stefan Metzmacher --- lib/util/binsearch.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 68 insertions(+), 0 deletions(-) create mode 100644 lib/util/binsearch.h diff --git a/lib/util/binsearch.h b/lib/util/binsearch.h new file mode 100644 index 0000000..ac83990 --- /dev/null +++ b/lib/util/binsearch.h @@ -0,0 +1,68 @@ +/* + Unix SMB/CIFS implementation. + + a generic binary search macro + + Copyright (C) Andrew Tridgell 2009 + + 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 3 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, see . +*/ + +#ifndef _BINSEARCH_H +#define _BINSEARCH_H + +/* a binary array search, where the array is an array of pointers to structures, + and we want to find a match for 'target' on 'field' in those structures. + + Inputs: + array: base pointer to an array of structures + arrray_size: number of elements in the array + field: the name of the field in the structure we are keying off + target: the field value we are looking for + comparison_fn: the comparison function + result: where the result of the search is put + + if the element is found, then 'result' is set to point to the found array element. If not, + then 'result' is set to NULL. + + The array is assumed to be sorted by the same comparison_fn as the + search (with, for example, qsort) + */ +#define BINARY_ARRAY_SEARCH_P(array, array_size, field, target, comparison_fn, result) do { \ + int32_t _b, _e; \ + (result) = NULL; \ + if (array_size) { for (_b = 0, _e = (array_size)-1; _b <= _e; ) { \ + int32_t _i = (_b+_e)/2; \ + int _r = comparison_fn(target, array[_i]->field); \ + if (_r == 0) { (result) = array[_i]; break; } \ + if (_r < 0) _e = _i - 1; else _b = _i + 1; \ + }} } while (0) + +/* + like BINARY_ARRAY_SEARCH_P, but assumes that the array is an array + of structures, rather than pointers to structures + + result points to the found structure, or NULL + */ +#define BINARY_ARRAY_SEARCH(array, array_size, field, target, comparison_fn, result) do { \ + int32_t _b, _e; \ + (result) = NULL; \ + if (array_size) { for (_b = 0, _e = (array_size)-1; _b <= _e; ) { \ + int32_t _i = (_b+_e)/2; \ + int _r = comparison_fn(target, array[_i].field); \ + if (_r == 0) { (result) = &array[_i]; break; } \ + if (_r < 0) _e = _i - 1; else _b = _i + 1; \ + }} } while (0) + +#endif -- 1.6.3.3 From 4251ce4063e8b5fa4884d27065b1e21bf0d2fa09 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 5 Feb 2010 18:08:46 +0100 Subject: [PATCH 4/5] s3:smbd: implement api_RNetServerEnum3 This is needed to support large browse lists. metze (cherry picked from commit 30eec0656c926d3d85a438dc28f17649b53318f8) --- source3/smbd/lanman.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 212 insertions(+), 1 deletions(-) diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index bd67739..83f0da4 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -29,6 +29,7 @@ #include "smbd/globals.h" #include "../librpc/gen_ndr/cli_samr.h" #include "../librpc/gen_ndr/srv_samr.h" +#include "../lib/util/binsearch.h" #ifdef CHECK_TYPES #undef CHECK_TYPES @@ -1352,7 +1353,8 @@ static int fill_srv_info(struct srv_info_struct *service, static bool srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2) { - return(strcmp(s1->name,s2->name)); +#undef strcasecmp + return strcasecmp(s1->name,s2->name); } /**************************************************************************** @@ -1513,6 +1515,214 @@ static bool api_RNetServerEnum2(connection_struct *conn, uint16 vuid, return True; } +static bool srv_name_match(const char *n1, const char *n2) +{ + /* + * [MS-RAP] footnote <88> for Section 3.2.5.15 says: + * + * In Windows, FirstNameToReturn need not be an exact match: + * the server will return a list of servers that exist on + * the network greater than or equal to the FirstNameToReturn. + */ + int ret = strcasecmp(n1, n2); + + if (ret <= 0) { + return 0; + } + + return ret; +} + +static bool api_RNetServerEnum3(connection_struct *conn, uint16 vuid, + char *param, int tpscnt, + char *data, int tdscnt, + int mdrcnt, int mprcnt, char **rdata, + char **rparam, int *rdata_len, int *rparam_len) +{ + char *str1 = get_safe_str_ptr(param, tpscnt, param, 2); + char *str2 = skip_string(param,tpscnt,str1); + char *p = skip_string(param,tpscnt,str2); + int uLevel = get_safe_SVAL(param, tpscnt, p, 0, -1); + int buf_len = get_safe_SVAL(param,tpscnt, p, 2, 0); + uint32 servertype = get_safe_IVAL(param,tpscnt,p,4, 0); + char *p2; + int data_len, fixed_len, string_len; + int f_len = 0, s_len = 0; + struct srv_info_struct *servers=NULL; + int counted=0,first=0,total=0; + int i,missed; + fstring domain; + fstring first_name; + bool domain_request; + bool local_request; + + if (!str1 || !str2 || !p) { + return False; + } + + /* If someone sets all the bits they don't really mean to set + DOMAIN_ENUM and LOCAL_LIST_ONLY, they just want all the + known servers. */ + + if (servertype == SV_TYPE_ALL) { + servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY); + } + + /* If someone sets SV_TYPE_LOCAL_LIST_ONLY but hasn't set + any other bit (they may just set this bit on its own) they + want all the locally seen servers. However this bit can be + set on its own so set the requested servers to be + ALL - DOMAIN_ENUM. */ + + if ((servertype & SV_TYPE_LOCAL_LIST_ONLY) && !(servertype & SV_TYPE_DOMAIN_ENUM)) { + servertype = SV_TYPE_ALL & ~(SV_TYPE_DOMAIN_ENUM); + } + + domain_request = ((servertype & SV_TYPE_DOMAIN_ENUM) != 0); + local_request = ((servertype & SV_TYPE_LOCAL_LIST_ONLY) != 0); + + p += 8; + + if (strcmp(str1, "WrLehDzz") != 0) { + return false; + } + if (!check_server_info(uLevel,str2)) { + return False; + } + + DEBUG(4, ("server request level: %s %8x ", str2, servertype)); + DEBUG(4, ("domains_req:%s ", BOOLSTR(domain_request))); + DEBUG(4, ("local_only:%s\n", BOOLSTR(local_request))); + + if (skip_string(param,tpscnt,p) == NULL) { + return False; + } + pull_ascii_fstring(domain, p); + if (domain[0] == '\0') { + fstrcpy(domain, lp_workgroup()); + } + p = skip_string(param,tpscnt,p); + if (skip_string(param,tpscnt,p) == NULL) { + return False; + } + pull_ascii_fstring(first_name, p); + + DEBUG(4, ("domain: '%s' first_name: '%s'\n", + domain, first_name)); + + if (lp_browse_list()) { + total = get_server_info(servertype,&servers,domain); + } + + data_len = fixed_len = string_len = 0; + missed = 0; + + if (total > 0) { + qsort(servers,total,sizeof(servers[0]),QSORT_CAST srv_comp); + } + + if (first_name[0] != '\0') { + struct srv_info_struct *first_server = NULL; + + BINARY_ARRAY_SEARCH(servers, total, name, first_name, + srv_name_match, first_server); + if (first_server) { + first = PTR_DIFF(first_server, servers) / sizeof(*servers); + /* + * The binary search may not find the exact match + * so we need to search backward to find the first match + * + * This implements the strange matching windows + * implements. (see the comment in srv_name_match(). + */ + for (;first > 0;) { + int ret; + ret = strcasecmp(first_name, + servers[first-1].name); + if (ret > 0) { + break; + } + first--; + } + } else { + /* we should return no entries */ + first = total; + } + } + + { + char *lastname=NULL; + + for (i=first;iname)) { + continue; + } + lastname = s->name; + data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0); + DEBUG(4,("fill_srv_info[%d] %20s %8x %25s %15s\n", + i, s->name, s->type, s->comment, s->domain)); + + if (data_len <= buf_len) { + counted++; + fixed_len += f_len; + string_len += s_len; + } else { + missed++; + } + } + } + + *rdata_len = fixed_len + string_len; + *rdata = smb_realloc_limit(*rdata,*rdata_len); + if (!*rdata) { + return False; + } + + p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */ + p = *rdata; + f_len = fixed_len; + s_len = string_len; + + { + char *lastname=NULL; + int count2 = counted; + + for (i = first; i < total && count2;i++) { + struct srv_info_struct *s = &servers[i]; + + if (lastname && strequal(lastname,s->name)) { + continue; + } + lastname = s->name; + fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata); + DEBUG(4,("fill_srv_info[%d] %20s %8x %25s %15s\n", + i, s->name, s->type, s->comment, s->domain)); + count2--; + } + } + + *rparam_len = 8; + *rparam = smb_realloc_limit(*rparam,*rparam_len); + if (!*rparam) { + return False; + } + SSVAL(*rparam,0,(missed == 0 ? NERR_Success : ERRmoredata)); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,counted); + SSVAL(*rparam,6,counted+missed); + + DEBUG(3,("NetServerEnum3 domain = %s uLevel=%d first=%d[%s => %s] counted=%d total=%d\n", + domain,uLevel,first,first_name, + first < total ? servers[first].name : "", + counted,counted+missed)); + + SAFE_FREE(servers); + + return True; +} + /**************************************************************************** command 0x34 - suspected of being a "Lookup Names" stub api ****************************************************************************/ @@ -4634,6 +4844,7 @@ static const struct { {"NetRemoteTOD", RAP_NetRemoteTOD, api_NetRemoteTOD}, {"WPrintQueuePurge", RAP_WPrintQPurge, api_WPrintQueueCtrl}, {"NetServerEnum2", RAP_NetServerEnum2, api_RNetServerEnum2}, /* anon OK */ + {"NetServerEnum3", RAP_NetServerEnum3, api_RNetServerEnum3}, /* anon OK */ {"WAccessGetUserPerms",RAP_WAccessGetUserPerms,api_WAccessGetUserPerms}, {"SetUserPassword", RAP_WUserPasswordSet2, api_SetUserPassword}, {"WWkstaUserLogon", RAP_WWkstaUserLogon, api_WWkstaUserLogon}, -- 1.6.3.3 From 55a90d682ac8ac88f1af832165fefd05553018bc Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 8 Feb 2010 18:38:03 +0100 Subject: [PATCH 5/5] s3:libsmb: fix NetServerEnum3 rap calls. metze (cherry picked from commit 9b5198dd443a00fdad4faa1f9cdabedd81012d93) --- source3/libsmb/clirap.c | 24 +++++++++++++++++++----- 1 files changed, 19 insertions(+), 5 deletions(-) diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index 3f77378..bccb0f2 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -248,11 +248,9 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype, 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)); + if (func == RAP_NetServerEnum3) { + strlcpy(p,"WrLehDzz", sizeof(param)-PTR_DIFF(p,param)); } else { strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param)); } @@ -271,7 +269,7 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype, * to continue from. */ len = push_ascii(p, - last_entry ? last_entry : workgroup, + workgroup, sizeof(param) - PTR_DIFF(p,param) - 1, STR_TERMINATE|STR_UPPER); @@ -281,6 +279,22 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype, } p += len; + if (func == RAP_NetServerEnum3) { + len = push_ascii(p, + last_entry ? last_entry : "", + sizeof(param) - PTR_DIFF(p,param) - 1, + STR_TERMINATE); + + if (len == (size_t)-1) { + SAFE_FREE(last_entry); + return false; + } + p += len; + } + + /* Next time through we need to use the continue api */ + func = RAP_NetServerEnum3; + if (!cli_api(cli, param, PTR_DIFF(p,param), 8, /* params, length, max */ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ -- 1.6.3.3