source: trunk/third-party/miniupnp/miniwget.c @ 13258

Last change on this file since 13258 was 13258, checked in by livings124, 9 years ago

#4825 update miniupnp to miniupnpc-1.6.20120320

File size: 13.1 KB
Line 
1/* $Id: miniwget.c,v 1.55 2012/03/05 19:42:47 nanard Exp $ */
2/* Project : miniupnp
3 * Website : http://miniupnp.free.fr/
4 * Author : Thomas Bernard
5 * Copyright (c) 2005-2012 Thomas Bernard
6 * This software is subject to the conditions detailed in the
7 * LICENCE file provided in this distribution. */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <ctype.h>
13#ifdef _WIN32
14#include <winsock2.h>
15#include <ws2tcpip.h>
16#include <io.h>
17#define MAXHOSTNAMELEN 64
18#define MIN(x,y) (((x)<(y))?(x):(y))
19#define snprintf _snprintf
20#define socklen_t int
21#ifndef strncasecmp
22#if defined(_MSC_VER) && (_MSC_VER >= 1400)
23#define strncasecmp _memicmp
24#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
25#define strncasecmp memicmp
26#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
27#endif /* #ifndef strncasecmp */
28#else /* #ifdef _WIN32 */
29#include <unistd.h>
30#include <sys/param.h>
31#if defined(__amigaos__) && !defined(__amigaos4__)
32#define socklen_t int
33#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
34#include <sys/select.h>
35#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
36#include <sys/socket.h>
37#include <netinet/in.h>
38#include <arpa/inet.h>
39#include <netdb.h>
40#define closesocket close
41/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
42 * during the connect() call */
43#define MINIUPNPC_IGNORE_EINTR
44#endif /* #else _WIN32 */
45#if defined(__sun) || defined(sun)
46#define MIN(x,y) (((x)<(y))?(x):(y))
47#endif
48
49#include "miniupnpcstrings.h"
50#include "miniwget.h"
51#include "connecthostport.h"
52#include "receivedata.h"
53
54/*
55 * Read a HTTP response from a socket.
56 * Process Content-Length and Transfer-encoding headers.
57 * return a pointer to the content buffer, which length is saved
58 * to the length parameter.
59 */
60void *
61getHTTPResponse(int s, int * size)
62{
63        char buf[2048];
64        int n;
65        int endofheaders = 0;
66        int chunked = 0;
67        int content_length = -1;
68        unsigned int chunksize = 0;
69        unsigned int bytestocopy = 0;
70        /* buffers : */
71        char * header_buf;
72        int header_buf_len = 2048;
73        int header_buf_used = 0;
74        char * content_buf;
75        int content_buf_len = 2048;
76        int content_buf_used = 0;
77        char chunksize_buf[32];
78        int chunksize_buf_index;
79
80        header_buf = malloc(header_buf_len);
81        content_buf = malloc(content_buf_len);
82        chunksize_buf[0] = '\0';
83        chunksize_buf_index = 0;
84
85        while((n = receivedata(s, buf, 2048, 5000)) > 0)
86        {
87                if(endofheaders == 0)
88                {
89                        int i;
90                        int linestart=0;
91                        int colon=0;
92                        int valuestart=0;
93                        if(header_buf_used + n > header_buf_len) {
94                                header_buf = realloc(header_buf, header_buf_used + n);
95                                header_buf_len = header_buf_used + n;
96                        }
97                        memcpy(header_buf + header_buf_used, buf, n);
98                        header_buf_used += n;
99                        /* search for CR LF CR LF (end of headers)
100                         * recognize also LF LF */
101                        i = 0;
102                        while(i < (header_buf_used-1) && (endofheaders == 0)) {
103                                if(header_buf[i] == '\r') {
104                                        i++;
105                                        if(header_buf[i] == '\n') {
106                                                i++;
107                                                if(i < header_buf_used && header_buf[i] == '\r') {
108                                                        i++;
109                                                        if(i < header_buf_used && header_buf[i] == '\n') {
110                                                                endofheaders = i+1;
111                                                        }
112                                                }
113                                        }
114                                } else if(header_buf[i] == '\n') {
115                                        i++;
116                                        if(header_buf[i] == '\n') {
117                                                endofheaders = i+1;
118                                        }
119                                }
120                                i++;
121                        }
122                        if(endofheaders == 0)
123                                continue;
124                        /* parse header lines */
125                        for(i = 0; i < endofheaders - 1; i++) {
126                                if(colon <= linestart && header_buf[i]==':')
127                                {
128                                        colon = i;
129                                        while(i < (endofheaders-1)
130                                              && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
131                                                i++;
132                                        valuestart = i + 1;
133                                }
134                                /* detecting end of line */
135                                else if(header_buf[i]=='\r' || header_buf[i]=='\n')
136                                {
137                                        if(colon > linestart && valuestart > colon)
138                                        {
139#ifdef DEBUG
140                                                printf("header='%.*s', value='%.*s'\n",
141                                                       colon-linestart, header_buf+linestart,
142                                                       i-valuestart, header_buf+valuestart);
143#endif
144                                                if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
145                                                {
146                                                        content_length = atoi(header_buf+valuestart);
147#ifdef DEBUG
148                                                        printf("Content-Length: %d\n", content_length);
149#endif
150                                                }
151                                                else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
152                                                   && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
153                                                {
154#ifdef DEBUG
155                                                        printf("chunked transfer-encoding!\n");
156#endif
157                                                        chunked = 1;
158                                                }
159                                        }
160                                        while(header_buf[i]=='\r' || header_buf[i] == '\n')
161                                                i++;
162                                        linestart = i;
163                                        colon = linestart;
164                                        valuestart = 0;
165                                }
166                        }
167                        /* copy the remaining of the received data back to buf */
168                        n = header_buf_used - endofheaders;
169                        memcpy(buf, header_buf + endofheaders, n);
170                        /* if(headers) */
171                }
172                if(endofheaders)
173                {
174                        /* content */
175                        if(chunked)
176                        {
177                                int i = 0;
178                                while(i < n)
179                                {
180                                        if(chunksize == 0)
181                                        {
182                                                /* reading chunk size */
183                                                if(chunksize_buf_index == 0) {
184                                                        /* skipping any leading CR LF */
185                                                        if(i<n && buf[i] == '\r') i++;
186                                                        if(i<n && buf[i] == '\n') i++;
187                                                }
188                                                while(i<n && isxdigit(buf[i])
189                                                     && chunksize_buf_index < (sizeof(chunksize_buf)-1))
190                                                {
191                                                        chunksize_buf[chunksize_buf_index++] = buf[i];
192                                                        chunksize_buf[chunksize_buf_index] = '\0';
193                                                        i++;
194                                                }
195                                                while(i<n && buf[i] != '\r' && buf[i] != '\n')
196                                                        i++; /* discarding chunk-extension */
197                                                if(i<n && buf[i] == '\r') i++;
198                                                if(i<n && buf[i] == '\n') {
199                                                        int j;
200                                                        for(j = 0; j < chunksize_buf_index; j++) {
201                                                        if(chunksize_buf[j] >= '0'
202                                                           && chunksize_buf[j] <= '9')
203                                                                chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
204                                                        else
205                                                                chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
206                                                        }
207                                                        chunksize_buf[0] = '\0';
208                                                        chunksize_buf_index = 0;
209                                                        i++;
210                                                } else {
211                                                        /* not finished to get chunksize */
212                                                        continue;
213                                                }
214#ifdef DEBUG
215                                                printf("chunksize = %u (%x)\n", chunksize, chunksize);
216#endif
217                                                if(chunksize == 0)
218                                                {
219#ifdef DEBUG
220                                                        printf("end of HTTP content - %d %d\n", i, n);
221                                                        /*printf("'%.*s'\n", n-i, buf+i);*/
222#endif
223                                                        goto end_of_stream;
224                                                }
225                                        }
226                                        bytestocopy = ((int)chunksize < n - i)?chunksize:(n - i);
227                                        if((int)(content_buf_used + bytestocopy) > content_buf_len)
228                                        {
229                                                if(content_length >= content_buf_used + (int)bytestocopy) {
230                                                        content_buf_len = content_length;
231                                                } else {
232                                                        content_buf_len = content_buf_used + (int)bytestocopy;
233                                                }
234                                                content_buf = (char *)realloc((void *)content_buf,
235                                                                              content_buf_len);
236                                        }
237                                        memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
238                                        content_buf_used += bytestocopy;
239                                        i += bytestocopy;
240                                        chunksize -= bytestocopy;
241                                }
242                        }
243                        else
244                        {
245                                /* not chunked */
246                                if(content_length > 0
247                                   && (content_buf_used + n) > content_length) {
248                                        /* skipping additional bytes */
249                                        n = content_length - content_buf_used;
250                                }
251                                if(content_buf_used + n > content_buf_len)
252                                {
253                                        if(content_length >= content_buf_used + n) {
254                                                content_buf_len = content_length;
255                                        } else {
256                                                content_buf_len = content_buf_used + n;
257                                        }
258                                        content_buf = (char *)realloc((void *)content_buf,
259                                                                      content_buf_len);
260                                }
261                                memcpy(content_buf + content_buf_used, buf, n);
262                                content_buf_used += n;
263                        }
264                }
265                /* use the Content-Length header value if available */
266                if(content_length > 0 && content_buf_used >= content_length)
267                {
268#ifdef DEBUG
269                        printf("End of HTTP content\n");
270#endif
271                        break;
272                }
273        }
274end_of_stream:
275        free(header_buf); header_buf = NULL;
276        *size = content_buf_used;
277        if(content_buf_used == 0)
278        {
279                free(content_buf);
280                content_buf = NULL;
281        }
282        return content_buf;
283}
284
285/* miniwget3() :
286 * do all the work.
287 * Return NULL if something failed. */
288static void *
289miniwget3(const char * url, const char * host,
290          unsigned short port, const char * path,
291          int * size, char * addr_str, int addr_str_len,
292          const char * httpversion)
293{
294        char buf[2048];
295    int s;
296        int n;
297        int len;
298        int sent;
299        void * content;
300
301        *size = 0;
302        s = connecthostport(host, port);
303        if(s < 0)
304                return NULL;
305
306        /* get address for caller ! */
307        if(addr_str)
308        {
309                struct sockaddr_storage saddr;
310                socklen_t saddrlen;
311
312                saddrlen = sizeof(saddr);
313                if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
314                {
315                        perror("getsockname");
316                }
317                else
318                {
319#if defined(__amigaos__) && !defined(__amigaos4__)
320        /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
321     * But his function make a string with the port :  nn.nn.nn.nn:port */
322/*              if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
323                            NULL, addr_str, (DWORD *)&addr_str_len))
324                {
325                    printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
326                }*/
327                        /* the following code is only compatible with ip v4 addresses */
328                        strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
329#else
330#if 0
331                        if(saddr.sa_family == AF_INET6) {
332                                inet_ntop(AF_INET6,
333                                          &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
334                                          addr_str, addr_str_len);
335                        } else {
336                                inet_ntop(AF_INET,
337                                          &(((struct sockaddr_in *)&saddr)->sin_addr),
338                                          addr_str, addr_str_len);
339                        }
340#endif
341                        /* getnameinfo return ip v6 address with the scope identifier
342                         * such as : 2a01:e35:8b2b:7330::%4281128194 */
343                        n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
344                                        addr_str, addr_str_len,
345                                        NULL, 0,
346                                        NI_NUMERICHOST | NI_NUMERICSERV);
347                        if(n != 0) {
348#ifdef _WIN32
349                                fprintf(stderr, "getnameinfo() failed : %d\n", n);
350#else
351                                fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
352#endif
353                        }
354#endif
355                }
356#ifdef DEBUG
357                printf("address miniwget : %s\n", addr_str);
358#endif
359        }
360
361        len = snprintf(buf, sizeof(buf),
362                 "GET %s HTTP/%s\r\n"
363                             "Host: %s:%d\r\n"
364                                 "Connection: Close\r\n"
365                                 "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
366
367                                 "\r\n",
368                           path, httpversion, host, port);
369        sent = 0;
370        /* sending the HTTP request */
371        while(sent < len)
372        {
373                n = send(s, buf+sent, len-sent, 0);
374                if(n < 0)
375                {
376                        perror("send");
377                        closesocket(s);
378                        return NULL;
379                }
380                else
381                {
382                        sent += n;
383                }
384        }
385        content = getHTTPResponse(s, size);
386        closesocket(s);
387        return content;
388}
389
390/* miniwget2() :
391 * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
392static void *
393miniwget2(const char * url, const char * host,
394                  unsigned short port, const char * path,
395                  int * size, char * addr_str, int addr_str_len)
396{
397        char * respbuffer;
398
399        respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
400/*
401        respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.0");
402        if (*size == 0)
403        {
404#ifdef DEBUG
405                printf("Retrying with HTTP/1.1\n");
406#endif
407                free(respbuffer);
408                respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
409        }
410*/
411        return respbuffer;
412}
413
414
415
416
417/* parseURL()
418 * arguments :
419 *   url :              source string not modified
420 *   hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
421 *   port :             port (destination)
422 *   path :             pointer to the path part of the URL
423 *
424 * Return values :
425 *    0 - Failure
426 *    1 - Success         */
427int parseURL(const char * url, char * hostname, unsigned short * port, char * * path)
428{
429        char * p1, *p2, *p3;
430        if(!url)
431                return 0;
432        p1 = strstr(url, "://");
433        if(!p1)
434                return 0;
435        p1 += 3;
436        if(  (url[0]!='h') || (url[1]!='t')
437           ||(url[2]!='t') || (url[3]!='p'))
438                return 0;
439        memset(hostname, 0, MAXHOSTNAMELEN + 1);
440        if(*p1 == '[')
441        {
442                /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
443                p2 = strchr(p1, ']');
444                p3 = strchr(p1, '/');
445                if(p2 && p3)
446                {
447                        p2++;
448                        strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
449                        if(*p2 == ':')
450                        {
451                                *port = 0;
452                                p2++;
453                                while( (*p2 >= '0') && (*p2 <= '9'))
454                                {
455                                        *port *= 10;
456                                        *port += (unsigned short)(*p2 - '0');
457                                        p2++;
458                                }
459                        }
460                        else
461                        {
462                                *port = 80;
463                        }
464                        *path = p3;
465                        return 1;
466                }
467        }
468        p2 = strchr(p1, ':');
469        p3 = strchr(p1, '/');
470        if(!p3)
471                return 0;
472        if(!p2 || (p2>p3))
473        {
474                strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
475                *port = 80;
476        }
477        else
478        {
479                strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
480                *port = 0;
481                p2++;
482                while( (*p2 >= '0') && (*p2 <= '9'))
483                {
484                        *port *= 10;
485                        *port += (unsigned short)(*p2 - '0');
486                        p2++;
487                }
488        }
489        *path = p3;
490        return 1;
491}
492
493void * miniwget(const char * url, int * size)
494{
495        unsigned short port;
496        char * path;
497        /* protocol://host:port/chemin */
498        char hostname[MAXHOSTNAMELEN+1];
499        *size = 0;
500        if(!parseURL(url, hostname, &port, &path))
501                return NULL;
502#ifdef DEBUG
503        printf("parsed url : hostname='%s' port=%hu path='%s'\n", hostname, port, path);
504#endif
505        return miniwget2(url, hostname, port, path, size, 0, 0);
506}
507
508void * miniwget_getaddr(const char * url, int * size, char * addr, int addrlen)
509{
510        unsigned short port;
511        char * path;
512        /* protocol://host:port/path */
513        char hostname[MAXHOSTNAMELEN+1];
514        *size = 0;
515        if(addr)
516                addr[0] = '\0';
517        if(!parseURL(url, hostname, &port, &path))
518                return NULL;
519#ifdef DEBUG
520        printf("parsed url : hostname='%s' port=%hu path='%s'\n", hostname, port, path);
521#endif
522        return miniwget2(url, hostname, port, path, size, addr, addrlen);
523}
524
Note: See TracBrowser for help on using the repository browser.