diff --git a/src/main/java/core/packetproxy/common/FilterTextParser.java b/src/main/java/core/packetproxy/common/FilterTextParser.java index 1f436d4..52af459 100644 --- a/src/main/java/core/packetproxy/common/FilterTextParser.java +++ b/src/main/java/core/packetproxy/common/FilterTextParser.java @@ -62,9 +62,10 @@ public class FilterTextParser { { put("modified", 10); } { put("type", 11); } { put("encode", 12); } - { put("group", 13); } - { put("full_text", 14); } - { put("full_text_i", 15); } + { put("alpn", 13); } + { put("group", 14); } + { put("full_text", 15); } + { put("full_text_i", 16); } }; FilterTextParser(String str) { diff --git a/src/main/java/core/packetproxy/encode/EncodeHTTPBase.java b/src/main/java/core/packetproxy/encode/EncodeHTTPBase.java index e2f9df0..a1017d3 100644 --- a/src/main/java/core/packetproxy/encode/EncodeHTTPBase.java +++ b/src/main/java/core/packetproxy/encode/EncodeHTTPBase.java @@ -18,7 +18,7 @@ import java.io.InputStream; import packetproxy.http.Http; -import packetproxy.http2.HTTP2; +import packetproxy.http2.Http2; import packetproxy.model.Packet; public abstract class EncodeHTTPBase extends Encoder @@ -27,7 +27,7 @@ public enum HTTPVersion { HTTP1, HTTP2 } private HTTPVersion httpVersion; - private HTTP2 http2; + private Http2 http2; public EncodeHTTPBase() { super("http/1.1"); @@ -42,7 +42,7 @@ public EncodeHTTPBase(String ALPN) throws Exception { httpVersion = HTTPVersion.HTTP1; } else if (ALPN.equals("h2") || ALPN.equals("grpc")) { httpVersion = HTTPVersion.HTTP2; - http2 = new HTTP2(); + http2 = new Http2(); } else { httpVersion = HTTPVersion.HTTP1; } diff --git a/src/main/java/core/packetproxy/encode/EncodeHTTPStreamingResponse.java b/src/main/java/core/packetproxy/encode/EncodeHTTPStreamingResponse.java index 3a422d8..f21e454 100644 --- a/src/main/java/core/packetproxy/encode/EncodeHTTPStreamingResponse.java +++ b/src/main/java/core/packetproxy/encode/EncodeHTTPStreamingResponse.java @@ -18,8 +18,8 @@ import java.io.InputStream; import packetproxy.http.Http; -import packetproxy.http2.HTTP2; -import packetproxy.http2.StreamingResponse; +import packetproxy.http1.Http1StreamingResponse; +import packetproxy.http2.Http2StreamingResponse; import packetproxy.model.Packet; public class EncodeHTTPStreamingResponse extends Encoder @@ -28,7 +28,8 @@ public enum HTTPVersion { HTTP1, HTTP2 } private HTTPVersion httpVersion; - private StreamingResponse http2StreamingResponse; + private Http1StreamingResponse http1StreamingResponse; + private Http2StreamingResponse http2StreamingResponse; public EncodeHTTPStreamingResponse() { super("http/1.1"); @@ -39,12 +40,15 @@ public EncodeHTTPStreamingResponse(String ALPN) throws Exception { super(ALPN); if (ALPN == null) { httpVersion = HTTPVersion.HTTP1; + } else if (ALPN.equals("http/1.0") || ALPN.equals("http/1.1")) { + httpVersion = HTTPVersion.HTTP1; } else if (ALPN.equals("h2") || ALPN.equals("grpc")) { httpVersion = HTTPVersion.HTTP2; - http2StreamingResponse = new StreamingResponse(); } else { httpVersion = HTTPVersion.HTTP1; } + http1StreamingResponse = new Http1StreamingResponse(); + http2StreamingResponse = new Http2StreamingResponse(); } public HTTPVersion getHttpVersion() { return httpVersion; } @@ -53,47 +57,56 @@ public EncodeHTTPStreamingResponse(String ALPN) throws Exception { public String getName() { return "HTTP Streaming Response"; } + + @Override + public int checkRequestDelimiter(byte[] data) throws Exception { + if (this.httpVersion == HTTPVersion.HTTP1) { + return http1StreamingResponse.checkRequestDelimiter(data); + } else { + return http2StreamingResponse.checkDelimiter(data); + } + } @Override - public int checkDelimiter(byte[] data) throws Exception { + public int checkResponseDelimiter(byte[] data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return Http.parseHttpDelimiter(data); + return http1StreamingResponse.checkResponseDelimiter(data); } else { return http2StreamingResponse.checkDelimiter(data); } } @Override - public void clientRequestArrived(byte[] frames) throws Exception { + public void clientRequestArrived(byte[] data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - super.clientRequestArrived(frames); + http1StreamingResponse.clientRequestArrived(data); } else { - http2StreamingResponse.clientRequestArrived(frames); + http2StreamingResponse.clientRequestArrived(data); } } @Override - public void serverResponseArrived(byte[] frames) throws Exception { + public void serverResponseArrived(byte[] data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - super.serverResponseArrived(frames); + http1StreamingResponse.serverResponseArrived(data); } else { - http2StreamingResponse.serverResponseArrived(frames); + http2StreamingResponse.serverResponseArrived(data); } } @Override public byte[] passThroughClientRequest() throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return super.passThroughClientRequest(); + return http1StreamingResponse.passThroughClientRequest(); } else { return http2StreamingResponse.passThroughClientRequest(); } } - + @Override public byte[] passThroughServerResponse() throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return super.passThroughServerResponse(); + return http1StreamingResponse.passThroughServerResponse(); } else { return http2StreamingResponse.passThroughServerResponse(); } @@ -102,7 +115,7 @@ public byte[] passThroughServerResponse() throws Exception { @Override public byte[] clientRequestAvailable() throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return super.clientRequestAvailable(); + return http1StreamingResponse.clientRequestAvailable(); } else { return http2StreamingResponse.clientRequestAvailable(); } @@ -111,7 +124,7 @@ public byte[] clientRequestAvailable() throws Exception { @Override public byte[] serverResponseAvailable() throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return super.serverResponseAvailable(); + return http1StreamingResponse.serverResponseAvailable(); } else { return http2StreamingResponse.serverResponseAvailable(); } @@ -120,7 +133,7 @@ public byte[] serverResponseAvailable() throws Exception { @Override final public byte[] decodeServerResponse(byte[] input_data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return input_data; + return http1StreamingResponse.decodeServerResponse(input_data); } else { return http2StreamingResponse.decodeServerResponse(input_data); } @@ -129,7 +142,7 @@ final public byte[] decodeServerResponse(byte[] input_data) throws Exception { @Override public byte[] encodeServerResponse(byte[] input_data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return input_data; + return http1StreamingResponse.encodeServerResponse(input_data); } else { return http2StreamingResponse.encodeServerResponse(input_data); } @@ -138,7 +151,7 @@ public byte[] encodeServerResponse(byte[] input_data) throws Exception { @Override public byte[] decodeClientRequest(byte[] input_data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return input_data; + return http1StreamingResponse.decodeClientRequest(input_data); } else { return http2StreamingResponse.decodeClientRequest(input_data); } @@ -147,7 +160,7 @@ public byte[] decodeClientRequest(byte[] input_data) throws Exception { @Override public byte[] encodeClientRequest(byte[] input_data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return input_data; + return http1StreamingResponse.encodeClientRequest(input_data); } else { return http2StreamingResponse.encodeClientRequest(input_data); } @@ -196,8 +209,7 @@ public String getContentType(byte[] input_data) throws Exception { } @Override - public String getSummarizedResponse(Packet packet) - { + public String getSummarizedResponse(Packet packet) { String summary = ""; if (packet.getDecodedData().length == 0 && packet.getModifiedData().length == 0) { return ""; } try { @@ -213,8 +225,7 @@ public String getSummarizedResponse(Packet packet) } @Override - public String getSummarizedRequest(Packet packet) - { + public String getSummarizedRequest(Packet packet) { String summary = ""; if (packet.getDecodedData().length == 0 && packet.getModifiedData().length == 0) { return ""; } try { @@ -237,4 +248,9 @@ public void setGroupId(Packet packet) throws Exception { } } + @Override + public int checkDelimiter(byte[] input_data) throws Exception { + return input_data.length; + } + } diff --git a/src/main/java/core/packetproxy/gui/GUIOption.java b/src/main/java/core/packetproxy/gui/GUIOption.java index 593a580..2ecd23f 100644 --- a/src/main/java/core/packetproxy/gui/GUIOption.java +++ b/src/main/java/core/packetproxy/gui/GUIOption.java @@ -150,6 +150,12 @@ public void actionPerformed(ActionEvent ae) { panel.add(createSeparator()); + panel.add(createElement("Priority Order of HTTP Versions", I18nString.get("Set order of priority between HTTP1 and HTTP2."))); + GUIOptionHttp http = new GUIOptionHttp(); + panel.add(http.createPanel()); + + panel.add(createSeparator()); + panel.add(createElement("PacketProxy CA Certificates", I18nString.get("Export CA certificate used to view SSL packets. It needs to be registered in trusted CA list of PC/Mac/Linux/Android/iOS"))); JPanel caPanel = new JPanel(); diff --git a/src/main/java/core/packetproxy/gui/GUIOptionHttp.java b/src/main/java/core/packetproxy/gui/GUIOptionHttp.java new file mode 100644 index 0000000..fd37ae7 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/GUIOptionHttp.java @@ -0,0 +1,61 @@ +package packetproxy.gui; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import javax.swing.BoxLayout; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import packetproxy.model.ConfigString; + +public class GUIOptionHttp { + + private JComboBox combo = new JComboBox<>(); + private ConfigString configPriority = new ConfigString("PriorityOrderOfHttpVersions"); + + public GUIOptionHttp() throws Exception { + combo.setPrototypeDisplayValue("xxxxxxx"); + combo.addItem("HTTP1"); + combo.addItem("HTTP2"); + combo.setMaximumRowCount(combo.getItemCount()); + String priority = configPriority.getString(); + if (priority == null || priority.length() == 0) { + configPriority.setString("HTTP2"); + priority = configPriority.getString(); + } + combo.setSelectedItem(priority); + combo.addItemListener(new ItemListener(){ + @Override + public void itemStateChanged(ItemEvent event) { + try { + if (event.getStateChange() != ItemEvent.SELECTED || combo.getSelectedItem() == null) { + return; + } + String priority = (String)combo.getSelectedItem(); + configPriority.setString(priority); + combo.setSelectedItem(priority); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + combo.setMaximumSize(new Dimension(combo.getPreferredSize().width, combo.getMinimumSize().height)); + } + + public JPanel createPanel() throws Exception { + JPanel panel = new JPanel(); + panel.setBackground(Color.WHITE); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.add(combo); + panel.add(new JLabel("has a high priority")); + panel.setAlignmentX(Component.LEFT_ALIGNMENT); + panel.setMaximumSize(new Dimension(Short.MAX_VALUE, panel.getMaximumSize().height)); + return panel; + } + +} diff --git a/src/main/java/core/packetproxy/http/Http.java b/src/main/java/core/packetproxy/http/Http.java index 7176ec1..21d6175 100644 --- a/src/main/java/core/packetproxy/http/Http.java +++ b/src/main/java/core/packetproxy/http/Http.java @@ -261,7 +261,7 @@ private byte[] getCookedBody(HttpHeader header, byte[] rawBody) throws Exception if (enc.isPresent() && enc.get().equalsIgnoreCase("chunked")) { header.removeAll(headerName); - cookedBody = getChankedHttpBody(cookedBody); + cookedBody = getChankedHttpBodyFussy(cookedBody); if (cookedBody == null) return null; } @@ -495,6 +495,31 @@ private static byte[] gzip(byte[] input_data) throws Exception return out.toByteArray(); } + private static byte[] getChankedHttpBodyFussy(byte[] input_data) throws Exception + { + // TODO 改行コードの対応 + byte[] search_word = new String("\r\n").getBytes(); + int index = 0; + int start_index = 0; + byte[] body = new byte[0]; + while ((index = Utils.indexOf(input_data, start_index, input_data.length, search_word)) >= 0) { + try { + byte[] chank_header = ArrayUtils.subarray(input_data, start_index, index); + String chank_length_str = new String(chank_header, "UTF-8").replaceAll("^0+([^0].*)$", "$1"); + int chank_length = Integer.parseInt(chank_length_str.trim(), 16); + if (chank_length == 0) { + return body; + } + byte[] chank = ArrayUtils.subarray(input_data, index + search_word.length, index + search_word.length + chank_length); + body = ArrayUtils.addAll(body, chank); + start_index = index + search_word.length*2 + chank_length; + } catch (Exception e) { + return body; + } + } + return body; + } + private static byte[] getChankedHttpBody(byte[] input_data) throws Exception { // TODO 改行コードの対応 @@ -514,9 +539,7 @@ private static byte[] getChankedHttpBody(byte[] input_data) throws Exception body = ArrayUtils.addAll(body, chank); start_index = index + search_word.length*2 + chank_length; } catch (Exception e) { - e.printStackTrace(); - Binary b = new Binary(input_data); - PacketProxyUtility.getInstance().packetProxyLog(new String(b.toHexString().toString())); + return null; } } return null; diff --git a/src/main/java/core/packetproxy/http/Https.java b/src/main/java/core/packetproxy/http/Https.java index 8b1c375..f6de2f0 100644 --- a/src/main/java/core/packetproxy/http/Https.java +++ b/src/main/java/core/packetproxy/http/Https.java @@ -26,6 +26,7 @@ import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import javax.net.ServerSocketFactory; @@ -49,6 +50,7 @@ import packetproxy.CertCacheManager; import packetproxy.common.ClientKeyManager; import packetproxy.common.Utils; +import packetproxy.model.ConfigString; import packetproxy.model.Server; import packetproxy.model.Servers; import packetproxy.model.CAs.CA; @@ -106,7 +108,16 @@ public static SSLSocket[] createBothSideSSLSockets(Socket clientSocket, InputStr serverSSLSocket[0] = (SSLSocket) createSSLSocketFactory().createSocket(serverSocket, null, true); serverSSLSocket[0].setUseClientMode(true); SSLParameters sp = serverSSLSocket[0].getSSLParameters(); - sp.setApplicationProtocols(clientProtocols.toArray(new String[clientProtocols.size()])); + + List alpns = new LinkedList<>(clientProtocols); + if (new ConfigString("PriorityOrderOfHttpVersions").getString().equals("HTTP1")) { + if (alpns.contains("http/1.1") || alpns.contains("http/1.0")) { + alpns.remove("h2"); + alpns.remove("grpc"); + } + } + sp.setApplicationProtocols(alpns.toArray(new String[alpns.size()])); + serverSSLSocket[0].setSSLParameters(sp); serverSSLSocket[0].startHandshake(); } catch (Exception e) { diff --git a/src/main/java/core/packetproxy/http1/Http1StreamingResponse.java b/src/main/java/core/packetproxy/http1/Http1StreamingResponse.java new file mode 100644 index 0000000..b9ddbe9 --- /dev/null +++ b/src/main/java/core/packetproxy/http1/Http1StreamingResponse.java @@ -0,0 +1,93 @@ +package packetproxy.http1; + +import java.io.ByteArrayOutputStream; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; + +import packetproxy.common.StringUtils; +import packetproxy.http.Http; +import packetproxy.model.Packet; +import packetproxy.model.Packets; + +public class Http1StreamingResponse +{ + private ByteArrayOutputStream clientInput = new ByteArrayOutputStream(); + private ByteArrayOutputStream serverInput = new ByteArrayOutputStream(); + + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(); + private boolean headerReceived = false; + + public int checkRequestDelimiter(byte[] data) throws Exception { return Http.parseHttpDelimiter(data); } + public void clientRequestArrived(byte[] data) throws Exception { clientInput.write(data); } + public byte[] passThroughClientRequest() throws Exception { return null; }; + public byte[] clientRequestAvailable() throws Exception { byte[] ret = clientInput.toByteArray(); clientInput.reset(); return ret; } + public byte[] decodeClientRequest(byte[] input_data) throws Exception { return input_data; } + public byte[] encodeClientRequest(byte[] input_data) throws Exception { return input_data; } + + public int checkResponseDelimiter(byte[] data) throws Exception { return data.length; } + public void serverResponseArrived(byte[] data) throws Exception { serverInput.write(data); } + public byte[] passThroughServerResponse() throws Exception { + buffer.write(serverInput.toByteArray()); + if (headerReceived == false) { + int endOfHeader = StringUtils.binaryFind(buffer.toByteArray(), "\r\n\r\n".getBytes()); + if (endOfHeader > 0) { + byte[] header = ArrayUtils.subarray(buffer.toByteArray(), 0, endOfHeader + 2); + byte[] body = ArrayUtils.subarray(buffer.toByteArray(), endOfHeader + 4, buffer.size()); + byte[] newHeader = ArrayUtils.addAll(header, String.format("X-PacketProxy-HTTP1-UUID: %s\r\n\r\n", StringUtils.randomUUID()).getBytes()); + byte[] newHttp = ArrayUtils.addAll(newHeader, body); + buffer.reset(); + buffer.write(newHttp); + headerBuffer.write(newHeader); + headerReceived = true; + } + } else { + Http http; + int delim = Http.parseHttpDelimiter(buffer.toByteArray()); + if (delim > 0) { + byte[] httpData = ArrayUtils.subarray(buffer.toByteArray(), 0, delim); + byte[] remaining = ArrayUtils.subarray(buffer.toByteArray(), delim, buffer.size()); + headerReceived = false; + buffer.reset(); + buffer.write(remaining); + http = new Http(httpData); + } else { + http = new Http(buffer.toByteArray()); + } + Thread guiHistoryUpdater = new Thread(new Runnable() { + public void run() { + try { + if (http.getBody() != null && http.getBody().length > 0) { + List packets = Packets.getInstance().queryFullText(http.getFirstHeader("X-PacketProxy-HTTP1-UUID")); + for (Packet packet: packets) { + Packet p = Packets.getInstance().query(packet.getId()); + p.setDecodedData(http.toByteArray()); + p.setModifiedData(http.toByteArray()); + Packets.getInstance().update(p); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + guiHistoryUpdater.start(); + } + byte[] out = serverInput.toByteArray(); + serverInput.reset(); + return out; + } + public byte[] serverResponseAvailable() throws Exception { + if (headerBuffer.size() == 0) { + return null; + } + byte[] out = headerBuffer.toByteArray(); + headerBuffer.reset(); + return out; + } + public byte[] decodeServerResponse(byte[] input_data) throws Exception { return input_data; } + public byte[] encodeServerResponse(byte[] input_data) throws Exception { return null; } + + +} diff --git a/src/main/java/core/packetproxy/http2/HTTP2.java b/src/main/java/core/packetproxy/http2/Http2.java similarity index 95% rename from src/main/java/core/packetproxy/http2/HTTP2.java rename to src/main/java/core/packetproxy/http2/Http2.java index 770e65f..38ee421 100644 --- a/src/main/java/core/packetproxy/http2/HTTP2.java +++ b/src/main/java/core/packetproxy/http2/Http2.java @@ -30,12 +30,12 @@ import packetproxy.http2.frames.HeadersFrame; import packetproxy.model.Packet; -public class HTTP2 extends FramesBase +public class Http2 extends FramesBase { private StreamManager clientStreamManager = new StreamManager(); private StreamManager serverStreamManager = new StreamManager(); - public HTTP2() throws Exception { + public Http2() throws Exception { super(); } @@ -45,14 +45,9 @@ public String getName() { } @Override - protected byte[] passFramesToDecodeClientRequest(List frames) throws Exception { - return filterFrames(clientStreamManager, frames); - } - + protected byte[] passFramesToDecodeClientRequest(List frames) throws Exception { return filterFrames(clientStreamManager, frames); } @Override - protected byte[] passFramesToDecodeServerResponse(List frames) throws Exception { - return filterFrames(serverStreamManager, frames); - } + protected byte[] passFramesToDecodeServerResponse(List frames) throws Exception { return filterFrames(serverStreamManager, frames); } private byte[] filterFrames(StreamManager streamManager, List frames) throws Exception { for (Frame frame : frames) { diff --git a/src/main/java/core/packetproxy/http2/StreamingResponse.java b/src/main/java/core/packetproxy/http2/Http2StreamingResponse.java similarity index 98% rename from src/main/java/core/packetproxy/http2/StreamingResponse.java rename to src/main/java/core/packetproxy/http2/Http2StreamingResponse.java index c83471f..6ae4f7e 100644 --- a/src/main/java/core/packetproxy/http2/StreamingResponse.java +++ b/src/main/java/core/packetproxy/http2/Http2StreamingResponse.java @@ -33,12 +33,12 @@ import packetproxy.model.Packet; import packetproxy.model.Packets; -public class StreamingResponse extends FramesBase +public class Http2StreamingResponse extends FramesBase { private StreamManager clientStreamManager = new StreamManager(); private StreamManager serverStreamManager = new StreamManager(); - public StreamingResponse() throws Exception { + public Http2StreamingResponse() throws Exception { } StreamManager stream = new StreamManager(); @@ -136,7 +136,6 @@ private byte[] filterFrames(StreamManager streamManager, List frames) thr @Override protected byte[] decodeClientRequestFromFrames(byte[] frames) throws Exception { return decodeFromFrames(frames); } - @Override protected byte[] decodeServerResponseFromFrames(byte[] frames) throws Exception { return decodeFromFrames(frames); } @@ -161,7 +160,6 @@ private byte[] decodeFromFrames(byte[] frames) throws Exception { @Override protected byte[] encodeClientRequestToFrames(byte[] http) throws Exception { return encodeToFrames(http, super.getClientHpackEncoder()); } - @Override protected byte[] encodeServerResponseToFrames(byte[] http) throws Exception { return null; }