source: filezilla/trunk/fuentes/src/engine/externalipresolver.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 10.0 KB
Line 
1#include <filezilla.h>
2#include "externalipresolver.h"
3#include "socket.h"
4#include "misc.h"
5
6#include <wx/regex.h>
7
8namespace {
9mutex s_sync;
10wxString ip;
11bool checked = false;
12}
13
14CExternalIPResolver::CExternalIPResolver(CEventHandler & handler)
15        : CEventHandler(handler.event_loop_)
16        , m_handler(&handler)
17{
18        ResetHttpData(true);
19}
20
21CExternalIPResolver::~CExternalIPResolver()
22{
23        delete [] m_pSendBuffer;
24        m_pSendBuffer = 0;
25        delete [] m_pRecvBuffer;
26        m_pRecvBuffer = 0;
27
28        delete m_pSocket;
29        m_pSocket = 0;
30}
31
32void CExternalIPResolver::GetExternalIP(const wxString& address, CSocket::address_family protocol, bool force /*=false*/)
33{
34        {
35                scoped_lock l(s_sync);
36                if (checked) {
37                        if (force)
38                                checked = false;
39                        else {
40                                m_done = true;
41                                return;
42                        }
43                }
44        }
45
46        m_address = address;
47        m_protocol = protocol;
48
49        wxString host;
50        int pos;
51        if ((pos = address.Find(_T("://"))) != -1)
52                host = address.Mid(pos + 3);
53        else
54                host = address;
55
56        if ((pos = host.Find(_T("/"))) != -1)
57                host = host.Left(pos);
58
59        wxString hostWithPort = host;
60
61        if ((pos = host.Find(':', true)) != -1) {
62                wxString port = host.Mid(pos + 1);
63                if (!port.ToULong(&m_port) || m_port < 1 || m_port > 65535)
64                        m_port = 80;
65                host = host.Left(pos);
66        }
67        else
68                m_port = 80;
69
70        if (host.empty()) {
71                m_done = true;
72                return;
73        }
74
75        m_pSocket = new CSocket(this);
76
77        int res = m_pSocket->Connect(host, m_port, protocol);
78        if (res && res != EINPROGRESS) {
79                Close(false);
80                return;
81        }
82
83        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, hostWithPort, wxString(PACKAGE_STRING, wxConvLocal));
84        m_pSendBuffer = new char[strlen(buffer.mb_str()) + 1];
85        strcpy(m_pSendBuffer, buffer.mb_str());
86}
87
88void CExternalIPResolver::operator()(CEventBase const& ev)
89{
90        Dispatch<CSocketEvent>(ev, this, &CExternalIPResolver::OnSocketEvent);
91}
92
93void CExternalIPResolver::OnSocketEvent(CSocketEventSource*, SocketEventType t, int error)
94{
95        if (!m_pSocket)
96                return;
97
98        switch (t)
99        {
100        case SocketEventType::read:
101                OnReceive();
102                break;
103        case SocketEventType::connection:
104                OnConnect(error);
105                break;
106        case SocketEventType::close:
107                OnClose();
108                break;
109        case SocketEventType::write:
110                OnSend();
111                break;
112        default:
113                break;
114        }
115
116}
117
118void CExternalIPResolver::OnConnect(int error)
119{
120        if (error)
121                Close(false);
122}
123
124void CExternalIPResolver::OnClose()
125{
126        if (m_data.empty())
127                Close(false);
128        else
129                OnData(0, 0);
130}
131
132void CExternalIPResolver::OnReceive()
133{
134        if (!m_pRecvBuffer) {
135                m_pRecvBuffer = new char[m_recvBufferLen];
136                m_recvBufferPos = 0;
137        }
138
139        if (m_pSendBuffer)
140                return;
141
142        while (m_pSocket) {
143                unsigned int len = m_recvBufferLen - m_recvBufferPos;
144                int error;
145                int read = m_pSocket->Read(m_pRecvBuffer + m_recvBufferPos, len, error);
146                if (read == -1) {
147                        if (error != EAGAIN)
148                                Close(false);
149                        return;
150                }
151
152                if (!read) {
153                        Close(false);
154                        return;
155                }
156
157                if (m_finished) {
158                        // Just ignore all further data
159                        m_recvBufferPos = 0;
160                        return;
161                }
162
163                m_recvBufferPos += read;
164
165                if (!m_gotHeader)
166                        OnHeader();
167                else {
168                        if (m_transferEncoding == chunked)
169                                OnChunkedData();
170                        else
171                                OnData(m_pRecvBuffer, m_recvBufferPos);
172                }
173        }
174}
175
176void CExternalIPResolver::OnSend()
177{
178        while (m_pSendBuffer) {
179                unsigned int len = strlen(m_pSendBuffer + m_sendBufferPos);
180                int error;
181                int written = m_pSocket->Write(m_pSendBuffer + m_sendBufferPos, len, error);
182                if (written == -1) {
183                        if (error != EAGAIN)
184                                Close(false);
185                        return;
186                }
187
188                if (!written) {
189                        Close(false);
190                        return;
191                }
192
193                if (written == (int)len) {
194                        delete [] m_pSendBuffer;
195                        m_pSendBuffer = 0;
196
197                        OnReceive();
198                }
199                else
200                        m_sendBufferPos += written;
201        }
202}
203
204void CExternalIPResolver::Close(bool successful)
205{
206        delete [] m_pSendBuffer;
207        m_pSendBuffer = 0;
208
209        delete [] m_pRecvBuffer;
210        m_pRecvBuffer = 0;
211
212        delete m_pSocket;
213        m_pSocket = 0;
214
215        if (m_done)
216                return;
217
218        m_done = true;
219
220        {
221                scoped_lock l(s_sync);
222                if (!successful) {
223                        ip.clear();
224                }
225                checked = true;
226        }
227
228        if (m_handler) {
229                m_handler->SendEvent<CExternalIPResolveEvent>();
230                m_handler = 0;
231        }
232}
233
234void CExternalIPResolver::OnHeader()
235{
236        // Parse the HTTP header.
237        // We do just the neccessary parsing and silently ignore most header fields
238        // Redirects are supported though if the server sends the Location field.
239
240        for (;;) {
241                // Find line ending
242                unsigned int i = 0;
243                for (i = 0; (i + 1) < m_recvBufferPos; ++i) {
244                        if (m_pRecvBuffer[i] == '\r') {
245                                if (m_pRecvBuffer[i + 1] != '\n') {
246                                        Close(false);
247                                        return;
248                                }
249                                break;
250                        }
251                }
252                if ((i + 1) >= m_recvBufferPos) {
253                        if (m_recvBufferPos == m_recvBufferLen) {
254                                // We don't support header lines larger than 4096
255                                Close(false);
256                                return;
257                        }
258                        return;
259                }
260
261                m_pRecvBuffer[i] = 0;
262
263                if (!m_responseCode) {
264                        m_responseString = wxString(m_pRecvBuffer, wxConvLocal);
265                        if (m_recvBufferPos < 16 || memcmp(m_pRecvBuffer, "HTTP/1.", 7)) {
266                                // Invalid HTTP Status-Line
267                                Close(false);
268                                return;
269                        }
270
271                        if (m_pRecvBuffer[9] < '1' || m_pRecvBuffer[9] > '5' ||
272                                m_pRecvBuffer[10] < '0' || m_pRecvBuffer[10] > '9' ||
273                                m_pRecvBuffer[11] < '0' || m_pRecvBuffer[11] > '9')
274                        {
275                                // Invalid response code
276                                Close(false);
277                                return;
278                        }
279
280                        m_responseCode = (m_pRecvBuffer[9] - '0') * 100 + (m_pRecvBuffer[10] - '0') * 10 + m_pRecvBuffer[11] - '0';
281
282                        if (m_responseCode >= 400) {
283                                // Failed request
284                                Close(false);
285                                return;
286                        }
287
288                        if (m_responseCode == 305) {
289                                // Unsupported redirect
290                                Close(false);
291                                return;
292                        }
293                }
294                else {
295                        if (!i) {
296                                // End of header, data from now on
297
298                                // Redirect if neccessary
299                                if (m_responseCode >= 300) {
300                                        delete m_pSocket;
301                                        m_pSocket = 0;
302
303                                        delete [] m_pRecvBuffer;
304                                        m_pRecvBuffer = 0;
305
306                                        wxString location = m_location;
307
308                                        ResetHttpData(false);
309
310                                        GetExternalIP(location, m_protocol);
311                                        return;
312                                }
313
314                                m_gotHeader = true;
315
316                                memmove(m_pRecvBuffer, m_pRecvBuffer + 2, m_recvBufferPos - 2);
317                                m_recvBufferPos -= 2;
318                                if (m_recvBufferPos) {
319                                        if (m_transferEncoding == chunked)
320                                                OnChunkedData();
321                                        else
322                                                OnData(m_pRecvBuffer, m_recvBufferPos);
323                                }
324                                return;
325                        }
326                        if (m_recvBufferPos > 12 && !memcmp(m_pRecvBuffer, "Location: ", 10)) {
327                                m_location = wxString(m_pRecvBuffer + 10, wxConvLocal);
328                        }
329                        else if (m_recvBufferPos > 21 && !memcmp(m_pRecvBuffer, "Transfer-Encoding: ", 19)) {
330                                if (!strcmp(m_pRecvBuffer + 19, "chunked"))
331                                        m_transferEncoding = chunked;
332                                else if (!strcmp(m_pRecvBuffer + 19, "identity"))
333                                        m_transferEncoding = identity;
334                                else
335                                        m_transferEncoding = unknown;
336                        }
337                }
338
339                memmove(m_pRecvBuffer, m_pRecvBuffer + i + 2, m_recvBufferPos - i - 2);
340                m_recvBufferPos -= i + 2;
341
342                if (!m_recvBufferPos)
343                        break;
344        }
345}
346
347void CExternalIPResolver::OnData(char* buffer, unsigned int len)
348{
349        if (buffer) {
350                unsigned int i;
351                for (i = 0; i < len; ++i) {
352                        if (buffer[i] == '\r' || buffer[i] == '\n')
353                                break;
354                        if (buffer[i] & 0x80) {
355                                Close(false);
356                                return;
357                        }
358                }
359
360                if (i)
361                        m_data += wxString(buffer, wxConvLocal, i);
362
363                if (i == len)
364                        return;
365        }
366
367        if (m_protocol == CSocket::ipv6) {
368                if (!m_data.empty() && m_data[0] == '[') {
369                        if (m_data.Last() != ']') {
370                                Close(false);
371                                return;
372                        }
373                        m_data.RemoveLast();
374                        m_data = m_data.Mid(1);
375                }
376
377                if (GetIPV6LongForm(m_data).empty()) {
378                        Close(false);
379                        return;
380                }
381
382                scoped_lock l(s_sync);
383                ip = m_data;
384        }
385        else {
386
387                // Validate ip address
388                wxString digit = _T("0*[0-9]{1,3}");
389                const wxChar* dot = _T("\\.");
390                wxString exp = _T("(^|[^\\.[:digit:]])(") + digit + dot + digit + dot + digit + dot + digit + _T(")([^\\.[:digit:]]|$)");
391                wxRegEx regex;
392                regex.Compile(exp);
393
394                if (!regex.Matches(m_data)) {
395                        Close(false);
396                        return;
397                }
398
399                scoped_lock l(s_sync);
400                ip = regex.GetMatch(m_data, 2);
401        }
402
403        Close(true);
404}
405
406void CExternalIPResolver::ResetHttpData(bool resetRedirectCount)
407{
408        m_gotHeader = false;
409        m_location.clear();
410        m_responseCode = 0;
411        m_responseString.clear();
412        if (resetRedirectCount)
413                m_redirectCount = 0;
414
415        m_transferEncoding = unknown;
416
417        m_chunkData = t_chunkData();
418
419        m_finished = false;
420}
421
422void CExternalIPResolver::OnChunkedData()
423{
424        char* p = m_pRecvBuffer;
425        unsigned int len = m_recvBufferPos;
426
427        for (;;) {
428                if (m_chunkData.size != 0) {
429                        unsigned int dataLen = len;
430                        if (m_chunkData.size < len)
431                                dataLen = static_cast<unsigned int>(m_chunkData.size);
432                        OnData(p, dataLen);
433                        if (!m_pRecvBuffer)
434                                return;
435
436                        m_chunkData.size -= dataLen;
437                        p += dataLen;
438                        len -= dataLen;
439
440                        if (m_chunkData.size == 0)
441                                m_chunkData.terminateChunk = true;
442
443                        if (!len)
444                                break;
445                }
446
447                // Find line ending
448                unsigned int i = 0;
449                for (i = 0; (i + 1) < len; ++i) {
450                        if (p[i] == '\r') {
451                                if (p[i + 1] != '\n') {
452                                        Close(false);
453                                        return;
454                                }
455                                break;
456                        }
457                }
458                if ((i + 1) >= len) {
459                        if (len == m_recvBufferLen) {
460                                // We don't support lines larger than 4096
461                                Close(false);
462                                return;
463                        }
464                        break;
465                }
466
467                p[i] = 0;
468
469                if (m_chunkData.terminateChunk) {
470                        if (i) {
471                                // Chunk has to end with CRLF
472                                Close(false);
473                                return;
474                        }
475                        m_chunkData.terminateChunk = false;
476                }
477                else if (m_chunkData.getTrailer) {
478                        if (!i) {
479                                m_finished = true;
480                                m_recvBufferPos = 0;
481                                return;
482                        }
483
484                        // Ignore the trailer
485                }
486                else {
487                        // Read chunk size
488                        char* q = p;
489                        while (*q) {
490                                if (*q >= '0' && *q <= '9') {
491                                        m_chunkData.size *= 16;
492                                        m_chunkData.size += *q - '0';
493                                }
494                                else if (*q >= 'A' && *q <= 'F') {
495                                        m_chunkData.size *= 10;
496                                        m_chunkData.size += *q - 'A' + 10;
497                                }
498                                else if (*q >= 'a' && *q <= 'f') {
499                                        m_chunkData.size *= 10;
500                                        m_chunkData.size += *q - 'a' + 10;
501                                }
502                                else if (*q == ';' || *q == ' ')
503                                        break;
504                                else {
505                                        // Invalid size
506                                        Close(false);
507                                        return;
508                                }
509                                q++;
510                        }
511                        if (m_chunkData.size == 0)
512                                m_chunkData.getTrailer = true;
513                }
514
515                p += i + 2;
516                len -= i + 2;
517
518                if (!len)
519                        break;
520        }
521
522        if (p != m_pRecvBuffer) {
523                memmove(m_pRecvBuffer, p, len);
524                m_recvBufferPos = len;
525        }
526}
527
528bool CExternalIPResolver::Successful() const
529{
530        scoped_lock l(s_sync);
531        return !ip.empty();
532}
533
534wxString CExternalIPResolver::GetIP() const
535{
536        scoped_lock l(s_sync);
537        return ip;
538}
Note: See TracBrowser for help on using the repository browser.