From 456c2d03a75d55b69eff67e37f597a42f70c4b29 Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sat, 15 Jan 2022 09:27:52 +0800 Subject: [PATCH 1/2] Add -webExternalNames setting --- h2/src/docsrc/html/advanced.html | 5 +- h2/src/main/org/h2/server/web/WebApp.java | 4 + h2/src/main/org/h2/server/web/WebServer.java | 29 +- h2/src/main/org/h2/server/web/WebThread.java | 271 +++++++++++------- .../main/org/h2/server/web/res/_text_cs.prop | 1 + .../main/org/h2/server/web/res/_text_de.prop | 1 + .../main/org/h2/server/web/res/_text_en.prop | 1 + .../main/org/h2/server/web/res/_text_es.prop | 1 + .../main/org/h2/server/web/res/_text_fr.prop | 1 + .../main/org/h2/server/web/res/_text_hi.prop | 1 + .../main/org/h2/server/web/res/_text_hu.prop | 1 + .../main/org/h2/server/web/res/_text_in.prop | 1 + .../main/org/h2/server/web/res/_text_it.prop | 1 + .../main/org/h2/server/web/res/_text_ja.prop | 1 + .../main/org/h2/server/web/res/_text_ko.prop | 1 + .../main/org/h2/server/web/res/_text_nl.prop | 1 + .../main/org/h2/server/web/res/_text_pl.prop | 1 + .../org/h2/server/web/res/_text_pt_br.prop | 1 + .../org/h2/server/web/res/_text_pt_pt.prop | 1 + .../main/org/h2/server/web/res/_text_ru.prop | 1 + .../main/org/h2/server/web/res/_text_sk.prop | 1 + .../main/org/h2/server/web/res/_text_tr.prop | 1 + .../main/org/h2/server/web/res/_text_uk.prop | 1 + .../org/h2/server/web/res/_text_zh_cn.prop | 1 + .../org/h2/server/web/res/_text_zh_tw.prop | 1 + h2/src/main/org/h2/server/web/res/admin.jsp | 3 + h2/src/main/org/h2/tools/Console.java | 2 + h2/src/main/org/h2/tools/Server.java | 7 +- h2/src/tools/org/h2/build/doc/dictionary.txt | 2 +- 29 files changed, 231 insertions(+), 113 deletions(-) diff --git a/h2/src/docsrc/html/advanced.html b/h2/src/docsrc/html/advanced.html index 9e25e1b794..68e865b1ff 100644 --- a/h2/src/docsrc/html/advanced.html +++ b/h2/src/docsrc/html/advanced.html @@ -1370,9 +1370,10 @@

Protection against Remote Access

If you enable remote access using -webAllowOthers, please ensure the web server can only be accessed from trusted networks. +If this option is specified, -webExternalNames should be also specified with +comma-separated list of external names or addresses of this server. The options -baseDir don't protect -access to the tools section, prevent remote shutdown of the web server, -changes to the preferences, the saved connection settings, +access to the saved connection settings, or access to other databases accessible from the system.

diff --git a/h2/src/main/org/h2/server/web/WebApp.java b/h2/src/main/org/h2/server/web/WebApp.java index 63789f80c2..945403679c 100644 --- a/h2/src/main/org/h2/server/web/WebApp.java +++ b/h2/src/main/org/h2/server/web/WebApp.java @@ -394,6 +394,7 @@ private String autoCompleteList() { private String admin() { session.put("port", Integer.toString(server.getPort())); session.put("allowOthers", Boolean.toString(server.getAllowOthers())); + session.put("webExternalNames", server.getExternalNames()); session.put("ssl", String.valueOf(server.getSSL())); session.put("sessions", server.getSessions()); return "admin.jsp"; @@ -408,6 +409,9 @@ private String adminSave() { boolean allowOthers = Utils.parseBoolean((String) attributes.get("allowOthers"), false, false); prop.setProperty("webAllowOthers", String.valueOf(allowOthers)); server.setAllowOthers(allowOthers); + String externalNames = (String) attributes.get("webExternalNames"); + prop.setProperty("webExternalNames", externalNames); + server.setExternalNames(externalNames); boolean ssl = Utils.parseBoolean((String) attributes.get("ssl"), false, false); prop.setProperty("webSSL", String.valueOf(ssl)); server.setSSL(ssl); diff --git a/h2/src/main/org/h2/server/web/WebServer.java b/h2/src/main/org/h2/server/web/WebServer.java index 381fc2f9d9..9e93917edc 100644 --- a/h2/src/main/org/h2/server/web/WebServer.java +++ b/h2/src/main/org/h2/server/web/WebServer.java @@ -159,6 +159,7 @@ public class WebServer implements Service { // private URLClassLoader urlClassLoader; private int port; private boolean allowOthers; + private String externalNames; private boolean isDaemon; private final Set running = Collections.synchronizedSet(new HashSet()); @@ -171,6 +172,7 @@ public class WebServer implements Service { private final HashSet languages = new HashSet<>(); private String startDateTime; private ServerSocket serverSocket; + private String host; private String url; private ShutdownHandler shutdownHandler; private Thread listenerThread; @@ -319,6 +321,7 @@ public void init(String... args) { "webSSL", false); allowOthers = SortedProperties.getBooleanProperty(prop, "webAllowOthers", false); + externalNames = SortedProperties.getStringProperty(prop, "webExternalNames", null); setAdminPassword(SortedProperties.getStringProperty(prop, "webAdminPassword", null)); commandHistoryString = prop.getProperty(COMMAND_HISTORY); for (int i = 0; args != null && i < args.length; i++) { @@ -329,6 +332,8 @@ public void init(String... args) { ssl = true; } else if (Tool.isOption(a, "-webAllowOthers")) { allowOthers = true; + } else if (Tool.isOption(a, "-webExternalNames")) { + externalNames = args[++i]; } else if (Tool.isOption(a, "-webDaemon")) { isDaemon = true; } else if (Tool.isOption(a, "-baseDir")) { @@ -375,10 +380,21 @@ public String getURL() { return url; } + /** + * @return host name + */ + public String getHost() { + if (host == null) { + updateURL(); + } + return host; + } + private void updateURL() { try { + host = NetUtils.getLocalAddress(); StringBuilder builder = new StringBuilder(ssl ? "https" : "http").append("://") - .append(NetUtils.getLocalAddress()).append(':').append(port); + .append(host).append(':').append(port); if (key != null && serverSocket != null) { builder.append("?key=").append(key); } @@ -551,6 +567,14 @@ public boolean getAllowOthers() { return allowOthers; } + void setExternalNames(String externalNames) { + this.externalNames = externalNames; + } + + String getExternalNames() { + return externalNames; + } + void setSSL(boolean b) { ssl = b; } @@ -731,6 +755,9 @@ synchronized void saveProperties(Properties prop) { Integer.toString(SortedProperties.getIntProperty(old, "webPort", port))); prop.setProperty("webAllowOthers", Boolean.toString(SortedProperties.getBooleanProperty(old, "webAllowOthers", allowOthers))); + if (externalNames != null) { + prop.setProperty("webExternalNames", externalNames); + } prop.setProperty("webSSL", Boolean.toString(SortedProperties.getBooleanProperty(old, "webSSL", ssl))); if (adminPassword != null) { diff --git a/h2/src/main/org/h2/server/web/WebThread.java b/h2/src/main/org/h2/server/web/WebThread.java index ad6a8a0f25..471fda78d8 100644 --- a/h2/src/main/org/h2/server/web/WebThread.java +++ b/h2/src/main/org/h2/server/web/WebThread.java @@ -9,6 +9,7 @@ import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; @@ -32,10 +33,16 @@ */ class WebThread extends WebApp implements Runnable { + private static final byte[] RN = { '\r', '\n' }; + + private static final byte[] RNRN = { '\r', '\n', '\r', '\n' }; + protected OutputStream output; protected final Socket socket; private final Thread thread; private InputStream input; + private String host; + private int dataLength; private String ifModifiedSince; WebThread(Socket socket, WebServer server) { @@ -112,112 +119,154 @@ public void run() { @SuppressWarnings("unchecked") private boolean process() throws IOException { - boolean keepAlive = false; String head = readHeaderLine(); - if (head.startsWith("GET ") || head.startsWith("POST ")) { - int begin = head.indexOf('/'), end = head.lastIndexOf(' '); - String file; - if (begin < 0 || end < begin) { - file = ""; - } else { - file = StringUtils.trimSubstring(head, begin + 1, end); - } - trace(head + ": " + file); - file = getAllowedFile(file); - attributes = new Properties(); - int paramIndex = file.indexOf('?'); - session = null; - String key = null; - if (paramIndex >= 0) { - String attrib = file.substring(paramIndex + 1); - parseAttributes(attrib); - String sessionId = attributes.getProperty("jsessionid"); - key = attributes.getProperty("key"); - file = file.substring(0, paramIndex); - session = server.getSession(sessionId); - } - keepAlive = parseHeader(); - file = processRequest(file, - new NetworkConnectionInfo( - NetUtils.ipToShortForm(new StringBuilder(server.getSSL() ? "https://" : "http://"), - socket.getLocalAddress().getAddress(), true) // - .append(':').append(socket.getLocalPort()).toString(), // - socket.getInetAddress().getAddress(), socket.getPort(), null)); - if (file.length() == 0) { - // asynchronous request - return true; + boolean get = head.startsWith("GET "); + if ((!get && !head.startsWith("POST ")) || !head.endsWith(" HTTP/1.1")) { + writeSimple("HTTP/1.1 400 Bad Request", "Bad request"); + return false; + } + String file = StringUtils.trimSubstring(head, get ? 4 : 5, head.length() - 9); + if (file.isEmpty() || file.charAt(0) != '/') { + writeSimple("HTTP/1.1 400 Bad Request", "Bad request"); + return false; + } + attributes = new Properties(); + boolean keepAlive = parseHeader(); + if (!checkHost(host)) { + return false; + } + file = file.substring(1); + trace(head + ": " + file); + file = getAllowedFile(file); + int paramIndex = file.indexOf('?'); + session = null; + String key = null; + if (paramIndex >= 0) { + String attrib = file.substring(paramIndex + 1); + parseAttributes(attrib); + String sessionId = attributes.getProperty("jsessionid"); + key = attributes.getProperty("key"); + file = file.substring(0, paramIndex); + session = server.getSession(sessionId); + } + parseBodyAttributes(); + file = processRequest(file, + new NetworkConnectionInfo( + NetUtils.ipToShortForm(new StringBuilder(server.getSSL() ? "https://" : "http://"), + socket.getLocalAddress().getAddress(), true) // + .append(':').append(socket.getLocalPort()).toString(), // + socket.getInetAddress().getAddress(), socket.getPort(), null)); + if (file.length() == 0) { + // asynchronous request + return true; + } + String message; + if (cache && ifModifiedSince != null && ifModifiedSince.equals(server.getStartDateTime())) { + writeSimple("HTTP/1.1 304 Not Modified", (byte[]) null); + return keepAlive; + } + byte[] bytes = server.getFile(file); + if (bytes == null) { + writeSimple("HTTP/1.1 404 Not Found", "File not found: " + file); + return keepAlive; + } + if (session != null && file.endsWith(".jsp")) { + if (key != null) { + session.put("key", key); } - String message; - byte[] bytes; - if (cache && ifModifiedSince != null && - ifModifiedSince.equals(server.getStartDateTime())) { - bytes = null; - message = "HTTP/1.1 304 Not Modified\r\n"; - } else { - bytes = server.getFile(file); - if (bytes == null) { - message = "HTTP/1.1 404 Not Found\r\n"; - bytes = ("File not found: " + file).getBytes(StandardCharsets.UTF_8); - message += "Content-Length: " + bytes.length + "\r\n"; - } else { - if (session != null && file.endsWith(".jsp")) { - if (key != null) { - session.put("key", key); - } - String page = new String(bytes, StandardCharsets.UTF_8); - if (SysProperties.CONSOLE_STREAM) { - Iterator it = (Iterator) session.map.remove("chunks"); - if (it != null) { - message = "HTTP/1.1 200 OK\r\n"; - message += "Content-Type: " + mimeType + "\r\n"; - message += "Cache-Control: no-cache\r\n"; - message += "Transfer-Encoding: chunked\r\n"; - message += "\r\n"; - trace(message); - output.write(message.getBytes()); - while (it.hasNext()) { - String s = it.next(); - s = PageParser.parse(s, session.map); - bytes = s.getBytes(StandardCharsets.UTF_8); - if (bytes.length == 0) { - continue; - } - output.write(Integer.toHexString(bytes.length).getBytes()); - output.write("\r\n".getBytes()); - output.write(bytes); - output.write("\r\n".getBytes()); - output.flush(); - } - output.write("0\r\n\r\n".getBytes()); - output.flush(); - return keepAlive; - } - } - page = PageParser.parse(page, session.map); - bytes = page.getBytes(StandardCharsets.UTF_8); - } + String page = new String(bytes, StandardCharsets.UTF_8); + if (SysProperties.CONSOLE_STREAM) { + Iterator it = (Iterator) session.map.remove("chunks"); + if (it != null) { message = "HTTP/1.1 200 OK\r\n"; message += "Content-Type: " + mimeType + "\r\n"; - if (!cache) { - message += "Cache-Control: no-cache\r\n"; - } else { - message += "Cache-Control: max-age=10\r\n"; - message += "Last-Modified: " + server.getStartDateTime() + "\r\n"; + message += "Cache-Control: no-cache\r\n"; + message += "Transfer-Encoding: chunked\r\n"; + message += "\r\n"; + trace(message); + output.write(message.getBytes()); + while (it.hasNext()) { + String s = it.next(); + s = PageParser.parse(s, session.map); + bytes = s.getBytes(StandardCharsets.UTF_8); + if (bytes.length == 0) { + continue; + } + output.write(Integer.toHexString(bytes.length).getBytes()); + output.write("\r\n".getBytes()); + output.write(bytes); + output.write("\r\n".getBytes()); + output.flush(); } - message += "Content-Length: " + bytes.length + "\r\n"; + output.write("0\r\n\r\n".getBytes()); + output.flush(); + return keepAlive; } } - message += "\r\n"; - trace(message); - output.write(message.getBytes()); - if (bytes != null) { - output.write(bytes); - } - output.flush(); + page = PageParser.parse(page, session.map); + bytes = page.getBytes(StandardCharsets.UTF_8); } + message = "HTTP/1.1 200 OK\r\n"; + message += "Content-Type: " + mimeType + "\r\n"; + if (!cache) { + message += "Cache-Control: no-cache\r\n"; + } else { + message += "Cache-Control: max-age=10\r\n"; + message += "Last-Modified: " + server.getStartDateTime() + "\r\n"; + } + message += "Content-Length: " + bytes.length + "\r\n"; + message += "\r\n"; + trace(message); + output.write(message.getBytes()); + output.write(bytes); + output.flush(); return keepAlive; } + private void writeSimple(String status, String text) throws IOException { + writeSimple(status, text != null ? text.getBytes(StandardCharsets.UTF_8) : null); + } + + private void writeSimple(String status, byte[] bytes) throws IOException { + trace(status); + output.write(status.getBytes(StandardCharsets.ISO_8859_1)); + if (bytes != null) { + output.write(RN); + String contentLength = "Content-Length: " + bytes.length; + trace(contentLength); + output.write(contentLength.getBytes(StandardCharsets.ISO_8859_1)); + output.write(RNRN); + output.write(bytes); + } else { + output.write(RNRN); + } + output.flush(); + } + + private boolean checkHost(String host) throws IOException { + if (host == null) { + writeSimple("HTTP/1.1 400 Bad Request", "Bad request"); + return false; + } + int index = host.indexOf(':'); + if (index >= 0) { + host = host.substring(0, index); + } + if (host.equals(server.getHost()) || host.equals("localhost") || host.equals("127.0.0.1")) { + return true; + } + String externalNames = server.getExternalNames(); + if (externalNames != null && !externalNames.isEmpty()) { + for (String s : externalNames.split(",")) { + if (host.equals(s.trim())) { + return true; + } + } + } + writeSimple("HTTP/1.1 404 Not Found", "Host " + host + " not found"); + return false; + } + private String readHeaderLine() throws IOException { StringBuilder buff = new StringBuilder(); while (true) { @@ -236,6 +285,17 @@ private String readHeaderLine() throws IOException { } } + private void parseBodyAttributes() throws IOException { + if (dataLength > 0) { + byte[] bytes = Utils.newBytes(dataLength); + for (int pos = 0; pos < dataLength;) { + pos += input.read(bytes, pos, dataLength - pos); + } + String s = new String(bytes, StandardCharsets.UTF_8); + parseAttributes(s); + } + } + private void parseAttributes(String s) { trace("data=" + s); while (s != null) { @@ -264,16 +324,15 @@ private boolean parseHeader() throws IOException { boolean keepAlive = false; trace("parseHeader"); int len = 0; + host = null; ifModifiedSince = null; boolean multipart = false; - while (true) { - String line = readHeaderLine(); - if (line == null) { - break; - } + for (String line; (line = readHeaderLine()) != null;) { trace(" " + line); String lower = StringUtils.toLowerEnglish(line); - if (lower.startsWith("if-modified-since")) { + if (lower.startsWith("host")) { + host = getHeaderLineValue(line); + } else if (lower.startsWith("if-modified-since")) { ifModifiedSince = getHeaderLineValue(line); } else if (lower.startsWith("connection")) { String conn = getHeaderLineValue(line); @@ -328,15 +387,11 @@ private boolean parseHeader() throws IOException { break; } } + dataLength = 0; if (multipart) { // not supported - } else if (session != null && len > 0) { - byte[] bytes = Utils.newBytes(len); - for (int pos = 0; pos < len;) { - pos += input.read(bytes, pos, len - pos); - } - String s = new String(bytes); - parseAttributes(s); + } else if (len > 0) { + dataLength = len; } return keepAlive; } diff --git a/h2/src/main/org/h2/server/web/res/_text_cs.prop b/h2/src/main/org/h2/server/web/res/_text_cs.prop index 2126edefac..75bd46bc01 100644 --- a/h2/src/main/org/h2/server/web/res/_text_cs.prop +++ b/h2/src/main/org/h2/server/web/res/_text_cs.prop @@ -25,6 +25,7 @@ adminLoginCancel=Zrušit adminLoginOk=OK adminLogout=Odhlásit adminOthers=Povolit připojení z jiných počítačů +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Číslo portu adminPortWeb=Číslo portu webového serveru adminRestart=Změny se projeví po restartu serveru. diff --git a/h2/src/main/org/h2/server/web/res/_text_de.prop b/h2/src/main/org/h2/server/web/res/_text_de.prop index ada6964e14..c576c16181 100644 --- a/h2/src/main/org/h2/server/web/res/_text_de.prop +++ b/h2/src/main/org/h2/server/web/res/_text_de.prop @@ -25,6 +25,7 @@ adminLoginCancel=Abbrechen adminLoginOk=OK adminLogout=Beenden adminOthers=Verbindungen von anderen Computern erlauben +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Admin Port adminPortWeb=Web-Server Port adminRestart=Änderungen werden nach einem Neustart des Servers aktiv. diff --git a/h2/src/main/org/h2/server/web/res/_text_en.prop b/h2/src/main/org/h2/server/web/res/_text_en.prop index 23d49bb328..cd8c54e1af 100644 --- a/h2/src/main/org/h2/server/web/res/_text_en.prop +++ b/h2/src/main/org/h2/server/web/res/_text_en.prop @@ -25,6 +25,7 @@ adminLoginCancel=Cancel adminLoginOk=OK adminLogout=Logout adminOthers=Allow connections from other computers +adminWebExternalNames=External names or addresses (comma-separated) adminPort=Port number adminPortWeb=Web server port number adminRestart=Changes take effect after restarting the server. diff --git a/h2/src/main/org/h2/server/web/res/_text_es.prop b/h2/src/main/org/h2/server/web/res/_text_es.prop index 8f1e1c576e..832d7aa946 100644 --- a/h2/src/main/org/h2/server/web/res/_text_es.prop +++ b/h2/src/main/org/h2/server/web/res/_text_es.prop @@ -25,6 +25,7 @@ adminLoginCancel=Cancelar adminLoginOk=Aceptar adminLogout=Desconectar adminOthers=Permitir conexiones desde otros ordenadores +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Puerto adminPortWeb=Puerto del servidor Web adminRestart=Los cambios tendrán efecto al reiniciar el servidor. diff --git a/h2/src/main/org/h2/server/web/res/_text_fr.prop b/h2/src/main/org/h2/server/web/res/_text_fr.prop index 8380c479c8..16375cf099 100644 --- a/h2/src/main/org/h2/server/web/res/_text_fr.prop +++ b/h2/src/main/org/h2/server/web/res/_text_fr.prop @@ -25,6 +25,7 @@ adminLoginCancel=Annuler adminLoginOk=OK adminLogout=Déconnexion adminOthers=Autoriser les connexions d'ordinateurs distants +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Numéro de port adminPortWeb=Numéro de port du serveur Web adminRestart=Modifications effectuées après redémarrage du serveur. diff --git a/h2/src/main/org/h2/server/web/res/_text_hi.prop b/h2/src/main/org/h2/server/web/res/_text_hi.prop index 553146ca06..eae5a9ddc4 100644 --- a/h2/src/main/org/h2/server/web/res/_text_hi.prop +++ b/h2/src/main/org/h2/server/web/res/_text_hi.prop @@ -25,6 +25,7 @@ adminLoginCancel=रद्द करना adminLoginOk=ठीक adminLogout=लोग आउट adminOthers=अन्य कंप्यूटर से कनेक्शन की अनुमति दें +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=पोर्ट नंबर adminPortWeb=वेब सर्वर पोर्ट नंबर adminRestart=सर्वर को पुनरारंभ करने के बाद परिवर्तन प्रभावी होते हैं। diff --git a/h2/src/main/org/h2/server/web/res/_text_hu.prop b/h2/src/main/org/h2/server/web/res/_text_hu.prop index 56aeddfbcc..faf63162ef 100644 --- a/h2/src/main/org/h2/server/web/res/_text_hu.prop +++ b/h2/src/main/org/h2/server/web/res/_text_hu.prop @@ -25,6 +25,7 @@ adminLoginCancel=Mégse adminLoginOk=OK adminLogout=Kilépés adminOthers=Más számítógépekről kezdeményezett kapcsolatok engedélyezése +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=#Port number adminPortWeb=Webkiszolgáló portszáma adminRestart=A változtatások a kiszolgáló újraindítása után lépnek érvénybe diff --git a/h2/src/main/org/h2/server/web/res/_text_in.prop b/h2/src/main/org/h2/server/web/res/_text_in.prop index 8a569cb42e..61427127c1 100644 --- a/h2/src/main/org/h2/server/web/res/_text_in.prop +++ b/h2/src/main/org/h2/server/web/res/_text_in.prop @@ -25,6 +25,7 @@ adminLoginCancel=Batal adminLoginOk=OK adminLogout=Keluar adminOthers=Ijinkan koneksi dari komputer lain +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Nomor port adminPortWeb=Nomor port web server adminRestart=Perubahan akan efektif setelah server di-restart. diff --git a/h2/src/main/org/h2/server/web/res/_text_it.prop b/h2/src/main/org/h2/server/web/res/_text_it.prop index ac32ed9406..5b68b64770 100644 --- a/h2/src/main/org/h2/server/web/res/_text_it.prop +++ b/h2/src/main/org/h2/server/web/res/_text_it.prop @@ -25,6 +25,7 @@ adminLoginCancel=Annulla adminLoginOk=OK adminLogout=Disconnessione adminOthers=Abilita connessioni da altri computers +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Numero di porta adminPortWeb=Numero di porta del server Web adminRestart=Le modifiche saranno effettive dopo il riavvio del server. diff --git a/h2/src/main/org/h2/server/web/res/_text_ja.prop b/h2/src/main/org/h2/server/web/res/_text_ja.prop index e16f17ae4b..814aa83396 100644 --- a/h2/src/main/org/h2/server/web/res/_text_ja.prop +++ b/h2/src/main/org/h2/server/web/res/_text_ja.prop @@ -25,6 +25,7 @@ adminLoginCancel=キャンセル adminLoginOk=OK adminLogout=ログアウト adminOthers=他のコンピュータからの接続を許可 +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=ポート番号 adminPortWeb=Webサーバポート番号 adminRestart=変更はサーバの再起動後に有効になります。 diff --git a/h2/src/main/org/h2/server/web/res/_text_ko.prop b/h2/src/main/org/h2/server/web/res/_text_ko.prop index 780072c65d..05c76c2ae3 100644 --- a/h2/src/main/org/h2/server/web/res/_text_ko.prop +++ b/h2/src/main/org/h2/server/web/res/_text_ko.prop @@ -25,6 +25,7 @@ adminLoginCancel=취소 adminLoginOk=확인 adminLogout=로그아웃 adminOthers=다른 컴퓨터에서의 연결 허가 +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=포트 번호 adminPortWeb=웹 서버 포트 번호 adminRestart=변경 사항은 서버 재시작 후 반영됩니다. diff --git a/h2/src/main/org/h2/server/web/res/_text_nl.prop b/h2/src/main/org/h2/server/web/res/_text_nl.prop index ccea089d33..b145e85f6a 100644 --- a/h2/src/main/org/h2/server/web/res/_text_nl.prop +++ b/h2/src/main/org/h2/server/web/res/_text_nl.prop @@ -25,6 +25,7 @@ adminLoginCancel=Annuleren adminLoginOk=OK adminLogout=Uitloggen adminOthers=Sta verbindingen vanaf andere computers toe +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Poortnummer adminPortWeb=Webserver poortnummer adminRestart=Wijzigingen worden doorgevoerd na herstarten server diff --git a/h2/src/main/org/h2/server/web/res/_text_pl.prop b/h2/src/main/org/h2/server/web/res/_text_pl.prop index 0c10899fef..d21d33bef1 100644 --- a/h2/src/main/org/h2/server/web/res/_text_pl.prop +++ b/h2/src/main/org/h2/server/web/res/_text_pl.prop @@ -25,6 +25,7 @@ adminLoginCancel=Anuluj adminLoginOk=OK adminLogout=Wyloguj adminOthers=Pozwalaj na połączenia zdalne +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Numer portu adminPortWeb=Numer portu serwera Web adminRestart=Zmiany będą widoczne po zrestartowaniu serwera. diff --git a/h2/src/main/org/h2/server/web/res/_text_pt_br.prop b/h2/src/main/org/h2/server/web/res/_text_pt_br.prop index 64ef3d7d21..f9a8f5a56a 100644 --- a/h2/src/main/org/h2/server/web/res/_text_pt_br.prop +++ b/h2/src/main/org/h2/server/web/res/_text_pt_br.prop @@ -25,6 +25,7 @@ adminLoginCancel=Cancelar adminLoginOk=Confirmar adminLogout=Sair adminOthers=Permitir conexões de outros computadores na rede +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Número da porta adminPortWeb=Número da porta do servidor adminRestart=As alterações serão aplicadas depois de reiniciar o servidor. diff --git a/h2/src/main/org/h2/server/web/res/_text_pt_pt.prop b/h2/src/main/org/h2/server/web/res/_text_pt_pt.prop index 205084a6ac..90a7528ddc 100644 --- a/h2/src/main/org/h2/server/web/res/_text_pt_pt.prop +++ b/h2/src/main/org/h2/server/web/res/_text_pt_pt.prop @@ -25,6 +25,7 @@ adminLoginCancel=Cancelar adminLoginOk=Confirmar adminLogout=Sair adminOthers=Permitir conexões a partir de outro computador na rede +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Número do porto adminPortWeb=Número do porto do servidor adminRestart=As alterações apenas serão aplicadas após reiniciar o servidor. diff --git a/h2/src/main/org/h2/server/web/res/_text_ru.prop b/h2/src/main/org/h2/server/web/res/_text_ru.prop index 9e3c683b0d..ecbf988683 100644 --- a/h2/src/main/org/h2/server/web/res/_text_ru.prop +++ b/h2/src/main/org/h2/server/web/res/_text_ru.prop @@ -25,6 +25,7 @@ adminLoginCancel=Отменить adminLoginOk=OK adminLogout=Выход adminOthers=Разрешить удаленные подключения +adminWebExternalNames=Внешние имена или адреса (через запятую) adminPort=Номер порта adminPortWeb=Порт web-сервера adminRestart=Изменения вступят в силу после перезагрузки сервера. diff --git a/h2/src/main/org/h2/server/web/res/_text_sk.prop b/h2/src/main/org/h2/server/web/res/_text_sk.prop index 2d9c227666..b863353419 100644 --- a/h2/src/main/org/h2/server/web/res/_text_sk.prop +++ b/h2/src/main/org/h2/server/web/res/_text_sk.prop @@ -25,6 +25,7 @@ adminLoginCancel=Zrušiť adminLoginOk=OK adminLogout=Odhlásiť adminOthers=Povoliť pripojenia z iných počítačov +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Číslo portu adminPortWeb=Číslo portu Web servera adminRestart=Zmeny sa vykonajú po reštarte servera diff --git a/h2/src/main/org/h2/server/web/res/_text_tr.prop b/h2/src/main/org/h2/server/web/res/_text_tr.prop index deac77695c..09c9c657af 100644 --- a/h2/src/main/org/h2/server/web/res/_text_tr.prop +++ b/h2/src/main/org/h2/server/web/res/_text_tr.prop @@ -25,6 +25,7 @@ adminLoginCancel=İptal et adminLoginOk=Tamam adminLogout=Bitir adminOthers=Başka bilgisayarlardan, veri tabanına bağlanma izni ver +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Port adminPortWeb=Web-Server Port adminRestart=Değişiklikler veri tabanı hizmetçisinin yeniden başlatılmasıyla etkinlik kazanacak. diff --git a/h2/src/main/org/h2/server/web/res/_text_uk.prop b/h2/src/main/org/h2/server/web/res/_text_uk.prop index 8b32ea913a..8a8a981579 100644 --- a/h2/src/main/org/h2/server/web/res/_text_uk.prop +++ b/h2/src/main/org/h2/server/web/res/_text_uk.prop @@ -25,6 +25,7 @@ adminLoginCancel=Відмінити adminLoginOk=OK adminLogout=Завершення сеансу adminOthers=Дозволити під'єднання з інших копм'ютерів +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=Номер порта adminPortWeb=Номер порта веб сервера adminRestart=Зміни вступлять в силу після перезавантаження сервера. diff --git a/h2/src/main/org/h2/server/web/res/_text_zh_cn.prop b/h2/src/main/org/h2/server/web/res/_text_zh_cn.prop index aac9fffdf9..1c74b214d8 100644 --- a/h2/src/main/org/h2/server/web/res/_text_zh_cn.prop +++ b/h2/src/main/org/h2/server/web/res/_text_zh_cn.prop @@ -25,6 +25,7 @@ adminLoginCancel=取消 adminLoginOk=确认 adminLogout=注销 adminOthers=允许来自其他远程计算机的连接 +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=端口号 adminPortWeb=Web 服务器端口号 adminRestart=更新配置将在重启服务器后生效. diff --git a/h2/src/main/org/h2/server/web/res/_text_zh_tw.prop b/h2/src/main/org/h2/server/web/res/_text_zh_tw.prop index cd3f35eb38..cfffc8cdd0 100644 --- a/h2/src/main/org/h2/server/web/res/_text_zh_tw.prop +++ b/h2/src/main/org/h2/server/web/res/_text_zh_tw.prop @@ -25,6 +25,7 @@ adminLoginCancel=取消 adminLoginOk=確定 adminLogout=登出 adminOthers=允許來自其他電腦的連接 +adminWebExternalNames=#External names or addresses (comma-separated) adminPort=通訊埠 adminPortWeb=Web 伺服器的通訊埠 adminRestart=伺服器重新啟動後修改才會生效. diff --git a/h2/src/main/org/h2/server/web/res/admin.jsp b/h2/src/main/org/h2/server/web/res/admin.jsp index c6d744788b..62aff3c882 100644 --- a/h2/src/main/org/h2/server/web/res/admin.jsp +++ b/h2/src/main/org/h2/server/web/res/admin.jsp @@ -39,6 +39,9 @@ Initial Developer: H2 Group ${text.adminOthers}

+

+ ${text.adminWebExternalNames}: +

${text.adminConnection}

diff --git a/h2/src/main/org/h2/tools/Console.java b/h2/src/main/org/h2/tools/Console.java index ab196fed36..42624300f1 100644 --- a/h2/src/main/org/h2/tools/Console.java +++ b/h2/src/main/org/h2/tools/Console.java @@ -113,6 +113,8 @@ public void runTool(String... args) throws SQLException { } else if ("-webAllowOthers".equals(arg)) { // no parameters webAllowOthers = true; + } else if ("-webExternalNames".equals(arg)) { + i++; } else if ("-webDaemon".equals(arg)) { // no parameters } else if ("-webSSL".equals(arg)) { diff --git a/h2/src/main/org/h2/tools/Server.java b/h2/src/main/org/h2/tools/Server.java index bb7e25a4b3..c1956012ca 100644 --- a/h2/src/main/org/h2/tools/Server.java +++ b/h2/src/main/org/h2/tools/Server.java @@ -65,6 +65,9 @@ public Server(Service service, String... args) throws SQLException { * Start the web server with the H2 Console * [-webAllowOthers] * Allow other computers to connect - see below + * [-webExternalNames <names>] + * The comma-separated list of external names and IP addresses of this server, + * used together with -webAllowOthers * [-webDaemon] * Use a daemon thread * [-webPort <port>] @@ -134,7 +137,9 @@ private void verifyArgs(String... args) throws SQLException { // ok } else if ("-webAllowOthers".equals(arg)) { // no parameters - } else if ("-webDaemon".equals(arg)) { + } else if ("-webExternalNames".equals(arg)) { + i++; + } else if ("-webDaemon".equals(arg)) { // no parameters } else if ("-webSSL".equals(arg)) { // no parameters diff --git a/h2/src/tools/org/h2/build/doc/dictionary.txt b/h2/src/tools/org/h2/build/doc/dictionary.txt index 2ac6528d81..9e48dba39b 100644 --- a/h2/src/tools/org/h2/build/doc/dictionary.txt +++ b/h2/src/tools/org/h2/build/doc/dictionary.txt @@ -847,4 +847,4 @@ ptf overlay precedes regr slope sqlerror multiset submultiset inout sxx sxy inte orientation eternal consideration erased fedc npgsql powers fffd uencode ampersand noversion ude considerable intro entirely skeleton discouraged pearson coefficient squares covariance mytab debuggers fonts glyphs filestore backstop tie breaker lockable lobtx btx waiter accounted aiobe spf resolvers generators -accidental wbr subtree recognising supplementary happier hasn officially +accidental wbr subtree recognising supplementary happier hasn officially rnrn From eb75633d0dfa86341e6ef77a861665c4a0f16ab8 Mon Sep 17 00:00:00 2001 From: Evgenij Ryazanov Date: Sat, 15 Jan 2022 10:09:47 +0800 Subject: [PATCH 2/2] Fix WebServer.getConnection() --- h2/src/main/org/h2/Driver.java | 2 +- h2/src/main/org/h2/jdbc/JdbcConnection.java | 7 ++++++- h2/src/main/org/h2/jdbcx/JdbcDataSource.java | 9 +++++---- h2/src/main/org/h2/server/TcpServer.java | 5 +++-- h2/src/main/org/h2/server/web/WebServer.java | 10 ++-------- h2/src/main/org/h2/tools/CreateCluster.java | 6 +++--- h2/src/main/org/h2/tools/GUIConsole.java | 2 +- h2/src/main/org/h2/tools/Upgrade.java | 2 +- h2/src/main/org/h2/util/JdbcUtils.java | 7 ++++--- h2/src/test/org/h2/test/unit/TestTools.java | 6 ++++++ 10 files changed, 32 insertions(+), 24 deletions(-) diff --git a/h2/src/main/org/h2/Driver.java b/h2/src/main/org/h2/Driver.java index 03056dc5b5..a0660fc5fd 100644 --- a/h2/src/main/org/h2/Driver.java +++ b/h2/src/main/org/h2/Driver.java @@ -56,7 +56,7 @@ public Connection connect(String url, Properties info) throws SQLException { if (url == null) { throw DbException.getJdbcSQLException(ErrorCode.URL_FORMAT_ERROR_2, null, Constants.URL_FORMAT, null); } else if (url.startsWith(Constants.START_URL)) { - return new JdbcConnection(url, info, null, null); + return new JdbcConnection(url, info, null, null, false); } else if (url.equals(DEFAULT_URL)) { return DEFAULT_CONNECTION.get(); } else { diff --git a/h2/src/main/org/h2/jdbc/JdbcConnection.java b/h2/src/main/org/h2/jdbc/JdbcConnection.java index 147118822a..9834e7a03f 100644 --- a/h2/src/main/org/h2/jdbc/JdbcConnection.java +++ b/h2/src/main/org/h2/jdbc/JdbcConnection.java @@ -103,12 +103,17 @@ public class JdbcConnection extends TraceObject implements Connection, JdbcConne * @param info of this connection * @param user of this connection * @param password for the user + * @param forbidCreation whether database creation is forbidden * @throws SQLException on failure */ @SuppressWarnings("resource") - public JdbcConnection(String url, Properties info, String user, Object password) throws SQLException { + public JdbcConnection(String url, Properties info, String user, Object password, boolean forbidCreation) + throws SQLException { try { ConnectionInfo ci = new ConnectionInfo(url, info, user, password); + if (forbidCreation) { + ci.setProperty("FORBID_CREATION", "TRUE"); + } String baseDir = SysProperties.getBaseDir(); if (baseDir != null) { ci.setBaseDir(baseDir); diff --git a/h2/src/main/org/h2/jdbcx/JdbcDataSource.java b/h2/src/main/org/h2/jdbcx/JdbcDataSource.java index 535f64114a..4c0ab0cfad 100644 --- a/h2/src/main/org/h2/jdbcx/JdbcDataSource.java +++ b/h2/src/main/org/h2/jdbcx/JdbcDataSource.java @@ -152,7 +152,7 @@ public void setLogWriter(PrintWriter out) { @Override public Connection getConnection() throws SQLException { debugCodeCall("getConnection"); - return new JdbcConnection(url, null, userName, StringUtils.cloneCharArray(passwordChars)); + return new JdbcConnection(url, null, userName, StringUtils.cloneCharArray(passwordChars), false); } /** @@ -169,7 +169,7 @@ public Connection getConnection(String user, String password) if (isDebugEnabled()) { debugCode("getConnection(" + quote(user) + ", \"\")"); } - return new JdbcConnection(url, null, user, password); + return new JdbcConnection(url, null, user, password, false); } /** @@ -319,7 +319,7 @@ public Reference getReference() { public XAConnection getXAConnection() throws SQLException { debugCodeCall("getXAConnection"); return new JdbcXAConnection(factory, getNextId(XA_DATA_SOURCE), - new JdbcConnection(url, null, userName, StringUtils.cloneCharArray(passwordChars))); + new JdbcConnection(url, null, userName, StringUtils.cloneCharArray(passwordChars), false)); } /** @@ -336,7 +336,8 @@ public XAConnection getXAConnection(String user, String password) if (isDebugEnabled()) { debugCode("getXAConnection(" + quote(user) + ", \"\")"); } - return new JdbcXAConnection(factory, getNextId(XA_DATA_SOURCE), new JdbcConnection(url, null, user, password)); + return new JdbcXAConnection(factory, getNextId(XA_DATA_SOURCE), + new JdbcConnection(url, null, user, password, false)); } /** diff --git a/h2/src/main/org/h2/server/TcpServer.java b/h2/src/main/org/h2/server/TcpServer.java index 99264bbf62..fe90ba41ba 100644 --- a/h2/src/main/org/h2/server/TcpServer.java +++ b/h2/src/main/org/h2/server/TcpServer.java @@ -86,7 +86,8 @@ private void initManagementDb() throws SQLException { managementPassword = StringUtils.convertBytesToHex(MathUtils.secureRandomBytes(32)); } // avoid using the driver manager - JdbcConnection conn = new JdbcConnection("jdbc:h2:" + getManagementDbName(port), null, "", managementPassword); + JdbcConnection conn = new JdbcConnection("jdbc:h2:" + getManagementDbName(port), null, "", managementPassword, + false); managementDb = conn; try (Statement stat = conn.createStatement()) { @@ -446,7 +447,7 @@ public static synchronized void shutdown(String url, String password, } String db = getManagementDbName(port); for (int i = 0; i < 2; i++) { - try (JdbcConnection conn = new JdbcConnection("jdbc:h2:" + url + '/' + db, null, "", password)) { + try (JdbcConnection conn = new JdbcConnection("jdbc:h2:" + url + '/' + db, null, "", password, true)) { PreparedStatement prep = conn.prepareStatement("CALL STOP_SERVER(?, ?, ?)"); prep.setInt(1, all ? 0 : port); prep.setString(2, password); diff --git a/h2/src/main/org/h2/server/web/WebServer.java b/h2/src/main/org/h2/server/web/WebServer.java index 9e93917edc..1822065d5b 100644 --- a/h2/src/main/org/h2/server/web/WebServer.java +++ b/h2/src/main/org/h2/server/web/WebServer.java @@ -801,16 +801,10 @@ Connection getConnection(String driver, String databaseUrl, String user, String password, String userKey, NetworkConnectionInfo networkConnectionInfo) throws SQLException { driver = driver.trim(); databaseUrl = databaseUrl.trim(); - if (databaseUrl.startsWith("jdbc:h2:")) { - if (!allowSecureCreation || key == null || !key.equals(userKey)) { - if (ifExists) { - databaseUrl += ";FORBID_CREATION=TRUE"; - } - } - } // do not trim the password, otherwise an // encrypted H2 database with empty user password doesn't work - return JdbcUtils.getConnection(driver, databaseUrl, user.trim(), password, networkConnectionInfo); + return JdbcUtils.getConnection(driver, databaseUrl, user.trim(), password, networkConnectionInfo, + ifExists && (!allowSecureCreation || key == null || !key.equals(userKey))); } /** diff --git a/h2/src/main/org/h2/tools/CreateCluster.java b/h2/src/main/org/h2/tools/CreateCluster.java index f58facf1c8..04508c784e 100644 --- a/h2/src/main/org/h2/tools/CreateCluster.java +++ b/h2/src/main/org/h2/tools/CreateCluster.java @@ -102,7 +102,7 @@ private static void process(String urlSource, String urlTarget, String user, String password, String serverList) throws SQLException { // use cluster='' so connecting is possible // even if the cluster is enabled - try (JdbcConnection connSource = new JdbcConnection(urlSource + ";CLUSTER=''", null, user, password); + try (JdbcConnection connSource = new JdbcConnection(urlSource + ";CLUSTER=''", null, user, password, false); Statement statSource = connSource.createStatement()) { // enable the exclusive mode and close other connections, // so that data can't change while restoring the second database @@ -120,7 +120,7 @@ private static void performTransfer(Statement statSource, String urlTarget, Stri String serverList) throws SQLException { // Delete the target database first. - try (JdbcConnection connTarget = new JdbcConnection(urlTarget + ";CLUSTER=''", null, user, password); + try (JdbcConnection connTarget = new JdbcConnection(urlTarget + ";CLUSTER=''", null, user, password, false); Statement statTarget = connTarget.createStatement()) { statTarget.execute("DROP ALL OBJECTS DELETE FILES"); } @@ -129,7 +129,7 @@ private static void performTransfer(Statement statSource, String urlTarget, Stri Future threadFuture = startWriter(pipeReader, statSource); // Read data from pipe reader, restore on target. - try (JdbcConnection connTarget = new JdbcConnection(urlTarget, null, user, password); + try (JdbcConnection connTarget = new JdbcConnection(urlTarget, null, user, password, false); Statement statTarget = connTarget.createStatement()) { RunScript.execute(connTarget, pipeReader); diff --git a/h2/src/main/org/h2/tools/GUIConsole.java b/h2/src/main/org/h2/tools/GUIConsole.java index ed1ca4f2e1..c2b9d1e8c3 100644 --- a/h2/src/main/org/h2/tools/GUIConsole.java +++ b/h2/src/main/org/h2/tools/GUIConsole.java @@ -464,7 +464,7 @@ private void createDatabase() { } String url = "jdbc:h2:" + path; try { - new JdbcConnection(url, null, user, password).close(); + new JdbcConnection(url, null, user, password, false).close(); errorArea.setForeground(new Color(0, 0x99, 0)); errorArea.setText("Database was created successfully.\n\n" + "JDBC URL for H2 Console:\n" diff --git a/h2/src/main/org/h2/tools/Upgrade.java b/h2/src/main/org/h2/tools/Upgrade.java index 2ebd408973..ca77508028 100644 --- a/h2/src/main/org/h2/tools/Upgrade.java +++ b/h2/src/main/org/h2/tools/Upgrade.java @@ -163,7 +163,7 @@ public static boolean upgrade(String url, Properties info, int version) throws E unloadH2(driver); } rename(name, false); - try (JdbcConnection conn = new JdbcConnection(url, info, null, null)) { + try (JdbcConnection conn = new JdbcConnection(url, info, null, null, false)) { StringBuilder builder = StringUtils.quoteStringSQL(new StringBuilder("RUNSCRIPT FROM "), script) .append(scriptCommandSuffix); if (version <= 200) { diff --git a/h2/src/main/org/h2/util/JdbcUtils.java b/h2/src/main/org/h2/util/JdbcUtils.java index f261a8438d..03a126c0a1 100644 --- a/h2/src/main/org/h2/util/JdbcUtils.java +++ b/h2/src/main/org/h2/util/JdbcUtils.java @@ -267,7 +267,7 @@ public static void closeSilently(ResultSet rs) { */ public static Connection getConnection(String driver, String url, String user, String password) throws SQLException { - return getConnection(driver, url, user, password, null); + return getConnection(driver, url, user, password, null, false); } /** @@ -278,13 +278,14 @@ public static Connection getConnection(String driver, String url, * @param user the user name or {@code null} * @param password the password or {@code null} * @param networkConnectionInfo the network connection information, or {@code null} + * @param forbidCreation whether database creation is forbidden * @return the database connection * @throws SQLException on failure */ public static Connection getConnection(String driver, String url, String user, String password, - NetworkConnectionInfo networkConnectionInfo) throws SQLException { + NetworkConnectionInfo networkConnectionInfo, boolean forbidCreation) throws SQLException { if (url.startsWith(Constants.START_URL)) { - JdbcConnection connection = new JdbcConnection(url, null, user, password); + JdbcConnection connection = new JdbcConnection(url, null, user, password, forbidCreation); if (networkConnectionInfo != null) { connection.getSession().setNetworkConnectionInfo(networkConnectionInfo); } diff --git a/h2/src/test/org/h2/test/unit/TestTools.java b/h2/src/test/org/h2/test/unit/TestTools.java index 81c3f48d10..69b8c9a0b2 100644 --- a/h2/src/test/org/h2/test/unit/TestTools.java +++ b/h2/src/test/org/h2/test/unit/TestTools.java @@ -538,6 +538,12 @@ private void testJdbcDriverUtils() { assertEquals("08001", e.getSQLState()); assertEquals("Only java scheme is supported for JNDI lookups", e.getMessage()); } + try { + JdbcUtils.getConnection("org.h2.Driver", "jdbc:h2:mem:", "sa", "", null, true); + fail("Expected SQLException: " + ErrorCode.REMOTE_DATABASE_NOT_FOUND_1); + } catch (SQLException e) { + assertEquals(ErrorCode.REMOTE_DATABASE_NOT_FOUND_1, e.getErrorCode()); + } } private void testWrongServer() throws Exception {