TigerVNC:C++源码分析RFB协议客户端与服务器连接过程

前言

这部分的内容就不做过多的介绍,关于RFB协议的简介能从书上,网上或者其他博客了解到很多。相信大家更加感兴趣的还是RFB协议里面客户端与服务器连接过程。
现在就通过分析远程工具TigerVNC中RFB协议C++源码,了解其客户端与服务器的连接。
客户端源码主要分布在CConnection.cxx,CMsgReader.cxx,CMsgWriter.cxx
服务端源码主要分布在SConnection.cxx,SMsgReader.cxx,SMsgWriter.cxx
协议主要分为三个阶段,下面依次介绍:

第一阶段:版本号认证

客户端: 进入等待服务端发送支持的协议版本号过来的状态

void CConnection::initialiseProtocol()
{
    
    
  state_ = RFBSTATE_PROTOCOL_VERSION;
}

服务端: 发送支持的版本号 cp.writeVersion(os),随后等待客户端发送客户端使用的版本号

void SConnection::initialiseProtocol()
{
    
    
  cp.writeVersion(os);
  state_ = RFBSTATE_PROTOCOL_VERSION;
}

客户端: 接收服务端支持版本号cp.readVersion(is, &done),然后发送客户端使用的版本给服务端cp.writeVersion(os)

  vlog.debug("reading protocol version");
  bool done;
  if (!cp.readVersion(is, &done)) {
    
    
    state_ = RFBSTATE_INVALID;
    throw Exception("reading version failed: not an RFB server?");
  }
  if (!done) return;

  vlog.info("Server supports RFB protocol version %d.%d",
            cp.majorVersion, cp.minorVersion);

  // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
  if (cp.beforeVersion(3,3)) {
    
    
    vlog.error("Server gave unsupported RFB protocol version %d.%d",
               cp.majorVersion, cp.minorVersion);
    state_ = RFBSTATE_INVALID;
    throw Exception("Server gave unsupported RFB protocol version %d.%d",
                    cp.majorVersion, cp.minorVersion);
  } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
    
    
    cp.setVersion(3,3);
  } else if (cp.afterVersion(3,8)) {
    
    
    cp.setVersion(3,8);
  }

  cp.writeVersion(os);
  state_ = RFBSTATE_SECURITY_TYPES;

  vlog.info("Using RFB protocol version %d.%d",
            cp.majorVersion, cp.minorVersion);

服务端: 获取客户端使用的版本号cp.readVersion(is, &done),确认能否支持

  vlog.debug("reading protocol version");
  bool done;
  if (!cp.readVersion(is, &done)) {
    
    
    state_ = RFBSTATE_INVALID;
    throw Exception("reading version failed: not an RFB client?");
  }
  if (!done) return;

  vlog.info("Client needs protocol version %d.%d",
            cp.majorVersion, cp.minorVersion);

  if (cp.majorVersion != 3) {
    
    
    // unknown protocol version
    char msg[256];
    sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
            cp.majorVersion, cp.minorVersion,
            defaultMajorVersion, defaultMinorVersion);
    throwConnFailedException(msg);
  }

  if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
    
    
    vlog.error("Client uses unofficial protocol version %d.%d",
               cp.majorVersion,cp.minorVersion);
    if (cp.minorVersion >= 8)
      cp.minorVersion = 8;
    else if (cp.minorVersion == 7)
      cp.minorVersion = 7;
    else
      cp.minorVersion = 3;
    vlog.error("Assuming compatibility with version %d.%d",
               cp.majorVersion,cp.minorVersion);
  }

  versionReceived();

第二阶段:安全认证

服务端: 服务端确认能够支持客户端使用版本后,向客户端发送能支持的安全类型数量os->writeU8(secTypes.size())和种类os->writeU8(*i)

  std::list<rdr::U8> secTypes;
  std::list<rdr::U8>::iterator i;
  secTypes = security->GetEnabledSecTypes();

  // list supported security types for >=3.7 clients

  if (secTypes.empty())
    throwConnFailedException("No supported security types");

  os->writeU8(secTypes.size());
  for (i=secTypes.begin(); i!=secTypes.end(); i++)
    os->writeU8(*i);
  os->flush();
  state_ = RFBSTATE_SECURITY_TYPE;

客户端: 获取服务端能够支持的安全类型数量 is->readU8()和种类 rdr::U8 serverSecType = is->readU8()

    int nServerSecTypes = is->readU8();
    if (nServerSecTypes == 0)
      throwConnFailedException();

    std::list<rdr::U8>::iterator j;

    for (int i = 0; i < nServerSecTypes; i++) {
    
    
      rdr::U8 serverSecType = is->readU8();
      vlog.debug("Server offers security type %s(%d)",
                 secTypeName(serverSecType), serverSecType);

      /*
       * Use the first type sent by server which matches client's type.
       * It means server's order specifies priority.
       */
      if (secType == secTypeInvalid) {
    
    
        for (j = secTypes.begin(); j != secTypes.end(); j++)
          if (*j == serverSecType) {
    
    
            secType = *j;
            break;
          }
       }
    }

客户端: 选择一个使用的安全类型发送给服务端 os->writeU8(secType)

    // Inform the server of our decision
    if (secType != secTypeInvalid) {
    
    
      os->writeU8(secType);
      os->flush();
      vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
    }

服务端: 接收客户端选择的安全类型 is->readU8(),随后验证合理性processSecurityType(int secType) => processSecurityMsg() => queryConnection(const char* userName) => approveConnection(bool accept, const char* reason) 。成功调用os->writeU32(secResultOK),失败调用os->writeU32(secResultFailed)并传送错误说明os->writeString(reason)。随后服务端进入状态RFBSTATE_INITIALISATION,等待客户端传客户端初始化信息过来。
备注:其他远程工具如UltraVNC还有Challenge等过程,例如有一种为VNC安全认证,当用VNC认证的时候,协议数据采用明文发送,服务器发送一个16字节的随机数验证给客户端,客户端用DES对验证进行加密,用用户密码作为密钥回复给服务器16字节,这时服务器会返回安全结果给客户端。如果成功就进入初始化报文阶段。不成功就关闭连接。

void SConnection::processSecurityTypeMsg()
{
    
    
  vlog.debug("processing security type message");
  int secType = is->readU8();

  processSecurityType(secType);
}

void SConnection::processSecurityType(int secType)
{
    
    
  // Verify that the requested security type should be offered
  std::list<rdr::U8> secTypes;
  std::list<rdr::U8>::iterator i;

  secTypes = security->GetEnabledSecTypes();
  for (i=secTypes.begin(); i!=secTypes.end(); i++)
    if (*i == secType) break;
  if (i == secTypes.end())
    throw Exception("Requested security type not available");

  vlog.info("Client requests security type %s(%d)",
            secTypeName(secType),secType);

  try {
    
    
    state_ = RFBSTATE_SECURITY;
    ssecurity = security->GetSSecurity(secType);
  } catch (rdr::Exception& e) {
    
    
    throwConnFailedException(e.str());
  }

  processSecurityMsg();
}

void SConnection::processSecurityMsg()
{
    
    
  vlog.debug("processing security message");
  try {
    
    
    bool done = ssecurity->processMsg(this);
    if (done) {
    
    
      state_ = RFBSTATE_QUERYING;
      queryConnection(ssecurity->getUserName());
      setAccessRights(ssecurity->getAccessRights());
    }
  } catch (AuthFailureException& e) {
    
    
    vlog.error("AuthFailureException: %s", e.str());
    os->writeU32(secResultFailed);
    if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
      os->writeString(e.str());
    os->flush();
    throw;
  }
}

void SConnection::queryConnection(const char* userName)
{
    
    
  approveConnection(true);
}

void SConnection::approveConnection(bool accept, const char* reason)
{
    
    
  if (state_ != RFBSTATE_QUERYING)
    throw Exception("SConnection::approveConnection: invalid state");

  if (!reason) reason = "Authentication failure";

  if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
    
    
    if (accept) {
    
    
      os->writeU32(secResultOK);
    } else {
    
    
      os->writeU32(secResultFailed);
      if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
        os->writeString(reason);
    }
    os->flush();
  }

  if (accept) {
    
    
    state_ = RFBSTATE_INITIALISATION;
    reader_ = new SMsgReader(this, is);
    writer_ = new SMsgWriter(&cp, os);
    authSuccess();
  } else {
    
    
    state_ = RFBSTATE_INVALID;
    throw AuthFailureException(reason);
  }
}

客户端: 获取服务端验证结果 is->readU32(),通过则继续调用 securityCompleted() 。失败则报出异常,获取错误说明 reason.buf = is->readString()

void CConnection::processSecurityResultMsg()
{
    
    
  vlog.debug("processing security result message");
  int result;
  if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
    
    
    result = secResultOK;
  } else {
    
    
    if (!is->checkNoWait(1)) return;
    result = is->readU32();
  }
  switch (result) {
    
    
  case secResultOK:
    securityCompleted();
    return;
  case secResultFailed:
    vlog.debug("auth failed");
    break;
  case secResultTooMany:
    vlog.debug("auth failed - too many tries");
    break;
  default:
    throw Exception("Unknown security result from server");
  }
  CharArray reason;
  if (cp.beforeVersion(3,8))
    reason.buf = strDup("Authentication failure");
  else
    reason.buf = is->readString();
  state_ = RFBSTATE_INVALID;
  throw AuthFailureException(reason.buf);
}

第三阶段:客户端和服务端互送初始化信息

客户端: 安全认证通过后,传送初始化信息给服务端 writer_->writeClientInit(shared),是否接受共享(shared),若不接受,则服务端只远程这一个客户端。同时进入状态RFBSTATE_INITIALISATION,等待服务端传初始化信息过来

void CConnection::securityCompleted()
{
    
    
  state_ = RFBSTATE_INITIALISATION;
  reader_ = new CMsgReader(this, is);
  writer_ = new CMsgWriter(&cp, os);
  vlog.debug("Authentication success!");
  authSuccess();
  writer_->writeClientInit(shared);
}

void CMsgWriter::writeClientInit(bool shared)
{
    
    
  os->writeU8(shared);
  endMsg();
}

服务端: 获取客户端初始化信息reader_->readClientInit() ,流程为processInitMsg() => readClientInit() => clientInit(bool shared)

void SConnection::processInitMsg()
{
    
    
  vlog.debug("reading client initialisation");
  reader_->readClientInit();
}

void SMsgReader::readClientInit()
{
    
    
  bool shared = is->readU8();
  handler->clientInit(shared);
}

void SConnection::clientInit(bool shared)
{
    
    
  writer_->writeServerInit();
  state_ = RFBSTATE_NORMAL;
}

服务端: 发送服务端初始化信息到客户端,然后进入RFBSTATE_NORMAL状态,可与客户端互发数据 reader_->readMsg();

void SConnection::clientInit(bool shared)
{
    
    
  writer_->writeServerInit();
  state_ = RFBSTATE_NORMAL;
}

void SMsgWriter::writeServerInit()
{
    
    
  os->writeU16(cp->width);
  os->writeU16(cp->height);
  cp->pf().write(os);
  os->writeString(cp->name());
  endMsg();
}

客户端: 获取服务端初始化信息,processInitMsg() => readServerInit() => serverInit() ,然后进入RFBSTATE_NORMAL状态。连接完毕,可与服务端互发数据 reader_->readMsg();

void CConnection::processInitMsg()
{
    
    
  vlog.debug("reading server initialisation");
  reader_->readServerInit();
}

void CMsgReader::readServerInit()
{
    
    
  int width = is->readU16();
  int height = is->readU16();
  handler->setDesktopSize(width, height);
  PixelFormat pf;
  pf.read(is);
  handler->setPixelFormat(pf);
  CharArray name(is->readString());
  handler->setName(name.buf);
  handler->serverInit();
}

void CConnection::serverInit()
{
    
    
  state_ = RFBSTATE_NORMAL;
  vlog.debug("initialisation done");
}

猜你喜欢

转载自blog.csdn.net/qq_33594636/article/details/128671129