undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.net.httpserver.simpleserver;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import com.sun.net.httpserver.SimpleFileServer;
|
||||
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A class that provides a simple HTTP file server to serve the content of
|
||||
* a given directory.
|
||||
*
|
||||
* <p> The server is an HttpServer bound to a given address. It comes with an
|
||||
* HttpHandler that serves files from a given directory path
|
||||
* (and its subdirectories) on the default file system, and an optional Filter
|
||||
* that prints log messages related to the exchanges handled by the server to
|
||||
* a given output stream.
|
||||
*
|
||||
* <p> Unless specified as arguments, the default values are:<ul>
|
||||
* <li>bind address: 127.0.0.1 or ::1 (loopback)</li>
|
||||
* <li>directory: current working directory</li>
|
||||
* <li>outputLevel: info</li></ul>
|
||||
* <li>port: 8000</li>
|
||||
* <p>
|
||||
* The implementation is provided via the main entry point of the jdk.httpserver
|
||||
* module.
|
||||
*/
|
||||
final class SimpleFileServerImpl {
|
||||
private static final InetAddress LOOPBACK_ADDR = InetAddress.getLoopbackAddress();
|
||||
private static final int DEFAULT_PORT = 8000;
|
||||
private static final Path DEFAULT_ROOT = Path.of("").toAbsolutePath();
|
||||
private static final OutputLevel DEFAULT_OUTPUT_LEVEL = OutputLevel.INFO;
|
||||
private static boolean addrSpecified = false;
|
||||
|
||||
private SimpleFileServerImpl() { throw new AssertionError(); }
|
||||
|
||||
/**
|
||||
* Starts a simple HTTP file server created on a directory.
|
||||
*
|
||||
* @param writer the writer to which output should be written
|
||||
* @param args the command line options
|
||||
* @param launcher the launcher the server is started from
|
||||
* @throws NullPointerException if any of the arguments are {@code null},
|
||||
* or if there are any {@code null} values in the {@code args} array
|
||||
* @return startup status code
|
||||
*/
|
||||
static int start(PrintWriter writer, String launcher, String[] args) {
|
||||
Objects.requireNonNull(args);
|
||||
for (var arg : args) {
|
||||
Objects.requireNonNull(arg);
|
||||
}
|
||||
Out out = new Out(writer);
|
||||
|
||||
InetAddress addr = LOOPBACK_ADDR;
|
||||
int port = DEFAULT_PORT;
|
||||
Path root = DEFAULT_ROOT;
|
||||
OutputLevel outputLevel = DEFAULT_OUTPUT_LEVEL;
|
||||
|
||||
// parse options
|
||||
Iterator<String> options = Arrays.asList(args).iterator();
|
||||
String option = null;
|
||||
String optionArg = null;
|
||||
try {
|
||||
while (options.hasNext()) {
|
||||
option = options.next();
|
||||
switch (option) {
|
||||
case "-h", "-?", "--help" -> {
|
||||
out.showHelp(launcher);
|
||||
return Startup.OK.statusCode;
|
||||
}
|
||||
case "-version", "--version" -> {
|
||||
out.showVersion(launcher);
|
||||
return Startup.OK.statusCode;
|
||||
}
|
||||
case "-b", "--bind-address" -> {
|
||||
addr = InetAddress.getByName(optionArg = options.next());
|
||||
addrSpecified = true;
|
||||
}
|
||||
case "-d", "--directory" ->
|
||||
root = Path.of(optionArg = options.next());
|
||||
case "-o", "--output" ->
|
||||
outputLevel = Enum.valueOf(OutputLevel.class,
|
||||
(optionArg = options.next()).toUpperCase(Locale.ROOT));
|
||||
case "-p", "--port" ->
|
||||
port = Integer.parseInt(optionArg = options.next());
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
}
|
||||
} catch (AssertionError ae) {
|
||||
out.reportError(ResourceBundleHelper.getMessage("err.unknown.option", option));
|
||||
out.showUsage(launcher);
|
||||
return Startup.CMDERR.statusCode;
|
||||
} catch (NoSuchElementException nsee) {
|
||||
out.reportError(ResourceBundleHelper.getMessage("err.missing.arg", option));
|
||||
out.showOption(option);
|
||||
return Startup.CMDERR.statusCode;
|
||||
} catch (Exception e) {
|
||||
out.reportError(ResourceBundleHelper.getMessage("err.invalid.arg", option, optionArg));
|
||||
e.printStackTrace(out.writer);
|
||||
return Startup.CMDERR.statusCode;
|
||||
} finally {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
// configure and start server
|
||||
try {
|
||||
root = realPath(root);
|
||||
var socketAddr = new InetSocketAddress(addr, port);
|
||||
var server = SimpleFileServer.createFileServer(socketAddr, root, outputLevel);
|
||||
server.start();
|
||||
out.printStartMessage(root, server);
|
||||
} catch (Throwable t) {
|
||||
out.reportError(ResourceBundleHelper.getMessage("err.server.config.failed", t.getMessage()));
|
||||
return Startup.SYSERR.statusCode;
|
||||
} finally {
|
||||
out.flush();
|
||||
}
|
||||
return Startup.OK.statusCode;
|
||||
}
|
||||
|
||||
private static Path realPath(Path root) {
|
||||
|
||||
// `toRealPath()` invocation below already checks if file exists, though
|
||||
// there is no way to figure out if it fails due to a non-existent file.
|
||||
// Hence, checking the existence here first to deliver the user a more
|
||||
// descriptive message.
|
||||
if (!Files.exists(root)) {
|
||||
throw new IllegalArgumentException("Path does not exist: " + root);
|
||||
}
|
||||
|
||||
// Obtain the real path
|
||||
try {
|
||||
return root.toRealPath();
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalArgumentException("Path is invalid: " + root, exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Out {
|
||||
private final PrintWriter writer;
|
||||
private Out() { throw new AssertionError(); }
|
||||
|
||||
Out(PrintWriter writer) {
|
||||
this.writer = Objects.requireNonNull(writer);
|
||||
}
|
||||
|
||||
void printStartMessage(Path root, HttpServer server)
|
||||
throws UnknownHostException
|
||||
{
|
||||
String port = Integer.toString(server.getAddress().getPort());
|
||||
var inetAddr = server.getAddress().getAddress();
|
||||
var isAnyLocal = inetAddr.isAnyLocalAddress();
|
||||
var addr = isAnyLocal ? InetAddress.getLocalHost().getHostAddress() : inetAddr.getHostAddress();
|
||||
if (!addrSpecified) {
|
||||
writer.println(ResourceBundleHelper.getMessage("loopback.info"));
|
||||
}
|
||||
if (inetAddr instanceof Inet6Address && addr.contains(":") && !addr.startsWith("[")) {
|
||||
// we use the "addr" when printing the URL, so make sure it
|
||||
// conforms to RFC-2732, section 2:
|
||||
// To use a literal IPv6 address in a URL, the literal
|
||||
// address should be enclosed in "[" and "]" characters.
|
||||
addr = "[" + addr + "]";
|
||||
}
|
||||
if (isAnyLocal) {
|
||||
writer.println(ResourceBundleHelper.getMessage("msg.start.anylocal", root, addr, port));
|
||||
} else {
|
||||
writer.println(ResourceBundleHelper.getMessage("msg.start.other", root, addr, port));
|
||||
}
|
||||
}
|
||||
|
||||
void showUsage(String launcher) {
|
||||
writer.println(ResourceBundleHelper.getMessage("usage." + launcher));
|
||||
}
|
||||
|
||||
void showVersion(String launcher) {
|
||||
writer.println(ResourceBundleHelper.getMessage("version", launcher, System.getProperty("java.version")));
|
||||
}
|
||||
|
||||
void showHelp(String launcher) {
|
||||
writer.println(ResourceBundleHelper.getMessage("usage." + launcher));
|
||||
writer.println(ResourceBundleHelper.getMessage("options", LOOPBACK_ADDR.getHostAddress()));
|
||||
}
|
||||
|
||||
void showOption(String option) {
|
||||
switch (option) {
|
||||
case "-b", "--bind-address" ->
|
||||
writer.println(ResourceBundleHelper.getMessage("opt.bindaddress", LOOPBACK_ADDR.getHostAddress()));
|
||||
case "-d", "--directory" ->
|
||||
writer.println(ResourceBundleHelper.getMessage("opt.directory"));
|
||||
case "-o", "--output" ->
|
||||
writer.println(ResourceBundleHelper.getMessage("opt.output"));
|
||||
case "-p", "--port" ->
|
||||
writer.println(ResourceBundleHelper.getMessage("opt.port"));
|
||||
}
|
||||
}
|
||||
|
||||
void reportError(String message) {
|
||||
writer.println(ResourceBundleHelper.getMessage("error.prefix") + " " + message);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private enum Startup {
|
||||
/** Started with no errors */
|
||||
OK(0),
|
||||
/** Not started, bad command-line arguments */
|
||||
CMDERR(1),
|
||||
/** Not started, system error or resource exhaustion */
|
||||
SYSERR(2);
|
||||
|
||||
Startup(int statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
public final int statusCode;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue