use of org.parosproxy.paros.network.HttpInputStream in project zaproxy by zaproxy.
the class ProxyThread method beginSSL.
/**
* @param targethost the host where you want to connect to
* @throws IOException if an error occurred while establishing the SSL/TLS connection
*/
private void beginSSL(String targethost) throws IOException {
// ZAP: added parameter 'targethost'
try {
inSocket = HttpSender.getSSLConnector().createTunnelServerSocket(targethost, inSocket);
} catch (MissingRootCertificateException e) {
// throw again, cause will be catched later.
throw new MissingRootCertificateException(e);
} catch (Exception e) {
// ZAP: transform for further processing
throw new IOException("Error while establishing SSL connection for '" + targethost + "'!", e);
}
httpIn = new HttpInputStream(inSocket);
httpOut = new HttpOutputStream(inSocket.getOutputStream());
}
use of org.parosproxy.paros.network.HttpInputStream in project zaproxy by zaproxy.
the class API method handleApiRequest.
public boolean handleApiRequest(HttpRequestHeader requestHeader, HttpInputStream httpIn, HttpOutputStream httpOut, boolean force) throws IOException {
String url = requestHeader.getURI().toString();
Format format = Format.OTHER;
ApiImplementor callbackImpl = null;
ApiImplementor shortcutImpl = null;
// Check for callbacks
if (url.contains(CALL_BACK_URL)) {
if (!isPermittedAddr(requestHeader)) {
return true;
}
logger.debug("handleApiRequest Callback: " + url);
for (Entry<String, ApiImplementor> callback : callBacks.entrySet()) {
if (url.startsWith(callback.getKey())) {
callbackImpl = callback.getValue();
break;
}
}
}
String path = requestHeader.getURI().getPath();
if (path != null) {
for (Entry<String, ApiImplementor> shortcut : shortcuts.entrySet()) {
if (path.startsWith(shortcut.getKey())) {
shortcutImpl = shortcut.getValue();
break;
}
}
}
if (shortcutImpl == null && callbackImpl == null && !url.startsWith(API_URL) && !url.startsWith(API_URL_S) && !force) {
return false;
}
if (!isPermittedAddr(requestHeader)) {
return true;
}
if (getOptionsParamApi().isSecureOnly() && !requestHeader.isSecure()) {
// Insecure request with secure only set, always ignore
logger.debug("handleApiRequest rejecting insecure request");
return true;
}
logger.debug("handleApiRequest " + url);
HttpMessage msg = new HttpMessage();
msg.setRequestHeader(requestHeader);
if (requestHeader.getContentLength() > 0) {
msg.setRequestBody(httpIn.readRequestBody(requestHeader));
}
String component = null;
ApiImplementor impl = null;
RequestType reqType = null;
String contentType = "text/plain; charset=UTF-8";
String response = "";
String name = null;
boolean error = false;
try {
JSONObject params = getParams(requestHeader.getURI().getEscapedQuery());
if (shortcutImpl != null) {
if (!getOptionsParamApi().isDisableKey() && !getOptionsParamApi().isNoKeyForSafeOps()) {
if (!this.hasValidKey(requestHeader, params)) {
throw new ApiException(ApiException.Type.BAD_API_KEY);
}
}
msg = shortcutImpl.handleShortcut(msg);
} else if (callbackImpl != null) {
// Callbacks have suitably random URLs and therefore don't require keys/nonces
response = callbackImpl.handleCallBack(msg);
} else {
// Parse the query:
// format of url is http://zap/format/component/reqtype/name/?params
// 0 1 2 3 4 5 6
String[] elements = url.split("/");
if (elements.length > 3 && elements[3].equalsIgnoreCase("favicon.ico")) {
// Treat the favicon as a special case:)
if (!getOptionsParamApi().isUiEnabled()) {
throw new ApiException(ApiException.Type.DISABLED);
}
InputStream is = API.class.getResourceAsStream("/resource/zap.ico");
byte[] icon = new byte[is.available()];
is.read(icon);
is.close();
msg.setResponseHeader(getDefaultResponseHeader(contentType));
msg.getResponseHeader().setContentLength(icon.length);
httpOut.write(msg.getResponseHeader());
httpOut.write(icon);
httpOut.flush();
httpOut.close();
httpIn.close();
return true;
} else if (elements.length > 3) {
try {
format = Format.valueOf(elements[3].toUpperCase());
switch(format) {
case JSON:
contentType = "application/json; charset=UTF-8";
break;
case JSONP:
contentType = "application/javascript; charset=UTF-8";
break;
case XML:
contentType = "text/xml; charset=UTF-8";
break;
case HTML:
contentType = "text/html; charset=UTF-8";
break;
case UI:
contentType = "text/html; charset=UTF-8";
break;
default:
break;
}
} catch (IllegalArgumentException e) {
format = Format.HTML;
throw new ApiException(ApiException.Type.BAD_FORMAT);
}
}
if (elements.length > 4) {
component = elements[4];
impl = implementors.get(component);
if (impl == null) {
throw new ApiException(ApiException.Type.NO_IMPLEMENTOR);
}
}
if (elements.length > 5) {
try {
reqType = RequestType.valueOf(elements[5]);
} catch (IllegalArgumentException e) {
throw new ApiException(ApiException.Type.BAD_TYPE);
}
}
if (elements.length > 6) {
name = elements[6];
if (name != null && name.indexOf("?") > 0) {
name = name.substring(0, name.indexOf("?"));
}
}
if (format.equals(Format.UI)) {
if (!isEnabled() || !getOptionsParamApi().isUiEnabled()) {
throw new ApiException(ApiException.Type.DISABLED);
}
response = webUI.handleRequest(component, impl, reqType, name);
contentType = "text/html; charset=UTF-8";
} else if (name != null) {
if (!isEnabled()) {
throw new ApiException(ApiException.Type.DISABLED);
}
// Do this now as it might contain the api key/nonce
if (requestHeader.getMethod().equalsIgnoreCase(HttpRequestHeader.POST)) {
String contentTypeHeader = requestHeader.getHeader(HttpHeader.CONTENT_TYPE);
if (contentTypeHeader != null && contentTypeHeader.equals(HttpHeader.FORM_URLENCODED_CONTENT_TYPE)) {
params = getParams(msg.getRequestBody().toString());
} else {
throw new ApiException(ApiException.Type.CONTENT_TYPE_NOT_SUPPORTED);
}
}
if (format.equals(Format.JSONP)) {
if (!getOptionsParamApi().isEnableJSONP()) {
// Not enabled
throw new ApiException(ApiException.Type.DISABLED);
}
if (!this.hasValidKey(requestHeader, params)) {
// An api key is required for ALL JSONP requests
throw new ApiException(ApiException.Type.BAD_API_KEY);
}
}
ApiResponse res;
switch(reqType) {
case action:
if (!getOptionsParamApi().isDisableKey()) {
if (!this.hasValidKey(requestHeader, params)) {
throw new ApiException(ApiException.Type.BAD_API_KEY);
}
}
ApiAction action = impl.getApiAction(name);
if (action != null) {
// Checking for null to handle option actions
List<String> mandatoryParams = action.getMandatoryParamNames();
if (mandatoryParams != null) {
for (String param : mandatoryParams) {
if (!params.has(param) || params.getString(param).length() == 0) {
throw new ApiException(ApiException.Type.MISSING_PARAMETER, param);
}
}
}
}
res = impl.handleApiOptionAction(name, params);
if (res == null) {
res = impl.handleApiAction(name, params);
}
switch(format) {
case JSON:
response = res.toJSON().toString();
break;
case JSONP:
response = this.getJsonpWrapper(res.toJSON().toString());
break;
case XML:
response = this.responseToXml(name, res);
break;
case HTML:
response = this.responseToHtml(name, res);
break;
default:
break;
}
break;
case view:
if (!getOptionsParamApi().isDisableKey() && !getOptionsParamApi().isNoKeyForSafeOps()) {
if (!this.hasValidKey(requestHeader, params)) {
throw new ApiException(ApiException.Type.BAD_API_KEY);
}
}
ApiView view = impl.getApiView(name);
if (view != null) {
// Checking for null to handle option actions
List<String> mandatoryParams = view.getMandatoryParamNames();
if (mandatoryParams != null) {
for (String param : mandatoryParams) {
if (!params.has(param) || params.getString(param).length() == 0) {
throw new ApiException(ApiException.Type.MISSING_PARAMETER, param);
}
}
}
}
res = impl.handleApiOptionView(name, params);
if (res == null) {
res = impl.handleApiView(name, params);
}
switch(format) {
case JSON:
response = res.toJSON().toString();
break;
case JSONP:
response = this.getJsonpWrapper(res.toJSON().toString());
break;
case XML:
response = this.responseToXml(name, res);
break;
case HTML:
response = this.responseToHtml(name, res);
break;
default:
break;
}
break;
case other:
ApiOther other = impl.getApiOther(name);
if (other != null) {
// Checking for null to handle option actions
if (!getOptionsParamApi().isDisableKey() && (!getOptionsParamApi().isNoKeyForSafeOps() || other.isRequiresApiKey())) {
// Check if a valid api key has been used
if (!this.hasValidKey(requestHeader, params)) {
throw new ApiException(ApiException.Type.BAD_API_KEY);
}
}
List<String> mandatoryParams = other.getMandatoryParamNames();
if (mandatoryParams != null) {
for (String param : mandatoryParams) {
if (!params.has(param) || params.getString(param).length() == 0) {
throw new ApiException(ApiException.Type.MISSING_PARAMETER, param);
}
}
}
}
msg = impl.handleApiOther(msg, name, params);
}
} else {
// Handle default front page, unless if the API UI is disabled
if (!isEnabled() || !getOptionsParamApi().isUiEnabled()) {
throw new ApiException(ApiException.Type.DISABLED);
}
response = webUI.handleRequest(requestHeader.getURI(), this.isEnabled());
format = Format.UI;
contentType = "text/html; charset=UTF-8";
}
}
logger.debug("handleApiRequest returning: " + response);
} catch (Exception e) {
if (!getOptionsParamApi().isReportPermErrors()) {
if (e instanceof ApiException) {
ApiException exception = (ApiException) e;
if (exception.getType().equals(ApiException.Type.DISABLED) || exception.getType().equals(ApiException.Type.BAD_API_KEY)) {
// Fail silently
return true;
}
}
}
handleException(msg, format, contentType, e);
error = true;
}
if (!error && !format.equals(Format.OTHER) && shortcutImpl == null) {
msg.setResponseHeader(getDefaultResponseHeader(contentType));
msg.setResponseBody(response);
msg.getResponseHeader().setContentLength(msg.getResponseBody().length());
}
if (impl != null) {
impl.addCustomHeaders(name, reqType, msg);
}
httpOut.write(msg.getResponseHeader());
httpOut.write(msg.getResponseBody().getBytes());
httpOut.flush();
httpOut.close();
httpIn.close();
return true;
}
use of org.parosproxy.paros.network.HttpInputStream in project zaproxy by zaproxy.
the class ProxyThread method run.
@Override
public void run() {
proxyThreadList.add(thread);
boolean isSecure = this instanceof ProxyThreadSSL;
HttpRequestHeader firstHeader = null;
try {
BufferedInputStream bufferedInputStream = new BufferedInputStream(inSocket.getInputStream(), 2048);
inSocket = new CustomStreamsSocket(inSocket, bufferedInputStream, inSocket.getOutputStream());
httpIn = new HttpInputStream(inSocket);
httpOut = new HttpOutputStream(inSocket.getOutputStream());
firstHeader = httpIn.readRequestHeader(isSecure);
firstHeader.setSenderAddress(inSocket.getInetAddress());
if (firstHeader.getMethod().equalsIgnoreCase(HttpRequestHeader.CONNECT)) {
HttpMessage connectMsg = new HttpMessage(firstHeader);
connectMsg.setTimeSentMillis(System.currentTimeMillis());
try {
httpOut.write(CONNECT_HTTP_200);
httpOut.flush();
connectMsg.setResponseHeader(CONNECT_HTTP_200);
connectMsg.setTimeElapsedMillis((int) (System.currentTimeMillis() - connectMsg.getTimeSentMillis()));
notifyConnectMessage(connectMsg);
byte[] bytes = new byte[3];
bufferedInputStream.mark(3);
bufferedInputStream.read(bytes);
bufferedInputStream.reset();
if (isSslTlsHandshake(bytes)) {
isSecure = true;
beginSSL(firstHeader.getHostName());
}
firstHeader = httpIn.readRequestHeader(isSecure);
firstHeader.setSenderAddress(inSocket.getInetAddress());
processHttp(firstHeader, isSecure);
} catch (MissingRootCertificateException e) {
// Unluckily Firefox and Internet Explorer will not show this message.
// We should find a way to let the browsers display this error message.
// May we can redirect to some kind of ZAP custom error page.
final HttpMessage errmsg = new HttpMessage(firstHeader);
setErrorResponse(errmsg, BAD_GATEWAY_RESPONSE_STATUS, e, "ZAP SSL Error");
writeHttpResponse(errmsg, httpOut);
throw new IOException(e);
}
} else {
processHttp(firstHeader, isSecure);
}
} catch (SocketTimeoutException e) {
// ZAP: Log the exception
if (firstHeader != null) {
if (HttpRequestHeader.CONNECT.equalsIgnoreCase(firstHeader.getMethod())) {
log.warn("Timeout reading (client) message after CONNECT to " + firstHeader.getURI());
} else {
log.warn("Timeout accessing " + firstHeader.getURI());
}
} else {
log.warn("Socket timeout while reading first message.");
if (log.isDebugEnabled()) {
log.debug(e, e);
}
}
} catch (HttpMalformedHeaderException e) {
log.warn("Malformed Header: ", e);
} catch (HttpException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.debug("IOException: ", e);
} finally {
proxyThreadList.remove(thread);
// ZAP: do only close if flag is false
if (!keepSocketOpen) {
disconnect();
}
}
}
Aggregations