001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.named.ipc;
020
021import java.io.IOException;
022import java.net.Inet6Address;
023import java.net.InetAddress;
024import java.net.InetSocketAddress;
025import java.net.SocketAddress;
026import java.net.StandardProtocolFamily;
027import java.net.UnixDomainSocketAddress;
028import java.net.UnknownHostException;
029import java.nio.channels.ServerSocketChannel;
030
031/**
032 * Socket factory.
033 *
034 * @since 2.0.1
035 */
036public enum SocketFamily {
037    inet,
038    unix;
039
040    public ServerSocketChannel openServerSocket() throws IOException {
041        switch (this) {
042            case inet:
043                return ServerSocketChannel.open().bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
044            case unix:
045                return ServerSocketChannel.open(StandardProtocolFamily.UNIX).bind(null, 0);
046            default:
047                throw new IllegalStateException();
048        }
049    }
050
051    public static SocketAddress fromString(String str) {
052        if (str.startsWith("inet:")) {
053            String s = str.substring("inet:".length());
054            int ic = s.lastIndexOf(':');
055            String ia = s.substring(0, ic);
056            int is = ia.indexOf('/');
057            String h = ia.substring(0, is);
058            String a = ia.substring(is + 1);
059            String p = s.substring(ic + 1);
060            InetAddress addr;
061            if ("<unresolved>".equals(a)) {
062                return InetSocketAddress.createUnresolved(h, Integer.parseInt(p));
063            } else {
064                if (a.indexOf('.') > 0) {
065                    String[] as = a.split("\\.");
066                    if (as.length != 4) {
067                        throw new IllegalArgumentException("Unsupported socket address: '" + str + "'");
068                    }
069                    byte[] ab = new byte[4];
070                    for (int i = 0; i < 4; i++) {
071                        ab[i] = (byte) Integer.parseInt(as[i]);
072                    }
073                    try {
074                        addr = InetAddress.getByAddress(h.isEmpty() ? null : h, ab);
075                    } catch (UnknownHostException e) {
076                        throw new IllegalArgumentException("Unsupported address: " + str, e);
077                    }
078                } else {
079                    throw new IllegalArgumentException("Unsupported address: " + str);
080                }
081                return new InetSocketAddress(addr, Integer.parseInt(p));
082            }
083        } else if (str.startsWith("unix:")) {
084            return UnixDomainSocketAddress.of(str.substring("unix:".length()));
085        } else {
086            throw new IllegalArgumentException("Unsupported socket address: '" + str + "'");
087        }
088    }
089
090    public static String toString(SocketAddress address) {
091        switch (familyOf(address)) {
092            case inet:
093                InetSocketAddress isa = (InetSocketAddress) address;
094                String host = isa.getHostString();
095                InetAddress addr = isa.getAddress();
096                int port = isa.getPort();
097                String formatted;
098                if (addr == null) {
099                    formatted = host + "/<unresolved>";
100                } else {
101                    formatted = addr.toString();
102                    if (addr instanceof Inet6Address) {
103                        int i = formatted.lastIndexOf("/");
104                        formatted = formatted.substring(0, i + 1) + "[" + formatted.substring(i + 1) + "]";
105                    }
106                }
107                return "inet:" + formatted + ":" + port;
108            case unix:
109                // to keep address string unchanged across all OSes
110                return "unix:" + address.toString().replace('\\', '/');
111            default:
112                throw new IllegalArgumentException("Unsupported socket address: '" + address + "'");
113        }
114    }
115
116    public static SocketFamily familyOf(SocketAddress address) {
117        if (address instanceof InetSocketAddress) {
118            return SocketFamily.inet;
119        } else if ("java.net.UnixDomainSocketAddress".equals(address.getClass().getName())) {
120            return SocketFamily.unix;
121        } else {
122            throw new IllegalArgumentException("Unsupported socket address '" + address + "'");
123        }
124    }
125}