/*
* Copyright 2013-2015 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.net;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import ccre.log.Logger;
import ccre.verifier.FlowPhase;
/**
* The global Network handler. This contains a location to store the current
* network provider, and methods to interact with the provider, which will
* automatically detect the provider in many cases.
*
* @author skeggsc
*/
public class Network {
/**
* Connect to the specified IP address and port, and return a ClientSocket
* representing the connection.
*
* @param targetAddress the IP address to connect to.
* @param port the port to connect to.
* @return the ClientSocket that represents the connection.
* @throws IOException if an IO error occurs.
*/
public static ClientSocket connect(String targetAddress, int port) throws IOException {
Socket sock = new Socket();
boolean leaveOpen = false;
try {
InetSocketAddress ina = new InetSocketAddress(targetAddress, port);
try {
sock.connect(ina, 500);// TODO: What timeout should be used?
leaveOpen = true;
return new ClientSocket(sock);
} catch (SocketTimeoutException ex) {
// Smaller traceback.
throw new ConnectException("Timed out while connecting to " + ina);
} catch (ConnectException ctc) {
if (ctc.getMessage().startsWith("Connection timed out")) {
// Smaller traceback.
throw new ConnectException("Timed out while connecting to " + ina);
} else if (ctc.getMessage().startsWith("Connection refused")) {
// Smaller traceback.
throw new ConnectException("Remote server not available: " + ina);
}
throw ctc;
}
} finally {
if (!leaveOpen) {
sock.close();
}
}
}
/**
* This is like connect(targetAddress, port), but if the target address ends
* in a colon followed by a number, that port will be used instead of the
* specified port.
*
* @param rawAddress the IP address to connect to, with an option port
* specifier.
* @param default_port the default port to connect to if there is no port
* specifier.
* @return the ClientSocket that represents the connection.
* @throws java.io.IOException if an IO error occurs or the port specifier
* is invalid.
*/
public static ClientSocket connectDynPort(String rawAddress, int default_port) throws IOException {
int cln = rawAddress.lastIndexOf(':');
int port = default_port;
String realAddress = rawAddress;
if (cln != -1) {
try {
port = Integer.parseInt(rawAddress.substring(cln + 1));
realAddress = rawAddress.substring(0, cln);
} catch (NumberFormatException ex) {
throw new IOException("Cannot connect to address - bad port specifier: " + rawAddress.substring(cln + 1));
}
}
return connect(realAddress, port);
}
/**
* Listen on the specified port, and return a ServerSocket representing the
* connection.
*
* @param port the port to listen on.
* @return the ServerSocket that represents the connection.
* @throws IOException if an IO error occurs.
*/
public static ServerSocket bind(int port) throws IOException {
return new ServerSocket(new java.net.ServerSocket(port));
}
/**
* List all IPv4 addresses of the current system. This includes 127.0.0.1.
*
* @return a collection of the IPv4 addresses of the current system.
*/
@FlowPhase
public static Collection<String> listIPv4Addresses() {
Enumeration<NetworkInterface> enm = null;
try {
enm = NetworkInterface.getNetworkInterfaces();
} catch (SocketException ex) {
Logger.severe("Could not enumerate IP addresses!", ex);
}
if (enm == null) {
return Collections.emptyList();
}
ArrayList<String> allAddresses = new ArrayList<String>();
while (enm.hasMoreElements()) {
NetworkInterface ni = enm.nextElement();
Enumeration<InetAddress> ins = ni.getInetAddresses();
while (ins.hasMoreElements()) {
InetAddress addr = ins.nextElement();
byte[] raw = addr.getAddress();
if (raw.length == 4) {
allAddresses.add(addr.getHostAddress());
} else if (raw.length != 16) {
Logger.warning("Found an address that's not 4 or 16 long: " + Arrays.toString(raw));
}
}
}
return allAddresses;
}
private Network() {
}
}