> 文档中心 > 软总线源码分析7:认证模块

软总线源码分析7:认证模块

根据上一章节对发现模块的分析,我们知道当发现了外部设备后,InnerDeviceFound会被调用,InnerDeviceFound函数会根据infoNode是否为内部节点,调用回调函数。

回想第4章节我们介绍组网模块的时候,组网会调用EnableCoapDisc函数将本地服务发布后,主动发现外部服务,EnableCoapDisc的源码如下:

static int32_t EnableCoapDisc(void){    LOG_INFO("EnableCoapDisc begin");    int32_t ret = DiscSetDiscoverCallback(MODULE_LNN, &g_discCb);    if (ret != SOFTBUS_OK) { LOG_ERR("DiscSetDiscoverCallback error!"); return SOFTBUS_ERR;    }    PublishInnerInfo publishInfo = { .publishId = LNN_PUBLISH_ID, .medium = COAP, .freq = HIGH, .capability = LNN_DISC_CAPABILITY, .capabilityData = (unsigned char *)LNN_DISC_CAPABILITY, .dataLen = strlen(LNN_DISC_CAPABILITY) + 1,    };    LOG_INFO("DiscStartScan!");    ret = DiscStartScan(MODULE_LNN, &publishInfo);    if (ret != SOFTBUS_OK) { LOG_ERR("DiscStartScan fail!"); return SOFTBUS_ERR;    }    SubscribeInnerInfo subscribeInfo = { .subscribeId = LNN_SUBSCRIBE_ID, .medium = COAP, .freq = HIGH, .isSameAccount = false, .isWakeRemote = false, .capability = LNN_DISC_CAPABILITY, .capabilityData = (unsigned char *)LNN_DISC_CAPABILITY, .dataLen = strlen(LNN_DISC_CAPABILITY) + 1,    };    LOG_INFO("DiscStartAdvertise!");    ret = DiscStartAdvertise(MODULE_LNN, &subscribeInfo);    if (ret != SOFTBUS_OK) { LOG_ERR("DiscStartAdvertise fail!"); return SOFTBUS_ERR;    }    return SOFTBUS_OK;}

我们主要关心该函数的第一行,其将g_discCb挂载到本地,具体调用链不展开,总之在组网场景下,当发现外部设备后,InnerDeviceFound函数会调用g_discCb的OnDeviceFound函数指针进行处理,其函数名为DeviceFound,源码如下:

static void DeviceFound(const DeviceInfo *device){    ConnectionAddr *para = NULL;    if (device == NULL) { LOG_ERR("DeviceFound error!"); return;    }    LOG_INFO("DeviceFound! type = %d", g_status.type);    para = (ConnectionAddr *)SoftBusCalloc(sizeof(ConnectionAddr));    if (para == NULL) { LOG_ERR("malloc init message fail"); return;    }    para->type = g_status.type;    para->info.ip.port = device->addr[0].port;    if (strncpy_s(para->info.ip.ip, IP_STR_MAX_LEN, device->addr[0].addr, strlen(device->addr[0].addr)) != EOK) { LOG_ERR("STR ERROR!"); SoftBusFree(para); return;    }    if (g_status.fsm != NULL) { LOG_INFO("PostMessageToFsm!"); (void)LnnFsmRemoveMessage(g_status.fsm, FSM_MSG_TYPE_DISCOVERY_TIMEOUT); if (LnnFsmPostMessage(g_status.fsm, FSM_MSG_TYPE_JOIN_LNN, para) != SOFTBUS_OK) {     LOG_ERR("LnnFsmPostMessage FSM_MSG_TYPE_JOIN_LNN error!");     SoftBusFree(para); }    } else { LOG_ERR("DeviceFound don't post to fsm!"); SoftBusFree(para);    }    return;}

该函数首先根据发现的设备信息,构造para地址信息,然后移除掉给状态机设置的超时定时器,然后向状态机提交FSM_MSG_TYPE_JOIN_LNN消息。

此时,该设备即向对端设备请求认证,这种场景下我们成本地发送认证消息的设备为客户端,对端接收认证消息的设备为服务端。

发送认证请求

状态机的机制已经在之前的章节介绍过了,因为当前状态机还在初始AUTH_INDEX状态下,因此该消息触发状态机的OnJoinLNNInAuth的函数。其源码如下:

static int32_t OnJoinLNNInAuth(ConnectionAddr *addr){    int32_t rc;    ConnInfo *connInfo = &g_netBuilder.connInfo;    ConnectOption option;    if (addr == NULL) { return SOFTBUS_INVALID_PARAM;    }    if (ConvertAddrToOption(addr, &option) == false) { SoftBusFree(addr); return SOFTBUS_ERR;    }    if ((connInfo->flag & (CONN_INFO_FLAG_JOINING_ACTIVE | CONN_INFO_FLAG_JOINING_PASSIVE)) != 0) { if (LnnIsSameConnectionAddr(addr, &connInfo->addr) == true) {     LOG_INFO("addr is same, waiting...");     SoftBusFree(addr);     return SOFTBUS_OK; } LOG_ERR("previous request is ongoing, reject it"); LnnNotifyJoinResult(addr, NULL, SOFTBUS_ERR); SoftBusFree(addr); return SOFTBUS_OK;    }    LOG_INFO("begin a new join request");    connInfo->addr = *addr;    connInfo->flag |= CONN_INFO_FLAG_JOINING_ACTIVE;    (void)AuthVerifyInit();    LOG_INFO("hichain init ok....");    rc = AuthVerifyDevice(LNN, &option);    if (rc != SOFTBUS_OK) { CompleteJoinLNN(NULL, SOFTBUS_ERR); (void)AuthVerifyDeinit();    } else { LnnFsmPostMessageDelay(&g_netBuilder.fsm, FSM_MSG_TYPE_JOIN_LNN_TIMEOUT,     NULL, JOIN_LNN_TIMEOUT_LEN);    }    SoftBusFree(addr);    LOG_INFO("rc = %d", rc);    return rc;}

OnJoinLNNInAuth首先将Addr转为Option,然后判断当前系统是否正在进行组网过程,如果是则直接返回。如果不是,则首先调用AuthVerifyInit初始化认证工作,然后调用AuthVerifyDevice进行认证处理,最后调用LnnFsmPostMessageDelay启动15S超时定时器。

认证处理初始化

AuthVerifyInit的源码如下:

int32_t AuthVerifyInit(void){    if (HichainServiceInit() != SOFTBUS_OK) { LOG_ERR("HichainServiceInit failed"); return SOFTBUS_ERR;    }    return SOFTBUS_OK;}

该函数即调用了HichainServiceInit,其源码如下:

static int32_t HichainServiceInit(void){    if (InitDeviceAuthService() != 0) { LOG_ERR("auth InitDeviceAuthService failed"); return SOFTBUS_ERR;    }    g_hichainGaInstance = GetGaInstance();    if (g_hichainGaInstance == NULL) { LOG_ERR("auth GetGaInstance failed"); return SOFTBUS_ERR;    }    g_hichainGmInstance = GetGmInstance();    if (g_hichainGmInstance == NULL) { LOG_ERR("auth GetGmInstance failed"); return SOFTBUS_ERR;    }    (void)memset_s(&g_hichainCallback, sizeof(DeviceAuthCallback), 0, sizeof(DeviceAuthCallback));    g_hichainCallback.onTransmit = AuthOnTransmit;    g_hichainCallback.onSessionKeyReturned = AuthOnSessionKeyReturned;    g_hichainCallback.onFinish = AuthOnFinish;    g_hichainCallback.onError = AuthOnError;    g_hichainCallback.onRequest = AuthOnRequest;    (void)memset_s(&g_hichainListener, sizeof(DataChangeListener), 0, sizeof(DataChangeListener));    g_hichainListener.onDeviceNotTrusted = AuthOnDeviceNotTrusted;    if (g_hichainGmInstance->regDataChangeListener(AUTH_APPID, &g_hichainListener) != 0) { LOG_ERR("auth RegDataChangeListener failed"); return SOFTBUS_ERR;    }    return SOFTBUS_OK;}

该函数首先调用InitDeviceAuthService完成认证服务的初始化工作,然后初始化群组认证管理实例g_hichainGaInstance,群组管理实例g_hichainGmInstance和认证回调实例g_hichainCallback,并在最后注册监听器。

初始化的细节不展开介绍,感兴趣可以对其自行分析。 

设备认证

AuthVerifyDevice的源码如下所示:

int32_t AuthVerifyDevice(AuthModuleId moduleId, const ConnectOption *option){    if (option == NULL) { LOG_ERR("invalid parameter"); return SOFTBUS_INVALID_PARAM;    }    if (pthread_mutex_lock(&g_authLock) != 0) { LOG_ERR("lock mutex failed"); return SOFTBUS_ERR;    }    if (IsNeedVerify(option) == false) { (void)pthread_mutex_unlock(&g_authLock); LOG_INFO("there is no need to verify!"); return SOFTBUS_AUTH_VERIFIED;    }    if (IsDeviceAuthingNow(option) == true) { (void)pthread_mutex_unlock(&g_authLock); LOG_ERR("authentication between two devices is in progress, please verify later"); return SOFTBUS_AUTH_VERIFYING;    }    (void)pthread_mutex_unlock(&g_authLock);    if (g_hichainGaInstance == NULL || g_hichainGmInstance == NULL) { LOG_ERR("need to call AuthVerifyInit!"); return SOFTBUS_ERR;    }    if (HandleVerifyDevice(moduleId, option) != SOFTBUS_OK) { LOG_ERR("auth HandleVerifyDevice failed"); return SOFTBUS_ERR;    }    return SOFTBUS_OK;}

该函数首先调用IsNeedVerify判断目标设备的IP地址是否需要进行认证,然后调用IsDeviceAuthingNow判断目标设备是否正在进行认证处理,如果都没有,则调用HandleVerifyDevice对设备进行认证处理。HandleVerifyDevice的源码如下:

static int32_t HandleVerifyDevice(AuthModuleId moduleId, const ConnectOption *option){    if (pthread_mutex_lock(&g_authLock) != 0) { LOG_ERR("lock mutex failed"); return SOFTBUS_ERR;    }    AuthManager *auth = (AuthManager *)SoftBusMalloc(sizeof(AuthManager));    if (auth == NULL) { LOG_ERR("SoftBusMalloc failed"); (void)pthread_mutex_unlock(&g_authLock); return SOFTBUS_ERR;    }    (void)memset_s(auth, sizeof(AuthManager), 0, sizeof(AuthManager));    if (InitNewAuthManager(auth, moduleId, option) != SOFTBUS_OK) { LOG_ERR("auth InitNewAuthManager failed"); (void)pthread_mutex_unlock(&g_authLock); SoftBusFree(auth); return SOFTBUS_ERR;    }    (void)pthread_mutex_unlock(&g_authLock);    if (option->type == CONNECT_TCP) { if (HandleIpVerifyDevice(auth, option) != SOFTBUS_OK) {     LOG_ERR("HandleIpVerifyDevice failed");     DeleteAuth(auth);     return SOFTBUS_ERR; }    } else if (option->type == CONNECT_BR) { if (ConnConnectDevice(option, auth->requestId, &g_connResult) != SOFTBUS_OK) {     LOG_ERR("auth ConnConnectDevice failed");     DeleteAuth(auth);     return SOFTBUS_ERR; }    } else { LOG_ERR("auth conn type %d is not support", option->type); DeleteAuth(auth); return SOFTBUS_ERR;    }    if (EventInLooper(auth->authId) != SOFTBUS_OK) { LOG_ERR("auth EventInLooper failed"); DeleteAuth(auth); return SOFTBUS_ERR;    }    LOG_INFO("start authentication process, authId is %lld", auth->authId);    return SOFTBUS_OK;}

该函数主要分为三步:

  1. 初始化AuthManager,并将其插入到g_authClientHead列表尾节点;
  2. 根据组网连接的类型,选择对应的认证方法。如果是IP类型,则调用HandleIpVerifyDevice进行认证,如果是BR类型,则调用ConnConnectDevice进行认证。
  3. 调用EventInLooper设置超时定时器,10S后若认证还未成功就进行超时处理

其中第1步和第3步比较简单,我们对第2步以IP链接为例,对HandleIpVerifyDevice函数展开分析,其源码如下:

int32_t HandleIpVerifyDevice(AuthManager *auth, const ConnectOption *option){    if (auth == NULL || option == NULL) { LOG_ERR("invalid parameter"); return SOFTBUS_ERR;    }    char localIp[IP_MAX_LEN] = {0};    if (LnnGetLocalStrInfo(STRING_KEY_WLAN_IP, localIp, IP_MAX_LEN) != SOFTBUS_OK) { LOG_ERR("auth get local ip failed"); return SOFTBUS_ERR;    }    int fd = OpenTcpClientSocket(option->info.ipOption.ip, localIp, option->info.ipOption.port);    if (fd fd = fd;    if (AddTrigger(AUTH, fd, RW_TRIGGER) != SOFTBUS_OK) { LOG_ERR("auth AddTrigger failed"); return SOFTBUS_ERR;    }    if (AuthSyncDeviceUuid(auth) != SOFTBUS_OK) { LOG_ERR("AuthSyncDeviceUuid failed"); return SOFTBUS_ERR;    }    return SOFTBUS_OK;}

该函数主要处理分为三步:

  1. 获取本地的IP后,调用OpenTcpClientSocket函数创建一个Socket,连接到对端设备的IP地址和端口
  2. 调用AddTrigger将该Socket的描述符fd添加到g_listenerList的info中。
  3. 调用AuthSyncDeviceUuid填写认证的消息报文后,将该消息发送出去。

该函数比较简单,因此不展开介绍。

接收认证请求消息

当服务端接收到发送端发送的认证消息后,