DtlsSocketソースコード分析

 

// 通过mSocketContext拿到设置了证书和私钥的ssl Context,创建ssl对象,分配输入和输出Bio
DtlsSocket::DtlsSocket(DtlsSocketContext* socketContext, enum SocketType type):
              mSocketContext(socketContext),
              mSocketType(type),
              mHandshakeCompleted(false) {
  ELOG_DEBUG("Creating Dtls Socket");
  mSocketContext->setDtlsSocket(this);
  SSL_CTX* mContext = mSocketContext->getSSLContext();
  assert(mContext);
  mSsl = SSL_new(mContext);
  assert(mSsl != 0);
  SSL_set_mtu(mSsl, DTLS_MTU);
  mSsl->ctx = mContext;
  mSsl->session_ctx = mContext;

  switch (type) {
    case Client:
      SSL_set_connect_state(mSsl);
      // SSL_set_mode(mSsl, SSL_MODE_ENABLE_PARTIAL_WRITE |
      //         SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
      break;
    case Server:
      SSL_set_accept_state(mSsl);
      SSL_set_verify(mSsl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, dummy_cb);
      break;
    default:
      assert(0);
  }
  BIO* memBIO1 = BIO_new(BIO_s_mem());
  mInBio = BIO_new(BIO_f_dwrap());
  BIO_push(mInBio, memBIO1);

  BIO* memBIO2 = BIO_new(BIO_s_mem());
  mOutBio = BIO_new(BIO_f_dwrap());
  BIO_push(mOutBio, memBIO2);

  SSL_set_bio(mSsl, mInBio, mOutBio);
  SSL_accept(mSsl);
  ELOG_DEBUG("Dtls Socket created");
}

// 析构函数,关闭ssl对象
DtlsSocket::~DtlsSocket() {
  close();
}

// 释放ssl对象
void DtlsSocket::close() {
  // Properly shutdown the socket and free it - note: this also free's the BIO's
  if (mSsl != NULL) {
    ELOG_DEBUG("SSL Shutdown");
    SSL_shutdown(mSsl);
    SSL_free(mSsl);
    mSsl = NULL;
  }
}

// 开启ssl客户端握手操作
void DtlsSocket::startClient() {
  assert(mSocketType == Client);
  doHandshakeIteration();
}

// 处理包
bool DtlsSocket::handlePacketMaybe(const unsigned char* bytes, unsigned int len) {
  if (mSsl == NULL) {
    ELOG_WARN("handlePacketMaybe called after DtlsSocket closed: %p", this);
    return false;
  }
  DtlsSocketContext::PacketType pType = DtlsSocketContext::demuxPacket(bytes, len);

  if (pType != DtlsSocketContext::dtls) {
    return false;
  }

  if (mSsl == nullptr) {
    return false;
  }

  (void) BIO_reset(mInBio);
  (void) BIO_reset(mOutBio);

  int r = BIO_write(mInBio, bytes, len);
  assert(r == static_cast<int>(len));  // Can't happen

  // Note: we must catch any below exceptions--if there are any
  try {
    doHandshakeIteration();
  } catch (int e) {
    return false;
  }
  return true;
}

// 强制重传
void DtlsSocket::forceRetransmit() {
  (void) BIO_reset(mInBio);
  (void) BIO_reset(mOutBio);
  BIO_ctrl(mInBio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, 0);

  doHandshakeIteration();
}

// DTLS ssl握手操作,握手完成拿到协商后 srtp的秘钥
void DtlsSocket::doHandshakeIteration() {
  boost::mutex::scoped_lock lock(handshakeMutex_);
  char errbuf[1024];
  int sslerr;

  if (mHandshakeCompleted)
  return;

  int r = SSL_do_handshake(mSsl);
  errbuf[0] = 0;
  ERR_error_string_n(ERR_peek_error(), errbuf, sizeof(errbuf));

  // See what was written
  unsigned char *outBioData;
  int outBioLen = BIO_get_mem_data(mOutBio, &outBioData);
  if (outBioLen > DTLS_MTU) {
    ELOG_WARN("message: BIO data bigger than MTU - packet could be lost, outBioLen %u, MTU %u",
        outBioLen, DTLS_MTU);
  }

  // Now handle handshake errors */
  switch (sslerr = SSL_get_error(mSsl, r)) {
    case SSL_ERROR_NONE:
      mHandshakeCompleted = true;
      mSocketContext->handshakeCompleted();
      break;
    case SSL_ERROR_WANT_READ:
      break;
    default:
      ELOG_ERROR("SSL error %d", sslerr);

      mSocketContext->handshakeFailed(errbuf);
      // Note: need to fall through to propagate alerts, if any
      break;
  }

  // If mOutBio is now nonzero-length, then we need to write the
  // data to the network. TODO(pedro): warning, MTU issues!
  if (outBioLen) {
    mSocketContext->write(outBioData, outBioLen);
  }
}

// 拿到远端认证指纹
bool DtlsSocket::getRemoteFingerprint(char *fprint) {
  X509* x = SSL_get_peer_certificate(mSsl);
  if (!x) {  // No certificate
    return false;
  }

  computeFingerprint(x, fprint);
  X509_free(x);
  return true;
}

// 将传入指纹与远端指纹进行对比验证
bool DtlsSocket::checkFingerprint(const char* fingerprint, unsigned int len) {
  char fprint[100];

  if (getRemoteFingerprint(fprint) == false) {
    return false;
  }

  // used to be strncasecmp
  if (strncmp(fprint, fingerprint, len)) {
    ELOG_WARN("Fingerprint mismatch, got %s expecting %s", fprint, fingerprint);
    return false;
  }

  return true;
}

// 获取本地指纹信息
void DtlsSocket::getMyCertFingerprint(char *fingerprint) {
  mSocketContext->getMyCertFingerprint(fingerprint);
}

// 获取srtp的秘钥信息
SrtpSessionKeys* DtlsSocket::getSrtpSessionKeys() {
  // TODO(pedro): probably an exception candidate
  assert(mHandshakeCompleted);

  SrtpSessionKeys* keys = new SrtpSessionKeys();

  unsigned char material[SRTP_MASTER_KEY_LEN << 1];
  if (!SSL_export_keying_material(mSsl, material, sizeof(material), "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
    return keys;
  }

  size_t offset = 0;

  memcpy(keys->clientMasterKey, &material[offset], SRTP_MASTER_KEY_KEY_LEN);
  offset += SRTP_MASTER_KEY_KEY_LEN;
  memcpy(keys->serverMasterKey, &material[offset], SRTP_MASTER_KEY_KEY_LEN);
  offset += SRTP_MASTER_KEY_KEY_LEN;
  memcpy(keys->clientMasterSalt, &material[offset], SRTP_MASTER_KEY_SALT_LEN);
  offset += SRTP_MASTER_KEY_SALT_LEN;
  memcpy(keys->serverMasterSalt, &material[offset], SRTP_MASTER_KEY_SALT_LEN);
  offset += SRTP_MASTER_KEY_SALT_LEN;
  keys->clientMasterKeyLen = SRTP_MASTER_KEY_KEY_LEN;
  keys->serverMasterKeyLen = SRTP_MASTER_KEY_KEY_LEN;
  keys->clientMasterSaltLen = SRTP_MASTER_KEY_SALT_LEN;
  keys->serverMasterSaltLen = SRTP_MASTER_KEY_SALT_LEN;

  return keys;
}

// 获取srtp的profile
SRTP_PROTECTION_PROFILE* DtlsSocket::getSrtpProfile() {
  // TODO(pedro): probably an exception candidate
  assert(mHandshakeCompleted);
  return SSL_get_selected_srtp_profile(mSsl);
}

// Fingerprint is assumed to be long enough
void DtlsSocket::computeFingerprint(X509 *cert, char *fingerprint) {
  unsigned char md[EVP_MAX_MD_SIZE];
  int r;
  unsigned int i, n;

  // r = X509_digest(cert, EVP_sha1(), md, &n);
  r = X509_digest(cert, EVP_sha256(), md, &n);
  // TODO(javier) - is sha1 vs sha256 supposed to come from DTLS handshake?
  // fixing to to SHA-256 for compatibility with current web-rtc implementations
  assert(r == 1);

  for (i = 0; i < n; i++) {
    sprintf(fingerprint, "%02X", md[i]);  // NOLINT
    fingerprint += 2;

    if (i < (n-1))
    *fingerprint++ = ':';
    else
    *fingerprint++ = 0;
  }
}

void DtlsSocket::handleTimeout() {
  (void) BIO_reset(mInBio);
  (void) BIO_reset(mOutBio);
  if (DTLSv1_handle_timeout(mSsl) > 0) {
    ELOG_DEBUG("Dtls timeout occurred!");

    // See what was written
    unsigned char *outBioData;
    int outBioLen = BIO_get_mem_data(mOutBio, &outBioData);
    if (outBioLen > DTLS_MTU) {
      ELOG_WARN("message: BIO data bigger than MTU - packet could be lost, outBioLen %u, MTU %u",
          outBioLen, DTLS_MTU);
    }

    // If mOutBio is now nonzero-length, then we need to write the
    // data to the network. TODO(pedro): warning, MTU issues!
    if (outBioLen) {
      mSocketContext->write(outBioData, outBioLen);
    }
  }
}

 

おすすめ

転載: blog.csdn.net/tong5956/article/details/108362879