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

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

Update new version: 3.15.02

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