diff --git a/DotNetty.sln b/DotNetty.sln index 1db307592..6e512ef9b 100644 --- a/DotNetty.sln +++ b/DotNetty.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26206.0 @@ -93,156 +92,332 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telnet.Client", "examples\T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telnet.Server", "examples\Telnet.Server\Telnet.Server.csproj", "{07C97A77-61B6-4BB5-9436-3A7FC379B75E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetty.Codecs.DNS", "src\DotNetty.Codecs.DNS\DotNetty.Codecs.DNS.csproj", "{B984C485-C620-4BD7-8D3B-0C16BE355DA5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetty.Codecs.DNS.Tests", "test\DotNetty.Codecs.DNS.Tests\DotNetty.Codecs.DNS.Tests.csproj", "{3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Debug|x86.ActiveCfg = Debug|Any CPU {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Release|Any CPU.Build.0 = Release|Any CPU + {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Release|x64.ActiveCfg = Release|Any CPU + {DF4FF0D0-A5CE-471F-B946-538C28C21CBB}.Release|x86.ActiveCfg = Release|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Debug|x64.ActiveCfg = Debug|Any CPU + {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Debug|x86.ActiveCfg = Debug|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Release|Any CPU.ActiveCfg = Release|Any CPU {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Release|Any CPU.Build.0 = Release|Any CPU + {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Release|x64.ActiveCfg = Release|Any CPU + {64508DA2-40F1-4CC3-93E8-EA3B18A64E7E}.Release|x86.ActiveCfg = Release|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82796E9E-1331-4858-90C3-8E74BA4CC383}.Debug|x64.ActiveCfg = Debug|Any CPU + {82796E9E-1331-4858-90C3-8E74BA4CC383}.Debug|x86.ActiveCfg = Debug|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Release|Any CPU.ActiveCfg = Release|Any CPU {82796E9E-1331-4858-90C3-8E74BA4CC383}.Release|Any CPU.Build.0 = Release|Any CPU + {82796E9E-1331-4858-90C3-8E74BA4CC383}.Release|x64.ActiveCfg = Release|Any CPU + {82796E9E-1331-4858-90C3-8E74BA4CC383}.Release|x86.ActiveCfg = Release|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Debug|x64.ActiveCfg = Debug|Any CPU + {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Debug|x86.ActiveCfg = Debug|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Release|Any CPU.Build.0 = Release|Any CPU + {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Release|x64.ActiveCfg = Release|Any CPU + {25F7AD69-7836-46E8-9B29-0FBB3C128FFB}.Release|x86.ActiveCfg = Release|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12DCCEFD-623B-46CC-979C-407FA265E239}.Debug|x64.ActiveCfg = Debug|Any CPU + {12DCCEFD-623B-46CC-979C-407FA265E239}.Debug|x86.ActiveCfg = Debug|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Release|Any CPU.ActiveCfg = Release|Any CPU {12DCCEFD-623B-46CC-979C-407FA265E239}.Release|Any CPU.Build.0 = Release|Any CPU + {12DCCEFD-623B-46CC-979C-407FA265E239}.Release|x64.ActiveCfg = Release|Any CPU + {12DCCEFD-623B-46CC-979C-407FA265E239}.Release|x86.ActiveCfg = Release|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Debug|x64.ActiveCfg = Debug|Any CPU + {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Debug|x86.ActiveCfg = Debug|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Release|Any CPU.ActiveCfg = Release|Any CPU {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Release|Any CPU.Build.0 = Release|Any CPU + {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Release|x64.ActiveCfg = Release|Any CPU + {5ADB0FF5-8EFC-475A-BF08-6B35EF728329}.Release|x86.ActiveCfg = Release|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D284C2BF-E06E-481B-B301-503A9D477B0E}.Debug|x64.ActiveCfg = Debug|Any CPU + {D284C2BF-E06E-481B-B301-503A9D477B0E}.Debug|x86.ActiveCfg = Debug|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Release|Any CPU.ActiveCfg = Release|Any CPU {D284C2BF-E06E-481B-B301-503A9D477B0E}.Release|Any CPU.Build.0 = Release|Any CPU + {D284C2BF-E06E-481B-B301-503A9D477B0E}.Release|x64.ActiveCfg = Release|Any CPU + {D284C2BF-E06E-481B-B301-503A9D477B0E}.Release|x86.ActiveCfg = Release|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Debug|x64.ActiveCfg = Debug|Any CPU + {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Debug|x86.ActiveCfg = Debug|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Release|Any CPU.ActiveCfg = Release|Any CPU {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Release|Any CPU.Build.0 = Release|Any CPU + {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Release|x64.ActiveCfg = Release|Any CPU + {75A1BCC1-A7F3-4893-99C5-3235F87DB00E}.Release|x86.ActiveCfg = Release|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F442118-A665-4891-B056-FE9E54C5B049}.Debug|x64.ActiveCfg = Debug|Any CPU + {1F442118-A665-4891-B056-FE9E54C5B049}.Debug|x86.ActiveCfg = Debug|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F442118-A665-4891-B056-FE9E54C5B049}.Release|Any CPU.Build.0 = Release|Any CPU + {1F442118-A665-4891-B056-FE9E54C5B049}.Release|x64.ActiveCfg = Release|Any CPU + {1F442118-A665-4891-B056-FE9E54C5B049}.Release|x86.ActiveCfg = Release|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {572E1914-489F-402D-944F-71EE0632E5D8}.Debug|x64.ActiveCfg = Debug|Any CPU + {572E1914-489F-402D-944F-71EE0632E5D8}.Debug|x86.ActiveCfg = Debug|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {572E1914-489F-402D-944F-71EE0632E5D8}.Release|Any CPU.Build.0 = Release|Any CPU + {572E1914-489F-402D-944F-71EE0632E5D8}.Release|x64.ActiveCfg = Release|Any CPU + {572E1914-489F-402D-944F-71EE0632E5D8}.Release|x86.ActiveCfg = Release|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Debug|x64.ActiveCfg = Debug|Any CPU + {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Debug|x86.ActiveCfg = Debug|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Release|Any CPU.Build.0 = Release|Any CPU + {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Release|x64.ActiveCfg = Release|Any CPU + {DA54DBAF-CCDA-4AD1-9FF9-EB6F890D1091}.Release|x86.ActiveCfg = Release|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C92F76-F804-4300-BFF1-459420D9EF0B}.Debug|x64.ActiveCfg = Debug|Any CPU + {72C92F76-F804-4300-BFF1-459420D9EF0B}.Debug|x86.ActiveCfg = Debug|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72C92F76-F804-4300-BFF1-459420D9EF0B}.Release|Any CPU.Build.0 = Release|Any CPU + {72C92F76-F804-4300-BFF1-459420D9EF0B}.Release|x64.ActiveCfg = Release|Any CPU + {72C92F76-F804-4300-BFF1-459420D9EF0B}.Release|x86.ActiveCfg = Release|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Debug|x64.ActiveCfg = Debug|Any CPU + {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Debug|x86.ActiveCfg = Debug|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Release|Any CPU.ActiveCfg = Release|Any CPU {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Release|Any CPU.Build.0 = Release|Any CPU + {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Release|x64.ActiveCfg = Release|Any CPU + {E31A5D46-71B7-4B7E-A9F8-3F011822F28A}.Release|x86.ActiveCfg = Release|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Debug|x64.ActiveCfg = Debug|Any CPU + {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Debug|x86.ActiveCfg = Debug|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Release|Any CPU.ActiveCfg = Release|Any CPU {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Release|Any CPU.Build.0 = Release|Any CPU + {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Release|x64.ActiveCfg = Release|Any CPU + {3AF8A62F-F5CE-4C2D-B356-8B9FDFA51668}.Release|x86.ActiveCfg = Release|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Debug|x86.ActiveCfg = Debug|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Release|Any CPU.Build.0 = Release|Any CPU + {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Release|x64.ActiveCfg = Release|Any CPU + {A7FC497E-790A-4980-B57C-32AF4AD4AB9D}.Release|x86.ActiveCfg = Release|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08C19033-23B2-47D7-8332-86273AE287BC}.Debug|x64.ActiveCfg = Debug|Any CPU + {08C19033-23B2-47D7-8332-86273AE287BC}.Debug|x86.ActiveCfg = Debug|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Release|Any CPU.ActiveCfg = Release|Any CPU {08C19033-23B2-47D7-8332-86273AE287BC}.Release|Any CPU.Build.0 = Release|Any CPU + {08C19033-23B2-47D7-8332-86273AE287BC}.Release|x64.ActiveCfg = Release|Any CPU + {08C19033-23B2-47D7-8332-86273AE287BC}.Release|x86.ActiveCfg = Release|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Debug|x64.ActiveCfg = Debug|Any CPU + {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Debug|x86.ActiveCfg = Debug|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Release|Any CPU.Build.0 = Release|Any CPU + {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Release|x64.ActiveCfg = Release|Any CPU + {CE97D2EC-3EA9-4FEC-B304-F57646DB54FD}.Release|x86.ActiveCfg = Release|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Debug|x64.ActiveCfg = Debug|Any CPU + {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Debug|x86.ActiveCfg = Debug|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Release|Any CPU.Build.0 = Release|Any CPU + {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Release|x64.ActiveCfg = Release|Any CPU + {D0DBB346-BDDA-4E28-A335-6D3E1F9902DF}.Release|x86.ActiveCfg = Release|Any CPU {5C3A8F6C-0F37-41A4-9A40-91FECE9C6022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5C3A8F6C-0F37-41A4-9A40-91FECE9C6022}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C3A8F6C-0F37-41A4-9A40-91FECE9C6022}.Debug|x64.ActiveCfg = Debug|Any CPU + {5C3A8F6C-0F37-41A4-9A40-91FECE9C6022}.Debug|x86.ActiveCfg = Debug|Any CPU {5C3A8F6C-0F37-41A4-9A40-91FECE9C6022}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C3A8F6C-0F37-41A4-9A40-91FECE9C6022}.Release|Any CPU.Build.0 = Release|Any CPU + {5C3A8F6C-0F37-41A4-9A40-91FECE9C6022}.Release|x64.ActiveCfg = Release|Any CPU + {5C3A8F6C-0F37-41A4-9A40-91FECE9C6022}.Release|x86.ActiveCfg = Release|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Debug|x64.ActiveCfg = Debug|Any CPU + {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Debug|x86.ActiveCfg = Debug|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Release|Any CPU.Build.0 = Release|Any CPU + {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Release|x64.ActiveCfg = Release|Any CPU + {5D5A3137-F118-4F6C-ABE7-2523184A3A2D}.Release|x86.ActiveCfg = Release|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDA099C5-E435-49DF-9922-58D63E11B764}.Debug|x64.ActiveCfg = Debug|Any CPU + {BDA099C5-E435-49DF-9922-58D63E11B764}.Debug|x86.ActiveCfg = Debug|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Release|Any CPU.ActiveCfg = Release|Any CPU {BDA099C5-E435-49DF-9922-58D63E11B764}.Release|Any CPU.Build.0 = Release|Any CPU + {BDA099C5-E435-49DF-9922-58D63E11B764}.Release|x64.ActiveCfg = Release|Any CPU + {BDA099C5-E435-49DF-9922-58D63E11B764}.Release|x86.ActiveCfg = Release|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Debug|x64.ActiveCfg = Debug|Any CPU + {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Debug|x86.ActiveCfg = Debug|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Release|Any CPU.ActiveCfg = Release|Any CPU {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Release|Any CPU.Build.0 = Release|Any CPU + {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Release|x64.ActiveCfg = Release|Any CPU + {3DA49C12-3614-40F5-B5CE-8B95F872EFC1}.Release|x86.ActiveCfg = Release|Any CPU {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Debug|x64.ActiveCfg = Debug|Any CPU + {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Debug|x86.ActiveCfg = Debug|Any CPU {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Release|Any CPU.ActiveCfg = Release|Any CPU {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Release|Any CPU.Build.0 = Release|Any CPU + {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Release|x64.ActiveCfg = Release|Any CPU + {9AE188E0-F328-4A88-AF5F-CE0C1D4D0036}.Release|x86.ActiveCfg = Release|Any CPU {66491D47-2BFB-45CF-A582-C11860219512}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66491D47-2BFB-45CF-A582-C11860219512}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66491D47-2BFB-45CF-A582-C11860219512}.Debug|x64.ActiveCfg = Debug|Any CPU + {66491D47-2BFB-45CF-A582-C11860219512}.Debug|x86.ActiveCfg = Debug|Any CPU {66491D47-2BFB-45CF-A582-C11860219512}.Release|Any CPU.ActiveCfg = Release|Any CPU {66491D47-2BFB-45CF-A582-C11860219512}.Release|Any CPU.Build.0 = Release|Any CPU + {66491D47-2BFB-45CF-A582-C11860219512}.Release|x64.ActiveCfg = Release|Any CPU + {66491D47-2BFB-45CF-A582-C11860219512}.Release|x86.ActiveCfg = Release|Any CPU {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Debug|x64.ActiveCfg = Debug|Any CPU + {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Debug|x86.ActiveCfg = Debug|Any CPU {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Release|Any CPU.Build.0 = Release|Any CPU + {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Release|x64.ActiveCfg = Release|Any CPU + {664ECD06-26EB-4F8F-8D88-5444A5E766E0}.Release|x86.ActiveCfg = Release|Any CPU {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Debug|x64.ActiveCfg = Debug|Any CPU + {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Debug|x86.ActiveCfg = Debug|Any CPU {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Release|Any CPU.ActiveCfg = Release|Any CPU {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Release|Any CPU.Build.0 = Release|Any CPU + {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Release|x64.ActiveCfg = Release|Any CPU + {EAE5000B-9A97-4308-B791-0B71DE0A8219}.Release|x86.ActiveCfg = Release|Any CPU {E854F61B-548A-4100-A766-35B972B9EE11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E854F61B-548A-4100-A766-35B972B9EE11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E854F61B-548A-4100-A766-35B972B9EE11}.Debug|x64.ActiveCfg = Debug|Any CPU + {E854F61B-548A-4100-A766-35B972B9EE11}.Debug|x86.ActiveCfg = Debug|Any CPU {E854F61B-548A-4100-A766-35B972B9EE11}.Release|Any CPU.ActiveCfg = Release|Any CPU {E854F61B-548A-4100-A766-35B972B9EE11}.Release|Any CPU.Build.0 = Release|Any CPU + {E854F61B-548A-4100-A766-35B972B9EE11}.Release|x64.ActiveCfg = Release|Any CPU + {E854F61B-548A-4100-A766-35B972B9EE11}.Release|x86.ActiveCfg = Release|Any CPU {22C53CFB-B54A-438E-820F-42C94D557345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22C53CFB-B54A-438E-820F-42C94D557345}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22C53CFB-B54A-438E-820F-42C94D557345}.Debug|x64.ActiveCfg = Debug|Any CPU + {22C53CFB-B54A-438E-820F-42C94D557345}.Debug|x86.ActiveCfg = Debug|Any CPU {22C53CFB-B54A-438E-820F-42C94D557345}.Release|Any CPU.ActiveCfg = Release|Any CPU {22C53CFB-B54A-438E-820F-42C94D557345}.Release|Any CPU.Build.0 = Release|Any CPU + {22C53CFB-B54A-438E-820F-42C94D557345}.Release|x64.ActiveCfg = Release|Any CPU + {22C53CFB-B54A-438E-820F-42C94D557345}.Release|x86.ActiveCfg = Release|Any CPU {A4E85E94-383F-40EA-9478-0545070E52F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A4E85E94-383F-40EA-9478-0545070E52F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4E85E94-383F-40EA-9478-0545070E52F5}.Debug|x64.ActiveCfg = Debug|Any CPU + {A4E85E94-383F-40EA-9478-0545070E52F5}.Debug|x86.ActiveCfg = Debug|Any CPU {A4E85E94-383F-40EA-9478-0545070E52F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {A4E85E94-383F-40EA-9478-0545070E52F5}.Release|Any CPU.Build.0 = Release|Any CPU + {A4E85E94-383F-40EA-9478-0545070E52F5}.Release|x64.ActiveCfg = Release|Any CPU + {A4E85E94-383F-40EA-9478-0545070E52F5}.Release|x86.ActiveCfg = Release|Any CPU {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Debug|x64.ActiveCfg = Debug|Any CPU + {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Debug|x86.ActiveCfg = Debug|Any CPU {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Release|Any CPU.ActiveCfg = Release|Any CPU {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Release|Any CPU.Build.0 = Release|Any CPU + {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Release|x64.ActiveCfg = Release|Any CPU + {928090A9-AAC7-496C-A8E3-D242D2D8BC74}.Release|x86.ActiveCfg = Release|Any CPU {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Debug|x64.ActiveCfg = Debug|Any CPU + {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Debug|x86.ActiveCfg = Debug|Any CPU {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Release|Any CPU.Build.0 = Release|Any CPU + {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Release|x64.ActiveCfg = Release|Any CPU + {D69B9924-5C2E-41BC-9354-A12DA7D03FDF}.Release|x86.ActiveCfg = Release|Any CPU {ED307D87-E1BF-49B6-9591-5250D431C758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED307D87-E1BF-49B6-9591-5250D431C758}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED307D87-E1BF-49B6-9591-5250D431C758}.Debug|x64.ActiveCfg = Debug|Any CPU + {ED307D87-E1BF-49B6-9591-5250D431C758}.Debug|x86.ActiveCfg = Debug|Any CPU {ED307D87-E1BF-49B6-9591-5250D431C758}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED307D87-E1BF-49B6-9591-5250D431C758}.Release|Any CPU.Build.0 = Release|Any CPU + {ED307D87-E1BF-49B6-9591-5250D431C758}.Release|x64.ActiveCfg = Release|Any CPU + {ED307D87-E1BF-49B6-9591-5250D431C758}.Release|x86.ActiveCfg = Release|Any CPU {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Debug|x64.ActiveCfg = Debug|Any CPU + {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Debug|x86.ActiveCfg = Debug|Any CPU {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Release|Any CPU.Build.0 = Release|Any CPU + {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Release|x64.ActiveCfg = Release|Any CPU + {0E4B622A-063B-4A39-87CF-F18AEACBDDC5}.Release|x86.ActiveCfg = Release|Any CPU {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Debug|x64.ActiveCfg = Debug|Any CPU + {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Debug|x86.ActiveCfg = Debug|Any CPU {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Release|Any CPU.Build.0 = Release|Any CPU + {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Release|x64.ActiveCfg = Release|Any CPU + {E2BEAAAE-497B-49F4-AD27-03BE20924311}.Release|x86.ActiveCfg = Release|Any CPU {F2490822-51F7-4B65-8B21-EE0082B76745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F2490822-51F7-4B65-8B21-EE0082B76745}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2490822-51F7-4B65-8B21-EE0082B76745}.Debug|x64.ActiveCfg = Debug|Any CPU + {F2490822-51F7-4B65-8B21-EE0082B76745}.Debug|x86.ActiveCfg = Debug|Any CPU {F2490822-51F7-4B65-8B21-EE0082B76745}.Release|Any CPU.ActiveCfg = Release|Any CPU {F2490822-51F7-4B65-8B21-EE0082B76745}.Release|Any CPU.Build.0 = Release|Any CPU + {F2490822-51F7-4B65-8B21-EE0082B76745}.Release|x64.ActiveCfg = Release|Any CPU + {F2490822-51F7-4B65-8B21-EE0082B76745}.Release|x86.ActiveCfg = Release|Any CPU {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Debug|x64.ActiveCfg = Debug|Any CPU + {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Debug|x86.ActiveCfg = Debug|Any CPU {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Release|Any CPU.ActiveCfg = Release|Any CPU {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Release|Any CPU.Build.0 = Release|Any CPU + {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Release|x64.ActiveCfg = Release|Any CPU + {07C97A77-61B6-4BB5-9436-3A7FC379B75E}.Release|x86.ActiveCfg = Release|Any CPU + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Debug|x64.ActiveCfg = Debug|x64 + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Debug|x64.Build.0 = Debug|x64 + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Debug|x86.ActiveCfg = Debug|x86 + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Debug|x86.Build.0 = Debug|x86 + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Release|Any CPU.Build.0 = Release|Any CPU + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Release|x64.ActiveCfg = Release|x64 + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Release|x64.Build.0 = Release|x64 + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Release|x86.ActiveCfg = Release|x86 + {B984C485-C620-4BD7-8D3B-0C16BE355DA5}.Release|x86.Build.0 = Release|x86 + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Debug|x64.ActiveCfg = Debug|x64 + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Debug|x64.Build.0 = Debug|x64 + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Debug|x86.ActiveCfg = Debug|x86 + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Debug|x86.Build.0 = Debug|x86 + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Release|Any CPU.Build.0 = Release|Any CPU + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Release|x64.ActiveCfg = Release|x64 + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Release|x64.Build.0 = Release|x64 + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Release|x86.ActiveCfg = Release|x86 + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -284,5 +459,7 @@ Global {E2BEAAAE-497B-49F4-AD27-03BE20924311} = {F716F1EF-81EF-4020-914A-5422A13A9E13} {F2490822-51F7-4B65-8B21-EE0082B76745} = {F716F1EF-81EF-4020-914A-5422A13A9E13} {07C97A77-61B6-4BB5-9436-3A7FC379B75E} = {F716F1EF-81EF-4020-914A-5422A13A9E13} + {B984C485-C620-4BD7-8D3B-0C16BE355DA5} = {126EA539-4B28-4B07-8B5D-D1D7F794D189} + {3DE1E4AA-9C2A-4033-9AD5-4DFBB400EC8A} = {541093F6-616E-43D9-B671-FCD1F9C0A181} EndGlobalSection EndGlobal diff --git a/src/DotNetty.Codecs.DNS/DatagramDnsQueryDecoder.cs b/src/DotNetty.Codecs.DNS/DatagramDnsQueryDecoder.cs new file mode 100644 index 000000000..d6db25035 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DatagramDnsQueryDecoder.cs @@ -0,0 +1,86 @@ +using DotNetty.Transport.Channels.Sockets; +using System; +using System.Collections.Generic; +using DotNetty.Transport.Channels; +using DotNetty.Codecs.DNS.Messages; +using DotNetty.Buffers; +using DotNetty.Codecs.DNS.Records; + +namespace DotNetty.Codecs.DNS +{ + public class DatagramDnsQueryDecoder : MessageToMessageDecoder + { + private readonly IDnsRecordDecoder recordDecoder; + + public DatagramDnsQueryDecoder() : this(new DefaultDnsRecordDecoder()) { } + + public DatagramDnsQueryDecoder(IDnsRecordDecoder recordDecoder) + { + this.recordDecoder = recordDecoder ?? throw new ArgumentNullException(nameof(recordDecoder)); + } + + protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List output) + { + IByteBuffer buffer = message.Content; + IDnsQuery query = NewQuery(message, buffer); + bool success = false; + + try + { + int questionCount = buffer.ReadUnsignedShort(); + int answerCount = buffer.ReadUnsignedShort(); + int authorityRecordCount = buffer.ReadUnsignedShort(); + int additionalRecordCount = buffer.ReadUnsignedShort(); + + DecodeQuestions(query, buffer, questionCount); + DecodeRecords(query, DnsSection.ANSWER, buffer, answerCount); + DecodeRecords(query, DnsSection.AUTHORITY, buffer, authorityRecordCount); + DecodeRecords(query, DnsSection.ADDITIONAL, buffer, additionalRecordCount); + + output.Add(query); + success = true; + } + finally + { + if (!success) + query.Release(); + } + } + + private static IDnsQuery NewQuery(DatagramPacket packet, IByteBuffer buffer) + { + int id = buffer.ReadUnsignedShort(); + int flags = buffer.ReadUnsignedShort(); + if (flags >> 15 == 1) + throw new CorruptedFrameException("not a query"); + + IDnsQuery query = new DatagramDnsQuery( + packet.Sender, packet.Recipient, id, + DnsOpCode.From((byte)(flags >> 11 & 0xf))); + + query.IsRecursionDesired = (flags >> 8 & 1) == 1; + query.Z = flags >> 4 & 0x7; + return query; + } + + private void DecodeQuestions(IDnsQuery query, IByteBuffer buffer, int questionCount) + { + for (int i = questionCount; i > 0; i--) + { + query.AddRecord(DnsSection.QUESTION, recordDecoder.DecodeQuestion(buffer)); + } + } + + private void DecodeRecords(IDnsQuery query, DnsSection section, IByteBuffer buffer, int count) + { + for (int i = count; i > 0; i--) + { + IDnsRecord r = recordDecoder.DecodeRecord(buffer); + if (r == null) + break; + + query.AddRecord(section, r); + } + } + } +} diff --git a/src/DotNetty.Codecs.DNS/DatagramDnsQueryEncoder.cs b/src/DotNetty.Codecs.DNS/DatagramDnsQueryEncoder.cs new file mode 100644 index 000000000..4280b8bed --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DatagramDnsQueryEncoder.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using DotNetty.Transport.Channels; +using DotNetty.Codecs.DNS.Messages; +using DotNetty.Buffers; +using DotNetty.Codecs.DNS.Records; +using System.Net; +using DotNetty.Transport.Channels.Sockets; + +namespace DotNetty.Codecs.DNS +{ + public class DatagramDnsQueryEncoder : MessageToMessageEncoder> + { + private readonly IDnsRecordEncoder recordEncoder; + + public DatagramDnsQueryEncoder() : this(new DefaultDnsRecordEncoder()) { } + + public DatagramDnsQueryEncoder(IDnsRecordEncoder recordEncoder) + { + this.recordEncoder = recordEncoder ?? throw new ArgumentNullException(nameof(recordEncoder)); + } + + protected override void Encode(IChannelHandlerContext context, IAddressedEnvelope message, List output) + { + EndPoint recipient = message.Recipient; + IDnsQuery query = message.Content; + IByteBuffer buffer = AllocateBuffer(context, message); + bool success = false; + + try + { + EncodeHeader(query, buffer); + EncodeQuestions(query, buffer); + EncodeRecords(query, DnsSection.ADDITIONAL, buffer); + success = true; + } + finally + { + if (!success) + buffer.Release(); + } + + output.Add(new DatagramPacket(buffer, recipient, null)); + } + + private IByteBuffer AllocateBuffer(IChannelHandlerContext ctx, + IAddressedEnvelope message) => ctx.Allocator.Buffer(1024); + + private void EncodeHeader(IDnsQuery query, IByteBuffer buffer) + { + buffer.WriteShort(query.Id); + int flags = 0; + flags |= (query.OpCode.ByteValue & 0xFF) << 14; + if (query.IsRecursionDesired) + flags |= 1 << 8; + + buffer.WriteShort(flags); + buffer.WriteShort(query.Count(DnsSection.QUESTION)); + buffer.WriteShort(0); + buffer.WriteShort(0); + buffer.WriteShort(query.Count(DnsSection.ADDITIONAL)); + } + + private void EncodeQuestions(IDnsQuery query, IByteBuffer buffer) + { + int count = query.Count(DnsSection.QUESTION); + for (int i = 0; i < count; i++) + { + recordEncoder.EncodeQuestion(query.GetRecord(DnsSection.QUESTION, i), buffer); + } + } + + private void EncodeRecords(IDnsQuery query, DnsSection section, IByteBuffer buffer) + { + int count = query.Count(section); + for (int i = 0; i < count; i++) + { + recordEncoder.EncodeRecord(query.GetRecord(section, i), buffer); + } + } + } +} diff --git a/src/DotNetty.Codecs.DNS/DatagramDnsResponseDecoder.cs b/src/DotNetty.Codecs.DNS/DatagramDnsResponseDecoder.cs new file mode 100644 index 000000000..c39c48da0 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DatagramDnsResponseDecoder.cs @@ -0,0 +1,85 @@ +using DotNetty.Transport.Channels.Sockets; +using System; +using System.Collections.Generic; +using DotNetty.Transport.Channels; +using DotNetty.Codecs.DNS.Messages; +using DotNetty.Buffers; +using DotNetty.Codecs.DNS.Records; + +namespace DotNetty.Codecs.DNS +{ + public class DatagramDnsResponseDecoder : MessageToMessageDecoder + { + private readonly IDnsRecordDecoder recordDecoder; + + public DatagramDnsResponseDecoder() : this(new DefaultDnsRecordDecoder()) { } + + public DatagramDnsResponseDecoder(IDnsRecordDecoder recordDecoder) + { + this.recordDecoder = recordDecoder ?? throw new ArgumentNullException(nameof(recordDecoder)); + } + + protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List output) + { + IByteBuffer buffer = message.Content; + IDnsResponse response = NewResponse(message, buffer); + bool success = false; + + try + { + int questionCount = buffer.ReadUnsignedShort(); + int answerCount = buffer.ReadUnsignedShort(); + int authorityRecordCount = buffer.ReadUnsignedShort(); + int additionalRecordCount = buffer.ReadUnsignedShort(); + + DecodeQuestions(response, buffer, questionCount); + DecodeRecords(response, DnsSection.ANSWER, buffer, answerCount); + DecodeRecords(response, DnsSection.AUTHORITY, buffer, authorityRecordCount); + DecodeRecords(response, DnsSection.ADDITIONAL, buffer, additionalRecordCount); + + output.Add(response); + success = true; + } + finally + { + if (!success) + response.Release(); + } + } + + private static IDnsResponse NewResponse(DatagramPacket packet, IByteBuffer buffer) + { + int id = buffer.ReadUnsignedShort(); + int flags = buffer.ReadUnsignedShort(); + if (flags >> 15 == 0) throw new CorruptedFrameException("not a response"); + + IDnsResponse response = new DatagramDnsResponse( + packet.Sender, + packet.Recipient, + id, + DnsOpCode.From((byte)(flags >> 1 & 0xf)), + DnsResponseCode.From((byte)(flags & 0xf))); + return response; + } + + private void DecodeQuestions(IDnsResponse response, IByteBuffer buffer, int questionCount) + { + for (int i = questionCount - 1; i > 0; i--) + { + response.AddRecord(DnsSection.QUESTION, recordDecoder.DecodeQuestion(buffer)); + } + } + + private void DecodeRecords(IDnsResponse response, DnsSection section, IByteBuffer buffer, int count) + { + for (int i = count - 1; i > 0; i--) + { + IDnsRecord r = recordDecoder.DecodeRecord(buffer); + if (r == null) + break; + + response.AddRecord(section, r); + } + } + } +} diff --git a/src/DotNetty.Codecs.DNS/DatagramDnsResponseEncoder.cs b/src/DotNetty.Codecs.DNS/DatagramDnsResponseEncoder.cs new file mode 100644 index 000000000..721bfd9cf --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DatagramDnsResponseEncoder.cs @@ -0,0 +1,97 @@ +using DotNetty.Buffers; +using DotNetty.Codecs.DNS.Messages; +using DotNetty.Codecs.DNS.Records; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Channels.Sockets; +using System; +using System.Collections.Generic; +using System.Net; + +namespace DotNetty.Codecs.DNS +{ + public class DatagramDnsResponseEncoder : MessageToMessageEncoder> + { + private IDnsRecordEncoder recordEncoder; + + public DatagramDnsResponseEncoder() : this(new DefaultDnsRecordEncoder()) { } + + public DatagramDnsResponseEncoder(IDnsRecordEncoder recordEncoder) + { + this.recordEncoder = recordEncoder ?? throw new ArgumentNullException(nameof(recordEncoder)); + } + + protected override void Encode(IChannelHandlerContext context, IAddressedEnvelope message, List output) + { + EndPoint recipient = message.Recipient; + IDnsResponse response = message.Content; + IByteBuffer buffer = AllocateBuffer(context, message); + + bool success = false; + try + { + EncodeHeader(response, buffer); + EncodeQuestions(response, buffer); + EncodeRecords(response, DnsSection.ANSWER, buffer); + EncodeRecords(response, DnsSection.AUTHORITY, buffer); + EncodeRecords(response, DnsSection.ADDITIONAL, buffer); + success = true; + } + finally + { + if (!success) + buffer.Release(); + } + + output.Add(new DatagramPacket(buffer, recipient, null)); + } + + protected IByteBuffer AllocateBuffer(IChannelHandlerContext ctx, + IAddressedEnvelope message) => ctx.Allocator.Buffer(1024); + + private static void EncodeHeader(IDnsResponse response, IByteBuffer buffer) + { + buffer.WriteShort(response.Id); + int flags = 32768; + flags |= (response.OpCode.ByteValue & 0xFF) << 11; + if (response.IsAuthoritativeAnswer) + flags |= 1 << 10; + + if (response.IsTruncated) + flags |= 1 << 9; + + if (response.IsRecursionDesired) + flags |= 1 << 8; + + if (response.IsRecursionAvailable) + flags |= 1 << 7; + + flags |= response.Z << 4; + flags |= response.Code.IntValue; + buffer.WriteShort(flags); + buffer.WriteShort(response.Count(DnsSection.QUESTION)); + buffer.WriteShort(response.Count(DnsSection.ANSWER)); + buffer.WriteShort(response.Count(DnsSection.AUTHORITY)); + buffer.WriteShort(response.Count(DnsSection.ADDITIONAL)); + } + + private void EncodeQuestions(IDnsResponse response, IByteBuffer buffer) + { + int count = response.Count(DnsSection.QUESTION); + for (int i = 0; i < count; i++) + { + recordEncoder.EncodeQuestion(response.GetRecord(DnsSection.QUESTION, i), buffer); + } + } + + private void EncodeRecords(IDnsResponse response, DnsSection section, IByteBuffer buffer) + { + int count = response.Count(section); + for (int i = 0; i < count; i++) + { + recordEncoder.EncodeRecord(response.GetRecord(section, i), buffer); + } + } + + + } +} diff --git a/src/DotNetty.Codecs.DNS/DefaultDnsRecordDecoder.cs b/src/DotNetty.Codecs.DNS/DefaultDnsRecordDecoder.cs new file mode 100644 index 000000000..f7e2fa24e --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DefaultDnsRecordDecoder.cs @@ -0,0 +1,120 @@ +using System.Text; +using DotNetty.Buffers; +using DotNetty.Codecs.DNS.Records; + +namespace DotNetty.Codecs.DNS +{ + public class DefaultDnsRecordDecoder : IDnsRecordDecoder + { + private const string ROOT = "."; + + internal DefaultDnsRecordDecoder() { } + + public IDnsQuestion DecodeQuestion(IByteBuffer inputBuffer) + { + string name = DecodeName(inputBuffer); + DnsRecordType type = DnsRecordType.From(inputBuffer.ReadUnsignedShort()); + var recordClass = (DnsRecordClass)inputBuffer.ReadUnsignedShort(); + return new DefaultDnsQuestion(name, type, recordClass); + } + + public IDnsRecord DecodeRecord(IByteBuffer inputBuffer) + { + int startOffset = inputBuffer.ReaderIndex; + string name = DecodeName(inputBuffer); + + int endOffset = inputBuffer.WriterIndex; + if (endOffset - startOffset < 10) + { + inputBuffer.SetReaderIndex(startOffset); + return null; + } + + DnsRecordType type = DnsRecordType.From(inputBuffer.ReadUnsignedShort()); + var recordClass = (DnsRecordClass)inputBuffer.ReadUnsignedShort(); + long ttl = inputBuffer.ReadUnsignedInt(); + int length = inputBuffer.ReadUnsignedShort(); + int offset = inputBuffer.ReaderIndex; + + if (endOffset - offset < length) + { + inputBuffer.SetReaderIndex(startOffset); + return null; + } + + IDnsRecord record = DecodeRecord(name, type, recordClass, ttl, inputBuffer, offset, length); + inputBuffer.SetReaderIndex(offset + length); + return record; + } + + protected IDnsRecord DecodeRecord(string name, DnsRecordType type, DnsRecordClass dnsClass, long timeToLive, + IByteBuffer inputBuffer, int offset, int length) + { + if (type == DnsRecordType.PTR) + return new DefaultDnsPtrRecord(name, dnsClass, timeToLive, DecodeName(inputBuffer.SetIndex(offset, offset + length))); + + return new DefaultDnsRawRecord(name, type, dnsClass, timeToLive, inputBuffer.SetIndex(offset, offset + length)); + } + + public static string DecodeName(IByteBuffer inputBuffer) + { + int position = -1; + int @checked = 0; + int end = inputBuffer.WriterIndex; + int readable = inputBuffer.ReadableBytes; + + if (readable == 0) + return ROOT; + + var name = new StringBuilder(readable << 1); + while (inputBuffer.IsReadable()) + { + int len = inputBuffer.ReadUnsignedShort(); + bool pointer = (len & 0xc0) == 0xc0; + + if (pointer) + { + if (position == -1) + position = inputBuffer.ReaderIndex + 1; + + if (!inputBuffer.IsReadable()) + throw new CorruptedFrameException("truncated pointer in a name"); + + int next = (len & 0x3f) << 8 | inputBuffer.ReadUnsignedShort(); + if (next >= end) + throw new CorruptedFrameException("name has an out-of-range pointer"); + + inputBuffer.SetReaderIndex(next); + + @checked += 2; + if (@checked >= end) + throw new CorruptedFrameException("name contains a loop"); + } + else if (len != 0) + { + if (!inputBuffer.IsReadable(len)) + throw new CorruptedFrameException("truncated label in a name"); + + name.Append(inputBuffer.ToString(inputBuffer.ReaderIndex, len, Encoding.UTF8)) + .Append('.'); + inputBuffer.SkipBytes(len); + } + else + { + break; + } + } + + if (position != -1) + inputBuffer.SetReaderIndex(position); + + if (name.Length == 0) + return ROOT; + + if (name[name.Length - 1] != '.') + name.Append('.'); + + return name.ToString(); + } + } +} diff --git a/src/DotNetty.Codecs.DNS/DefaultDnsRecordEncoder.cs b/src/DotNetty.Codecs.DNS/DefaultDnsRecordEncoder.cs new file mode 100644 index 000000000..493aa87b4 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DefaultDnsRecordEncoder.cs @@ -0,0 +1,171 @@ +using System; +using System.Text; +using DotNetty.Buffers; +using DotNetty.Codecs.DNS.Records; +using System.Net.Sockets; +using DotNetty.Common.Utilities; + +namespace DotNetty.Codecs.DNS +{ + public class DefaultDnsRecordEncoder : IDnsRecordEncoder + { + private const int PREFIX_MASK = sizeof(byte) - 1; + private const string ROOT = "."; + + internal DefaultDnsRecordEncoder() { } + + public void EncodeQuestion(IDnsQuestion question, IByteBuffer output) + { + EncodeName(question.Name, output); + output.WriteShort(question.Type.IntValue); + output.WriteShort((int)question.DnsClass); + } + + public void EncodeRecord(IDnsRecord record, IByteBuffer output) + { + if (record is IDnsQuestion) + { + EncodeQuestion((IDnsQuestion)record, output); + } + else if (record is IDnsPtrRecord) + { + EncodePtrRecord((IDnsPtrRecord)record, output); + } + else if (record is IDnsOptEcsRecord) + { + EncodeOptEcsRecord((IDnsOptEcsRecord)record, output); + } + else if (record is IDnsOptPseudoRecord) + { + EncodeOptPseudoRecord((IDnsOptPseudoRecord)record, output); + } + else if (record is IDnsRawRecord) + { + EncodeRawRecord((IDnsRawRecord)record, output); + } + else + { + throw new UnsupportedMessageTypeException(record.Type.Name); + } + } + + private void EncodeRawRecord(IDnsRawRecord record, IByteBuffer output) + { + EncodeRecordCore(record, output); + + IByteBuffer content = record.Content; + int contentLen = content.ReadableBytes; + output.WriteShort(contentLen); + output.WriteBytes(content, content.ReaderIndex, contentLen); + } + + private void EncodeOptPseudoRecord(IDnsOptPseudoRecord record, IByteBuffer output) + { + EncodeRecordCore(record, output); + output.WriteShort(0); + } + + private void EncodeOptEcsRecord(IDnsOptEcsRecord record, IByteBuffer output) + { + EncodeRecordCore(record, output); + + int sourcePrefixLength = record.SourcePrefixLength; + int scopePrefixLength = record.ScopePrefixLength; + int lowOrderBitsToPreserve = sourcePrefixLength & PREFIX_MASK; + + byte[] bytes = record.Address; + int addressBits = bytes.Length << 3; + if (addressBits < sourcePrefixLength || sourcePrefixLength < 0) + throw new ArgumentException($"{sourcePrefixLength}: {sourcePrefixLength} (expected 0 >= {addressBits})"); + + short addressNumber = (short)(bytes.Length == 4 ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6); + int payloadLength = CalculateEcsAddressLength(sourcePrefixLength, lowOrderBitsToPreserve); + int fullPayloadLength = 2 + 2 + 2 + 1 + 1 + payloadLength; + + output.WriteShort(fullPayloadLength); + output.WriteShort(8); + output.WriteShort(fullPayloadLength - 4); + output.WriteShort(addressNumber); + output.WriteByte(sourcePrefixLength); + output.WriteByte(scopePrefixLength); + + if (lowOrderBitsToPreserve > 0) + { + int bytesLength = payloadLength - 1; + output.WriteBytes(bytes, 0, bytesLength); + output.WriteByte(PadWithZeros(bytes[bytesLength], lowOrderBitsToPreserve)); + } + else + { + output.WriteBytes(bytes, 0, payloadLength); + } + } + + private static int CalculateEcsAddressLength(int sourcePrefixLength, int lowOrderBitsToPreserve) + { + return sourcePrefixLength.RightUShift(3) + (lowOrderBitsToPreserve != 0 ? 1 : 0); + } + + private void EncodePtrRecord(IDnsPtrRecord record, IByteBuffer output) + { + EncodeRecordCore(record, output); + EncodeName(record.HostName, output); + } + + private void EncodeRecordCore(IDnsRecord record, IByteBuffer output) + { + EncodeName(record.Name, output); + output.WriteShort(record.Type.IntValue); + output.WriteShort((int)record.DnsClass); + output.WriteInt((int)record.TimeToLive); + } + + protected void EncodeName(string name, IByteBuffer buffer) + { + if (ROOT.Equals(name)) + { + buffer.WriteByte(0); + return; + } + + string[] labels = name.Split('.'); + foreach (var label in labels) + { + int labelLen = label.Length; + if (labelLen == 0) + break; + + buffer.WriteByte(labelLen); + buffer.WriteBytes(Encoding.UTF8.GetBytes(label)); //TODO: Use ByteBufferUtil.WriteAscii() when available + } + buffer.WriteByte(0); + } + + private static byte PadWithZeros(byte b, int lowOrderBitsToPreserve) + { + switch (lowOrderBitsToPreserve) + { + case 0: + return 0; + case 1: + return (byte)(0x01 & b); + case 2: + return (byte)(0x03 & b); + case 3: + return (byte)(0x07 & b); + case 4: + return (byte)(0x0F & b); + case 5: + return (byte)(0x1F & b); + case 6: + return (byte)(0x3F & b); + case 7: + return (byte)(0x7F & b); + case 8: + return b; + default: + throw new ArgumentException($"lowOrderBitsToPreserve: {lowOrderBitsToPreserve}"); + } + } + } +} diff --git a/src/DotNetty.Codecs.DNS/DnsOpCode.cs b/src/DotNetty.Codecs.DNS/DnsOpCode.cs new file mode 100644 index 000000000..0b3943885 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DnsOpCode.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DotNetty.Codecs.DNS +{ + public class DnsOpCode + { + public static readonly DnsOpCode QUERY = new DnsOpCode(0x00, "QUERY"); + + public static readonly DnsOpCode IQUERY = new DnsOpCode(0x01, "IQUERY"); + + public static readonly DnsOpCode STATUS = new DnsOpCode(0x02, "STATUS"); + + public static readonly DnsOpCode NOTIFY = new DnsOpCode(0x04, "NOTIFY"); + + public static readonly DnsOpCode UPDATE = new DnsOpCode(0x05, "UPDATE"); + + public byte ByteValue { get; } + public string Name { get; } + private string text; + + public static DnsOpCode From(int byteValue) + { + switch (byteValue) + { + case 0x00: + return QUERY; + case 0x01: + return IQUERY; + case 0x02: + return STATUS; + case 0x04: + return NOTIFY; + case 0x05: + return UPDATE; + default: + return new DnsOpCode(byteValue); + } + } + + public DnsOpCode(int byteValue, string name = "UNKNOWN") + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + ByteValue = (byte)byteValue; + } + + public override bool Equals(object obj) + { + if (this == obj) + return true; + + if (!(obj is DnsOpCode)) + return false; + + return ByteValue == ((DnsOpCode)obj).ByteValue; + } + + public override int GetHashCode() + { + return ByteValue; + } + + public override string ToString() + { + string text = this.text; + if (string.IsNullOrWhiteSpace(text)) + this.text = text = $"{Name}({ByteValue & 0xFF})"; + + return text; + } + } +} diff --git a/src/DotNetty.Codecs.DNS/DnsSection.cs b/src/DotNetty.Codecs.DNS/DnsSection.cs new file mode 100644 index 000000000..946502a12 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DnsSection.cs @@ -0,0 +1,10 @@ +namespace DotNetty.Codecs.DNS +{ + public enum DnsSection + { + QUESTION, + ANSWER, + AUTHORITY, + ADDITIONAL + } +} diff --git a/src/DotNetty.Codecs.DNS/DotNetty.Codecs.DNS.csproj b/src/DotNetty.Codecs.DNS/DotNetty.Codecs.DNS.csproj new file mode 100644 index 000000000..4d696087f --- /dev/null +++ b/src/DotNetty.Codecs.DNS/DotNetty.Codecs.DNS.csproj @@ -0,0 +1,41 @@ + + + + netstandard1.3;net451 + true + DotNetty.Codecs.DNS + DNS codec for DotNetty + Copyright © Microsoft Corporation + DotNetty: DNS codec + en-US + 0.4.2 + Microsoft Azure + $(NoWarn);CS1591 + false + true + DotNetty.Codecs.DNS + ../../DotNetty.snk + true + true + dns;nameserver;socket;tcp;protocol;netty;dotnetty;network; + https://github.com/Azure/DotNetty/ + https://github.com/Azure/DotNetty/blob/master/LICENSE.txt + git + https://github.com/Azure/DotNetty/ + 1.6.1 + + + + + + + + + + + + + + + + diff --git a/src/DotNetty.Codecs.DNS/IDnsRecordDecoder.cs b/src/DotNetty.Codecs.DNS/IDnsRecordDecoder.cs new file mode 100644 index 000000000..39deb4c62 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/IDnsRecordDecoder.cs @@ -0,0 +1,11 @@ +using DotNetty.Buffers; +using DotNetty.Codecs.DNS.Records; + +namespace DotNetty.Codecs.DNS +{ + public interface IDnsRecordDecoder + { + IDnsQuestion DecodeQuestion(IByteBuffer inputBuffer); + IDnsRecord DecodeRecord(IByteBuffer inputBuffer); + } +} diff --git a/src/DotNetty.Codecs.DNS/IDnsRecordEncoder.cs b/src/DotNetty.Codecs.DNS/IDnsRecordEncoder.cs new file mode 100644 index 000000000..e7521ae81 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/IDnsRecordEncoder.cs @@ -0,0 +1,11 @@ +using DotNetty.Buffers; +using DotNetty.Codecs.DNS.Records; + +namespace DotNetty.Codecs.DNS +{ + public interface IDnsRecordEncoder + { + void EncodeQuestion(IDnsQuestion question, IByteBuffer output); + void EncodeRecord(IDnsRecord record, IByteBuffer output); + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/AbstractDnsMessage.cs b/src/DotNetty.Codecs.DNS/Messages/AbstractDnsMessage.cs new file mode 100644 index 000000000..acbfd68bf --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/AbstractDnsMessage.cs @@ -0,0 +1,323 @@ +using DotNetty.Codecs.DNS.Records; +using DotNetty.Common; +using DotNetty.Common.Utilities; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace DotNetty.Codecs.DNS.Messages +{ + public class AbstractDnsMessage : AbstractReferenceCounted, IDnsMessage + { + private static readonly ResourceLeakDetector leakDetector = ResourceLeakDetector.Create(); + private readonly IResourceLeak leak; + private const DnsSection SECTION_QUESTION = DnsSection.QUESTION; + private const int SECTION_COUNT = 4; + private object questions; + private object answers; + private object authorities; + private object additionals; + + public int Id { get; set; } + public DnsOpCode OpCode { get; set; } + public bool IsRecursionDesired { get; set; } + public int Z { get; set; } + + protected AbstractDnsMessage(int id) : this(id, DnsOpCode.QUERY) { } + + protected AbstractDnsMessage(int id, DnsOpCode opcode) + { + Id = id; + OpCode = opcode; + leak = leakDetector.Open(this); + } + + public int Count() + { + int count = 0; + for (int i = 0; i < SECTION_COUNT; i++) + { + count += Count((DnsSection)i); + } + return count; + } + + public int Count(DnsSection section) + { + object records = SectionAt(section); + if (records == null) + return 0; + + if (records is IDnsRecord) + return 1; + + List recordList = (List)records; + return recordList.Count; + } + + private object SectionAt(DnsSection section) + { + switch (section) + { + case DnsSection.QUESTION: + return questions; + case DnsSection.ANSWER: + return answers; + case DnsSection.AUTHORITY: + return authorities; + case DnsSection.ADDITIONAL: + return additionals; + default: + return null; + } + } + + public void AddRecord(DnsSection section, IDnsRecord record) + { + CheckQuestion(section, record); + + object records = SectionAt(section); + if (records == null) + { + SetSection(section, record); + return; + } + + List recordList; + if (records is IDnsRecord) + { + recordList = new List(2); + recordList.Add((IDnsRecord)records); + recordList.Add(record); + SetSection(section, recordList); + return; + } + + recordList = (List)records; + recordList.Add(record); + } + + public void AddRecord(DnsSection section, int index, IDnsRecord record) + { + CheckQuestion(section, record); + + object records = SectionAt(section); + if (records == null) + { + if (index != 0) + throw new IndexOutOfRangeException($"index: {index} (expected: 0)"); + + SetSection(section, record); + return; + } + + List recordList; + if (records is IDnsRecord) + { + if (index == 0) + { + recordList = new List(); + recordList.Add(record); + recordList.Add((IDnsRecord)records); + } + else if (index == 1) + { + recordList = new List(); + recordList.Add((IDnsRecord)records); + recordList.Add(record); + } + else + { + throw new IndexOutOfRangeException($"index: {index} (expected: 0 or 1)"); + } + SetSection(section, recordList); + return; + } + + recordList = (List)records; + recordList[index] = record; + } + + public void Clear(DnsSection section) + { + object recordOrList = SectionAt(section); + SetSection(section, null); + + if (recordOrList is IReferenceCounted) + { + ((IReferenceCounted)recordOrList).Release(); + } + else if (recordOrList is IList) + { + List list = (List)recordOrList; + if (list.Count == 0) + { + foreach (var r in list) + { + ReferenceCountUtil.Release(r); + } + } + } + } + + public void Clear() + { + for (int i = 0; i < SECTION_COUNT; i++) + { + Clear((DnsSection)i); + } + } + + public TRecord GetRecord(DnsSection section) where TRecord : IDnsRecord + { + object records = SectionAt(section); + if (records == null) + return default(TRecord); + + if (records is IDnsRecord) + return (TRecord)records; + + List recordList = (List)records; + if (recordList.Count == 0) + return default(TRecord); + + return (TRecord)recordList[0]; + } + + public TRecord GetRecord(DnsSection section, int index) where TRecord : IDnsRecord + { + object records = SectionAt(section); + if (records == null) + throw new IndexOutOfRangeException($"index: {index} (expected: none)"); + + if (records is IDnsRecord) + { + if (index == 0) + return (TRecord)records; + + throw new IndexOutOfRangeException($"index: {index} (expected: 0)"); + } + + List recordList = (List)records; + return (TRecord)recordList[index]; + } + + public void RemoveRecord(DnsSection section, int index) + { + object records = SectionAt(section); + if (records == null) + throw new IndexOutOfRangeException($"index: {index} (expected: none)"); + + if (records is IDnsRecord) + { + if (index != 0) + throw new IndexOutOfRangeException($"index: {index} (expected: 0)"); + + SetSection(section, null); + } + + List recordList = (List)records; + recordList.RemoveAt(index); + } + + public void SetRecord(DnsSection section, IDnsRecord record) + { + Clear(section); + SetSection(section, record); + } + + public void SetRecord(DnsSection section, int index, IDnsRecord record) + { + CheckQuestion(section, record); + + object records = SectionAt(section); + if (records == null) + throw new IndexOutOfRangeException($"index: {index} (expected: none)"); + + if (records is IDnsRecord) + { + if (index == 0) + { + SetSection(section, record); + } + else + { + throw new IndexOutOfRangeException($"index: {index} (expected: 0)"); + } + } + + List recordList = (List)records; + recordList[index] = record; + } + + private void SetSection(DnsSection section, object value) + { + switch (section) + { + case DnsSection.QUESTION: + questions = value; + break; + case DnsSection.ANSWER: + answers = value; + break; + case DnsSection.AUTHORITY: + authorities = value; + break; + case DnsSection.ADDITIONAL: + additionals = value; + break; + } + } + + private static void CheckQuestion(DnsSection section, IDnsRecord record) + { + if (section == SECTION_QUESTION && + record != null && + !(record is IDnsQuestion)) + throw new ArgumentException($"record: {record} (expected: DnsQuestion)"); + } + + public override IReferenceCounted Touch(object hint) + { + if (leak != null) + leak.Record(hint); + + return this; + } + + protected override void Deallocate() + { + Clear(); + if (leak != null) + leak.Close(); + } + + public override bool Equals(object obj) + { + if (this == obj) return true; + + if (!(obj is IDnsMessage)) return false; + + IDnsMessage that = (IDnsMessage)obj; + if (Id != that.Id) + return false; + + if (this is IDnsQuestion) + { + if (!(that is IDnsQuestion)) + return false; + } + else if (that is IDnsQuestion) + { + return false; + } + + return true; + } + + public override int GetHashCode() + { + return Id * 31 + (this is IDnsQuestion ? 0 : 1); + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/DatagramDnsQuery.cs b/src/DotNetty.Codecs.DNS/Messages/DatagramDnsQuery.cs new file mode 100644 index 000000000..ad5c3d9c8 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/DatagramDnsQuery.cs @@ -0,0 +1,73 @@ +using DotNetty.Transport.Channels; +using System; +using System.Net; + +namespace DotNetty.Codecs.DNS.Messages +{ + public class DatagramDnsQuery : DefaultDnsQuery, IAddressedEnvelope + { + public DatagramDnsQuery Content => this; + + public EndPoint Sender { get; } + + public EndPoint Recipient { get; } + + public DatagramDnsQuery(EndPoint sender, EndPoint recipient, int id) : this(sender, recipient, id, DnsOpCode.QUERY) { } + + public DatagramDnsQuery(EndPoint sender, EndPoint recipient, int id, DnsOpCode opCode) : base(id, opCode) + { + if (recipient == null && sender == null) + throw new ArgumentNullException("recipient and sender"); + + Sender = sender; + Recipient = recipient; + } + + public override bool Equals(object obj) + { + if (this == obj) + return false; + + if (!base.Equals(obj)) + return false; + + if (!(obj is IAddressedEnvelope)) + return false; + + IAddressedEnvelope that = (IAddressedEnvelope)obj; + if (Sender == null) + { + if (that.Sender != null) + return false; + } + else if (!Sender.Equals(that.Sender)) + { + return false; + } + + if (Recipient == null) + { + if (that.Recipient != null) + return false; + } + else if (!Recipient.Equals(that.Recipient)) + { + return false; + } + + return true; + } + + public override int GetHashCode() + { + int hashCode = base.GetHashCode(); + if (Sender != null) + hashCode = hashCode * 31 + Sender.GetHashCode(); + + if (Recipient != null) + hashCode = hashCode * 31 + Recipient.GetHashCode(); + + return hashCode; + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/DatagramDnsResponse.cs b/src/DotNetty.Codecs.DNS/Messages/DatagramDnsResponse.cs new file mode 100644 index 000000000..4b5b26491 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/DatagramDnsResponse.cs @@ -0,0 +1,77 @@ +using DotNetty.Transport.Channels; +using System; +using System.Net; + +namespace DotNetty.Codecs.DNS.Messages +{ + public class DatagramDnsResponse : DefaultDnsResponse, IAddressedEnvelope + { + public EndPoint Sender { get; } + + public EndPoint Recipient { get; } + + public DatagramDnsResponse Content { get; } + + public DatagramDnsResponse(EndPoint sender, EndPoint recipient, int id) + : this(sender, recipient, id, DnsOpCode.QUERY, DnsResponseCode.NOERROR) { } + + public DatagramDnsResponse(EndPoint sender, EndPoint recipient, int id, DnsOpCode opCode) + : this(sender, recipient, id, opCode, DnsResponseCode.NOERROR) { } + + public DatagramDnsResponse(EndPoint sender, EndPoint recipient, int id, DnsOpCode opCode, DnsResponseCode responseCode) + : base(id, opCode, responseCode) + { + Sender = sender ?? throw new ArgumentNullException(nameof(sender)); + Recipient = recipient ?? throw new ArgumentNullException(nameof(recipient)); + } + + public override bool Equals(object obj) + { + if (this == obj) return true; + + if (!base.Equals(obj)) return false; + + if (!(obj is IAddressedEnvelope)) return false; + + var that = (IAddressedEnvelope)obj; + + if (Sender == null) + { + if (that.Sender != null) + return true; + } + else if (!Sender.Equals(that.Sender)) + { + return false; + } + + if (Recipient == null) + { + if (that.Recipient != null) + return false; + } + else if (!Recipient.Equals(that.Recipient)) + { + return false; + } + + return true; + } + + public override int GetHashCode() + { + int hashCode = base.GetHashCode(); + if (Sender != null) + { + hashCode = hashCode * 31 + Sender.GetHashCode(); + } + + if (Recipient != null) + { + hashCode = hashCode * 31 + Recipient.GetHashCode(); + } + + return hashCode; + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/DefaultDnsQuery.cs b/src/DotNetty.Codecs.DNS/Messages/DefaultDnsQuery.cs new file mode 100644 index 000000000..b30ceec38 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/DefaultDnsQuery.cs @@ -0,0 +1,9 @@ +namespace DotNetty.Codecs.DNS.Messages +{ + public class DefaultDnsQuery : AbstractDnsMessage, IDnsQuery + { + public DefaultDnsQuery(int id) : base(id) { } + + public DefaultDnsQuery(int id, DnsOpCode opCode) : base(id, opCode) { } + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/DefaultDnsResponse.cs b/src/DotNetty.Codecs.DNS/Messages/DefaultDnsResponse.cs new file mode 100644 index 000000000..419bf9271 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/DefaultDnsResponse.cs @@ -0,0 +1,30 @@ +using System; +using System.Text; + +namespace DotNetty.Codecs.DNS.Messages +{ + public class DefaultDnsResponse : AbstractDnsMessage, IDnsResponse + { + public bool IsAuthoritativeAnswer { get; set; } + public bool IsTruncated { get; set; } + public bool IsRecursionAvailable { get; set; } + public DnsResponseCode Code { get; set; } + + public DefaultDnsResponse(int id) + : this(id, DnsOpCode.QUERY, DnsResponseCode.NOERROR) { } + + public DefaultDnsResponse(int id, DnsOpCode opCode) + : this(id, opCode, DnsResponseCode.NOERROR) { } + + public DefaultDnsResponse(int id, DnsOpCode opCode, DnsResponseCode code) + : base(id, opCode) + { + Code = code ?? throw new ArgumentNullException(nameof(code)); + } + + public override string ToString() + { + return new StringBuilder(128).AppendResponse(this).ToString(); + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/DnsResponseCode.cs b/src/DotNetty.Codecs.DNS/Messages/DnsResponseCode.cs new file mode 100644 index 000000000..9f6b40086 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/DnsResponseCode.cs @@ -0,0 +1,105 @@ +using System; + +namespace DotNetty.Codecs.DNS.Messages +{ + public class DnsResponseCode + { + public static DnsResponseCode NOERROR = new DnsResponseCode(0, "NoError"); + public static DnsResponseCode FORMERR = new DnsResponseCode(1, "FormErr"); + public static DnsResponseCode SERVFAIL = new DnsResponseCode(2, "ServFail"); + public static DnsResponseCode NXDOMAIN = new DnsResponseCode(3, "NXDomain"); + public static DnsResponseCode NOTIMP = new DnsResponseCode(4, "NotImp"); + public static DnsResponseCode REFUSED = new DnsResponseCode(5, "Refused"); + public static DnsResponseCode YXDOMAIN = new DnsResponseCode(6, "YXDomain"); + public static DnsResponseCode YXRRSET = new DnsResponseCode(7, "YXRRSet"); + public static DnsResponseCode NXRRSET = new DnsResponseCode(8, "NXRRSet"); + public static DnsResponseCode NOTAUTH = new DnsResponseCode(9, "NotAuth"); + public static DnsResponseCode NOTZONE = new DnsResponseCode(10, "NotZone"); + public static DnsResponseCode BADVERS_OR_BADSIG = new DnsResponseCode(16, "BADVERS_OR_BADSIG"); + public static DnsResponseCode BADKEY = new DnsResponseCode(17, "BADKEY"); + public static DnsResponseCode BADTIME = new DnsResponseCode(18, "BADTIME"); + public static DnsResponseCode BADMODE = new DnsResponseCode(19, "BADMODE"); + public static DnsResponseCode BADNAME = new DnsResponseCode(20, "BADNAME"); + public static DnsResponseCode BADALG = new DnsResponseCode(21, "BADALG"); + + private string text; + + public int IntValue { get; } + public string Name { get; } + + private DnsResponseCode(int code) : this(code, "UNKNOWN") { } + + public DnsResponseCode(int code, string name) + { + if (code < 0 || code > 65535) + throw new ArgumentException($"code: {code} (expected: 0 ~ 65535)"); + + IntValue = code; + Name = name ?? throw new ArgumentNullException(nameof(name)); + } + + public static DnsResponseCode From(int responseCode) + { + switch (responseCode) + { + case 0: + return NOERROR; + case 1: + return FORMERR; + case 2: + return SERVFAIL; + case 3: + return NXDOMAIN; + case 4: + return NOTIMP; + case 5: + return REFUSED; + case 6: + return YXDOMAIN; + case 7: + return YXRRSET; + case 8: + return NXRRSET; + case 9: + return NOTAUTH; + case 10: + return NOTZONE; + case 16: + return BADVERS_OR_BADSIG; + case 17: + return BADKEY; + case 18: + return BADTIME; + case 19: + return BADMODE; + case 20: + return BADNAME; + case 21: + return BADALG; + default: + return new DnsResponseCode(responseCode); + } + } + + public override int GetHashCode() + { + return IntValue; + } + + public override bool Equals(object obj) + { + if (!(obj is DnsResponseCode)) + return false; + + return IntValue == ((DnsResponseCode)obj).IntValue; + } + + public override string ToString() + { + string text = this.text; + if (text == null) + this.text = text = $"{Name} ({IntValue})"; + return text; + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/IDnsMessage.cs b/src/DotNetty.Codecs.DNS/Messages/IDnsMessage.cs new file mode 100644 index 000000000..bbd8c4c7f --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/IDnsMessage.cs @@ -0,0 +1,24 @@ +using DotNetty.Common; +using DotNetty.Codecs.DNS.Records; + +namespace DotNetty.Codecs.DNS.Messages +{ + public interface IDnsMessage : IReferenceCounted + { + int Id { get; set; } + DnsOpCode OpCode { get; set; } + bool IsRecursionDesired { get; set; } + int Z { get; set; } + int Count(DnsSection section); + int Count(); + TRecord GetRecord(DnsSection section) where TRecord : IDnsRecord; + TRecord GetRecord(DnsSection section, int index) where TRecord : IDnsRecord; + void SetRecord(DnsSection section, IDnsRecord record); + void SetRecord(DnsSection section, int index, IDnsRecord record); + void AddRecord(DnsSection section, IDnsRecord record); + void AddRecord(DnsSection section, int index, IDnsRecord record); + void RemoveRecord(DnsSection section, int index); + void Clear(DnsSection section); + void Clear(); + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/IDnsQuery.cs b/src/DotNetty.Codecs.DNS/Messages/IDnsQuery.cs new file mode 100644 index 000000000..806417d9d --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/IDnsQuery.cs @@ -0,0 +1,8 @@ + +namespace DotNetty.Codecs.DNS.Messages +{ + public interface IDnsQuery : IDnsMessage + { + + } +} diff --git a/src/DotNetty.Codecs.DNS/Messages/IDnsResponse.cs b/src/DotNetty.Codecs.DNS/Messages/IDnsResponse.cs new file mode 100644 index 000000000..71335b010 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Messages/IDnsResponse.cs @@ -0,0 +1,10 @@ +namespace DotNetty.Codecs.DNS.Messages +{ + public interface IDnsResponse : IDnsMessage + { + bool IsAuthoritativeAnswer { get; set; } + bool IsTruncated { get; set; } + bool IsRecursionAvailable { get; set; } + DnsResponseCode Code { get; set; } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/AbstractDnsOptPseudoRrRecord.cs b/src/DotNetty.Codecs.DNS/Records/AbstractDnsOptPseudoRrRecord.cs new file mode 100644 index 000000000..07297a90f --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/AbstractDnsOptPseudoRrRecord.cs @@ -0,0 +1,48 @@ +using System.Reflection; +using System.Text; + +namespace DotNetty.Codecs.DNS.Records +{ + public abstract class AbstractDnsOptPseudoRrRecord : AbstractDnsRecord, IDnsOptPseudoRecord + { + private const string EMPTY_STRING = ""; + + protected AbstractDnsOptPseudoRrRecord(int maxPayloadSize, int extendedRcode, int version) + : base(EMPTY_STRING, DnsRecordType.OPT, PackIntoLong(extendedRcode, version), (DnsRecordClass)maxPayloadSize ) { } + + protected AbstractDnsOptPseudoRrRecord(int maxPayloadSize) + : base(EMPTY_STRING, DnsRecordType.OPT, 0, (DnsRecordClass)maxPayloadSize) { } + + private static long PackIntoLong(int val, int val2) + { + return ((val & 0xff) << 24 | (val2 & 0xff) << 16 | (0 & 0xff) << 8 | 0 & 0xff) & 0xFFFFFFFFL; + } + + public int ExtendedRcode => (short) (((int) TimeToLive >> 16) & 0xff); + + public int Version => (short)(((int)TimeToLive >> 16) & 0xff); + + public int Flags => (short)((short)TimeToLive & 0xff); + + public override string ToString() + { + return GetBuilder().ToString(); + } + + protected StringBuilder GetBuilder() + { + return new StringBuilder(64) + .Append(GetType().GetTypeInfo().Name) + .Append('(') + .Append("OPT flags:") + .Append(Flags) + .Append(" version:") + .Append(Version) + .Append(" extendedRecode:") + .Append(ExtendedRcode) + .Append(" udp:") + .Append(DnsClass) + .Append(')'); + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/AbstractDnsRecord.cs b/src/DotNetty.Codecs.DNS/Records/AbstractDnsRecord.cs new file mode 100644 index 000000000..bd8003321 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/AbstractDnsRecord.cs @@ -0,0 +1,100 @@ +using System; +using System.Globalization; +using System.Reflection; +using System.Text; + +namespace DotNetty.Codecs.DNS.Records +{ + public abstract class AbstractDnsRecord : IDnsRecord + { + private readonly IdnMapping idn = new IdnMapping(); + private int hashCode; + + public DnsRecordType Type { get; } + public string Name { get; } + public DnsRecordClass DnsClass { get; } + public long TimeToLive { get; set; } + + protected AbstractDnsRecord(string name, DnsRecordType type, + long timeToLive, DnsRecordClass dnsClass = DnsRecordClass.IN) + { + if (TimeToLive < 0) + throw new ArgumentException($"timeToLive: {timeToLive} (expected: >= 0)"); + + TimeToLive = timeToLive; + + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentNullException(nameof(name)); + + Name = AppendTrailingDot(idn.GetAscii(name)); + Type = type ?? throw new ArgumentNullException(nameof(type)); + DnsClass = dnsClass; + } + + private static string AppendTrailingDot(string name) + { + if (name.Length > 0 && !name.EndsWith(".")) + return name + "."; + + return name; + } + + public override bool Equals(object obj) + { + if (this == obj) + return true; + + if (!(obj is AbstractDnsRecord)) + return false; + + var that = (AbstractDnsRecord)obj; + int hashCode = GetHashCode(); + if (hashCode != 0 && hashCode != that.GetHashCode()) + return false; + + return Type.IntValue == that.Type.IntValue && + DnsClass == that.DnsClass && + Name.Equals(that.Name); + } + + public override int GetHashCode() + { + int hashCode = this.hashCode; + if (hashCode != 0) + return hashCode; + + return this.hashCode = Name.GetHashCode() * 31 + + Type.IntValue * 31 + (int)DnsClass; + + } + + public override string ToString() + { + var builder = new StringBuilder(64); + builder.Append(GetType().GetTypeInfo().Name) + .Append('(') + .Append(Name) + .Append(' ') + .Append(TimeToLive) + .Append(' ') + .AppendRecordClass(DnsClass) + .Append(' ') + .Append(Type.Name) + .Append(')'); + + return builder.ToString(); + } + + } + + public enum DnsRecordClass : int + { + IN = 0x0001, + CSNET = 0x0002, + CHAOS = 0x0003, + HESIOD = 0x0004, + NONE = 0x00fe, + ANY = 0x00ff + } + +} diff --git a/src/DotNetty.Codecs.DNS/Records/DefaultDnsOptEcsRecord.cs b/src/DotNetty.Codecs.DNS/Records/DefaultDnsOptEcsRecord.cs new file mode 100644 index 000000000..43e62bd04 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/DefaultDnsOptEcsRecord.cs @@ -0,0 +1,52 @@ +using System; +using System.Net; +using System.Text; + +namespace DotNetty.Codecs.DNS.Records +{ + public class DefaultDnsOptEcsRecord : AbstractDnsOptPseudoRrRecord, IDnsOptEcsRecord + { + private readonly int srcPrefixLength; + private readonly byte[] address; + + public int SourcePrefixLength { get; } + + public int ScopePrefixLength => 0; + + public byte[] Address => (byte[])address.Clone(); + + public DefaultDnsOptEcsRecord(int maxPayloadSize, int extendedRcode, int version, + int srcPrefixLength, byte[] address) : base(maxPayloadSize, extendedRcode, version) + { + SourcePrefixLength = srcPrefixLength; + address = VerifyAddress(address); + } + + public DefaultDnsOptEcsRecord(int maxPayloadSize, int srcPrefixLength, byte[] address) + : this(maxPayloadSize, 0, 0, srcPrefixLength, address) { } + + public DefaultDnsOptEcsRecord(int maxPayloadSize, IPAddress address) + : this(maxPayloadSize, 0, 0, 0, address.GetAddressBytes()) { } + + private static byte[] VerifyAddress(byte[] bytes) + { + if (bytes.Length == 4 || bytes.Length == 16) + return bytes; + + throw new ArgumentException("bytes.length must either 4 or 16"); + } + + public override string ToString() + { + StringBuilder builder = GetBuilder(); + builder.Length = builder.Length - 1; + return builder.Append(" address:") + .Append(string.Join(".", address, 0, address.Length)) + .Append(" sourcePrefixLength:") + .Append(SourcePrefixLength) + .Append(" scopePrefixLength:") + .Append(ScopePrefixLength) + .Append(')').ToString(); + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/DefaultDnsPtrRecord.cs b/src/DotNetty.Codecs.DNS/Records/DefaultDnsPtrRecord.cs new file mode 100644 index 000000000..fea8f293e --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/DefaultDnsPtrRecord.cs @@ -0,0 +1,39 @@ +using System; +using System.Reflection; +using System.Text; + +namespace DotNetty.Codecs.DNS.Records +{ + public class DefaultDnsPtrRecord : AbstractDnsRecord, IDnsPtrRecord + { + public string HostName { get; } + + public DefaultDnsPtrRecord(string name, DnsRecordClass dnsClass, long timeToLive, string hostname) + : base(name, DnsRecordType.PTR, timeToLive, dnsClass) + { + if (string.IsNullOrWhiteSpace(hostname)) + throw new ArgumentNullException(hostname); + + HostName = hostname; + } + + public override string ToString() + { + var builder = new StringBuilder(64); + + builder.Append(GetType().GetTypeInfo().Name) + .Append('(') + .Append(string.IsNullOrWhiteSpace(Name) ? "" : Name) + .Append(' ') + .Append(TimeToLive) + .Append(' ') + .AppendRecordClass(DnsClass) + .Append(' ') + .Append(Type.Name) + .Append(' ') + .Append(HostName); + + return builder.ToString(); + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/DefaultDnsQuestion.cs b/src/DotNetty.Codecs.DNS/Records/DefaultDnsQuestion.cs new file mode 100644 index 000000000..d05c079a8 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/DefaultDnsQuestion.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Text; + +namespace DotNetty.Codecs.DNS.Records +{ + public class DefaultDnsQuestion : AbstractDnsRecord, IDnsQuestion + { + public DefaultDnsQuestion(string name, + DnsRecordType type, long timeToLive, + DnsRecordClass dnsClass = DnsRecordClass.IN) + : base(name, type, timeToLive, dnsClass) { } + + public DefaultDnsQuestion(string name, DnsRecordType type) : base(name, type, 0) { } + + public DefaultDnsQuestion(string name, DnsRecordType type, DnsRecordClass dnsClass) : + base(name, type, 0, dnsClass){ } + + public override string ToString() + { + var builder = new StringBuilder(64); + + builder.Append(GetType().GetTypeInfo().Name) + .Append('(') + .Append(Name) + .Append(' ') + .AppendRecordClass(DnsClass) + .Append(' ') + .Append(Type.Name) + .Append(')'); + + return builder.ToString(); + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/DefaultDnsRawRecord.cs b/src/DotNetty.Codecs.DNS/Records/DefaultDnsRawRecord.cs new file mode 100644 index 000000000..ed46f00b4 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/DefaultDnsRawRecord.cs @@ -0,0 +1,105 @@ +using System; +using System.Text; +using DotNetty.Buffers; +using DotNetty.Common; +using System.Reflection; + +namespace DotNetty.Codecs.DNS.Records +{ + public class DefaultDnsRawRecord : AbstractDnsRecord, IDnsRawRecord + { + public IByteBuffer Content { get; } + + public int ReferenceCount { get; } + + public DefaultDnsRawRecord(string name, DnsRecordType type, long timeToLive, + IByteBuffer content) : this(name, type, DnsRecordClass.IN, timeToLive, content) + { + } + + public DefaultDnsRawRecord(string name, DnsRecordType type, DnsRecordClass dnsClass, + long timeToLive, IByteBuffer content) : base(name, type, timeToLive, dnsClass) + { + Content = content ?? throw new ArgumentNullException(nameof(content)); + } + + public IByteBufferHolder Copy() + { + return Replace(Content.Copy()); + } + + public IByteBufferHolder Duplicate() + { + return Replace(Content.Duplicate()); + } + + public bool Release() + { + return Content.Release(); + } + + public bool Release(int decrement) + { + return Content.Release(decrement); + } + + public IReferenceCounted Retain() + { + Content.Retain(); + return this; + } + + public IReferenceCounted Retain(int increment) + { + Content.Retain(increment); + return this; + } + + public IReferenceCounted Touch() + { + Content.Touch(); + return this; + } + + public IReferenceCounted Touch(object hint) + { + Content.Touch(hint); + return this; + } + + private IDnsRawRecord Replace(IByteBuffer content) + { + return new DefaultDnsRawRecord(Name, Type, DnsClass, TimeToLive, content); + } + + public override string ToString() + { + var builder = new StringBuilder(64); + builder.Append(GetType().GetTypeInfo().Name).Append('('); + + if (Type != DnsRecordType.OPT) + { + builder.Append(string.IsNullOrWhiteSpace(Name) ? "" : Name) + .Append(' ') + .Append(TimeToLive) + .Append(' ') + .AppendRecordClass(DnsClass) + .Append(' ') + .Append(Type.Name); + } + else + { + builder.Append("OPT flags:") + .Append(TimeToLive) + .Append(" udp:") + .Append(DnsClass); + } + + builder.Append(' ') + .Append(Content.ReadableBytes) + .Append("B)"); + + return builder.ToString(); + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/DnsRecordType.cs b/src/DotNetty.Codecs.DNS/Records/DnsRecordType.cs new file mode 100644 index 000000000..46f22cd1a --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/DnsRecordType.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DotNetty.Codecs.DNS.Records +{ + /// + /// Represents a DNS record type. + /// + public class DnsRecordType + { + #region Types + public static readonly DnsRecordType A = new DnsRecordType(0x0001, "A"); + + public static readonly DnsRecordType NS = new DnsRecordType(0x0002, "NS"); + + public static readonly DnsRecordType CNAME = new DnsRecordType(0x0005, "CNAME"); + + public static readonly DnsRecordType SOA = new DnsRecordType(0x0006, "SOA"); + + public static readonly DnsRecordType PTR = new DnsRecordType(0x000c, "PTR"); + + public static readonly DnsRecordType MX = new DnsRecordType(0x000f, "MX"); + + public static readonly DnsRecordType TXT = new DnsRecordType(0x0010, "TXT"); + + public static readonly DnsRecordType RP = new DnsRecordType(0x0011, "RP"); + + public static readonly DnsRecordType AFSDB = new DnsRecordType(0x0012, "AFSDB"); + + public static readonly DnsRecordType SIG = new DnsRecordType(0x0018, "SIG"); + + public static readonly DnsRecordType KEY = new DnsRecordType(0x0019, "KEY"); + + public static readonly DnsRecordType AAAA = new DnsRecordType(0x001c, "AAAA"); + + public static readonly DnsRecordType LOC = new DnsRecordType(0x001d, "LOC"); + + public static readonly DnsRecordType SRV = new DnsRecordType(0x0021, "SRV"); + + public static readonly DnsRecordType NAPTR = new DnsRecordType(0x0023, "NAPTR"); + + public static readonly DnsRecordType KX = new DnsRecordType(0x0024, "KX"); + + public static readonly DnsRecordType CERT = new DnsRecordType(0x0025, "CERT"); + + public static readonly DnsRecordType DNAME = new DnsRecordType(0x0027, "DNAME"); + + public static readonly DnsRecordType OPT = new DnsRecordType(0x0029, "OPT"); + + public static readonly DnsRecordType APL = new DnsRecordType(0x002a, "APL"); + + public static readonly DnsRecordType DS = new DnsRecordType(0x002b, "DS"); + + public static readonly DnsRecordType SSHFP = new DnsRecordType(0x002c, "SSHFP"); + + public static readonly DnsRecordType IPSECKEY = new DnsRecordType(0x002d, "IPSECKEY"); + + public static readonly DnsRecordType RRSIG = new DnsRecordType(0x002e, "RRSIG"); + + public static readonly DnsRecordType NSEC = new DnsRecordType(0x002f, "NSEC"); + + public static readonly DnsRecordType DNSKEY = new DnsRecordType(0x0030, "DNSKEY"); + + public static readonly DnsRecordType DHCID = new DnsRecordType(0x0031, "DHCID"); + + public static readonly DnsRecordType NSEC3 = new DnsRecordType(0x0032, "NSEC3"); + + public static readonly DnsRecordType NSEC3PARAM = new DnsRecordType(0x0033, "NSEC3PARAM"); + + public static readonly DnsRecordType TLSA = new DnsRecordType(0x0034, "TLSA"); + + public static readonly DnsRecordType HIP = new DnsRecordType(0x0037, "HIP"); + + public static readonly DnsRecordType SPF = new DnsRecordType(0x0063, "SPF"); + + public static readonly DnsRecordType TKEY = new DnsRecordType(0x00f9, "TKEY"); + + public static readonly DnsRecordType TSIG = new DnsRecordType(0x00fa, "TSIG"); + + public static readonly DnsRecordType IXFR = new DnsRecordType(0x00fb, "IXFR"); + + public static readonly DnsRecordType AXFR = new DnsRecordType(0x00fc, "AXFR"); + + public static readonly DnsRecordType ANY = new DnsRecordType(0x00ff, "ANY"); + + public static readonly DnsRecordType CAA = new DnsRecordType(0x0101, "CAA"); + + public static readonly DnsRecordType TA = new DnsRecordType(0x8000, "TA"); + + public static readonly DnsRecordType DLV = new DnsRecordType(0x8001, "DLV"); + #endregion + + private static readonly Dictionary byName = new Dictionary(); + private static readonly Dictionary byType = new Dictionary(); + private static readonly string EXPECTED; + private string text = string.Empty; + + public int IntValue { get; } + public string Name { get; } + + private DnsRecordType(int intValue) : this(intValue, "UNKNOWN") { } + + public DnsRecordType(int intValue, string name) + { + if ((intValue & 0xffff) != intValue) + { + throw new ArgumentException("intValue: " + intValue + " (expected: 0 ~ 65535)"); + } + IntValue = intValue; + Name = name; + } + + static DnsRecordType() + { + DnsRecordType[] all = { + A, NS, CNAME, SOA, PTR, MX, TXT, RP, AFSDB, SIG, KEY, AAAA, LOC, SRV, NAPTR, KX, CERT, DNAME, OPT, APL, + DS, SSHFP, IPSECKEY, RRSIG, NSEC, DNSKEY, DHCID, NSEC3, NSEC3PARAM, TLSA, HIP, SPF, TKEY, TSIG, IXFR, + AXFR, ANY, CAA, TA, DLV + }; + + var expected = new StringBuilder(512); + + expected.Append(" (expected: "); + + foreach (var type in all) + { + byName.Add(type.Name, type); + byType.Add(type.IntValue, type); + + expected.Append(type.Name) + .Append('(') + .Append(type.IntValue) + .Append("), "); + } + + expected.Length = expected.Length - 2; + expected.Append(')'); + EXPECTED = expected.ToString(); + } + + public static DnsRecordType From(int intValue) + { + if (byType.ContainsKey(intValue)) + return byType[intValue]; + + return new DnsRecordType(intValue); + } + + public static DnsRecordType From(string name) + { + if (byName.ContainsKey(name)) + return byName[name]; + + throw new ArgumentException($"name: {name} {EXPECTED}"); + } + + public override int GetHashCode() + { + return IntValue; + } + + public override bool Equals(object obj) + { + return obj is DnsRecordType && ((DnsRecordType)obj).IntValue == IntValue; + } + + public override string ToString() + { + string text = this.text; + if (text == null) + this.text = text = Name + '(' + IntValue + ')'; + + return text; + } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/IDnsOptEcsRecord.cs b/src/DotNetty.Codecs.DNS/Records/IDnsOptEcsRecord.cs new file mode 100644 index 000000000..9dca7a61b --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/IDnsOptEcsRecord.cs @@ -0,0 +1,9 @@ +namespace DotNetty.Codecs.DNS.Records +{ + public interface IDnsOptEcsRecord : IDnsOptPseudoRecord + { + int SourcePrefixLength { get; } + int ScopePrefixLength { get; } + byte[] Address { get; } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/IDnsOptPseudoRecord.cs b/src/DotNetty.Codecs.DNS/Records/IDnsOptPseudoRecord.cs new file mode 100644 index 000000000..82ad7e57f --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/IDnsOptPseudoRecord.cs @@ -0,0 +1,9 @@ +namespace DotNetty.Codecs.DNS.Records +{ + public interface IDnsOptPseudoRecord : IDnsRecord + { + int ExtendedRcode { get; } + int Version { get; } + int Flags { get; } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/IDnsPtrRecord.cs b/src/DotNetty.Codecs.DNS/Records/IDnsPtrRecord.cs new file mode 100644 index 000000000..2d3cc1950 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/IDnsPtrRecord.cs @@ -0,0 +1,8 @@ + +namespace DotNetty.Codecs.DNS.Records +{ + public interface IDnsPtrRecord : IDnsRecord + { + string HostName { get; } + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/IDnsQuestion.cs b/src/DotNetty.Codecs.DNS/Records/IDnsQuestion.cs new file mode 100644 index 000000000..8abab5ba9 --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/IDnsQuestion.cs @@ -0,0 +1,6 @@ +namespace DotNetty.Codecs.DNS.Records +{ + public interface IDnsQuestion : IDnsRecord + { + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/IDnsRawRecord.cs b/src/DotNetty.Codecs.DNS/Records/IDnsRawRecord.cs new file mode 100644 index 000000000..c85d9984e --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/IDnsRawRecord.cs @@ -0,0 +1,8 @@ +using DotNetty.Buffers; + +namespace DotNetty.Codecs.DNS.Records +{ + public interface IDnsRawRecord : IDnsRecord, IByteBufferHolder + { + } +} diff --git a/src/DotNetty.Codecs.DNS/Records/IDnsRecord.cs b/src/DotNetty.Codecs.DNS/Records/IDnsRecord.cs new file mode 100644 index 000000000..5cd8f703e --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Records/IDnsRecord.cs @@ -0,0 +1,10 @@ +namespace DotNetty.Codecs.DNS.Records +{ + public interface IDnsRecord + { + DnsRecordClass DnsClass { get; } + string Name { get; } + long TimeToLive { get; set; } + DnsRecordType Type { get; } + } +} diff --git a/src/DotNetty.Codecs.DNS/Utils.cs b/src/DotNetty.Codecs.DNS/Utils.cs new file mode 100644 index 000000000..12769999c --- /dev/null +++ b/src/DotNetty.Codecs.DNS/Utils.cs @@ -0,0 +1,154 @@ +using DotNetty.Codecs.DNS.Messages; +using DotNetty.Codecs.DNS.Records; +using DotNetty.Transport.Channels; +using System; +using System.Reflection; +using System.Text; + +namespace DotNetty.Codecs.DNS +{ + internal static class Utils + { + internal static StringBuilder AppendRecordClass(this StringBuilder builder, DnsRecordClass dnsClass) + { + string name; + switch (dnsClass) + { + case DnsRecordClass.IN: + name = "IN"; + break; + case DnsRecordClass.CSNET: + name = "CSNET"; + break; + case DnsRecordClass.CHAOS: + name = "CHAOS"; + break; + case DnsRecordClass.HESIOD: + name = "HESIOD"; + break; + case DnsRecordClass.NONE: + name = "NONE"; + break; + case DnsRecordClass.ANY: + name = "ANY"; + break; + default: + name = $"UNKNOWN({dnsClass})"; + break; + } + + builder.Append(name); + return builder; + } + + internal static StringBuilder AppendResponse(this StringBuilder builder, IDnsResponse response) + { + builder.AppendResponseHeader(response) + .AppendAllRecords(response); + return builder; + } + + private static StringBuilder AppendAddresses(this StringBuilder builder, IDnsMessage response) + { + + if (!(response is IAddressedEnvelope)) + return builder; + + IAddressedEnvelope envelope = (IAddressedEnvelope) response; + + var addr = envelope.Sender; + if (addr != null) + { + builder.Append("from: ") + .Append(addr) + .Append(", "); + } + + addr = envelope.Recipient; + if (addr != null) + { + builder.Append("to: ") + .Append(addr) + .Append(", "); + } + + return builder; + } + + internal static StringBuilder AppendResponseHeader(this StringBuilder builder, IDnsResponse response) + { + builder.Append(response.GetType().GetTypeInfo().Name) + .Append('(') + .AppendAddresses(response) + .Append(response.Id) + .Append(", ") + .Append(response.OpCode) + .Append(", ") + .Append(response.Code) + .Append(','); + + bool hasComma = true; + if (response.IsRecursionDesired) + { + hasComma = false; + builder.Append(" RD"); + } + if (response.IsAuthoritativeAnswer) + { + hasComma = false; + builder.Append(" AA"); + } + if (response.IsTruncated) + { + hasComma = false; + builder.Append(" TC"); + } + if (response.IsRecursionAvailable) + { + hasComma = false; + builder.Append(" RA"); + } + if (response.Z != 0) + { + if (!hasComma) + { + builder.Append(','); + } + builder.Append(" Z: ") + .Append(response.Z); + } + + if (hasComma) + { + builder[builder.Length - 1] = ')'; + } + else + { + builder.Append(')'); + } + + return builder; + } + + private static StringBuilder AppendAllRecords(this StringBuilder builder, IDnsMessage msg) + { + return builder.AppendRecords(msg, DnsSection.QUESTION) + .AppendRecords(msg, DnsSection.ANSWER) + .AppendRecords(msg, DnsSection.AUTHORITY) + .AppendRecords(msg, DnsSection.ADDITIONAL); + } + + private static StringBuilder AppendRecords(this StringBuilder builder, IDnsMessage message, DnsSection section) + { + int count = message.Count(section); + for (int i = 0; i < count; i++) + { + builder.Append(Environment.NewLine) + .Append('\t') + .Append(message.GetRecord(section, i)); + } + + return builder; + } + } +} diff --git a/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj b/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj index 603a7a485..b82dcc176 100644 --- a/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj +++ b/test/DotNetty.Buffers.Tests/DotNetty.Buffers.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/test/DotNetty.Codecs.DNS.Tests/DnsCodecTests.cs b/test/DotNetty.Codecs.DNS.Tests/DnsCodecTests.cs new file mode 100644 index 000000000..8821ee47c --- /dev/null +++ b/test/DotNetty.Codecs.DNS.Tests/DnsCodecTests.cs @@ -0,0 +1,16 @@ +using DotNetty.Buffers; +using System; +using Xunit; + +namespace DotNetty.Codecs.DNS.Tests +{ + public class DnsCodecTests + { + static readonly IByteBufferAllocator Allocator = new UnpooledByteBufferAllocator(); + + [Fact] + public void Test1() + { + } + } +} diff --git a/test/DotNetty.Codecs.DNS.Tests/DotNetty.Codecs.DNS.Tests.csproj b/test/DotNetty.Codecs.DNS.Tests/DotNetty.Codecs.DNS.Tests.csproj new file mode 100644 index 000000000..c9dc07139 --- /dev/null +++ b/test/DotNetty.Codecs.DNS.Tests/DotNetty.Codecs.DNS.Tests.csproj @@ -0,0 +1,37 @@ + + + + true + netcoreapp1.1;net451 + false + ../../DotNetty.snk + true + + + win-x64 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/DotNetty.Codecs.Mqtt.Tests/DotNetty.Codecs.Mqtt.Tests.csproj b/test/DotNetty.Codecs.Mqtt.Tests/DotNetty.Codecs.Mqtt.Tests.csproj index 8bfe729ee..c51405d32 100644 --- a/test/DotNetty.Codecs.Mqtt.Tests/DotNetty.Codecs.Mqtt.Tests.csproj +++ b/test/DotNetty.Codecs.Mqtt.Tests/DotNetty.Codecs.Mqtt.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/DotNetty.Codecs.Protobuf.Tests/DotNetty.Codecs.Protobuf.Tests.csproj b/test/DotNetty.Codecs.Protobuf.Tests/DotNetty.Codecs.Protobuf.Tests.csproj index 75b1d0624..bd4259b4f 100644 --- a/test/DotNetty.Codecs.Protobuf.Tests/DotNetty.Codecs.Protobuf.Tests.csproj +++ b/test/DotNetty.Codecs.Protobuf.Tests/DotNetty.Codecs.Protobuf.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/DotNetty.Codecs.ProtocolBuffers.Tests/DotNetty.Codecs.ProtocolBuffers.Tests.csproj b/test/DotNetty.Codecs.ProtocolBuffers.Tests/DotNetty.Codecs.ProtocolBuffers.Tests.csproj index 41910afa7..dad221623 100644 --- a/test/DotNetty.Codecs.ProtocolBuffers.Tests/DotNetty.Codecs.ProtocolBuffers.Tests.csproj +++ b/test/DotNetty.Codecs.ProtocolBuffers.Tests/DotNetty.Codecs.ProtocolBuffers.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/test/DotNetty.Codecs.Redis.Tests/DotNetty.Codecs.Redis.Tests.csproj b/test/DotNetty.Codecs.Redis.Tests/DotNetty.Codecs.Redis.Tests.csproj index e4b106529..3596c62c2 100644 --- a/test/DotNetty.Codecs.Redis.Tests/DotNetty.Codecs.Redis.Tests.csproj +++ b/test/DotNetty.Codecs.Redis.Tests/DotNetty.Codecs.Redis.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/DotNetty.Codecs.Tests/DotNetty.Codecs.Tests.csproj b/test/DotNetty.Codecs.Tests/DotNetty.Codecs.Tests.csproj index b15cc64c5..b1f59d789 100644 --- a/test/DotNetty.Codecs.Tests/DotNetty.Codecs.Tests.csproj +++ b/test/DotNetty.Codecs.Tests/DotNetty.Codecs.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/DotNetty.Common.Tests/DotNetty.Common.Tests.csproj b/test/DotNetty.Common.Tests/DotNetty.Common.Tests.csproj index 609eb9bbf..c36962be1 100644 --- a/test/DotNetty.Common.Tests/DotNetty.Common.Tests.csproj +++ b/test/DotNetty.Common.Tests/DotNetty.Common.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/DotNetty.Handlers.Tests/DotNetty.Handlers.Tests.csproj b/test/DotNetty.Handlers.Tests/DotNetty.Handlers.Tests.csproj index d6b313f3e..42fd596b7 100644 --- a/test/DotNetty.Handlers.Tests/DotNetty.Handlers.Tests.csproj +++ b/test/DotNetty.Handlers.Tests/DotNetty.Handlers.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/DotNetty.Microbench/DotNetty.Microbench.csproj b/test/DotNetty.Microbench/DotNetty.Microbench.csproj index 46022a223..50c0caba3 100644 --- a/test/DotNetty.Microbench/DotNetty.Microbench.csproj +++ b/test/DotNetty.Microbench/DotNetty.Microbench.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/DotNetty.Tests.Common/DotNetty.Tests.Common.csproj b/test/DotNetty.Tests.Common/DotNetty.Tests.Common.csproj index 14de66cf9..0f3321464 100644 --- a/test/DotNetty.Tests.Common/DotNetty.Tests.Common.csproj +++ b/test/DotNetty.Tests.Common/DotNetty.Tests.Common.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/DotNetty.Tests.End2End/DotNetty.Tests.End2End.csproj b/test/DotNetty.Tests.End2End/DotNetty.Tests.End2End.csproj index a59aac2b0..1aa43c434 100644 --- a/test/DotNetty.Tests.End2End/DotNetty.Tests.End2End.csproj +++ b/test/DotNetty.Tests.End2End/DotNetty.Tests.End2End.csproj @@ -10,7 +10,7 @@ win-x64 - + diff --git a/test/DotNetty.Transport.Tests/DotNetty.Transport.Tests.csproj b/test/DotNetty.Transport.Tests/DotNetty.Transport.Tests.csproj index 4a895e1f3..0a13f509a 100644 --- a/test/DotNetty.Transport.Tests/DotNetty.Transport.Tests.csproj +++ b/test/DotNetty.Transport.Tests/DotNetty.Transport.Tests.csproj @@ -14,7 +14,7 @@ - +