{"record":{"id":"c8467c5e64ab9c2b","repo":"TechnitiumSoftware/DnsServer","slug":"external-to-internal-translation-entries-must-have","errorCode":null,"errorMessage":"External to internal translation entries must have same address family: {strExternal} - {strInternal}","messagePattern":"External to internal translation entries must have same address family: (.+?) - (.+?)","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"Apps/SplitHorizonApp/AddressTranslation.cs","lineNumber":386,"sourceCode":"                _enabled = jsonGroup.GetProperty(\"enabled\").GetBoolean();\n                _translateReverseLookups = jsonGroup.GetProperty(\"translateReverseLookups\").GetBoolean();\n\n                JsonElement jsonExternalToInternalTranslation = jsonGroup.GetProperty(\"externalToInternalTranslation\");\n\n                Dictionary<IPAddress, IPAddress> externalToInternalIpTranslation = new Dictionary<IPAddress, IPAddress>();\n                Dictionary<IPAddress, IPAddress> internalToExternalIpTranslation = new Dictionary<IPAddress, IPAddress>();\n                List<KeyValuePair<NetworkAddress, NetworkAddress>> externalToInternalNetworkTranslation = new List<KeyValuePair<NetworkAddress, NetworkAddress>>();\n\n                foreach (JsonProperty jsonProperty in jsonExternalToInternalTranslation.EnumerateObject())\n                {\n                    string strExternal = jsonProperty.Name;\n                    string strInternal = jsonProperty.Value.GetString();\n\n                    NetworkAddress external = NetworkAddress.Parse(strExternal);\n                    NetworkAddress @internal = NetworkAddress.Parse(strInternal);\n\n                    if (external.AddressFamily != @internal.AddressFamily)\n                        throw new InvalidDataException(\"External to internal translation entries must have same address family: \" + strExternal + \" - \" + strInternal);\n\n                    if (external.PrefixLength != @internal.PrefixLength)\n                        throw new InvalidDataException(\"External to internal translation entries must have same prefix length: \" + strExternal + \" - \" + strInternal);\n\n                    if (\n                        ((external.AddressFamily == AddressFamily.InterNetwork) && (external.PrefixLength == 32)) ||\n                        ((external.AddressFamily == AddressFamily.InterNetworkV6) && (external.PrefixLength == 128))\n                       )\n                    {\n                        externalToInternalIpTranslation.TryAdd(external.Address, @internal.Address);\n\n                        if (_translateReverseLookups)\n                            internalToExternalIpTranslation.TryAdd(@internal.Address, external.Address);\n                    }\n                    else\n                    {\n                        externalToInternalNetworkTranslation.Add(new KeyValuePair<NetworkAddress, NetworkAddress>(external, @internal));\n                    }","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/Apps/SplitHorizonApp/AddressTranslation.cs#L368-L404","documentation":"SplitHorizonApp's external-to-internal translation maps an external network/address to an internal one. NAT-style translation requires both sides to use the same IP address family (both IPv4 or both IPv6). This InvalidDataException fires when the parsed external and internal NetworkAddress values differ in AddressFamily.","triggerScenarios":"In the 'externalToInternalTranslation' (or equivalent) config object, a property name (external) and its value (internal) parse to different families, e.g. external '203.0.113.0/24' (IPv4) mapped to internal 'fd00::/64' (IPv6).","commonSituations":"Mixing IPv4 external addresses with IPv6 internal subnets (or vice versa) when configuring 1:1 translation; copy-pasting examples that used a different family than your actual network.","solutions":["Make both the external key and internal value use the same address family.","If translating IPv4 traffic, pair IPv4-with-IPv4; for IPv6, pair IPv6-with-IPv6.","Set up separate translation entries for each family rather than cross-family mappings."],"exampleFix":"// before\n\"externalToInternalTranslation\": {\n  \"203.0.113.10/32\": \"fd00::10\"\n}\n\n// after\n\"externalToInternalTranslation\": {\n  \"203.0.113.10/32\": \"10.0.0.10\",\n  \"2001:db8::10/128\": \"fd00::10\"\n}","handlingStrategy":"validation","validationCode":"using System.Net;\nusing System.Net.Sockets;\n\nstatic void AssertSameFamily(string ext, string intl)\n{\n    var e = IPAddress.TryParse(ext.Split('/')[0], out var ei) ? ei.AddressFamily : throw new InvalidDataException(\"bad external\");\n    var i = IPAddress.TryParse(intl.Split('/')[0], out var ii) ? ii.AddressFamily : throw new InvalidDataException(\"bad internal\");\n    if (e != i)\n        throw new InvalidDataException($\"Address family mismatch: {ext} - {intl}\");\n}\n\nforeach (var (ext, intl) in translationEntries)\n    AssertSameFamily(ext, intl);","typeGuard":null,"tryCatchPattern":"try { /* load translation config */ }\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"same address family\"))\n{ /* flag the offending entry to the operator */ }","preventionTips":["Pair IPv4 external with IPv4 internal, IPv6 with IPv6, always.","Keep separate translation maps per address family to avoid cross-family mistakes.","Add a pre-deploy config validator that checks family parity."],"tags":["splithorizon","config","networking","nat","validation"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}