/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// internal void OnRequest(ProxyRequest request) { if (request.Header != null) { notifyQueueMutex_.WaitOne(); notifyQueue_.Enqueue(request); threadNotifyEvent_.Set(); notifyQueueMutex_.ReleaseMutex(); } }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Drop(ProxyRequest request) { request.pass_ = false; request.skipRemainingHandlers_ = true; if (request.inbound_ != null) { request.inbound_.Close(); } request.encoding_ = null; request.inbound_ = null; }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void WwwProxy_Error(IPEndPoint ipEndPoint, WwwProxy.ProxyRequest request, Exception exception, string error) { Console.WriteLine("WwwProxy_Error(): {0}", error); if (exception != null) { if (exception is SocketException) { if (((SocketException)exception).ErrorCode == 10048) { exitEvent_.Set(); } } } }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Pass(ProxyRequest request) { request.pass_ = true; if (!request.skipRemainingHandlers_ && (PostRequest != null)) { PostRequest(request); } if (request.pass_) { if (request.inbound_ != null) { request.inbound_.Complete(request); } } else { Drop(request); } }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// internal void OnError(Inbound inbound, ProxyRequest request, Exception exception, string error) { string s = error + "\r\n"; if (exception != null) { s += exception.Message + "\r\n"; s += exception.StackTrace + "\r\n"; } log_.Write("WwwProxy.OnError", s); OnErrorParams onErrorParams; onErrorParams.inbound = inbound; onErrorParams.request = request; onErrorParams.exception = exception; onErrorParams.error = error; Thread onErrorThread = new Thread(new ParameterizedThreadStart(DoError)); onErrorThread.Start(onErrorParams); }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void WwwProxy_Response(IPEndPoint ipEndPoint, WwwProxy.ProxyRequest request, WwwProxy.ProxyResponse response) { try { pythonMutex_.WaitOne(); object[] args = new object[2]; args[0] = request; args[1] = response; object rtnValue = Ops.Invoke(wwwPyFilterClassObject_, SymbolTable.StringToId("response_filter"), args); if (rtnValue is bool) { if (!response.Completable || (bool)rtnValue) { wwwProxy_.Pass(response); } else { wwwProxy_.Drop(response); } } else { Console.WriteLine("WwwProxy_Response Warning: response_filter returned non-bool"); wwwProxy_.Pass(response); } } catch (Exception e) { Console.WriteLine("Error: {0}", e.Message); Console.WriteLine(); Console.WriteLine(e.StackTrace); } finally { pythonMutex_.ReleaseMutex(); } }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// internal void Send(ProxyRequest request) { request_ = request; if (ssl_) { if ((sslStream_ != null) && (sslStream_.IsAuthenticated)) { byte[] requestBytes = wwwProxy_.defaultEncoding_.GetBytes(request_.completedHeader_); Send(requestBytes, 0, requestBytes.Length); request_.sent_ = true; } } else { if ((outboundSocket_ != null) && outboundSocket_.Connected) { byte[] requestBytes = wwwProxy_.defaultEncoding_.GetBytes(request_.completedHeader_); Send(requestBytes, 0, requestBytes.Length); request_.sent_ = true; } } }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void RunNotifier() { WaitHandle[] waitHandles = new WaitHandle[2]; waitHandles[0] = threadExitEvent_; waitHandles[1] = threadNotifyEvent_; while (!threadExitEvent_.WaitOne(0, false)) { object notifyObject = null; if (WaitHandle.WaitAny(waitHandles, Timeout.Infinite, false) == 0) { break; } notifyQueueMutex_.WaitOne(); if (notifyQueue_.Count > 0) { notifyObject = notifyQueue_.Dequeue(); } else { threadNotifyEvent_.Reset(); } notifyQueueMutex_.ReleaseMutex(); if (notifyObject != null) { if (notifyObject is ProxyRequest) { ProxyRequest notifyRequest = (ProxyRequest)notifyObject; notifyRequest.pass_ = true; notifyRequest.skipRemainingHandlers_ = false; if (PreRequest != null) { PreRequest(notifyRequest); } if (!notifyRequest.skipRemainingHandlers_ && (Request != null)) { Request(notifyRequest); } else if (notifyRequest.pass_) { Pass(notifyRequest); } else { Drop(notifyRequest); } } else if (notifyObject is ProxyResponse) { ProxyResponse notifyResponse = (ProxyResponse)notifyObject; notifyResponse.pass_ = true; notifyResponse.skipRemainingHandlers_ = notifyResponse.request_.skipRemainingHandlers_; if (notifyResponse.completable_ && !notifyResponse.skipRemainingHandlers_ && (PreResponse != null)) { PreResponse(notifyResponse.request_, notifyResponse); } if (!notifyResponse.skipRemainingHandlers_ && (Response != null)) { Response(notifyResponse.request_, notifyResponse); } else if (notifyResponse.pass_) { Pass(notifyResponse); } else { Drop(notifyResponse); } } } } }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// internal int OnRequest(byte[] receiveBuffer, int bytesReceived, bool internalRequest) { string proxyRequest = wwwProxy_.defaultEncoding_.GetString(receiveBuffer, 0, bytesReceived); int processedRequestBytes = 0; wwwProxy_.log_.Write("Inbound.OnRequest()", proxyRequest); Match httpMatch = Regex.Match(proxyRequest, "^([A-Z]+)\\s+(\\S*)?\\s+HTTP/\\d.\\d\r$", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (httpMatch.Success) { string defaultCharset = "iso-8859-1"; string charset = defaultCharset; string httpVerb = httpMatch.Groups[1].Value; if (httpVerb != "CONNECT") { Match getEndMatch = Regex.Match(proxyRequest, "\r\n\r\n", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (getEndMatch.Success) { string requestRaw = proxyRequest.Substring(0, getEndMatch.Index); string postRaw = null; processedRequestBytes = getEndMatch.Index + 4; Match contentEncodingTypeMatch = Regex.Match(proxyRequest, "^Content-Type:\\s+\\S+?\\s*;\\s*charset=(\\S+)?", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (contentEncodingTypeMatch.Success) { charset = contentEncodingTypeMatch.Groups[1].Value.ToLower(); } Encoding requestEncoding = null; try { requestEncoding = Encoding.GetEncoding(charset); } catch (ArgumentException) { requestEncoding = wwwProxy_.defaultEncoding_; } Match contentLengthMatch = Regex.Match(requestRaw, "^Content-Length:\\s+(\\d+)[ \t]*[\r\n]*", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (contentLengthMatch.Success) { int contentsLength = Convert.ToInt32(contentLengthMatch.Groups[1].Value); if (proxyRequest.Length >= processedRequestBytes + contentsLength) { postRaw = requestEncoding.GetString(receiveBuffer, processedRequestBytes, contentsLength); processedRequestBytes += contentsLength; for (int i = processedRequestBytes; i < bytesReceived; ++i) { if ((receiveBuffer[i] == '\r') || (receiveBuffer[i] == '\n')) { processedRequestBytes++; } else { break; } } } else { requestRaw = null; postRaw = null; } } if (requestRaw != null) { requestRaw = Regex.Replace(requestRaw, "([A-Z]+)\\s+([a-zA-Z]+://.*?/)(.*?)\\s+(HTTP/\\d.\\d)", "$1 /$3 $4", RegexOptions.IgnoreCase | RegexOptions.Multiline); requestRaw = Regex.Replace(requestRaw, "^Accept-Encoding:\\s+(.*)?[ \t]*[\r\n]*", "", RegexOptions.IgnoreCase | RegexOptions.Multiline); requestRaw = Regex.Replace(requestRaw, "^Content-Length:\\s+(.*)?[ \t]*[\r\n]*", "", RegexOptions.IgnoreCase | RegexOptions.Multiline); requestRaw = Regex.Replace(requestRaw, "^Proxy-Connection:\\s+(.*)?[ \t]*[\r\n]*", "", RegexOptions.IgnoreCase | RegexOptions.Multiline); requestRaw = requestRaw.TrimEnd(" \t\r\n".ToCharArray()); Match hostMatch = Regex.Match(requestRaw, "^Host:\\s+(\\S*)?", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (hostMatch.Success) { string host = hostMatch.Groups[1].Value; string port = ssl_ ? "443" : "80"; Match portMatch = Regex.Match(host, "(:)(\\d{1,5})"); if (portMatch.Success) { port = host.Substring(portMatch.Groups[2].Index, portMatch.Groups[2].Length); host = host.Substring(0, portMatch.Groups[1].Index); } ProxyRequest request = new ProxyRequest(); request.inbound_ = this; request.ssl_ = ssl_; request.header_ = requestRaw; request.data_ = postRaw; Outbound sendOutbound = null; currentOutboundsMutex_.WaitOne(); foreach (Outbound o in currentOutbounds_.Keys) { if ((o.host_ == host) && (o.port_ == Convert.ToUInt16(port)) && (currentOutbounds_[o] == null)) { sendOutbound = o; currentOutbounds_[o] = request; break; } } currentOutboundsMutex_.ReleaseMutex(); if (sendOutbound == null) { sendOutbound = new Outbound(wwwProxy_, this, ssl_, host, Convert.ToUInt16(port)); currentOutboundsMutex_.WaitOne(); currentOutbounds_.Add(sendOutbound, request); currentOutboundsMutex_.ReleaseMutex(); } if (!internalRequest) { wwwProxy_.OnRequest(request); } else { wwwProxy_.Pass(request); } StartKeepAliveTimer(); } } else { processedRequestBytes = 0; } } } else { string connectHost = httpMatch.Groups[2].Value; string host = connectHost; string port = "443"; Match portMatch = Regex.Match(host, "(:)(\\d{1,5})"); if (portMatch.Success) { port = host.Substring(portMatch.Groups[2].Index, portMatch.Groups[2].Length); host = host.Substring(0, portMatch.Groups[1].Index); } Outbound outbound = new Outbound(wwwProxy_, this, true, host, Convert.ToUInt16(port)); currentOutboundsMutex_.WaitOne(); currentOutbounds_.Add(outbound, null); currentOutboundsMutex_.ReleaseMutex(); Match connectEndMatch = Regex.Match(proxyRequest, "\r\n\r\n", RegexOptions.Multiline); if (connectEndMatch.Success) { processedRequestBytes = connectEndMatch.Index + 4; } byte[] sslResponseBytes = wwwProxy_.defaultEncoding_.GetBytes("HTTP/1.0 200 OK\r\n\r\n"); Send(sslResponseBytes, 0, sslResponseBytes.Length); ssl_ = true; sslNetworkStream_ = new NetworkStream(inboundSocket_); sslStream_ = new SslStream(sslNetworkStream_, false, new RemoteCertificateValidationCallback(OnRemoteCertificateValidation)); X509Certificate serverCertificate = new X509Certificate((wwwProxy_.certificate_ != null) ? wwwProxy_.certificate_ : "WwwProxy.cer"); sslStream_.AuthenticateAsServer(serverCertificate); } } else { int lineEnd = proxyRequest.IndexOf("\r\n"); throw new Exception("Unhandled Request \"" + proxyRequest.Substring(0, (lineEnd > 0) ? lineEnd : proxyRequest.Length) + "\""); } return(processedRequestBytes); }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// internal void Complete(ProxyRequest request) { Outbound sendOutbound = null; try { currentOutboundsMutex_.WaitOne(); foreach (Outbound o in currentOutbounds_.Keys) { if (currentOutbounds_[o] == request) { sendOutbound = o; break; } } currentOutboundsMutex_.ReleaseMutex(); } catch (ObjectDisposedException) { } request.completedHeader_ = request.header_; if (request.data_ != null) { request.completedHeader_ += "\r\nContent-Length: " + Convert.ToString(request.data_.Length) + "\r\n\r\n" + request.data_; } else { request.completedHeader_ += "\r\n\r\n"; } if (sendOutbound != null) { if (sendOutbound.useRemoteProxy_) { Match noUrlMatch = Regex.Match(request.completedHeader_, "([A-Z]+)\\s+/(.*?)\\s+(HTTP/\\d.\\d)", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (noUrlMatch.Success) { Match hostMatch = Regex.Match(request.completedHeader_, "^Host:\\s+(\\S*)?", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (hostMatch.Success) { string host = hostMatch.Groups[1].Value; string insert = (sendOutbound.ssl_ ? "https://" : "http://") + host + "/"; request.completedHeader_ = Regex.Replace(request.completedHeader_, "([A-Z]+)\\s+/(.*)\\s+(HTTP/\\d.\\d)", "$1 " + insert + "$2 $3", RegexOptions.IgnoreCase | RegexOptions.Multiline); } } } if (wwwProxy_.ntlmEnabled_) { MatchCollection basicCredentials = Regex.Matches(request.completedHeader_, "^(Proxy-)*Authorization:\\s+Basic\\s+([a-zA-Z0-9=]*)[ \t]*[\r\n]*", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (basicCredentials.Count > 0) { Match urlMatch = Regex.Match(request.completedHeader_, "([A-Z]+)\\s+(.*?)\\s+HTTP/\\d.\\d\r$", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (urlMatch.Success) { string ntlmSite = sendOutbound.host_ + ":" + sendOutbound.port_; string ntlmPath = urlMatch.Groups[2].Value; INtlm wwwProxyNtlm = null; try { wwwProxy_.ntlmListMutex_.WaitOne(); foreach (INtlm w in wwwProxy_.ntlmList_) { if ((w.Site == ntlmSite) && (w.Path == ntlmPath)) { foreach (Match basicCredentialsMatch in basicCredentials) { if ((w.Type == basicCredentialsMatch.Groups[1].Value) || ((w.Type == "WWW") && (basicCredentialsMatch.Groups[1].Value == ""))) { w.Basic = basicCredentialsMatch.Groups[2].Value; wwwProxyNtlm = w; break; } } if (wwwProxyNtlm != null) { break; } } } if (wwwProxyNtlm == null) { foreach (INtlm w in wwwProxy_.ntlmList_) { if ((w.Site == ntlmSite) && (w.Basic != null)) { foreach (Match basicCredentialsMatch in basicCredentials) { if ((w.Basic == basicCredentialsMatch.Groups[2].Value) && ((w.Type == basicCredentialsMatch.Groups[1].Value) || ((w.Type == "WWW") && (basicCredentialsMatch.Groups[1].Value == "")))) { INtlm cloneWwwProxyNtlm = wwwProxy_.ntlmFactory_.CreateInstance(); if (cloneWwwProxyNtlm != null) { cloneWwwProxyNtlm.Site = ntlmSite; cloneWwwProxyNtlm.Path = ntlmPath; cloneWwwProxyNtlm.Type = w.Type; cloneWwwProxyNtlm.Basic = w.Basic; wwwProxy_.ntlmList_.Add(cloneWwwProxyNtlm); wwwProxyNtlm = cloneWwwProxyNtlm; } break; } } if (wwwProxyNtlm != null) { break; } } } } if (wwwProxyNtlm != null) { string[] credentials = wwwProxy_.defaultEncoding_.GetString(Convert.FromBase64String(wwwProxyNtlm.Basic)).Split(':'); string ntlmNegotiate = null; string domain = sendOutbound.host_; string username = credentials[0]; string password = credentials[1]; if (username.Contains("\\")) { int domainUsernameSeparator = username.IndexOf('\\'); if ((domainUsernameSeparator > 0) && (domainUsernameSeparator < (username.Length - 1))) { domain = username.Substring(0, domainUsernameSeparator); username = username.Substring(username.IndexOf('\\') + 1); } } wwwProxyNtlm.Initialise(domain, username, password); ntlmNegotiate = wwwProxyNtlm.Continue(null); if (ntlmNegotiate == null) { ntlmNegotiate = "???"; } string authReplace = "^(" + (wwwProxyNtlm.Type == "Proxy" ? "Proxy-" : "") + "Authorization:\\s+)Basic\\s+[a-zA-Z0-9=]+[ \t]*([\r\n]*)"; request.completedHeader_ = Regex.Replace(request.completedHeader_, authReplace, "${1}NTLM " + ntlmNegotiate + "${2}", RegexOptions.IgnoreCase | RegexOptions.Multiline); } } finally { wwwProxy_.ntlmListMutex_.ReleaseMutex(); } } } } wwwProxy_.log_.Write("Inbound.Complete()", request.completedHeader_); sendOutbound.Connect(); try { sendOutbound.Send(request); } catch (IOException) { } catch (SocketException) { } } }