/// <summary> /// Parses the content and returns an object of proper type /// </summary> /// <param name="contentBytes"></param> /// <param name="type"></param> /// <param name="code"></param> /// <param name="authenticator"></param> /// <param name="sharedSecret"></param> /// <returns></returns> private static Object ParseContentBytes(Byte[] contentBytes, String type, UInt32 code, Byte[] authenticator, Byte[] sharedSecret) { switch (type) { case "string": return(Encoding.UTF8.GetString(contentBytes)); case "tagged-string": return(Encoding.UTF8.GetString(contentBytes)); case "octet": // If this is a password attribute it must be decrypted if (code == 2) { return(RadiusPassword.Decrypt(sharedSecret, authenticator, contentBytes)); } return(contentBytes); case "integer": return(BitConverter.ToUInt32(contentBytes.Reverse().ToArray(), 0)); case "tagged-integer": return(BitConverter.ToUInt32(contentBytes.Reverse().ToArray(), 0)); case "ipaddr": return(new IPAddress(contentBytes)); default: return(null); } }
/// <summary> /// Parses the content and returns an object of proper type /// </summary> /// <param name="contentBytes"></param> /// <param name="type"></param> /// <param name="code"></param> /// <param name="authenticator"></param> /// <param name="sharedSecret"></param> /// <returns></returns> private static Object ParseContentBytes(Byte[] contentBytes, String type, UInt32 code, Byte[] authenticator, Byte[] sharedSecret) { switch (type) { case "string": case "String": case "tagged-string": return(Encoding.UTF8.GetString(contentBytes)); case "octet": case "octets": // If this is a password attribute it must be decrypted if (code == 2) { return(RadiusPassword.Decrypt(sharedSecret, authenticator, contentBytes)); } return(contentBytes); case "ipaddr": case "ipv6addr": return(new IPAddress(contentBytes)); case "date": return(DateTimeOffset.FromUnixTimeSeconds(BitConverter.ToUInt32(contentBytes.Reverse().ToArray(), 0))); case "short": return(BitConverter.ToUInt16(contentBytes.Reverse().ToArray(), 0)); case "integer": case "signed": case "tagged-integer": return(BitConverter.ToUInt32(contentBytes.Reverse().ToArray(), 0)); case "integer64": return(BitConverter.ToUInt64(contentBytes.Reverse().ToArray(), 0)); case "abinary": // Ascend binary filter format. case "byte": // 8-bit unsigned integer. case "bytes": case "combo-ip": case "ether": // Ethernet MAC address. case "extended": case "ifid": // Interface Id (hex:hex:hex). case "ipv4prefix": // IPv4 Prefix as given in RFC 6572. case "ipv6prefix": // IPv6 prefix, with mask. 2001:db8:a000:ff::/64 case "long-extended": case "struct": // Fixed size structures. case "tlv": // Type-Length-Value (allows nested attributes). default: _log.Warn($"No content parser for type '{type}'."); return(null); } }
/// <summary> /// Get the raw packet bytes /// </summary> /// <returns></returns> public Byte[] GetBytes(RadiusDictionary dictionary) { var packetBytes = new List <Byte> { (Byte)Code, Identifier }; packetBytes.AddRange(new Byte[18]); // Placeholder for length and authenticator var messageAuthenticatorPosition = 0; foreach (var attribute in Attributes) { // todo add logic to check attribute object type matches type in dictionary? foreach (var value in attribute.Value) { var contentBytes = GetAttributeValueBytes(value); var headerBytes = new Byte[2]; dictionary.AttributeNames.TryGetValue(attribute.Key, out var attributeType); switch (attributeType) { case DictionaryVendorAttribute _attributeType: headerBytes = new Byte[8]; headerBytes[0] = 26; // VSA type var vendorId = BitConverter.GetBytes(_attributeType.VendorId); Array.Reverse(vendorId); Buffer.BlockCopy(vendorId, 0, headerBytes, 2, 4); headerBytes[6] = (Byte)_attributeType.VendorCode; headerBytes[7] = (Byte)(2 + contentBytes.Length); // length of the vsa part break; case DictionaryAttribute _attributeType: headerBytes[0] = attributeType.Code; // Encrypt password if this is a User-Password attribute if (_attributeType.Code == 2) { contentBytes = RadiusPassword.Encrypt(SharedSecret, Authenticator, contentBytes); } else if (_attributeType.Code == 80) // Remember the position of the message authenticator, because it has to be added after everything else { messageAuthenticatorPosition = packetBytes.Count; } break; default: throw new InvalidOperationException($"Unknown attribute {attribute.Key}, check spelling or dictionary"); } headerBytes[1] = (Byte)(headerBytes.Length + contentBytes.Length); packetBytes.AddRange(headerBytes); packetBytes.AddRange(contentBytes); } } // Note the order of the bytes... var packetLengthBytes = BitConverter.GetBytes(packetBytes.Count); packetBytes[2] = packetLengthBytes[1]; packetBytes[3] = packetLengthBytes[0]; var packetBytesArray = packetBytes.ToArray(); if (Code == PacketCode.AccountingRequest || Code == PacketCode.DisconnectRequest) { var authenticator = CalculateRequestAuthenticator(SharedSecret, packetBytesArray); Buffer.BlockCopy(authenticator, 0, packetBytesArray, 4, 16); } else { var authenticator = _requestAuthenticator != null?CalculateResponseAuthenticator(SharedSecret, _requestAuthenticator, packetBytesArray) : Authenticator; Buffer.BlockCopy(authenticator, 0, packetBytesArray, 4, 16); } if (messageAuthenticatorPosition != 0) { var temp = new Byte[16]; Buffer.BlockCopy(temp, 0, packetBytesArray, messageAuthenticatorPosition + 2, 16); var messageAuthenticatorBytes = CalculateMessageAuthenticator(packetBytesArray, SharedSecret); Buffer.BlockCopy(messageAuthenticatorBytes, 0, packetBytesArray, messageAuthenticatorPosition + 2, 16); } return(packetBytesArray); }