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.  

  • 相关阅读:
    白天写代码,晚上摆地摊!9年前摆地摊学会了这些道理...
    啪啪,打脸了!领导说:try-catch必须放在循环体外!
    阿里巴巴为什么让初始化集合时必须指定大小?
    永远不要使用双花括号初始化实例,否则就会OOM!
    限流的6大狠招,附详细代码和评测结果
    HashMap 的 7 种遍历方式与性能分析!(强烈推荐)
    String性能提升10倍的几个方法!(源码+原理分析)
    9个小技巧让你的 if else看起来更优雅
    用了自定义Banner后,SpringBoot瞬间变的高大上了...
    别再问我 new 字符串创建了几个对象了!我来证明给你看!
  • 原文地址:https://www.cnblogs.com/dolphi/p/2651344.html
Copyright © 2011-2022 走看看