// 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 static 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}')")); } FCnt serverFCnt; string devEUI = req.Query["DevEUI"]; string fCntDown = req.Query["FCntDown"]; string fCntUp = req.Query["FCntUp"]; string gatewayId = req.Query["GatewayId"]; string abpFcntCacheReset = req.Query["ABPFcntCacheReset"]; int newFCntDown = 0; if (redisCache == null) { lock (typeof(FCntCacheCheck)) { if (redisCache == null) { var config = new ConfigurationBuilder() .SetBasePath(context.FunctionAppDirectory) .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); var redisConnectionString = config.GetConnectionString("RedisConnectionString"); if (string.IsNullOrEmpty(redisConnectionString)) { string errorMsg = "Missing RedisConnectionString in settings"; throw new Exception(errorMsg); } var redis = ConnectionMultiplexer.Connect(redisConnectionString); redisCache = redis.GetDatabase(); } } } if (!string.IsNullOrEmpty(abpFcntCacheReset)) { redisCache.KeyDelete(devEUI); return((ActionResult) new OkObjectResult(null)); } if (!string.IsNullOrEmpty(devEUI) && !string.IsNullOrEmpty(fCntDown) && !string.IsNullOrEmpty(fCntUp) && !string.IsNullOrEmpty(gatewayId)) { int clientFCntDown = int.Parse(fCntDown); int clientFCntUp = int.Parse(fCntUp); string cacheKey = devEUI; try { if (redisCache.LockTake(cacheKey + "msglock", gatewayId, new TimeSpan(0, 0, 10))) { string cachedFCnt = redisCache.StringGet(cacheKey, CommandFlags.DemandMaster); // we have it cached if (!string.IsNullOrEmpty(cachedFCnt)) { serverFCnt = (FCnt)JsonConvert.DeserializeObject(cachedFCnt, typeof(FCnt)); // it is a new message coming up by the first gateway if (clientFCntUp > serverFCnt.FCntUp) { if (clientFCntDown >= serverFCnt.FCntDown) { newFCntDown = (int)(clientFCntDown + 1); } else { newFCntDown = (int)(serverFCnt.FCntDown + 1); } serverFCnt.FCntUp = clientFCntUp; serverFCnt.FCntDown = newFCntDown; serverFCnt.GatewayId = gatewayId; CacheFcnt(serverFCnt, redisCache, cacheKey); } // it is a retry message coming up by the same first gateway else if (clientFCntUp == serverFCnt.FCntUp && gatewayId == serverFCnt.GatewayId) { newFCntDown = serverFCnt.FCntDown + 1; serverFCnt.FCntDown = newFCntDown; CacheFcnt(serverFCnt, redisCache, cacheKey); } else { // we tell not to send any ack or downstream msg newFCntDown = 0; } } // it is the first message from this device else { newFCntDown = clientFCntDown + 1; serverFCnt = new FCnt(); serverFCnt.FCntDown = newFCntDown; serverFCnt.FCntUp = clientFCntUp; serverFCnt.GatewayId = gatewayId; CacheFcnt(serverFCnt, redisCache, cacheKey); } } } finally { redisCache.LockRelease(cacheKey + "msglock", gatewayId); } } else { string errorMsg = "Missing DevEUI or FCntDown or FCntUp or GatewayId"; throw new Exception(errorMsg); } return((ActionResult) new OkObjectResult(newFCntDown)); }