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

Last change on this file since 12593 was 12593, checked in by livings124, 10 years ago

#4387 Update miniupnpc to 1.6

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