public async Task <List <IoTHubDeviceInfo> > GetDeviceList(DevEui?devEUI, string gatewayId, DevNonce?devNonce, DevAddr?devAddr, ILogger log = null) { var results = new List <IoTHubDeviceInfo>(); if (devEUI is { } someDevEui) { var joinInfo = await TryGetJoinInfoAndValidateAsync(someDevEui, gatewayId, log); // OTAA join using var deviceCache = new LoRaDeviceCache(this.cacheStore, someDevEui, gatewayId); var cacheKeyDevNonce = string.Concat(devEUI, ":", devNonce); if (this.cacheStore.StringSet(cacheKeyDevNonce, devNonce?.ToString(), TimeSpan.FromMinutes(5), onlyIfNotExists: true)) { var iotHubDeviceInfo = new IoTHubDeviceInfo { DevEUI = someDevEui, PrimaryKey = joinInfo.PrimaryKey }; results.Add(iotHubDeviceInfo); if (await deviceCache.TryToLockAsync()) { deviceCache.ClearCache(); // clear the fcnt up/down after the join log?.LogDebug("Removed key '{key}':{gwid}", someDevEui, gatewayId); } else { log?.LogWarning("Failed to acquire lock for '{key}'", someDevEui); } } else { log?.LogDebug("dev nonce already used. Ignore request '{key}':{gwid}", someDevEui, gatewayId); throw new DeviceNonceUsedException(); } }
// Runner public static async Task <IActionResult> Run(HttpRequest req, ILogger log, ExecutionContext context, ApiVersion currentApiVersion) { // set the current version in the response header req.HttpContext.Response.Headers.Add(ApiVersion.HttpHeaderName, currentApiVersion.Version); var requestedVersion = req.GetRequestedVersion(); if (requestedVersion == null || !currentApiVersion.SupportsVersion(requestedVersion)) { return(new BadRequestObjectResult($"Incompatible versions (requested: '{requestedVersion.Name ?? string.Empty}', current: '{currentApiVersion.Name}')")); } //ABP Case string devAddr = req.Query["DevAddr"]; //OTAA Case string devEUI = req.Query["DevEUI"]; string devNonce = req.Query["DevNonce"]; string gatewayId = req.Query["GatewayId"]; if (redisCache == null || registryManager == null) { lock (typeof(FCntCacheCheck)) { if (redisCache == null || registryManager == null) { var config = new ConfigurationBuilder() .SetBasePath(context.FunctionAppDirectory) .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); string connectionString = config.GetConnectionString("IoTHubConnectionString"); if (connectionString == null) { string errorMsg = "Missing IoTHubConnectionString in settings"; throw new Exception(errorMsg); } string redisConnectionString = config.GetConnectionString("RedisConnectionString"); if (redisConnectionString == null) { string errorMsg = "Missing RedisConnectionString in settings"; throw new Exception(errorMsg); } registryManager = RegistryManager.CreateFromConnectionString(connectionString); var redis = ConnectionMultiplexer.Connect(redisConnectionString); redisCache = redis.GetDatabase(); } } } List <IoTHubDeviceInfo> results = new List <IoTHubDeviceInfo>(); //OTAA join if (devEUI != null) { string cacheKey = devEUI + devNonce; try { if (redisCache.LockTake(cacheKey + "joinlock", gatewayId, new TimeSpan(0, 0, 10))) { //check if we already got the same devEUI and devNonce it can be a reaply attack or a multigateway setup recieving the same join.We are rfusing all other than the first one. string cachedDevNonce = redisCache.StringGet(cacheKey, CommandFlags.DemandMaster); if (!String.IsNullOrEmpty(cachedDevNonce)) { return((ActionResult) new BadRequestObjectResult("UsedDevNonce")); } redisCache.StringSet(cacheKey, devNonce, new TimeSpan(0, 1, 0), When.Always, CommandFlags.DemandMaster); IoTHubDeviceInfo iotHubDeviceInfo = new IoTHubDeviceInfo(); var device = await registryManager.GetDeviceAsync(devEUI); if (device != null) { iotHubDeviceInfo.DevEUI = devEUI; iotHubDeviceInfo.PrimaryKey = device.Authentication.SymmetricKey.PrimaryKey; results.Add(iotHubDeviceInfo); //clear device FCnt cache after join redisCache.KeyDelete(devEUI); } } } finally { redisCache.LockRelease(cacheKey + "joinlock", gatewayId); } } //ABP or normal message else if (devAddr != null) { //TODO check for sql injection devAddr = devAddr.Replace('\'', ' '); var query = registryManager.CreateQuery($"SELECT * FROM devices WHERE properties.desired.DevAddr = '{devAddr}' OR properties.reported.DevAddr ='{devAddr}'", 100); while (query.HasMoreResults) { var page = await query.GetNextAsTwinAsync(); foreach (var twin in page) { if (twin.DeviceId != null) { IoTHubDeviceInfo iotHubDeviceInfo = new IoTHubDeviceInfo(); iotHubDeviceInfo.DevAddr = devAddr; var device = await registryManager.GetDeviceAsync(twin.DeviceId); iotHubDeviceInfo.DevEUI = twin.DeviceId; iotHubDeviceInfo.PrimaryKey = device.Authentication.SymmetricKey.PrimaryKey; results.Add(iotHubDeviceInfo); } } } } else { string errorMsg = "Missing devEUI or devAddr"; throw new Exception(errorMsg); } string json = JsonConvert.SerializeObject(results); return((ActionResult) new OkObjectResult(json)); }
public async Task <List <IoTHubDeviceInfo> > GetDeviceList(string devEUI, string gatewayId, string devNonce, string devAddr) { var results = new List <IoTHubDeviceInfo>(); if (devEUI != null) { // OTAA join string cacheKey = devEUI + devNonce; using (var deviceCache = new LoRaDeviceCache(this.cacheStore, devEUI, gatewayId, cacheKey)) { if (deviceCache.HasValue()) { throw new DeviceNonceUsedException(); } if (deviceCache.TryToLock(cacheKey + "joinlock")) { if (deviceCache.HasValue()) { throw new DeviceNonceUsedException(); } deviceCache.SetValue(devNonce, TimeSpan.FromMinutes(1)); var device = await this.registryManager.GetDeviceAsync(devEUI); if (device != null) { var iotHubDeviceInfo = new IoTHubDeviceInfo { DevEUI = devEUI, PrimaryKey = device.Authentication.SymmetricKey.PrimaryKey }; results.Add(iotHubDeviceInfo); this.cacheStore.KeyDelete(devEUI); } } else { throw new DeviceNonceUsedException(); } } } else if (devAddr != null) { // ABP or normal message // TODO check for sql injection devAddr = devAddr.Replace('\'', ' '); var query = this.registryManager.CreateQuery($"SELECT * FROM devices WHERE properties.desired.DevAddr = '{devAddr}' OR properties.reported.DevAddr ='{devAddr}'", 100); while (query.HasMoreResults) { var page = await query.GetNextAsTwinAsync(); foreach (var twin in page) { if (twin.DeviceId != null) { var device = await this.registryManager.GetDeviceAsync(twin.DeviceId); var iotHubDeviceInfo = new IoTHubDeviceInfo { DevAddr = devAddr, DevEUI = twin.DeviceId, PrimaryKey = device.Authentication.SymmetricKey.PrimaryKey }; results.Add(iotHubDeviceInfo); } } } } else { throw new Exception("Missing devEUI or devAddr"); } return(results); }
public async Task <List <IoTHubDeviceInfo> > GetDeviceList(string devEUI, string gatewayId, string devNonce, string devAddr, ILogger log = null) { var results = new List <IoTHubDeviceInfo>(); if (devEUI != null) { var joinInfo = await this.TryGetJoinInfoAndValidateAsync(devEUI, gatewayId, log); // OTAA join using (var deviceCache = new LoRaDeviceCache(this.cacheStore, devEUI, gatewayId)) { var cacheKeyDevNonce = string.Concat(devEUI, ":", devNonce); var lockKeyDevNonce = string.Concat(cacheKeyDevNonce, ":joinlockdevnonce"); if (this.cacheStore.StringSet(cacheKeyDevNonce, devNonce, TimeSpan.FromMinutes(5), onlyIfNotExists: true)) { var iotHubDeviceInfo = new IoTHubDeviceInfo { DevEUI = devEUI, PrimaryKey = joinInfo.PrimaryKey }; results.Add(iotHubDeviceInfo); if (await deviceCache.TryToLockAsync()) { this.cacheStore.KeyDelete(devEUI); log?.LogDebug("Removed key '{key}':{gwid}", devEUI, gatewayId); } else { log?.LogWarning("Failed to acquire lock for '{key}'", devEUI); } } else { log?.LogDebug("dev nonce already used. Ignore request '{key}':{gwid}", devEUI, gatewayId); throw new DeviceNonceUsedException(); } } } else if (devAddr != null) { // ABP or normal message // TODO check for sql injection devAddr = devAddr.Replace('\'', ' '); var devAddrCache = new LoRaDevAddrCache(this.cacheStore, this.registryManager, log, gatewayId); if (await devAddrCache.TryTakeDevAddrUpdateLock(devAddr)) { try { if (devAddrCache.TryGetInfo(devAddr, out List <DevAddrCacheInfo> devAddressesInfo)) { for (int i = 0; i < devAddressesInfo.Count; i++) { if (!string.IsNullOrEmpty(devAddressesInfo[i].DevEUI)) { // device was not yet populated if (!string.IsNullOrEmpty(devAddressesInfo[i].PrimaryKey)) { results.Add(devAddressesInfo[i]); } else { // we need to load the primaryKey from IoTHub // Add a lock loadPrimaryKey get lock get devAddressesInfo[i].PrimaryKey = await this.LoadPrimaryKeyAsync(devAddressesInfo[i].DevEUI); results.Add(devAddressesInfo[i]); devAddrCache.StoreInfo(devAddressesInfo[i]); } // even if we fail to acquire the lock we wont enter in the next condition as devaddressinfo is not null } } } // if the cache results are null, we query the IoT Hub. // if the device is not found is the cache we query, if there was something, it is probably not our device. if (results.Count == 0 && devAddressesInfo == null) { if (await devAddrCache.TryTakeDevAddrUpdateLock(devAddr)) { try { var query = this.registryManager.CreateQuery($"SELECT * FROM devices WHERE properties.desired.DevAddr = '{devAddr}' OR properties.reported.DevAddr ='{devAddr}'", 100); int resultCount = 0; while (query.HasMoreResults) { var page = await query.GetNextAsTwinAsync(); foreach (var twin in page) { if (twin.DeviceId != null) { var device = await this.registryManager.GetDeviceAsync(twin.DeviceId); var iotHubDeviceInfo = new DevAddrCacheInfo { DevAddr = devAddr, DevEUI = twin.DeviceId, PrimaryKey = device.Authentication.SymmetricKey.PrimaryKey, GatewayId = twin.GetGatewayID(), NwkSKey = twin.GetNwkSKey(), LastUpdatedTwins = twin.Properties.Desired.GetLastUpdated() }; results.Add(iotHubDeviceInfo); devAddrCache.StoreInfo((DevAddrCacheInfo)iotHubDeviceInfo); } resultCount++; } } // todo save when not our devaddr if (resultCount == 0) { devAddrCache.StoreInfo(new DevAddrCacheInfo() { DevAddr = devAddr, DevEUI = string.Empty }); } } finally { devAddrCache.ReleaseDevAddrUpdateLock(devAddr); } } } } finally { devAddrCache.ReleaseDevAddrUpdateLock(devAddr); } } } else { throw new Exception("Missing devEUI or devAddr"); } return(results); }