// // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // package com.cloud.utils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.Inet6Address; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import java.util.StringTokenizer; import javax.net.ssl.HttpsURLConnection; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.HeadMethod; import org.apache.commons.httpclient.util.URIUtil; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; import org.apache.log4j.Logger; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.exception.CloudRuntimeException; public class UriUtils { public static final Logger s_logger = Logger.getLogger(UriUtils.class.getName()); public static String formNfsUri(String host, String path) { try { URI uri = new URI("nfs", host, path, null); return uri.toString(); } catch (URISyntaxException e) { throw new CloudRuntimeException("Unable to form nfs URI: " + host + " - " + path); } } public static String formIscsiUri(String host, String iqn, Integer lun) { try { String path = iqn; if (lun != null) { path += "/" + lun.toString(); } URI uri = new URI("iscsi", host, path, null); return uri.toString(); } catch (URISyntaxException e) { throw new CloudRuntimeException("Unable to form iscsi URI: " + host + " - " + iqn + " - " + lun); } } public static String formFileUri(String path) { File file = new File(path); return file.toURI().toString(); } // a simple URI component helper (Note: it does not deal with URI paramemeter area) public static String encodeURIComponent(String url) { int schemeTail = url.indexOf("://"); int pathStart = 0; if (schemeTail > 0) pathStart = url.indexOf('/', schemeTail + 3); else pathStart = url.indexOf('/'); if (pathStart > 0) { String[] tokens = url.substring(pathStart + 1).split("/"); StringBuilder sb = new StringBuilder(url.substring(0, pathStart)); for (String token : tokens) { sb.append("/").append(URLEncoder.encode(token)); } return sb.toString(); } // no need to do URL component encoding return url; } public static String getCifsUriParametersProblems(URI uri) { if (!UriUtils.hostAndPathPresent(uri)) { String errMsg = "cifs URI missing host and/or path. Make sure it's of the format cifs://hostname/path"; s_logger.warn(errMsg); return errMsg; } return null; } public static boolean hostAndPathPresent(URI uri) { return !(uri.getHost() == null || uri.getHost().trim().isEmpty() || uri.getPath() == null || uri.getPath().trim().isEmpty()); } public static boolean cifsCredentialsPresent(URI uri) { List<NameValuePair> args = URLEncodedUtils.parse(uri, "UTF-8"); boolean foundUser = false; boolean foundPswd = false; for (NameValuePair nvp : args) { String name = nvp.getName(); if (name.equals("user")) { foundUser = true; s_logger.debug("foundUser is" + foundUser); } else if (name.equals("password")) { foundPswd = true; s_logger.debug("foundPswd is" + foundPswd); } } return (foundUser && foundPswd); } public static String getUpdateUri(String url, boolean encrypt) { String updatedPath = null; try { String query = URIUtil.getQuery(url); URIBuilder builder = new URIBuilder(url); builder.removeQuery(); StringBuilder updatedQuery = new StringBuilder(); List<NameValuePair> queryParams = getUserDetails(query); ListIterator<NameValuePair> iterator = queryParams.listIterator(); while (iterator.hasNext()) { NameValuePair param = iterator.next(); String value = null; if ("password".equalsIgnoreCase(param.getName()) && param.getValue() != null) { value = encrypt ? DBEncryptionUtil.encrypt(param.getValue()) : DBEncryptionUtil.decrypt(param.getValue()); } else { value = param.getValue(); } if (updatedQuery.length() == 0) { updatedQuery.append(param.getName()).append('=') .append(value); } else { updatedQuery.append('&').append(param.getName()) .append('=').append(value); } } String schemeAndHost = ""; URI newUri = builder.build(); if (newUri.getScheme() != null) { schemeAndHost = newUri.getScheme() + "://" + newUri.getHost(); } updatedPath = schemeAndHost + newUri.getPath() + "?" + updatedQuery; } catch (URISyntaxException e) { throw new CloudRuntimeException("Couldn't generate an updated uri. " + e.getMessage()); } return updatedPath; } private static List<NameValuePair> getUserDetails(String query) { List<NameValuePair> details = new ArrayList<NameValuePair>(); if (query != null && !query.isEmpty()) { StringTokenizer allParams = new StringTokenizer(query, "&"); while (allParams.hasMoreTokens()) { String param = allParams.nextToken(); details.add(new BasicNameValuePair(param.substring(0, param.indexOf("=")), param.substring(param.indexOf("=") + 1))); } } return details; } // Get the size of a file from URL response header. public static Long getRemoteSize(String url) { Long remoteSize = (long)0; HttpURLConnection httpConn = null; HttpsURLConnection httpsConn = null; try { URI uri = new URI(url); if (uri.getScheme().equalsIgnoreCase("http")) { httpConn = (HttpURLConnection)uri.toURL().openConnection(); if (httpConn != null) { httpConn.setConnectTimeout(2000); httpConn.setReadTimeout(5000); String contentLength = httpConn.getHeaderField("content-length"); if (contentLength != null) { remoteSize = Long.parseLong(contentLength); } httpConn.disconnect(); } } else if (uri.getScheme().equalsIgnoreCase("https")) { httpsConn = (HttpsURLConnection)uri.toURL().openConnection(); if (httpsConn != null) { String contentLength = httpsConn.getHeaderField("content-length"); if (contentLength != null) { remoteSize = Long.parseLong(contentLength); } httpsConn.disconnect(); } } } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid URL " + url); } catch (IOException e) { throw new IllegalArgumentException("Unable to establish connection with URL " + url); } return remoteSize; } public static Pair<String, Integer> validateUrl(String url) throws IllegalArgumentException { return validateUrl(null, url); } public static Pair<String, Integer> validateUrl(String format, String url) throws IllegalArgumentException { try { URI uri = new URI(url); if ((uri.getScheme() == null) || (!uri.getScheme().equalsIgnoreCase("http") && !uri.getScheme().equalsIgnoreCase("https") && !uri.getScheme().equalsIgnoreCase("file"))) { throw new IllegalArgumentException("Unsupported scheme for url: " + url); } int port = uri.getPort(); if (!(port == 80 || port == 8080 || port == 443 || port == -1)) { throw new IllegalArgumentException("Only ports 80, 8080 and 443 are allowed"); } if (port == -1 && uri.getScheme().equalsIgnoreCase("https")) { port = 443; } else if (port == -1 && uri.getScheme().equalsIgnoreCase("http")) { port = 80; } String host = uri.getHost(); try { InetAddress hostAddr = InetAddress.getByName(host); if (hostAddr.isAnyLocalAddress() || hostAddr.isLinkLocalAddress() || hostAddr.isLoopbackAddress() || hostAddr.isMulticastAddress()) { throw new IllegalArgumentException("Illegal host specified in url"); } if (hostAddr instanceof Inet6Address) { throw new IllegalArgumentException("IPV6 addresses not supported (" + hostAddr.getHostAddress() + ")"); } } catch (UnknownHostException uhe) { throw new IllegalArgumentException("Unable to resolve " + host); } // verify format if (format != null) { String uripath = uri.getPath(); checkFormat(format, uripath); } return new Pair<String, Integer>(host, port); } catch (URISyntaxException use) { throw new IllegalArgumentException("Invalid URL: " + url); } } // use http HEAD method to validate url public static void checkUrlExistence(String url) { if (url.toLowerCase().startsWith("http") || url.toLowerCase().startsWith("https")) { HttpClient httpClient = new HttpClient(new MultiThreadedHttpConnectionManager()); HeadMethod httphead = new HeadMethod(url); try { if (httpClient.executeMethod(httphead) != HttpStatus.SC_OK) { throw new IllegalArgumentException("Invalid URL: " + url); } } catch (HttpException hte) { throw new IllegalArgumentException("Cannot reach URL: " + url); } catch (IOException ioe) { throw new IllegalArgumentException("Cannot reach URL: " + url); } } } // verify if a URI path is compliance with the file format given private static void checkFormat(String format, String uripath) { if ((!uripath.toLowerCase().endsWith("vhd")) && (!uripath.toLowerCase().endsWith("vhd.zip")) && (!uripath.toLowerCase().endsWith("vhd.bz2")) && (!uripath.toLowerCase().endsWith("vhdx")) && (!uripath.toLowerCase().endsWith("vhdx.gz")) && (!uripath.toLowerCase().endsWith("vhdx.bz2")) && (!uripath.toLowerCase().endsWith("vhdx.zip")) && (!uripath.toLowerCase().endsWith("vhd.gz")) && (!uripath.toLowerCase().endsWith("qcow2")) && (!uripath.toLowerCase().endsWith("qcow2.zip")) && (!uripath.toLowerCase().endsWith("qcow2.bz2")) && (!uripath.toLowerCase().endsWith("qcow2.gz")) && (!uripath.toLowerCase().endsWith("ova")) && (!uripath.toLowerCase().endsWith("ova.zip")) && (!uripath.toLowerCase().endsWith("ova.bz2")) && (!uripath.toLowerCase().endsWith("ova.gz")) && (!uripath.toLowerCase().endsWith("tar")) && (!uripath.toLowerCase().endsWith("tar.zip")) && (!uripath.toLowerCase().endsWith("tar.bz2")) && (!uripath.toLowerCase().endsWith("tar.gz")) && (!uripath.toLowerCase().endsWith("vmdk")) && (!uripath.toLowerCase().endsWith("vmdk.gz")) && (!uripath.toLowerCase().endsWith("vmdk.zip")) && (!uripath.toLowerCase().endsWith("vmdk.bz2")) && (!uripath.toLowerCase().endsWith("img")) && (!uripath.toLowerCase().endsWith("img.gz")) && (!uripath.toLowerCase().endsWith("img.zip")) && (!uripath.toLowerCase().endsWith("img.bz2")) && (!uripath.toLowerCase().endsWith("raw")) && (!uripath.toLowerCase().endsWith("raw.gz")) && (!uripath.toLowerCase().endsWith("raw.bz2")) && (!uripath.toLowerCase().endsWith("raw.zip")) && (!uripath.toLowerCase().endsWith("iso")) && (!uripath.toLowerCase().endsWith("iso.zip")) && (!uripath.toLowerCase().endsWith("iso.bz2")) && (!uripath.toLowerCase().endsWith("iso.gz"))) { throw new IllegalArgumentException("Please specify a valid " + format.toLowerCase()); } if ((format.equalsIgnoreCase("vhd") && (!uripath.toLowerCase().endsWith("vhd") && !uripath.toLowerCase().endsWith("vhd.zip") && !uripath.toLowerCase().endsWith("vhd.bz2") && !uripath.toLowerCase().endsWith("vhd.gz"))) || (format.equalsIgnoreCase("vhdx") && (!uripath.toLowerCase().endsWith("vhdx") && !uripath.toLowerCase().endsWith("vhdx.zip") && !uripath.toLowerCase().endsWith("vhdx.bz2") && !uripath.toLowerCase().endsWith("vhdx.gz"))) || (format.equalsIgnoreCase("qcow2") && (!uripath.toLowerCase().endsWith("qcow2") && !uripath.toLowerCase().endsWith("qcow2.zip") && !uripath.toLowerCase().endsWith("qcow2.bz2") && !uripath.toLowerCase().endsWith("qcow2.gz"))) || (format.equalsIgnoreCase("ova") && (!uripath.toLowerCase().endsWith("ova") && !uripath.toLowerCase().endsWith("ova.zip") && !uripath.toLowerCase().endsWith("ova.bz2") && !uripath.toLowerCase().endsWith("ova.gz"))) || (format.equalsIgnoreCase("tar") && (!uripath.toLowerCase().endsWith("tar") && !uripath.toLowerCase().endsWith("tar.zip") && !uripath.toLowerCase().endsWith("tar.bz2") && !uripath.toLowerCase().endsWith("tar.gz"))) || (format.equalsIgnoreCase("raw") && (!uripath.toLowerCase().endsWith("img") && !uripath.toLowerCase().endsWith("img.zip") && !uripath.toLowerCase().endsWith("img.bz2") && !uripath.toLowerCase().endsWith("img.gz") && !uripath.toLowerCase().endsWith("raw") && !uripath.toLowerCase().endsWith("raw.bz2") && !uripath.toLowerCase().endsWith("raw.zip") && !uripath.toLowerCase().endsWith("raw.gz"))) || (format.equalsIgnoreCase("vmdk") && (!uripath.toLowerCase().endsWith("vmdk") && !uripath.toLowerCase().endsWith("vmdk.zip") && !uripath.toLowerCase().endsWith("vmdk.bz2") && !uripath.toLowerCase().endsWith("vmdk.gz"))) || (format.equalsIgnoreCase("iso") && (!uripath.toLowerCase().endsWith("iso") && !uripath.toLowerCase().endsWith("iso.zip") && !uripath.toLowerCase().endsWith("iso.bz2") && !uripath.toLowerCase().endsWith("iso.gz")))) { throw new IllegalArgumentException("Please specify a valid URL. URL:" + uripath + " is an invalid for the format " + format.toLowerCase()); } } public static InputStream getInputStreamFromUrl(String url, String user, String password) { try { Pair<String, Integer> hostAndPort = validateUrl(url); HttpClient httpclient = new HttpClient(new MultiThreadedHttpConnectionManager()); if ((user != null) && (password != null)) { httpclient.getParams().setAuthenticationPreemptive(true); Credentials defaultcreds = new UsernamePasswordCredentials(user, password); httpclient.getState().setCredentials(new AuthScope(hostAndPort.first(), hostAndPort.second(), AuthScope.ANY_REALM), defaultcreds); s_logger.info("Added username=" + user + ", password=" + password + "for host " + hostAndPort.first() + ":" + hostAndPort.second()); } // Execute the method. GetMethod method = new GetMethod(url); int statusCode = httpclient.executeMethod(method); if (statusCode != HttpStatus.SC_OK) { s_logger.error("Failed to read from URL: " + url); return null; } return method.getResponseBodyAsStream(); } catch (Exception ex) { s_logger.error("Failed to read from URL: " + url); return null; } } }