1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty5.resolver.dns;
18
19 import io.netty5.util.NetUtil;
20 import io.netty5.util.collection.IntObjectHashMap;
21 import io.netty5.util.collection.IntObjectMap;
22
23 import java.net.Inet4Address;
24 import java.net.Inet6Address;
25 import java.net.InetAddress;
26 import java.net.InetSocketAddress;
27 import java.net.UnknownHostException;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.concurrent.ThreadLocalRandom;
31
32 final class DnsQueryContextManager {
33
34
35
36
37
38 final Map<InetSocketAddress, IntObjectMap<DnsQueryContext>> map = new HashMap<>();
39
40 int add(DnsQueryContext qCtx) {
41 final IntObjectMap<DnsQueryContext> contexts = getOrCreateContextMap(qCtx.nameServerAddr());
42
43 int id = ThreadLocalRandom.current().nextInt(65536 - 1) + 1;
44 final int maxTries = 65535 << 1;
45 int tries = 0;
46
47 synchronized (contexts) {
48 for (;;) {
49 if (!contexts.containsKey(id)) {
50 contexts.put(id, qCtx);
51 return id;
52 }
53
54 id = id + 1 & 0xFFFF;
55
56 if (++tries >= maxTries) {
57 throw new IllegalStateException("query ID space exhausted: " + qCtx.question());
58 }
59 }
60 }
61 }
62
63 DnsQueryContext get(InetSocketAddress nameServerAddr, int id) {
64 final IntObjectMap<DnsQueryContext> contexts = getContextMap(nameServerAddr);
65 final DnsQueryContext qCtx;
66 if (contexts != null) {
67 synchronized (contexts) {
68 qCtx = contexts.get(id);
69 }
70 } else {
71 qCtx = null;
72 }
73
74 return qCtx;
75 }
76
77 DnsQueryContext remove(InetSocketAddress nameServerAddr, int id) {
78 final IntObjectMap<DnsQueryContext> contexts = getContextMap(nameServerAddr);
79 if (contexts == null) {
80 return null;
81 }
82
83 synchronized (contexts) {
84 return contexts.remove(id);
85 }
86 }
87
88 private IntObjectMap<DnsQueryContext> getContextMap(InetSocketAddress nameServerAddr) {
89 synchronized (map) {
90 return map.get(nameServerAddr);
91 }
92 }
93
94 private IntObjectMap<DnsQueryContext> getOrCreateContextMap(InetSocketAddress nameServerAddr) {
95 synchronized (map) {
96 final IntObjectMap<DnsQueryContext> contexts = map.get(nameServerAddr);
97 if (contexts != null) {
98 return contexts;
99 }
100
101 final IntObjectMap<DnsQueryContext> newContexts = new IntObjectHashMap<DnsQueryContext>();
102 final InetAddress a = nameServerAddr.getAddress();
103 final int port = nameServerAddr.getPort();
104 map.put(nameServerAddr, newContexts);
105
106 if (a instanceof Inet4Address) {
107
108 final Inet4Address a4 = (Inet4Address) a;
109 if (a4.isLoopbackAddress()) {
110 map.put(new InetSocketAddress(NetUtil.LOCALHOST6, port), newContexts);
111 } else {
112 map.put(new InetSocketAddress(toCompactAddress(a4), port), newContexts);
113 }
114 } else if (a instanceof Inet6Address) {
115
116 final Inet6Address a6 = (Inet6Address) a;
117 if (a6.isLoopbackAddress()) {
118 map.put(new InetSocketAddress(NetUtil.LOCALHOST4, port), newContexts);
119 } else if (a6.isIPv4CompatibleAddress()) {
120 map.put(new InetSocketAddress(toIPv4Address(a6), port), newContexts);
121 }
122 }
123
124 return newContexts;
125 }
126 }
127
128 private static Inet6Address toCompactAddress(Inet4Address a4) {
129 byte[] b4 = a4.getAddress();
130 byte[] b6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b4[0], b4[1], b4[2], b4[3] };
131 try {
132 return (Inet6Address) InetAddress.getByAddress(b6);
133 } catch (UnknownHostException e) {
134 throw new Error(e);
135 }
136 }
137
138 private static Inet4Address toIPv4Address(Inet6Address a6) {
139 byte[] b6 = a6.getAddress();
140 byte[] b4 = { b6[12], b6[13], b6[14], b6[15] };
141 try {
142 return (Inet4Address) InetAddress.getByAddress(b4);
143 } catch (UnknownHostException e) {
144 throw new Error(e);
145 }
146 }
147 }