1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.ssl;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.channel.ChannelHandlerContext;
20 import io.netty5.util.concurrent.Future;
21
22 import java.nio.charset.StandardCharsets;
23 import java.util.Locale;
24
25
26
27
28
29
30
31
32 public abstract class AbstractSniHandler<T> extends SslClientHelloHandler<T> {
33
34 private static String extractSniHostname(Buffer in) {
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 int offset = in.readerOffset();
56 int endOffset = in.writerOffset();
57 offset += 34;
58
59 if (endOffset - offset >= 6) {
60 final int sessionIdLength = in.getUnsignedByte(offset);
61 offset += sessionIdLength + 1;
62
63 final int cipherSuitesLength = in.getUnsignedShort(offset);
64 offset += cipherSuitesLength + 2;
65
66 final int compressionMethodLength = in.getUnsignedByte(offset);
67 offset += compressionMethodLength + 1;
68
69 final int extensionsLength = in.getUnsignedShort(offset);
70 offset += 2;
71 final int extensionsLimit = offset + extensionsLength;
72
73
74 if (extensionsLimit <= endOffset) {
75 while (extensionsLimit - offset >= 4) {
76 final int extensionType = in.getUnsignedShort(offset);
77 offset += 2;
78
79 final int extensionLength = in.getUnsignedShort(offset);
80 offset += 2;
81
82 if (extensionsLimit - offset < extensionLength) {
83 break;
84 }
85
86
87
88 if (extensionType == 0) {
89 offset += 2;
90 if (extensionsLimit - offset < 3) {
91 break;
92 }
93
94 final int serverNameType = in.getUnsignedByte(offset);
95 offset++;
96
97 if (serverNameType == 0) {
98 final int serverNameLength = in.getUnsignedShort(offset);
99 offset += 2;
100
101 if (extensionsLimit - offset < serverNameLength) {
102 break;
103 }
104
105 String hostname = in.copy(offset, serverNameLength).toString(StandardCharsets.US_ASCII);
106 return hostname.toLowerCase(Locale.US);
107 } else {
108
109 break;
110 }
111 }
112
113 offset += extensionLength;
114 }
115 }
116 }
117 return null;
118 }
119
120 private String hostname;
121
122 @Override
123 protected Future<T> lookup(ChannelHandlerContext ctx, Buffer clientHello) throws Exception {
124 hostname = clientHello == null ? null : extractSniHostname(clientHello);
125
126 return lookup(ctx, hostname);
127 }
128
129 @Override
130 protected void onLookupComplete(ChannelHandlerContext ctx, Future<? extends T> future) throws Exception {
131 try {
132 onLookupComplete(ctx, hostname, future);
133 } finally {
134 fireSniCompletionEvent(
135
136
137 ctx.isRemoved() ? ctx.pipeline().firstContext() : ctx, hostname, future);
138 }
139 }
140
141
142
143
144
145
146
147 protected abstract Future<T> lookup(ChannelHandlerContext ctx, String hostname) throws Exception;
148
149
150
151
152
153
154 protected abstract void onLookupComplete(ChannelHandlerContext ctx,
155 String hostname, Future<? extends T> future) throws Exception;
156
157 private static void fireSniCompletionEvent(ChannelHandlerContext ctx, String hostname, Future<?> future) {
158 Throwable cause = future.cause();
159 if (cause == null) {
160 ctx.fireChannelInboundEvent(new SniCompletionEvent(hostname));
161 } else {
162 ctx.fireChannelInboundEvent(new SniCompletionEvent(hostname, cause));
163 }
164 }
165 }