#include "FileZilla.h" #include "externalipresolver.h" #include "asynchostresolver.h" #include "wx/regex.h" const wxEventType fzEVT_EXTERNALIPRESOLVE = wxNewEventType(); fzExternalIPResolveEvent::fzExternalIPResolveEvent(int id) : wxEvent(id, fzEVT_EXTERNALIPRESOLVE) { } wxEvent* fzExternalIPResolveEvent::Clone() const { return new fzExternalIPResolveEvent(GetId()); } wxString CExternalIPResolver::m_ip; bool CExternalIPResolver::m_checked = false; BEGIN_EVENT_TABLE(CExternalIPResolver, wxEvtHandler) EVT_FZ_ASYNCHOSTRESOLVE(wxID_ANY, CExternalIPResolver::OnAsyncHostResolver) EVT_SOCKET(0, CExternalIPResolver::OnSocketEvent) END_EVENT_TABLE(); CExternalIPResolver::CExternalIPResolver(wxEvtHandler* handler, int id /*=wxID_ANY*/) : m_handler(handler), m_id(id), m_pHostResolver(0) { m_pSocket = 0; m_done = false; m_pSendBuffer = 0; m_sendBufferPos = 0; m_pRecvBuffer = 0; m_recvBufferPos = 0; ResetHttpData(true); } CExternalIPResolver::~CExternalIPResolver() { delete [] m_pSendBuffer; m_pSendBuffer = 0; delete [] m_pRecvBuffer; m_pRecvBuffer = 0; delete m_pSocket; m_pSocket = 0; if (m_pHostResolver) { m_pHostResolver->SetObsolete(); m_pHostResolver->Wait(); delete m_pHostResolver; } } void CExternalIPResolver::GetExternalIP(const wxString& address /*=_T("")*/, bool force /*=false*/) { if (m_checked) { if (force) m_checked = false; else { m_done = true; return; } } m_address = address; wxString host; int pos; if ((pos = address.Find(_T("://"))) != -1) host = address.Mid(pos + 3); else host = address; if ((pos = host.Find(_T("/"))) != -1) host = host.Left(pos); wxString hostWithPort = host; if ((pos = host.Find(':', true)) != -1) { wxString port = host.Mid(pos + 1); if (!port.ToULong(&m_port) || m_port < 1 || m_port > 65535) m_port = 80; host = host.Left(pos); } else m_port = 80; if (host == _T("")) { m_done = true; return; } m_pHostResolver = new CAsyncHostResolver(this, host); m_pHostResolver->Create(); m_pHostResolver->Run(); wxString buffer = wxString::Format(_T("GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\n\r\n"), address.c_str(), hostWithPort.c_str(), wxString(PACKAGE_STRING, wxConvLocal).c_str()); m_pSendBuffer = new char[strlen(buffer.mb_str()) + 1]; strcpy(m_pSendBuffer, buffer.mb_str()); } void CExternalIPResolver::OnAsyncHostResolver(fzAsyncHostResolveEvent& event) { if (!m_pHostResolver) return; if (!m_pHostResolver->Done()) return; if (!m_pHostResolver->Successful()) { Close(false); return; } m_pHostResolver->Wait(); if (m_pSocket) { delete m_pHostResolver; m_pHostResolver = 0; return; } m_pSocket = new wxSocketClient(wxSOCKET_NOWAIT); m_pSocket->SetEventHandler(*this, 0); m_pSocket->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG | wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG); m_pSocket->Notify(true); m_pHostResolver->m_Address.Service(m_port); m_pSocket->Connect(m_pHostResolver->m_Address, false); delete m_pHostResolver; m_pHostResolver = 0; } void CExternalIPResolver::OnSocketEvent(wxSocketEvent& event) { if (!m_pSocket) return; switch (event.GetSocketEvent()) { case wxSOCKET_INPUT: OnReceive(); break; case wxSOCKET_CONNECTION: OnConnect(); break; case wxSOCKET_LOST: OnClose(); break; case wxSOCKET_OUTPUT: OnSend(); break; } } void CExternalIPResolver::OnConnect() { } void CExternalIPResolver::OnClose() { if (m_data != _T("")) OnData(0, 0); else Close(false); } void CExternalIPResolver::OnReceive() { if (!m_pRecvBuffer) { m_pRecvBuffer = new char[m_recvBufferLen]; m_recvBufferPos = 0; } if (m_pSendBuffer) return; unsigned int len = m_recvBufferLen - m_recvBufferPos; m_pSocket->Read(m_pRecvBuffer + m_recvBufferPos, len); if (m_pSocket->Error()) { if (m_pSocket->LastError() != wxSOCKET_WOULDBLOCK) { Close(false); } return; } int read; if (!(read = m_pSocket->LastCount())) { Close(false); return; } if (m_finished) { // Just ignore all further data m_recvBufferPos = 0; return; } m_recvBufferPos += read; if (!m_gotHeader) OnHeader(); else { if (m_transferEncoding == chunked) OnChunkedData(); else OnData(m_pRecvBuffer, m_recvBufferPos); } } void CExternalIPResolver::OnSend() { if (!m_pSendBuffer) return; unsigned int len = strlen(m_pSendBuffer + m_sendBufferPos); m_pSocket->Write(m_pSendBuffer + m_sendBufferPos, len); if (m_pSocket->Error()) { if (m_pSocket->LastError() != wxSOCKET_WOULDBLOCK) { Close(false); } return; } unsigned int written; if (!(written = m_pSocket->LastCount())) { Close(false); return; } if (written == len) { delete [] m_pSendBuffer; m_pSendBuffer = 0; OnReceive(); } else m_sendBufferPos += written; } void CExternalIPResolver::Close(bool successful) { delete [] m_pSendBuffer; m_pSendBuffer = 0; delete [] m_pRecvBuffer; m_pRecvBuffer = 0; delete m_pSocket; m_pSocket = 0; if (m_done) return; m_done = true; if (!successful) m_ip = _T(""); m_checked = true; if (m_handler) { fzExternalIPResolveEvent event; wxPostEvent(m_handler, event); m_handler = 0; } } void CExternalIPResolver::OnHeader() { // Parse the HTTP header. // We do just the neccessary parsing and silently ignore most header fields // Redirects are supported though if the server sends the Location field. while (true) { // Find line ending unsigned int i = 0; for (i = 0; (i + 1) < m_recvBufferPos; i++) { if (m_pRecvBuffer[i] == '\r') { if (m_pRecvBuffer[i + 1] != '\n') { Close(false); return; } break; } } if ((i + 1) >= m_recvBufferPos) { if (m_recvBufferPos == m_recvBufferLen) { // We don't support header lines larger than 4096 Close(false); return; } return; } m_pRecvBuffer[i] = 0; if (!m_responseCode) { m_responseString = wxString(m_pRecvBuffer, wxConvLocal); if (m_recvBufferPos < 16 || memcmp(m_pRecvBuffer, "HTTP/1.", 7)) { // Invalid HTTP Status-Line Close(false); return; } if (m_pRecvBuffer[9] < '1' || m_pRecvBuffer[9] > '5' || m_pRecvBuffer[10] < '0' || m_pRecvBuffer[10] > '9' || m_pRecvBuffer[11] < '0' || m_pRecvBuffer[11] > '9') { // Invalid response code Close(false); return; } m_responseCode = (m_pRecvBuffer[9] - '0') * 100 + (m_pRecvBuffer[10] - '0') * 10 + m_pRecvBuffer[11] - '0'; if (m_responseCode >= 400) { // Failed request Close(false); return; } if (m_responseCode == 305) { // Unsupported redirect Close(false); return; } } else { if (!i) { // End of header, data from now on // Redirect if neccessary if (m_responseCode >= 300) { delete m_pSocket; m_pSocket = 0; delete [] m_pRecvBuffer; m_pRecvBuffer = 0; wxString location = m_location; ResetHttpData(false); GetExternalIP(location); return; } m_gotHeader = true; memmove(m_pRecvBuffer, m_pRecvBuffer + 2, m_recvBufferPos - 2); m_recvBufferPos -= 2; if (m_recvBufferPos) { if (m_transferEncoding == chunked) OnChunkedData(); else OnData(m_pRecvBuffer, m_recvBufferPos); } return; } if (m_recvBufferPos > 12 && !memcmp(m_pRecvBuffer, "Location: ", 10)) { m_location = wxString(m_pRecvBuffer + 10, wxConvLocal); } else if (m_recvBufferPos > 21 && !memcmp(m_pRecvBuffer, "Transfer-Encoding: ", 19)) { if (!strcmp(m_pRecvBuffer + 19, "chunked")) m_transferEncoding = chunked; else if (!strcmp(m_pRecvBuffer + 19, "identity")) m_transferEncoding = identity; else m_transferEncoding = unknown; } } memmove(m_pRecvBuffer, m_pRecvBuffer + i + 2, m_recvBufferPos - i - 2); m_recvBufferPos -= i + 2; if (!m_recvBufferPos) break; } } void CExternalIPResolver::OnData(char* buffer, unsigned int len) { if (buffer) { unsigned int i; for (i = 0; i < len; i++) { if (buffer[i] == '\r' || buffer[i] == '\n') break; } if (i) m_data += wxString(buffer, wxConvLocal, i); if (i == len) return; } // Validate ip address wxString digit = _T("0*[0-9]{1,3}"); const wxChar* dot = _T("\\."); wxString exp = _T("(^|[^\\.[:digit:]])(") + digit + dot + digit + dot + digit + dot + digit + _T(")([^\\.[:digit:]]|$)"); wxRegEx regex; regex.Compile(exp); if (!regex.Matches(m_data)) { Close(false); return; } m_ip = regex.GetMatch(m_data, 2); Close(true); } void CExternalIPResolver::ResetHttpData(bool resetRedirectCount) { m_gotHeader = false; m_location = _T(""); m_responseCode = 0; m_responseString = _T(""); if (resetRedirectCount) m_redirectCount = 0; m_transferEncoding = unknown; m_chunkData.getTrailer = false; m_chunkData.size = 0; m_chunkData.terminateChunk = false; m_finished = false; } void CExternalIPResolver::OnChunkedData() { char* p = m_pRecvBuffer; unsigned int len = m_recvBufferPos; while (true) { if (m_chunkData.size != 0) { unsigned int dataLen = len; if (m_chunkData.size < len) dataLen = m_chunkData.size.GetLo(); OnData(p, dataLen); if (!m_pRecvBuffer) return; m_chunkData.size -= dataLen; p += dataLen; len -= dataLen; if (m_chunkData.size == 0) m_chunkData.terminateChunk = true; if (!len) break; } // Find line ending unsigned int i = 0; for (i = 0; (i + 1) < len; i++) { if (p[i] == '\r') { if (p[i + 1] != '\n') { Close(false); return; } break; } } if ((i + 1) >= len) { if (len == m_recvBufferLen) { // We don't support lines larger than 4096 Close(false); return; } break; } p[i] = 0; if (m_chunkData.terminateChunk) { if (i) { // Chunk has to end with CRLF Close(false); return; } m_chunkData.terminateChunk = false; } else if (m_chunkData.getTrailer) { if (!i) { m_finished = true; m_recvBufferPos = 0; return; } // Ignore the trailer } else { // Read chunk size char* q = p; while (*q) { if (*q >= '0' && *q <= '9') { m_chunkData.size *= 16; m_chunkData.size += *q - '0'; } else if (*q >= 'A' && *q <= 'F') { m_chunkData.size *= 10; m_chunkData.size += *q - 'A' + 10; } else if (*q >= 'a' && *q <= 'f') { m_chunkData.size *= 10; m_chunkData.size += *q - 'a' + 10; } else if (*q == ';' || *q == ' ') break; else { // Invalid size Close(false); return; } q++; } if (m_chunkData.size == 0) m_chunkData.getTrailer = true; } p += i + 2; len -= i + 2; if (!len) break; } if (p != m_pRecvBuffer) { memmove(m_pRecvBuffer, p, len); m_recvBufferPos = len; } }