Annotation of embedaddon/miniupnpc/miniwget.c, revision 1.1

1.1     ! misho       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:  */
        !            58: void *
        !            59: getHTTPResponse(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:        }
        !           272: end_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. */
        !           286: static void *
        !           287: miniwget3(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. */
        !           390: static void *
        !           391: miniwget2(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         */
        !           425: int 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: 
        !           491: void * 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: 
        !           506: void * 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: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>