View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2026 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix.tipc;
19  
20  import java.io.FileDescriptor;
21  import java.io.IOException;
22  import java.math.BigInteger;
23  import java.net.Socket;
24  import java.net.SocketException;
25  
26  import org.newsclub.net.unix.AFSocket;
27  import org.newsclub.net.unix.AFSocketCapability;
28  import org.newsclub.net.unix.AFSocketFactory;
29  import org.newsclub.net.unix.AFTIPCSocketAddress;
30  import org.newsclub.net.unix.AFTIPCSocketImplExtensions;
31  
32  import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
33  
34  /**
35   * Implementation of an {@code AF_TIPC} socket.
36   *
37   * @author Christian Kohlschütter
38   */
39  public final class AFTIPCSocket extends AFSocket<AFTIPCSocketAddress> implements
40      AFTIPCSocketExtensions {
41    private static AFTIPCSocketImplExtensions staticExtensions = null;
42  
43    AFTIPCSocket(FileDescriptor fdObj, AFSocketFactory<AFTIPCSocketAddress> factory)
44        throws SocketException {
45      super(new AFTIPCSocketImpl(fdObj), factory);
46    }
47  
48    private static synchronized AFTIPCSocketImplExtensions getStaticImplExtensions()
49        throws IOException {
50      if (staticExtensions == null) {
51        try (AFTIPCSocket socket = new AFTIPCSocket(null, null)) {
52          staticExtensions = (AFTIPCSocketImplExtensions) socket.getImplExtensions();
53        }
54      }
55      return staticExtensions;
56    }
57  
58    /**
59     * Returns <code>true</code> iff {@link AFTIPCSocket}s (sockets of type "AF_TIPC") are supported
60     * by the current Java VM and the kernel.
61     *
62     * To support {@link AFTIPCSocket}s, a custom JNI library must be loaded that is supplied with
63     * <em>junixsocket</em>, and the system must support AF_TIPC sockets.
64     *
65     * This call is equivalent to checking {@link AFSocket#isSupported()} and
66     * {@link AFSocket#supports(AFSocketCapability)} with {@link AFSocketCapability#CAPABILITY_TIPC}.
67     *
68     * @return {@code true} iff supported.
69     */
70    @SuppressFBWarnings("HSM_HIDING_METHOD")
71    public static boolean isSupported() {
72      return AFSocket.isSupported() && AFSocket.supports(AFSocketCapability.CAPABILITY_TIPC);
73    }
74  
75    @Override
76    protected AFTIPCSocketChannel newChannel() {
77      return new AFTIPCSocketChannel(this);
78    }
79  
80    /**
81     * Creates a new, unbound {@link AFSocket}.
82     *
83     * This "default" implementation is a bit "lenient" with respect to the specification.
84     *
85     * In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and
86     * {@link Socket#setTcpNoDelay(boolean)}.
87     *
88     * @return A new, unbound socket.
89     * @throws IOException if the operation fails.
90     */
91    public static AFTIPCSocket newInstance() throws IOException {
92      return (AFTIPCSocket) AFSocket.newInstance(AFTIPCSocket::new, (AFTIPCSocketFactory) null);
93    }
94  
95    static AFTIPCSocket newInstance(AFTIPCSocketFactory factory) throws SocketException {
96      return (AFTIPCSocket) AFSocket.newInstance(AFTIPCSocket::new, factory);
97    }
98  
99    /**
100    * Creates a new, unbound, "strict" {@link AFSocket}.
101    *
102    * This call uses an implementation that tries to be closer to the specification than
103    * {@link #newInstance()}, at least for some cases.
104    *
105    * @return A new, unbound socket.
106    * @throws IOException if the operation fails.
107    */
108   public static AFTIPCSocket newStrictInstance() throws IOException {
109     return (AFTIPCSocket) AFSocket.newInstance(AFTIPCSocket::new, (AFTIPCSocketFactory) null);
110   }
111 
112   /**
113    * Creates a new {@link AFSocket} and connects it to the given {@link AFTIPCSocketAddress}.
114    *
115    * @param addr The address to connect to.
116    * @return A new, connected socket.
117    * @throws IOException if the operation fails.
118    */
119   public static AFTIPCSocket connectTo(AFTIPCSocketAddress addr) throws IOException {
120     return (AFTIPCSocket) AFSocket.connectTo(AFTIPCSocket::new, addr);
121   }
122 
123   @Override
124   public AFTIPCSocketChannel getChannel() {
125     return (AFTIPCSocketChannel) super.getChannel();
126   }
127 
128   /**
129    * Very basic self-test function.
130    *
131    * Prints "supported" and "capabilities" status to System.out.
132    *
133    * @param args ignored.
134    */
135   public static void main(String[] args) {
136     // If you want to run this directly from within Eclipse, see
137     // org.newsclub.net.unix.tipc.SocketTest#testMain.
138     System.out.print(AFTIPCSocket.class.getName() + ".isSupported(): ");
139     System.out.flush();
140     System.out.println(AFTIPCSocket.isSupported());
141   }
142 
143   @Override
144   public AFTIPCErrInfo getErrInfo() {
145     return AFTIPCErrInfo.of(((AFTIPCSocketImplExtensions) getImplExtensions()).getTIPCErrInfo());
146   }
147 
148   @Override
149   public AFTIPCDestName getDestName() {
150     return AFTIPCDestName.of(((AFTIPCSocketImplExtensions) getImplExtensions()).getTIPCDestName());
151   }
152 
153   /**
154    * Retrieves the 16-byte node ID given a node hash.
155    *
156    * @param peerId The node hash.
157    * @return The node ID, or {@code  null} if unsupported.
158    * @throws IOException on error.
159    */
160   public static byte[] getNodeIdentity(int peerId) throws IOException {
161     return getStaticImplExtensions().getTIPCNodeId(peerId);
162   }
163 
164   /**
165    * Retrieves the TIPC node identity given the node hash of the given address.
166    *
167    * @param address The address.
168    * @return The node identity, or {@code  null} if unsupported.
169    * @throws IOException on error.
170    */
171   public byte[] getNodeIdentity(AFTIPCSocketAddress address) throws IOException {
172     return AFTIPCSocket.getNodeIdentity(address.getTIPCNodeHash());
173   }
174 
175   /**
176    * Retrieves the node ID given a node hash, as a hexadecimal string.
177    *
178    * @param peerId The node hash.
179    * @return The node ID, or {@code  null} if unsupported.
180    * @throws IOException on error.
181    */
182   public static String getNodeIdHexString(int peerId) throws IOException {
183     byte[] id = getNodeIdentity(peerId);
184     return id == null ? null : new BigInteger(1, id).toString(16);
185   }
186 
187   /**
188    * Retrieves the link name given a node hash and a bearer ID.
189    *
190    * @param peerId The node hash.
191    * @param bearerId The bearer Id.
192    * @return The link name, or {@code  null} if unsupported.
193    * @throws IOException on error.
194    */
195   public static String getLinkName(int peerId, int bearerId) throws IOException {
196     return getStaticImplExtensions().getTIPCLinkName(peerId, bearerId);
197   }
198 }