软总线源码分析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;}
该函数主要分为三步:
- 初始化AuthManager,并将其插入到g_authClientHead列表尾节点;
- 根据组网连接的类型,选择对应的认证方法。如果是IP类型,则调用HandleIpVerifyDevice进行认证,如果是BR类型,则调用ConnConnectDevice进行认证。
- 调用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;}
该函数主要处理分为三步:
- 获取本地的IP后,调用OpenTcpClientSocket函数创建一个Socket,连接到对端设备的IP地址和端口
- 调用AddTrigger将该Socket的描述符fd添加到g_listenerList的info中。
- 调用AuthSyncDeviceUuid填写认证的消息报文后,将该消息发送出去。
该函数比较简单,因此不展开介绍。
接收认证请求消息
当服务端接收到发送端发送的认证消息后,