--- /dev/null
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * Browser NET API calls
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __WINE_LMBROWSR_H
+#define __WINE_LMBROWSR_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _BROWSER_EMULATED_DOMAIN {
+ LPWSTR DomainName;
+ LPWSTR EmulatedServerName;
+ DWORD Role;
+} BROWSER_EMULATED_DOMAIN, *PBROWSER_EMULATED_DOMAIN;
+
+NET_API_STATUS WINAPI I_BrowserSetNetlogonState(
+ LPWSTR ServerName, LPWSTR DomainName, LPWSTR EmulatedServerName,
+ DWORD Role);
+
+NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains(
+ LPWSTR ServerName, PBROWSER_EMULATED_DOMAIN *EmulatedDomains,
+ LPDWORD EntriesRead);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
#include <prsht.h>
#include <aclui.h>
#include <sddl.h>
+#include <winternl.h>
#include <ntsecapi.h>
#if SUPPORT_UXTHEME
#include <uxtheme.h>
<directory name="mswsock">
<xi:include href="mswsock/mswsock.xml" />
</directory>
+<directory name="netapi32">
+ <xi:include href="netapi32/netapi32.xml" />
+</directory>
<directory name="netcfgx">
<xi:include href="netcfgx/netcfgx.xml" />
</directory>
--- /dev/null
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 access functions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "lmcons.h"
+#include "lmaccess.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "netapi32_misc.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+static const WCHAR sAdminUserName[] = {'A','d','m','i','n','i','s','t','r','a','t',
+ 'o','r',0};
+static const WCHAR sGuestUserName[] = {'G','u','e','s','t',0};
+
+/************************************************************
+ * NETAPI_ValidateServername
+ *
+ * Validates server name
+ */
+static NET_API_STATUS NETAPI_ValidateServername(LPCWSTR ServerName)
+{
+ if (ServerName)
+ {
+ if (ServerName[0] == 0)
+ return ERROR_BAD_NETPATH;
+ else if (
+ ((ServerName[0] == '\\') &&
+ (ServerName[1] != '\\'))
+ ||
+ ((ServerName[0] == '\\') &&
+ (ServerName[1] == '\\') &&
+ (ServerName[2] == 0))
+ )
+ return ERROR_INVALID_NAME;
+ }
+ return NERR_Success;
+}
+
+/************************************************************
+ * NETAPI_IsKnownUser
+ *
+ * Checks whether the user name indicates current user.
+ */
+static BOOL NETAPI_IsKnownUser(LPCWSTR UserName)
+{
+ DWORD dwSize = UNLEN + 1;
+ BOOL Result;
+ LPWSTR buf;
+
+ if (!lstrcmpW(UserName, sAdminUserName) ||
+ !lstrcmpW(UserName, sGuestUserName))
+ return TRUE;
+ NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &buf);
+ Result = GetUserNameW(buf, &dwSize);
+
+ Result = Result && !lstrcmpW(UserName, buf);
+ NetApiBufferFree(buf);
+
+ return Result;
+}
+
+#define NETAPI_ForceKnownUser(UserName, FailureCode) \
+ if (!NETAPI_IsKnownUser(UserName)) \
+ { \
+ FIXME("Can't find information for user %s\n", \
+ debugstr_w(UserName)); \
+ return FailureCode; \
+ }
+
+/************************************************************
+ * NetUserAdd (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserAdd(LPCWSTR servername,
+ DWORD level, LPBYTE bufptr, LPDWORD parm_err)
+{
+ NET_API_STATUS status;
+ FIXME("(%s, %ld, %p, %p) stub!\n", debugstr_w(servername), level, bufptr, parm_err);
+
+ status = NETAPI_ValidateServername(servername);
+ if (status != NERR_Success)
+ return status;
+
+ if ((bufptr != NULL) && (level > 0) && (level <= 4))
+ {
+ PUSER_INFO_1 ui = (PUSER_INFO_1) bufptr;
+ TRACE("usri%ld_name: %s\n", level, debugstr_w(ui->usri1_name));
+ TRACE("usri%ld_password: %s\n", level, debugstr_w(ui->usri1_password));
+ TRACE("usri%ld_comment: %s\n", level, debugstr_w(ui->usri1_comment));
+ }
+ return status;
+}
+
+/************************************************************
+ * NetUserDel (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserDel(LPCWSTR servername, LPCWSTR username)
+{
+ NET_API_STATUS status;
+ FIXME("(%s, %s) stub!\n", debugstr_w(servername), debugstr_w(username));
+
+ status = NETAPI_ValidateServername(servername);
+ if (status != NERR_Success)
+ return status;
+
+ if (!NETAPI_IsKnownUser(username))
+ return NERR_UserNotFound;
+
+ /* Delete the user here */
+ return status;
+}
+
+/************************************************************
+ * NetUserGetInfo (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI
+NetUserGetInfo(LPCWSTR servername, LPCWSTR username, DWORD level,
+ LPBYTE* bufptr)
+{
+ NET_API_STATUS status;
+ TRACE("(%s, %s, %ld, %p)\n", debugstr_w(servername), debugstr_w(username),
+ level, bufptr);
+ status = NETAPI_ValidateServername(servername);
+ if (status != NERR_Success)
+ return status;
+ NETAPI_ForceLocalComputer(servername, NERR_InvalidComputer);
+ NETAPI_ForceKnownUser(username, NERR_UserNotFound);
+
+ switch (level)
+ {
+ case 0:
+ {
+ PUSER_INFO_0 ui;
+ int name_sz;
+
+ name_sz = lstrlenW(username) + 1;
+
+ /* set up buffer */
+ NetApiBufferAllocate(sizeof(USER_INFO_0) + name_sz * sizeof(WCHAR),
+ (LPVOID *) bufptr);
+
+ ui = (PUSER_INFO_0) *bufptr;
+ ui->usri0_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_0));
+
+ /* get data */
+ lstrcpyW(ui->usri0_name, username);
+ break;
+ }
+
+ case 10:
+ {
+ PUSER_INFO_10 ui;
+ PUSER_INFO_0 ui0;
+ NET_API_STATUS status;
+ /* sizes of the field buffers in WCHARS */
+ int name_sz, comment_sz, usr_comment_sz, full_name_sz;
+
+ comment_sz = 1;
+ usr_comment_sz = 1;
+ full_name_sz = 1;
+
+ /* get data */
+ status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0);
+ if (status != NERR_Success)
+ {
+ NetApiBufferFree(ui0);
+ return status;
+ }
+ name_sz = lstrlenW(ui0->usri0_name) + 1;
+
+ /* set up buffer */
+ NetApiBufferAllocate(sizeof(USER_INFO_10) +
+ (name_sz + comment_sz + usr_comment_sz +
+ full_name_sz) * sizeof(WCHAR),
+ (LPVOID *) bufptr);
+ ui = (PUSER_INFO_10) *bufptr;
+ ui->usri10_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_10));
+ ui->usri10_comment = (LPWSTR) (
+ ((PBYTE) ui->usri10_name) + name_sz * sizeof(WCHAR));
+ ui->usri10_usr_comment = (LPWSTR) (
+ ((PBYTE) ui->usri10_comment) + comment_sz * sizeof(WCHAR));
+ ui->usri10_full_name = (LPWSTR) (
+ ((PBYTE) ui->usri10_usr_comment) + usr_comment_sz * sizeof(WCHAR));
+
+ /* set data */
+ lstrcpyW(ui->usri10_name, ui0->usri0_name);
+ NetApiBufferFree(ui0);
+ ui->usri10_comment[0] = 0;
+ ui->usri10_usr_comment[0] = 0;
+ ui->usri10_full_name[0] = 0;
+ break;
+ }
+
+ case 1:
+ {
+ static const WCHAR homedirW[] = {'H','O','M','E',0};
+ PUSER_INFO_1 ui;
+ PUSER_INFO_0 ui0;
+ NET_API_STATUS status;
+ /* sizes of the field buffers in WCHARS */
+ int name_sz, password_sz, home_dir_sz, comment_sz, script_path_sz;
+
+ password_sz = 1; /* not filled out for security reasons for NetUserGetInfo*/
+ comment_sz = 1;
+ script_path_sz = 1;
+
+ /* get data */
+ status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0);
+ if (status != NERR_Success)
+ {
+ NetApiBufferFree(ui0);
+ return status;
+ }
+ name_sz = lstrlenW(ui0->usri0_name) + 1;
+ home_dir_sz = GetEnvironmentVariableW(homedirW, NULL,0);
+ /* set up buffer */
+ NetApiBufferAllocate(sizeof(USER_INFO_1) +
+ (name_sz + password_sz + home_dir_sz +
+ comment_sz + script_path_sz) * sizeof(WCHAR),
+ (LPVOID *) bufptr);
+
+ ui = (PUSER_INFO_1) *bufptr;
+ ui->usri1_name = (LPWSTR) (ui + 1);
+ ui->usri1_password = ui->usri1_name + name_sz;
+ ui->usri1_home_dir = ui->usri1_password + password_sz;
+ ui->usri1_comment = ui->usri1_home_dir + home_dir_sz;
+ ui->usri1_script_path = ui->usri1_comment + comment_sz;
+ /* set data */
+ lstrcpyW(ui->usri1_name, ui0->usri0_name);
+ NetApiBufferFree(ui0);
+ ui->usri1_password[0] = 0;
+ ui->usri1_password_age = 0;
+ ui->usri1_priv = 0;
+ GetEnvironmentVariableW(homedirW, ui->usri1_home_dir,home_dir_sz);
+ ui->usri1_comment[0] = 0;
+ ui->usri1_flags = 0;
+ ui->usri1_script_path[0] = 0;
+ break;
+ }
+ case 2:
+ case 3:
+ case 4:
+ case 11:
+ case 20:
+ case 23:
+ case 1003:
+ case 1005:
+ case 1006:
+ case 1007:
+ case 1008:
+ case 1009:
+ case 1010:
+ case 1011:
+ case 1012:
+ case 1013:
+ case 1014:
+ case 1017:
+ case 1018:
+ case 1020:
+ case 1023:
+ case 1024:
+ case 1025:
+ case 1051:
+ case 1052:
+ case 1053:
+ {
+ FIXME("Level %ld is not implemented\n", level);
+ break;
+ }
+ default:
+ ERR("Invalid level %ld is specified\n", level);
+ return ERROR_INVALID_LEVEL;
+ }
+ return NERR_Success;
+}
+
+
+
+/************************************************************
+ * NetUserEnum (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI
+NetUserEnum(LPCWSTR servername, DWORD level, DWORD filter, LPBYTE* bufptr,
+ DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries,
+ LPDWORD resume_handle)
+{
+ FIXME("(%s,%ld, 0x%ld,%p,%ld,%p,%p,%p) stub!\n", debugstr_w(servername), level,
+ filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
+
+ return ERROR_ACCESS_DENIED;
+}
+
+/************************************************************
+ * ACCESS_QueryAdminDisplayInformation
+ *
+ * Creates a buffer with information for the Admin User
+ */
+static void ACCESS_QueryAdminDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize)
+{
+ static const WCHAR sAdminUserName[] = {
+ 'A','d','m','i','n','i','s','t','r','a','t','o','r',0};
+
+ /* sizes of the field buffers in WCHARS */
+ int name_sz, comment_sz, full_name_sz;
+ PNET_DISPLAY_USER usr;
+
+ /* set up buffer */
+ name_sz = lstrlenW(sAdminUserName);
+ comment_sz = 1;
+ full_name_sz = 1;
+
+ *pdwSize = sizeof(NET_DISPLAY_USER);
+ *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+ NetApiBufferAllocate(*pdwSize, (LPVOID *) buf);
+
+ usr = *buf;
+ usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER));
+ usr->usri1_comment = (LPWSTR) (
+ ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR));
+ usr->usri1_full_name = (LPWSTR) (
+ ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR));
+
+ /* set data */
+ lstrcpyW(usr->usri1_name, sAdminUserName);
+ usr->usri1_comment[0] = 0;
+ usr->usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
+ usr->usri1_full_name[0] = 0;
+ usr->usri1_user_id = 500;
+ usr->usri1_next_index = 0;
+}
+
+/************************************************************
+ * ACCESS_QueryGuestDisplayInformation
+ *
+ * Creates a buffer with information for the Guest User
+ */
+static void ACCESS_QueryGuestDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize)
+{
+ static const WCHAR sGuestUserName[] = {
+ 'G','u','e','s','t',0 };
+
+ /* sizes of the field buffers in WCHARS */
+ int name_sz, comment_sz, full_name_sz;
+ PNET_DISPLAY_USER usr;
+
+ /* set up buffer */
+ name_sz = lstrlenW(sGuestUserName);
+ comment_sz = 1;
+ full_name_sz = 1;
+
+ *pdwSize = sizeof(NET_DISPLAY_USER);
+ *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+ NetApiBufferAllocate(*pdwSize, (LPVOID *) buf);
+
+ usr = *buf;
+ usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER));
+ usr->usri1_comment = (LPWSTR) (
+ ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR));
+ usr->usri1_full_name = (LPWSTR) (
+ ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR));
+
+ /* set data */
+ lstrcpyW(usr->usri1_name, sGuestUserName);
+ usr->usri1_comment[0] = 0;
+ usr->usri1_flags = UF_ACCOUNTDISABLE | UF_SCRIPT | UF_NORMAL_ACCOUNT |
+ UF_DONT_EXPIRE_PASSWD;
+ usr->usri1_full_name[0] = 0;
+ usr->usri1_user_id = 500;
+ usr->usri1_next_index = 0;
+}
+
+/************************************************************
+ * NetQueryDisplayInformation (NETAPI32.@)
+ * Copies NET_DISPLAY_USER record.
+ */
+static void ACCESS_CopyDisplayUser(PNET_DISPLAY_USER dest, LPWSTR *dest_buf,
+ PNET_DISPLAY_USER src)
+{
+ LPWSTR str = *dest_buf;
+
+ src->usri1_name = str;
+ lstrcpyW(src->usri1_name, dest->usri1_name);
+ str = (LPWSTR) (
+ ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+ src->usri1_comment = str;
+ lstrcpyW(src->usri1_comment, dest->usri1_comment);
+ str = (LPWSTR) (
+ ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+ src->usri1_flags = dest->usri1_flags;
+
+ src->usri1_full_name = str;
+ lstrcpyW(src->usri1_full_name, dest->usri1_full_name);
+ str = (LPWSTR) (
+ ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+ src->usri1_user_id = dest->usri1_user_id;
+ src->usri1_next_index = dest->usri1_next_index;
+ *dest_buf = str;
+}
+
+/************************************************************
+ * NetQueryDisplayInformation (NETAPI32.@)
+ *
+ * The buffer structure:
+ * - array of fixed size record of the level type
+ * - strings, referenced by the record of the level type
+ */
+NET_API_STATUS WINAPI
+NetQueryDisplayInformation(
+ LPCWSTR ServerName, DWORD Level, DWORD Index, DWORD EntriesRequested,
+ DWORD PreferredMaximumLength, LPDWORD ReturnedEntryCount,
+ PVOID *SortedBuffer)
+{
+ TRACE("(%s, %ld, %ld, %ld, %ld, %p, %p)\n", debugstr_w(ServerName),
+ Level, Index, EntriesRequested, PreferredMaximumLength,
+ ReturnedEntryCount, SortedBuffer);
+ NETAPI_ForceLocalComputer(ServerName, ERROR_ACCESS_DENIED);
+ switch (Level)
+ {
+ case 1:
+ {
+ /* current record */
+ PNET_DISPLAY_USER inf;
+ /* current available strings buffer */
+ LPWSTR str;
+ PNET_DISPLAY_USER admin, guest;
+ DWORD admin_size, guest_size;
+ LPWSTR name = NULL;
+ DWORD dwSize;
+
+ /* sizes of the field buffers in WCHARS */
+ int name_sz, comment_sz, full_name_sz;
+
+ /* number of the records, returned in SortedBuffer
+ 3 - for current user, Administrator and Guest users
+ */
+ int records = 3;
+
+ FIXME("Level %ld partially implemented\n", Level);
+ *ReturnedEntryCount = records;
+ comment_sz = 1;
+ full_name_sz = 1;
+
+ /* get data */
+ dwSize = UNLEN + 1;
+ NetApiBufferAllocate(dwSize, (LPVOID *) &name);
+ if (!GetUserNameW(name, &dwSize))
+ {
+ NetApiBufferFree(name);
+ return ERROR_ACCESS_DENIED;
+ }
+ name_sz = dwSize;
+ ACCESS_QueryAdminDisplayInformation(&admin, &admin_size);
+ ACCESS_QueryGuestDisplayInformation(&guest, &guest_size);
+
+ /* set up buffer */
+ dwSize = sizeof(NET_DISPLAY_USER) * records;
+ dwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+
+ NetApiBufferAllocate(dwSize +
+ admin_size - sizeof(NET_DISPLAY_USER) +
+ guest_size - sizeof(NET_DISPLAY_USER),
+ (LPVOID *) SortedBuffer);
+ inf = (PNET_DISPLAY_USER) *SortedBuffer;
+ str = (LPWSTR) ((PBYTE) inf + sizeof(NET_DISPLAY_USER) * records);
+ inf->usri1_name = str;
+ str = (LPWSTR) (
+ ((PBYTE) str) + name_sz * sizeof(WCHAR));
+ inf->usri1_comment = str;
+ str = (LPWSTR) (
+ ((PBYTE) str) + comment_sz * sizeof(WCHAR));
+ inf->usri1_full_name = str;
+ str = (LPWSTR) (
+ ((PBYTE) str) + full_name_sz * sizeof(WCHAR));
+
+ /* set data */
+ lstrcpyW(inf->usri1_name, name);
+ NetApiBufferFree(name);
+ inf->usri1_comment[0] = 0;
+ inf->usri1_flags =
+ UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
+ inf->usri1_full_name[0] = 0;
+ inf->usri1_user_id = 0;
+ inf->usri1_next_index = 0;
+
+ inf++;
+ ACCESS_CopyDisplayUser(admin, &str, inf);
+ NetApiBufferFree(admin);
+
+ inf++;
+ ACCESS_CopyDisplayUser(guest, &str, inf);
+ NetApiBufferFree(guest);
+ break;
+ }
+
+ case 2:
+ case 3:
+ {
+ FIXME("Level %ld is not implemented\n", Level);
+ break;
+ }
+
+ default:
+ ERR("Invalid level %ld is specified\n", Level);
+ return ERROR_INVALID_LEVEL;
+ }
+ return NERR_Success;
+}
+
+/************************************************************
+ * NetGetDCName (NETAPI32.@)
+ *
+ * Return the name of the primary domain controller (PDC)
+ */
+
+NET_API_STATUS WINAPI
+NetGetDCName(LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr)
+{
+ FIXME("(%s, %s, %p) stub!\n", debugstr_w(servername),
+ debugstr_w(domainname), bufptr);
+ return NERR_DCNotFound; /* say we can't find a domain controller */
+}
+
+
+/************************************************************
+ * NetUserModalsGet (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserModalsGet(LPCWSTR szServer, DWORD level, LPBYTE *pbuffer)
+{
+ FIXME("(%s %ld %p) stub!\n", debugstr_w(szServer), level, pbuffer);
+ return NERR_InternalError;
+}
+
+/************************************************************
+ * NetLocalGroupAdd (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetLocalGroupAdd(LPCWSTR servername, DWORD level,
+ LPBYTE buf, LPDWORD parm_err)
+{
+ FIXME("(%s %ld %p %p) stub!\n", debugstr_w(servername), level, buf, parm_err);
+ return NERR_Success;
+}
+
+/************************************************************
+ * NetLocalGroupSetMember (NETAPI32.@)
+ */
+
+NET_API_STATUS WINAPI NetLocalGroupSetMembers(LPCWSTR servername,
+ LPCWSTR groupname, DWORD level, LPBYTE buf, DWORD totalentries)
+{
+ FIXME("(%s %s %ld %p %ld) stub!\n", debugstr_w(servername),
+ debugstr_w(groupname),level, buf, totalentries);
+ return NERR_Success;
+}
--- /dev/null
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * Net API buffer calls
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "lmcons.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "winerror.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ * NetApiBufferAllocate (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferAllocate(DWORD ByteCount, LPVOID* Buffer)
+{
+ TRACE("(%ld, %p)\n", ByteCount, Buffer);
+ *Buffer = HeapAlloc(GetProcessHeap(), 0, ByteCount);
+ if (*Buffer)
+ return NERR_Success;
+ else
+ return GetLastError();
+}
+
+/************************************************************
+ * NetApiBufferFree (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferFree(LPVOID Buffer)
+{
+ TRACE("(%p)\n", Buffer);
+ HeapFree(GetProcessHeap(), 0, Buffer);
+ return NERR_Success;
+}
+
+/************************************************************
+ * NetApiBufferReallocate (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferReallocate(LPVOID OldBuffer, DWORD NewByteCount,
+ LPVOID* NewBuffer)
+{
+ TRACE("(%p, %ld, %p)\n", OldBuffer, NewByteCount, NewBuffer);
+ if (NewByteCount)
+ {
+ if (OldBuffer)
+ *NewBuffer = HeapReAlloc(GetProcessHeap(), 0, OldBuffer, NewByteCount);
+ else
+ *NewBuffer = HeapAlloc(GetProcessHeap(), 0, NewByteCount);
+ return *NewBuffer ? NERR_Success : GetLastError();
+ }
+ else
+ {
+ if (!HeapFree(GetProcessHeap(), 0, OldBuffer)) return GetLastError();
+ *NewBuffer = 0;
+ return NERR_Success;
+ }
+}
+
+/************************************************************
+ * NetApiBufferSize (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferSize(LPVOID Buffer, LPDWORD ByteCount)
+{
+ DWORD dw;
+
+ TRACE("(%p, %p)\n", Buffer, ByteCount);
+ if (Buffer == NULL)
+ return ERROR_INVALID_PARAMETER;
+ dw = HeapSize(GetProcessHeap(), 0, Buffer);
+ TRACE("size: %ld\n", dw);
+ if (dw != 0xFFFFFFFF)
+ *ByteCount = dw;
+ else
+ *ByteCount = 0;
+
+ return NERR_Success;
+}
--- /dev/null
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 browser functions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "lmcons.h"
+#include "lmbrowsr.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ * I_BrowserSetNetlogonState (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI I_BrowserSetNetlogonState(
+ LPWSTR ServerName, LPWSTR DomainName, LPWSTR EmulatedServerName,
+ DWORD Role)
+{
+ return ERROR_NOT_SUPPORTED;
+}
+
+/************************************************************
+ * I_BrowserQueryEmulatedDomains (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains(
+ LPWSTR ServerName, PBROWSER_EMULATED_DOMAIN *EmulatedDomains,
+ LPDWORD EntriesRead)
+{
+ return ERROR_NOT_SUPPORTED;
+}
+
+/************************************************************
+ * NetShareEnum (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetShareEnum( LPWSTR servername, DWORD level, LPBYTE* bufptr,
+ DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries, LPDWORD resume_handle)
+{
+ FIXME("%s %ld %p %ld %p %p %p\n", debugstr_w(servername), level, bufptr,
+ prefmaxlen, entriesread, totalentries, resume_handle);
+ return ERROR_NOT_SUPPORTED;
+}
--- /dev/null
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "config.h"
+#include "wine/debug.h"
+#include "nbcmdqueue.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+struct NBCmdQueue
+{
+ HANDLE heap;
+ CRITICAL_SECTION cs;
+ PNCB head;
+};
+
+#define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserve)
+#define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserve + sizeof(HANDLE))
+
+/* The reserved area of an ncb will be used for the following data:
+ * - a cancelled flag (BOOL, 4 bytes??)
+ * - a handle to an event that's set by a cancelled command on completion
+ * (HANDLE, 4 bytes)
+ * These members are used in the following way
+ * - on cancel, set the event member of the reserved field (with create event)
+ * - NBCmdComplete will delete the ncb from the queue of there's no event;
+ * otherwise it will set the event and not delete the ncb
+ * - cancel must lock the queue before finding the ncb in it, and can unlock it
+ * once it's set the event (and the cancelled flag)
+ * - NBCmdComplete must lock the queue before attempting to remove the ncb or
+ * check the event
+ * - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue.
+ * It'll then unlock the queue, and wait on the event in the head of the queue
+ * until there's no more ncb's in the queue.
+ * Space optimization: use the handle as a boolean. NULL == 0 => not cancelled.
+ * Non-NULL == valid handle => cancelled. This allows storing a next pointer
+ * in the ncb's reserved field as well, avoiding a memory alloc for a new
+ * command (cool).
+ */
+
+struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap)
+{
+ struct NBCmdQueue *queue;
+
+ if (heap == NULL)
+ heap = GetProcessHeap();
+ queue = HeapAlloc(heap, 0, sizeof(struct NBCmdQueue));
+ if (queue)
+ {
+ queue->heap = heap;
+ InitializeCriticalSection(&queue->cs);
+ queue->head = NULL;
+ }
+ return queue;
+}
+
+UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb)
+{
+ UCHAR ret;
+
+ TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+ if (!queue)
+ return NRC_BADDR;
+ if (!ncb)
+ return NRC_INVADDRESS;
+
+ *CANCEL_EVENT_PTR(ncb) = NULL;
+ EnterCriticalSection(&queue->cs);
+ *NEXT_PTR(ncb) = queue->head;
+ queue->head = ncb;
+ ret = NRC_GOODRET;
+ LeaveCriticalSection(&queue->cs);
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static PNCB *NBCmdQueueFindNBC(struct NBCmdQueue *queue, PNCB ncb)
+{
+ PNCB *ret;
+
+ if (!queue || !ncb)
+ ret = NULL;
+ else
+ {
+ ret = &queue->head;
+ while (ret && *ret != ncb)
+ ret = NEXT_PTR(*ret);
+ }
+ return ret;
+}
+
+UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb)
+{
+ UCHAR ret;
+ PNCB *spot;
+
+ TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+ if (!queue)
+ return NRC_BADDR;
+ if (!ncb)
+ return NRC_INVADDRESS;
+
+ EnterCriticalSection(&queue->cs);
+ spot = NBCmdQueueFindNBC(queue, ncb);
+ if (spot)
+ {
+ *CANCEL_EVENT_PTR(*spot) = CreateEventW(NULL, FALSE, FALSE, NULL);
+ WaitForSingleObject(*CANCEL_EVENT_PTR(*spot), INFINITE);
+ CloseHandle(*CANCEL_EVENT_PTR(*spot));
+ *spot = *NEXT_PTR(*spot);
+ if (ncb->ncb_retcode == NRC_CMDCAN)
+ ret = NRC_CMDCAN;
+ else
+ ret = NRC_CANOCCR;
+ }
+ else
+ ret = NRC_INVADDRESS;
+ LeaveCriticalSection(&queue->cs);
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode)
+{
+ UCHAR ret;
+ PNCB *spot;
+
+ TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+ if (!queue)
+ return NRC_BADDR;
+ if (!ncb)
+ return NRC_INVADDRESS;
+
+ EnterCriticalSection(&queue->cs);
+ spot = NBCmdQueueFindNBC(queue, ncb);
+ if (spot)
+ {
+ if (*CANCEL_EVENT_PTR(*spot))
+ SetEvent(*CANCEL_EVENT_PTR(*spot));
+ else
+ *spot = *NEXT_PTR(*spot);
+ ret = NRC_GOODRET;
+ }
+ else
+ ret = NRC_INVADDRESS;
+ LeaveCriticalSection(&queue->cs);
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue)
+{
+ UCHAR ret;
+
+ TRACE(": queue %p\n", queue);
+
+ if (!queue)
+ return NRC_BADDR;
+
+ EnterCriticalSection(&queue->cs);
+ while (queue->head)
+ {
+ TRACE(": waiting for ncb %p (command 0x%02x)\n", queue->head,
+ queue->head->ncb_command);
+ NBCmdQueueCancel(queue, queue->head);
+ }
+ LeaveCriticalSection(&queue->cs);
+ ret = NRC_GOODRET;
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+void NBCmdQueueDestroy(struct NBCmdQueue *queue)
+{
+ TRACE(": queue %p\n", queue);
+
+ if (queue)
+ {
+ NBCmdQueueCancelAll(queue);
+ DeleteCriticalSection(&queue->cs);
+ HeapFree(queue->heap, 0, queue);
+ }
+}
--- /dev/null
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __NBCMDQUEUE_H__
+#define __NBCMDQUEUE_H__
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "nb30.h"
+
+/* This file defines a queue of pending NetBIOS commands. The queue operations
+ * are thread safe, with the exception of NBCmdQueueDestroy: ensure no other
+ * threads are manipulating the queue when calling NBCmdQueueDestroy.
+ */
+
+struct NBCmdQueue;
+
+/* Allocates a new command queue from heap. */
+struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap);
+
+/* Adds ncb to queue. Assumes queue is not NULL, and ncb is not already in the
+ * queue. If ncb is already in the queue, returns NRC_TOOMANY.
+ */
+UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb);
+
+/* Cancels the given ncb. Blocks until the command completes. Implicitly
+ * removes ncb from the queue. Assumes queue and ncb are not NULL, and that
+ * ncb has been added to queue previously.
+ * Returns NRC_CMDCAN on a successful cancellation, NRC_CMDOCCR if the command
+ * completed before it could be cancelled, and various other return values for
+ * different failures.
+ */
+UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb);
+
+/* Sets the return code of the given ncb, and implicitly removes the command
+ * from the queue. Assumes queue and ncb are not NULL, and that ncb has been
+ * added to queue previously.
+ * Returns NRC_GOODRET on success.
+ */
+UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode);
+
+/* Cancels all pending commands in the queue (useful for a RESET or a shutdown).
+ * Returns when all commands have been completed.
+ */
+UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue);
+
+/* Frees all memory associated with the queue. Blocks until all commands
+ * pending in the queue have been completed.
+ */
+void NBCmdQueueDestroy(struct NBCmdQueue *queue);
+
+#endif /* __NBCMDQUEUE_H__ */
--- /dev/null
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This implementation uses a linked list, because I don't have a decent
+ * hash table implementation handy. This is somewhat inefficient, but it's
+ * rather more efficient than not having a name cache at all.
+ */
+
+#include "config.h"
+#include "wine/port.h"
+#include "wine/debug.h"
+
+#include "nbnamecache.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+typedef struct _NBNameCacheNode
+{
+ DWORD expireTime;
+ NBNameCacheEntry *entry;
+ struct _NBNameCacheNode *next;
+} NBNameCacheNode;
+
+struct NBNameCache
+{
+ HANDLE heap;
+ CRITICAL_SECTION cs;
+ DWORD entryExpireTimeMS;
+ NBNameCacheNode *head;
+};
+
+/* Unlinks the node pointed to by *prev, and frees any associated memory.
+ * If that node's next pointed to another node, *prev now points to it.
+ * Assumes the caller owns cache's lock.
+ */
+static void NBNameCacheUnlinkNode(struct NBNameCache *cache,
+ NBNameCacheNode **prev)
+{
+ if (cache && prev && *prev)
+ {
+ NBNameCacheNode *next = (*prev)->next;
+
+ HeapFree(cache->heap, 0, (*prev)->entry);
+ HeapFree(cache->heap, 0, *prev);
+ *prev = next;
+ }
+}
+
+/* Walks the list beginning with cache->head looking for the node with name
+ * name. If the node is found, returns a pointer to the next pointer of the
+ * node _prior_ to the found node (or head if head points to it). Thus, if the
+ * node's all you want, dereference the return value twice. If you want to
+ * modify the list, modify the referent of the return value.
+ * While it's at it, deletes nodes whose time has expired (except the node
+ * you're looking for, of course).
+ * Returns NULL if the node isn't found.
+ * Assumes the caller owns cache's lock.
+ */
+static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache,
+ const char name[NCBNAMSZ])
+{
+ NBNameCacheNode **ret = NULL;
+
+ if (cache && cache->head)
+ {
+ NBNameCacheNode **ptr;
+
+ ptr = &cache->head;
+ while (ptr && *ptr && (*ptr)->entry)
+ {
+ if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1))
+ ret = ptr;
+ else
+ {
+ if (GetTickCount() > (*ptr)->expireTime)
+ NBNameCacheUnlinkNode(cache, ptr);
+ }
+ if (*ptr)
+ ptr = &(*ptr)->next;
+ }
+ }
+ return ret;
+}
+
+struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS)
+{
+ struct NBNameCache *cache;
+
+
+ if (!heap)
+ heap = GetProcessHeap();
+ cache = HeapAlloc(heap, 0, sizeof(struct NBNameCache));
+ if (cache)
+ {
+ cache->heap = heap;
+ InitializeCriticalSection(&cache->cs);
+ cache->entryExpireTimeMS = entryExpireTimeMS;
+ cache->head = NULL;
+ }
+ return cache;
+}
+
+BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry)
+{
+ BOOL ret;
+
+ if (cache && entry)
+ {
+ NBNameCacheNode **node;
+
+ EnterCriticalSection(&cache->cs);
+ node = NBNameCacheWalk(cache, (char*)entry->name);
+ if (node)
+ {
+ (*node)->expireTime = GetTickCount() +
+ cache->entryExpireTimeMS;
+ HeapFree(cache->heap, 0, (*node)->entry);
+ (*node)->entry = entry;
+ ret = TRUE;
+ }
+ else
+ {
+ NBNameCacheNode *newNode = HeapAlloc(cache->heap, 0, sizeof(NBNameCacheNode));
+ if (newNode)
+ {
+ newNode->expireTime = GetTickCount() +
+ cache->entryExpireTimeMS;
+ newNode->entry = entry;
+ newNode->next = cache->head;
+ cache->head = newNode;
+ ret = TRUE;
+ }
+ else
+ ret = FALSE;
+ }
+ LeaveCriticalSection(&cache->cs);
+ }
+ else
+ ret = FALSE;
+ return ret;
+}
+
+const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ])
+{
+ const NBNameCacheEntry *ret;
+ UCHAR printName[NCBNAMSZ];
+
+ memcpy(printName, name, NCBNAMSZ - 1);
+ printName[NCBNAMSZ - 1] = '\0';
+ if (cache)
+ {
+ NBNameCacheNode **node;
+
+ EnterCriticalSection(&cache->cs);
+ node = NBNameCacheWalk(cache, (char*)name);
+ if (node)
+ ret = (*node)->entry;
+ else
+ ret = NULL;
+ LeaveCriticalSection(&cache->cs);
+ }
+ else
+ ret = NULL;
+ return ret;
+}
+
+BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ])
+{
+ BOOL ret;
+
+ if (cache)
+ {
+ NBNameCacheNode **node;
+
+ EnterCriticalSection(&cache->cs);
+ node = NBNameCacheWalk(cache, (char*)name);
+ if (node && *node && (*node)->entry)
+ {
+ memcpy((*node)->entry->nbname, nbname, NCBNAMSZ);
+ ret = TRUE;
+ }
+ else
+ ret = FALSE;
+ LeaveCriticalSection(&cache->cs);
+ }
+ else
+ ret = FALSE;
+ return ret;
+}
+
+void NBNameCacheDestroy(struct NBNameCache *cache)
+{
+ if (cache)
+ {
+ DeleteCriticalSection(&cache->cs);
+ while (cache->head)
+ NBNameCacheUnlinkNode(cache, &cache->head);
+ HeapFree(cache->heap, 0, cache);
+ }
+}
--- /dev/null
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __WINE_NBNAMECACHE_H
+#define __WINE_NBNAMECACHE_H
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "nb30.h"
+
+struct NBNameCache;
+
+/* Represents an entry in the name cache. If the NetBIOS name is known, it's
+ * in nbname. Otherwise, nbname begins with '*'. numAddresses defines the
+ * number of addresses in addresses.
+ * Notice that it allows multiple addresses per name, but doesn't explicitly
+ * allow group names. That's because all names so far are unique; if a use for
+ * group names comes up, adding a flag here is simple enough.
+ * Also, only the first NCBNAMSZ - 1 bytes are considered significant. This is
+ * because a name may have been resolved using DNS, and the suffix byte is
+ * always truncated for DNS lookups.
+ */
+typedef struct _NBNameCacheEntry
+{
+ UCHAR name[NCBNAMSZ];
+ UCHAR nbname[NCBNAMSZ];
+ DWORD numAddresses;
+ DWORD addresses[1];
+} NBNameCacheEntry;
+
+/* Functions that create, manipulate, and destroy a name cache. Thread-safe,
+ * with the exception of NBNameCacheDestroy--ensure that no other threads are
+ * manipulating the cache before destoying it.
+ */
+
+/* Allocates a new name cache from heap, and sets the expire time on new
+ * entries to entryExpireTimeMS after a cache entry is added.
+ */
+struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS);
+
+/* Adds an entry to the cache. The entry is assumed to have been allocated
+ * from the same heap as the name cache; the name cache will own the entry
+ * from now on. The entry's expire time is initialized at this time to
+ * entryExpireTimeMS + the current time in MS. If an existing entry with the
+ * same name was in the cache, the entry is replaced. Returns TRUE on success
+ * or FALSE on failure.
+ */
+BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry);
+
+/* Finds the entry with name name in the cache and returns a pointer to it, or
+ * NULL if it isn't found.
+ */
+const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ]);
+
+/* If the entry with name name is in the cache, updates its nbname member to
+ * nbname. The entry's expire time is implicitly updated to entryExpireTimeMS
+ * + the current time in MS, since getting the NetBIOS name meant validating
+ * the name and address anyway.
+ * Returns TRUE on success or FALSE on failure.
+ */
+BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ]);
+
+void NBNameCacheDestroy(struct NBNameCache *cache);
+
+#endif /* ndef __WINE_NBNAMECACHE_H */
--- /dev/null
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * I am heavily indebted to Chris Hertel's excellent Implementing CIFS,
+ * http://ubiqx.org/cifs/ , for whatever understanding I have of NBT.
+ * I also stole from Mike McCormack's smb.c and netapi32.c, although little of
+ * that code remains.
+ * Lack of understanding and bugs are my fault.
+ *
+ * FIXME:
+ * - Of the NetBIOS session functions, only client functions are supported, and
+ * it's likely they'll be the only functions supported. NBT requires session
+ * servers to listen on TCP/139. This requires root privilege, and Samba is
+ * likely to be listening here already. This further restricts NetBIOS
+ * applications, both explicit users and implicit ones: CreateNamedPipe
+ * won't actually create a listening pipe, for example, so applications can't
+ * act as RPC servers using a named pipe protocol binding, DCOM won't be able
+ * to support callbacks or servers over the named pipe protocol, etc.
+ *
+ * - Datagram support is omitted for the same reason. To send a NetBIOS
+ * datagram, you must include the NetBIOS name by which your application is
+ * known. This requires you to have registered the name previously, and be
+ * able to act as a NetBIOS datagram server (listening on UDP/138).
+ *
+ * - Name registration functions are omitted for the same reason--registering a
+ * name requires you to be able to defend it, and this means listening on
+ * UDP/137.
+ * Win98 requires you either use your computer's NetBIOS name (with the NULL
+ * suffix byte) as the calling name when creating a session, or to register
+ * a new name before creating one: it disallows '*' as the calling name.
+ * Win2K initially starts with an empty name table, and doesn't allow you to
+ * use the machine's NetBIOS name (with the NULL suffix byte) as the calling
+ * name. Although it allows sessions to be created with '*' as the calling
+ * name, doing so results in timeouts for all receives, because the
+ * application never gets them.
+ * So, a well-behaved Netbios application will typically want to register a
+ * name. I should probably support a do-nothing name list that allows
+ * NCBADDNAME to add to it, but doesn't actually register the name, or does
+ * attempt to register it without being able to defend it.
+ *
+ * - Name lookups may not behave quite as you'd expect/like if you have
+ * multiple LANAs. If a name is resolvable through DNS, or if you're using
+ * WINS, it'll resolve on _any_ LANA. So, a Call will succeed on any LANA as
+ * well.
+ * I'm not sure how Windows behaves in this case. I could try to force
+ * lookups to the correct adapter by using one of the GetPreferred*
+ * functions, but with the possibility of multiple adapters in the same
+ * same subnet, there's no guarantee that what IpHlpApi thinks is the
+ * preferred adapter will actually be a LANA. (It's highly probable because
+ * this is an unusual configuration, but not guaranteed.)
+ *
+ * See also other FIXMEs in the code.
+ */
+
+#include "config.h"
+#include <stdarg.h>
+
+#include "winsock2.h"
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "winreg.h"
+#include "iphlpapi.h"
+
+#include "netbios.h"
+#include "nbnamecache.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+#define PORT_NBNS 137
+#define PORT_NBDG 138
+#define PORT_NBSS 139
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ~0UL
+#endif
+
+#define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
+#define NBR_GETWORD(p) ntohs(*(WORD *)(p))
+
+#define MIN_QUERIES 1
+#define MAX_QUERIES 0xffff
+#define MIN_QUERY_TIMEOUT 100
+#define MAX_QUERY_TIMEOUT 0xffffffff
+#define BCAST_QUERIES 3
+#define BCAST_QUERY_TIMEOUT 750
+#define WINS_QUERIES 3
+#define WINS_QUERY_TIMEOUT 750
+#define MAX_WINS_SERVERS 2
+#define MIN_CACHE_TIMEOUT 60000
+#define CACHE_TIMEOUT 360000
+
+#define MAX_NBT_NAME_SZ (NCBNAMSZ * 2 + MAX_DOMAIN_NAME_LEN + 2)
+#define SIMPLE_NAME_QUERY_PKT_SIZE 26 + MAX_NBT_NAME_SZ
+
+#define DEFAULT_NBT_SESSIONS 16
+
+#define NBNS_TYPE_NB 0x0020
+#define NBNS_TYPE_NBSTAT 0x0021
+#define NBNS_CLASS_INTERNET 0x00001
+#define NBNS_HEADER_SIZE (sizeof(WORD) * 6)
+#define NBNS_RESPONSE_AND_OPCODE 0xf800
+#define NBNS_RESPONSE_AND_QUERY 0x8000
+#define NBNS_REPLYCODE 0x0f
+
+#define NBSS_HDRSIZE 4
+
+#define NBSS_MSG 0x00
+#define NBSS_REQ 0x81
+#define NBSS_ACK 0x82
+#define NBSS_NACK 0x83
+#define NBSS_RETARGET 0x84
+#define NBSS_KEEPALIVE 0x85
+
+#define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80
+#define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
+#define NBSS_ERR_BAD_NAME 0x82
+#define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83
+
+#define NBSS_EXTENSION 0x01
+
+typedef struct _NetBTSession
+{
+ CRITICAL_SECTION cs;
+ SOCKET fd;
+ DWORD bytesPending;
+} NetBTSession;
+
+typedef struct _NetBTAdapter
+{
+ MIB_IPADDRROW ipr;
+ WORD nameQueryXID;
+ struct NBNameCache *nameCache;
+ DWORD xmit_success;
+ DWORD recv_success;
+} NetBTAdapter;
+
+static ULONG gTransportID;
+static BOOL gEnableDNS;
+static DWORD gBCastQueries;
+static DWORD gBCastQueryTimeout;
+static DWORD gWINSQueries;
+static DWORD gWINSQueryTimeout;
+static DWORD gWINSServers[MAX_WINS_SERVERS];
+static int gNumWINSServers;
+static char gScopeID[MAX_DOMAIN_NAME_LEN];
+static DWORD gCacheTimeout;
+static struct NBNameCache *gNameCache;
+
+/* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
+ * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
+ * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes
+ * if p is NULL-terminated. Returns the number of bytes stored in buffer.
+ */
+static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
+{
+ int i,len=0;
+
+ if (!p) return 0;
+ if (!buffer) return 0;
+
+ buffer[len++] = NCBNAMSZ * 2;
+ for (i = 0; p[i] && i < NCBNAMSZ; i++)
+ {
+ buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
+ buffer[len++] = (p[i] & 0x0f) + 'A';
+ }
+ while (len < NCBNAMSZ * 2)
+ {
+ buffer[len++] = 'C';
+ buffer[len++] = 'A';
+ }
+ if (*gScopeID)
+ {
+ int scopeIDLen = strlen(gScopeID);
+
+ memcpy(buffer + len, gScopeID, scopeIDLen);
+ len += scopeIDLen;
+ }
+ buffer[len++] = 0; /* add second terminator */
+ return len;
+}
+
+/* Creates a NBT name request packet for name in buffer. If broadcast is true,
+ * creates a broadcast request, otherwise creates a unicast request.
+ * Returns the number of bytes stored in buffer.
+ */
+static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
+ BOOL broadcast, UCHAR *buffer, int len)
+{
+ int i = 0;
+
+ if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
+
+ NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */
+ if (broadcast)
+ {
+ NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
+ i+=2;
+ }
+ else
+ {
+ NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
+ i+=2;
+ }
+ NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
+ NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
+ NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
+ NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
+
+ i += NetBTNameEncode(name, &buffer[i]);
+
+ NBR_ADDWORD(&buffer[i],qtype); i+=2;
+ NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
+
+ return i;
+}
+
+/* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on
+ * fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not
+ * NULL.
+ * Returns 0 on success, -1 on failure.
+ */
+static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
+ WORD qtype, DWORD destAddr, BOOL broadcast)
+{
+ int ret = 0, on = 1;
+ struct in_addr addr;
+
+ addr.s_addr = destAddr;
+ TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
+
+ if (broadcast)
+ ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
+ if(ret == 0)
+ {
+ WSABUF wsaBuf;
+ UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_addr.s_addr = destAddr;
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(PORT_NBNS);
+
+ wsaBuf.buf = (CHAR*)buf;
+ wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
+ sizeof(buf));
+ if (wsaBuf.len > 0)
+ {
+ DWORD bytesSent;
+
+ ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
+ (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
+ if (ret < 0 || bytesSent < wsaBuf.len)
+ ret = -1;
+ else
+ ret = 0;
+ }
+ else
+ ret = -1;
+ }
+ return ret;
+}
+
+typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rdLength);
+
+/* Waits on fd until GetTickCount() returns a value greater than or equal to
+ * waitUntil for a name service response. If a name response matching xid
+ * is received, calls answerCallback once for each answer resource record in
+ * the response. (The callback's answerCount will be the total number of
+ * answers to expect, and answerIndex will be the 0-based index that's being
+ * sent this time.) Quits parsing if answerCallback returns FALSE.
+ * Returns NRC_GOODRET on timeout or a valid response received, something else
+ * on error.
+ */
+static UCHAR NetBTWaitForNameResponse(NetBTAdapter *adapter, SOCKET fd,
+ DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
+{
+ BOOL found = FALSE;
+ DWORD now;
+ UCHAR ret = NRC_GOODRET;
+
+ if (!adapter) return NRC_BADDR;
+ if (fd == INVALID_SOCKET) return NRC_BADDR;
+ if (!answerCallback) return NRC_BADDR;
+
+ while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil)
+ {
+ DWORD msToWait = waitUntil - now;
+ struct fd_set fds;
+ struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
+ int r;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ r = select(fd + 1, &fds, NULL, NULL, &timeout);
+ if (r < 0)
+ ret = NRC_SYSTEM;
+ else if (r == 1)
+ {
+ /* FIXME: magic #, is this always enough? */
+ UCHAR buffer[256];
+ int fromsize;
+ struct sockaddr_in fromaddr;
+ WORD respXID, flags, queryCount, answerCount;
+ WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer };
+ DWORD bytesReceived, recvFlags = 0;
+
+ fromsize = sizeof(fromaddr);
+ r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
+ (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
+ if(r < 0)
+ {
+ ret = NRC_SYSTEM;
+ break;
+ }
+
+ if (bytesReceived < NBNS_HEADER_SIZE)
+ continue;
+
+ respXID = NBR_GETWORD(buffer);
+ if (adapter->nameQueryXID != respXID)
+ continue;
+
+ flags = NBR_GETWORD(buffer + 2);
+ queryCount = NBR_GETWORD(buffer + 4);
+ answerCount = NBR_GETWORD(buffer + 6);
+
+ /* a reply shouldn't contain a query, ignore bad packet */
+ if (queryCount > 0)
+ continue;
+
+ if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
+ {
+ if ((flags & NBNS_REPLYCODE) != 0)
+ ret = NRC_NAMERR;
+ else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
+ {
+ PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
+ BOOL shouldContinue = TRUE;
+ WORD answerIndex = 0;
+
+ found = TRUE;
+ /* decode one answer at a time */
+ while (ret == NRC_GOODRET && answerIndex < answerCount &&
+ ptr - buffer < bytesReceived && shouldContinue)
+ {
+ WORD rLen;
+
+ /* scan past name */
+ for (; ptr[0] && ptr - buffer < bytesReceived; )
+ ptr += ptr[0] + 1;
+ ptr++;
+ ptr += 2; /* scan past type */
+ if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
+ && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
+ ptr += sizeof(WORD);
+ else
+ ret = NRC_SYSTEM; /* parse error */
+ ptr += sizeof(DWORD); /* TTL */
+ rLen = NBR_GETWORD(ptr);
+ rLen = min(rLen, bytesReceived - (ptr - buffer));
+ ptr += sizeof(WORD);
+ shouldContinue = answerCallback(data, answerCount,
+ answerIndex, ptr, rLen);
+ ptr += rLen;
+ answerIndex++;
+ }
+ }
+ }
+ }
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+typedef struct _NetBTNameQueryData {
+ NBNameCacheEntry *cacheEntry;
+ UCHAR ret;
+} NetBTNameQueryData;
+
+/* Name query callback function for NetBTWaitForNameResponse, creates a cache
+ * entry on the first answer, adds each address as it's called again (as long
+ * as there's space). If there's an error that should be propagated as the
+ * NetBIOS error, modifies queryData's ret member to the proper return code.
+ */
+static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rLen)
+{
+ NetBTNameQueryData *queryData = (NetBTNameQueryData *)pVoid;
+ BOOL ret;
+
+ if (queryData)
+ {
+ if (queryData->cacheEntry == NULL)
+ {
+ queryData->cacheEntry = HeapAlloc(
+ GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
+ (answerCount - 1) * sizeof(DWORD));
+ if (queryData->cacheEntry)
+ queryData->cacheEntry->numAddresses = 0;
+ else
+ {
+ ret = FALSE;
+ queryData->ret = NRC_OSRESNOTAV;
+ }
+ }
+ if (rLen == 6 && queryData->cacheEntry &&
+ queryData->cacheEntry->numAddresses < answerCount)
+ {
+ queryData->cacheEntry->addresses[queryData->cacheEntry->
+ numAddresses++] = *(PDWORD)(rData + 2);
+ ret = queryData->cacheEntry->numAddresses < answerCount;
+ }
+ else
+ ret = FALSE;
+ }
+ else
+ ret = FALSE;
+ return ret;
+}
+
+/* Workhorse NetBT name lookup function. Sends a name lookup query for
+ * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
+ * adapter->nameQueryXID as the transaction ID. Waits up to timeout
+ * milliseconds, and retries up to maxQueries times, waiting for a reply.
+ * If a valid response is received, stores the looked up addresses as a
+ * NBNameCacheEntry in *cacheEntry.
+ * Returns NRC_GOODRET on success, though this may not mean the name was
+ * resolved--check whether *cacheEntry is NULL.
+ */
+static UCHAR NetBTNameWaitLoop(NetBTAdapter *adapter, SOCKET fd, PNCB ncb,
+ DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
+ NBNameCacheEntry **cacheEntry)
+{
+ unsigned int queries;
+ NetBTNameQueryData queryData;
+
+ if (!adapter) return NRC_BADDR;
+ if (fd == INVALID_SOCKET) return NRC_BADDR;
+ if (!ncb) return NRC_BADDR;
+ if (!cacheEntry) return NRC_BADDR;
+
+ queryData.cacheEntry = NULL;
+ queryData.ret = NRC_GOODRET;
+ for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries;
+ queries++)
+ {
+ if (!NCB_CANCELLED(ncb))
+ {
+ int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
+ adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
+
+ if (r == 0)
+ queryData.ret = NetBTWaitForNameResponse(adapter, fd,
+ GetTickCount() + timeout, NetBTFindNameAnswerCallback,
+ &queryData);
+ else
+ queryData.ret = NRC_SYSTEM;
+ }
+ else
+ queryData.ret = NRC_CMDCAN;
+ }
+ if (queryData.cacheEntry)
+ {
+ memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ);
+ memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ);
+ }
+ *cacheEntry = queryData.cacheEntry;
+ return queryData.ret;
+}
+
+/* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
+ * has not yet been created, creates it, using gCacheTimeout as the cache
+ * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails,
+ * frees cacheEntry.
+ * Returns NRC_GOODRET on success, and something else on failure.
+ */
+static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
+ NBNameCacheEntry *cacheEntry)
+{
+ UCHAR ret;
+
+ if (!nameCache) return NRC_BADDR;
+ if (!cacheEntry) return NRC_BADDR;
+
+ if (!*nameCache)
+ *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
+ if (*nameCache)
+ ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
+ ? NRC_GOODRET : NRC_OSRESNOTAV;
+ else
+ {
+ HeapFree(GetProcessHeap(), 0, cacheEntry);
+ ret = NRC_OSRESNOTAV;
+ }
+ return ret;
+}
+
+/* Attempts to resolve name using inet_addr(), then gethostbyname() if
+ * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>. If the name
+ * can be looked up, returns 0 and stores the looked up addresses as a
+ * NBNameCacheEntry in *cacheEntry.
+ * Returns NRC_GOODRET on success, though this may not mean the name was
+ * resolved--check whether *cacheEntry is NULL. Returns something else on
+ * error.
+ */
+static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ],
+ NBNameCacheEntry **cacheEntry)
+{
+ UCHAR ret = NRC_GOODRET;
+
+ TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
+
+ if (!name) return NRC_BADDR;
+ if (!cacheEntry) return NRC_BADDR;
+
+ if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
+ name[NCBNAMSZ - 1] == 0x20))
+ {
+ CHAR toLookup[NCBNAMSZ];
+ unsigned int i;
+
+ for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
+ toLookup[i] = name[i];
+ toLookup[i] = '\0';
+
+ if (isdigit(toLookup[0]))
+ {
+ unsigned long addr = inet_addr(toLookup);
+
+ if (addr != INADDR_NONE)
+ {
+ *cacheEntry = HeapAlloc(GetProcessHeap(),
+ 0, sizeof(NBNameCacheEntry));
+ if (*cacheEntry)
+ {
+ memcpy((*cacheEntry)->name, name, NCBNAMSZ);
+ memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
+ (*cacheEntry)->nbname[0] = '*';
+ (*cacheEntry)->numAddresses = 1;
+ (*cacheEntry)->addresses[0] = addr;
+ }
+ else
+ ret = NRC_OSRESNOTAV;
+ }
+ }
+ if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry)
+ {
+ struct hostent *host;
+
+ if ((host = gethostbyname(toLookup)) != NULL)
+ {
+ for (i = 0; ret == NRC_GOODRET && host->h_addr_list &&
+ host->h_addr_list[i]; i++)
+ ;
+ if (host->h_addr_list && host->h_addr_list[0])
+ {
+ *cacheEntry = HeapAlloc(
+ GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
+ (i - 1) * sizeof(DWORD));
+ if (*cacheEntry)
+ {
+ memcpy((*cacheEntry)->name, name, NCBNAMSZ);
+ memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
+ (*cacheEntry)->nbname[0] = '*';
+ (*cacheEntry)->numAddresses = i;
+ for (i = 0; i < (*cacheEntry)->numAddresses; i++)
+ (*cacheEntry)->addresses[i] =
+ (DWORD)host->h_addr_list[i];
+ }
+ else
+ ret = NRC_OSRESNOTAV;
+ }
+ }
+ }
+ }
+
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+/* Looks up the name in ncb->ncb_callname, first in the name caches (global
+ * and this adapter's), then using gethostbyname(), next by WINS if configured,
+ * and finally using broadcast NetBT name resolution. In NBT parlance, this
+ * makes this an "H-node". Stores an entry in the appropriate name cache for a
+ * found node, and returns it as *cacheEntry.
+ * Assumes data, ncb, and cacheEntry are not NULL.
+ * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
+ * just that all name lookup operations completed successfully--and something
+ * else on failure. *cacheEntry will be NULL if the name was not found.
+ */
+static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb,
+ const NBNameCacheEntry **cacheEntry)
+{
+ UCHAR ret = NRC_GOODRET;
+
+ TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry);
+
+ if (!cacheEntry) return NRC_BADDR;
+ *cacheEntry = NULL;
+
+ if (!adapter) return NRC_BADDR;
+ if (!ncb) return NRC_BADDR;
+
+ if (ncb->ncb_callname[0] == '*')
+ ret = NRC_NOWILD;
+ else
+ {
+ *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
+ if (!*cacheEntry)
+ *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
+ ncb->ncb_callname);
+ if (!*cacheEntry)
+ {
+ NBNameCacheEntry *newEntry = NULL;
+
+ ret = NetBTinetResolve(ncb->ncb_callname, &newEntry);
+ if (ret == NRC_GOODRET && newEntry)
+ {
+ ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
+ if (ret != NRC_GOODRET)
+ newEntry = NULL;
+ }
+ else
+ {
+ SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
+ 0, WSA_FLAG_OVERLAPPED);
+
+ if(fd == INVALID_SOCKET)
+ ret = NRC_OSRESNOTAV;
+ else
+ {
+ int winsNdx;
+
+ adapter->nameQueryXID++;
+ for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL
+ && winsNdx < gNumWINSServers; winsNdx++)
+ ret = NetBTNameWaitLoop(adapter, fd, ncb,
+ gWINSServers[winsNdx], FALSE, gWINSQueryTimeout,
+ gWINSQueries, &newEntry);
+ if (ret == NRC_GOODRET && newEntry)
+ {
+ ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
+ if (ret != NRC_GOODRET)
+ newEntry = NULL;
+ }
+ if (ret == NRC_GOODRET && *cacheEntry == NULL)
+ {
+ DWORD bcastAddr =
+ adapter->ipr.dwAddr & adapter->ipr.dwMask;
+
+ if (adapter->ipr.dwBCastAddr)
+ bcastAddr |= ~adapter->ipr.dwMask;
+ ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr,
+ TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry);
+ if (ret == NRC_GOODRET && newEntry)
+ {
+ ret = NetBTStoreCacheEntry(&adapter->nameCache,
+ newEntry);
+ if (ret != NRC_GOODRET)
+ newEntry = NULL;
+ }
+ }
+ closesocket(fd);
+ }
+ }
+ *cacheEntry = newEntry;
+ }
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+typedef struct _NetBTNodeQueryData
+{
+ BOOL gotResponse;
+ PADAPTER_STATUS astat;
+ WORD astatLen;
+} NetBTNodeQueryData;
+
+/* Callback function for NetBTAstatRemote, parses the rData for the node
+ * status and name list of the remote node. Always returns FALSE, since
+ * there's never more than one answer we care about in a node status response.
+ */
+static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rLen)
+{
+ NetBTNodeQueryData *data = (NetBTNodeQueryData *)pVoid;
+
+ if (data && !data->gotResponse && rData && rLen >= 1)
+ {
+ /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
+ if (rLen >= rData[0] * (NCBNAMSZ + 2))
+ {
+ WORD i;
+ PUCHAR src;
+ PNAME_BUFFER dst;
+
+ data->gotResponse = TRUE;
+ data->astat->name_count = rData[0];
+ for (i = 0, src = rData + 1,
+ dst = (PNAME_BUFFER)((PUCHAR)data->astat +
+ sizeof(ADAPTER_STATUS));
+ i < data->astat->name_count && src - rData < rLen &&
+ (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
+ i++, dst++, src += NCBNAMSZ + 2)
+ {
+ UCHAR flags = *(src + NCBNAMSZ);
+
+ memcpy(dst->name, src, NCBNAMSZ);
+ /* we won't actually see a registering name in the returned
+ * response. It's useful to see if no other flags are set; if
+ * none are, then the name is registered. */
+ dst->name_flags = REGISTERING;
+ if (flags & 0x80)
+ dst->name_flags |= GROUP_NAME;
+ if (flags & 0x10)
+ dst->name_flags |= DEREGISTERED;
+ if (flags & 0x08)
+ dst->name_flags |= DUPLICATE;
+ if (dst->name_flags == REGISTERING)
+ dst->name_flags = REGISTERED;
+ }
+ /* arbitrarily set HW type to Ethernet */
+ data->astat->adapter_type = 0xfe;
+ if (src - rData < rLen)
+ memcpy(data->astat->adapter_address, src,
+ min(rLen - (src - rData), 6));
+ }
+ }
+ return FALSE;
+}
+
+/* This uses the WINS timeout and query values, as they're the
+ * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
+ */
+static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret = NRC_GOODRET;
+ const NBNameCacheEntry *cacheEntry = NULL;
+
+ TRACE("adapter %p, NCB %p\n", adapter, ncb);
+
+ if (!adapter) return NRC_BADDR;
+ if (!ncb) return NRC_INVADDRESS;
+
+ ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+ if (ret == NRC_GOODRET && cacheEntry)
+ {
+ if (cacheEntry->numAddresses > 0)
+ {
+ SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
+ WSA_FLAG_OVERLAPPED);
+
+ if(fd == INVALID_SOCKET)
+ ret = NRC_OSRESNOTAV;
+ else
+ {
+ NetBTNodeQueryData queryData;
+ DWORD queries;
+ PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+
+ adapter->nameQueryXID++;
+ astat->name_count = 0;
+ queryData.gotResponse = FALSE;
+ queryData.astat = astat;
+ queryData.astatLen = ncb->ncb_length;
+ for (queries = 0; !queryData.gotResponse &&
+ queries < gWINSQueries; queries++)
+ {
+ if (!NCB_CANCELLED(ncb))
+ {
+ int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
+ adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
+ cacheEntry->addresses[0], FALSE);
+
+ if (r == 0)
+ ret = NetBTWaitForNameResponse(adapter, fd,
+ GetTickCount() + gWINSQueryTimeout,
+ NetBTNodeStatusAnswerCallback, &queryData);
+ else
+ ret = NRC_SYSTEM;
+ }
+ else
+ ret = NRC_CMDCAN;
+ }
+ closesocket(fd);
+ }
+ }
+ else
+ ret = NRC_CMDTMO;
+ }
+ else if (ret == NRC_CMDCAN)
+ ; /* do nothing, we were cancelled */
+ else
+ ret = NRC_CMDTMO;
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR NetBTAstat(void *adapt, PNCB ncb)
+{
+ NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+ UCHAR ret;
+
+ TRACE("adapt %p, NCB %p\n", adapt, ncb);
+
+ if (!adapter) return NRC_ENVNOTDEF;
+ if (!ncb) return NRC_INVADDRESS;
+ if (!ncb->ncb_buffer) return NRC_BADDR;
+ if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
+
+ if (ncb->ncb_callname[0] == '*')
+ {
+ DWORD physAddrLen;
+ MIB_IFROW ifRow;
+ PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+
+ memset(astat, 0, sizeof(ADAPTER_STATUS));
+ astat->rev_major = 3;
+ ifRow.dwIndex = adapter->ipr.dwIndex;
+ if (GetIfEntry(&ifRow) != NO_ERROR)
+ ret = NRC_BRIDGE;
+ else
+ {
+ physAddrLen = min(ifRow.dwPhysAddrLen, 6);
+ if (physAddrLen > 0)
+ memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
+ /* doubt anyone cares, but why not.. */
+ if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
+ astat->adapter_type = 0xff;
+ else
+ astat->adapter_type = 0xfe; /* for Ethernet */
+ astat->max_sess_pkt_size = 0xffff;
+ astat->xmit_success = adapter->xmit_success;
+ astat->recv_success = adapter->recv_success;
+ }
+ ret = NRC_GOODRET;
+ }
+ else
+ ret = NetBTAstatRemote(adapter, ncb);
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR NetBTFindName(void *adapt, PNCB ncb)
+{
+ NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+ UCHAR ret;
+ const NBNameCacheEntry *cacheEntry = NULL;
+ PFIND_NAME_HEADER foundName;
+
+ TRACE("adapt %p, NCB %p\n", adapt, ncb);
+
+ if (!adapter) return NRC_ENVNOTDEF;
+ if (!ncb) return NRC_INVADDRESS;
+ if (!ncb->ncb_buffer) return NRC_BADDR;
+ if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
+
+ foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
+ memset(foundName, 0, sizeof(FIND_NAME_HEADER));
+
+ ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+ if (ret == NRC_GOODRET)
+ {
+ if (cacheEntry)
+ {
+ DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
+ sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
+ DWORD ndx;
+
+ for (ndx = 0; ndx < spaceFor; ndx++)
+ {
+ PFIND_NAME_BUFFER findNameBuffer;
+
+ findNameBuffer =
+ (PFIND_NAME_BUFFER)((PUCHAR)foundName +
+ sizeof(FIND_NAME_HEADER) + foundName->node_count *
+ sizeof(FIND_NAME_BUFFER));
+ memset(findNameBuffer->destination_addr, 0, 2);
+ memcpy(findNameBuffer->destination_addr + 2,
+ &adapter->ipr.dwAddr, sizeof(DWORD));
+ memset(findNameBuffer->source_addr, 0, 2);
+ memcpy(findNameBuffer->source_addr + 2,
+ &cacheEntry->addresses[ndx], sizeof(DWORD));
+ foundName->node_count++;
+ }
+ if (spaceFor < cacheEntry->numAddresses)
+ ret = NRC_BUFLEN;
+ }
+ else
+ ret = NRC_CMDTMO;
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
+ const UCHAR *callingName)
+{
+ UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
+ int r;
+ unsigned int len = 0;
+ DWORD bytesSent, bytesReceived, recvFlags = 0;
+ WSABUF wsaBuf;
+
+ buffer[0] = NBSS_REQ;
+ buffer[1] = 0;
+
+ len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
+ len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
+
+ NBR_ADDWORD(&buffer[2], len);
+
+ wsaBuf.len = len + NBSS_HDRSIZE;
+ wsaBuf.buf = (char*)buffer;
+
+ r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
+ if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
+ {
+ ERR("send failed\n");
+ return NRC_SABORT;
+ }
+
+ /* I've already set the recv timeout on this socket (if it supports it), so
+ * just block. Hopefully we'll always receive the session acknowledgement
+ * within one timeout.
+ */
+ wsaBuf.len = NBSS_HDRSIZE + 1;
+ r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
+ if (r < 0 || bytesReceived < NBSS_HDRSIZE)
+ ret = NRC_SABORT;
+ else if (buffer[0] == NBSS_NACK)
+ {
+ if (r == NBSS_HDRSIZE + 1)
+ {
+ switch (buffer[NBSS_HDRSIZE])
+ {
+ case NBSS_ERR_INSUFFICIENT_RESOURCES:
+ ret = NRC_REMTFUL;
+ break;
+ default:
+ ret = NRC_NOCALL;
+ }
+ }
+ else
+ ret = NRC_NOCALL;
+ }
+ else if (buffer[0] == NBSS_RETARGET)
+ {
+ FIXME("Got a session retarget, can't deal\n");
+ ret = NRC_NOCALL;
+ }
+ else if (buffer[0] == NBSS_ACK)
+ ret = NRC_GOODRET;
+ else
+ ret = NRC_SYSTEM;
+
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
+{
+ NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+ UCHAR ret;
+ const NBNameCacheEntry *cacheEntry = NULL;
+
+ TRACE("adapt %p, ncb %p\n", adapt, ncb);
+
+ if (!adapter) return NRC_ENVNOTDEF;
+ if (!ncb) return NRC_INVADDRESS;
+ if (!sess) return NRC_BADDR;
+
+ ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+ if (ret == NRC_GOODRET)
+ {
+ if (cacheEntry && cacheEntry->numAddresses > 0)
+ {
+ SOCKET fd;
+
+ fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+ WSA_FLAG_OVERLAPPED);
+ if (fd != INVALID_SOCKET)
+ {
+ DWORD timeout;
+ struct sockaddr_in sin;
+
+ if (ncb->ncb_rto > 0)
+ {
+ timeout = ncb->ncb_rto * 500;
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
+ sizeof(timeout));
+ }
+ if (ncb->ncb_rto > 0)
+ {
+ timeout = ncb->ncb_sto * 500;
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
+ sizeof(timeout));
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
+ sizeof(sin.sin_addr));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(PORT_NBSS);
+ /* FIXME: use nonblocking mode for the socket, check the
+ * cancel flag periodically
+ */
+ if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
+ == SOCKET_ERROR)
+ ret = NRC_CMDTMO;
+ else
+ {
+ static UCHAR fakedCalledName[] = "*SMBSERVER";
+ const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
+ ? fakedCalledName : cacheEntry->nbname;
+
+ ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
+ if (ret != NRC_GOODRET && calledParty[0] == '*')
+ {
+ FIXME("NBT session to \"*SMBSERVER\" refused,\n");
+ FIXME("should try finding name using ASTAT\n");
+ }
+ }
+ if (ret != NRC_GOODRET)
+ closesocket(fd);
+ else
+ {
+ NetBTSession *session = HeapAlloc(
+ GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
+
+ if (session)
+ {
+ session->fd = fd;
+ InitializeCriticalSection(&session->cs);
+ *sess = session;
+ }
+ else
+ {
+ ret = NRC_OSRESNOTAV;
+ closesocket(fd);
+ }
+ }
+ }
+ else
+ ret = NRC_OSRESNOTAV;
+ }
+ else
+ ret = NRC_NAMERR;
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+/* Notice that I don't protect against multiple thread access to NetBTSend.
+ * This is because I don't update any data in the adapter, and I only make a
+ * single call to WSASend, which I assume to act atomically (not interleaving
+ * data from other threads).
+ * I don't lock, because I only depend on the fd being valid, and this won't be
+ * true until a session setup is completed.
+ */
+static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb)
+{
+ NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+ NetBTSession *session = (NetBTSession *)sess;
+ UCHAR buffer[NBSS_HDRSIZE], ret;
+ int r;
+ WSABUF wsaBufs[2];
+ DWORD bytesSent;
+
+ TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
+
+ if (!adapter) return NRC_ENVNOTDEF;
+ if (!ncb) return NRC_INVADDRESS;
+ if (!ncb->ncb_buffer) return NRC_BADDR;
+ if (!session) return NRC_SNUMOUT;
+ if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
+
+ buffer[0] = NBSS_MSG;
+ buffer[1] = 0;
+ NBR_ADDWORD(&buffer[2], ncb->ncb_length);
+
+ wsaBufs[0].len = NBSS_HDRSIZE;
+ wsaBufs[0].buf = (char*)buffer;
+ wsaBufs[1].len = ncb->ncb_length;
+ wsaBufs[1].buf = (char*)ncb->ncb_buffer;
+
+ r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]),
+ &bytesSent, 0, NULL, NULL);
+ if (r == SOCKET_ERROR)
+ {
+ NetBIOSHangupSession(ncb);
+ ret = NRC_SABORT;
+ }
+ else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
+ {
+ FIXME("Only sent %ld bytes (of %d), hanging up session\n", bytesSent,
+ NBSS_HDRSIZE + ncb->ncb_length);
+ NetBIOSHangupSession(ncb);
+ ret = NRC_SABORT;
+ }
+ else
+ {
+ ret = NRC_GOODRET;
+ adapter->xmit_success++;
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
+{
+ NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+ NetBTSession *session = (NetBTSession *)sess;
+ UCHAR buffer[NBSS_HDRSIZE], ret;
+ int r;
+ WSABUF wsaBufs[2];
+ DWORD bufferCount, bytesReceived, flags;
+
+ TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
+
+ if (!adapter) return NRC_ENVNOTDEF;
+ if (!ncb) return NRC_BADDR;
+ if (!ncb->ncb_buffer) return NRC_BADDR;
+ if (!session) return NRC_SNUMOUT;
+ if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
+
+ EnterCriticalSection(&session->cs);
+ bufferCount = 0;
+ if (session->bytesPending == 0)
+ {
+ bufferCount++;
+ wsaBufs[0].len = NBSS_HDRSIZE;
+ wsaBufs[0].buf = (char*)buffer;
+ }
+ wsaBufs[bufferCount].len = ncb->ncb_length;
+ wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer;
+ bufferCount++;
+
+ flags = 0;
+ /* FIXME: should poll a bit so I can check the cancel flag */
+ r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
+ NULL, NULL);
+ if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
+ {
+ LeaveCriticalSection(&session->cs);
+ ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
+ NetBIOSHangupSession(ncb);
+ ret = NRC_SABORT;
+ }
+ else if (NCB_CANCELLED(ncb))
+ {
+ LeaveCriticalSection(&session->cs);
+ ret = NRC_CMDCAN;
+ }
+ else
+ {
+ if (bufferCount == 2)
+ {
+ if (buffer[0] == NBSS_KEEPALIVE)
+ {
+ LeaveCriticalSection(&session->cs);
+ FIXME("Oops, received a session keepalive and lost my place\n");
+ /* need to read another session header until we get a session
+ * message header. */
+ NetBIOSHangupSession(ncb);
+ ret = NRC_SABORT;
+ }
+ else if (buffer[0] != NBSS_MSG)
+ {
+ LeaveCriticalSection(&session->cs);
+ FIXME("Received unexpected session msg type %d\n", buffer[0]);
+ NetBIOSHangupSession(ncb);
+ ret = NRC_SABORT;
+ }
+ else
+ {
+ if (buffer[1] & NBSS_EXTENSION)
+ {
+ LeaveCriticalSection(&session->cs);
+ FIXME("Received a message that's too long for my taste\n");
+ NetBIOSHangupSession(ncb);
+ ret = NRC_SABORT;
+ }
+ else
+ {
+ session->bytesPending = NBSS_HDRSIZE
+ + NBR_GETWORD(&buffer[2]) - bytesReceived;
+ ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
+ LeaveCriticalSection(&session->cs);
+ }
+ }
+ }
+ else
+ {
+ if (bytesReceived < session->bytesPending)
+ session->bytesPending -= bytesReceived;
+ else
+ session->bytesPending = 0;
+ LeaveCriticalSection(&session->cs);
+ ncb->ncb_length = bytesReceived;
+ }
+ if (session->bytesPending > 0)
+ ret = NRC_INCOMP;
+ else
+ {
+ ret = NRC_GOODRET;
+ adapter->recv_success++;
+ }
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR NetBTHangup(void *adapt, void *sess)
+{
+ NetBTSession *session = (NetBTSession *)sess;
+
+ TRACE("adapt %p, session %p\n", adapt, session);
+
+ if (!session) return NRC_SNUMOUT;
+
+ /* I don't lock the session, because NetBTRecv knows not to decrement
+ * past 0, so if a receive completes after this it should still deal.
+ */
+ closesocket(session->fd);
+ session->fd = INVALID_SOCKET;
+ session->bytesPending = 0;
+ DeleteCriticalSection(&session->cs);
+ HeapFree(GetProcessHeap(), 0, session);
+
+ return NRC_GOODRET;
+}
+
+static void NetBTCleanupAdapter(void *adapt)
+{
+ TRACE("adapt %p\n", adapt);
+ if (adapt)
+ {
+ NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+
+ if (adapter->nameCache)
+ NBNameCacheDestroy(adapter->nameCache);
+ HeapFree(GetProcessHeap(), 0, adapt);
+ }
+}
+
+static void NetBTCleanup(void)
+{
+ TRACE("\n");
+ if (gNameCache)
+ {
+ NBNameCacheDestroy(gNameCache);
+ gNameCache = NULL;
+ }
+}
+
+static UCHAR NetBTRegisterAdapter(PMIB_IPADDRROW ipRow)
+{
+ UCHAR ret;
+ NetBTAdapter *adapter;
+
+ if (!ipRow) return NRC_BADDR;
+
+ adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter));
+ if (adapter)
+ {
+ memcpy(&adapter->ipr, ipRow, sizeof(MIB_IPADDRROW));
+ if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
+ {
+ NetBTCleanupAdapter(adapter);
+ ret = NRC_SYSTEM;
+ }
+ else
+ ret = NRC_GOODRET;
+ }
+ else
+ ret = NRC_OSRESNOTAV;
+ return ret;
+}
+
+/* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to
+ * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
+ * NetBIOS adapter table. For each callback, checks if the passed-in adapt
+ * has an entry in the table; if so, this adapter was enumerated previously,
+ * and it's enabled. As a flag, the table's dwAddr entry is changed to
+ * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
+ * The NetBTEnum function will add any remaining adapters from the
+ * MIB_IPADDRTABLE to the NetBIOS adapter table.
+ */
+static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
+{
+ BOOL ret;
+ PMIB_IPADDRTABLE table = (PMIB_IPADDRTABLE)closure;
+
+ if (table && data)
+ {
+ DWORD ndx;
+
+ ret = FALSE;
+ for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
+ {
+ const NetBTAdapter *adapter = (const NetBTAdapter *)data->data;
+
+ if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
+ {
+ NetBIOSEnableAdapter(data->lana);
+ table->table[ndx].dwAddr = INADDR_LOOPBACK;
+ ret = TRUE;
+ }
+ }
+ }
+ else
+ ret = FALSE;
+ return ret;
+}
+
+/* Enumerates adapters by:
+ * - retrieving the IP address table for the local machine
+ * - eliminating loopback addresses from the table
+ * - eliminating redundant addresses, that is, multiple addresses on the same
+ * subnet
+ * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
+ * data. The callback reenables each adapter that's already in the NetBIOS
+ * table. After NetBIOSEnumAdapters returns, this function adds any remaining
+ * adapters to the NetBIOS table.
+ */
+static UCHAR NetBTEnum(void)
+{
+ UCHAR ret;
+ DWORD size = 0;
+
+ TRACE("\n");
+
+ if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
+ {
+ PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
+ DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
+ sizeof(MIB_IPADDRROW) + 1;
+
+ ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+ if (ipAddrs)
+ coalesceTable = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
+ (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
+ if (ipAddrs && coalesceTable)
+ {
+ if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
+ {
+ DWORD ndx;
+
+ for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
+ {
+ if ((ipAddrs->table[ndx].dwAddr &
+ ipAddrs->table[ndx].dwMask) !=
+ htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
+ {
+ BOOL newNetwork = TRUE;
+ DWORD innerIndex;
+
+ /* make sure we don't have more than one entry
+ * for a subnet */
+ for (innerIndex = 0; newNetwork &&
+ innerIndex < coalesceTable->dwNumEntries; innerIndex++)
+ if ((ipAddrs->table[ndx].dwAddr &
+ ipAddrs->table[ndx].dwMask) ==
+ (coalesceTable->table[innerIndex].dwAddr
+ & coalesceTable->table[innerIndex].dwMask))
+ newNetwork = FALSE;
+
+ if (newNetwork)
+ memcpy(&coalesceTable->table[
+ coalesceTable->dwNumEntries++],
+ &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
+ }
+ }
+
+ NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
+ coalesceTable);
+ ret = NRC_GOODRET;
+ for (ndx = 0; ret == NRC_GOODRET &&
+ ndx < coalesceTable->dwNumEntries; ndx++)
+ if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
+ ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
+ }
+ else
+ ret = NRC_SYSTEM;
+ HeapFree(GetProcessHeap(), 0, ipAddrs);
+ HeapFree(GetProcessHeap(), 0, coalesceTable);
+ }
+ else
+ ret = NRC_OSRESNOTAV;
+ }
+ else
+ ret = NRC_SYSTEM;
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r',
+ 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v',
+ 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' };
+static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u',
+ 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r',
+ 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t',
+ 'e','r','s','\0' };
+static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' };
+static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m',
+ 'e','Q','u','e','r','y','C','o','u','n','t','\0' };
+static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m',
+ 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
+static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v',
+ 'Q','u','e','r','y','C','o','u','n','t','\0' };
+static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v',
+ 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
+static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' };
+static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o',
+ 'u','t','\0' };
+static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\',
+ 'W','i','n','e','\\','N','e','t','w','o','r','k','\0' };
+
+/* Initializes global variables and registers the NetBT transport */
+void NetBTInit(void)
+{
+ HKEY hKey;
+ NetBIOSTransport transport;
+ LONG ret;
+
+ TRACE("\n");
+
+ gEnableDNS = TRUE;
+ gBCastQueries = BCAST_QUERIES;
+ gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
+ gWINSQueries = WINS_QUERIES;
+ gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
+ gNumWINSServers = 0;
+ memset(gWINSServers, 0, sizeof(gWINSServers));
+ gScopeID[0] = '\0';
+ gCacheTimeout = CACHE_TIMEOUT;
+
+ /* Try to open the Win9x NetBT configuration key */
+ ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey);
+ /* If that fails, try the WinNT NetBT configuration key */
+ if (ret != ERROR_SUCCESS)
+ ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ,
+ &hKey);
+ if (ret == ERROR_SUCCESS)
+ {
+ DWORD dword, size;
+
+ size = sizeof(dword);
+ if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL,
+ (LPBYTE)&dword, &size) == ERROR_SUCCESS)
+ gEnableDNS = dword;
+ size = sizeof(dword);
+ if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL,
+ (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
+ && dword <= MAX_QUERIES)
+ gBCastQueries = dword;
+ size = sizeof(dword);
+ if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL,
+ (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT
+ && dword <= MAX_QUERY_TIMEOUT)
+ gBCastQueryTimeout = dword;
+ size = sizeof(dword);
+ if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL,
+ (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
+ && dword <= MAX_QUERIES)
+ gWINSQueries = dword;
+ size = sizeof(dword);
+ if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL,
+ (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT
+ && dword <= MAX_QUERY_TIMEOUT)
+ gWINSQueryTimeout = dword;
+ size = MAX_DOMAIN_NAME_LEN - 1;
+ if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size)
+ == ERROR_SUCCESS)
+ {
+ /* convert into L2-encoded version, suitable for use by
+ NetBTNameEncode */
+ char *ptr, *lenPtr;
+
+ for (ptr = gScopeID + 1; *ptr &&
+ ptr - gScopeID < MAX_DOMAIN_NAME_LEN; )
+ {
+ for (lenPtr = ptr - 1, *lenPtr = 0; *ptr && *ptr != '.' &&
+ ptr - gScopeID < MAX_DOMAIN_NAME_LEN; ptr++)
+ *lenPtr += 1;
+ ptr++;
+ }
+ }
+ if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL,
+ (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT)
+ gCacheTimeout = dword;
+ RegCloseKey(hKey);
+ }
+ /* WINE-specific NetBT registry settings. Because our adapter naming is
+ * different than MS', we can't do per-adapter WINS configuration in the
+ * same place. Just do a global WINS configuration instead.
+ */
+ /* @@ Wine registry key: HKCU\Software\Wine\Network */
+ if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS)
+ {
+ static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" };
+ char nsString[16];
+ DWORD size, ndx;
+
+ for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]);
+ ndx++)
+ {
+ size = sizeof(nsString) / sizeof(char);
+ if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
+ (LPBYTE)nsString, &size) == ERROR_SUCCESS)
+ {
+ unsigned long addr = inet_addr(nsString);
+
+ if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
+ gWINSServers[gNumWINSServers++] = addr;
+ }
+ }
+ RegCloseKey(hKey);
+ }
+
+ transport.enumerate = NetBTEnum;
+ transport.astat = NetBTAstat;
+ transport.findName = NetBTFindName;
+ transport.call = NetBTCall;
+ transport.send = NetBTSend;
+ transport.recv = NetBTRecv;
+ transport.hangup = NetBTHangup;
+ transport.cleanupAdapter = NetBTCleanupAdapter;
+ transport.cleanup = NetBTCleanup;
+ memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
+ NetBIOSRegisterTransport(gTransportID, &transport);
+}
--- /dev/null
+/* Copyright 2001 Mike McCormack
+ * Copyright 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include "wine/debug.h"
+#include "netbios.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+HMODULE NETAPI32_hModule = 0;
+
+BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
+
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ {
+ DisableThreadLibraryCalls(hinstDLL);
+ NETAPI32_hModule = hinstDLL;
+ NetBIOSInit();
+ NetBTInit();
+ break;
+ }
+ case DLL_PROCESS_DETACH:
+ {
+ NetBIOSShutdown();
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+NET_API_STATUS WINAPI NetServerEnum(
+ LPCWSTR servername,
+ DWORD level,
+ LPBYTE* bufptr,
+ DWORD prefmaxlen,
+ LPDWORD entriesread,
+ LPDWORD totalentries,
+ DWORD servertype,
+ LPCWSTR domain,
+ LPDWORD resume_handle
+)
+{
+ FIXME("Stub (%s %ld %p %ld %p %p %ld %s %p)\n", debugstr_w(servername),
+ level, bufptr, prefmaxlen, entriesread, totalentries, servertype,
+ debugstr_w(domain), resume_handle);
+
+ return ERROR_NO_BROWSER_SERVERS_FOUND;
+}
+
+
+/************************************************************
+ * NetServerGetInfo (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetServerGetInfo(LMSTR servername, DWORD level, LPBYTE* bufptr)
+{
+ FIXME("stub (%p, %ld, %p)\n", servername, level, bufptr);
+ return ERROR_ACCESS_DENIED;
+}
+
+
+/************************************************************
+ * NetStatisticsGet (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetStatisticsGet(LPWSTR server, LPWSTR service,
+ DWORD level, DWORD options,
+ LPBYTE *bufptr)
+{
+ TRACE("(%p, %p, %ld, %ld, %p)\n", server, service, level, options, bufptr);
+ return NERR_InternalError;
+}
+
+DWORD WINAPI NetpNetBiosStatusToApiStatus(DWORD nrc)
+{
+ DWORD ret;
+
+ switch (nrc)
+ {
+ case NRC_GOODRET:
+ ret = NO_ERROR;
+ break;
+ case NRC_NORES:
+ ret = NERR_NoNetworkResource;
+ break;
+ case NRC_DUPNAME:
+ ret = NERR_AlreadyExists;
+ break;
+ case NRC_NAMTFUL:
+ ret = NERR_TooManyNames;
+ break;
+ case NRC_ACTSES:
+ ret = NERR_DeleteLater;
+ break;
+ case NRC_REMTFUL:
+ ret = ERROR_REM_NOT_LIST;
+ break;
+ case NRC_NOCALL:
+ ret = NERR_NameNotFound;
+ break;
+ case NRC_NOWILD:
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ case NRC_INUSE:
+ ret = NERR_DuplicateName;
+ break;
+ case NRC_NAMERR:
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ case NRC_NAMCONF:
+ ret = NERR_DuplicateName;
+ break;
+ default:
+ ret = NERR_NetworkError;
+ }
+ return ret;
+}
--- /dev/null
+@ stub I_BrowserDebugCall
+@ stub I_BrowserDebugTrace
+@ stdcall I_BrowserQueryEmulatedDomains(wstr ptr ptr)
+@ stub I_BrowserQueryOtherDomains
+@ stub I_BrowserQueryStatistics
+@ stub I_BrowserResetNetlogonState
+@ stub I_BrowserResetStatistics
+@ stub I_BrowserServerEnum
+@ stdcall I_BrowserSetNetlogonState(wstr wstr wstr long)
+@ stub I_NetAccountDeltas
+@ stub I_NetAccountSync
+@ stub I_NetDatabaseDeltas
+@ stub I_NetDatabaseRedo
+@ stub I_NetDatabaseSync2
+@ stub I_NetDatabaseSync
+@ stub I_NetDfsCreateExitPoint
+@ stub I_NetDfsCreateLocalPartition
+@ stub I_NetDfsDeleteExitPoint
+@ stub I_NetDfsDeleteLocalPartition
+@ stub I_NetDfsFixLocalVolume
+@ stub I_NetDfsGetVersion
+@ stub I_NetDfsIsThisADomainName
+@ stub I_NetDfsModifyPrefix
+@ stub I_NetDfsSetLocalVolumeState
+@ stub I_NetDfsSetServerInfo
+@ stub I_NetGetDCList
+@ stub I_NetListCanonicalize
+@ stub I_NetListTraverse
+@ stub I_NetLogonControl2
+@ stub I_NetLogonControl
+@ stub I_NetLogonSamLogoff
+@ stub I_NetLogonSamLogon
+@ stub I_NetLogonUasLogoff
+@ stub I_NetLogonUasLogon
+@ stub I_NetNameCanonicalize
+@ stdcall I_NetNameCompare(ptr wstr wstr ptr ptr)
+@ stdcall I_NetNameValidate(ptr wstr ptr ptr)
+@ stub I_NetPathCanonicalize
+@ stub I_NetPathCompare
+@ stub I_NetPathType
+@ stub I_NetServerAuthenticate2
+@ stub I_NetServerAuthenticate
+@ stub I_NetServerPasswordSet
+@ stub I_NetServerReqChallenge
+@ stub I_NetServerSetServiceBits
+@ stub I_NetServerSetServiceBitsEx
+@ stub NetAlertRaise
+@ stub NetAlertRaiseEx
+@ stdcall NetApiBufferAllocate(long ptr)
+@ stdcall NetApiBufferFree(ptr)
+@ stdcall NetApiBufferReallocate(ptr long ptr)
+@ stdcall NetApiBufferSize(ptr ptr)
+@ stub NetAuditClear
+@ stub NetAuditRead
+@ stub NetAuditWrite
+@ stub NetBrowserStatisticsGet
+@ stub NetConfigGet
+@ stub NetConfigGetAll
+@ stub NetConfigSet
+@ stub NetConnectionEnum
+@ stub NetDfsAdd
+@ stub NetDfsEnum
+@ stub NetDfsGetInfo
+@ stub NetDfsManagerGetConfigInfo
+@ stub NetDfsMove
+@ stub NetDfsRemove
+@ stub NetDfsRename
+@ stub NetDfsSetInfo
+@ stub NetEnumerateTrustedDomains
+@ stub NetErrorLogClear
+@ stub NetErrorLogRead
+@ stub NetErrorLogWrite
+@ stub NetFileClose
+@ stub NetFileEnum
+@ stub NetFileGetInfo
+@ stub NetGetAnyDCName
+@ stdcall NetGetDCName(wstr wstr ptr)
+@ stub NetGetDisplayInformationIndex
+@ stdcall NetGetJoinInformation(wstr ptr ptr)
+@ stub NetGroupAdd
+@ stub NetGroupAddUser
+@ stub NetGroupDel
+@ stub NetGroupDelUser
+@ stub NetGroupEnum
+@ stub NetGroupGetInfo
+@ stub NetGroupGetUsers
+@ stub NetGroupSetInfo
+@ stub NetGroupSetUsers
+@ stdcall NetLocalGroupAdd(wstr long ptr ptr)
+@ stub NetLocalGroupAddMember
+@ stub NetLocalGroupAddMembers
+@ stub NetLocalGroupDel
+@ stub NetLocalGroupDelMember
+@ stub NetLocalGroupDelMembers
+@ stub NetLocalGroupEnum
+@ stub NetLocalGroupGetInfo
+@ stub NetLocalGroupGetMembers
+@ stub NetLocalGroupSetInfo
+@ stdcall NetLocalGroupSetMembers(wstr wstr long ptr ptr)
+@ stub NetMessageBufferSend
+@ stub NetMessageNameAdd
+@ stub NetMessageNameDel
+@ stub NetMessageNameEnum
+@ stub NetMessageNameGetInfo
+@ stdcall NetQueryDisplayInformation(wstr long long long long ptr ptr)
+@ stub NetRemoteComputerSupports
+@ stub NetRemoteTOD
+@ stub NetReplExportDirAdd
+@ stub NetReplExportDirDel
+@ stub NetReplExportDirEnum
+@ stub NetReplExportDirGetInfo
+@ stub NetReplExportDirLock
+@ stub NetReplExportDirSetInfo
+@ stub NetReplExportDirUnlock
+@ stub NetReplGetInfo
+@ stub NetReplImportDirAdd
+@ stub NetReplImportDirDel
+@ stub NetReplImportDirEnum
+@ stub NetReplImportDirGetInfo
+@ stub NetReplImportDirLock
+@ stub NetReplImportDirUnlock
+@ stub NetReplSetInfo
+@ stub NetRplAdapterAdd
+@ stub NetRplAdapterDel
+@ stub NetRplAdapterEnum
+@ stub NetRplBootAdd
+@ stub NetRplBootDel
+@ stub NetRplBootEnum
+@ stub NetRplClose
+@ stub NetRplConfigAdd
+@ stub NetRplConfigDel
+@ stub NetRplConfigEnum
+@ stub NetRplGetInfo
+@ stub NetRplOpen
+@ stub NetRplProfileAdd
+@ stub NetRplProfileClone
+@ stub NetRplProfileDel
+@ stub NetRplProfileEnum
+@ stub NetRplProfileGetInfo
+@ stub NetRplProfileSetInfo
+@ stub NetRplSetInfo
+@ stub NetRplSetSecurity
+@ stub NetRplVendorAdd
+@ stub NetRplVendorDel
+@ stub NetRplVendorEnum
+@ stub NetRplWkstaAdd
+@ stub NetRplWkstaClone
+@ stub NetRplWkstaDel
+@ stub NetRplWkstaEnum
+@ stub NetRplWkstaGetInfo
+@ stub NetRplWkstaSetInfo
+@ stub NetScheduleJobAdd
+@ stub NetScheduleJobDel
+@ stub NetScheduleJobEnum
+@ stub NetScheduleJobGetInfo
+@ stub NetServerComputerNameAdd
+@ stub NetServerComputerNameDel
+@ stub NetServerDiskEnum
+@ stdcall NetServerEnum(wstr long ptr long ptr ptr long wstr ptr)
+@ stub NetServerEnumEx
+@ stdcall NetServerGetInfo(wstr long ptr)
+@ stub NetServerSetInfo
+@ stub NetServerTransportAdd
+@ stub NetServerTransportAddEx
+@ stub NetServerTransportDel
+@ stub NetServerTransportEnum
+@ stub NetServiceControl
+@ stub NetServiceEnum
+@ stub NetServiceGetInfo
+@ stub NetServiceInstall
+@ stub NetSessionDel
+@ stub NetSessionEnum
+@ stub NetSessionGetInfo
+@ stub NetShareAdd
+@ stub NetShareCheck
+@ stub NetShareDel
+@ stub NetShareDelSticky
+@ stdcall NetShareEnum(wstr long ptr long ptr ptr ptr)
+@ stub NetShareEnumSticky
+@ stub NetShareGetInfo
+@ stub NetShareSetInfo
+@ stdcall NetStatisticsGet(wstr wstr long long ptr)
+@ stub NetUseAdd
+@ stub NetUseDel
+@ stub NetUseEnum
+@ stub NetUseGetInfo
+@ stdcall NetUserAdd(wstr long ptr ptr)
+@ stub NetUserChangePassword
+@ stdcall NetUserDel(wstr wstr)
+@ stdcall NetUserEnum(wstr long long ptr long ptr ptr ptr)
+@ stub NetUserGetGroups
+@ stdcall NetUserGetInfo(wstr wstr long ptr)
+@ stub NetUserGetLocalGroups
+@ stdcall NetUserModalsGet(wstr long ptr)
+@ stub NetUserModalsSet
+@ stub NetUserSetGroups
+@ stub NetUserSetInfo
+@ stdcall NetWkstaGetInfo(wstr long ptr)
+@ stub NetWkstaSetInfo
+@ stub NetWkstaTransportAdd
+@ stub NetWkstaTransportDel
+@ stdcall NetWkstaTransportEnum (wstr long ptr long ptr ptr ptr)
+@ stub NetWkstaUserEnum
+@ stdcall NetWkstaUserGetInfo(wstr long ptr)
+@ stub NetWkstaUserSetInfo
+@ stdcall NetapipBufferAllocate(long ptr) NetApiBufferAllocate
+@ stdcall Netbios(ptr)
+@ stub NetpAccessCheck
+@ stub NetpAccessCheckAndAudit
+@ stub NetpAllocConfigName
+@ stub NetpAllocStrFromStr
+@ stub NetpAllocStrFromWStr
+@ stub NetpAllocTStrFromString
+@ stub NetpAllocWStrFromStr
+@ stub NetpAllocWStrFromWStr
+@ stub NetpApiStatusToNtStatus
+@ stub NetpAssertFailed
+@ stub NetpCloseConfigData
+@ stub NetpCopyStringToBuffer
+@ stub NetpCreateSecurityObject
+@ stub NetpDbgDisplayServerInfo
+@ stub NetpDbgPrint
+@ stdcall NetpDeleteSecurityObject(long) ntdll.RtlDeleteSecurityObject
+@ stdcall NetpGetComputerName(ptr)
+@ stub NetpGetConfigBool
+@ stub NetpGetConfigDword
+@ stub NetpGetConfigTStrArray
+@ stub NetpGetConfigValue
+@ stub NetpGetDomainName
+@ stub NetpGetFileSecurity
+@ stub NetpGetPrivilege
+@ stub NetpHexDump
+@ stdcall NetpInitOemString(ptr str) ntdll.RtlInitAnsiString
+@ stub NetpIsRemote
+@ stub NetpIsUncComputerNameValid
+@ stub NetpLocalTimeZoneOffset
+@ stub NetpLogonPutUnicodeString
+@ stub NetpNetBiosAddName
+@ stub NetpNetBiosCall
+@ stub NetpNetBiosDelName
+@ stub NetpNetBiosGetAdapterNumbers
+@ stub NetpNetBiosHangup
+@ stub NetpNetBiosReceive
+@ stub NetpNetBiosReset
+@ stub NetpNetBiosSend
+@ stdcall NetpNetBiosStatusToApiStatus(long)
+@ stub NetpNtStatusToApiStatus
+@ stub NetpOpenConfigData
+@ stub NetpPackString
+@ stub NetpReleasePrivilege
+@ stub NetpSetConfigBool
+@ stub NetpSetConfigDword
+@ stub NetpSetConfigTStrArray
+@ stub NetpSetFileSecurity
+@ stub NetpSmbCheck
+@ stub NetpStringToNetBiosName
+@ stub NetpTStrArrayEntryCount
+@ stub NetpwNameCanonicalize
+@ stub NetpwNameCompare
+@ stub NetpwNameValidate
+@ stub NetpwPathCanonicalize
+@ stub NetpwPathCompare
+@ stub NetpwPathType
+@ stub NlBindingAddServerToCache
+@ stub NlBindingRemoveServerFromCache
+@ stub NlBindingSetAuthInfo
+@ stub RxNetAccessAdd
+@ stub RxNetAccessDel
+@ stub RxNetAccessEnum
+@ stub RxNetAccessGetInfo
+@ stub RxNetAccessGetUserPerms
+@ stub RxNetAccessSetInfo
+@ stub RxNetServerEnum
+@ stub RxNetUserPasswordSet
+@ stub RxRemoteApi
--- /dev/null
+<module name="netapi32" type="win32dll" baseaddress="${BASEADDRESS_NETAPI32}" installbase="system32" installname="netapi32.dll" allowwarnings="true">
+ <importlibrary definition="netapi32.spec.def" />
+ <include base="netapi32">.</include>
+ <include base="ReactOS">include/wine</include>
+ <define name="__REACTOS__" />
+ <define name="__USE_W32API" />
+ <define name="_WIN32_IE">0x600</define>
+ <define name="_WIN32_WINNT">0x501</define>
+ <define name="WINVER">0x501</define>
+ <define name="_SVRAPI_" />
+ <library>wine</library>
+ <library>ntdll</library>
+ <library>kernel32</library>
+ <library>advapi32</library>
+ <library>ws2_32</library>
+ <library>iphlpapi</library>
+ <file>access.c</file>
+ <file>apibuf.c</file>
+ <file>browsr.c</file>
+ <file>nbcmdqueue.c</file>
+ <file>nbnamecache.c</file>
+ <file>nbt.c</file>
+ <file>netapi32.c</file>
+ <file>netbios.c</file>
+ <file>wksta.c</file>
+ <file>netapi32.spec</file>
+</module>
--- /dev/null
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 internal functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, writ
+e to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __WINE_NETAPI32_MISC_H
+#define __WINE_NETAPI32_MISC_H
+
+extern BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName);
+
+#define NETAPI_ForceLocalComputer(ServerName, FailureCode) \
+ if (!NETAPI_IsLocalComputer(ServerName)) \
+ { \
+ FIXME("Action Implemented for local computer only. " \
+ "Requested for server %s\n", debugstr_w(ServerName)); \
+ return FailureCode; \
+ }
+
+#endif
--- /dev/null
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "config.h"
+#include "wine/debug.h"
+#include "nbcmdqueue.h"
+#include "netbios.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+/* This file provides a NetBIOS emulator that implements the NetBIOS interface,
+ * including thread safety and asynchronous call support. The protocol
+ * implementation is separate, with blocking (synchronous) functions.
+ */
+
+#define ADAPTERS_INCR 8
+#define DEFAULT_NUM_SESSIONS 16
+
+typedef struct _NetBIOSTransportTableEntry
+{
+ ULONG id;
+ NetBIOSTransport transport;
+} NetBIOSTransportTableEntry;
+
+typedef struct _NetBIOSSession
+{
+ BOOL inUse;
+ UCHAR state;
+ UCHAR local_name[NCBNAMSZ];
+ UCHAR remote_name[NCBNAMSZ];
+ void *data;
+} NetBIOSSession;
+
+/* This struct needs a little explanation, unfortunately. enabled is only
+ * used by nbInternalEnum (see). If transport_id is not 0 and transport
+ * is not NULL, the adapter is considered valid. (transport is a pointer to
+ * an entry in a NetBIOSTransportTableEntry.) data has data for the callers of
+ * NetBIOSEnumAdapters to be able to see. The lana is repeated there, even
+ * though I don't use it internally--it's for transports to use reenabling
+ * adapters using NetBIOSEnableAdapter.
+ */
+typedef struct _NetBIOSAdapter
+{
+ BOOL enabled;
+ BOOL shuttingDown;
+ LONG resetting;
+ ULONG transport_id;
+ NetBIOSTransport *transport;
+ NetBIOSAdapterImpl impl;
+ struct NBCmdQueue *cmdQueue;
+ CRITICAL_SECTION cs;
+ DWORD sessionsLen;
+ NetBIOSSession *sessions;
+} NetBIOSAdapter;
+
+typedef struct _NetBIOSAdapterTable {
+ CRITICAL_SECTION cs;
+ BOOL enumerated;
+ BOOL enumerating;
+ UCHAR tableSize;
+ NetBIOSAdapter *table;
+} NetBIOSAdapterTable;
+
+/* Just enough space for NBT right now */
+static NetBIOSTransportTableEntry gTransports[1];
+static UCHAR gNumTransports = 0;
+static NetBIOSAdapterTable gNBTable;
+
+static UCHAR nbResizeAdapterTable(UCHAR newSize)
+{
+ UCHAR ret;
+
+ if (gNBTable.table)
+ gNBTable.table = HeapReAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY, gNBTable.table,
+ newSize * sizeof(NetBIOSAdapter));
+ else
+ gNBTable.table = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY, newSize * sizeof(NetBIOSAdapter));
+ if (gNBTable.table)
+ {
+ gNBTable.tableSize = newSize;
+ ret = NRC_GOODRET;
+ }
+ else
+ ret = NRC_OSRESNOTAV;
+ return ret;
+}
+
+void NetBIOSInit(void)
+{
+ memset(&gNBTable, 0, sizeof(gNBTable));
+ InitializeCriticalSection(&gNBTable.cs);
+}
+
+void NetBIOSShutdown(void)
+{
+ UCHAR i;
+
+ EnterCriticalSection(&gNBTable.cs);
+ for (i = 0; i < gNBTable.tableSize; i++)
+ {
+ if (gNBTable.table[i].transport &&
+ gNBTable.table[i].transport->cleanupAdapter)
+ gNBTable.table[i].transport->cleanupAdapter(
+ gNBTable.table[i].impl.data);
+ }
+ for (i = 0; i < gNumTransports; i++)
+ if (gTransports[i].transport.cleanup)
+ gTransports[i].transport.cleanup();
+ LeaveCriticalSection(&gNBTable.cs);
+ DeleteCriticalSection(&gNBTable.cs);
+ HeapFree(GetProcessHeap(), 0, gNBTable.table);
+}
+
+BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport)
+{
+ BOOL ret;
+
+ TRACE(": transport 0x%08lx, p %p\n", id, transport);
+ if (!transport)
+ ret = FALSE;
+ else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0]))
+ {
+ FIXME("You tried to add %d transports, but I only have space for %d\n",
+ gNumTransports + 1, sizeof(gTransports) / sizeof(gTransports[0]));
+ ret = FALSE;
+ }
+ else
+ {
+ UCHAR i;
+
+ ret = FALSE;
+ for (i = 0; !ret && i < gNumTransports; i++)
+ {
+ if (gTransports[i].id == id)
+ {
+ WARN("Replacing NetBIOS transport ID %ld\n", id);
+ memcpy(&gTransports[i].transport, transport,
+ sizeof(NetBIOSTransport));
+ ret = TRUE;
+ }
+ }
+ if (!ret)
+ {
+ gTransports[gNumTransports].id = id;
+ memcpy(&gTransports[gNumTransports].transport, transport,
+ sizeof(NetBIOSTransport));
+ gNumTransports++;
+ ret = TRUE;
+ }
+ }
+ TRACE("returning %d\n", ret);
+ return ret;
+}
+
+/* In this, I acquire the table lock to make sure no one else is modifying it.
+ * This is _probably_ overkill since it should only be called during the
+ * context of a NetBIOSEnum call, but just to be safe..
+ */
+BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data)
+{
+ BOOL ret;
+ UCHAR i;
+
+ TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex,
+ data);
+ for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++)
+ ;
+ if (gTransports[i].id == transport)
+ {
+ NetBIOSTransport *transportPtr = &gTransports[i].transport;
+
+ TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport);
+
+ EnterCriticalSection(&gNBTable.cs);
+ ret = FALSE;
+ for (i = 0; i < gNBTable.tableSize &&
+ gNBTable.table[i].transport != 0; i++)
+ ;
+ if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1)
+ {
+ UCHAR newSize;
+
+ if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR)
+ newSize = gNBTable.tableSize + ADAPTERS_INCR;
+ else
+ newSize = MAX_LANA + 1;
+ nbResizeAdapterTable(newSize);
+ }
+ if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0)
+ {
+ TRACE(": registering as LANA %d\n", i);
+ gNBTable.table[i].transport_id = transport;
+ gNBTable.table[i].transport = transportPtr;
+ gNBTable.table[i].impl.lana = i;
+ gNBTable.table[i].impl.ifIndex = ifIndex;
+ gNBTable.table[i].impl.data = data;
+ gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap());
+ InitializeCriticalSection(&gNBTable.table[i].cs);
+ gNBTable.table[i].enabled = TRUE;
+ ret = TRUE;
+ }
+ LeaveCriticalSection(&gNBTable.cs);
+ }
+ else
+ ret = FALSE;
+ TRACE("returning %d\n", ret);
+ return ret;
+}
+
+/* In this, I acquire the table lock to make sure no one else is modifying it.
+ * This is _probably_ overkill since it should only be called during the
+ * context of a NetBIOSEnum call, but just to be safe..
+ */
+void NetBIOSEnableAdapter(UCHAR lana)
+{
+ TRACE(": %d\n", lana);
+ if (lana < gNBTable.tableSize)
+ {
+ EnterCriticalSection(&gNBTable.cs);
+ if (gNBTable.table[lana].transport != 0)
+ gNBTable.table[lana].enabled = TRUE;
+ LeaveCriticalSection(&gNBTable.cs);
+ }
+}
+
+static void nbShutdownAdapter(NetBIOSAdapter *adapter)
+{
+ if (adapter)
+ {
+ adapter->shuttingDown = TRUE;
+ NBCmdQueueCancelAll(adapter->cmdQueue);
+ if (adapter->transport->cleanupAdapter)
+ adapter->transport->cleanupAdapter(adapter->impl.data);
+ NBCmdQueueDestroy(adapter->cmdQueue);
+ DeleteCriticalSection(&adapter->cs);
+ memset(adapter, 0, sizeof(NetBIOSAdapter));
+ }
+}
+
+static void nbInternalEnum(void)
+{
+ UCHAR i;
+
+ EnterCriticalSection(&gNBTable.cs);
+ TRACE("before mark\n");
+ /* mark: */
+ for (i = 0; i < gNBTable.tableSize; i++)
+ if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
+ gNBTable.table[i].enabled = FALSE;
+
+ TRACE("marked, before store, %d transports\n", gNumTransports);
+ /* store adapters: */
+ for (i = 0; i < gNumTransports; i++)
+ if (gTransports[i].transport.enumerate)
+ gTransports[i].transport.enumerate();
+
+ TRACE("before sweep\n");
+ /* sweep: */
+ for (i = 0; i < gNBTable.tableSize; i++)
+ if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
+ nbShutdownAdapter(&gNBTable.table[i]);
+ gNBTable.enumerated = TRUE;
+ LeaveCriticalSection(&gNBTable.cs);
+}
+
+UCHAR NetBIOSNumAdapters(void)
+{
+ UCHAR ret, i;
+
+ if (!gNBTable.enumerated)
+ nbInternalEnum();
+ for (i = 0, ret = 0; i < gNBTable.tableSize; i++)
+ if (gNBTable.table[i].transport != 0)
+ ret++;
+ return ret;
+}
+
+void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
+ void *closure)
+{
+ TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb,
+ closure);
+ if (cb)
+ {
+ BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0;
+ UCHAR i, numLANAs = 0;
+
+ EnterCriticalSection(&gNBTable.cs);
+ if (!gNBTable.enumerating)
+ {
+ gNBTable.enumerating = TRUE;
+ nbInternalEnum();
+ gNBTable.enumerating = FALSE;
+ }
+ for (i = 0; i < gNBTable.tableSize; i++)
+ if (enumAll || gNBTable.table[i].transport_id == transport)
+ numLANAs++;
+ if (numLANAs > 0)
+ {
+ UCHAR lanaIndex = 0;
+
+ for (i = 0; i < gNBTable.tableSize; i++)
+ if (gNBTable.table[i].transport_id != 0 &&
+ (enumAll || gNBTable.table[i].transport_id == transport))
+ cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id,
+ &gNBTable.table[i].impl, closure);
+ }
+ LeaveCriticalSection(&gNBTable.cs);
+ }
+}
+
+static NetBIOSAdapter *nbGetAdapter(UCHAR lana)
+{
+ NetBIOSAdapter *ret = NULL;
+
+ TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize);
+ if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0
+ && gNBTable.table[lana].transport)
+ ret = &gNBTable.table[lana];
+ TRACE("returning %p\n", ret);
+ return ret;
+}
+
+static UCHAR nbEnum(PNCB ncb)
+{
+ PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer;
+ UCHAR i, ret;
+
+ TRACE(": ncb %p\n", ncb);
+
+ if (!lanas)
+ ret = NRC_BUFLEN;
+ else if (ncb->ncb_length < sizeof(LANA_ENUM))
+ ret = NRC_BUFLEN;
+ else
+ {
+ nbInternalEnum();
+ lanas->length = 0;
+ for (i = 0; i < gNBTable.tableSize; i++)
+ if (gNBTable.table[i].transport)
+ {
+ lanas->length++;
+ lanas->lana[i] = i;
+ }
+ ret = NRC_GOODRET;
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session);
+
+static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret;
+
+ TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+ if (!adapter) return NRC_BRIDGE;
+ if (!ncb) return NRC_INVADDRESS;
+
+ switch (ncb->ncb_command & 0x7f)
+ {
+ case NCBCANCEL:
+ case NCBADDNAME:
+ case NCBADDGRNAME:
+ case NCBDELNAME:
+ case NCBRESET:
+ case NCBSSTAT:
+ ret = NRC_CANCEL;
+ break;
+
+ /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated
+ * session if cancelled */
+ case NCBCALL:
+ case NCBSEND:
+ case NCBCHAINSEND:
+ case NCBSENDNA:
+ case NCBCHAINSENDNA:
+ case NCBHANGUP:
+ {
+ if (ncb->ncb_lsn >= adapter->sessionsLen)
+ ret = NRC_SNUMOUT;
+ else if (!adapter->sessions[ncb->ncb_lsn].inUse)
+ ret = NRC_SNUMOUT;
+ else
+ {
+ ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
+ if (ret == NRC_CMDCAN || ret == NRC_CANOCCR)
+ nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]);
+ }
+ break;
+ }
+
+ default:
+ ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+/* Resizes adapter to contain space for at least sessionsLen sessions.
+ * If allocating more space for sessions, sets the adapter's sessionsLen to
+ * sessionsLen. If the adapter's sessionsLen was already at least sessionsLen,
+ * does nothing. Does not modify existing sessions. Assumes the adapter is
+ * locked.
+ * Returns NRC_GOODRET on success, and something else on failure.
+ */
+static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen)
+{
+ UCHAR ret = NRC_GOODRET;
+
+ if (adapter && adapter->sessionsLen < sessionsLen)
+ {
+ NetBIOSSession *newSessions;
+
+ if (adapter->sessions)
+ newSessions = HeapReAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen *
+ sizeof(NetBIOSSession));
+ else
+ newSessions = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession));
+ if (newSessions)
+ {
+ adapter->sessions = newSessions;
+ adapter->sessionsLen = sessionsLen;
+ }
+ else
+ ret = NRC_OSRESNOTAV;
+ }
+ return ret;
+}
+
+static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret;
+
+ TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+ if (!adapter) return NRC_BRIDGE;
+ if (!ncb) return NRC_INVADDRESS;
+
+ if (InterlockedIncrement(&adapter->resetting) == 1)
+ {
+ UCHAR i, resizeTo;
+
+ NBCmdQueueCancelAll(adapter->cmdQueue);
+
+ EnterCriticalSection(&adapter->cs);
+ for (i = 0; i < adapter->sessionsLen; i++)
+ if (adapter->sessions[i].inUse)
+ nbInternalHangup(adapter, &adapter->sessions[i]);
+ if (!ncb->ncb_lsn)
+ resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS :
+ ncb->ncb_callname[0];
+ else if (adapter->sessionsLen == 0)
+ resizeTo = DEFAULT_NUM_SESSIONS;
+ else
+ resizeTo = 0;
+ if (resizeTo > 0)
+ ret = nbResizeAdapter(adapter, resizeTo);
+ else
+ ret = NRC_GOODRET;
+ LeaveCriticalSection(&adapter->cs);
+ }
+ else
+ ret = NRC_TOOMANY;
+ InterlockedDecrement(&adapter->resetting);
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret, i, spaceFor;
+ PSESSION_HEADER sstat;
+
+ TRACE(": adapter %p, NCB %p\n", adapter, ncb);
+
+ if (!adapter) return NRC_BADDR;
+ if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
+ if (!ncb) return NRC_INVADDRESS;
+ if (!ncb->ncb_buffer) return NRC_BADDR;
+ if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN;
+
+ sstat = (PSESSION_HEADER)ncb->ncb_buffer;
+ ret = NRC_GOODRET;
+ memset(sstat, 0, sizeof(SESSION_HEADER));
+ spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) /
+ sizeof(SESSION_BUFFER);
+ EnterCriticalSection(&adapter->cs);
+ for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++)
+ {
+ if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' ||
+ !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ)))
+ {
+ if (sstat->num_sess < spaceFor)
+ {
+ PSESSION_BUFFER buf;
+
+ buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER)
+ + sstat->num_sess * sizeof(SESSION_BUFFER));
+ buf->lsn = i;
+ buf->state = adapter->sessions[i].state;
+ memcpy(buf->local_name, adapter->sessions[i].local_name,
+ NCBNAMSZ);
+ memcpy(buf->remote_name, adapter->sessions[i].remote_name,
+ NCBNAMSZ);
+ buf->rcvs_outstanding = buf->sends_outstanding = 0;
+ sstat->num_sess++;
+ }
+ else
+ ret = NRC_BUFLEN;
+ }
+ }
+ LeaveCriticalSection(&adapter->cs);
+
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret, i;
+
+ TRACE(": adapter %p, NCB %p\n", adapter, ncb);
+
+ if (!adapter) return NRC_BRIDGE;
+ if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
+ if (!adapter->transport->call) return NRC_ILLCMD;
+ if (!ncb) return NRC_INVADDRESS;
+
+ EnterCriticalSection(&adapter->cs);
+ for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++)
+ ;
+ if (i < adapter->sessionsLen)
+ {
+ adapter->sessions[i].inUse = TRUE;
+ adapter->sessions[i].state = CALL_PENDING;
+ memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ);
+ memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ);
+ ret = NRC_GOODRET;
+ }
+ else
+ ret = NRC_LOCTFUL;
+ LeaveCriticalSection(&adapter->cs);
+
+ if (ret == NRC_GOODRET)
+ {
+ ret = adapter->transport->call(adapter->impl.data, ncb,
+ &adapter->sessions[i].data);
+ if (ret == NRC_GOODRET)
+ {
+ ncb->ncb_lsn = i;
+ adapter->sessions[i].state = SESSION_ESTABLISHED;
+ }
+ else
+ {
+ adapter->sessions[i].inUse = FALSE;
+ adapter->sessions[i].state = 0;
+ }
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret;
+ NetBIOSSession *session;
+
+ if (!adapter) return NRC_BRIDGE;
+ if (!adapter->transport->send) return NRC_ILLCMD;
+ if (!ncb) return NRC_INVADDRESS;
+ if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+ if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+ if (!ncb->ncb_buffer) return NRC_BADDR;
+
+ session = &adapter->sessions[ncb->ncb_lsn];
+ if (session->state != SESSION_ESTABLISHED)
+ ret = NRC_SNUMOUT;
+ else
+ ret = adapter->transport->send(adapter->impl.data, session->data, ncb);
+ return ret;
+}
+
+static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret;
+ NetBIOSSession *session;
+
+ if (!adapter) return NRC_BRIDGE;
+ if (!adapter->transport->recv) return NRC_ILLCMD;
+ if (!ncb) return NRC_INVADDRESS;
+ if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+ if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+ if (!ncb->ncb_buffer) return NRC_BADDR;
+
+ session = &adapter->sessions[ncb->ncb_lsn];
+ if (session->state != SESSION_ESTABLISHED)
+ ret = NRC_SNUMOUT;
+ else
+ ret = adapter->transport->recv(adapter->impl.data, session->data, ncb);
+ return ret;
+}
+
+static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session)
+{
+ UCHAR ret;
+
+ if (!adapter) return NRC_BRIDGE;
+ if (!session) return NRC_SNUMOUT;
+
+ if (adapter->transport->hangup)
+ ret = adapter->transport->hangup(adapter->impl.data, session->data);
+ else
+ ret = NRC_ILLCMD;
+ EnterCriticalSection(&adapter->cs);
+ memset(session, 0, sizeof(NetBIOSSession));
+ LeaveCriticalSection(&adapter->cs);
+ return NRC_GOODRET;
+}
+
+static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret;
+ NetBIOSSession *session;
+
+ if (!adapter) return NRC_BRIDGE;
+ if (!ncb) return NRC_INVADDRESS;
+ if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+ if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+
+ session = &adapter->sessions[ncb->ncb_lsn];
+ if (session->state != SESSION_ESTABLISHED)
+ ret = NRC_SNUMOUT;
+ else
+ {
+ session->state = HANGUP_PENDING;
+ ret = nbInternalHangup(adapter, session);
+ }
+ return ret;
+}
+
+void NetBIOSHangupSession(PNCB ncb)
+{
+ NetBIOSAdapter *adapter;
+
+ if (!ncb) return;
+
+ adapter = nbGetAdapter(ncb->ncb_lana_num);
+ if (adapter)
+ {
+ if (ncb->ncb_lsn < adapter->sessionsLen &&
+ adapter->sessions[ncb->ncb_lsn].inUse)
+ nbHangup(adapter, ncb);
+ }
+}
+
+static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret;
+
+ if (!adapter) return NRC_BRIDGE;
+ if (!adapter->transport->astat) return NRC_ILLCMD;
+ if (!ncb) return NRC_INVADDRESS;
+ if (!ncb->ncb_buffer) return NRC_BADDR;
+ if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
+
+ ret = adapter->transport->astat(adapter->impl.data, ncb);
+ if (ncb->ncb_callname[0] == '*')
+ {
+ PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+
+ astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen;
+ }
+ return ret;
+}
+
+static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb)
+{
+ UCHAR ret, cmd;
+
+ TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+ if (!adapter) return NRC_BRIDGE;
+ if (!ncb) return NRC_INVADDRESS;
+
+ cmd = ncb->ncb_command & 0x7f;
+ if (cmd == NCBRESET)
+ ret = nbReset(adapter, ncb);
+ else
+ {
+ ret = NBCmdQueueAdd(adapter->cmdQueue, ncb);
+ if (ret == NRC_GOODRET)
+ {
+ switch (cmd)
+ {
+ case NCBCALL:
+ ret = nbCall(adapter, ncb);
+ break;
+
+ /* WinNT doesn't chain sends, it always sends immediately.
+ * Doubt there's any real significance to the NA variants.
+ */
+ case NCBSEND:
+ case NCBSENDNA:
+ case NCBCHAINSEND:
+ case NCBCHAINSENDNA:
+ ret = nbSend(adapter, ncb);
+ break;
+
+ case NCBRECV:
+ ret = nbRecv(adapter, ncb);
+ break;
+
+ case NCBHANGUP:
+ ret = nbHangup(adapter, ncb);
+ break;
+
+ case NCBASTAT:
+ ret = nbAStat(adapter, ncb);
+ break;
+
+ case NCBFINDNAME:
+ if (adapter->transport->findName)
+ ret = adapter->transport->findName(adapter->impl.data,
+ ncb);
+ else
+ ret = NRC_ILLCMD;
+ break;
+
+ default:
+ FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command);
+ ret = NRC_ILLCMD;
+ }
+ NBCmdQueueComplete(adapter->cmdQueue, ncb, ret);
+ }
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
+
+static DWORD WINAPI nbCmdThread(LPVOID lpVoid)
+{
+ PNCB ncb = (PNCB)lpVoid;
+
+ if (ncb)
+ {
+ UCHAR ret;
+ NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
+
+ if (adapter)
+ ret = nbDispatch(adapter, ncb);
+ else
+ ret = NRC_BRIDGE;
+ ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret;
+ if (ncb->ncb_post)
+ ncb->ncb_post(ncb);
+ else if (ncb->ncb_event)
+ SetEvent(ncb->ncb_event);
+ }
+ return 0;
+}
+
+UCHAR WINAPI Netbios(PNCB ncb)
+{
+ UCHAR ret, cmd;
+
+ TRACE("ncb = %p\n", ncb);
+
+ if (!ncb) return NRC_INVADDRESS;
+
+ TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n",
+ ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length);
+ cmd = ncb->ncb_command & 0x7f;
+
+ if (cmd == NCBENUM)
+ ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb);
+ else if (cmd == NCBADDNAME)
+ {
+ FIXME("NCBADDNAME: stub, returning success");
+ ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET;
+ }
+ else
+ {
+ NetBIOSAdapter *adapter;
+
+ /* Apps not specifically written for WinNT won't do an NCBENUM first,
+ * so make sure the table has been enumerated at least once
+ */
+ if (!gNBTable.enumerated)
+ nbInternalEnum();
+ adapter = nbGetAdapter(ncb->ncb_lana_num);
+ if (!adapter)
+ ret = NRC_BRIDGE;
+ else
+ {
+ if (adapter->shuttingDown)
+ ret = NRC_IFBUSY;
+ else if (adapter->resetting)
+ ret = NRC_TOOMANY;
+ else
+ {
+ /* non-asynch commands first */
+ if (cmd == NCBCANCEL)
+ ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+ nbCancel(adapter, ncb);
+ else if (cmd == NCBSSTAT)
+ ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+ nbSStat(adapter, ncb);
+ else
+ {
+ if (ncb->ncb_command & ASYNCH)
+ {
+ HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb,
+ CREATE_SUSPENDED, NULL);
+
+ if (thread != NULL)
+ {
+ ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING;
+ if (ncb->ncb_event)
+ ResetEvent(ncb->ncb_event);
+ ResumeThread(thread);
+ CloseHandle(thread);
+ ret = NRC_GOODRET;
+ }
+ else
+ ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+ NRC_OSRESNOTAV;
+ }
+ else
+ ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+ nbDispatch(adapter, ncb);
+ }
+ }
+ }
+ }
+ TRACE("returning 0x%02x\n", ret);
+ return ret;
+}
--- /dev/null
+/* Copyright (c) 2003 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __WINE_NETBIOS_H__
+#define __WINE_NETBIOS_H__
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "lm.h"
+#include "nb30.h"
+
+/* This file describes the interface WINE's NetBIOS implementation uses to
+ * interact with a transport implementation (where a transport might be
+ * NetBIOS-over-TCP/IP (aka NetBT, NBT), NetBIOS-over-IPX, etc.)
+ */
+
+/**
+ * Public functions
+ */
+
+void NetBIOSInit(void);
+void NetBIOSShutdown(void);
+
+struct _NetBIOSTransport;
+
+/* A transport should register itself during its init function (see below) with
+ * a unique id (the transport_id of ACTION_HEADER, for example) and an
+ * implementation. Returns TRUE on success, and FALSE on failure.
+ */
+BOOL NetBIOSRegisterTransport(ULONG id, struct _NetBIOSTransport *transport);
+
+/* Registers an adapter with the given transport and ifIndex with NetBIOS.
+ * ifIndex is an interface index usable by the IpHlpApi. ifIndex is not
+ * required to be unique, but is required so that NetWkstaTransportEnum can use
+ * GetIfEntry to get the name and hardware address of the adapter.
+ * Returns TRUE on success, FALSE on failure.
+ * FIXME: need functions for retrieving the name and hardware index, rather
+ * than assuming a correlation with IpHlpApi.
+ */
+BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *adapter);
+
+/* During enumeration, all adapters from your transport are disabled
+ * internally. If an adapter is still valid, reenable it with this function.
+ * Adapters you don't enable will have their transport's NetBIOSCleanupAdapter
+ * function (see below) called on them, and will be removed from the table.
+ * (This is to deal with lack of plug-and-play--sorry.)
+ */
+void NetBIOSEnableAdapter(UCHAR lana);
+
+/* Gets a quick count of the number of NetBIOS adapters. Not guaranteed not
+ * to change from one call to the next, depending on what's been enumerated
+ * lately. See also NetBIOSEnumAdapters.
+ */
+UCHAR NetBIOSNumAdapters(void);
+
+typedef struct _NetBIOSAdapterImpl {
+ UCHAR lana;
+ DWORD ifIndex;
+ void *data;
+} NetBIOSAdapterImpl;
+
+typedef BOOL (*NetBIOSEnumAdaptersCallback)(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure);
+
+/* Enumerates all NetBIOS adapters for the transport transport, or for all
+ * transports if transport is ALL_TRANSPORTS. Your callback will be called
+ * once for every enumerated adapter, with a count of how many adapters have
+ * been enumerated, a 0-based index relative to that count, the adapter's
+ * transport, and its ifIndex.
+ * Your callback should return FALSE if it no longer wishes to be called.
+ */
+void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
+ void *closure);
+
+/* Hangs up the session identified in the NCB; the NCB need not be a NCBHANGUP.
+ * Will result in the transport's hangup function being called, so release any
+ * locks you own before calling to avoid deadlock.
+ * This function is intended for use by a transport, if the session is closed
+ * by some error in the transport layer.
+ */
+void NetBIOSHangupSession(PNCB ncb);
+
+/**
+ * Functions a transport implementation must implement
+ */
+
+/* This function is called to ask a transport implementation to enumerate any
+ * LANAs into the NetBIOS adapter table by:
+ * - calling NetBIOSRegisterAdapter for any new adapters
+ * - calling NetBIOSEnableAdapter for any existing adapters
+ * NetBIOSEnumAdapters (see) may be of use to determine which adapters already
+ * exist.
+ * A transport can assume no other thread is modifying the NetBIOS adapter
+ * table during the lifetime of its NetBIOSEnum function (and, therefore, that
+ * this function won't be called reentrantly).
+ */
+typedef UCHAR (*NetBIOSEnum)(void);
+
+/* A cleanup function for a transport. This is the last function called on a
+ * transport.
+ */
+typedef void (*NetBIOSCleanup)(void);
+
+/* Adapter functions */
+
+/* Functions with direct mappings to the Netbios interface. These functions
+ * are expected to be synchronous, although the first four bytes of the
+ * reserved member of the ncb are a cancel flag. A long-running function
+ * should check whether this is not FALSE from time to time (see the
+ * NCB_CANCELLED macro), and return NRC_CMDCAN if it's been cancelled. (The
+ * remainder of the NCB's reserved field is, well, reserved.)
+ */
+
+/* Used to see whether the pointer to an NCB has been cancelled. The NetBIOS
+ * interface designates certain functions as non-cancellable functions, but I
+ * use this flag for all NCBs. Support it if you can.
+ * FIXME: this isn't enough, need to support an EVENT or some such, because
+ * some calls (recv) will block indefinitely, so a reset, shutdown, etc. will
+ * never occur.
+ */
+#define NCB_CANCELLED(pncb) *(PBOOL)((pncb)->ncb_reserve)
+
+typedef UCHAR (*NetBIOSAstat)(void *adapter, PNCB ncb);
+typedef UCHAR (*NetBIOSFindName)(void *adapter, PNCB ncb);
+
+/* Functions to support the session service */
+
+/* Implement to support the NCBCALL command. If you need data stored for the
+ * session, return it in *session. You can clean it up in your NetBIOSHangup
+ * function (see).
+ */
+typedef UCHAR (*NetBIOSCall)(void *adapter, PNCB ncb, void **session);
+typedef UCHAR (*NetBIOSSend)(void *adapter, void *session, PNCB ncb);
+typedef UCHAR (*NetBIOSRecv)(void *adapter, void *session, PNCB ncb);
+typedef UCHAR (*NetBIOSHangup)(void *adapter, void *session);
+
+/* The last function called on an adapter; it is not called reentrantly, and
+ * no new calls will be made on the adapter once this has been entered. Clean
+ * up any resources allocated for the adapter here.
+ */
+typedef void (*NetBIOSCleanupAdapter)(void *adapter);
+
+typedef struct _NetBIOSTransport
+{
+ NetBIOSEnum enumerate;
+ NetBIOSAstat astat;
+ NetBIOSFindName findName;
+ NetBIOSCall call;
+ NetBIOSSend send;
+ NetBIOSRecv recv;
+ NetBIOSHangup hangup;
+ NetBIOSCleanupAdapter cleanupAdapter;
+ NetBIOSCleanup cleanup;
+} NetBIOSTransport;
+
+/* Transport-specific functions. When adding a transport, add a call to its
+ * init function in netapi32's DllMain. The transport can do any global
+ * initialization it needs here. It should call NetBIOSRegisterTransport to
+ * register itself with NetBIOS.
+ */
+
+/* NetBIOS-over-TCP/IP (NetBT) functions */
+
+/* Not defined by MS, so make my own private define: */
+#define TRANSPORT_NBT "MNBT"
+
+void NetBTInit(void);
+
+#endif /* ndef __WINE_NETBIOS_H__ */
--- /dev/null
+/* Copyright 2002 Andriy Palamarchuk
+ * Copyright (c) 2003 Juan Lang
+ *
+ * netapi32 user functions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winsock2.h"
+#include "nb30.h"
+#include "lmcons.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "lmwksta.h"
+#include "iphlpapi.h"
+#include "winerror.h"
+#undef WIN32_NO_STATUS
+#include "ntstatus.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "ntsecapi.h"
+#include "netbios.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ * NETAPI_IsLocalComputer
+ *
+ * Checks whether the server name indicates local machine.
+ */
+BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName)
+{
+ if (!ServerName)
+ {
+ return TRUE;
+ }
+ else if (ServerName[0] == '\0')
+ return TRUE;
+ else
+ {
+ DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
+ BOOL Result;
+ LPWSTR buf;
+
+ NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &buf);
+ Result = GetComputerNameW(buf, &dwSize);
+ if (Result && (ServerName[0] == '\\') && (ServerName[1] == '\\'))
+ ServerName += 2;
+ Result = Result && !lstrcmpW(ServerName, buf);
+ NetApiBufferFree(buf);
+
+ return Result;
+ }
+}
+
+static void wprint_mac(WCHAR* buffer, int len, PMIB_IFROW ifRow)
+{
+ int i;
+ unsigned char val;
+
+ if (!buffer)
+ return;
+ if (len < 1)
+ return;
+ if (!ifRow)
+ {
+ *buffer = '\0';
+ return;
+ }
+
+ for (i = 0; i < ifRow->dwPhysAddrLen && 2 * i < len; i++)
+ {
+ val = ifRow->bPhysAddr[i];
+ if ((val >>4) >9)
+ buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10);
+ else
+ buffer[2*i] = (WCHAR)((val >>4) + '0');
+ if ((val & 0xf ) >9)
+ buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10);
+ else
+ buffer[2*i+1] = (WCHAR)((val & 0xf) + '0');
+ }
+ buffer[2*i]=(WCHAR)0;
+}
+
+/* Theoretically this could be too short, except that MS defines
+ * MAX_ADAPTER_NAME as 128, and MAX_INTERFACE_NAME_LEN as 256, and both
+ * represent a count of WCHARs, so even with an extroardinarily long header
+ * this will be plenty
+ */
+#define MAX_TRANSPORT_NAME MAX_INTERFACE_NAME_LEN
+#define MAX_TRANSPORT_ADDR 13
+
+#define NBT_TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_"
+#define UNKNOWN_TRANSPORT_NAME_HEADER "\\Device\\UnknownTransport_"
+
+static void wprint_name(WCHAR *buffer, int len, ULONG transport,
+ PMIB_IFROW ifRow)
+{
+ WCHAR *ptr1, *ptr2;
+ const char *name;
+
+ if (!buffer)
+ return;
+ if (!ifRow)
+ {
+ *buffer = '\0';
+ return;
+ }
+
+ if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG)))
+ name = NBT_TRANSPORT_NAME_HEADER;
+ else
+ name = UNKNOWN_TRANSPORT_NAME_HEADER;
+
+ for (ptr1 = buffer; *name && ptr1 < buffer + len; ptr1++, name++)
+ *ptr1 = *name;
+ for (ptr2 = ifRow->wszName; *ptr2 && ptr1 < buffer + len; ptr1++, ptr2++)
+ *ptr1 = *ptr2;
+ *ptr1 = '\0';
+}
+
+/***********************************************************************
+ * NetWkstaTransportEnum (NETAPI32.@)
+ */
+
+struct WkstaTransportEnumData
+{
+ UCHAR n_adapt;
+ UCHAR n_read;
+ DWORD prefmaxlen;
+ LPBYTE *pbuf;
+ NET_API_STATUS ret;
+};
+
+/**********************************************************************/
+
+static BOOL WkstaEnumAdaptersCallback(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
+{
+ BOOL ret;
+ struct WkstaTransportEnumData *enumData = (struct WkstaTransportEnumData *)
+ closure;
+
+ if (enumData && enumData->pbuf)
+ {
+ if (lanaIndex == 0)
+ {
+ DWORD toAllocate;
+
+ enumData->n_adapt = totalLANAs;
+ enumData->n_read = 0;
+
+ toAllocate = totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0)
+ + MAX_TRANSPORT_NAME * sizeof(WCHAR) +
+ MAX_TRANSPORT_ADDR * sizeof(WCHAR));
+ if (enumData->prefmaxlen != MAX_PREFERRED_LENGTH)
+ toAllocate = enumData->prefmaxlen;
+ NetApiBufferAllocate(toAllocate, (LPVOID *)enumData->pbuf);
+ }
+ if (*(enumData->pbuf))
+ {
+ UCHAR spaceFor;
+
+ if (enumData->prefmaxlen == MAX_PREFERRED_LENGTH)
+ spaceFor = totalLANAs;
+ else
+ spaceFor = enumData->prefmaxlen /
+ (sizeof(WKSTA_TRANSPORT_INFO_0) + (MAX_TRANSPORT_NAME +
+ MAX_TRANSPORT_ADDR) * sizeof(WCHAR));
+ if (enumData->n_read < spaceFor)
+ {
+ PWKSTA_TRANSPORT_INFO_0 ti;
+ LPWSTR transport_name, transport_addr;
+ MIB_IFROW ifRow;
+
+ ti = (PWKSTA_TRANSPORT_INFO_0)(*(enumData->pbuf) +
+ enumData->n_read * sizeof(WKSTA_TRANSPORT_INFO_0));
+ transport_name = (LPWSTR)(*(enumData->pbuf) +
+ totalLANAs * sizeof(WKSTA_TRANSPORT_INFO_0) +
+ enumData->n_read * MAX_TRANSPORT_NAME * sizeof(WCHAR));
+ transport_addr = (LPWSTR)(*(enumData->pbuf) +
+ totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) +
+ MAX_TRANSPORT_NAME * sizeof(WCHAR)) +
+ enumData->n_read * MAX_TRANSPORT_ADDR * sizeof(WCHAR));
+
+ ifRow.dwIndex = data->ifIndex;
+ GetIfEntry(&ifRow);
+ ti->wkti0_quality_of_service = 0;
+ ti->wkti0_number_of_vcs = 0;
+ ti->wkti0_transport_name = transport_name;
+ wprint_name(ti->wkti0_transport_name, MAX_TRANSPORT_NAME,
+ transport, &ifRow);
+ ti->wkti0_transport_address = transport_addr;
+ wprint_mac(ti->wkti0_transport_address, MAX_TRANSPORT_ADDR,
+ &ifRow);
+ if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG)))
+ ti->wkti0_wan_ish = TRUE;
+ else
+ ti->wkti0_wan_ish = FALSE;
+ TRACE("%d of %d:ti at %p\n", lanaIndex, totalLANAs, ti);
+ TRACE("transport_name at %p %s\n",
+ ti->wkti0_transport_name,
+ debugstr_w(ti->wkti0_transport_name));
+ TRACE("transport_address at %p %s\n",
+ ti->wkti0_transport_address,
+ debugstr_w(ti->wkti0_transport_address));
+ enumData->n_read++;
+ enumData->ret = NERR_Success;
+ ret = TRUE;
+ }
+ else
+ {
+ enumData->ret = ERROR_MORE_DATA;
+ ret = FALSE;
+ }
+ }
+ else
+ {
+ enumData->ret = ERROR_OUTOFMEMORY;
+ ret = FALSE;
+ }
+ }
+ else
+ ret = FALSE;
+ return ret;
+}
+
+/**********************************************************************/
+
+NET_API_STATUS WINAPI
+NetWkstaTransportEnum(LPWSTR ServerName, DWORD level, PBYTE* pbuf,
+ DWORD prefmaxlen, LPDWORD read_entries,
+ PDWORD total_entries, PDWORD hresume)
+{
+ NET_API_STATUS ret;
+
+ TRACE(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName),
+ level, pbuf, prefmaxlen, read_entries, total_entries,hresume);
+ if (!NETAPI_IsLocalComputer(ServerName))
+ {
+ FIXME(":not implemented for non-local computers\n");
+ ret = ERROR_INVALID_LEVEL;
+ }
+ else
+ {
+ if (hresume && *hresume)
+ {
+ FIXME(":resume handle not implemented\n");
+ return ERROR_INVALID_LEVEL;
+ }
+
+ switch (level)
+ {
+ case 0: /* transport info */
+ {
+ ULONG allTransports;
+ struct WkstaTransportEnumData enumData;
+
+ if (NetBIOSNumAdapters() == 0)
+ return ERROR_NETWORK_UNREACHABLE;
+ if (!read_entries)
+ return STATUS_ACCESS_VIOLATION;
+ if (!total_entries || !pbuf)
+ return RPC_X_NULL_REF_POINTER;
+
+ enumData.prefmaxlen = prefmaxlen;
+ enumData.pbuf = pbuf;
+ memcpy(&allTransports, ALL_TRANSPORTS, sizeof(ULONG));
+ NetBIOSEnumAdapters(allTransports, WkstaEnumAdaptersCallback,
+ &enumData);
+ *read_entries = enumData.n_read;
+ *total_entries = enumData.n_adapt;
+ if (hresume) *hresume= 0;
+ ret = enumData.ret;
+ break;
+ }
+ default:
+ ERR("Invalid level %ld is specified\n", level);
+ ret = ERROR_INVALID_LEVEL;
+ }
+ }
+ return ret;
+}
+
+
+/************************************************************
+ * NetWkstaUserGetInfo (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetWkstaUserGetInfo(LPWSTR reserved, DWORD level,
+ PBYTE* bufptr)
+{
+ TRACE("(%s, %ld, %p)\n", debugstr_w(reserved), level, bufptr);
+ switch (level)
+ {
+ case 0:
+ {
+ PWKSTA_USER_INFO_0 ui;
+ DWORD dwSize = UNLEN + 1;
+
+ /* set up buffer */
+ NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_0) + dwSize * sizeof(WCHAR),
+ (LPVOID *) bufptr);
+
+ ui = (PWKSTA_USER_INFO_0) *bufptr;
+ ui->wkui0_username = (LPWSTR) (*bufptr + sizeof(WKSTA_USER_INFO_0));
+
+ /* get data */
+ if (!GetUserNameW(ui->wkui0_username, &dwSize))
+ {
+ NetApiBufferFree(ui);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ NetApiBufferReallocate(
+ *bufptr, sizeof(WKSTA_USER_INFO_0) +
+ (lstrlenW(ui->wkui0_username) + 1) * sizeof(WCHAR),
+ (LPVOID *) bufptr);
+ break;
+ }
+
+ case 1:
+ {
+ PWKSTA_USER_INFO_1 ui;
+ PWKSTA_USER_INFO_0 ui0;
+ DWORD dwSize;
+ LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE PolicyHandle;
+ PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo;
+ NTSTATUS NtStatus;
+
+ /* sizes of the field buffers in WCHARS */
+ int username_sz, logon_domain_sz, oth_domains_sz, logon_server_sz;
+
+ FIXME("Level 1 processing is partially implemented\n");
+ oth_domains_sz = 1;
+ logon_server_sz = 1;
+
+ /* get some information first to estimate size of the buffer */
+ ui0 = NULL;
+ NetWkstaUserGetInfo(NULL, 0, (PBYTE *) &ui0);
+ username_sz = lstrlenW(ui0->wkui0_username) + 1;
+
+ ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
+ NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle);
+ if (NtStatus != STATUS_SUCCESS)
+ {
+ ERR("LsaOpenPolicyFailed with NT status %lx\n",
+ LsaNtStatusToWinError(NtStatus));
+ NetApiBufferFree(ui0);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation,
+ (PVOID*) &DomainInfo);
+ logon_domain_sz = lstrlenW(DomainInfo->DomainName.Buffer) + 1;
+ LsaClose(PolicyHandle);
+
+ /* set up buffer */
+ NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1) +
+ (username_sz + logon_domain_sz +
+ oth_domains_sz + logon_server_sz) * sizeof(WCHAR),
+ (LPVOID *) bufptr);
+ ui = (WKSTA_USER_INFO_1 *) *bufptr;
+ ui->wkui1_username = (LPWSTR) (*bufptr + sizeof(WKSTA_USER_INFO_1));
+ ui->wkui1_logon_domain = (LPWSTR) (
+ ((PBYTE) ui->wkui1_username) + username_sz * sizeof(WCHAR));
+ ui->wkui1_oth_domains = (LPWSTR) (
+ ((PBYTE) ui->wkui1_logon_domain) +
+ logon_domain_sz * sizeof(WCHAR));
+ ui->wkui1_logon_server = (LPWSTR) (
+ ((PBYTE) ui->wkui1_oth_domains) +
+ oth_domains_sz * sizeof(WCHAR));
+
+ /* get data */
+ dwSize = username_sz;
+ lstrcpyW(ui->wkui1_username, ui0->wkui0_username);
+ NetApiBufferFree(ui0);
+
+ lstrcpynW(ui->wkui1_logon_domain, DomainInfo->DomainName.Buffer,
+ logon_domain_sz);
+ LsaFreeMemory(DomainInfo);
+
+ /* FIXME. Not implemented. Populated with empty strings */
+ ui->wkui1_oth_domains[0] = 0;
+ ui->wkui1_logon_server[0] = 0;
+ break;
+ }
+ case 1101:
+ {
+ PWKSTA_USER_INFO_1101 ui;
+ DWORD dwSize = 1;
+
+ FIXME("Stub. Level 1101 processing is not implemented\n");
+ /* FIXME see also wkui1_oth_domains for level 1 */
+
+ /* set up buffer */
+ NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1101) + dwSize * sizeof(WCHAR),
+ (LPVOID *) bufptr);
+
+ ui = (PWKSTA_USER_INFO_1101) *bufptr;
+ ui->wkui1101_oth_domains = (LPWSTR)(ui + 1);
+
+ /* get data */
+ ui->wkui1101_oth_domains[0] = 0;
+ break;
+ }
+ default:
+ ERR("Invalid level %ld is specified\n", level);
+ return ERROR_INVALID_LEVEL;
+ }
+ return NERR_Success;
+}
+
+/************************************************************
+ * NetpGetComputerName (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetpGetComputerName(LPWSTR *Buffer)
+{
+ DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
+
+ TRACE("(%p)\n", Buffer);
+ NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) Buffer);
+ if (GetComputerNameW(*Buffer, &dwSize))
+ {
+ NetApiBufferReallocate(
+ *Buffer, dwSize * sizeof(WCHAR),
+ (LPVOID *) Buffer);
+ return NERR_Success;
+ }
+ else
+ {
+ NetApiBufferFree(*Buffer);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+}
+
+NET_API_STATUS WINAPI I_NetNameCompare(LPVOID p1, LPWSTR wkgrp, LPWSTR comp,
+ LPVOID p4, LPVOID p5)
+{
+ FIXME("(%p %s %s %p %p): stub\n", p1, debugstr_w(wkgrp), debugstr_w(comp),
+ p4, p5);
+ return ERROR_INVALID_PARAMETER;
+}
+
+NET_API_STATUS WINAPI I_NetNameValidate(LPVOID p1, LPWSTR wkgrp, LPVOID p3,
+ LPVOID p4)
+{
+ FIXME("(%p %s %p %p): stub\n", p1, debugstr_w(wkgrp), p3, p4);
+ return ERROR_INVALID_PARAMETER;
+}
+
+NET_API_STATUS WINAPI NetWkstaGetInfo( LPWSTR servername, DWORD level,
+ LPBYTE* bufptr)
+{
+ NET_API_STATUS ret;
+
+ TRACE("%s %ld %p\n", debugstr_w( servername ), level, bufptr );
+ if (servername)
+ {
+ if (!NETAPI_IsLocalComputer(servername))
+ {
+ FIXME("remote computers not supported\n");
+ return ERROR_INVALID_LEVEL;
+ }
+ }
+ if (!bufptr) return ERROR_INVALID_PARAMETER;
+
+ switch (level)
+ {
+ case 100:
+ {
+ DWORD computerNameLen, domainNameLen, size;
+ WCHAR computerName[MAX_COMPUTERNAME_LENGTH + 1];
+ LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE PolicyHandle;
+ NTSTATUS NtStatus;
+
+ computerNameLen = MAX_COMPUTERNAME_LENGTH + 1;
+ GetComputerNameW(computerName, &computerNameLen);
+ computerNameLen++; /* include NULL terminator */
+
+ ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
+ NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle);
+ if (NtStatus != STATUS_SUCCESS)
+ ret = LsaNtStatusToWinError(NtStatus);
+ else
+ {
+ PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo;
+
+ LsaQueryInformationPolicy(PolicyHandle,
+ PolicyAccountDomainInformation, (PVOID*)&DomainInfo);
+ domainNameLen = lstrlenW(DomainInfo->DomainName.Buffer) + 1;
+ size = sizeof(WKSTA_INFO_100) + computerNameLen * sizeof(WCHAR)
+ + domainNameLen * sizeof(WCHAR);
+ ret = NetApiBufferAllocate(size, (LPVOID *)bufptr);
+ if (ret == NERR_Success)
+ {
+ PWKSTA_INFO_100 info = (PWKSTA_INFO_100)*bufptr;
+ OSVERSIONINFOW verInfo;
+
+ info->wki100_platform_id = PLATFORM_ID_NT;
+ info->wki100_computername = (LPWSTR)(*bufptr +
+ sizeof(WKSTA_INFO_100));
+ memcpy(info->wki100_computername, computerName,
+ computerNameLen * sizeof(WCHAR));
+ info->wki100_langroup = (LPWSTR)(*bufptr +
+ sizeof(WKSTA_INFO_100) + computerNameLen * sizeof(WCHAR));
+ memcpy(info->wki100_langroup, DomainInfo->DomainName.Buffer,
+ domainNameLen * sizeof(WCHAR));
+ memset(&verInfo, 0, sizeof(verInfo));
+ verInfo.dwOSVersionInfoSize = sizeof(verInfo);
+ GetVersionExW(&verInfo);
+ info->wki100_ver_major = verInfo.dwMajorVersion;
+ info->wki100_ver_minor = verInfo.dwMinorVersion;
+ }
+ LsaFreeMemory(DomainInfo);
+ LsaClose(PolicyHandle);
+ }
+ break;
+ }
+
+ default:
+ FIXME("level %ld unimplemented\n", level);
+ ret = ERROR_INVALID_LEVEL;
+ }
+ return ret;
+}
+
+/************************************************************
+ * NetGetJoinInformation (NETAPI32.@)
+ */
+NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation(
+ LPCWSTR Server,
+ LPWSTR *Name,
+ PNETSETUP_JOIN_STATUS type)
+{
+ FIXME("Stub %s %p %p\n", wine_dbgstr_w(Server), Name, type);
+
+ *Name = NULL;
+ *type = NetSetupUnknownStatus;
+
+ return NERR_Success;
+}
reactos/lib/msimg32 # Synced to Wine-0_9_1
reactos/lib/msi # Synced to Wine-0_9_1
reactos/lib/msvideo # Out of sync
-reactos/lib/netapi32 # Out of sync
+reactos/lib/netapi32 # Synced to Wine-0_9_1
reactos/lib/objsel # Synced to Wine-0_9_1
reactos/lib/odbc32 # Out of sync. Depends on port of Linux ODBC.
reactos/lib/ole32 # Synced to Wine-0_9_1
#include <lmwksta.h>
#include <lmserver.h>
#include <lmstats.h>
+#include <lmjoin.h>
#endif
+++ /dev/null
-#ifndef _LMBROWSR_H
-#define _LMBROWSR_H
-#if __GNUC__ >=3
-#pragma GCC system_header
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-#define BROWSER_ROLE_PDC 1
-#define BROWSER_ROLE_BDC 2
-typedef struct _BROWSER_STATISTICS {
- LARGE_INTEGER StatisticsStartTime;
- LARGE_INTEGER NumberOfServerAnnouncements;
- LARGE_INTEGER NumberOfDomainAnnouncements;
- ULONG NumberOfElectionPackets;
- ULONG NumberOfMailslotWrites;
- ULONG NumberOfGetBrowserServerListRequests;
- ULONG NumberOfServerEnumerations;
- ULONG NumberOfDomainEnumerations;
- ULONG NumberOfOtherEnumerations;
- ULONG NumberOfMissedServerAnnouncements;
- ULONG NumberOfMissedMailslotDatagrams;
- ULONG NumberOfMissedGetBrowserServerListRequests;
- ULONG NumberOfFailedServerAnnounceAllocations;
- ULONG NumberOfFailedMailslotAllocations;
- ULONG NumberOfFailedMailslotReceives;
- ULONG NumberOfFailedMailslotWrites;
- ULONG NumberOfFailedMailslotOpens;
- ULONG NumberOfDuplicateMasterAnnouncements;
-LARGE_INTEGER NumberOfIllegalDatagrams;
-} BROWSER_STATISTICS,*PBROWSER_STATISTICS,*LPBROWSER_STATISTICS;
-typedef struct _BROWSER_STATISTICS_100 {
- LARGE_INTEGER StartTime;
- LARGE_INTEGER NumberOfServerAnnouncements;
- LARGE_INTEGER NumberOfDomainAnnouncements;
- ULONG NumberOfElectionPackets;
- ULONG NumberOfMailslotWrites;
- ULONG NumberOfGetBrowserServerListRequests;
- LARGE_INTEGER NumberOfIllegalDatagrams;
-} BROWSER_STATISTICS_100,*PBROWSER_STATISTICS_100;
-typedef struct _BROWSER_STATISTICS_101 {
- LARGE_INTEGER StartTime;
- LARGE_INTEGER NumberOfServerAnnouncements;
- LARGE_INTEGER NumberOfDomainAnnouncements;
- ULONG NumberOfElectionPackets;
- ULONG NumberOfMailslotWrites;
- ULONG NumberOfGetBrowserServerListRequests;
- LARGE_INTEGER NumberOfIllegalDatagrams;
- ULONG NumberOfMissedServerAnnouncements;
- ULONG NumberOfMissedMailslotDatagrams;
- ULONG NumberOfMissedGetBrowserServerListRequests;
- ULONG NumberOfFailedServerAnnounceAllocations;
- ULONG NumberOfFailedMailslotAllocations;
- ULONG NumberOfFailedMailslotReceives;
- ULONG NumberOfFailedMailslotWrites;
- ULONG NumberOfFailedMailslotOpens;
- ULONG NumberOfDuplicateMasterAnnouncements;
-} BROWSER_STATISTICS_101,*PBROWSER_STATISTICS_101;
-
-NET_API_STATUS WINAPI I_BrowserServerEnum(LPCWSTR,LPCWSTR,LPCWSTR,DWORD,PBYTE*,DWORD,PDWORD,PDWORD,DWORD,LPCWSTR,PDWORD);
-NET_API_STATUS WINAPI I_BrowserServerEnumEx(LPCWSTR,LPCWSTR,LPCWSTR,DWORD,PBYTE*,DWORD,PDWORD,PDWORD,DWORD,LPCWSTR,LPCWSTR);
-NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains(LPWSTR,PBYTE*,PDWORD);
-NET_API_STATUS I_BrowserQueryOtherDomains(LPCWSTR,PBYTE*,PDWORD,PDWORD);
-NET_API_STATUS I_BrowserResetNetlogonState(LPCWSTR);
-NET_API_STATUS WINAPI I_BrowserSetNetlogonState(LPWSTR,LPWSTR,LPWSTR,DWORD);
-NET_API_STATUS I_BrowserQueryStatistics(LPCWSTR,LPBROWSER_STATISTICS*);
-NET_API_STATUS I_BrowserResetStatistics(LPCWSTR);
-WORD I_BrowserServerEnumForXactsrv(LPCWSTR,LPCWSTR,ULONG,USHORT,PVOID,WORD,DWORD,PDWORD,PDWORD,DWORD,LPCWSTR,LPCWSTR,PWORD);
-NET_API_STATUS I_BrowserDebugTrace(PWCHAR,PCHAR);
-#ifdef __cplusplus
-}
-#endif
-#endif
--- /dev/null
+/*
+ * Copyright 2005 Ulrich Czekalla (For CodeWeavers)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __WINE_LMJOIN_H
+#define __WINE_LMJOIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum tagNETSETUP_JOIN_STATUS
+{
+ NetSetupUnknownStatus = 0,
+ NetSetupUnjoined,
+ NetSetupWorkgroupName,
+ NetSetupDomainName
+} NETSETUP_JOIN_STATUS, *PNETSETUP_JOIN_STATUS;
+
+NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation(
+ LPCWSTR Server,
+ LPWSTR *Name,
+ PNETSETUP_JOIN_STATUS type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
#if !defined(_NTDEF_H) && !defined(_SUBAUTH_H)
typedef LONG NTSTATUS, *PNTSTATUS;
-typedef struct _UNICODE_STRING {
- USHORT Length;
- USHORT MaximumLength;
- PWSTR Buffer;
-} UNICODE_STRING, *PUNICODE_STRING;
-typedef struct _STRING {
- USHORT Length;
- USHORT MaximumLength;
- PCHAR Buffer;
-} STRING, *PSTRING;
#endif
#if defined (_NTDEF_H)