对于ubuntu,懒得编译源码可以直接安装
sudo apt-get install libssl–dev
/usr/include/openssl/ssl.h
CMakeLists中添加
link_libraries(ssl crypto)
apt-get安装不需要再制定libssl.a, libcrypto.a的路径了, 就像用libc标准库一样。源码安装要指定-L/path/to/libssl.a或者-L/path/to/libssl.so , -lssl。客户端不需要生成证书。
sale.txt
- GET /uploadfile/photo/20231022/37cac8bc693780b740b4e659846689cd.jpg HTTP/1.1
- Accept: image/jpeg,image/avif,image/webp,image/apng,*/*
- Accept-Encoding: gzip, deflate
- Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,ja;q=0.6
- Connection: keep-alive
- Host: img.sx2737.com
- Upgrade-Insecure-Requests: 1
- User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36
-
* main.c
- #include
- /* ... */
- #include "https.h"
-
-
- static int get_host_ip(char ip[], char hostname[]) {
- struct ipv4head ipv4List;
- struct ipv4_entry *n1;
- int count = 0;
- /* get ip by Gateway 10.0.0.1 */
- bcl_dns_query(&ipv4List, hostname, "10.0.0.1");
- while(!TAILQ_EMPTY(&ipv4List)) {
- n1 = TAILQ_FIRST(&ipv4List);
- TAILQ_REMOVE(&ipv4List, n1, entries);
- printf("ip=%s\n", n1->data);
- strncpy(ip, n1->data, 16);
- free(n1);
- count += 1;
- }
- return count;
- }
-
- int main(int argc, char *argv[]) {
- char path_dst[256];
- char path_src[256];
- char hostname[32] = {'\0'};
- char ip[16] = {'\0'};
- int sockfd = -1;
-
- bclerrinit();
-
- strcpy(path_src, "/mnt/e/CLionProjects/arp/fap30/input/sale.txt");
- strcpy(path_dst, "/mnt/e/CLionProjects/arp/fap30/print/sale.jpg");
-
- strcpy(hostname, "img.sx2737.com"); /* octipus.net, www.google.co.il */
- get_host_ip(ip, hostname);
-
- printf("path_dst=%s\n", path_dst);
- if (E_FAIL == https_get_body(ip,path_src, path_dst)) {
- bclerrlog(E_SOCKFD_RECV, _FL_, "https_get_body(%s,%s,%s)",ip,path_src,path_dst);
- return E_FAIL;
- }
- bclerrend();
- return E_OK;
- }
网关直接固定写的10.0.0.1, 需要改。或者再实现一个拿到网关Gateway IP地址的函数 查DNS
DNS没做缓存,简化版测试查询DNS发UDP包。 一个域名对应多个ip,也不知道多少个ip,用的/usr/include/x86_64-linux-gnu/sys/queue.h 参照下面的bcl_udp.h
#include
* https.h
- #ifndef ARP_TEST_HTTPS_H
- #define ARP_TEST_HTTPS_H
-
- #include
/* SSL_Library_init() */ - #include
-
- int https_get_body(char *ip, char *in_path, char *out_path);
-
- #endif //ARP_TEST_HTTPS_H
* https.c
- /*
- * @ref: https://aticleworld.com/ssl-server-client-using-openssl-in-c/
- * sudo apt-get install libssl–dev
- * -L/path/to/ssl_dir -lssl -lcrypto
- */
- #include
/* memset */ - /* ... */
- #include "https.h"
-
- extern unsigned int ip2int(const char *ip);
-
- static SSL_CTX *InitCTX(void) {
- SSL_METHOD *method;
- SSL_CTX *ctx;
- OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
- SSL_load_error_strings(); /* Bring in and register error messages */
- method = TLSv1_2_client_method(); /* Create new client-method instance */
- ctx = SSL_CTX_new(method); /* Create new context */
- if ( ctx == NULL ) {
- ERR_print_errors_fp(stderr);
- abort();
- }
- return ctx;
- }
-
- ssize_t send_from_file_ssl(SSL *ssl, char *path) {
- byte_t *buf = NULL;
- size_t isz = 0;
- ssize_t outBytes;
-
- buf = file_get_contents(path, &isz);
- outBytes = SSL_write(ssl, buf, isz);
- /* bcl_xxd(buf, isz); */
- free(buf);
- return outBytes;
- }
-
- int https_get_body(char *ip, char *in_path, char *out_path) {
- SSL_CTX *ctx = NULL;
- SSL *ssl;
- int bytes;
- #define HTTPS_BUFF_SIZE 4096
- char buf[HTTPS_BUFF_SIZE];
- int sockfd = -1;
- FILE *out = NULL;
-
- SSL_library_init();
- ctx = InitCTX();
- ssl = SSL_new(ctx); /* create new SSL connection state */
- sockfd = bcl_tcp_connector(ip2int(ip), 443);
- SSL_set_fd(ssl, sockfd); /* attach the socket descriptor */
- if ( SSL_connect(ssl) < 0 ) { /* perform the connection */
- ERR_print_errors_fp(stderr);
- return E_FAIL;
- }
- send_from_file_ssl(ssl, in_path);
- bytes = SSL_read(ssl, buf, HTTPS_BUFF_SIZE);
- /* Dump HTTP header */
- bcl_xxd(buf, bytes);
-
- out = fopen(out_path, "w");
- if (NULL == out) {
- bclerreg(E_OSCALL, _FL_, "fopen(%s)", out_path);
- return E_FAIL;
- }
- while (0<(bytes = SSL_read(ssl, buf, HTTPS_BUFF_SIZE))) {
- fwrite(buf, 1, bytes, out);
- memset(buf, 0, bytes);
- }
- fclose(out);
- bcl_closesock(sockfd);
-
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- return E_OK;
- }
先准备ssl需要的库, SSL_Libary_init(), 创建SSL_CTX *ctx对象
封装socket fd为SSL *, 操作SSL *取代 int fd。SSL_read替换recv, SSL_write替换send
close(fd)关闭socket fd。用完SSL_free(ssl), 释放https链接。销毁SSL_CTX *ctx对象
SSL_read第0次调用拿到http header,再调用得到的是http body。不需要手动解析http \r\n 0x0d 0x0a, 也省了很多麻烦。
- int bcl_tcp_connector(u_int32_t _addr, u_int16_t _port) {
- struct sockaddr_in saddr;
- int sock;
- int ret;
-
- for (;;) {
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock <0) {
- /* ... */
- return E_FAIL;
- }
- memset(&saddr, 0x00, sizeof (saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_addr.s_addr = _addr;
- saddr.sin_port = htons(_port);
-
- ret = connect(sock, (struct sockaddr *)&saddr, sizeof(saddr));
- if (ret >= 0) {
- return sock;
- }
- /* ERROR connect */
- if (errno == EINTR) {
- bcl_closesock(sock);
- continue;
- }
- char ip[16];
- bcl_int2ip(ip, _addr);
- bclerreg(E_OSCALL, _FL_, "Error connect %s:%d", ip, _port);
- return E_FAIL;
- }
- }
创建socket fd,调用connect,常规方法。
bcl_xxd函数hexdump调试用
- uint8_t arc_xxd(byte *s, uint8_t len, void *param);
-
- void bcl_xxd(const void *s, size_t len)
- {
- uint16_t line = 0;
- byte *t = (byte *)s;
- uint8_t b;
- size_t left = len;
-
- for (; ;) {
- if (left > 16) {
- b = arc_xxd(t, 16, &line);
- } else {
- b = arc_xxd(t, left, &line);
- }
- if (b < 1) {break;}
- left -= b;
- if (left <= 0) {break;}
- t += b;
- }
- }
-
- uint8_t arc_xxd(byte *s, uint8_t len, void *param)
- {
- uint8_t i;
- uint16_t *line = (uint16_t *)param;
- fprintf(stdout, "%08x:", *line << 4);
- for (i = 0; i+2 < len; i+=2) {
- fprintf(stdout, " %02x%02x", s[i], s[i+1]);
- }
- if (i
fprintf(stdout, " %02x", s[i]); i+= 1;} - if (i
fprintf(stdout, "%02x", s[i]); i+=1;} - if (i < 16 && 0x00 != (i & 0x01)) { fputs(" ", stdout); i+=1;}
- for (; i <16; i+= 2) {
- fputs(" ", stdout); /* space * 5 */
- }
- fputs(" ", stdout);
- for (i = 0; i
- if (s[i] <0x20 || s[i] > 0x7e) {
- fputc('.', stdout);
- } else {
- fputc(s[i], stdout);
- }
- }
- fputs("\r\n", stdout);
- *line += 1;
- return i;
- }
* bcl_udp.h
- #ifndef ERRLOG_BCL_UDP_H
- #define ERRLOG_BCL_UDP_H
-
- #include
-
- typedef union {
- unsigned char c[4];
- unsigned int addr;
- } IPv4_t;
-
- void IPv4_toString(char *s, IPv4_t ipv4);
- /* in_addr_t */ unsigned int ip2int(const char *ip);
-
-
- struct ipv4_entry {
- char data[16];
- TAILQ_ENTRY(ipv4_entry) entries;
- };
- TAILQ_HEAD(ipv4head, ipv4_entry);
-
- int bcl_dns_query(struct ipv4head *head, char *host_name, const char *dns_ip);
-
- #endif //ERRLOG_BCL_UDP_H
* bcl_udp.c
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- #include
/* bzero */ - #include
/* inet_addr */ - #include
/* close */ - #include
/* strcat, memset */ - #include
/* getenv(), rand() */ -
- #include "bcl_udp.h"
- #include "arclog.h"
- #include "bcl_socket.h"
-
- #define PRIVATE static
-
- unsigned int /* in_addr_t */ip2int(const char *ip) {
- IPv4_t un;
- int i, j;
- un.c[0] = un.c[1] = un.c[2] = un.c[3] = 0;
- for (i = 0, j = 0; i < 16 && ip[i] != '\0'; i++) {
- if (0x30 <= ip[i] && ip[i] < 0x3a) { /* '0','9' */
- un.c[j] *= 10;
- un.c[j] += ip[i] - 0x30;
- } else if (ip[i] == 0x2e) { /* '.' */
- j += 1;
- }
- }
- return un.addr;
- }
-
-
- typedef struct {
- u_short transId; /* 0xb6f6 */
- u_short flags; /* 0x0100 Standard query */
- u_short questions; /* 0x0001 */
- u_char rss[6]; /* 00 00 00 00 00 00 */
- } DnsHdr_t;
-
- #define HOST_LEN 64
-
- typedef struct {
- u_char name[2]; /* ptr */
- u_short type; /* 0x0005 alias */
- u_short in; /* 0x0001 */
- u_int32_t ttl; /* 81, 1min21s */
- u_short dataLen; /* 0x0017, 23 */
- char cname[HOST_LEN]; /* 3www31633com8163jiasu3com */
- } __attribute__((packed)) DnsAnswer_t;
-
- /**
- * @ref: https://www.linuxquestions.org/questions/linux-networking-3/dns-packet-structure-289886/
- * @param name "p3-sign.douyinpic.com"
- * @param entry "7p3-sign9douyinpic3com"
- * @return
- */
- PRIVATE char *dns_entry(char *entry, const char *name) {
- const char *p = name;
- char *q = entry;
- int8_t len;
-
- while ('\0' != *p) {
- len = 0;
- for (; *p && *p != '.'; p++) {
- len += 1;
- }
- *q++ = len;
- memcpy(q, p - len, len);
- q += len;
- p++;
- }
- return q;
- }
-
- #define SWAP(a, b, type) do { type _t = a; a = b; b = _t;} while(0);
-
- #define REVERSE(a, b, type) do { \
- type *p = a, *q = b; \
- type t; \
- if (p > q) { SWAP(p, q, type *);} \
- for (; p < q; p++, q--) { \
- t = *p; \
- *p = *q; \
- *q = t; \
- } \
- } while (0);
-
- void IPv4_toString(char *s, IPv4_t ipv4) {
- int i;
- char *t;
- #ifdef __ORDER_LITTLE_ENDIAN__
- for (i = 0; i < 4; i++) {
- #else
- for (i = 3; 0 <= i; i--) {
- #endif
- t = s;
- while (ipv4.c[i]) {
- *s++ = ipv4.c[i] % 10 + 0x30;
- ipv4.c[i] /= 10;
- }
- REVERSE(s-1, t, char);
- *s++ = '.';
- }
- *(s-1) = '\0';
- }
-
- int bcl_dns_query(struct ipv4head *head, char *host_name, const char *dns_ip) {
- int sockfd;
- char buf[1024] = {'\0'};
- struct sockaddr_in dest_addr;
- DnsHdr_t header;
- char *p = NULL;
- off_t queryLen;
- DnsAnswer_t ans;
- IPv4_t ipv4;
- struct ipv4_entry *n1 = NULL;
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0) {
- bclerreg(E_OSCALL, _FL_, "socket() UDP");
- return E_FAIL;
- }
- /* DNS address */
- memset(&dest_addr, 0, sizeof(struct sockaddr_in));
- dest_addr.sin_family = AF_INET;
- dest_addr.sin_addr.s_addr = ip2int(dns_ip);
- dest_addr.sin_port = htons(53); /* DNS port 53 */
- if (connect(sockfd, (const struct sockaddr *) &dest_addr, sizeof(dest_addr)) < 0) {
- bclerreg(E_OSCALL, _FL_, "connect()");
- return E_FAIL;
- }
- /* DNS query packet */
- header.transId = (u_short) rand();
- header.flags = htons(0x0100);
- header.questions = htons(0x0001);
- memset(header.rss, 0, sizeof(header.rss));
-
- memcpy(buf, &header, sizeof(header));
- p = buf + sizeof(header);
- /* queries: p3-sign.douyinpic.com */
- p = dns_entry(p, host_name);
- *p++ = '\0';
- /* type A, class IN */
- *p++ = 0x00; *p++ = 0x01;
- *p++ = 0x00; *p++ = 0x01;
-
- queryLen = p - buf;
- if (send(sockfd, buf, queryLen, 0) < 0) {
- bclerreg(E_OSCALL, _FL_, "send()");
- return E_FAIL;
- }
- memset(buf, 0, 1024);
- if (recv(sockfd, buf, 1024, 0) < 1) {
- bclerreg(E_OSCALL, _FL_, "recv()");
- return E_FAIL;
- }
- p = buf + queryLen;
- /* Answers: buf + queryLen */
- TAILQ_INIT(head);
- while (*p != 0x00) {
- /* bcl_xxd(p, 0xa0); */
- if (*(u_char *)p == 0xc0) {
- /* c0 0c, c0 14 */
- ans.dataLen = ntohs(((DnsAnswer_t *)p)->dataLen);
- }
- #if DEBUG
- printf("dataLen=%d\n", ans.dataLen);
- bcl_xxd(((DnsAnswer_t *)p)->cname, ans.dataLen);
- #endif
- if (4 == ans.dataLen) { /* IPv4 */
- memcpy(&ipv4, &((DnsAnswer_t *)p)->cname, 4);
- n1 = (struct ipv4_entry *)malloc(sizeof(struct ipv4_entry));
- IPv4_toString(n1->data, ipv4);
- TAILQ_INSERT_TAIL(head, n1, entries);
- }
- p += sizeof(DnsAnswer_t) - HOST_LEN + ans.dataLen;
- }
- return E_OK;
- }
-
- #ifdef __cplusplus
- };
- #endif
查询DNS以上
C:\Windows\system32\wsl.exe --distribution Ubuntu --exec /bin/bash -c "export ESWTDIR=/mnt/e/CLionProjects/arp && export FAPWORKDIR=/mnt/e/CLionProjects/arp/fap30 && cd /mnt/e/CLionProjects/arp/fap30 && /mnt/e/CLionProjects/arp/cmake-build-debug/arp_test ./input/banner.txt banner_3.jpg"
ip=61.164.142.245
ip=115.231.71.216
path_dst=/mnt/e/CLionProjects/arp/fap30/print/sale.jpg
00000000: 4854 5450 2f31 2e31 2032 3030 204f 4b0d HTTP/1.1 200 OK.
00000010: 0a4c 6173 742d 4d6f 6469 6669 6564 3a20 .Last-Modified:
00000020: 5375 6e2c 2032 3220 4f63 7420 3230 3233 Sun, 22 Oct 2023
00000030: 2030 383a 3036 3a30 3320 474d 540d 0a45 08:06:03 GMT..E
00000040: 7461 673a 2022 3336 3333 3661 6665 3533 tag: "36336afe53
00000050: 3039 6561 3933 6163 6633 3930 6162 3934 09ea93acf390ab94
00000060: 3637 6132 6634 220d 0a43 6f6e 7465 6e74 67a2f4"..Content
00000070: 2d54 7970 653a 2069 6d61 6765 2f6a 7065 -Type: image/jpe
00000080: 670d 0a44 6174 653a 2053 756e 2c20 3232 g..Date: Sun, 22
00000090: 204f 6374 2032 3032 3320 3038 3a30 363a Oct 2023 08:06:
000000a0: 3034 2047 4d54 0d0a 5365 7276 6572 3a20 04 GMT..Server:
000000b0: 7465 6e63 656e 742d 636f 730d 0a78 2d63 tencent-cos..x-c
000000c0: 6f73 2d68 6173 682d 6372 6336 3465 636d os-hash-crc64ecm
000000d0: 613a 2031 3534 3631 3131 3537 3934 3831 a: 1546111579481
000000e0: 3535 3537 3339 340d 0a78 2d63 6f73 2d72 5557394..x-cos-r
000000f0: 6571 7565 7374 2d69 643a 204e 6a55 7a4e equest-id: NjUzN
00000100: 4751 335a 574e 664e 5755 7a4e 6a51 774d GQ3ZWNfNWUzNjQwM
00000110: 474a 664d 5745 305a 4446 664f 5459 314f GJfMWE0ZDFfOTY1O
00000120: 4442 6b4e 673d 3d0d 0a43 6f6e 7465 6e74 DBkNg==..Content
00000130: 2d4c 656e 6774 683a 2032 3237 3938 0d0a -Length: 22798..
00000140: 4163 6365 7074 2d52 616e 6765 733a 2062 Accept-Ranges: b
00000150: 7974 6573 0d0a 582d 4e57 532d 4c4f 472d ytes..X-NWS-LOG-
00000160: 5555 4944 3a20 3735 3537 3138 3835 3632 UUID: 7557188562
00000170: 3130 3431 3837 3231 320d 0a43 6f6e 6e65 104187212..Conne
00000180: 6374 696f 6e3a 206b 6565 702d 616c 6976 ction: keep-aliv
00000190: 650d 0a58 2d43 6163 6865 2d4c 6f6f 6b75 e..X-Cache-Looku
000001a0: 703a 2043 6163 6865 2048 6974 0d0a 0d0a p: Cache Hit....
Process finished with exit code 0
查看下载 E:\CLionProjects\arp\fap30\print\sale.jpg

对于http响应body,有的返回是gzip压缩,要用zlib库解压。分段的内容要解析
- /** @ref: https://www.iteye.com/blog/dbscx-830644 */
- char *http_chunked_parse(char *s, u_int16_t *len) {
- int i;
- /* 0x1000 == 4096 */
- *len = 0;
- for (i = 0; i < 4; i++) {
- if (s[i] == 0x0d && s[i+1] == 0x0a) {
- break;
- }
- /* 32 63 31 0d 0a <=> "3c1\r\n"(LEN=0x2c1, Dec:705) */
- *len *= 16;
- if (0x30<=s[i] && s[i] < 0x40) {
- *len += s[i] - 0x30;
- } else if (0x61 <= s[i] && s[i] < 0x67) {
- *len += s[i] - 0x61 + 10;
- }
- }
- if (s[i] == 0x0d && s[i+1] == 0x0a) {
- s = &s[i] + 2;
- }
- return s;
- }
0d 0a 30 0d 0a 0d 0a 结束