Annotation of embedaddon/miniupnpd/miniupnpc/src/miniwget.c, revision 1.1.1.1
1.1 misho 1: /* $Id: miniwget.c,v 1.82 2020/05/29 21:14:22 nanard Exp $ */
2: /* Project : miniupnp
3: * Website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
4: * Author : Thomas Bernard
5: * Copyright (c) 2005-2020 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: #include "win32_snprintf.h"
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 <netinet/in.h>
37: #include <arpa/inet.h>
38: #include <net/if.h>
39: #include <netdb.h>
40: #define closesocket close
41: #include <strings.h>
42: #endif /* #else _WIN32 */
43: #ifdef __GNU__
44: #define MAXHOSTNAMELEN 64
45: #endif /* __GNU__ */
46:
47: #ifndef MIN
48: #define MIN(x,y) (((x)<(y))?(x):(y))
49: #endif /* MIN */
50:
51:
52: #include "miniupnpcstrings.h"
53: #include "miniwget.h"
54: #include "connecthostport.h"
55: #include "receivedata.h"
56:
57: #ifndef MAXHOSTNAMELEN
58: #define MAXHOSTNAMELEN 64
59: #endif
60:
61: /*
62: * Read a HTTP response from a socket.
63: * Process Content-Length and Transfer-encoding headers.
64: * return a pointer to the content buffer, which length is saved
65: * to the length parameter.
66: */
67: void *
68: getHTTPResponse(SOCKET s, int * size, int * status_code)
69: {
70: char buf[2048];
71: int n;
72: int endofheaders = 0;
73: int chunked = 0;
74: int content_length = -1;
75: unsigned int chunksize = 0;
76: unsigned int bytestocopy = 0;
77: /* buffers : */
78: char * header_buf;
79: unsigned int header_buf_len = 2048;
80: unsigned int header_buf_used = 0;
81: char * content_buf;
82: unsigned int content_buf_len = 2048;
83: unsigned int content_buf_used = 0;
84: char chunksize_buf[32];
85: unsigned int chunksize_buf_index;
86: #ifdef DEBUG
87: char * reason_phrase = NULL;
88: int reason_phrase_len = 0;
89: #endif
90:
91: if(status_code) *status_code = -1;
92: header_buf = malloc(header_buf_len);
93: if(header_buf == NULL)
94: {
95: #ifdef DEBUG
96: fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
97: #endif /* DEBUG */
98: *size = -1;
99: return NULL;
100: }
101: content_buf = malloc(content_buf_len);
102: if(content_buf == NULL)
103: {
104: free(header_buf);
105: #ifdef DEBUG
106: fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
107: #endif /* DEBUG */
108: *size = -1;
109: return NULL;
110: }
111: chunksize_buf[0] = '\0';
112: chunksize_buf_index = 0;
113:
114: while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0)
115: {
116: if(endofheaders == 0)
117: {
118: int i;
119: int linestart=0;
120: int colon=0;
121: int valuestart=0;
122: if(header_buf_used + n > header_buf_len) {
123: char * tmp = realloc(header_buf, header_buf_used + n);
124: if(tmp == NULL) {
125: /* memory allocation error */
126: free(header_buf);
127: free(content_buf);
128: *size = -1;
129: return NULL;
130: }
131: header_buf = tmp;
132: header_buf_len = header_buf_used + n;
133: }
134: memcpy(header_buf + header_buf_used, buf, n);
135: header_buf_used += n;
136: /* search for CR LF CR LF (end of headers)
137: * recognize also LF LF */
138: i = 0;
139: while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
140: if(header_buf[i] == '\r') {
141: i++;
142: if(header_buf[i] == '\n') {
143: i++;
144: if(i < (int)header_buf_used && header_buf[i] == '\r') {
145: i++;
146: if(i < (int)header_buf_used && header_buf[i] == '\n') {
147: endofheaders = i+1;
148: }
149: }
150: }
151: } else if(header_buf[i] == '\n') {
152: i++;
153: if(header_buf[i] == '\n') {
154: endofheaders = i+1;
155: }
156: }
157: i++;
158: }
159: if(endofheaders == 0)
160: continue;
161: /* parse header lines */
162: for(i = 0; i < endofheaders - 1; i++) {
163: if(linestart > 0 && colon <= linestart && header_buf[i]==':')
164: {
165: colon = i;
166: while(i < (endofheaders-1)
167: && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
168: i++;
169: valuestart = i + 1;
170: }
171: /* detecting end of line */
172: else if(header_buf[i]=='\r' || header_buf[i]=='\n')
173: {
174: if(linestart == 0 && status_code)
175: {
176: /* Status line
177: * HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
178: int sp;
179: for(sp = 0; sp < i - 1; sp++)
180: if(header_buf[sp] == ' ')
181: {
182: if(*status_code < 0)
183: {
184: if (header_buf[sp+1] >= '1' && header_buf[sp+1] <= '9')
185: *status_code = atoi(header_buf + sp + 1);
186: }
187: else
188: {
189: #ifdef DEBUG
190: reason_phrase = header_buf + sp + 1;
191: reason_phrase_len = i - sp - 1;
192: #endif
193: break;
194: }
195: }
196: #ifdef DEBUG
197: printf("HTTP status code = %d, Reason phrase = %.*s\n",
198: *status_code, reason_phrase_len, reason_phrase);
199: #endif
200: }
201: else if(colon > linestart && valuestart > colon)
202: {
203: #ifdef DEBUG
204: printf("header='%.*s', value='%.*s'\n",
205: colon-linestart, header_buf+linestart,
206: i-valuestart, header_buf+valuestart);
207: #endif
208: if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
209: {
210: content_length = atoi(header_buf+valuestart);
211: #ifdef DEBUG
212: printf("Content-Length: %d\n", content_length);
213: #endif
214: }
215: else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
216: && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
217: {
218: #ifdef DEBUG
219: printf("chunked transfer-encoding!\n");
220: #endif
221: chunked = 1;
222: }
223: }
224: while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n'))
225: i++;
226: linestart = i;
227: colon = linestart;
228: valuestart = 0;
229: }
230: }
231: /* copy the remaining of the received data back to buf */
232: n = header_buf_used - endofheaders;
233: memcpy(buf, header_buf + endofheaders, n);
234: /* if(headers) */
235: }
236: /* if we get there, endofheaders != 0.
237: * In the other case, there was a continue above */
238: /* content */
239: if(chunked)
240: {
241: int i = 0;
242: while(i < n)
243: {
244: if(chunksize == 0)
245: {
246: /* reading chunk size */
247: if(chunksize_buf_index == 0) {
248: /* skipping any leading CR LF */
249: if(buf[i] == '\r') i++;
250: if(i<n && buf[i] == '\n') i++;
251: }
252: while(i<n && isxdigit(buf[i])
253: && chunksize_buf_index < (sizeof(chunksize_buf)-1))
254: {
255: chunksize_buf[chunksize_buf_index++] = buf[i];
256: chunksize_buf[chunksize_buf_index] = '\0';
257: i++;
258: }
259: while(i<n && buf[i] != '\r' && buf[i] != '\n')
260: i++; /* discarding chunk-extension */
261: if(i<n && buf[i] == '\r') i++;
262: if(i<n && buf[i] == '\n') {
263: unsigned int j;
264: for(j = 0; j < chunksize_buf_index; j++) {
265: if(chunksize_buf[j] >= '0'
266: && chunksize_buf[j] <= '9')
267: chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
268: else
269: chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
270: }
271: chunksize_buf[0] = '\0';
272: chunksize_buf_index = 0;
273: i++;
274: } else {
275: /* not finished to get chunksize */
276: continue;
277: }
278: #ifdef DEBUG
279: printf("chunksize = %u (%x)\n", chunksize, chunksize);
280: #endif
281: if(chunksize == 0)
282: {
283: #ifdef DEBUG
284: printf("end of HTTP content - %d %d\n", i, n);
285: /*printf("'%.*s'\n", n-i, buf+i);*/
286: #endif
287: goto end_of_stream;
288: }
289: }
290: /* it is guaranteed that (n >= i) */
291: bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i);
292: if((content_buf_used + bytestocopy) > content_buf_len)
293: {
294: char * tmp;
295: if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
296: content_buf_len = content_length;
297: } else {
298: content_buf_len = content_buf_used + bytestocopy;
299: }
300: tmp = realloc(content_buf, content_buf_len);
301: if(tmp == NULL) {
302: /* memory allocation error */
303: free(content_buf);
304: free(header_buf);
305: *size = -1;
306: return NULL;
307: }
308: content_buf = tmp;
309: }
310: memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
311: content_buf_used += bytestocopy;
312: i += bytestocopy;
313: chunksize -= bytestocopy;
314: }
315: }
316: else
317: {
318: /* not chunked */
319: if(content_length > 0
320: && (content_buf_used + n) > (unsigned int)content_length) {
321: /* skipping additional bytes */
322: n = content_length - content_buf_used;
323: }
324: if(content_buf_used + n > content_buf_len)
325: {
326: char * tmp;
327: if(content_length >= 0
328: && (unsigned int)content_length >= (content_buf_used + n)) {
329: content_buf_len = content_length;
330: } else {
331: content_buf_len = content_buf_used + n;
332: }
333: tmp = realloc(content_buf, content_buf_len);
334: if(tmp == NULL) {
335: /* memory allocation error */
336: free(content_buf);
337: free(header_buf);
338: *size = -1;
339: return NULL;
340: }
341: content_buf = tmp;
342: }
343: memcpy(content_buf + content_buf_used, buf, n);
344: content_buf_used += n;
345: }
346: /* use the Content-Length header value if available */
347: if(content_length > 0 && content_buf_used >= (unsigned int)content_length)
348: {
349: #ifdef DEBUG
350: printf("End of HTTP content\n");
351: #endif
352: break;
353: }
354: }
355: end_of_stream:
356: free(header_buf);
357: *size = content_buf_used;
358: if(content_buf_used == 0)
359: {
360: free(content_buf);
361: content_buf = NULL;
362: }
363: return content_buf;
364: }
365:
366: /* miniwget3() :
367: * do all the work.
368: * Return NULL if something failed. */
369: static void *
370: miniwget3(const char * host,
371: unsigned short port, const char * path,
372: int * size, char * addr_str, int addr_str_len,
373: const char * httpversion, unsigned int scope_id,
374: int * status_code)
375: {
376: char buf[2048];
377: SOCKET s;
378: int n;
379: int len;
380: int sent;
381: void * content;
382:
383: *size = 0;
384: s = connecthostport(host, port, scope_id);
385: if(ISINVALID(s))
386: return NULL;
387:
388: /* get address for caller ! */
389: if(addr_str)
390: {
391: struct sockaddr_storage saddr;
392: socklen_t saddrlen;
393:
394: saddrlen = sizeof(saddr);
395: if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
396: {
397: perror("getsockname");
398: }
399: else
400: {
401: #if defined(__amigaos__) && !defined(__amigaos4__)
402: /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
403: * But his function make a string with the port : nn.nn.nn.nn:port */
404: /* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
405: NULL, addr_str, (DWORD *)&addr_str_len))
406: {
407: printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
408: }*/
409: /* the following code is only compatible with ip v4 addresses */
410: strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
411: #else
412: #if 0
413: if(saddr.sa_family == AF_INET6) {
414: inet_ntop(AF_INET6,
415: &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
416: addr_str, addr_str_len);
417: } else {
418: inet_ntop(AF_INET,
419: &(((struct sockaddr_in *)&saddr)->sin_addr),
420: addr_str, addr_str_len);
421: }
422: #endif
423: /* getnameinfo return ip v6 address with the scope identifier
424: * such as : 2a01:e35:8b2b:7330::%4281128194 */
425: n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
426: addr_str, addr_str_len,
427: NULL, 0,
428: NI_NUMERICHOST | NI_NUMERICSERV);
429: if(n != 0) {
430: #ifdef _WIN32
431: fprintf(stderr, "getnameinfo() failed : %d\n", n);
432: #else
433: fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
434: #endif
435: }
436: #endif
437: }
438: #ifdef DEBUG
439: printf("address miniwget : %s\n", addr_str);
440: #endif
441: }
442:
443: len = snprintf(buf, sizeof(buf),
444: "GET %s HTTP/%s\r\n"
445: "Host: %s:%d\r\n"
446: "Connection: Close\r\n"
447: "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
448:
449: "\r\n",
450: path, httpversion, host, port);
451: if ((unsigned int)len >= sizeof(buf))
452: {
453: closesocket(s);
454: return NULL;
455: }
456: sent = 0;
457: /* sending the HTTP request */
458: while(sent < len)
459: {
460: n = send(s, buf+sent, len-sent, 0);
461: if(n < 0)
462: {
463: perror("send");
464: closesocket(s);
465: return NULL;
466: }
467: else
468: {
469: sent += n;
470: }
471: }
472: content = getHTTPResponse(s, size, status_code);
473: closesocket(s);
474: return content;
475: }
476:
477: /* miniwget2() :
478: * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
479: static void *
480: miniwget2(const char * host,
481: unsigned short port, const char * path,
482: int * size, char * addr_str, int addr_str_len,
483: unsigned int scope_id, int * status_code)
484: {
485: char * respbuffer;
486:
487: #if 1
488: respbuffer = miniwget3(host, port, path, size,
489: addr_str, addr_str_len, "1.1",
490: scope_id, status_code);
491: #else
492: respbuffer = miniwget3(host, port, path, size,
493: addr_str, addr_str_len, "1.0",
494: scope_id, status_code);
495: if (*size == 0)
496: {
497: #ifdef DEBUG
498: printf("Retrying with HTTP/1.1\n");
499: #endif
500: free(respbuffer);
501: respbuffer = miniwget3(host, port, path, size,
502: addr_str, addr_str_len, "1.1",
503: scope_id, status_code);
504: }
505: #endif
506: return respbuffer;
507: }
508:
509:
510:
511:
512: /* parseURL()
513: * arguments :
514: * url : source string not modified
515: * hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
516: * port : port (destination)
517: * path : pointer to the path part of the URL
518: *
519: * Return values :
520: * 0 - Failure
521: * 1 - Success */
522: int
523: parseURL(const char * url,
524: char * hostname, unsigned short * port,
525: char * * path, unsigned int * scope_id)
526: {
527: char * p1, *p2, *p3;
528: if(!url)
529: return 0;
530: p1 = strstr(url, "://");
531: if(!p1)
532: return 0;
533: p1 += 3;
534: if( (url[0]!='h') || (url[1]!='t')
535: ||(url[2]!='t') || (url[3]!='p'))
536: return 0;
537: memset(hostname, 0, MAXHOSTNAMELEN + 1);
538: if(*p1 == '[')
539: {
540: /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
541: char * scope;
542: scope = strchr(p1, '%');
543: p2 = strchr(p1, ']');
544: if(p2 && scope && scope < p2 && scope_id) {
545: /* parse scope */
546: #ifdef IF_NAMESIZE
547: char tmp[IF_NAMESIZE];
548: int l;
549: scope++;
550: /* "%25" is just '%' in URL encoding */
551: if(scope[0] == '2' && scope[1] == '5')
552: scope += 2; /* skip "25" */
553: l = p2 - scope;
554: if(l >= IF_NAMESIZE)
555: l = IF_NAMESIZE - 1;
556: memcpy(tmp, scope, l);
557: tmp[l] = '\0';
558: *scope_id = if_nametoindex(tmp);
559: if(*scope_id == 0) {
560: *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
561: }
562: #else
563: /* under windows, scope is numerical */
564: char tmp[8];
565: size_t l;
566: scope++;
567: /* "%25" is just '%' in URL encoding */
568: if(scope[0] == '2' && scope[1] == '5')
569: scope += 2; /* skip "25" */
570: l = p2 - scope;
571: if(l >= sizeof(tmp))
572: l = sizeof(tmp) - 1;
573: memcpy(tmp, scope, l);
574: tmp[l] = '\0';
575: *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
576: #endif
577: }
578: p3 = strchr(p1, '/');
579: if(p2 && p3)
580: {
581: p2++;
582: strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
583: if(*p2 == ':')
584: {
585: *port = 0;
586: p2++;
587: while( (*p2 >= '0') && (*p2 <= '9'))
588: {
589: *port *= 10;
590: *port += (unsigned short)(*p2 - '0');
591: p2++;
592: }
593: }
594: else
595: {
596: *port = 80;
597: }
598: *path = p3;
599: return 1;
600: }
601: }
602: p2 = strchr(p1, ':');
603: p3 = strchr(p1, '/');
604: if(!p3)
605: return 0;
606: if(!p2 || (p2>p3))
607: {
608: strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
609: *port = 80;
610: }
611: else
612: {
613: strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
614: *port = 0;
615: p2++;
616: while( (*p2 >= '0') && (*p2 <= '9'))
617: {
618: *port *= 10;
619: *port += (unsigned short)(*p2 - '0');
620: p2++;
621: }
622: }
623: *path = p3;
624: return 1;
625: }
626:
627: void *
628: miniwget(const char * url, int * size,
629: unsigned int scope_id, int * status_code)
630: {
631: unsigned short port;
632: char * path;
633: /* protocol://host:port/chemin */
634: char hostname[MAXHOSTNAMELEN+1];
635: *size = 0;
636: if(!parseURL(url, hostname, &port, &path, &scope_id))
637: return NULL;
638: #ifdef DEBUG
639: printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
640: hostname, port, path, scope_id);
641: #endif
642: return miniwget2(hostname, port, path, size, 0, 0, scope_id, status_code);
643: }
644:
645: void *
646: miniwget_getaddr(const char * url, int * size,
647: char * addr, int addrlen, unsigned int scope_id,
648: int * status_code)
649: {
650: unsigned short port;
651: char * path;
652: /* protocol://host:port/path */
653: char hostname[MAXHOSTNAMELEN+1];
654: *size = 0;
655: if(addr)
656: addr[0] = '\0';
657: if(!parseURL(url, hostname, &port, &path, &scope_id))
658: return NULL;
659: #ifdef DEBUG
660: printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
661: hostname, port, path, scope_id);
662: #endif
663: return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, status_code);
664: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>