package cd.casic.server; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.ExecCreateCmdResponse; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.TimeUnit; @SpringBootTest(classes = {OpsServerApplication.class}) @ActiveProfiles("local") public class DockerTest { // TODO 测试通过,明天继续整通用的 public static String executeParsedDockerCommand( String dockerHostUri, String dockerRunCommand, String[] commandInContainer, int execTimeoutMinutes) { // 1. 解析docker run命令 String[] parts = dockerRunCommand.split("\\s+"); if (parts.length < 6 || !"docker".equals(parts[0]) || !"run".equals(parts[1])) { throw new RuntimeException("Invalid docker run command format"); } // 2. 提取参数 String volumeArg = null; String imageName = null; String initialCommand = null; for (int i = 2; i < parts.length; i++) { if ("-v".equals(parts[i]) && i + 1 < parts.length) { volumeArg = parts[++i]; } else if ("-it".equals(parts[i])) { // 跳过交互式参数 } else if (imageName == null) { imageName = parts[i]; } else if (initialCommand == null) { initialCommand = parts[i]; } } if (volumeArg == null || imageName == null) { throw new RuntimeException("Missing required parameters in docker run command"); } // 3. 解析卷挂载参数 String[] volumeParts = volumeArg.split(":"); if (volumeParts.length != 2) { throw new RuntimeException("Invalid volume format, expected host_path:container_path"); } String hostPath = volumeParts[0]; String containerPath = volumeParts[1]; ApacheDockerHttpClient httpClient; try { httpClient = new ApacheDockerHttpClient.Builder().dockerHost(new URI(dockerHostUri)).build(); } catch (URISyntaxException e) { throw new RuntimeException(e); } DockerClient dockerClient = DockerClientBuilder.getInstance().withDockerHttpClient(httpClient).build(); try { // 5. 创建容器 HostConfig hostConfig = HostConfig.newHostConfig() .withBinds(Bind.parse(volumeArg)); CreateContainerResponse container = dockerClient.createContainerCmd(imageName) .withHostConfig(hostConfig) .withCmd(initialCommand) .withTty(true) .exec(); String containerId = container.getId(); // 6. 启动容器 dockerClient.startContainerCmd(containerId).exec(); try { // 7. 构建要在容器内执行的完整命令 String[] fullCommand = new String[commandInContainer.length + 2]; fullCommand[0] = "sh"; fullCommand[1] = "-c"; fullCommand[2] = "cd " + containerPath + " && " + StringUtils.join(commandInContainer, " "); // System.arraycopy(commandInContainer, 0, fullCommand, 3, commandInContainer.length - 1); // 8. 创建exec实例 ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId) .withAttachStdout(true) .withAttachStderr(true) .withCmd(fullCommand) .exec(); // 9. 执行命令并获取输出 String output = dockerClient.execStartCmd(execCreateCmdResponse.getId()) .exec(new ExecStartResultCallback()) .awaitOutput(execTimeoutMinutes, TimeUnit.MINUTES); return output; } finally { // 10. 清理容器 dockerClient.stopContainerCmd(containerId).exec(); dockerClient.removeContainerCmd(containerId).exec(); } } finally { try { dockerClient.close(); } catch (IOException e) { } } } private static class ExecStartResultCallback extends com.github.dockerjava.api.async.ResultCallback.Adapter { private final StringBuilder output = new StringBuilder(); @Override public void onNext(Frame frame) { output.append(new String(frame.getPayload())); } public String awaitOutput(long timeout, TimeUnit timeUnit) { try { super.awaitCompletion(timeout, timeUnit); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Command execution interrupted", e); } return output.toString(); } } // 示例用法 @Test public void test() { // *** 请将此处的远程 Docker TCP 地址替换为您实际的值 *** String remoteDockerHostUri = "tcp://175.6.27.228:22375"; // <-- !!! REPLACE THIS !!! // *** 提供您希望解析的 docker run 命令字符串 *** // 注意:此解析器仅支持简化的格式 String dockerRunCmdString = "docker run -v /home/yunqi/:/test -it aflplusplus/aflplusplus bash"; System.out.println("待解析并执行的 Docker run 命令: " + dockerRunCmdString); // 要在容器内执行的 afl-fuzz 命令及其参数 // 这部分仍然需要单独提供,因为解析器不负责从 docker run 命令中提取后续要 exec 的命令 String[] aflFuzzCommand = { "afl-fuzz", "-i", "case", "-o", "ai_afl", "-t", "3000", "-Q", "./CaseGenerator/testdata/libpng/libpng/pngfix", "@@" }; System.out.print("将在容器内执行的命令: "); for (String arg : aflFuzzCommand) { System.out.print(arg + " "); } System.out.println(); // 命令执行超时时间(分钟) int commandTimeoutMinutes = 60; // 例如,给 afl-fuzz 60 分钟运行时间 try { // 调用解析并执行的方法 System.out.println("\n开始执行解析后的 Docker 命令序列..."); String aflOutput = executeParsedDockerCommand( remoteDockerHostUri, dockerRunCmdString, aflFuzzCommand, commandTimeoutMinutes ); System.out.println("\n--- AFL-fuzz 命令的标准输出 ---"); System.out.println(aflOutput); System.out.println("------------------------------"); } catch (RuntimeException e) { System.err.println("\n--- 执行过程中发生错误 ---"); System.err.println(e.getMessage()); e.printStackTrace(); System.err.println("---------------------------"); } } }