使用 C 和 Lua 中的套接字保存来自 http 响应的图像

Save image from http response with sockets in C and Lua

我创建了一个通过 https 发送 http 请求的 C 函数,现在我需要使用 Lua 抓取通过该请求检索到的图像。 但是当我删除 header 并保存 body 时,图像无效。

当请求的文件是文本时,不会发生任何错误。

这是发送http请求的函数

static char* receive(BIO *bio, int chunksize) {
    int  readbytes, count = 1;
    char tmp[chunksize], *buf = NULL;

    buf = (char *) malloc(chunksize * sizeof(char));
    buf[0] = '[=11=]';
    while(1) {
        readbytes = BIO_read(bio, tmp, chunksize - 1); 
        if(readbytes <= 0) break;
        tmp[readbytes] = '[=11=]';

        buf = (char *) realloc(buf, chunksize * sizeof(char) * count);

        strncat(buf, tmp, strlen(tmp));

        count++;
    }
    return buf;
}

static int ssl_get(const char *host, const char *port, const char *request, char **response, const int datasize, const int hostsize) {
    char host_port[hostsize];
    int host_size;

    /* ssl */
    BIO *bio;
    SSL *ssl;
    SSL_CTX *ctx;

    /* init ssl lib */
    SSL_library_init();
    ERR_load_BIO_strings();
    OpenSSL_add_all_algorithms();

    /* set up ssl context */
    ctx = SSL_CTX_new(SSLv23_client_method());

    /* set up connection */
    bio = BIO_new_ssl_connect(ctx);
    BIO_get_ssl(bio, &ssl);
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

    /* create connection */
    /* host and port on the form of <host>:<port> */
    strncpy(host_port, host, strlen(host));
    host_size = strlen(host);
    host_port[host_size] = ':';
    host_port[host_size + 1] = '[=11=]';
    strncat(host_port, port, strlen(port));


    BIO_set_conn_hostname(bio, host_port);

    if (BIO_do_connect(bio) <= 0) {
        //fprintf(stderr, "Error attempting to connect\n");
        //ERR_print_errors_fp(stderr);
        BIO_free_all(bio);
        SSL_CTX_free(ctx);
        return -2;
    }

    /* Send request */
    BIO_write(bio, request, strlen(request));

    /* read response */
    (*response) = receive(bio, datasize);


    BIO_free_all(bio);
    SSL_CTX_free(ctx);

    return 0;
}

这是请求

GET host.org/path/to/image.png HTTP/1.0\r\n\r\n

这是我收到的第一部分

HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Sun, 19 Feb 2017 20:41:58 GMT
Content-Type: image/png
Content-Length: 88746
Last-Modified: Thu, 10 Nov 2016 00:30:48 GMT
Connection: close
ETag: "5823bfb8-15aaa"
Strict-Transport-Security: max-age=15768000
Accept-Ranges: bytes

PNG

Ü­¢
;«&_éÓöñ}®¸Ú6ËpkÕ²®ÚÃz·f­KÑÙfuY#¸   #åÂj½põ¿§Lá[ÉjùÝ5®çsã9ûIåå>%¸°Ôà`Pé§x×|QWqXá²!jØZOö­ºÿýõͺ¡Ws¦04ÜGM9¦¡FÄ¡¶|Ú*)˺9Émß6>8­ö»I#/jI®ÛfËþ.ã50Ï­ïð.éѦÚAíõX

与浏览器下载的图片对比

PNG

���
IHDR��U��m���Þ¡Z²���sRGB�®Îé���gAMA��±üa��� pHYs��Ä��Ä+���bKGD�ÿ�ÿ�ÿ ½§��ÿIDATx^ìüEÙÇ}­¼( Ò;IÐ;üCïI`�¥ªüc{M@"*JUD
 "EPФ'T!@hI�é  `Ë;ß¹Û¹½Ù½Ý»Ý»½»çûù<Éý÷övgggwç·Ï3Ï,´@£AAA¦x_ð¿   Ð"ªAAAZ@D   B¨AAAhU   - ¢JAA¡DT       ´*AAAQ%  Ð^Q5ïj¡ªµg¨yÁ÷!óÔ#¾o¾r´Z¶äßO7NM>çÎôqýÛOuïlc\ý^ÒãôqÁ6¬y¶U6êÊdÁñ×ÅuFç{Û)ª­z
uö_EÜ÷ÜãHs~Ò!¹\½Ò²Õ]/Ñç7®ÜyO{ÛM~×g§Îkñõ­>òì¹íÆ$·Î·ÝrU03øÛaæ5THôfº§ëaÔäàÏ>ÅÝød5¥íuáµ�®70|hðIènjëÅ_CmG®&éÁ¶ 8tëù+w¿µW©Z<î~­ÓÞ¢FTÍ;ã@åÓS.G¹jt?cZ°@Ûñú¯2ÑjÙü¿>n9E2]Õh*Íd¯ª*®î³ã¼9³OjâÜJyf/WK£¨¶^ü5Ô~Ês´ûvº

全部代码

Complete C library

Aux Lua module

Lua 脚本

#!/usr/bin/lua

sslrequest = require "sslrequest"
parse = require "parse"

local protocol_, host, port, request = parse.spliturl("https://host.fake/path/to/image.png")
local res = sslrequest.ssl_get(host, port, "GET " .. request .. " HTTP/1.0\r\n\r\n", 1024, 100)
local _, body = separate_http(res)

local f = assert(io.open("img.png", "wb"))
f:write(body)
f:close()

我不知道我错过了什么

编辑: 十六进制图像文件

用浏览器下载的图片

00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
00000010: 0000 0355 0000 026d 0806 0000 00de a15a  ...U...m.......Z
00000020: b200 0000 0173 5247 4200 aece 1ce9 0000  .....sRGB.......
00000030: 0004 6741 4d41 0000 b18f 0bfc 6105 0000  ..gAMA......a...
00000040: 0009 7048 5973 0000 0ec4 0000 0ec4 0195  ..pHYs..........
00000050: 2b0e 1b00 0000 0662 4b47 4400 ff00 ff00  +......bKGD.....
00000060: ffa0 bda7 9300 00ff 8049 4441 5478 5eec  .........IDATx^.
00000070: 9d07 fc1c 45d9 c707 7dad bc28 20d2 3b49  ....E...}..( .;I
00000080: 90d0 3bfc 43ef 4904 8260 00a5 aafc 637b  ..;.C.I..`....c{
00000090: 4d40 9022 2a4a 5544 820d 120b 2022 4581  M@."*JUD.... "E.
000000a0: 5092 d0a4 2754 2140 6849 00e9 108a 2020  P...'T!@hI....  
000000b0: 60cb 3bdf b99d dbb9 bdd9 bddd bbdd bbbd  `.;.............
000000c0: bbe7 fbf9 3cc9 fdf7 f676 6767 6777 e7b7  ....<....vgggw..
000000d0: cf33 cf2c b440 a304 4110 0441 1004 4110  .3.,.@..A..A..A.
000000e0: 84a6 785f f0bf 2008 8220 0882 2008 82d0  ..x_.. .. .. ...
000000f0: 0422 aa04 4110 0441 1004 4110 5a40 4495  ."..A..A..A.Z@D.
00000100: 2008 8220 0882 2008 420b 88a8 1204 4110   .. .. .B.....A.
00000110: 0441 1004 4168 0111 5582 2008 8220 0882  .A..Ah..U. .. ..
00000120: 2008 2d20 a24a 1004 4110 0441 1004 a105   .- .J..A..A....
00000130: 4454 0982 2008 8220 0882 20b4 8088 2a41  DT.. .. .. ...*A
00000140: 1004 4110 0441 1084 1610 5125 0882 2008  ..A..A....Q%.. .
00000150: 8220 0882 d002 5e51 35ef 8c11 6aa1 8516  . ....^Q5...j...
00000160: aab5 1167 a879 c1f7 21f3 d419 2392 be6f  ...g.y..!...#..o
00000170: 17be 72b4 5ab6 e4df 4f1f 374e 4d0f 3ee7  ..r.Z...O.7NM.>.
00000180: cef4 7195 fd1a 8bdb 4f1e 75ef 6c63 5cfd  ..q.....O.u.lc\.
00000190: 5ed2 1ee3 f471 c136 ac79 b655 36ea ca9c  ^....q.6.y.U6...
000001a0: 64c1 f184 d7c5 0875 46e7 1a7b db29 aaad  d......uF..{.)..
000001b0: 177a 0d75 80f6 5f07 45dc f7dc e348 737e  .z.u.._.E....Hs~
000001c0: d296 21b9 5cbd d216 b2d5 5d2f d19b e737  ..!.\.....]/...7
000001d0: aedc 791f 4f7b db4d 7ed7 67a7 ce6b f1f5  ..y.O{.M~.g..k..
000001e0: 95ad 3ef2 ec1b 14b9 edc6 241f b78f ceb7  ..>.......$.....
000001f0: dd72 1111 5595 020f 9d30 33f8 db61 e604  .r..U....03..a..
00000200: 3554 1f48 17f4 970b 66ba 1aa7 eb61 d4e4  5T.H....f....a..
00000210: e0cf 0298 3ec5 ddf8 6435 a5ed 759e e118  ....>...d5..u...
00000220: b500 8cae 3730 7c68 f049 e86e 8a6a ebc5  ....70|h.I.n.j..
00000230: 5f43 6d47 ae83 26e9 c1b6 2038 74eb f98d  _CmG..&... 8t...
00000240: 2b77 bfb5 57a9 875a 8a3c ee7e add3 dea2  +w..W..Z.<.~....
00000250: 4654 cd3b e340 e5d3 532e 9347 b96a 7488  FT.;.@..S..G.jt.
00000260: 1a3f 6381 5ab0 40db 8cf1 faaf 32d1 6ad9  .?c.Z.@.....2.j.
00000270: fcbf 9f3e 6e94 9639 4532 5dd5 682a cd64  ...>n..9E2].h*.d
00000280: afaa 2aae eeb3 1ce3 bc39 b383 4f03 6ae2  ..*......9..O.j.
00000290: dc4a 7966 8c2f 574b 109a a3a8 b65e fc35  .Jyf./WK.....^.5
000002a0: d47e ca73 1db4 fb9e 9c76 7f9d ba9f 0aed  .~.s.....v......
000002b0: a1b7 ce6f 5cb9 7bb7 bd66 3b7f bd7f ddb6  ...o\.{..f;.....
000002c0: bf3d 97a3 4ed3 decf 8538 6a44 d5dc 87ad  .=..N....8jD....
000002d0: a20a 3b06 159b ab26 0e04 5fe9 d31e f6f1  ..;....&.._.....
000002e0: 7dae b88a da36 cb70 6bd5 84b2 859e aeda  }....6.pk.......
000002f0: 10c3 7ab7 66ad 4bd1 d966 7559 231a b809  ..z.f.K..fuY#...
00000300: 23e5 c26a bd70 f5bf a74c e15b 84c9 6a94  #..j.p...L.[..j.
00000310: f9dd 0835 c2ae e773 e339 fb49 e5e5 9b3e  ...5...s.9.I...>

随程序下载的图像

00000000: 8950 4e47 0d0a 1a0a 36cb 706b d584 b285  .PNG....6.pk....
00000010: 9eae da10 c37a b766 ad4b d1d9 6675 5923  .....z.f.K..fuY#
00000020: 1ab8 0923 e5c2 6abd 70f5 bfa7 4ce1 5b84  ...#..j.p...L.[.
00000030: c96a 94f9 dd08 35c2 aee7 73e3 39fb 49e5  .j....5...s.9.I.
00000040: e59b 3e25 b8b0 06d4 e060 50e9 93a7 788e  ..>%.....`P...x.
00000050: d77f 7c51 5771 588f e1b2 9021 6ad8 5a95  ..|QWqX....!j.Z.
00000060: 4ff6 adba ff18 fdf5 cdba a157 73a6 9a30  O..........Ws..0
00000070: 34dc 474d 39a6 9fa1 46c4 94a1 b67c da12  4.GM9...F....|..
00000080: 2a29 cbba 8d18 39c9 6ddf 9136 3e38 adf6  *)....9.m..6>8..
00000090: bb49 2383 2f6a 49ae db90 66cb 1dfe 2ee3  .I#./jI...f.....
000000a0: 3530 cfad ef8a 8df0 142e e91c d1a6 93da  50..............
000000b0: 41ed f51b 580a 773c c714 b7dd 52b6 9914  A...X.w<....R...
000000c0: 75c9 3ee2 ae03 1f45 1c67 48c2 7dcf 732c  u.>....E.gH.}.s,
000000d0: 49e5 ac10 6977 75e7 b8c1 7db6 4a07 eea7  I...iwu...}.J...
000000e0: 298f b7e6 7ce8 ef6a eb3d be7e d2ae 1747  )...|..j.=.~...G
000000f0: d66b 2853 7b68 f87c 73b7 17bd 9784 e7dc  .k(S{h.|s.......
00000100: 6deb c9ed b633 cf4b df39 883f 2e4d caeb  m....3.K.9.?.M..
00000110: d977 8f1a 17b3 bcba 8fa6 ae2f 1f8d aeb9  .w........./....
00000120: 90da e3d7 96a6 d2bc a43d 7f29 ea41 93a6  .........=.).A..
00000130: 5ccd dd07 d3d6 67dc 7d29 fe7e 55dd 8f59  \.....g.}).~U..Y
00000140: 9ebe 3e7c 359e b5cc 456c bb76 3d6d a9da  ..>|5...El.v=m..
00000150: 467c fd40 33c7 15ae af2d a90c 29ae cd86  F|.@3....-..)...
00000160: 64dc 86ef 78c2 65d4 bf53 1fbe b2fb ee57  d...x.e..S.....W
00000170: bad3 5865 daa0 5ac0 22dd ab5c 302d 5856  ..Xe..Z."..[=17=]-XV
00000180: 65ee c405 bacf 69be 1fd0 8a2b 58b8 4077  e.....i....+X.@w
00000190: 442b bf19 98a8 ff82 690b 0683 f5b2 59ed  D+......i.....Y.
000001a0: 3ec3 b2c4 d9c0 826a 31bc e5f0 2dab 90b4  >......j1...-...
000001b0: eda4 63f3 ff6e 70c1 c489 03d5 cfd1 7a4b  ..c..np.......zK
000001c0: ac53 0fd5 f5d9 a7b7 ce2d fee3 9b5b 2d4b  .S.......-...[-K
000001d0: 9cb9 f516 ae6f b71f 778c beb2 fbd7 ad6c  .....o..w......l
000001e0: 3fb6 1cd5 b226 b593 e8fe b2ac db2c 4e7d  ?....&.......,N}
000001f0: 0efa b7d8 b86e f32d b7bf 7e5d ab3d 9790  .....n.-..~].=..
00000200: 58c6 c875 9074 8e26 26b4 83e4 7a48 3eae  X..u.t.&&...zH>.
00000210: a4f6 55b6 3693 b62e 93ae 031f f91d 6786  ..U.6.........g.
00000220: fbde b441 671b 51ab 2d6b e376 d74c 19ea  ...Ag.Q.-k.v.L..
00000230: 97c5 b585 5cee a719 8e37 b93d 63d1 73e9  ....\....7.=c.s.
00000240: 1c4b ac25 972f db35 94ad 2d27 9d3f f739  .K.%./.5..-'.?.9
00000250: 125f 97e1 fedc f593 db6d 9bcf 6fec 3918  ._.......m..o.9.
00000260: 5830 505d 5ebb 8dd6 aee7 c105 8331 cbcd  X0P]^........1..
00000270: 3e32 b437 1f49 e7ac 62f1 e7a8 f1ba 515a  >2.7.I..b.....QZ
00000280: bb3e 13eb 2143 b91a df07 5bbb cec2 edbb  .>..!C....[.....
00000290: ebb8 e573 cf4b b4cd a7af 0fb6 9d7c 3d63  ...s.K.......|=c
000002a0: f1e5 847c b79d 77db 80ac e722 5b19 d25e  ...|..w...."[..^
000002b0: 9bf1 e5cb b20d 883b 9efa fb87 bf1d 5508  .......;......U.
000002c0: cf5b f85d 8da8 aa2f 54a3 9b81 efa0 b323  .[.].../T......#
000002d0: f7a0 e396 3b05 f637 8cfa fdd6 9f30 5f39  ....;..7.....0_9
000002e0: fc95 ef3f d914 83ca 7797 65f9 bdb3 ae5b  ...?....w.e....[
000002f0: 58e7 c4d5 d79d 87ea fae1 4d20 6c14 d1ce  X.........M l...
00000300: 9abf 7c6e 23aa d9a7 dbe8 1a14 26ae 8e7c  ..|n#.......&..|
00000310: c495 2fb6 1c86 f80b 3cfc 9ddd 7796 755b  ../.....<...w.u[

编辑2: 当使用另一个客户端(下面的代码)通过不安全的 HTTP 请求图像时,也会发生这种情况。第一个块(直到 PNG header)没问题,但如果我继续执行 recv,我只会收到垃圾。

另一个奇怪的事情是,无论缓冲区大小如何,第一个 recv 总是只检索 PNG header。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define MAXDATASIZE 100000


void *get_in_addr(struct sockaddr *sa){
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int http_get(char *host, char *port, char *msg) {
    int sockfd, numbytes;
    char buf[MAXDATASIZE];
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(host, port, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    for (p=servinfo; p!=NULL; p=p->ai_next){
        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("client: connect");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    // convert the IP to string
    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof(s));
    printf("client: connecting to %s\n", s);

    freeaddrinfo(servinfo);

    // send
    if (send(sockfd, msg, strlen(msg), 0) == -1) {
        perror("send"); 
        exit(1);
    }

    // recv
    if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
        perror("recv");
        exit(1);
    }
    buf[numbytes] = '[=18=]';

    printf("%s\n", buf);
    printf("%d\n", numbytes);

    close(sockfd);

    return 0;
}

int main() {
    http_get("host", "80", "GET /image.png  HTTP/1.1\r\nHost: host:80\r\n\r\n");
}

终于!

我在 header 之后得到垃圾的原因是因为我在图像数据上使用了以 NULL 结尾的字符串的函数(我想这就是文本响应显示得很好的原因)。

我没有使用 strncat,而是创建了一个使用固定大小连接字符串的连接函数。

static char* concat(const char *dest, const char *src, size_t dest_size, size_t src_size) {
    char *new = (char *) malloc(dest_size + src_size);

    if (!dest && !src) return NULL;

    if (!new) return NULL;

    if (!dest) for (int i=0; i < src_size; i++) new[i] = src[i];
    else if (!src) for (int i=0; i < dest_size; i++) new[i] = dest[i];
    else {
        for (int i=0; i< dest_size; i++) new[i] = dest[i];
        for (int i=dest_size, j=0; j < src_size ; i++, j++) new[i] = src[j];
    }

    return new;
}

然后将非 NULL 终止的字符串推送到 Lua 堆栈,我需要使用 lua_pushlstring 而不是 lua_pushstring,指定接收到的数据的总大小。

Here 是完整代码。

谢谢! :)