zoukankan      html  css  js  c++  java
  • [转]应用SMB/CIFS协议

    第一节 本文的目的
       Microsoft公开了CIFS协议的所有细节,这使得我们可以了解这个协议并且编写基于这个协议的应用程序。 SMB/CIFS协议在Windows系统中的被广泛的应用,这要求我们对这个协议应该有所了解,下面文字就我的一点实际经验与大家进行交流,如果有错误的地方,真诚的希望得到大家的指正,我的Email:ilsy@whitecell.org。
    第二节 什么是SMB/CIFS协议?
       CIFS(Common Internet File System)是开放的跨平台的,其实现是基于SMB(Server Message Block)协议的,使用户可以使用这个协议方便的向支持SMB协议的网络服务器请求文件和打印服务。 
       在不同的操作系统上,都有相应的SMB协议的实现,包括我们知道的Samba、Windows的不同版本等,而应用CIFS协议都可以与其通讯,CIFS是一个开放的SMB版本。可以从http://msdn.microsoft.com/downloads /default.asp?url=/downloads/sample.asp?url=/msdn-files/027/001/902 /msdncompositedoc.xml下载CIFS详细的说明文档或者看最新的Platform SDK文档。
       
       在所有的SMB数据报中都会包含下面的数据头:
       typedef unsigned char UCHAR;                // 8  unsigned bits
       typedef unsigned short USHORT;              // 16 unsigned bits
       typedef unsigned long ULONG;                // 32 unsigned bits
       typedef struct {
           ULONG LowPart;
           LONG HighPart;
       } LARGE_INTEGER;                            // 64 bits of data
       //  结构根据不同的SMB请求会有不同的变化
       typedef struct  {
           UCHAR Protocol[4];                      // 协议的名字'SMB', 前面放一个0xFF
           UCHAR Command;                          // 请求的命令类型
           union {
               struct {
                   UCHAR ErrorClass;               // Error class
                   UCHAR Reserved;                 // Reserved for future use
                   USHORT Error;                   // Error code
               } DosError;
               ULONG Status;                       // 32- bit 错误代码
           } Status;
           UCHAR Flags;                            // 标记
           USHORT Flags2;                          // 扩展标记
           union {
               USHORT Pad[6];                      // Ensure section is 12 bytes long
               struct {
                   USHORT PidHigh;                 // High part of PID
                   UCHAR SecuritySignature[8];     // reserved for security
             } Extra;
           };
           USHORT Tid;                             // Tree 标识, 用来识别资源
           USHORT Pid;                             // Caller's process id
           USHORT Uid;                             // 用来识别用户
           USHORT Mid;                             // multiplex id
           UCHAR  WordCount;                       // Count of parameter words
           USHORT ParameterWords[ WordCount ];     // The parameter words
           USHORT ByteCount;                       // Count of bytes
           UCHAR  Buffer[ ByteCount ];             // The bytes
       } SMB_HEADER;
       
       而每一个SMB数据报都会包含这样的一个NetBIOS会话头:
       
       //  NBT_HEADER 结构
       typedef struct  {
            struct  {
                    u_char  packetType;            // 数据报类型,在使用SMB数据报时,这个字段的值是0x00
                    u_char  packetFlags;           // 这个字段总是为0x00
                    u_short packetLen;             // 在SMB数据报中,为SMB数据报的总长度,但不包含这个结构的长度
                    } s;
       } NBT_HEADER;
       
       实际上,一个SMB数据报是这样构成的: NBT Header + SMB Header + SMB Command Header + SMB Data
    第三节 建立一个空会话
       SMB可以运行在TCP/IP等协议之上,如在我们常用的系统Windows系列操作系统上,如果要在TCP/IP上使用SMB协议,那么,你需要支持NetBIOS名字的传输。NETBIOS名字用来在网络上鉴别一台提供SMB服务计算机,使用SMB协议前,首先应该用NetBIOS名字去鉴别一台提供SMB服务的计算机。
       我们可以用下面的代码去取得一台计算机的NetBios名字:
       
       #include 
       #include 
       #include 
       #define xmalloc(s)   (char *)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, (s)) //  分配内存
       #define xfree(p)     (char *)HeapFree (GetProcessHeap(),0, (p))                //  释放内存
       //  NBT_NAME_HEADER 结构
       typedef struct  {
     USHORT ID;
     USHORT Flags;
     USHORT Questions;
     USHORT Answer;
     USHORT Authority;
     USHORT Additional;
       }NBT_NAME_HEADER;
       //  NBT_NAME_ANSWER 结构
       typedef struct  {
     char   Name_[34];
     USHORT Type;
            USHORT Class;
     ULONG  TTL;
     USHORT DataLen;
     UCHAR  Number;
     char   Name[16];
       }NBT_NAME_ANSWER;
       //  NetBIOS 名字查询字符串
       unsigned char NetBiosQuery[] = 
          "/x20/x43/x4B/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41"
          "/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41/x41"
          "/x41/x00/x00/x21/x00/x01";
       int GET_NetBiosName(char * IpAddress, char * szNetBiosName, int timeout)
       {
          SOCKET             csock;
          NBT_NAME_HEADER    NbtNameHeader;
          NBT_NAME_ANSWER    NbtNameAnswer;
          struct sockaddr_in dest, from;
          int                fromlen = sizeof(from);
          int                iRet,iTimeout;
          char *             SendBuf;
          csock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
          if(csock == SOCKET_ERROR)return NULL;
          //  建立UDP端口,查询一台计算机的NetBIOS名字要通过UDP的137端口查询
          
          iTimeout = timeout;
          iRet = setsockopt(csock,SOL_SOCKET,SO_SNDTIMEO,(char*)&iTimeout,sizeof(iTimeout));
          if(csock == SOCKET_ERROR)
          {
                closesocket(csock);
                return 1;
          }
          iTimeout = timeout;
          iRet = setsockopt(csock,SOL_SOCKET,SO_RCVTIMEO,(char*)&iTimeout,sizeof(iTimeout));
          if(csock == SOCKET_ERROR)
          {
                closesocket(csock);
                return 1;
          }
          //  建立超时
          
          dest.sin_family      = AF_INET;
          dest.sin_port        = htons(137);
          dest.sin_addr.s_addr = inet_addr(IpAddress);
          //  填充sockaddr_in结构
           
          memset(&NbtNameHeader, 0, sizeof(NBT_NAME_HEADER));
          NbtNameHeader.ID         = htons(0x01F8);
          NbtNameHeader.Flags      = htons(0x0010);
          NbtNameHeader.Questions  = htons(0x0001);
          NbtNameHeader.Answer     = 0;
          NbtNameHeader.Additional = 0;
          NbtNameHeader.Authority  = 0;
          //  填充NBT_NAME_HEADER结构
           
          SendBuf = xmalloc(sizeof(NBT_NAME_HEADER) + sizeof(NetBiosQuery));
          if(!SendBuf)
          {
                closesocket(csock);
                return 1;
          }
          memcpy(SendBuf, &NbtNameHeader, sizeof(NBT_NAME_HEADER));
          memcpy(SendBuf+sizeof(NBT_NAME_HEADER), NetBiosQuery, sizeof(NetBiosQuery));
          //  建立发送缓冲区,复制NBT_NAME_HEADER结构和NetBiosQuery到发送缓冲区
          
          iRet = sendto(csock, SendBuf, sizeof(NBT_NAME_HEADER) + sizeof(NetBiosQuery)-1, 0, (const sockaddr *)&dest, sizeof(dest));
          if(iRet <= 0)
          {
                closesocket(csock);
                xfree(SendBuf);
                return 1;
          }
          //  发送数据
          
          memset(&from, 0, sizeof(from));
          memset(&NbtNameAnswer, 0, sizeof(NBT_NAME_ANSWER));
          xfree(SendBuf);
          SendBuf = xmalloc(1024);
          if(!SendBuf)
          {
                closesocket(csock);
                return 1;
          }
          iRet = recvfrom(csock, SendBuf, 1024, 0, (sockaddr *)&from, &fromlen);
          if(iRet < (sizeof(NBT_NAME_ANSWER) + sizeof(NBT_NAME_HEADER)))
          {
                closesocket(csock);
                xfree(SendBuf);
                return 1;
          }
          //  接收数据
          
          memcpy(&NbtNameAnswer, SendBuf + sizeof(NBT_NAME_HEADER), sizeof(NBT_NAME_ANSWER));
          //  复制接收的数据到NBT_NAME_ANSWER结构
          strncpy(szNetBiosName, NbtNameAnswer.Name, sizeof(NbtNameAnswer.Name));
          //  复制接受到的NetBIOS名字到变量szNetBiosName
          
          closesocket(csock);
          xfree(SendBuf);
          return 0;
       }
       
     与一台计算机建立空连接一般需要下面这些步骤:
       
       1 - 客户端发送已经编码的服务器的NetBIOS名字,如果服务端确认,发送经确认NetBIOS数据
    报给客户端,客户端在确认NetBIOS名字后才能继续用SMB协议与服务端交互。
           NetBIOS 名字在Windows系统下总是大写的,长度为16字节,它编码后为32字节,是这样编码的:
           例如名字PPSP,不足16字节后面会补充空格,16进制为0x50 0x50 0x53 0x50 0x20 0x20...,系统
       把每一个字节分为4位一组,变换后成为 0x5 0x0 0x5 0x0 0x5 0x3 0x5 0x0 0x2 0x0 0x2 0x0...,然后
       把每一组数据都加上 ACSII码'A'的值(0x41),最后,NetBIOS名字被编码为32字节。实际中的编码后的缓冲区向下面这样:  
       00000030                                   46 41 46 41 46       ...D.FAFAF
       00000040  44 46 41 43 41 43 41 43 41 43 41 43 41 43 41 43 DFACACACACACACAC
       00000050  41 43 41 43 41 43 41 43 41 43 41 00 20 46 45 45 ACACACACACA..FEE
       00000060  46 46 44 46 45 43 41 43 41 43 41 43 41 43 41 43 FFDFECACACACACAC
       00000070  41 43 41 43 41 43 41 43 41 43 41 41 41 00       ACACACACACAAA.  

  • 相关阅读:
    Solidworks如何设置零件材料,如何评估零件质量
    Office 如何双面打印Word文档
    Discuz常见小问题2-如何在新建的页面上只显示一部分板块
    Discuz常见小问题2-如何在数据库搜索指定关键字
    Discuz常见小问题2-如何修改整个网站的默认字体为微软雅黑
    Discuz常见小问题2-如何修改管理员密码,修改admin账户密码
    php使用include报错require_once(../include.php): failed to open stream: No such file or directo
    PHP中的__get()和__set()方法获取设置私有属性
    php->是什么意思
    PHP中require(),include(),require_once()和include_once()有什么区别
  • 原文地址:https://www.cnblogs.com/dolphi/p/2651344.html
Copyright © 2011-2022 走看看