View Javadoc
1   /*
2    * Copyright 2024 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.uring;
17  
18  import io.netty.channel.unix.DomainSocketAddress;
19  import io.netty.util.internal.PlatformDependent;
20  
21  import java.net.Inet4Address;
22  import java.net.Inet6Address;
23  import java.net.InetAddress;
24  import java.net.InetSocketAddress;
25  import java.net.UnknownHostException;
26  import java.nio.ByteBuffer;
27  import java.nio.ByteOrder;
28  import java.nio.charset.StandardCharsets;
29  
30  final class SockaddrIn {
31      static final byte[] IPV4_MAPPED_IPV6_PREFIX = {
32              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff };
33      static final int IPV4_ADDRESS_LENGTH = 4;
34      static final int IPV6_ADDRESS_LENGTH = 16;
35      static final byte[] SOCKADDR_IN6_EMPTY_ARRAY = new byte[Native.SIZEOF_SOCKADDR_IN6];
36      static final byte[] SOCKADDR_IN_EMPTY_ARRAY = new byte[Native.SIZEOF_SOCKADDR_IN];
37  
38      private SockaddrIn() { }
39  
40      static int set(boolean ipv6, ByteBuffer memory, InetSocketAddress address) {
41          if (ipv6) {
42              return setIPv6(memory, address.getAddress(), address.getPort());
43          }
44          return setIPv4(memory, address.getAddress(), address.getPort());
45      }
46  
47      /**
48       * <pre>{@code
49       * struct sockaddr_in {
50       *      sa_family_t    sin_family; // address family: AF_INET
51       *      in_port_t      sin_port;   // port in network byte order
52       *      struct in_addr sin_addr;   // internet address
53       * };
54       *
55       * // Internet address.
56       * struct in_addr {
57       *     uint32_t       s_addr;     // address in network byte order
58       * };
59       * }</pre>
60       */
61      static int setIPv4(ByteBuffer memory, InetAddress address, int port) {
62          int position = memory.position();
63          memory.mark();
64          try {
65              // memset
66              memory.put(SOCKADDR_IN_EMPTY_ARRAY);
67  
68              memory.putShort(position + Native.SOCKADDR_IN_OFFSETOF_SIN_FAMILY, Native.AF_INET);
69              memory.putShort(position + Native.SOCKADDR_IN_OFFSETOF_SIN_PORT, handleNetworkOrder(memory.order(),
70                      (short) port));
71  
72              byte[] bytes = address.getAddress();
73              int offset = 0;
74              if (bytes.length == IPV6_ADDRESS_LENGTH) {
75                  // IPV6 mapped IPV4 address, we only need the last 4 bytes.
76                  offset = IPV4_MAPPED_IPV6_PREFIX.length;
77              }
78              assert bytes.length == offset + IPV4_ADDRESS_LENGTH;
79              memory.position(position + Native.SOCKADDR_IN_OFFSETOF_SIN_ADDR + Native.IN_ADDRESS_OFFSETOF_S_ADDR);
80              memory.put(bytes, offset, IPV4_ADDRESS_LENGTH);
81              return Native.SIZEOF_SOCKADDR_IN;
82          } finally {
83              // Restore position as we did change it via memory.put(byte[]...).
84              memory.reset();
85          }
86      }
87  
88      /**
89       * <pre>{@code
90       * struct sockaddr_in6 {
91       *     sa_family_t     sin6_family;   // AF_INET6
92       *     in_port_t       sin6_port;     // port number
93       *     uint32_t        sin6_flowinfo; // IPv6 flow information
94       *     struct in6_addr sin6_addr;     // IPv6 address
95       *     uint32_t        sin6_scope_id; /* Scope ID (new in 2.4)
96       * };
97       *
98       * struct in6_addr {
99       *     unsigned char s6_addr[16];   // IPv6 address
100      * };
101      * }</pre>
102      */
103     static int setIPv6(ByteBuffer memory, InetAddress address, int port) {
104         int position = memory.position();
105         memory.mark();
106         try {
107             // memset
108             memory.put(SOCKADDR_IN6_EMPTY_ARRAY);
109             memory.putShort(position + Native.SOCKADDR_IN6_OFFSETOF_SIN6_FAMILY, Native.AF_INET6);
110             memory.putShort(position + Native.SOCKADDR_IN6_OFFSETOF_SIN6_PORT,
111                     handleNetworkOrder(memory.order(), (short) port));
112             // Skip sin6_flowinfo as we did memset before
113             byte[] bytes = address.getAddress();
114             int offset = Native.SOCKADDR_IN6_OFFSETOF_SIN6_ADDR + Native.IN6_ADDRESS_OFFSETOF_S6_ADDR;
115             if (bytes.length == IPV4_ADDRESS_LENGTH) {
116                 memory.position(position + offset);
117                 memory.put(IPV4_MAPPED_IPV6_PREFIX);
118                 memory.put(bytes, 0, IPV4_ADDRESS_LENGTH);
119 
120                 // Skip sin6_scope_id as we did memset before
121             } else {
122                 memory.position(position + offset);
123                 memory.put(bytes, 0, IPV6_ADDRESS_LENGTH);
124 
125                 memory.putInt(position + Native.SOCKADDR_IN6_OFFSETOF_SIN6_SCOPE_ID,
126                         ((Inet6Address) address).getScopeId());
127             }
128             return Native.SIZEOF_SOCKADDR_IN6;
129         } finally {
130             memory.reset();
131         }
132     }
133 
134     /**
135      * <pre>{@code
136      * struct sockaddr_un {
137      *      sa_family_t sun_family;  // AF_UNIX
138      *      char sun_path[108];      // Pathname
139      * }
140      * }</pre>
141      */
142     static int setUds(ByteBuffer memory, DomainSocketAddress address) {
143         byte[] path = address.path().getBytes(StandardCharsets.UTF_8);
144 
145         // Check if this is an abstract namespace socket (starts with '\0')
146         boolean isAbstract = path.length > 0 && path[0] == 0;
147 
148         // For pathname sockets, we need space for the null terminator
149         // For abstract sockets, we don't add a null terminator
150         int requiredLength = isAbstract ? path.length : path.length + 1;
151 
152         if (requiredLength > Native.MAX_SUN_PATH_LEN) {
153             throw new IllegalArgumentException("path too long: " + address.path());
154         }
155 
156         int position = memory.position();
157         memory.mark();
158         try {
159             memory.putShort(position + Native.SOCKADDR_UN_OFFSETOF_SUN_FAMILY, Native.AF_UNIX);
160             memory.position(position + Native.SOCKADDR_UN_OFFSETOF_SUN_PATH);
161             memory.put(path);
162 
163             // Only add null terminator for pathname sockets, not for abstract sockets
164             if (!isAbstract) {
165                 memory.put((byte) 0);
166             }
167 
168             // Return the actual address length:
169             // - For pathname sockets: offsetof(sun_path) + strlen(path) + 1
170             // - For abstract sockets: offsetof(sun_path) + name_length
171             return Native.SOCKADDR_UN_OFFSETOF_SUN_PATH + requiredLength;
172         } finally {
173             memory.reset();
174         }
175     }
176 
177     static InetSocketAddress getIPv4(ByteBuffer memory, byte[] tmpArray) {
178         assert tmpArray.length == IPV4_ADDRESS_LENGTH;
179         int position = memory.position();
180         memory.mark();
181         try {
182             int port = handleNetworkOrder(memory.order(),
183                     memory.getShort(position + Native.SOCKADDR_IN_OFFSETOF_SIN_PORT)) & 0xFFFF;
184             memory.position(position + Native.SOCKADDR_IN_OFFSETOF_SIN_ADDR + Native.IN_ADDRESS_OFFSETOF_S_ADDR);
185             memory.get(tmpArray);
186             try {
187                 return new InetSocketAddress(InetAddress.getByAddress(tmpArray), port);
188             } catch (UnknownHostException ignore) {
189                 return null;
190             }
191         } finally {
192             memory.reset();
193         }
194     }
195 
196     static InetSocketAddress getIPv6(ByteBuffer memory, byte[] ipv6Array, byte[] ipv4Array) {
197         assert ipv6Array.length == IPV6_ADDRESS_LENGTH;
198         assert ipv4Array.length == IPV4_ADDRESS_LENGTH;
199         int position = memory.position();
200         memory.mark();
201         try {
202             int port = handleNetworkOrder(memory.order(), memory.getShort(
203                     position + Native.SOCKADDR_IN6_OFFSETOF_SIN6_PORT)) & 0xFFFF;
204             memory.position(position + Native.SOCKADDR_IN6_OFFSETOF_SIN6_ADDR + Native.IN6_ADDRESS_OFFSETOF_S6_ADDR);
205             memory.get(ipv6Array);
206             if (PlatformDependent.equals(
207                     ipv6Array, 0, IPV4_MAPPED_IPV6_PREFIX, 0, IPV4_MAPPED_IPV6_PREFIX.length)) {
208                 System.arraycopy(ipv6Array, IPV4_MAPPED_IPV6_PREFIX.length, ipv4Array, 0, IPV4_ADDRESS_LENGTH);
209                 try {
210                     return new InetSocketAddress(Inet4Address.getByAddress(ipv4Array), port);
211                 } catch (UnknownHostException ignore) {
212                     return null;
213                 }
214             } else {
215                 int scopeId = memory.getInt(position + Native.SOCKADDR_IN6_OFFSETOF_SIN6_SCOPE_ID);
216                 try {
217                     return new InetSocketAddress(Inet6Address.getByAddress(null, ipv6Array, scopeId), port);
218                 } catch (UnknownHostException ignore) {
219                     return null;
220                 }
221             }
222         } finally {
223             memory.reset();
224         }
225     }
226 
227     static boolean hasPortIpv4(ByteBuffer memory) {
228         int port = memory.getShort(memory.position() + Native.SOCKADDR_IN_OFFSETOF_SIN_PORT) & 0xFFFF;
229         return port > 0;
230     }
231 
232     static boolean hasPortIpv6(ByteBuffer memory) {
233         int port = memory.getShort(memory.position() + Native.SOCKADDR_IN6_OFFSETOF_SIN6_PORT) & 0xFFFF;
234         return port > 0;
235     }
236 
237     private static short handleNetworkOrder(ByteOrder order, short v) {
238         return order != ByteOrder.nativeOrder() ? v : Short.reverseBytes(v);
239     }
240 }