OpenHarmony源码分析之分布式软总线:authmanager模块(2)/设备认证通信管理

一、 概述

authmanager模块是鸿蒙为设备提供认证机制的模块。模块内的主要处理过程包括报文的接收、解密、再次封装、加密、发送的步骤。备注:该版本的鸿蒙仅实现了基于WiFi即局域网的设备身份认证机制。 本文重点介绍在设备间建立起socket连接之后,系统是如何处理接收到的新数据。处理过程主要集中在wifi_auth_manager.c文件中。

二、 源码分析

  1. 当有设备发起连接请求时,首先在trans_service模块建立socket连接,建立连接之后,若有设备发送认证请求的数据,将在函数ProcessDataEvent()中进行处理,对该函数的阅读理解如下:
scss 复制代码
/*
函数功能:处理设备间通信(新数据到达)事件
函数参数:fd    用于通信的套接字fd
函数返回值:无
详细:
*/
void ProcessDataEvent(int fd)
{
    SOFTBUS_PRINT("[AUTH] ProcessDataEvent fd = %d\n", fd);
    AuthConn *conn = FindAuthConnByFd(fd);//通过fd查找认证连接链表中是否已存在该设备
    if (conn == NULL) {
        SOFTBUS_PRINT("ProcessDataEvent get authConn fail\n");
        return;
    }
    //已存在,则进行下一步
    if (conn->db.buf == NULL) {//如果是第一次通信,则初始化数据缓冲区
        conn->db.buf = (char *)malloc(DEFAULT_BUF_SIZE);//申请用于设备间通信的数据缓冲区内存
        if (conn->db.buf == NULL) {
            return;
        }
        (void)memset_s(conn->db.buf, DEFAULT_BUF_SIZE, 0, DEFAULT_BUF_SIZE);//清空数据缓冲区
        conn->db.size = DEFAULT_BUF_SIZE;//默认缓冲区大小1536
        conn->db.used = 0;//缓冲区已使用量为0
    }
    //用局部变量保存数据缓冲区信息,因为参数通过地址传递,防止改变原有地址空间内容
    DataBuffer *db = &conn->db;
    char *buf = db->buf;
    int used = db->used;
    int size = db->size;
    int rc = AuthConnRecv(fd, buf, used, size - used, 0);//接收设备认证过程中传输的数据,预读取size-used大小
    if (rc == 0) {//若没有数据,则返回
        return;
    } else if (rc < 0) {
        CloseConn(conn);
        return;
    }
    used += rc;//更新缓冲区已使用空间
    int processed = ProcessPackets(conn, buf, size, used);//处理身份认证协议数据包
    if (processed > 0) {
        used -= processed;//处理完的数据应从缓冲区移除
        if (used != 0) {
            if (memmove_s(buf, processed, buf, used) != EOK) {//将缓冲区中已占用的部分拷贝到缓冲区起始处
                CloseConn(conn);
                return;
            }
        }
    } else if (processed < 0) {
        CloseConn(conn);
        return;
    }
    db->used = used;
    SOFTBUS_PRINT("[AUTH] ProcessDataEvent ok\n");
}
  1. 下面开始逐步分析该函数的具体内容,首先通过fd查找认证连接链表中是否已存在该设备,若不存在则返回认证连接失败,因为之前在处理新连接时会将该设备加入设备认证连接链表中,具体在函数FindAuthConnByFd()中实现:
ini 复制代码
/*
函数功能:查找该连接的套接字fd是否已在设备链表中
函数参数:
    fd:建立连接的套接字fd
函数返回值:
    若该设备已存在则返回设备连接信息,若不存在则返回NULL
详细:
*/
static AuthConn* FindAuthConnByFd(int fd)
{
    if (g_fdMap == NULL) {
        return NULL;
    }
    AuthConnNode *node = NULL;
    List *pos = NULL;
    List *tmp = NULL;
    LIST_FOR_EACH_SAFE(pos, tmp, g_fdMap) {//遍历g_fdMap链表,该链表只存储认证连接设备节点地址
        node = (AuthConnNode*)pos;
        if (node->aconn == NULL) {
            continue;
        }
        if (node->aconn->fd == fd) {//若该设备已存在,则返回该设备的连接信息
            return node->aconn;
        }
    }
    return NULL;
}
  1. 然后接收设备认证过程中传输的数据,在AuthConnRecv()中实现:
arduino 复制代码
/*
函数功能:接收设备认证过程中传输的数据
函数参数:
    fd:用于TCP通信的套接字fd
    buf:数据缓冲区首地址
    offset:缓冲区数据偏移
    count:预读取的数据量
    timeout:超时时间
函数返回值:
    成功:返回收到的数据大小
    失败:返回-1
详细:
*/
int AuthConnRecv(int fd, char *buf, int offset, int count, int timeout)
{
    if ((buf == NULL) || (offset < 0) || (count <= 0) || (offset + count <= 0)) {//健壮性检查,越界检查
        return -1;
    }
    return TcpRecvData(fd, buf + offset, count, timeout);//传入数据偏移后的地址,用于存储新收到的数据
}
/*
函数功能:接收TCP连接的通信数据,保存到buf缓冲区中
函数参数:
    fd:用于TCP通信的套接字fd;
    buf:用于保存接收数据的数据缓冲区地址;
    len:预读取数据长度;
    timeout:超时时间
函数返回值:返回收到的数据字节数
*/
int TcpRecvData(int fd, char *buf, int len, int timeout)
{
    return TcpRecvMessages(fd, buf, len, timeout, 0);//相当于read函数
}
/*
函数功能:接收TCP连接的通信数据
函数参数:
    fd:用于TCP通信的套接字fd;
    buf:用于保存接收数据的数据缓冲区地址;
    len:预读取数据长度;
    timeout:超时时间;
    flags:用于recv函数的接收数据模式参数
函数返回值:
    成功:返回recv函数实际读到的数据字节数
    失败:返回-1
*/
static int32_t TcpRecvMessages(int fd, char *buf, uint32_t len, int timeout, int flags)
{
    if (fd < 0 || buf == NULL || len == 0 || timeout < 0) {//健壮性检查
        return -1;
    }
    errno = 0;
    int32_t rc = recv(fd, buf, len, flags);//接收对端发送的数据
    if ((rc == -1) && (errno == EAGAIN)) {//表示当前缓冲区没有数据可读
        rc = 0;
    } else if (rc <= 0) {//返回0表明对端已关闭连接
        rc = -1;
    }
    return rc;
}
  1. 收到数据之后,就开始处理身份认证协议数据包,具体在函数ProcessPackets()中实现:
arduino 复制代码
/*
函数功能:处理身份认证协议数据包
函数参数:conn------认证连接信息结构体;buf------数据缓冲区地址;size------数据缓冲区总大小;used------数据缓冲区已使用量
函数返回值:返回已成功处理完的数据量
详细:循环解析收到的每一个数据包,先解析其头部,再解析其数据负载部分。
*/
static int ProcessPackets(AuthConn *conn, const char *buf, int size, int used)
{
    int processed = 0;
    while (processed + PACKET_HEAD_SIZE < used) {//循环解析数据包头部
        Packet *pkt = ParsePacketHead(buf, processed, used - processed, size);//解析包头部
        if (pkt == NULL) {
            SOFTBUS_PRINT("[AUTH] ProcessPackets ParsePacketHead fail\n");
            return -1;
        }
        int len = pkt->dataLen;//获取数据负载部分的长度
        if ((len > PACKET_DATA_SIZE) || (processed + PACKET_HEAD_SIZE + len > used)) {//如果产生越界,跳出循环
            free(pkt);
            pkt = NULL;
            break;
        }
        processed += PACKET_HEAD_SIZE;//将processed偏移一个数据包头部长度,继续处理后面的数据,即更新已处理数据量
        OnDataReceived(conn, pkt, buf + processed);//解析数据负载部分
        processed += len;//将processed偏移一个数据包负载部分的长度,继续处理后面的数据,即更新已处理数据量
        free(pkt);
        pkt = NULL;
    }
    return processed;
}

DD一下: 欢迎大家关注公众号<程序猿百晓生>,可以了解到一下知识点。

erlang 复制代码
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案) 
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......
  1. 在处理认证数据包过程中,首先在函数ParsePacketHead()中解析数据包头部:
arduino 复制代码
/*
函数功能:解析身份认证协议数据包头部,头部长度为24字节,共五个字段
函数参数:buf------接收数据缓冲区首地址;offset------认证协议各字段偏移量;len------待处理的数据长度;size------接收数据缓冲区大小
函数返回值:解析成功则返回包含数据包头部信息的结构体地址
详细:
*/
static Packet *ParsePacketHead(const char *buf, int offset, int len, int size)
{
    if ((buf == NULL) || (offset < 0) || (len < PACKET_HEAD_SIZE) ||
        (offset + len <= 0) || (offset + len > size)) {
        return NULL;
    }
    unsigned int identifier = GetIntFromBuf(buf, offset);//获取标识符字段
    if (identifier != PKG_HEADER_IDENTIFIER) {//如果标识符不是PKG_HEADER_IDENTIFIER,幻数,则输出错误
        SOFTBUS_PRINT("[AUTH] ParsePacketHead invalid magic number\n");
        return NULL;
    }
    offset += DEFAULT_INT_LEN;
    int module = GetIntFromBuf(buf, offset);//获取module字段
    offset += DEFAULT_INT_LEN;
    long long seq = 0;
    if (GetLongFromBuf(buf, offset, &seq) != 0) {//获取seq字段
        return NULL;
    }
    offset += DEFAULT_LONG_LEN;
    int flags = GetIntFromBuf(buf, offset);//获取flags字段
    offset += DEFAULT_INT_LEN;
    int dataLen = GetIntFromBuf(buf, offset);//获取dataLen字段
    SOFTBUS_PRINT("[AUTH] ParsePacketHead module=%d, seq=%lld, flags=%d, datalen=%d\n", module, seq, flags, dataLen);
    if (module < 0 || flags < 0 || dataLen < 0) {
        return NULL;
    }
    Packet *packet = (Packet *)malloc(sizeof(Packet));//申请认证协议数据包头部所需空间
    if (packet == NULL) {
        return NULL;
    }
    //将解析出来的数据包头部字段保存在packet结构体中
    packet->module = module;
    packet->seq = seq;
    packet->flags = flags;
    packet->dataLen = dataLen;
    return packet;
}
  1. 然后在函数OnDataReceived()中解析数据包负载部分:
rust 复制代码
/*
函数功能:处理接收到的认证协议数据包数据负载部分
函数参数:conn------认证设备信息结构体;pkt------认证协议数据包头部结构体的地址;data------数据负载部分的起始地址
函数返回值:无
详细:
*/
static void OnDataReceived(AuthConn *conn, const Packet *pkt, const char *data)
{
    SOFTBUS_PRINT("[AUTH] OnDataReceived\n");
    if ((pkt->module > MODULE_HICHAIN) && (pkt->module <= MODULE_AUTH_SDK)) {//如果module字段为MODULE_AUTH_SDK,则调用AuthInterfaceOnDataReceived继续进行处理。这里没有直接用==,是为了可扩展性
        AuthInterfaceOnDataReceived(conn, pkt->module, pkt->seq, data, pkt->dataLen);//若数据包类型为MODULE_AUTH_SDK,表示对端请求创建设备身份认证环境,即该设备未进行身份认证
        return;
    }
    //如果类型不在MODULE_HICHAIN与MODULE_AUTH_SDK之间,则进行解密处理
    cJSON *msg = DecryptMessage(pkt->module, data, pkt->dataLen);//解密消息,返回cJSON格式的数据
    if (msg == NULL) {
        SOFTBUS_PRINT("[AUTH] OnDataReceived DecryptMessage fail\n");
        return;
    }
    OnModuleMessageReceived(conn, pkt->module, pkt->flags, pkt->seq, msg);//根据数据包类型字段module,对接收到的cJSON数据选择不同的处理方式
    cJSON_Delete(msg);
    msg = NULL;
}

至此,设备间身份认证的通信管理过程结束,至于具体的身份认证协议过程,由于篇幅有限,将在下一篇文章中进行详细叙述。

相关推荐
大聪明-PLUS5 小时前
在 C++ 中开发接口类
linux·嵌入式·arm·smarc
xiaocao_10237 小时前
鸿蒙手机上使用的备忘录怎么导出来?
华为·智能手机·harmonyos
s1ckrain7 小时前
数字逻辑笔记—组合逻辑电路
笔记·fpga开发·嵌入式
春卷同学9 小时前
打砖块 - Electron for 鸿蒙PC项目实战案例
android·electron·harmonyos
春卷同学12 小时前
Electron for鸿蒙PC开发的骰子游戏应用
游戏·electron·harmonyos
春卷同学12 小时前
Electron for 鸿蒙pc开发的二十一点游戏
游戏·electron·harmonyos
挽天java12 小时前
智能终端开发文档
嵌入式
不老刘13 小时前
react native for OpenHarmony iconfont 图标不显示问题
react native·harmonyos·iconfont
汉堡黄•᷄ࡇ•᷅13 小时前
鸿蒙开发: 案例集合List:ListItem侧滑(删除、收藏)
harmonyos·鸿蒙·鸿蒙系统
brave and determined13 小时前
传感器学习(day02)毫米波雷达:重塑人机交互新维度
单片机·嵌入式硬件·嵌入式·人机交互·传感器·毫米波·嵌入式设计