private IRadiusPacket Authenticate(IRadiusPacket packet) { var msisdn = packet.GetAttribute <String>("Calling-Station-Id"); if (!packet.Attributes.ContainsKey("3GPP-User-Location-Info")) { _log.Warn("Missing 3GPP-User-Location-Info in packet, ignoring"); return(null); } var locationInfo = Utils.GetMccMncFrom3GPPLocationInfo(packet.GetAttribute <Byte[]>("3GPP-User-Location-Info")); _log.Debug($"Handling authentication packet for {msisdn} on network {locationInfo.locationType}:{locationInfo.mccmnc}"); using (var db = _contextFactory.GetContext()) { if (locationInfo.mccmnc == "99999") { _log.Warn($"No location info for msisdn {msisdn} check m2m portal 3GPP-SGSN-Address: {packet.GetAttribute<IPAddress>("3GPP-SGSN-Address")}"); return(packet.CreateResponsePacket(PacketCode.AccessReject)); } var result = db.Authenticate1(msisdn, "flexinets", msisdn, locationInfo.mccmnc).ToList(); if (result.Count > 0 && result.First() == null) { var response = packet.CreateResponsePacket(PacketCode.AccessAccept); response.AddAttribute("Acct-Interim-Interval", 60); return(response); } else { try { var mccmnc = Convert.ToInt32(locationInfo.mccmnc); var network = db.Networks.SingleOrDefault(o => o.mccmnc == mccmnc); var simcard = db.SimCards.SingleOrDefault(o => o.Msisdn == msisdn); var sb = new StringBuilder(); sb.AppendLine($"Authentication failed for {msisdn} on network {mccmnc} ({network?.providername}, {network?.countryname})"); if (simcard.user_id == null) { sb.AppendLine("Sim card not mapped to a user"); } else { sb.AppendLine($"User: {simcard.UserSetting.user.username}@{simcard.UserSetting.user.realm}, group: {simcard.UserSetting.user.directory.name}"); } _log.Warn(sb.ToString().Trim()); // todo needs throttling to reduce unwanted spam } catch (Exception ex) { _log.Error("huh?", ex); } return(packet.CreateResponsePacket(PacketCode.AccessReject)); } } }
public void PingRadiusServer(object state) { try { using (var db = _contextFactory.GetContext()) { _ipAddresses = db.FL1Settings.SingleOrDefault(o => o.Name == "PingIpAddresses").Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); } } catch (Exception ex) { _log.Error("Unable to refresh ip list, using old values", ex); } Parallel.ForEach(_ipAddresses, ipAddress => { try { var reply = SendPing(ipAddress, 5000, 1); var message = $"Ping from {Environment.MachineName} to {ipAddress} response {reply?.Status} in {reply?.RoundtripTime}ms"; _log.Debug(message); using (var db = _contextFactory.GetContext()) { db.ServerMonitorLogs.Add(new ServerMonitorLog { EventId = 1, EventTimestamp = DateTime.UtcNow, MachineName = Environment.MachineName, Message = message, Status = reply.Status.ToString(), Target = ipAddress }); db.SaveChanges(); } } catch (Exception ex) { _log.Error("Something went wrong with ping", ex); } }); }
/// <summary> /// Proxy iPass authentication to an external server /// </summary> /// <param name="rawusername"></param> /// <param name="password"></param> /// <returns></returns> public PacketCode?ProxyAuthentication(String rawusername, String password) { using (var db = _contextFactory.GetContext()) { var usernamedomain = UsernameDomain.Parse(rawusername, true); var server = db.Roamservers.FirstOrDefault(o => o.domain == usernamedomain.Domain); if (server != null) { _log.Debug($"Found proxy server {server.host} for username {rawusername}"); if (!String.IsNullOrEmpty(server.rewritedomain)) { usernamedomain.Domain = server.rewritedomain; _log.Debug($"Rewriting username from {rawusername} to {usernamedomain}"); rawusername = usernamedomain.FullUsername; } ProcessStartInfo startinfo; if (server.uselegacy) { startinfo = ProxyAuthenticationSsl(rawusername, password, server.host); } else { startinfo = ProxyAuthenticationNew(rawusername, password, server.host); } using (var process = new Process { StartInfo = startinfo }) { var sb = new StringBuilder(); process.OutputDataReceived += (sender, args) => sb.AppendLine(args.Data); process.Start(); process.BeginOutputReadLine(); process.StandardInput.WriteLine(); // Exits the script process.WaitForExit(); var content = sb.ToString(); _log.Debug(content); if (content.Contains("Status: accept")) { return(PacketCode.AccessAccept); } if (content.Contains("LDAP User found but memberOf validation failed")) { _log.Warn($"MemberOf failed for user {rawusername}"); } if (content.Contains("Message: LDAP search found no entries for this user")) { _log.Warn($"Username {rawusername} not found"); } if (content.Contains("Status: reject")) { _log.Warn($"Got reject for user {rawusername} from proxy"); } if (!(content.Contains("reject") || content.Contains("accept"))) { _log.Error($"Invalid proxy response: {content}"); throw new InvalidOperationException("Something went wrong with proxy"); } } return(PacketCode.AccessReject); } } return(null); }
/// <summary> /// Accounting /// </summary> /// <param name="packet"></param> /// <returns></returns> private IRadiusPacket HandleAccountingPacket(IRadiusPacket packet) { var acctStatusType = packet.GetAttribute <AcctStatusType>("Acct-Status-Type"); if (acctStatusType == AcctStatusType.Start || acctStatusType == AcctStatusType.Stop) { var usernamedomain = UsernameDomain.Parse(packet.GetAttribute <String>("User-Name")); var nodeid = GetUserNodeId(usernamedomain.Username, usernamedomain.Domain); _log.Info($"Handling {acctStatusType} packet for {usernamedomain}"); try { using (var db = _contextFactory.GetContext()) { var entry = new radiatoraccounting { username = usernamedomain.Username, realm = usernamedomain.Domain, node_id = nodeid, ACCTSTATUSTYPE = (packet.GetAttribute <AcctStatusType>("Acct-Status-Type")).ToString(), ACCTINPUTOCTETS = Convert.ToUInt32(packet.GetAttribute <UInt32?>("Acct-Input-Octets")), ACCTOUTPUTOCTETS = Convert.ToUInt32(packet.GetAttribute <UInt32?>("Acct-Output-Octets")), ACCTSESSIONID = packet.GetAttribute <String>("Acct-Session-Id"), ACCTSESSIONTIME = Convert.ToInt32(packet.GetAttribute <UInt32?>("Acct-Session-Time")), NASIDENTIFIER = packet.GetAttribute <String>("NAS-Identifier"), NASPORT = packet.GetAttribute <UInt32?>("NAS-Port"), NASPORTTYPE = packet.GetAttribute <UInt32?>("NAS-Port-Type").ToString(), WISPrLocationName = packet.GetAttribute <String>("WISPr-Location-Name"), temp = packet.GetAttribute <String>("Ipass-Location-Description"), timestamp_datetime = packet.Attributes.ContainsKey("Timestamp") ? (DateTime?)DateTimeOffset.FromUnixTimeSeconds(packet.GetAttribute <Int32>("Timestamp")).UtcDateTime : DateTime.UtcNow }; db.radiatoraccountings.Add(entry); db.SaveChanges(); } } catch (DbUpdateConcurrencyException) { _log.Info($"Duplicate {acctStatusType} request received"); } catch (Exception ex) { _log.Error("Something went wrong", ex); } if (acctStatusType == AcctStatusType.Start) { try { using (var db = _contextFactory.GetContext()) { db.radiatoronlines.Add(new radiatoronline { username = usernamedomain.Username, realm = usernamedomain.Domain, node_id = nodeid, ACCTSESSIONID = packet.GetAttribute <String>("Acct-Session-Id"), timestamp_datetime = packet.Attributes.ContainsKey("Timestamp") ? (DateTime?)DateTimeOffset.FromUnixTimeSeconds(packet.GetAttribute <Int32>("Timestamp")).UtcDateTime : DateTime.UtcNow, NASIDENTIFIER = packet.GetAttribute <String>("NAS-Identifier"), NASPORT = packet.GetAttribute <UInt32?>("NAS-Port"), NASPORTTYPE = packet.GetAttribute <UInt32?>("NAS-Port-Type").ToString(), WISPrLocationName = packet.GetAttribute <String>("Ipass-Location-Description") }); db.SaveChanges(); } } catch (DbUpdateConcurrencyException) { _log.Info("Cannot insert duplicate in radiatoronline"); } } if (acctStatusType == AcctStatusType.Stop) { try { using (var db = _contextFactory.GetContext()) { var acctsessionid = packet.GetAttribute <String>("Acct-Session-Id"); var online = db.radiatoronlines.SingleOrDefault(o => o.ACCTSESSIONID == acctsessionid); if (online != null) { db.radiatoronlines.Remove(online); db.SaveChanges(); } } } catch (DbUpdateConcurrencyException) { _log.Info("Nothing to remove from online"); } } } return(packet.CreateResponsePacket(PacketCode.AccountingResponse)); }
public async Task LoadSessionsAsync(DateTime fromdate, DateTime todate) { _log.Info($"Loading sessions from {fromdate} to {todate}"); using (var db = _contextFactory.GetContext()) { var customers = await GetAccountsAsync(); using (var client = await CreateAuthenticatedHttpClientAsync()) { client.Timeout = TimeSpan.FromMinutes(2); foreach (var customer in customers) { try { var url = $"https://openmobile.ipass.com/moservices/rest/api/ipass/{customer.customerId}/mo/reports/central-csv?report=connectionActivity&startdate={fromdate:MM-dd-yyyy}&enddate={todate:MM-dd-yyyy}&sess-type=initiated&pageSize=0"; using (var contentstream = await client.GetStreamAsync(url)) { using (var connection = _contextFactory.GetSqlConnection()) { var reader = new StreamReader(contentstream); var content = await reader.ReadToEndAsync(); content = content.Replace("\"", ""); var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)); var csvreader = new CsvReader(new StreamReader(stream), true, ','); var bulkcopy = new SqlBulkCopy(connection) { DestinationTableName = "Insight.SessionsV2", BulkCopyTimeout = 60, BatchSize = 10000 }; bulkcopy.ColumnMappings.Add(0, "AccountId"); bulkcopy.ColumnMappings.Add(1, "[UsernameDomain]"); bulkcopy.ColumnMappings.Add(3, "[ProfileId]"); bulkcopy.ColumnMappings.Add(4, "[SessionId]"); bulkcopy.ColumnMappings.Add(5, "[StartTime]"); bulkcopy.ColumnMappings.Add(6, "[SessionLength]"); bulkcopy.ColumnMappings.Add(7, "[AuthenticationTime]"); bulkcopy.ColumnMappings.Add(8, "[ConnectionType]"); bulkcopy.ColumnMappings.Add(9, "[ConnectionStatus]"); bulkcopy.ColumnMappings.Add(10, "[ConnectionStatusCode]"); bulkcopy.ColumnMappings.Add(11, "[ConnectionStatusCodeType]"); bulkcopy.ColumnMappings.Add(12, "[ConnectionStatusCodeDescription]"); bulkcopy.ColumnMappings.Add(13, "[DisconnectCode]"); bulkcopy.ColumnMappings.Add(14, "[ClientIpAddress]"); bulkcopy.ColumnMappings.Add(15, "[DownloadedMB]"); bulkcopy.ColumnMappings.Add(17, "[UploadedMB]"); bulkcopy.ColumnMappings.Add(18, "[SignalStrength]"); bulkcopy.ColumnMappings.Add(19, "[Country]"); bulkcopy.ColumnMappings.Add(20, "[ClientMacAddress]"); bulkcopy.ColumnMappings.Add(21, "[Ssid]"); bulkcopy.ColumnMappings.Add(22, "[AuthMethod]"); bulkcopy.ColumnMappings.Add(23, "[SecurityMode]"); bulkcopy.ColumnMappings.Add(24, "[DeviceType]"); bulkcopy.ColumnMappings.Add(25, "[DeviceOS]"); bulkcopy.ColumnMappings.Add(26, "[ClientVersion]"); bulkcopy.ColumnMappings.Add(27, "[SdkVersion]"); bulkcopy.ColumnMappings.Add(28, "[ConnectReason]"); connection.Open(); // Veeeewwy veeewwy important! Otherwise decimal parsing from eg 5.53 will fail Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; await bulkcopy.WriteToServerAsync(csvreader); _log.Info($"Imported sessions for {customer.name}"); } } } catch (Exception ex) { _log.Error($"{customer.name} failed to download: ", ex); } } } _log.Info("Updating nodeids"); var update = @" BEGIN TRANSACTION UPDATE Insight.SessionsV2 SET node_id = users.node_id FROM Insight.SessionsV2 AS i LEFT JOIN users ON CONCAT(users.username, '@', users.realm) = i.UsernameDomain WHERE CONCAT(users.username, '@', users.realm) = i.UsernameDomain AND i.node_id IS NULL UPDATE Insight.SessionsV2 SET node_id = (SELECT TOP 1 t1.node_id FROM ipass_realms AS t1 LEFT JOIN directory ON(t1.node_id = directory.node_id) WHERE realm = SUBSTRING(UsernameDomain, LEN(usernamedomain) - CHARINDEX('@', REVERSE(UsernameDomain)) + 2, LEN(UsernameDomain)) ORDER BY directory.lft) FROM Insight.SessionsV2 AS i WHERE i.node_id IS NULL UPDATE Insight.SessionsV2 SET node_id = (SELECT TOP 1 node_id FROM directory WHERE ipassCustomerID = AccountId ORDER BY directory.lft) FROM Insight.SessionsV2 AS i WHERE i.node_id IS NULL COMMIT"; await db.Database.ExecuteSqlCommandAsync(update); _log.Info("Updating summary tables"); var updatesummary = @" DELETE FROM ipass.CombinedCdrUsers WHERE Month >= @fromdate INSERT INTO ipass.CombinedCdrUsers (Month, UsernameDomain, NodeId, GiSessions, DsSessions, Sum, Seconds) SELECT monthdatetime, UsernameDomain, node_id, SUM(CASE WHEN c.type = 'gi' THEN 1 ELSE 0 END) AS gisessions, SUM(CASE WHEN c.type = 'ds' THEN 1 ELSE 0 END) AS dssessions, SUM(sum) AS sum, SUM(duration) AS seconds FROM ipass.CdrCombined AS c WHERE node_id IS NOT NULL AND UsernameDomain IS NOT NULL AND monthdatetime >= @fromdate GROUP BY monthdatetime, UsernameDomain,node_id DELETE FROM ipass.combinedcdrgroups WHERE month >= @fromdate INSERT INTO iPass.CombinedCdrGroups (Month, NodeId, Seconds, Sum, BothUsers, GiUsers, DsUsers, DsSessions, GiSession) SELECT u.Month, u.NodeId, SUM(u.Seconds), SUM(u.Sum), SUM(CASE WHEN u.DsSessions > 0 AND u.GiSessions > 0 THEN 1 ELSE 0 END) AS bothuser, SUM(CASE WHEN u.DsSessions = 0 AND u.GiSessions > 0 THEN 1 ELSE 0 END) AS giuser, SUM(CASE WHEN u.DsSessions > 0 AND u.GiSessions = 0 THEN 1 ELSE 0 END) AS dsuser, SUM(u.DsSessions), SUM(u.GiSessions) FROM iPass.CombinedCdrUsers AS u WHERE Month >= @fromdate GROUP BY u.Month, u.NodeId"; var summaryfromdate = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.AddMonths(-1).Month, 1); await db.Database.ExecuteSqlCommandAsync(updatesummary, new SqlParameter("@fromdate", summaryfromdate)); } _log.Info("Import done"); }
public void CheckWelcomeSms(String msisdn) { _log.Debug($"Checking welcome sms for msisdn {msisdn}"); using (var db = _contextFactory.GetContext()) { var currentsession = (from o in db.Onlines where o.Calling_Station_Id == msisdn select o).SingleOrDefault(); if (currentsession == null) { _log.Error($"Couldnt find current session for msisdn: {msisdn}"); return; } if (!currentsession.NetworkId.HasValue || currentsession.NetworkId.Value == 0) { _log.Error($"Invalid networkid found for msisdn: {msisdn}"); return; } var simcard = (from o in db.SimCards where o.Msisdn == msisdn select o).SingleOrDefault(); if (simcard == null) { _log.Warn($"Couldnt find sim card with msisdn: {msisdn}"); return; } if (simcard.UserSetting == null) { _log.Warn($"Sim card {simcard.Msisdn} not mapped to a user?!"); return; } _log.Debug($"Found sim card with msisdn: {simcard.Msisdn}"); var network = (from o in db.PricesViews where o.mccmnc == currentsession.NetworkId && o.PricelistId == simcard.UserSetting.user.directory.GroupSetting.PricelistId select o).SingleOrDefault(); if (network == null) { _log.Error($"NetworkId {currentsession.NetworkId} not found?! Wtf?"); return; } // Check if there has been a session on the same network within a week var lastsession = (from o in db.Accountings orderby o.Event_Timestamp descending where o.Calling_Station_Id == simcard.Msisdn && o.Acct_Status_Type == "stop" select o).FirstOrDefault(); if (lastsession == null) { // Dont send an sms on the first connection, allow setup _log.Debug($"Sim card {simcard.Msisdn}, previous NetworkId: n/a, current NetworkId: {currentsession.NetworkId}"); //SendWelcomeSms(simcard.UserSetting.user, network); } else if (lastsession.Event_Timestamp < DateTime.UtcNow.AddDays(-7)) // Over a week since the last session { _log.Debug($"Sim card {simcard.Msisdn}, over 1 week since last connection, current NetworkId: {currentsession.NetworkId}"); SendWelcomeSms(simcard.UserSetting.user, network); } else if (lastsession.NetworkId.HasValue && lastsession.NetworkId.Value != currentsession.NetworkId) { _log.Debug($"Sim card {simcard.Msisdn}, previous NetworkId: {lastsession.NetworkId}, current NetworkId: {currentsession.NetworkId}"); SendWelcomeSms(simcard.UserSetting.user, network); } else { _log.Debug($"Sim card {simcard.Msisdn} on same network"); } } }