001/* 002 * acme4j - Java ACME client 003 * 004 * Copyright (C) 2017 Richard "Shred" Körber 005 * http://acme4j.shredzone.org 006 * 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 013 */ 014package org.shredzone.acme4j.it; 015 016import java.io.IOException; 017import java.util.Collections; 018import java.util.Objects; 019 020import org.apache.http.HttpStatus; 021import org.apache.http.client.ClientProtocolException; 022import org.apache.http.client.HttpClient; 023import org.apache.http.client.methods.HttpPost; 024import org.apache.http.entity.ContentType; 025import org.apache.http.entity.StringEntity; 026import org.apache.http.impl.client.HttpClients; 027import org.apache.http.util.EntityUtils; 028import org.shredzone.acme4j.toolbox.JSONBuilder; 029 030/** 031 * The BammBamm client connects to the pebble-challtestsrv. 032 */ 033public class BammBammClient { 034 private static final HttpClient CLIENT = HttpClients.createDefault(); 035 036 private final String baseUrl; 037 038 /** 039 * Creates a new BammBamm client. 040 * 041 * @param baseUrl 042 * Base URL of the pebble-challtestsrv server to connect to. 043 */ 044 public BammBammClient(String baseUrl) { 045 this.baseUrl = Objects.requireNonNull(baseUrl) + '/'; 046 } 047 048 /** 049 * Adds a HTTP token. 050 * 051 * @param token 052 * Token to add 053 * @param challenge 054 * Challenge to respond with 055 */ 056 public void httpAddToken(String token, String challenge) throws IOException { 057 var jb = new JSONBuilder(); 058 jb.put("token", token); 059 jb.put("content", challenge); 060 sendRequest("add-http01", jb.toString()); 061 } 062 063 /** 064 * Removes a HTTP token. 065 * 066 * @param token 067 * Token to remove 068 */ 069 public void httpRemoveToken(String token) throws IOException { 070 var jb = new JSONBuilder(); 071 jb.put("token", token); 072 sendRequest("del-http01", jb.toString()); 073 } 074 075 /** 076 * Adds an A Record to the DNS. Only one A Record is supported per domain. If another 077 * A Record is set, it will replace the existing one. 078 * 079 * @param domain 080 * Domain of the A Record 081 * @param ip 082 * IP address or domain name. If a domain name is used, it will be resolved 083 * and the IP will be used. 084 */ 085 public void dnsAddARecord(String domain, String ip) throws IOException { 086 var jb = new JSONBuilder(); 087 jb.put("host", domain); 088 jb.array("addresses", Collections.singletonList(ip)); 089 sendRequest("add-a", jb.toString()); 090 } 091 092 /** 093 * Removes an A Record from the DNS. 094 * 095 * @param domain 096 * Domain to remove the A Record from 097 */ 098 public void dnsRemoveARecord(String domain) throws IOException { 099 var jb = new JSONBuilder(); 100 jb.put("host", domain); 101 sendRequest("clear-a", jb.toString()); 102 } 103 104 /** 105 * Adds a TXT Record to the DNS. Only one TXT Record is supported per domain. If 106 * another TXT Record is set, it will replace the existing one. 107 * 108 * @param domain 109 * Domain name to add the TXT Record to 110 * @param txt 111 * TXT record to add 112 */ 113 public void dnsAddTxtRecord(String domain, String txt) throws IOException { 114 var jb = new JSONBuilder(); 115 jb.put("host", domain); 116 jb.put("value", txt); 117 sendRequest("set-txt", jb.toString()); 118 } 119 120 /** 121 * Removes a TXT Record from the DNS. 122 * 123 * @param domain 124 * Domain to remove the TXT Record from 125 */ 126 public void dnsRemoveTxtRecord(String domain) throws IOException { 127 var jb = new JSONBuilder(); 128 jb.put("host", domain); 129 sendRequest("clear-txt", jb.toString()); 130 } 131 132 /** 133 * Adds a CNAME Record to the DNS. Only one CNAME Record is supported per domain. If 134 * another CNAME Record is set, it will replace the existing one. 135 * 136 * @param domain 137 * Domain to add the CNAME Record to 138 * @param cname 139 * CNAME Record to add 140 * @since 2.9 141 */ 142 public void dnsAddCnameRecord(String domain, String cname) throws IOException { 143 var jb = new JSONBuilder(); 144 jb.put("host", domain); 145 jb.put("target", cname); 146 sendRequest("set-cname", jb.toString()); 147 } 148 149 /** 150 * Removes a CNAME Record from the DNS. 151 * 152 * @param domain 153 * Domain to remove the CNAME Record from 154 * @since 2.9 155 */ 156 public void dnsRemoveCnameRecord(String domain) throws IOException { 157 var jb = new JSONBuilder(); 158 jb.put("host", domain); 159 sendRequest("clear-cname", jb.toString()); 160 } 161 162 /** 163 * Simulates a SERVFAIL for the given domain. 164 * 165 * @param domain 166 * Domain that will give a SERVFAIL response 167 * @since 2.9 168 */ 169 public void dnsAddServFailRecord(String domain) throws IOException { 170 var jb = new JSONBuilder(); 171 jb.put("host", domain); 172 sendRequest("set-servfail", jb.toString()); 173 } 174 175 /** 176 * Removes a SERVFAIL Record from the DNS. 177 * 178 * @param domain 179 * Domain to remove the SEVFAIL Record from 180 * @since 2.9 181 */ 182 public void dnsRemoveServFailRecord(String domain) throws IOException { 183 var jb = new JSONBuilder(); 184 jb.put("host", domain); 185 sendRequest("clear-servfail", jb.toString()); 186 } 187 188 /** 189 * Adds a certificate for TLS-ALPN tests. 190 * 191 * @param domain 192 * Certificate domain to be added 193 * @param keyauth 194 * Key authorization to be used for validation 195 */ 196 public void tlsAlpnAddCertificate(String domain, String keyauth) throws IOException { 197 var jb = new JSONBuilder(); 198 jb.put("host", domain); 199 jb.put("content", keyauth); 200 sendRequest("add-tlsalpn01", jb.toString()); 201 } 202 203 /** 204 * Removes a certificate. 205 * 206 * @param domain 207 * Certificate domain to be removed 208 */ 209 public void tlsAlpnRemoveCertificate(String domain) throws IOException { 210 var jb = new JSONBuilder(); 211 jb.put("host", domain); 212 sendRequest("del-tlsalpn01", jb.toString()); 213 } 214 215 /** 216 * Sends a request to the pebble-challtestsrv. 217 * 218 * @param call 219 * Endpoint to be called 220 * @param body 221 * JSON body 222 */ 223 private void sendRequest(String call, String body) throws IOException { 224 try { 225 var httppost = new HttpPost(baseUrl + call); 226 httppost.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON)); 227 228 var response = CLIENT.execute(httppost); 229 230 EntityUtils.consume(response.getEntity()); 231 232 if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { 233 throw new IOException(response.getStatusLine().getReasonPhrase()); 234 } 235 } catch (ClientProtocolException ex) { 236 throw new IOException(ex); 237 } 238 } 239 240}