ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

nbt.c
Go to the documentation of this file.
00001 /* Copyright (c) 2003 Juan Lang
00002  *
00003  * This library is free software; you can redistribute it and/or
00004  * modify it under the terms of the GNU Lesser General Public
00005  * License as published by the Free Software Foundation; either
00006  * version 2.1 of the License, or (at your option) any later version.
00007  *
00008  * This library is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  * Lesser General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU Lesser General Public
00014  * License along with this library; if not, write to the Free Software
00015  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00016  *
00017  * I am heavily indebted to Chris Hertel's excellent Implementing CIFS,
00018  * http://ubiqx.org/cifs/ , for whatever understanding I have of NBT.
00019  * I also stole from Mike McCormack's smb.c and netapi32.c, although little of
00020  * that code remains.
00021  * Lack of understanding and bugs are my fault.
00022  *
00023  * FIXME:
00024  * - Of the NetBIOS session functions, only client functions are supported, and
00025  *   it's likely they'll be the only functions supported.  NBT requires session
00026  *   servers to listen on TCP/139.  This requires root privilege, and Samba is
00027  *   likely to be listening here already.  This further restricts NetBIOS
00028  *   applications, both explicit users and implicit ones:  CreateNamedPipe
00029  *   won't actually create a listening pipe, for example, so applications can't
00030  *   act as RPC servers using a named pipe protocol binding, DCOM won't be able
00031  *   to support callbacks or servers over the named pipe protocol, etc.
00032  *
00033  * - Datagram support is omitted for the same reason.  To send a NetBIOS
00034  *   datagram, you must include the NetBIOS name by which your application is
00035  *   known.  This requires you to have registered the name previously, and be
00036  *   able to act as a NetBIOS datagram server (listening on UDP/138).
00037  *
00038  * - Name registration functions are omitted for the same reason--registering a
00039  *   name requires you to be able to defend it, and this means listening on
00040  *   UDP/137.
00041  *   Win98 requires you either use your computer's NetBIOS name (with the NULL
00042  *   suffix byte) as the calling name when creating a session, or to register
00043  *   a new name before creating one:  it disallows '*' as the calling name.
00044  *   Win2K initially starts with an empty name table, and doesn't allow you to
00045  *   use the machine's NetBIOS name (with the NULL suffix byte) as the calling
00046  *   name.  Although it allows sessions to be created with '*' as the calling
00047  *   name, doing so results in timeouts for all receives, because the
00048  *   application never gets them.
00049  *   So, a well-behaved NetBIOS application will typically want to register a
00050  *   name.  I should probably support a do-nothing name list that allows
00051  *   NCBADDNAME to add to it, but doesn't actually register the name, or does
00052  *   attempt to register it without being able to defend it.
00053  *
00054  * - Name lookups may not behave quite as you'd expect/like if you have
00055  *   multiple LANAs.  If a name is resolvable through DNS, or if you're using
00056  *   WINS, it'll resolve on _any_ LANA.  So, a Call will succeed on any LANA as
00057  *   well.
00058  *   I'm not sure how Windows behaves in this case.  I could try to force
00059  *   lookups to the correct adapter by using one of the GetPreferred*
00060  *   functions, but with the possibility of multiple adapters in the same
00061  *   same subnet, there's no guarantee that what IpHlpApi thinks is the
00062  *   preferred adapter will actually be a LANA.  (It's highly probable because
00063  *   this is an unusual configuration, but not guaranteed.)
00064  *
00065  * See also other FIXMEs in the code.
00066  */
00067 
00068 #include "config.h"
00069 #include <stdarg.h>
00070 
00071 #include "winsock2.h"
00072 #include "windef.h"
00073 #include "winbase.h"
00074 #include "wine/debug.h"
00075 #include "winreg.h"
00076 #include "iphlpapi.h"
00077 
00078 #include "netbios.h"
00079 #include "nbnamecache.h"
00080 
00081 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
00082 
00083 #define PORT_NBNS 137
00084 #define PORT_NBDG 138
00085 #define PORT_NBSS 139
00086 
00087 #ifndef INADDR_NONE
00088 #define INADDR_NONE ~0UL
00089 #endif
00090 
00091 #define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
00092 #define NBR_GETWORD(p) ntohs(*(WORD *)(p))
00093 
00094 #define MIN_QUERIES         1
00095 #define MAX_QUERIES         0xffff
00096 #define MIN_QUERY_TIMEOUT   100
00097 #define MAX_QUERY_TIMEOUT   0xffffffff
00098 #define BCAST_QUERIES       3
00099 #define BCAST_QUERY_TIMEOUT 750
00100 #define WINS_QUERIES        3
00101 #define WINS_QUERY_TIMEOUT  750
00102 #define MAX_WINS_SERVERS    2
00103 #define MIN_CACHE_TIMEOUT   60000
00104 #define CACHE_TIMEOUT       360000
00105 
00106 #define MAX_NBT_NAME_SZ            255
00107 #define SIMPLE_NAME_QUERY_PKT_SIZE 16 + MAX_NBT_NAME_SZ
00108 
00109 #define NBNS_TYPE_NB             0x0020
00110 #define NBNS_TYPE_NBSTAT         0x0021
00111 #define NBNS_CLASS_INTERNET      0x00001
00112 #define NBNS_HEADER_SIZE         (sizeof(WORD) * 6)
00113 #define NBNS_RESPONSE_AND_OPCODE 0xf800
00114 #define NBNS_RESPONSE_AND_QUERY  0x8000
00115 #define NBNS_REPLYCODE           0x0f
00116 
00117 #define NBSS_HDRSIZE 4
00118 
00119 #define NBSS_MSG       0x00
00120 #define NBSS_REQ       0x81
00121 #define NBSS_ACK       0x82
00122 #define NBSS_NACK      0x83
00123 #define NBSS_RETARGET  0x84
00124 #define NBSS_KEEPALIVE 0x85
00125 
00126 #define NBSS_ERR_NOT_LISTENING_ON_NAME    0x80
00127 #define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
00128 #define NBSS_ERR_BAD_NAME                 0x82
00129 #define NBSS_ERR_INSUFFICIENT_RESOURCES   0x83
00130 
00131 #define NBSS_EXTENSION 0x01
00132 
00133 typedef struct _NetBTSession
00134 {
00135     CRITICAL_SECTION cs;
00136     SOCKET           fd;
00137     DWORD            bytesPending;
00138 } NetBTSession;
00139 
00140 typedef struct _NetBTAdapter
00141 {
00142     MIB_IPADDRROW       ipr;
00143     WORD                nameQueryXID;
00144     struct NBNameCache *nameCache;
00145     DWORD               xmit_success;
00146     DWORD               recv_success;
00147 } NetBTAdapter;
00148 
00149 static ULONG gTransportID;
00150 static BOOL  gEnableDNS;
00151 static DWORD gBCastQueries;
00152 static DWORD gBCastQueryTimeout;
00153 static DWORD gWINSQueries;
00154 static DWORD gWINSQueryTimeout;
00155 static DWORD gWINSServers[MAX_WINS_SERVERS];
00156 static int   gNumWINSServers;
00157 static char  gScopeID[MAX_SCOPE_ID_LEN];
00158 static DWORD gCacheTimeout;
00159 static struct NBNameCache *gNameCache;
00160 
00161 /* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
00162  * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
00163  * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes.  Pads with space bytes
00164  * if p is NULL-terminated.  Returns the number of bytes stored in buffer.
00165  */
00166 static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
00167 {
00168     int i,len=0;
00169 
00170     if (!p) return 0;
00171     if (!buffer) return 0;
00172 
00173     buffer[len++] = NCBNAMSZ * 2;
00174     for (i = 0; p[i] && i < NCBNAMSZ; i++)
00175     {
00176         buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
00177         buffer[len++] =  (p[i] & 0x0f) + 'A';
00178     }
00179     while (len < NCBNAMSZ * 2)
00180     {
00181         buffer[len++] = 'C';
00182         buffer[len++] = 'A';
00183     }
00184     if (*gScopeID)
00185     {
00186         int scopeIDLen = strlen(gScopeID);
00187 
00188         memcpy(buffer + len, gScopeID, scopeIDLen);
00189         len += scopeIDLen;
00190     }
00191     buffer[len++] = 0;     /* add second terminator */
00192     return len;
00193 }
00194 
00195 /* Creates a NBT name request packet for name in buffer.  If broadcast is true,
00196  * creates a broadcast request, otherwise creates a unicast request.
00197  * Returns the number of bytes stored in buffer.
00198  */
00199 static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
00200  BOOL broadcast, UCHAR *buffer, int len)
00201 {
00202     int i = 0;
00203 
00204     if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
00205 
00206     NBR_ADDWORD(&buffer[i],xid);    i+=2; /* transaction */
00207     if (broadcast)
00208     {
00209         NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
00210         i+=2;
00211     }
00212     else
00213     {
00214         NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
00215         i+=2;
00216     }
00217     NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
00218     NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
00219     NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
00220     NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
00221 
00222     i += NetBTNameEncode(name, &buffer[i]);
00223 
00224     NBR_ADDWORD(&buffer[i],qtype); i+=2;
00225     NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
00226 
00227     return i;
00228 }
00229 
00230 /* Sends a name query request for name on fd to destAddr.  Sets SO_BROADCAST on
00231  * fd if broadcast is TRUE.  Assumes fd is not INVALID_SOCKET, and name is not
00232  * NULL.
00233  * Returns 0 on success, -1 on failure.
00234  */
00235 static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
00236  WORD qtype, DWORD destAddr, BOOL broadcast)
00237 {
00238     int ret = 0, on = 1;
00239     struct in_addr addr;
00240 
00241     addr.s_addr = destAddr;
00242     TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
00243 
00244     if (broadcast)
00245         ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
00246     if(ret == 0)
00247     {
00248         WSABUF wsaBuf;
00249         UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
00250         struct sockaddr_in sin;
00251 
00252         memset(&sin, 0, sizeof(sin));
00253         sin.sin_addr.s_addr = destAddr;
00254         sin.sin_family      = AF_INET;
00255         sin.sin_port        = htons(PORT_NBNS);
00256 
00257         wsaBuf.buf = (CHAR*)buf;
00258         wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
00259          sizeof(buf));
00260         if (wsaBuf.len > 0)
00261         {
00262             DWORD bytesSent;
00263 
00264             ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
00265              (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
00266             if (ret < 0 || bytesSent < wsaBuf.len)
00267                 ret = -1;
00268             else
00269                 ret = 0;
00270         }
00271         else
00272             ret = -1;
00273     }
00274     return ret;
00275 }
00276 
00277 typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
00278  WORD answerIndex, PUCHAR rData, WORD rdLength);
00279 
00280 /* Waits on fd until GetTickCount() returns a value greater than or equal to
00281  * waitUntil for a name service response.  If a name response matching xid
00282  * is received, calls answerCallback once for each answer resource record in
00283  * the response.  (The callback's answerCount will be the total number of
00284  * answers to expect, and answerIndex will be the 0-based index that's being
00285  * sent this time.)  Quits parsing if answerCallback returns FALSE.
00286  * Returns NRC_GOODRET on timeout or a valid response received, something else
00287  * on error.
00288  */
00289 static UCHAR NetBTWaitForNameResponse(const NetBTAdapter *adapter, SOCKET fd,
00290  DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
00291 {
00292     BOOL found = FALSE;
00293     DWORD now;
00294     UCHAR ret = NRC_GOODRET;
00295 
00296     if (!adapter) return NRC_BADDR;
00297     if (fd == INVALID_SOCKET) return NRC_BADDR;
00298     if (!answerCallback) return NRC_BADDR;
00299 
00300     while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil)
00301     {
00302         DWORD msToWait = waitUntil - now;
00303         struct fd_set fds;
00304         struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
00305         int r;
00306 
00307         FD_ZERO(&fds);
00308         FD_SET(fd, &fds);
00309         r = select(fd + 1, &fds, NULL, NULL, &timeout);
00310         if (r < 0)
00311             ret = NRC_SYSTEM;
00312         else if (r == 1)
00313         {
00314             /* FIXME: magic #, is this always enough? */
00315             UCHAR buffer[256];
00316             int fromsize;
00317             struct sockaddr_in fromaddr;
00318             WORD respXID, flags, queryCount, answerCount;
00319             WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer };
00320             DWORD bytesReceived, recvFlags = 0;
00321 
00322             fromsize = sizeof(fromaddr);
00323             r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
00324              (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
00325             if(r < 0)
00326             {
00327                 ret = NRC_SYSTEM;
00328                 break;
00329             }
00330 
00331             if (bytesReceived < NBNS_HEADER_SIZE)
00332                 continue;
00333 
00334             respXID = NBR_GETWORD(buffer);
00335             if (adapter->nameQueryXID != respXID)
00336                 continue;
00337 
00338             flags = NBR_GETWORD(buffer + 2);
00339             queryCount = NBR_GETWORD(buffer + 4);
00340             answerCount = NBR_GETWORD(buffer + 6);
00341 
00342             /* a reply shouldn't contain a query, ignore bad packet */
00343             if (queryCount > 0)
00344                 continue;
00345 
00346             if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
00347             {
00348                 if ((flags & NBNS_REPLYCODE) != 0)
00349                     ret = NRC_NAMERR;
00350                 else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
00351                 {
00352                     PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
00353                     BOOL shouldContinue = TRUE;
00354                     WORD answerIndex = 0;
00355 
00356                     found = TRUE;
00357                     /* decode one answer at a time */
00358                     while (ret == NRC_GOODRET && answerIndex < answerCount &&
00359                      ptr - buffer < bytesReceived && shouldContinue)
00360                     {
00361                         WORD rLen;
00362 
00363                         /* scan past name */
00364                         for (; ptr[0] && ptr - buffer < bytesReceived; )
00365                             ptr += ptr[0] + 1;
00366                         ptr++;
00367                         ptr += 2; /* scan past type */
00368                         if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
00369                          && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
00370                             ptr += sizeof(WORD);
00371                         else
00372                             ret = NRC_SYSTEM; /* parse error */
00373                         ptr += sizeof(DWORD); /* TTL */
00374                         rLen = NBR_GETWORD(ptr);
00375                         rLen = min(rLen, bytesReceived - (ptr - buffer));
00376                         ptr += sizeof(WORD);
00377                         shouldContinue = answerCallback(data, answerCount,
00378                          answerIndex, ptr, rLen);
00379                         ptr += rLen;
00380                         answerIndex++;
00381                     }
00382                 }
00383             }
00384         }
00385     }
00386     TRACE("returning 0x%02x\n", ret);
00387     return ret;
00388 }
00389 
00390 typedef struct _NetBTNameQueryData {
00391     NBNameCacheEntry *cacheEntry;
00392     UCHAR ret;
00393 } NetBTNameQueryData;
00394 
00395 /* Name query callback function for NetBTWaitForNameResponse, creates a cache
00396  * entry on the first answer, adds each address as it's called again (as long
00397  * as there's space).  If there's an error that should be propagated as the
00398  * NetBIOS error, modifies queryData's ret member to the proper return code.
00399  */
00400 static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount,
00401  WORD answerIndex, PUCHAR rData, WORD rLen)
00402 {
00403     NetBTNameQueryData *queryData = pVoid;
00404     BOOL ret;
00405 
00406     if (queryData)
00407     {
00408         if (queryData->cacheEntry == NULL)
00409         {
00410             queryData->cacheEntry = HeapAlloc(
00411              GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
00412              (answerCount - 1) * sizeof(DWORD));
00413             if (queryData->cacheEntry)
00414                 queryData->cacheEntry->numAddresses = 0;
00415             else
00416                 queryData->ret = NRC_OSRESNOTAV;
00417         }
00418         if (rLen == 6 && queryData->cacheEntry &&
00419          queryData->cacheEntry->numAddresses < answerCount)
00420         {
00421             queryData->cacheEntry->addresses[queryData->cacheEntry->
00422              numAddresses++] = *(const DWORD *)(rData + 2);
00423             ret = queryData->cacheEntry->numAddresses < answerCount;
00424         }
00425         else
00426             ret = FALSE;
00427     }
00428     else
00429         ret = FALSE;
00430     return ret;
00431 }
00432 
00433 /* Workhorse NetBT name lookup function.  Sends a name lookup query for
00434  * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
00435  * adapter->nameQueryXID as the transaction ID.  Waits up to timeout
00436  * milliseconds, and retries up to maxQueries times, waiting for a reply.
00437  * If a valid response is received, stores the looked up addresses as a
00438  * NBNameCacheEntry in *cacheEntry.
00439  * Returns NRC_GOODRET on success, though this may not mean the name was
00440  * resolved--check whether *cacheEntry is NULL.
00441  */
00442 static UCHAR NetBTNameWaitLoop(const NetBTAdapter *adapter, SOCKET fd, const NCB *ncb,
00443  DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
00444  NBNameCacheEntry **cacheEntry)
00445 {
00446     unsigned int queries;
00447     NetBTNameQueryData queryData;
00448 
00449     if (!adapter) return NRC_BADDR;
00450     if (fd == INVALID_SOCKET) return NRC_BADDR;
00451     if (!ncb) return NRC_BADDR;
00452     if (!cacheEntry) return NRC_BADDR;
00453 
00454     queryData.cacheEntry = NULL;
00455     queryData.ret = NRC_GOODRET;
00456     for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries;
00457      queries++)
00458     {
00459         if (!NCB_CANCELLED(ncb))
00460         {
00461             int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
00462              adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
00463 
00464             if (r == 0)
00465                 queryData.ret = NetBTWaitForNameResponse(adapter, fd,
00466                  GetTickCount() + timeout, NetBTFindNameAnswerCallback,
00467                  &queryData);
00468             else
00469                 queryData.ret = NRC_SYSTEM;
00470         }
00471         else
00472             queryData.ret = NRC_CMDCAN;
00473     }
00474     if (queryData.cacheEntry)
00475     {
00476         memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ);
00477         memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ);
00478     }
00479     *cacheEntry = queryData.cacheEntry;
00480     return queryData.ret;
00481 }
00482 
00483 /* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
00484  * has not yet been created, creates it, using gCacheTimeout as the cache
00485  * entry timeout.  If memory allocation fails, or if NBNameCacheAddEntry fails,
00486  * frees cacheEntry.
00487  * Returns NRC_GOODRET on success, and something else on failure.
00488  */
00489 static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
00490  NBNameCacheEntry *cacheEntry)
00491 {
00492     UCHAR ret;
00493 
00494     if (!nameCache) return NRC_BADDR;
00495     if (!cacheEntry) return NRC_BADDR;
00496 
00497     if (!*nameCache)
00498         *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
00499     if (*nameCache)
00500         ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
00501          ?  NRC_GOODRET : NRC_OSRESNOTAV;
00502     else
00503     {
00504         HeapFree(GetProcessHeap(), 0, cacheEntry);
00505         ret = NRC_OSRESNOTAV;
00506     }
00507     return ret;
00508 }
00509 
00510 /* Attempts to resolve name using inet_addr(), then gethostbyname() if
00511  * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>.  If the name
00512  * can be looked up, returns 0 and stores the looked up addresses as a
00513  * NBNameCacheEntry in *cacheEntry.
00514  * Returns NRC_GOODRET on success, though this may not mean the name was
00515  * resolved--check whether *cacheEntry is NULL.  Returns something else on
00516  * error.
00517  */
00518 static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ],
00519  NBNameCacheEntry **cacheEntry)
00520 {
00521     UCHAR ret = NRC_GOODRET;
00522 
00523     TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
00524 
00525     if (!name) return NRC_BADDR;
00526     if (!cacheEntry) return NRC_BADDR;
00527 
00528     if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
00529      name[NCBNAMSZ - 1] == 0x20))
00530     {
00531         CHAR toLookup[NCBNAMSZ];
00532         unsigned int i;
00533 
00534         for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
00535             toLookup[i] = name[i];
00536         toLookup[i] = '\0';
00537 
00538         if (isdigit(toLookup[0]))
00539         {
00540             unsigned long addr = inet_addr(toLookup);
00541 
00542             if (addr != INADDR_NONE)
00543             {
00544                 *cacheEntry = HeapAlloc(GetProcessHeap(),
00545                  0, sizeof(NBNameCacheEntry));
00546                 if (*cacheEntry)
00547                 {
00548                     memcpy((*cacheEntry)->name, name, NCBNAMSZ);
00549                     memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
00550                     (*cacheEntry)->nbname[0] = '*';
00551                     (*cacheEntry)->numAddresses = 1;
00552                     (*cacheEntry)->addresses[0] = addr;
00553                 }
00554                 else
00555                     ret = NRC_OSRESNOTAV;
00556             }
00557         }
00558         if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry)
00559         {
00560             struct hostent *host;
00561 
00562             if ((host = gethostbyname(toLookup)) != NULL)
00563             {
00564                 for (i = 0; ret == NRC_GOODRET && host->h_addr_list &&
00565                  host->h_addr_list[i]; i++)
00566                     ;
00567                 if (host->h_addr_list && host->h_addr_list[0])
00568                 {
00569                     *cacheEntry = HeapAlloc(
00570                      GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
00571                      (i - 1) * sizeof(DWORD));
00572                     if (*cacheEntry)
00573                     {
00574                         memcpy((*cacheEntry)->name, name, NCBNAMSZ);
00575                         memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
00576                         (*cacheEntry)->nbname[0] = '*';
00577                         (*cacheEntry)->numAddresses = i;
00578                         for (i = 0; i < (*cacheEntry)->numAddresses; i++)
00579                             (*cacheEntry)->addresses[i] =
00580                              (DWORD)host->h_addr_list[i];
00581                     }
00582                     else
00583                         ret = NRC_OSRESNOTAV;
00584                 }
00585             }
00586         }
00587     }
00588 
00589     TRACE("returning 0x%02x\n", ret);
00590     return ret;
00591 }
00592 
00593 /* Looks up the name in ncb->ncb_callname, first in the name caches (global
00594  * and this adapter's), then using gethostbyname(), next by WINS if configured,
00595  * and finally using broadcast NetBT name resolution.  In NBT parlance, this
00596  * makes this an "H-node".  Stores an entry in the appropriate name cache for a
00597  * found node, and returns it as *cacheEntry.
00598  * Assumes data, ncb, and cacheEntry are not NULL.
00599  * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
00600  * just that all name lookup operations completed successfully--and something
00601  * else on failure.  *cacheEntry will be NULL if the name was not found.
00602  */
00603 static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb,
00604  const NBNameCacheEntry **cacheEntry)
00605 {
00606     UCHAR ret = NRC_GOODRET;
00607 
00608     TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry);
00609 
00610     if (!cacheEntry) return NRC_BADDR;
00611     *cacheEntry = NULL;
00612 
00613     if (!adapter) return NRC_BADDR;
00614     if (!ncb) return NRC_BADDR;
00615 
00616     if (ncb->ncb_callname[0] == '*')
00617         ret = NRC_NOWILD;
00618     else
00619     {
00620         *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
00621         if (!*cacheEntry)
00622             *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
00623              ncb->ncb_callname);
00624         if (!*cacheEntry)
00625         {
00626             NBNameCacheEntry *newEntry = NULL;
00627 
00628             ret = NetBTinetResolve(ncb->ncb_callname, &newEntry);
00629             if (ret == NRC_GOODRET && newEntry)
00630             {
00631                 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
00632                 if (ret != NRC_GOODRET)
00633                     newEntry = NULL;
00634             }
00635             else
00636             {
00637                 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
00638                  0, WSA_FLAG_OVERLAPPED);
00639 
00640                 if(fd == INVALID_SOCKET)
00641                     ret = NRC_OSRESNOTAV;
00642                 else
00643                 {
00644                     int winsNdx;
00645 
00646                     adapter->nameQueryXID++;
00647                     for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL
00648                      && winsNdx < gNumWINSServers; winsNdx++)
00649                         ret = NetBTNameWaitLoop(adapter, fd, ncb,
00650                          gWINSServers[winsNdx], FALSE, gWINSQueryTimeout,
00651                          gWINSQueries, &newEntry);
00652                     if (ret == NRC_GOODRET && newEntry)
00653                     {
00654                         ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
00655                         if (ret != NRC_GOODRET)
00656                             newEntry = NULL;
00657                     }
00658                     if (ret == NRC_GOODRET && *cacheEntry == NULL)
00659                     {
00660                         DWORD bcastAddr =
00661                          adapter->ipr.dwAddr & adapter->ipr.dwMask;
00662 
00663                         if (adapter->ipr.dwBCastAddr)
00664                             bcastAddr |= ~adapter->ipr.dwMask;
00665                         ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr,
00666                          TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry);
00667                         if (ret == NRC_GOODRET && newEntry)
00668                         {
00669                             ret = NetBTStoreCacheEntry(&adapter->nameCache,
00670                              newEntry);
00671                             if (ret != NRC_GOODRET)
00672                                 newEntry = NULL;
00673                         }
00674                     }
00675                     closesocket(fd);
00676                 }
00677             }
00678             *cacheEntry = newEntry;
00679         }
00680     }
00681     TRACE("returning 0x%02x\n", ret);
00682     return ret;
00683 }
00684 
00685 typedef struct _NetBTNodeQueryData
00686 {
00687     BOOL gotResponse;
00688     PADAPTER_STATUS astat;
00689     WORD astatLen;
00690 } NetBTNodeQueryData;
00691 
00692 /* Callback function for NetBTAstatRemote, parses the rData for the node
00693  * status and name list of the remote node.  Always returns FALSE, since
00694  * there's never more than one answer we care about in a node status response.
00695  */
00696 static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
00697  WORD answerIndex, PUCHAR rData, WORD rLen)
00698 {
00699     NetBTNodeQueryData *data = pVoid;
00700 
00701     if (data && !data->gotResponse && rData && rLen >= 1)
00702     {
00703         /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
00704         if (rLen >= rData[0] * (NCBNAMSZ + 2))
00705         {
00706             WORD i;
00707             PUCHAR src;
00708             PNAME_BUFFER dst;
00709 
00710             data->gotResponse = TRUE;
00711             data->astat->name_count = rData[0];
00712             for (i = 0, src = rData + 1,
00713              dst = (PNAME_BUFFER)((PUCHAR)data->astat +
00714               sizeof(ADAPTER_STATUS));
00715              i < data->astat->name_count && src - rData < rLen &&
00716              (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
00717              i++, dst++, src += NCBNAMSZ + 2)
00718             {
00719                 UCHAR flags = *(src + NCBNAMSZ);
00720 
00721                 memcpy(dst->name, src, NCBNAMSZ);
00722                 /* we won't actually see a registering name in the returned
00723                  * response.  It's useful to see if no other flags are set; if
00724                  * none are, then the name is registered. */
00725                 dst->name_flags = REGISTERING;
00726                 if (flags & 0x80)
00727                     dst->name_flags |= GROUP_NAME;
00728                 if (flags & 0x10)
00729                     dst->name_flags |= DEREGISTERED;
00730                 if (flags & 0x08)
00731                     dst->name_flags |= DUPLICATE;
00732                 if (dst->name_flags == REGISTERING)
00733                     dst->name_flags = REGISTERED;
00734             }
00735             /* arbitrarily set HW type to Ethernet */
00736             data->astat->adapter_type = 0xfe;
00737             if (src - rData < rLen)
00738                 memcpy(data->astat->adapter_address, src,
00739                  min(rLen - (src - rData), 6));
00740         }
00741     }
00742     return FALSE;
00743 }
00744 
00745 /* This uses the WINS timeout and query values, as they're the
00746  * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
00747  */
00748 static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
00749 {
00750     UCHAR ret = NRC_GOODRET;
00751     const NBNameCacheEntry *cacheEntry = NULL;
00752 
00753     TRACE("adapter %p, NCB %p\n", adapter, ncb);
00754 
00755     if (!adapter) return NRC_BADDR;
00756     if (!ncb) return NRC_INVADDRESS;
00757 
00758     ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
00759     if (ret == NRC_GOODRET && cacheEntry)
00760     {
00761         if (cacheEntry->numAddresses > 0)
00762         {
00763             SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
00764              WSA_FLAG_OVERLAPPED);
00765 
00766             if(fd == INVALID_SOCKET)
00767                 ret = NRC_OSRESNOTAV;
00768             else
00769             {
00770                 NetBTNodeQueryData queryData;
00771                 DWORD queries;
00772                 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
00773 
00774                 adapter->nameQueryXID++;
00775                 astat->name_count = 0;
00776                 queryData.gotResponse = FALSE;
00777                 queryData.astat = astat;
00778                 queryData.astatLen = ncb->ncb_length;
00779                 for (queries = 0; !queryData.gotResponse &&
00780                  queries < gWINSQueries; queries++)
00781                 {
00782                     if (!NCB_CANCELLED(ncb))
00783                     {
00784                         int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
00785                          adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
00786                          cacheEntry->addresses[0], FALSE);
00787 
00788                         if (r == 0)
00789                             ret = NetBTWaitForNameResponse(adapter, fd,
00790                              GetTickCount() + gWINSQueryTimeout,
00791                              NetBTNodeStatusAnswerCallback, &queryData);
00792                         else
00793                             ret = NRC_SYSTEM;
00794                     }
00795                     else
00796                         ret = NRC_CMDCAN;
00797                 }
00798                 closesocket(fd);
00799             }
00800         }
00801         else
00802             ret = NRC_CMDTMO;
00803     }
00804     else if (ret == NRC_CMDCAN)
00805         ; /* do nothing, we were cancelled */
00806     else
00807         ret = NRC_CMDTMO;
00808     TRACE("returning 0x%02x\n", ret);
00809     return ret;
00810 }
00811 
00812 static UCHAR NetBTAstat(void *adapt, PNCB ncb)
00813 {
00814     NetBTAdapter *adapter = adapt;
00815     UCHAR ret;
00816 
00817     TRACE("adapt %p, NCB %p\n", adapt, ncb);
00818 
00819     if (!adapter) return NRC_ENVNOTDEF;
00820     if (!ncb) return NRC_INVADDRESS;
00821     if (!ncb->ncb_buffer) return NRC_BADDR;
00822     if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
00823 
00824     if (ncb->ncb_callname[0] == '*')
00825     {
00826         DWORD physAddrLen;
00827         MIB_IFROW ifRow;
00828         PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
00829   
00830         memset(astat, 0, sizeof(ADAPTER_STATUS));
00831         astat->rev_major = 3;
00832         ifRow.dwIndex = adapter->ipr.dwIndex;
00833         if (GetIfEntry(&ifRow) != NO_ERROR)
00834             ret = NRC_BRIDGE;
00835         else
00836         {
00837             physAddrLen = min(ifRow.dwPhysAddrLen, 6);
00838             if (physAddrLen > 0)
00839                 memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
00840             /* doubt anyone cares, but why not.. */
00841             if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
00842                 astat->adapter_type = 0xff;
00843             else
00844                 astat->adapter_type = 0xfe; /* for Ethernet */
00845             astat->max_sess_pkt_size = 0xffff;
00846             astat->xmit_success = adapter->xmit_success;
00847             astat->recv_success = adapter->recv_success;
00848             ret = NRC_GOODRET;
00849         }
00850     }
00851     else
00852         ret = NetBTAstatRemote(adapter, ncb);
00853     TRACE("returning 0x%02x\n", ret);
00854     return ret;
00855 }
00856 
00857 static UCHAR NetBTFindName(void *adapt, PNCB ncb)
00858 {
00859     NetBTAdapter *adapter = adapt;
00860     UCHAR ret;
00861     const NBNameCacheEntry *cacheEntry = NULL;
00862     PFIND_NAME_HEADER foundName;
00863 
00864     TRACE("adapt %p, NCB %p\n", adapt, ncb);
00865 
00866     if (!adapter) return NRC_ENVNOTDEF;
00867     if (!ncb) return NRC_INVADDRESS;
00868     if (!ncb->ncb_buffer) return NRC_BADDR;
00869     if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
00870 
00871     foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
00872     memset(foundName, 0, sizeof(FIND_NAME_HEADER));
00873 
00874     ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
00875     if (ret == NRC_GOODRET)
00876     {
00877         if (cacheEntry)
00878         {
00879             DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
00880              sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
00881             DWORD ndx;
00882 
00883             for (ndx = 0; ndx < spaceFor; ndx++)
00884             {
00885                 PFIND_NAME_BUFFER findNameBuffer;
00886 
00887                 findNameBuffer =
00888                  (PFIND_NAME_BUFFER)((PUCHAR)foundName +
00889                  sizeof(FIND_NAME_HEADER) + foundName->node_count *
00890                  sizeof(FIND_NAME_BUFFER));
00891                 memset(findNameBuffer->destination_addr, 0, 2);
00892                 memcpy(findNameBuffer->destination_addr + 2,
00893                  &adapter->ipr.dwAddr, sizeof(DWORD));
00894                 memset(findNameBuffer->source_addr, 0, 2);
00895                 memcpy(findNameBuffer->source_addr + 2,
00896                  &cacheEntry->addresses[ndx], sizeof(DWORD));
00897                 foundName->node_count++;
00898             }
00899             if (spaceFor < cacheEntry->numAddresses)
00900                 ret = NRC_BUFLEN;
00901         }
00902         else
00903             ret = NRC_CMDTMO;
00904     }
00905     TRACE("returning 0x%02x\n", ret);
00906     return ret;
00907 }
00908 
00909 static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
00910  const UCHAR *callingName)
00911 {
00912     UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
00913     int r;
00914     unsigned int len = 0;
00915     DWORD bytesSent, bytesReceived, recvFlags = 0;
00916     WSABUF wsaBuf;
00917 
00918     buffer[0] = NBSS_REQ;
00919     buffer[1] = 0;
00920 
00921     len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
00922     len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
00923 
00924     NBR_ADDWORD(&buffer[2], len);
00925 
00926     wsaBuf.len = len + NBSS_HDRSIZE;
00927     wsaBuf.buf = (char*)buffer;
00928 
00929     r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
00930     if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
00931     {
00932         ERR("send failed\n");
00933         return NRC_SABORT;
00934     }
00935 
00936     /* I've already set the recv timeout on this socket (if it supports it), so
00937      * just block.  Hopefully we'll always receive the session acknowledgement
00938      * within one timeout.
00939      */
00940     wsaBuf.len = NBSS_HDRSIZE + 1;
00941     r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
00942     if (r < 0 || bytesReceived < NBSS_HDRSIZE)
00943         ret = NRC_SABORT;
00944     else if (buffer[0] == NBSS_NACK)
00945     {
00946         if (r == NBSS_HDRSIZE + 1)
00947         {
00948             switch (buffer[NBSS_HDRSIZE])
00949             {
00950                 case NBSS_ERR_INSUFFICIENT_RESOURCES:
00951                     ret = NRC_REMTFUL;
00952                     break;
00953                 default:
00954                     ret = NRC_NOCALL;
00955             }
00956         }
00957         else
00958             ret = NRC_NOCALL;
00959     }
00960     else if (buffer[0] == NBSS_RETARGET)
00961     {
00962         FIXME("Got a session retarget, can't deal\n");
00963         ret = NRC_NOCALL;
00964     }
00965     else if (buffer[0] == NBSS_ACK)
00966         ret = NRC_GOODRET;
00967     else
00968         ret = NRC_SYSTEM;
00969 
00970     TRACE("returning 0x%02x\n", ret);
00971     return ret;
00972 }
00973 
00974 static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
00975 {
00976     NetBTAdapter *adapter = adapt;
00977     UCHAR ret;
00978     const NBNameCacheEntry *cacheEntry = NULL;
00979 
00980     TRACE("adapt %p, ncb %p\n", adapt, ncb);
00981 
00982     if (!adapter) return NRC_ENVNOTDEF;
00983     if (!ncb) return NRC_INVADDRESS;
00984     if (!sess) return NRC_BADDR;
00985 
00986     ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
00987     if (ret == NRC_GOODRET)
00988     {
00989         if (cacheEntry && cacheEntry->numAddresses > 0)
00990         {
00991             SOCKET fd;
00992 
00993             fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
00994              WSA_FLAG_OVERLAPPED);
00995             if (fd != INVALID_SOCKET)
00996             {
00997                 DWORD timeout;
00998                 struct sockaddr_in sin;
00999 
01000                 if (ncb->ncb_rto > 0)
01001                 {
01002                     timeout = ncb->ncb_rto * 500;
01003                     setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
01004                      sizeof(timeout));
01005                 }
01006                 if (ncb->ncb_rto > 0)
01007                 {
01008                     timeout = ncb->ncb_sto * 500;
01009                     setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
01010                      sizeof(timeout));
01011                 }
01012 
01013                 memset(&sin, 0, sizeof(sin));
01014                 memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
01015                  sizeof(sin.sin_addr));
01016                 sin.sin_family = AF_INET;
01017                 sin.sin_port   = htons(PORT_NBSS);
01018                 /* FIXME: use nonblocking mode for the socket, check the
01019                  * cancel flag periodically
01020                  */
01021                 if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
01022                  == SOCKET_ERROR)
01023                     ret = NRC_CMDTMO;
01024                 else
01025                 {
01026                     static const UCHAR fakedCalledName[] = "*SMBSERVER";
01027                     const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
01028                      ? fakedCalledName : cacheEntry->nbname;
01029 
01030                     ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
01031                     if (ret != NRC_GOODRET && calledParty[0] == '*')
01032                     {
01033                         FIXME("NBT session to \"*SMBSERVER\" refused,\n");
01034                         FIXME("should try finding name using ASTAT\n");
01035                     }
01036                 }
01037                 if (ret != NRC_GOODRET)
01038                     closesocket(fd);
01039                 else
01040                 {
01041                     NetBTSession *session = HeapAlloc(
01042                      GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
01043 
01044                     if (session)
01045                     {
01046                         session->fd = fd;
01047                         InitializeCriticalSection(&session->cs);
01048                         session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NetBTSession.cs");
01049                         *sess = session;
01050                     }
01051                     else
01052                     {
01053                         ret = NRC_OSRESNOTAV;
01054                         closesocket(fd);
01055                     }
01056                 }
01057             }
01058             else
01059                 ret = NRC_OSRESNOTAV;
01060         }
01061         else
01062             ret = NRC_NAMERR;
01063     }
01064     TRACE("returning 0x%02x\n", ret);
01065     return ret;
01066 }
01067 
01068 /* Notice that I don't protect against multiple thread access to NetBTSend.
01069  * This is because I don't update any data in the adapter, and I only make a
01070  * single call to WSASend, which I assume to act atomically (not interleaving
01071  * data from other threads).
01072  * I don't lock, because I only depend on the fd being valid, and this won't be
01073  * true until a session setup is completed.
01074  */
01075 static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb)
01076 {
01077     NetBTAdapter *adapter = adapt;
01078     NetBTSession *session = sess;
01079     UCHAR buffer[NBSS_HDRSIZE], ret;
01080     int r;
01081     WSABUF wsaBufs[2];
01082     DWORD bytesSent;
01083 
01084     TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
01085 
01086     if (!adapter) return NRC_ENVNOTDEF;
01087     if (!ncb) return NRC_INVADDRESS;
01088     if (!ncb->ncb_buffer) return NRC_BADDR;
01089     if (!session) return NRC_SNUMOUT;
01090     if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
01091 
01092     buffer[0] = NBSS_MSG;
01093     buffer[1] = 0;
01094     NBR_ADDWORD(&buffer[2], ncb->ncb_length);
01095 
01096     wsaBufs[0].len = NBSS_HDRSIZE;
01097     wsaBufs[0].buf = (char*)buffer;
01098     wsaBufs[1].len = ncb->ncb_length;
01099     wsaBufs[1].buf = (char*)ncb->ncb_buffer;
01100 
01101     r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]),
01102      &bytesSent, 0, NULL, NULL);
01103     if (r == SOCKET_ERROR)
01104     {
01105         NetBIOSHangupSession(ncb);
01106         ret = NRC_SABORT;
01107     }
01108     else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
01109     {
01110         FIXME("Only sent %d bytes (of %d), hanging up session\n", bytesSent,
01111          NBSS_HDRSIZE + ncb->ncb_length);
01112         NetBIOSHangupSession(ncb);
01113         ret = NRC_SABORT;
01114     }
01115     else
01116     {
01117         ret = NRC_GOODRET;
01118         adapter->xmit_success++;
01119     }
01120     TRACE("returning 0x%02x\n", ret);
01121     return ret;
01122 }
01123 
01124 static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
01125 {
01126     NetBTAdapter *adapter = adapt;
01127     NetBTSession *session = sess;
01128     UCHAR buffer[NBSS_HDRSIZE], ret;
01129     int r;
01130     WSABUF wsaBufs[2];
01131     DWORD bufferCount, bytesReceived, flags;
01132 
01133     TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
01134 
01135     if (!adapter) return NRC_ENVNOTDEF;
01136     if (!ncb) return NRC_BADDR;
01137     if (!ncb->ncb_buffer) return NRC_BADDR;
01138     if (!session) return NRC_SNUMOUT;
01139     if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
01140 
01141     EnterCriticalSection(&session->cs);
01142     bufferCount = 0;
01143     if (session->bytesPending == 0)
01144     {
01145         bufferCount++;
01146         wsaBufs[0].len = NBSS_HDRSIZE;
01147         wsaBufs[0].buf = (char*)buffer;
01148     }
01149     wsaBufs[bufferCount].len = ncb->ncb_length;
01150     wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer;
01151     bufferCount++;
01152 
01153     flags = 0;
01154     /* FIXME: should poll a bit so I can check the cancel flag */
01155     r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
01156      NULL, NULL);
01157     if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
01158     {
01159         LeaveCriticalSection(&session->cs);
01160         ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
01161         NetBIOSHangupSession(ncb);
01162         ret = NRC_SABORT;
01163     }
01164     else if (NCB_CANCELLED(ncb))
01165     {
01166         LeaveCriticalSection(&session->cs);
01167         ret = NRC_CMDCAN;
01168     }
01169     else
01170     {
01171         if (bufferCount == 2)
01172         {
01173             if (buffer[0] == NBSS_KEEPALIVE)
01174             {
01175                 LeaveCriticalSection(&session->cs);
01176                 FIXME("Oops, received a session keepalive and lost my place\n");
01177                 /* need to read another session header until we get a session
01178                  * message header. */
01179                 NetBIOSHangupSession(ncb);
01180                 ret = NRC_SABORT;
01181                 goto error;
01182             }
01183             else if (buffer[0] != NBSS_MSG)
01184             {
01185                 LeaveCriticalSection(&session->cs);
01186                 FIXME("Received unexpected session msg type %d\n", buffer[0]);
01187                 NetBIOSHangupSession(ncb);
01188                 ret = NRC_SABORT;
01189                 goto error;
01190             }
01191             else
01192             {
01193                 if (buffer[1] & NBSS_EXTENSION)
01194                 {
01195                     LeaveCriticalSection(&session->cs);
01196                     FIXME("Received a message that's too long for my taste\n");
01197                     NetBIOSHangupSession(ncb);
01198                     ret = NRC_SABORT;
01199                     goto error;
01200                 }
01201                 else
01202                 {
01203                     session->bytesPending = NBSS_HDRSIZE
01204                      + NBR_GETWORD(&buffer[2]) - bytesReceived;
01205                     ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
01206                     LeaveCriticalSection(&session->cs);
01207                 }
01208             }
01209         }
01210         else
01211         {
01212             if (bytesReceived < session->bytesPending)
01213                 session->bytesPending -= bytesReceived;
01214             else
01215                 session->bytesPending = 0;
01216             LeaveCriticalSection(&session->cs);
01217             ncb->ncb_length = bytesReceived;
01218         }
01219         if (session->bytesPending > 0)
01220             ret = NRC_INCOMP;
01221         else
01222         {
01223             ret = NRC_GOODRET;
01224             adapter->recv_success++;
01225         }
01226     }
01227 error:
01228     TRACE("returning 0x%02x\n", ret);
01229     return ret;
01230 }
01231 
01232 static UCHAR NetBTHangup(void *adapt, void *sess)
01233 {
01234     NetBTSession *session = sess;
01235 
01236     TRACE("adapt %p, session %p\n", adapt, session);
01237 
01238     if (!session) return NRC_SNUMOUT;
01239 
01240     /* I don't lock the session, because NetBTRecv knows not to decrement
01241      * past 0, so if a receive completes after this it should still deal.
01242      */
01243     closesocket(session->fd);
01244     session->fd = INVALID_SOCKET;
01245     session->bytesPending = 0;
01246     session->cs.DebugInfo->Spare[0] = 0;
01247     DeleteCriticalSection(&session->cs);
01248     HeapFree(GetProcessHeap(), 0, session);
01249 
01250     return NRC_GOODRET;
01251 }
01252 
01253 static void NetBTCleanupAdapter(void *adapt)
01254 {
01255     TRACE("adapt %p\n", adapt);
01256     if (adapt)
01257     {
01258         NetBTAdapter *adapter = adapt;
01259 
01260         if (adapter->nameCache)
01261             NBNameCacheDestroy(adapter->nameCache);
01262         HeapFree(GetProcessHeap(), 0, adapt);
01263     }
01264 }
01265 
01266 static void NetBTCleanup(void)
01267 {
01268     TRACE("\n");
01269     if (gNameCache)
01270     {
01271         NBNameCacheDestroy(gNameCache);
01272         gNameCache = NULL;
01273     }
01274 }
01275 
01276 static UCHAR NetBTRegisterAdapter(const MIB_IPADDRROW *ipRow)
01277 {
01278     UCHAR ret;
01279     NetBTAdapter *adapter;
01280 
01281     if (!ipRow) return NRC_BADDR;
01282 
01283     adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter));
01284     if (adapter)
01285     {
01286         adapter->ipr = *ipRow;
01287         if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
01288         {
01289             NetBTCleanupAdapter(adapter);
01290             ret = NRC_SYSTEM;
01291         }
01292         else
01293             ret = NRC_GOODRET;
01294     }
01295     else
01296         ret = NRC_OSRESNOTAV;
01297     return ret;
01298 }
01299 
01300 /* Callback for NetBIOS adapter enumeration.  Assumes closure is a pointer to
01301  * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
01302  * NetBIOS adapter table.  For each callback, checks if the passed-in adapt
01303  * has an entry in the table; if so, this adapter was enumerated previously,
01304  * and it's enabled.  As a flag, the table's dwAddr entry is changed to
01305  * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
01306  * The NetBTEnum function will add any remaining adapters from the
01307  * MIB_IPADDRTABLE to the NetBIOS adapter table.
01308  */
01309 static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
01310  ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
01311 {
01312     BOOL ret;
01313     PMIB_IPADDRTABLE table = closure;
01314 
01315     if (table && data)
01316     {
01317         DWORD ndx;
01318 
01319         ret = FALSE;
01320         for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
01321         {
01322             const NetBTAdapter *adapter = data->data;
01323 
01324             if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
01325             {
01326                 NetBIOSEnableAdapter(data->lana);
01327                 table->table[ndx].dwAddr = INADDR_LOOPBACK;
01328                 ret = TRUE;
01329             }
01330         }
01331     }
01332     else
01333         ret = FALSE;
01334     return ret;
01335 }
01336 
01337 /* Enumerates adapters by:
01338  * - retrieving the IP address table for the local machine
01339  * - eliminating loopback addresses from the table
01340  * - eliminating redundant addresses, that is, multiple addresses on the same
01341  *   subnet
01342  * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
01343  * data.  The callback reenables each adapter that's already in the NetBIOS
01344  * table.  After NetBIOSEnumAdapters returns, this function adds any remaining
01345  * adapters to the NetBIOS table.
01346  */
01347 static UCHAR NetBTEnum(void)
01348 {
01349     UCHAR ret;
01350     DWORD size = 0;
01351 
01352     TRACE("\n");
01353 
01354     if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
01355     {
01356         PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
01357         DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
01358          sizeof(MIB_IPADDRROW) + 1;
01359 
01360         ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
01361         if (ipAddrs)
01362             coalesceTable = HeapAlloc(GetProcessHeap(),
01363              HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
01364              (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
01365         if (ipAddrs && coalesceTable)
01366         {
01367             if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
01368             {
01369                 DWORD ndx;
01370 
01371                 for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
01372                 {
01373                     if ((ipAddrs->table[ndx].dwAddr &
01374                      ipAddrs->table[ndx].dwMask) !=
01375                      htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
01376                     {
01377                         BOOL newNetwork = TRUE;
01378                         DWORD innerIndex;
01379 
01380                         /* make sure we don't have more than one entry
01381                          * for a subnet */
01382                         for (innerIndex = 0; newNetwork &&
01383                          innerIndex < coalesceTable->dwNumEntries; innerIndex++)
01384                             if ((ipAddrs->table[ndx].dwAddr &
01385                              ipAddrs->table[ndx].dwMask) ==
01386                              (coalesceTable->table[innerIndex].dwAddr
01387                              & coalesceTable->table[innerIndex].dwMask))
01388                                 newNetwork = FALSE;
01389 
01390                         if (newNetwork)
01391                             memcpy(&coalesceTable->table[
01392                              coalesceTable->dwNumEntries++],
01393                              &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
01394                     }
01395                 }
01396 
01397                 NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
01398                  coalesceTable);
01399                 ret = NRC_GOODRET;
01400                 for (ndx = 0; ret == NRC_GOODRET &&
01401                  ndx < coalesceTable->dwNumEntries; ndx++)
01402                     if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
01403                         ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
01404             }
01405             else
01406                 ret = NRC_SYSTEM;
01407             HeapFree(GetProcessHeap(), 0, ipAddrs);
01408             HeapFree(GetProcessHeap(), 0, coalesceTable);
01409         }
01410         else
01411             ret = NRC_OSRESNOTAV;
01412     }
01413     else
01414         ret = NRC_SYSTEM;
01415     TRACE("returning 0x%02x\n", ret);
01416     return ret;
01417 }
01418 
01419 static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r',
01420  'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v',
01421  'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' };
01422 static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u',
01423  'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r',
01424  'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t',
01425  'e','r','s','\0' };
01426 static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' };
01427 static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m',
01428  'e','Q','u','e','r','y','C','o','u','n','t','\0' };
01429 static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m',
01430  'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
01431 static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v',
01432  'Q','u','e','r','y','C','o','u','n','t','\0' };
01433 static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v',
01434  'Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
01435 static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' };
01436 static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o',
01437  'u','t','\0' };
01438 static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\',
01439                                          'W','i','n','e','\\','N','e','t','w','o','r','k','\0' };
01440 
01441 /* Initializes global variables and registers the NetBT transport */
01442 void NetBTInit(void)
01443 {
01444     HKEY hKey;
01445     NetBIOSTransport transport;
01446     LONG ret;
01447 
01448     TRACE("\n");
01449 
01450     gEnableDNS = TRUE;
01451     gBCastQueries = BCAST_QUERIES;
01452     gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
01453     gWINSQueries = WINS_QUERIES;
01454     gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
01455     gNumWINSServers = 0;
01456     memset(gWINSServers, 0, sizeof(gWINSServers));
01457     gScopeID[0] = '\0';
01458     gCacheTimeout = CACHE_TIMEOUT;
01459 
01460     /* Try to open the Win9x NetBT configuration key */
01461     ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey);
01462     /* If that fails, try the WinNT NetBT configuration key */
01463     if (ret != ERROR_SUCCESS)
01464         ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ,
01465          &hKey);
01466     if (ret == ERROR_SUCCESS)
01467     {
01468         DWORD dword, size;
01469 
01470         size = sizeof(dword);
01471         if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL,
01472          (LPBYTE)&dword, &size) == ERROR_SUCCESS)
01473             gEnableDNS = dword;
01474         size = sizeof(dword);
01475         if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL,
01476          (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
01477          && dword <= MAX_QUERIES)
01478             gBCastQueries = dword;
01479         size = sizeof(dword);
01480         if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL,
01481          (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT)
01482             gBCastQueryTimeout = dword;
01483         size = sizeof(dword);
01484         if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL,
01485          (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
01486          && dword <= MAX_QUERIES)
01487             gWINSQueries = dword;
01488         size = sizeof(dword);
01489         if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL,
01490          (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT)
01491             gWINSQueryTimeout = dword;
01492         size = sizeof(gScopeID) - 1;
01493         if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size)
01494          == ERROR_SUCCESS)
01495         {
01496             /* convert into L2-encoded version, suitable for use by
01497                NetBTNameEncode */
01498             char *ptr, *lenPtr;
01499 
01500             for (ptr = gScopeID + 1; ptr - gScopeID < sizeof(gScopeID) && *ptr; )
01501             {
01502                 for (lenPtr = ptr - 1, *lenPtr = 0;
01503                      ptr - gScopeID < sizeof(gScopeID) && *ptr && *ptr != '.';
01504                      ptr++)
01505                     *lenPtr += 1;
01506                 ptr++;
01507             }
01508         }
01509         if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL,
01510          (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT)
01511             gCacheTimeout = dword;
01512         RegCloseKey(hKey);
01513     }
01514     /* WINE-specific NetBT registry settings.  Because our adapter naming is
01515      * different than MS', we can't do per-adapter WINS configuration in the
01516      * same place.  Just do a global WINS configuration instead.
01517      */
01518     /* @@ Wine registry key: HKCU\Software\Wine\Network */
01519     if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS)
01520     {
01521         static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" };
01522         char nsString[16];
01523         DWORD size, ndx;
01524 
01525         for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]);
01526          ndx++)
01527         {
01528             size = sizeof(nsString) / sizeof(char);
01529             if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
01530              (LPBYTE)nsString, &size) == ERROR_SUCCESS)
01531             {
01532                 unsigned long addr = inet_addr(nsString);
01533 
01534                 if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
01535                     gWINSServers[gNumWINSServers++] = addr;
01536             }
01537         }
01538         RegCloseKey(hKey);
01539     }
01540 
01541     transport.enumerate      = NetBTEnum;
01542     transport.astat          = NetBTAstat;
01543     transport.findName       = NetBTFindName;
01544     transport.call           = NetBTCall;
01545     transport.send           = NetBTSend;
01546     transport.recv           = NetBTRecv;
01547     transport.hangup         = NetBTHangup;
01548     transport.cleanupAdapter = NetBTCleanupAdapter;
01549     transport.cleanup        = NetBTCleanup;
01550     memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
01551     NetBIOSRegisterTransport(gTransportID, &transport);
01552 }

Generated on Sat May 26 2012 04:23:59 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.