Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/log.pp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env packetproxy-cli

log
exit
21 changes: 13 additions & 8 deletions src/main/java/core/packetproxy/PacketProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.sql.SQLException;
import javax.swing.*;
import org.apache.commons.io.IOUtils;
import packetproxy.common.ClientKeyManager;
import packetproxy.common.I18nString;
import packetproxy.common.Utils;
import packetproxy.gui.GUIMain;
Expand All @@ -37,11 +36,21 @@ public PacketProxy() throws Exception {
}

public static void main(String[] args) throws Exception {
// バイナリへの引数の解釈とセット
String gulpMode = getOption("--gulp", args);
Logging.init(gulpMode != null);
String settingsJson = getOption("--settings-json", args);
AppInitializer.setArgs(gulpMode != null, settingsJson);
AppInitializer.initCore();

if (gulpMode != null) {
String settingsJson = getOption("--settings-json", args);
try {
AppInitializer.initGulp();
AppInitializer.initComponents();
} catch (Exception e) {
Logging.errWithStackTrace(e);
System.exit(1);
}

Logging.log("Gulp Mode: " + settingsJson);
GulpTerminal.run(settingsJson, gulpMode);
System.exit(0);
Expand Down Expand Up @@ -106,11 +115,7 @@ private static String getOption(String option, String[] args) {

public void start() throws Exception {
startGUI();
ClientKeyManager.initialize();
listenPortManager = ListenPortManager.getInstance();
// encoderのロードに1,2秒かかるのでここでロードをしておく(ここでしておかないと通信がacceptされたタイミングでロードする)
EncoderManager.getInstance();
VulCheckerManager.getInstance();
AppInitializer.initComponents();
}

private void startGUI() throws Exception {
Expand Down
177 changes: 177 additions & 0 deletions src/main/kotlin/core/packetproxy/AppInitializer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package packetproxy

import java.nio.file.Paths
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import kotlin.system.exitProcess
import packetproxy.common.ClientKeyManager
import packetproxy.common.ConfigIO
import packetproxy.common.Utils
import packetproxy.model.Database
import packetproxy.model.Packets
import packetproxy.util.Logging

object AppInitializer {
private var isGulp = false // Gulp modeか否か
private var settingsPath = "" // 設定用JSONのファイルpath

private var isCoreNotReady = true
private var isGulpNotReady = true
private var isComponentsNotReady = true

@JvmStatic
fun setArgs(isGulp: Boolean, settingsPath: String?) {
this.isGulp = isGulp
this.settingsPath = settingsPath ?: ""
}

/** GUI / CLI(Gulp) に関連なく最初に実行するべき初期化を一度のみ実行する */
@JvmStatic
fun initCore() {
check(isCoreNotReady) { "initCore() has already been done !" }

// ログ機能のエラーについては標準エラー出力への出力を行い終了する
try {
Logging.init(isGulp)
} catch (e: Exception) {
System.err.println("[FATAL ERROR]: Logging.init(), exit 1")
System.err.println(e.message)
e.printStackTrace(System.err)

exitProcess(1)
}

Logging.log("Launching PacketProxy !")

isCoreNotReady = false
}

/** CLI(Gulp) 専用の初期化を実行 GUI ではGUIMainなどで実行されている処理 */
@JvmStatic
fun initGulp() {
check(isGulp) { "initGulp() is for gulp mode only !" }
check(isGulpNotReady) { "initGulp() has already been done !" }

initDatabase()
initPackets()

isGulpNotReady = false
}

private fun initDatabase() {
val dbPath =
Paths.get(System.getProperty("user.home"), ".packetproxy", "db", "resources.sqlite3")
Database.getInstance().openAt(dbPath.toString())
Logging.log("Databaseを初期化しました: $dbPath")
}

private fun initPackets() {
Packets.getInstance(false) // CLIモードでは履歴を復元しない
Logging.log("Packetsを初期化しました")
}

/**
* GUI / CLI(Gulp) に共通の初期化を GUI の表示よりも後回しして良い初期化を一度のみ実行する
*
* 並列処理による高速化:
* - EncoderManagerとVulCheckerManagerは完全に独立しているため、並列実行可能
* - ClientKeyManagerとListenPortManagerはDatabaseに依存しているが、
* Databaseは既に初期化済み(GUIモードではstartGUI()で、CLIモードではinitGulp()で初期化)
* かつ、それぞれ異なるテーブル(ClientCertificates/Servers/ListenPorts)にアクセスするため、 読み取り操作のみであれば並列実行可能
*
* 依存関係の整理:
* 1. ClientKeyManager: ClientCertificates → Database (読み取りのみ)
* 2. ListenPortManager: ListenPorts + Servers → Database (読み取りのみ)
* 3. EncoderManager: クラスパス/JARファイルのスキャン(Database非依存)
* 4. VulCheckerManager: クラスパスのスキャン(Database非依存)
*/
@JvmStatic
fun initComponents() {
check(isComponentsNotReady) { "initComponents() has already been done !" }

// Database依存のコンポーネントを並列実行
// 注意: Databaseは既に初期化済みであることを前提とする
val dbDependentFuture1 = CompletableFuture.runAsync { initClientKeyManager() }

val dbDependentFuture2 = CompletableFuture.runAsync { initListenPortManager() }

// Database非依存のコンポーネントを並列実行
val independentFuture1 =
CompletableFuture.runAsync {
// encoderのロードに1,2秒かかるのでここでロードをしておく(ここでしておかないと通信がacceptされたタイミングでロードする)
initEncoderManager()
}

val independentFuture2 = CompletableFuture.runAsync { initVulCheckerManager() }

// 全ての初期化が完了するまで待機
try {
CompletableFuture.allOf(
dbDependentFuture1,
dbDependentFuture2,
independentFuture1,
independentFuture2,
)
.get()

Logging.log("全てのコンポーネントの初期化が完了しました")
} catch (e: ExecutionException) {
// ExecutionExceptionは、CompletableFuture内で発生した例外をラップした例外
// e.causeで実際の例外を取得できる
val cause = e.cause
if (cause is Exception) {
Logging.errWithStackTrace(cause)
throw cause
} else {
Logging.errWithStackTrace(e)
throw e
}
} catch (e: InterruptedException) {
Logging.errWithStackTrace(e)
Thread.currentThread().interrupt()
throw RuntimeException("初期化が中断されました", e)
}

loadSettingsFromJson()

isComponentsNotReady = false
}

private fun initClientKeyManager() {
ClientKeyManager.initialize()
Logging.log("ClientKeyManagerを初期化しました")
}

private fun initListenPortManager() {
ListenPortManager.getInstance()
Logging.log("ListenPortManagerを初期化しました")
}

private fun initEncoderManager() {
EncoderManager.getInstance()
Logging.log("EncoderManagerを初期化しました")
}

private fun initVulCheckerManager() {
VulCheckerManager.getInstance()
Logging.log("VulCheckerManagerを初期化しました")
}

/** JSON設定ファイルを読み込んで適用 ListenPortManager初期化後に呼び出すことで、設定ファイル内の有効なプロキシが自動的に開始される */
private fun loadSettingsFromJson() {
if (settingsPath.isEmpty()) return

try {
val jsonBytes = Utils.readfile(settingsPath)
val json = String(jsonBytes, Charsets.UTF_8)

val configIO = ConfigIO()
configIO.setOptions(json)

Logging.log("設定ファイルを正常に読み込みました: $settingsPath")
} catch (e: Exception) {
Logging.err("設定ファイルの読み込みに失敗しました: ${e.message}", e)
Logging.errWithStackTrace(e)
}
}
}
23 changes: 15 additions & 8 deletions src/main/kotlin/core/packetproxy/gulp/CLIModeHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
*/
package packetproxy.cli

import core.packetproxy.gulp.command.EchoCommand
import core.packetproxy.gulp.command.LogCommand
import core.packetproxy.gulp.command.SourceCommand
import org.jline.builtins.Completers.TreeCompleter
import org.jline.builtins.Completers.TreeCompleter.node
import packetproxy.common.I18nString
import packetproxy.gulp.CommandContext
import packetproxy.gulp.ParsedCommand

/** CLIモードのハンドラーインターフェース 各モード(encode/decode)で異なるコマンド処理と補完を提供 */
Expand All @@ -32,6 +34,7 @@ abstract class CLIModeHandler {
node("switch"),
node("decode"),
node("encode"),
node("echo"),
node("log"),
node("source"),
node("help"),
Expand All @@ -49,33 +52,37 @@ abstract class CLIModeHandler {
* コマンドを処理
*
* @param parsed コマンド
* @param ctx コマンドコンテキスト
*/
suspend fun handleCommand(parsed: ParsedCommand) {
suspend fun handleCommand(parsed: ParsedCommand, ctx: CommandContext) {
when (parsed.cmd) {
"help" -> println(getHelpMessage())
"help" -> ctx.println(getHelpMessage())

".",
"source" -> SourceCommand(parsed)
"source" -> SourceCommand(parsed, ctx)

"l",
"log" -> LogCommand(parsed)
"log" -> LogCommand(parsed, ctx)

else -> extensionCommand(parsed)
"echo" -> EchoCommand(parsed, ctx)

else -> extensionCommand(parsed, ctx)
}
}

protected fun commandNotDefined(parsed: ParsedCommand) {
println(I18nString.get("command not defined: %s", parsed.raw))
protected fun commandNotDefined(parsed: ParsedCommand, ctx: CommandContext) {
ctx.println(I18nString.get("command not defined: %s", parsed.raw))
}

abstract fun getOppositeMode(): CLIModeHandler

protected abstract fun extensionCommand(parsed: ParsedCommand)
protected abstract fun extensionCommand(parsed: ParsedCommand, ctx: CommandContext)

fun getHelpMessage(): String {
return """共通コマンド:
exit - 終了
help - ヘルプ
echo <args> - 引数を出力
l, log - ログ継続出力
s, switch - Mode切り替え

Expand Down
19 changes: 18 additions & 1 deletion src/main/kotlin/core/packetproxy/gulp/CommandContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,29 @@ package packetproxy.gulp
import kotlinx.coroutines.Job
import packetproxy.cli.CLIModeHandler
import packetproxy.cli.EncodeModeHandler
import packetproxy.gulp.output.CommandOutput
import packetproxy.gulp.output.ConsoleOutput
import packetproxy.gulp.output.OutputStyle

class CommandContext {
/**
* コマンド実行コンテキスト
*
* @param output 出力先(デフォルト: 標準出力)
*/
class CommandContext(val output: CommandOutput = ConsoleOutput) {
var currentHandler: CLIModeHandler = EncodeModeHandler
var executionJob: Job? = null

fun cancelJob() {
executionJob?.cancel()
}

// 便利メソッド: 出力
fun println(text: String = "") = output.println(text)

fun print(text: String) = output.print(text)

// スタイル(色付け)へのショートカット
val style: OutputStyle
get() = output.style
}
7 changes: 4 additions & 3 deletions src/main/kotlin/core/packetproxy/gulp/DecodeModeHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.fusesource.jansi.Ansi
import org.fusesource.jansi.Ansi.Color.CYAN
import org.jline.builtins.Completers.TreeCompleter
import org.jline.builtins.Completers.TreeCompleter.node
import packetproxy.gulp.CommandContext
import packetproxy.gulp.ParsedCommand

/** Decode Modeのハンドラー */
Expand All @@ -35,11 +36,11 @@ object DecodeModeHandler : CLIModeHandler() {
return EncodeModeHandler
}

override fun extensionCommand(parsed: ParsedCommand) {
override fun extensionCommand(parsed: ParsedCommand, ctx: CommandContext) {
when (parsed.cmd) {
"status" -> println("Decode Mode !")
"status" -> ctx.println("Decode Mode !")

else -> commandNotDefined(parsed)
else -> commandNotDefined(parsed, ctx)
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/main/kotlin/core/packetproxy/gulp/EncodeModeHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.fusesource.jansi.Ansi.Color.GREEN
import org.jline.builtins.Completers.TreeCompleter
import org.jline.builtins.Completers.TreeCompleter.node
import org.jline.reader.impl.completer.StringsCompleter
import packetproxy.gulp.CommandContext
import packetproxy.gulp.ParsedCommand

/** Encode Modeのハンドラー */
Expand All @@ -39,11 +40,11 @@ object EncodeModeHandler : CLIModeHandler() {
return DecodeModeHandler
}

override fun extensionCommand(parsed: ParsedCommand) {
override fun extensionCommand(parsed: ParsedCommand, ctx: CommandContext) {
when (parsed.cmd) {
"status" -> println("Encode Mode !")
"status" -> ctx.println("Encode Mode !")

else -> commandNotDefined(parsed)
else -> commandNotDefined(parsed, ctx)
}
}

Expand Down
Loading