Samba 源码解析之SMBclient命令流
阅读原文时间:2023年07月11日阅读:1

smbclient提供了类似FTP式的共享文件操作功能, 本篇从源码角度讲解smbclient的实现,smbclient命令的具体使用可通过help命令和互联网查到大量资料。

以下从源码角度分析一个smbclient命令是如何发到远端机器上和处理返回结果的。这里以一个简单的命令“close ”为例,分析程序的整个过程如下:

step 1. cmd_close(void)位于source3/client/client.c中。每个smbclient命令都有一个类似cmd_***命名的函数,这些函数作用是为step 2中的cli_***函数准备参数。

- 从全局内存stackframe获取临时内存“停靠点”。该步主要使用talloc库并配合samba自身的需要完成内存的管理。具体可参见上一篇博文的talloc加深理解。

- 分析传入的, 简单调用atoi转换为字符串类型。

- 调用cli_close()。

Step 2. cli_close(cli, fnum)位于source3/client/client.c中。 它接收cmd_***传递的函数,做实质性的工作。这里第一个参数cli是cli_state全局类型数据,该数据结构中几乎包含了当前连接的connection的绝大多数信息,例如:

cli_state的前驱和后继、当前connection 信息、客户domain名、用户名、server domain、os、posix 能力、打开的管道list、机会锁信息、使用smb1 or smb2?及其相关联的session、tree connection、打开的句柄。第二

个参数是上面准备的打开的句柄。不同的cli_***command可能需要另外的参数,这些参数主要是为下层协议具体command准备的。可参见CIFS or SMB2协议来确定每个命令具体需要的参数有哪些。

- 根据cli->connection->protocol类型判断当前connection所使用的协议类型,若为SMB2,则调用SMB2处理函数cli_smb2_close_fnum(位于source3/libsmb/cli_smb2_fnum.c中)。这里重点分析SMB1处理。

- 若当前connection使用smb1,则根据当前connection->的event等待队列来判断是否有outgoing或pending的message,如果有则出错返回。(因为这里我们使用的都是同步的smb1 message)

- 调用samba_tevent_context_init分配tevent

- 调用cli_close_send()发送close immediate CIFS message。后续具体分析这个函数。

- 调用tevent_req_poll()等待event接收事件

- 调用cli_close_recv()接收server返回的close 数据报并返回

上面蓝色标记部分为IO处理的tevent库使用,会另文介绍。本文继续深入分析红色部分的协议处理部分,两个红色标记函数完成了协议组包、协议发送和协议接收处理。

cli_close_send():

Step 1: 调用cli_close_create(),根据协议组包并设置event类型和callback函数

- 调用tevent_req_create()创建一个immediate类型event,并设置状态为TEVENT_REQ_IN_PROGRESS状态;

- 调用cli_smb_req_create(TALLOC_CTX, tevent_context, cli_state, smb_command, additinal_flags, wct, *vwv, iov_count, iovec*),然后又向下调用smb1cli_req_create()(位于libcli/smb/smbXcli_base.c中)完成

按协议的具体组包工作。组包时需要注意,SMB1在组包时并没有定义每个CIFS协议的完整数据包格式,只是定义了header(加上wct)字段,发送是通过控制iov和iov_count来进行的,具体看下面code:

smb1cli\_req\_flags(conn->protocol,  
          conn->smb1.capabilities,  
          smb\_command,  
          additional\_flags,  
          clear\_flags,  
          &flags,  
          additional\_flags2,  
          clear\_flags2,  
          &flags2);  //获取当前命令所需的flags  
    //设置CIFS header的各个域,完成大小端转换  
SIVAL(state->smb1.hdr, 0,           SMB\_MAGIC);  
SCVAL(state->smb1.hdr, HDR\_COM,     smb\_command);  
SIVAL(state->smb1.hdr, HDR\_RCLS,    NT\_STATUS\_V(NT\_STATUS\_OK));  
SCVAL(state->smb1.hdr, HDR\_FLG,     flags);  
SSVAL(state->smb1.hdr, HDR\_FLG2,    flags2);  
SSVAL(state->smb1.hdr, HDR\_PIDHIGH, pid >> 16);  
SSVAL(state->smb1.hdr, HDR\_TID,     tid);  
SSVAL(state->smb1.hdr, HDR\_PID,     pid);  
SSVAL(state->smb1.hdr, HDR\_UID,     uid);  
SSVAL(state->smb1.hdr, HDR\_MID,     0); /\* this comes later \*/  
SCVAL(state->smb1.hdr, HDR\_WCT,     wct);

state->smb1.vwv = vwv;// ???

    //计算bcc字段  
SSVAL(state->smb1.bytecount\_buf, 0, smbXcli\_iov\_len(bytes\_iov, iov\_count));

    //以下为每个SMB1 command都包含的域  
state->smb1.iov\[0\].iov\_base = (void \*)state->length\_hdr; //“SMB”  
state->smb1.iov\[0\].iov\_len  = sizeof(state->length\_hdr);  
state->smb1.iov\[1\].iov\_base = (void \*)state->smb1.hdr; //header域  
state->smb1.iov\[1\].iov\_len  = sizeof(state->smb1.hdr);  
state->smb1.iov\[2\].iov\_base = (void \*)state->smb1.vwv; //wct字段具体包含的数据  
state->smb1.iov\[2\].iov\_len  = wct \* sizeof(uint16\_t);  
state->smb1.iov\[3\].iov\_base = (void \*)state->smb1.bytecount\_buf;//bcc域  
state->smb1.iov\[3\].iov\_len  = sizeof(uint16\_t);

    //特殊smb1 command若还有其他字段则通过iov\[4\]进行发送  
if (iov\_count != 0) {  
    memcpy(&state->smb1.iov\[4\], bytes\_iov,  
           iov\_count \* sizeof(\*bytes\_iov));  
}  
state->smb1.iov\_count = iov\_count + 4;

- 组包结束后调用tevent_req_set_callbak()设置该event的callback函数为cli_close_done(),在该函数中调用cli_smb_recv()函数完成smb数据包的接收。注意,此时组包和event相关注册活动均已完成,ready for 发送。

Step 2: 调用smb1cli_req_chain_submit(),该函数完成具体的message发送工作。

cli_close_recv():

- 由于注册的是immediate时间,程序将block在上一步的tevent_req_poll()直到对应event接到通知,并从注册的callback中返回。此时我们已经接收收到了close的返回数据报(callback函数接收处理的),针对close command只需要简单处理返回值即可。其他复杂command可能需要对返回值作进一步的分析,如保存打开的文件句柄、保存treeid等信息。

总结:

其实client端的实现还是相对比较简单,关键点是tevent库的使用和协议的组包分析。tevent库的使用可以通过资料快速掌握,但协议的组包和处理包括大量细节,没必要全部掌握,分析一个简单命令即可。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章