initial commit

This commit is contained in:
2025-08-05 15:53:44 +08:00
commit 09dc02ae52
553 changed files with 137665 additions and 0 deletions

10
third_party/libhv/protocol/README.md vendored Executable file
View File

@@ -0,0 +1,10 @@
## 目录结构
```
.
├── dns.h DNS协议
├── ftp.h FTP协议
├── icmp.h ICMP协议
└── smtp.h SMTP协议
```

336
third_party/libhv/protocol/dns.c vendored Executable file
View File

@@ -0,0 +1,336 @@
#include "dns.h"
#include "hdef.h"
#include "hsocket.h"
#include "herr.h"
void dns_free(dns_t* dns) {
SAFE_FREE(dns->questions);
SAFE_FREE(dns->answers);
SAFE_FREE(dns->authorities);
SAFE_FREE(dns->addtionals);
}
// www.example.com => 3www7example3com
int dns_name_encode(const char* domain, char* buf) {
const char* p = domain;
char* plen = buf++;
int buflen = 1;
int len = 0;
while (*p != '\0') {
if (*p != '.') {
++len;
*buf = *p;
}
else {
*plen = len;
//printf("len=%d\n", len);
plen = buf;
len = 0;
}
++p;
++buf;
++buflen;
}
*plen = len;
//printf("len=%d\n", len);
*buf = '\0';
if (len != 0) {
++buflen; // include last '\0'
}
return buflen;
}
// 3www7example3com => www.example.com
int dns_name_decode(const char* buf, char* domain) {
const char* p = buf;
int len = *p++;
//printf("len=%d\n", len);
int buflen = 1;
while (*p != '\0') {
if (len-- == 0) {
len = *p;
//printf("len=%d\n", len);
*domain = '.';
}
else {
*domain = *p;
}
++p;
++domain;
++buflen;
}
*domain = '\0';
++buflen; // include last '\0'
return buflen;
}
int dns_rr_pack(dns_rr_t* rr, char* buf, int len) {
char* p = buf;
char encoded_name[256];
int encoded_namelen = dns_name_encode(rr->name, encoded_name);
int packetlen = encoded_namelen + 2 + 2 + (rr->data ? (4+2+rr->datalen) : 0);
if (len < packetlen) {
return -1;
}
memcpy(p, encoded_name, encoded_namelen);
p += encoded_namelen;
uint16_t* pushort = (uint16_t*)p;
*pushort = htons(rr->rtype);
p += 2;
pushort = (uint16_t*)p;
*pushort = htons(rr->rclass);
p += 2;
// ...
if (rr->datalen && rr->data) {
uint32_t* puint = (uint32_t*)p;
*puint = htonl(rr->ttl);
p += 4;
pushort = (uint16_t*)p;
*pushort = htons(rr->datalen);
p += 2;
memcpy(p, rr->data, rr->datalen);
p += rr->datalen;
}
return packetlen;
}
int dns_rr_unpack(char* buf, int len, dns_rr_t* rr, int is_question) {
char* p = buf;
int off = 0;
int namelen = 0;
if (*(uint8_t*)p >= 192) {
// name off, we ignore
namelen = 2;
//uint16_t nameoff = (*(uint8_t*)p - 192) * 256 + *(uint8_t*)(p+1);
}
else {
namelen = dns_name_decode(buf, rr->name);
}
if (namelen < 0) return -1;
p += namelen;
off += namelen;
if (len < off + 4) return -1;
uint16_t* pushort = (uint16_t*)p;
rr->rtype = ntohs(*pushort);
p += 2;
pushort = (uint16_t*)p;
rr->rclass = ntohs(*pushort);
p += 2;
off += 4;
if (!is_question) {
if (len < off + 6) return -1;
uint32_t* puint = (uint32_t*)p;
rr->ttl = ntohl(*puint);
p += 4;
pushort = (uint16_t*)p;
rr->datalen = ntohs(*pushort);
p += 2;
off += 6;
if (len < off + rr->datalen) return -1;
rr->data = p;
p += rr->datalen;
off += rr->datalen;
}
return off;
}
int dns_pack(dns_t* dns, char* buf, int len) {
if (len < sizeof(dnshdr_t)) return -1;
int off = 0;
dnshdr_t* hdr = &dns->hdr;
dnshdr_t htonhdr = dns->hdr;
htonhdr.transaction_id = htons(hdr->transaction_id);
htonhdr.nquestion = htons(hdr->nquestion);
htonhdr.nanswer = htons(hdr->nanswer);
htonhdr.nauthority = htons(hdr->nauthority);
htonhdr.naddtional = htons(hdr->naddtional);
memcpy(buf, &htonhdr, sizeof(dnshdr_t));
off += sizeof(dnshdr_t);
int i;
for (i = 0; i < hdr->nquestion; ++i) {
int packetlen = dns_rr_pack(dns->questions+i, buf+off, len-off);
if (packetlen < 0) return -1;
off += packetlen;
}
for (i = 0; i < hdr->nanswer; ++i) {
int packetlen = dns_rr_pack(dns->answers+i, buf+off, len-off);
if (packetlen < 0) return -1;
off += packetlen;
}
for (i = 0; i < hdr->nauthority; ++i) {
int packetlen = dns_rr_pack(dns->authorities+i, buf+off, len-off);
if (packetlen < 0) return -1;
off += packetlen;
}
for (i = 0; i < hdr->naddtional; ++i) {
int packetlen = dns_rr_pack(dns->addtionals+i, buf+off, len-off);
if (packetlen < 0) return -1;
off += packetlen;
}
return off;
}
int dns_unpack(char* buf, int len, dns_t* dns) {
memset(dns, 0, sizeof(dns_t));
if (len < sizeof(dnshdr_t)) return -1;
int off = 0;
dnshdr_t* hdr = &dns->hdr;
memcpy(hdr, buf, sizeof(dnshdr_t));
off += sizeof(dnshdr_t);
hdr->transaction_id = ntohs(hdr->transaction_id);
hdr->nquestion = ntohs(hdr->nquestion);
hdr->nanswer = ntohs(hdr->nanswer);
hdr->nauthority = ntohs(hdr->nauthority);
hdr->naddtional = ntohs(hdr->naddtional);
int i;
if (hdr->nquestion) {
int bytes = hdr->nquestion * sizeof(dns_rr_t);
SAFE_ALLOC(dns->questions, bytes);
for (i = 0; i < hdr->nquestion; ++i) {
int packetlen = dns_rr_unpack(buf+off, len-off, dns->questions+i, 1);
if (packetlen < 0) return -1;
off += packetlen;
}
}
if (hdr->nanswer) {
int bytes = hdr->nanswer * sizeof(dns_rr_t);
SAFE_ALLOC(dns->answers, bytes);
for (i = 0; i < hdr->nanswer; ++i) {
int packetlen = dns_rr_unpack(buf+off, len-off, dns->answers+i, 0);
if (packetlen < 0) return -1;
off += packetlen;
}
}
if (hdr->nauthority) {
int bytes = hdr->nauthority * sizeof(dns_rr_t);
SAFE_ALLOC(dns->authorities, bytes);
for (i = 0; i < hdr->nauthority; ++i) {
int packetlen = dns_rr_unpack(buf+off, len-off, dns->authorities+i, 0);
if (packetlen < 0) return -1;
off += packetlen;
}
}
if (hdr->naddtional) {
int bytes = hdr->naddtional * sizeof(dns_rr_t);
SAFE_ALLOC(dns->addtionals, bytes);
for (i = 0; i < hdr->naddtional; ++i) {
int packetlen = dns_rr_unpack(buf+off, len-off, dns->addtionals+i, 0);
if (packetlen < 0) return -1;
off += packetlen;
}
}
return off;
}
// dns_pack -> sendto -> recvfrom -> dns_unpack
int dns_query(dns_t* query, dns_t* response, const char* nameserver) {
char buf[1024];
int buflen = sizeof(buf);
buflen = dns_pack(query, buf, buflen);
if (buflen < 0) {
return buflen;
}
#ifdef OS_WIN
WSAInit();
#endif
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return ERR_SOCKET;
}
so_sndtimeo(sockfd, 5000);
so_rcvtimeo(sockfd, 5000);
int ret = 0;
int nsend, nrecv;
int nparse;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
memset(&addr, 0, addrlen);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(nameserver);
addr.sin_port = htons(DNS_PORT);
nsend = sendto(sockfd, buf, buflen, 0, (struct sockaddr*)&addr, addrlen);
if (nsend != buflen) {
ret = ERR_SENDTO;
goto error;
}
nrecv = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &addrlen);
if (nrecv <= 0) {
ret = ERR_RECVFROM;
goto error;
}
nparse = dns_unpack(buf, nrecv, response);
if (nparse != nrecv) {
ret = -ERR_INVALID_PACKAGE;
goto error;
}
error:
if (sockfd != INVALID_SOCKET) {
closesocket(sockfd);
}
return ret;
}
int nslookup(const char* domain, uint32_t* addrs, int naddr, const char* nameserver) {
dns_t query;
memset(&query, 0, sizeof(query));
query.hdr.transaction_id = getpid();
query.hdr.qr = DNS_QUERY;
query.hdr.rd = 1;
query.hdr.nquestion = 1;
dns_rr_t question;
memset(&question, 0, sizeof(question));
strncpy(question.name, domain, sizeof(question.name));
question.rtype = DNS_TYPE_A;
question.rclass = DNS_CLASS_IN;
query.questions = &question;
dns_t resp;
memset(&resp, 0, sizeof(resp));
int ret = dns_query(&query, &resp, nameserver);
if (ret != 0) {
return ret;
}
dns_rr_t* rr = resp.answers;
int addr_cnt = 0;
if (resp.hdr.transaction_id != query.hdr.transaction_id ||
resp.hdr.qr != DNS_RESPONSE ||
resp.hdr.rcode != 0) {
ret = -ERR_MISMATCH;
goto end;
}
if (resp.hdr.nanswer == 0) {
ret = 0;
goto end;
}
for (int i = 0; i < resp.hdr.nanswer; ++i, ++rr) {
if (rr->rtype == DNS_TYPE_A) {
if (addr_cnt < naddr && rr->datalen == 4) {
memcpy(addrs+addr_cnt, rr->data, 4);
}
++addr_cnt;
}
/*
else if (rr->rtype == DNS_TYPE_CNAME) {
char name[256];
dns_name_decode(rr->data, name);
}
*/
}
ret = addr_cnt;
end:
dns_free(&resp);
return ret;
}

105
third_party/libhv/protocol/dns.h vendored Executable file
View File

@@ -0,0 +1,105 @@
#ifndef HV_DNS_H_
#define HV_DNS_H_
#include "hexport.h"
#include "hplatform.h"
#define DNS_PORT 53
#define DNS_QUERY 0
#define DNS_RESPONSE 1
#define DNS_TYPE_A 1 // ipv4
#define DNS_TYPE_NS 2
#define DNS_TYPE_CNAME 5
#define DNS_TYPE_SOA 6
#define DNS_TYPE_WKS 11
#define DNS_TYPE_PTR 12
#define DNS_TYPE_HINFO 13
#define DNS_TYPE_MX 15
#define DNS_TYPE_AAAA 28 // ipv6
#define DNS_TYPE_AXFR 252
#define DNS_TYPE_ANY 255
#define DNS_CLASS_IN 1
#define DNS_NAME_MAXLEN 256
// sizeof(dnshdr_t) = 12
typedef struct dnshdr_s {
uint16_t transaction_id;
// flags
#if BYTE_ORDER == LITTLE_ENDIAN
uint8_t rd:1;
uint8_t tc:1;
uint8_t aa:1;
uint8_t opcode:4;
uint8_t qr:1;
uint8_t rcode:4;
uint8_t cd:1;
uint8_t ad:1;
uint8_t res:1;
uint8_t ra:1;
#elif BYTE_ORDER == BIG_ENDIAN
uint8_t qr:1; // DNS_QUERY or DNS_RESPONSE
uint8_t opcode:4;
uint8_t aa:1; // authoritative
uint8_t tc:1; // truncated
uint8_t rd:1; // recursion desired
uint8_t ra:1; // recursion available
uint8_t res:1; // reserved
uint8_t ad:1; // authenticated data
uint8_t cd:1; // checking disable
uint8_t rcode:4;
#else
#error "BYTE_ORDER undefined!"
#endif
uint16_t nquestion;
uint16_t nanswer;
uint16_t nauthority;
uint16_t naddtional;
} dnshdr_t;
typedef struct dns_rr_s {
char name[DNS_NAME_MAXLEN]; // original domain, such as www.example.com
uint16_t rtype;
uint16_t rclass;
uint32_t ttl;
uint16_t datalen;
char* data;
} dns_rr_t;
typedef struct dns_s {
dnshdr_t hdr;
dns_rr_t* questions;
dns_rr_t* answers;
dns_rr_t* authorities;
dns_rr_t* addtionals;
} dns_t;
BEGIN_EXTERN_C
// www.example.com => 3www7example3com
HV_EXPORT int dns_name_encode(const char* domain, char* buf);
// 3www7example3com => www.example.com
HV_EXPORT int dns_name_decode(const char* buf, char* domain);
HV_EXPORT int dns_rr_pack(dns_rr_t* rr, char* buf, int len);
HV_EXPORT int dns_rr_unpack(char* buf, int len, dns_rr_t* rr, int is_question);
HV_EXPORT int dns_pack(dns_t* dns, char* buf, int len);
HV_EXPORT int dns_unpack(char* buf, int len, dns_t* dns);
// NOTE: free dns->rrs
HV_EXPORT void dns_free(dns_t* dns);
// dns_pack -> sendto -> recvfrom -> dns_unpack
HV_EXPORT int dns_query(dns_t* query, dns_t* response, const char* nameserver DEFAULT("127.0.1.1"));
// domain -> dns_t query; -> dns_query -> dns_t response; -> addrs
HV_EXPORT int nslookup(const char* domain, uint32_t* addrs, int naddr, const char* nameserver DEFAULT("127.0.1.1"));
END_EXTERN_C
#endif // HV_DNS_H_

247
third_party/libhv/protocol/ftp.c vendored Executable file
View File

@@ -0,0 +1,247 @@
#include "ftp.h"
#include "hsocket.h"
#include "herr.h"
const char* ftp_command_str(enum ftp_command cmd) {
switch (cmd) {
#define X(name) case FTP_##name: return #name;
FTP_COMMAND_MAP(X)
#undef X
default: return "<unknown>";
}
}
const char* ftp_status_str(enum ftp_status status) {
switch (status) {
#define XXX(code, name, string) case FTP_STATUS_##name: return #string;
FTP_STATUS_MAP(XXX)
#undef XXX
default: return "<unknown>";
}
}
int ftp_connect(ftp_handle_t* hftp, const char* host, int port) {
int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
if (sockfd < 0) {
return sockfd;
}
so_sndtimeo(sockfd, 5000);
so_rcvtimeo(sockfd, 5000);
hftp->sockfd = sockfd;
int ret = 0;
int status_code = 0;
memset(hftp->recvbuf, 0, FTP_RECV_BUFSIZE);
int nrecv = recv(sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
status_code = atoi(hftp->recvbuf);
if (status_code != FTP_STATUS_READY) {
ret = status_code;
goto error;
}
return 0;
error:
closesocket(sockfd);
return ret;
}
int ftp_login(ftp_handle_t* hftp, const char* username, const char* password) {
int status_code = ftp_exec(hftp, "USER", username);
status_code = ftp_exec(hftp, "PASS", password);
return status_code == FTP_STATUS_LOGIN_OK ? 0 : status_code;
}
int ftp_quit(ftp_handle_t* hftp) {
ftp_exec(hftp, "QUIT", NULL);
closesocket(hftp->sockfd);
return 0;
}
int ftp_exec(ftp_handle_t* hftp, const char* cmd, const char* param) {
char buf[1024];
int len = 0;
if (param && *param) {
len = snprintf(buf, sizeof(buf), "%s %s\r\n", cmd, param);
}
else {
len = snprintf(buf, sizeof(buf), "%s\r\n", cmd);
}
int nsend, nrecv;
int ret = 0;
nsend = send(hftp->sockfd, buf, len, 0);
if (nsend != len) {
ret = ERR_SEND;
goto error;
}
//printf("> %s", buf);
memset(hftp->recvbuf, 0, FTP_RECV_BUFSIZE);
nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
//printf("< %s", hftp->recvbuf);
return atoi(hftp->recvbuf);
error:
closesocket(hftp->sockfd);
return ret;
}
static int ftp_parse_pasv(const char* resp, char* host, int* port) {
// 227 Entering Passive Mode (127,0,0,1,4,51)
const char* str = strchr(resp, '(');
if (str == NULL) {
return ERR_RESPONSE;
}
int arr[6];
sscanf(str, "(%d,%d,%d,%d,%d,%d)",
&arr[0], &arr[1], &arr[2], &arr[3], &arr[4], &arr[5]);
sprintf(host, "%d.%d.%d.%d", arr[0], arr[1], arr[2], arr[3]);
*port = arr[4] << 8 | arr[5];
return 0;
}
int ftp_download_with_cb(ftp_handle_t* hftp, const char* filepath, ftp_download_cb cb) {
int status_code = ftp_exec(hftp, "PASV", NULL);
if (status_code != FTP_STATUS_PASV) {
return status_code;
}
char host[64];
int port = 0;
int ret = ftp_parse_pasv(hftp->recvbuf, host, &port);
if (ret != 0) {
return ret;
}
//ftp_exec(hftp, "RETR", filepath);
char request[1024];
int len = snprintf(request, sizeof(request), "RETR %s\r\n", filepath);
int nsend = send(hftp->sockfd, request, len, 0);
if (nsend != len) {
closesocket(hftp->sockfd);
return ERR_SEND;
}
//printf("> %s", request);
int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
if (sockfd < 0) {
return sockfd;
}
int nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
if (nrecv <= 0) {
closesocket(hftp->sockfd);
return ERR_RECV;
}
//printf("< %s", hftp->recvbuf);
{
// you can create thread to recv data
char recvbuf[1024];
int ntotal = 0;
while (1) {
nrecv = recv(sockfd, recvbuf, sizeof(recvbuf), 0);
if (cb) {
cb(hftp, recvbuf, nrecv);
}
if (nrecv <= 0) break;
ntotal += nrecv;
}
}
closesocket(sockfd);
nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
if (nrecv <= 0) {
closesocket(hftp->sockfd);
return ERR_RECV;
}
//printf("< %s", hftp->recvbuf);
status_code = atoi(hftp->recvbuf);
return status_code == FTP_STATUS_TRANSFER_COMPLETE ? 0 : status_code;
}
// local => remote
int ftp_upload(ftp_handle_t* hftp, const char* local_filepath, const char* remote_filepath) {
int status_code = ftp_exec(hftp, "PASV", NULL);
if (status_code != FTP_STATUS_PASV) {
return status_code;
}
char host[64];
int port = 0;
int ret = ftp_parse_pasv(hftp->recvbuf, host, &port);
if (ret != 0) {
return ret;
}
//ftp_exec(hftp, "STOR", remote_filepath);
char request[1024];
int len = snprintf(request, sizeof(request), "STOR %s\r\n", remote_filepath);
int nsend = send(hftp->sockfd, request, len, 0);
if (nsend != len) {
closesocket(hftp->sockfd);
return ERR_SEND;
}
//printf("> %s", request);
int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
if (sockfd < 0) {
return sockfd;
}
int nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
if (nrecv <= 0) {
closesocket(hftp->sockfd);
return ERR_RECV;
}
//printf("< %s", hftp->recvbuf);
{
// you can create thread to send data
FILE* fp = fopen(local_filepath, "rb");
if (fp == NULL) {
closesocket(sockfd);
return ERR_OPEN_FILE;
}
char sendbuf[1024];
int nread, nsend;
int ntotal = 0;
while (1) {
nread = fread(sendbuf, 1, sizeof(sendbuf), fp);
if (nread == 0) break;
nsend = send(sockfd, sendbuf, nread, 0);
if (nsend != nread) break;
ntotal += nsend;
}
fclose(fp);
}
closesocket(sockfd);
nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
if (nrecv <= 0) {
closesocket(hftp->sockfd);
return ERR_RECV;
}
//printf("< %s", hftp->recvbuf);
status_code = atoi(hftp->recvbuf);
return status_code == FTP_STATUS_TRANSFER_COMPLETE ? 0 : status_code;
}
static int s_ftp_download_cb(ftp_handle_t* hftp, char* buf, int len) {
FILE* fp = (FILE*)hftp->userdata;
if (fp == NULL) return -1;
if (len <= 0) {
fclose(fp);
hftp->userdata = NULL;
return 0;
}
return fwrite(buf, 1, len, fp);
}
// remote => local
int ftp_download(ftp_handle_t* hftp, const char* remote_filepath, const char* local_filepath) {
FILE* fp = fopen(local_filepath, "wb");
if (fp == NULL) {
return ERR_OPEN_FILE;
}
hftp->userdata = (void*)fp;
int ret = ftp_download_with_cb(hftp, remote_filepath, s_ftp_download_cb);
// ensure fclose
if (hftp->userdata != NULL) {
fclose(fp);
hftp->userdata = NULL;
}
return ret;
}

96
third_party/libhv/protocol/ftp.h vendored Executable file
View File

@@ -0,0 +1,96 @@
#ifndef HV_FTP_H_
#define HV_FTP_H_
#include "hexport.h"
#define FTP_COMMAND_PORT 21
#define FTP_DATA_PORT 20
// ftp_command
// X(name)
#define FTP_COMMAND_MAP(X) \
X(HELP) \
X(USER) \
X(PASS) \
X(PWD) \
X(CWD) \
X(CDUP) \
X(MKD) \
X(RMD) \
X(STAT) \
X(SIZE) \
X(DELE) \
X(RNFR) \
X(RNTO) \
X(PORT) \
X(PASV) \
X(LIST) \
X(NLST) \
X(APPE) \
X(RETR) \
X(STOR) \
X(QUIT) \
enum ftp_command {
#define X(name) FTP_##name,
FTP_COMMAND_MAP(X)
#undef X
};
// ftp_status
// XXX(code, name, string)
#define FTP_STATUS_MAP(XXX) \
XXX(220, READY, Ready) \
XXX(221, BYE, Bye) \
XXX(226, TRANSFER_COMPLETE, Transfer complete) \
XXX(227, PASV, Entering Passive Mode) \
XXX(331, PASS, Password required) \
XXX(230, LOGIN_OK, Login OK) \
XXX(250, OK, OK) \
XXX(500, BAD_SYNTAX, Bad syntax) \
XXX(530, NOT_LOGIN, Not login) \
enum ftp_status {
#define XXX(code, name, string) FTP_STATUS_##name = code,
FTP_STATUS_MAP(XXX)
#undef XXX
};
// more friendly macros
#define FTP_MKDIR FTP_MKD
#define FTP_RMDIR FTP_RMD
#define FTP_APPEND FTP_APPE
#define FTP_REMOVE FTP_DELE
#define FTP_DOWNLOAD FTP_RETR
#define FTP_UPLOAD FTP_STOR
#define FTP_RECV_BUFSIZE 8192
typedef struct ftp_handle_s {
int sockfd;
char recvbuf[FTP_RECV_BUFSIZE];
void* userdata;
} ftp_handle_t;
BEGIN_EXTERN_C
HV_EXPORT const char* ftp_command_str(enum ftp_command cmd);
HV_EXPORT const char* ftp_status_str(enum ftp_status status);
HV_EXPORT int ftp_connect(ftp_handle_t* hftp, const char* host, int port);
HV_EXPORT int ftp_login(ftp_handle_t* hftp, const char* username, const char* password);
HV_EXPORT int ftp_quit(ftp_handle_t* hftp);
HV_EXPORT int ftp_exec(ftp_handle_t* hftp, const char* cmd, const char* param);
// local => remote
HV_EXPORT int ftp_upload(ftp_handle_t* hftp, const char* local_filepath, const char* remote_filepath);
// remote => local
HV_EXPORT int ftp_download(ftp_handle_t* hftp, const char* remote_filepath, const char* local_filepath);
typedef int (*ftp_download_cb)(ftp_handle_t* hftp, char* buf, int len);
HV_EXPORT int ftp_download_with_cb(ftp_handle_t* hftp, const char* filepath, ftp_download_cb cb);
END_EXTERN_C
#endif // HV_FTP_H_

125
third_party/libhv/protocol/icmp.c vendored Executable file
View File

@@ -0,0 +1,125 @@
#include "icmp.h"
#include "netinet.h"
#include "hdef.h"
#include "hsocket.h"
#include "htime.h"
#define PING_TIMEOUT 1000 // ms
int ping(const char* host, int cnt) {
static uint16_t seq = 0;
uint16_t pid16 = (uint16_t)getpid();
char ip[64] = {0};
uint32_t start_tick, end_tick;
uint64_t start_hrtime, end_hrtime;
int timeout = 0;
int sendbytes = 64;
char sendbuf[64];
char recvbuf[128]; // iphdr + icmp = 84 at least
icmp_t* icmp_req = (icmp_t*)sendbuf;
iphdr_t* ipheader = (iphdr_t*)recvbuf;
icmp_t* icmp_res;
// ping stat
int send_cnt = 0;
int recv_cnt = 0;
int ok_cnt = 0;
float rtt, min_rtt, max_rtt, total_rtt;
rtt = max_rtt = total_rtt = 0.0f;
min_rtt = 1000000.0f;
//min_rtt = MIN(rtt, min_rtt);
//max_rtt = MAX(rtt, max_rtt);
// gethostbyname -> socket -> setsockopt -> sendto -> recvfrom -> closesocket
sockaddr_u peeraddr;
socklen_t addrlen = sizeof(peeraddr);
memset(&peeraddr, 0, addrlen);
int ret = ResolveAddr(host, &peeraddr);
if (ret != 0) return ret;
sockaddr_ip(&peeraddr, ip, sizeof(ip));
int sockfd = socket(peeraddr.sa.sa_family, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0) {
perror("socket");
if (errno == EPERM) {
fprintf(stderr, "please use root or sudo to create a raw socket.\n");
}
return -socket_errno();
}
timeout = PING_TIMEOUT;
ret = so_sndtimeo(sockfd, timeout);
if (ret < 0) {
perror("setsockopt");
goto error;
}
timeout = PING_TIMEOUT;
ret = so_rcvtimeo(sockfd, timeout);
if (ret < 0) {
perror("setsockopt");
goto error;
}
icmp_req->icmp_type = ICMP_ECHO;
icmp_req->icmp_code = 0;
icmp_req->icmp_id = pid16;
for (int i = 0; i < sendbytes - sizeof(icmphdr_t); ++i) {
icmp_req->icmp_data[i] = i;
}
start_tick = gettick_ms();
while (cnt-- > 0) {
// NOTE: checksum
icmp_req->icmp_seq = ++seq;
icmp_req->icmp_cksum = 0;
icmp_req->icmp_cksum = checksum((uint8_t*)icmp_req, sendbytes);
start_hrtime = gethrtime_us();
addrlen = sockaddr_len(&peeraddr);
int nsend = sendto(sockfd, sendbuf, sendbytes, 0, &peeraddr.sa, addrlen);
if (nsend < 0) {
perror("sendto");
continue;
}
++send_cnt;
addrlen = sizeof(peeraddr);
int nrecv = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, &peeraddr.sa, &addrlen);
if (nrecv < 0) {
perror("recvfrom");
continue;
}
++recv_cnt;
end_hrtime = gethrtime_us();
// check valid
bool valid = false;
int iphdr_len = ipheader->ihl * 4;
int icmp_len = nrecv - iphdr_len;
if (icmp_len == sendbytes) {
icmp_res = (icmp_t*)(recvbuf + ipheader->ihl*4);
if (icmp_res->icmp_type == ICMP_ECHOREPLY &&
icmp_res->icmp_id == pid16 &&
icmp_res->icmp_seq == seq) {
valid = true;
}
}
if (valid == false) {
printd("recv invalid icmp packet!\n");
continue;
}
rtt = (end_hrtime-start_hrtime) / 1000.0f;
min_rtt = MIN(rtt, min_rtt);
max_rtt = MAX(rtt, max_rtt);
total_rtt += rtt;
printd("%d bytes from %s: icmp_seq=%u ttl=%u time=%.1f ms\n", icmp_len, ip, seq, ipheader->ttl, rtt);
fflush(stdout);
++ok_cnt;
if (cnt > 0) hv_sleep(1); // sleep a while, then agian
}
end_tick = gettick_ms();
printd("--- %s ping statistics ---\n", host);
printd("%d packets transmitted, %d received, %d%% packet loss, time %d ms\n",
send_cnt, recv_cnt, (send_cnt-recv_cnt)*100/(send_cnt==0?1:send_cnt), end_tick-start_tick);
printd("rtt min/avg/max = %.3f/%.3f/%.3f ms\n",
min_rtt, total_rtt/(ok_cnt==0?1:ok_cnt), max_rtt);
closesocket(sockfd);
return ok_cnt;
error:
closesocket(sockfd);
return socket_errno() > 0 ? -socket_errno() : -1;
}

15
third_party/libhv/protocol/icmp.h vendored Executable file
View File

@@ -0,0 +1,15 @@
#ifndef HV_ICMP_H_
#define HV_ICMP_H_
#include "hexport.h"
BEGIN_EXTERN_C
// @param cnt: ping count
// @return: ok count
// @note: printd $CC -DPRINT_DEBUG
HV_EXPORT int ping(const char* host, int cnt DEFAULT(4));
END_EXTERN_C
#endif // HV_ICMP_H_

256
third_party/libhv/protocol/smtp.c vendored Executable file
View File

@@ -0,0 +1,256 @@
#include "smtp.h"
#include "hsocket.h"
#include "herr.h"
#include "base64.h"
const char* smtp_command_str(enum smtp_command cmd) {
switch (cmd) {
#define XX(name, string) case SMTP_##name: return #string;
SMTP_COMMAND_MAP(XX)
#undef XX
default: return "<unknown>";
}
}
const char* smtp_status_str(enum smtp_status status) {
switch (status) {
#define XXX(code, name, string) case SMTP_STATUS_##name: return #string;
SMTP_STATUS_MAP(XXX)
#undef XXX
default: return "<unknown>";
}
}
int smtp_build_command(enum smtp_command cmd, const char* param, char* buf, int buflen) {
switch (cmd) {
// unary
case SMTP_DATA:
case SMTP_QUIT:
return snprintf(buf, buflen, "%s\r\n", smtp_command_str(cmd));
// <address>
case SMTP_MAIL:
case SMTP_RCPT:
return snprintf(buf, buflen, "%s <%s>\r\n", smtp_command_str(cmd), param);
default:
return snprintf(buf, buflen, "%s %s\r\n", smtp_command_str(cmd), param);
}
}
// EHLO => AUTH PLAIN => MAIL => RCPT => DATA => data => EOB => QUIT
int sendmail(const char* smtp_server,
const char* username,
const char* password,
mail_t* mail) {
char buf[1024] = {0};
int buflen = sizeof(buf);
int cmdlen = 0;
int status_code = 0;
char basic[256];
int basiclen;
int sockfd = ConnectTimeout(smtp_server, SMTP_PORT, DEFAULT_CONNECT_TIMEOUT);
if (sockfd < 0) {
return sockfd;
}
so_sndtimeo(sockfd, 5000);
so_rcvtimeo(sockfd, 5000);
int ret, nsend, nrecv;
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
status_code = atoi(buf);
if (status_code != SMTP_STATUS_READY) {
ret = status_code;
goto error;
}
// EHLO smtp.xxx.com\r\n
cmdlen = smtp_build_command(SMTP_EHLO, smtp_server, buf, buflen);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
status_code = atoi(buf);
if (status_code != SMTP_STATUS_OK) {
ret = status_code;
goto error;
}
// AUTH PLAIN\r\n
cmdlen = smtp_build_command(SMTP_AUTH, "PLAIN", buf, buflen);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
status_code = atoi(buf);
if (status_code != SMTP_STATUS_AUTH) {
ret = status_code;
goto error;
}
{
// BASE64 \0username\0password
int usernamelen = strlen(username);
int passwordlen = strlen(password);
basic[0] = '\0';
memcpy(basic+1, username, usernamelen);
basic[1+usernamelen] = '\0';
memcpy(basic+1+usernamelen+1, password, passwordlen);
basiclen = 1 + usernamelen + 1 + passwordlen;
}
hv_base64_encode((unsigned char*)basic, basiclen, buf);
cmdlen = BASE64_ENCODE_OUT_SIZE(basiclen);
buf[cmdlen] = '\r';
buf[cmdlen+1] = '\n';
cmdlen += 2;
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
status_code = atoi(buf);
if (status_code != SMTP_STATUS_AUTH_SUCCESS) {
ret = status_code;
goto error;
}
// MAIL FROM: <from>\r\n
cmdlen = smtp_build_command(SMTP_MAIL, mail->from, buf, buflen);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
status_code = atoi(buf);
if (status_code != SMTP_STATUS_OK) {
ret = status_code;
goto error;
}
// RCPT TO: <to>\r\n
cmdlen = smtp_build_command(SMTP_RCPT, mail->to, buf, buflen);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
status_code = atoi(buf);
if (status_code != SMTP_STATUS_OK) {
ret = status_code;
goto error;
}
// DATA\r\n
cmdlen = smtp_build_command(SMTP_DATA, NULL, buf, buflen);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
status_code = atoi(buf);
// SMTP_STATUS_DATA
if (status_code >= 400) {
ret = status_code;
goto error;
}
// From:
cmdlen = snprintf(buf, buflen, "From:%s\r\n", mail->from);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
// To:
cmdlen = snprintf(buf, buflen, "To:%s\r\n", mail->to);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
// Subject:
cmdlen = snprintf(buf, buflen, "Subject:%s\r\n\r\n", mail->subject);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
// body
cmdlen = strlen(mail->body);
nsend = send(sockfd, mail->body, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
// EOB
nsend = send(sockfd, SMTP_EOB, SMTP_EOB_LEN, 0);
if (nsend != SMTP_EOB_LEN) {
ret = ERR_SEND;
goto error;
}
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_SEND;
goto error;
}
status_code = atoi(buf);
if (status_code != SMTP_STATUS_OK) {
ret = status_code;
goto error;
}
// QUIT\r\n
cmdlen = smtp_build_command(SMTP_QUIT, NULL, buf, buflen);
nsend = send(sockfd, buf, cmdlen, 0);
if (nsend != cmdlen) {
ret = ERR_SEND;
goto error;
}
nrecv = recv(sockfd, buf, buflen, 0);
if (nrecv <= 0) {
ret = ERR_RECV;
goto error;
}
/*
status_code = atoi(buf);
if (status_code != SMTP_STATUS_BYE) {
ret = status_code;
goto error;
}
*/
ret = SMTP_STATUS_OK;
error:
if (sockfd != INVALID_SOCKET) {
closesocket(sockfd);
}
return ret;
}

74
third_party/libhv/protocol/smtp.h vendored Executable file
View File

@@ -0,0 +1,74 @@
#ifndef HV_SMTP_H_
#define HV_SMTP_H_
#include "hexport.h"
#define SMTP_PORT 25
#define SMTPS_PORT 465
#define SMTP_EOB "\r\n.\r\n"
#define SMTP_EOB_LEN 5
// smtp_command
// XX(name, string)
#define SMTP_COMMAND_MAP(XX)\
XX(HELO, HELO) \
XX(EHLO, EHLO) \
XX(AUTH, AUTH) \
XX(MAIL, MAIL FROM:) \
XX(RCPT, RCPT TO:) \
XX(DATA, DATA) \
XX(QUIT, QUIT) \
enum smtp_command {
#define XX(name, string) SMTP_##name,
SMTP_COMMAND_MAP(XX)
#undef XX
};
// smtp_status
// XXX(code, name, string)
#define SMTP_STATUS_MAP(XXX) \
XXX(220, READY, Ready) \
XXX(221, BYE, Bye) \
XXX(235, AUTH_SUCCESS, Authentication success) \
XXX(250, OK, OK) \
XXX(334, AUTH, Auth input) \
XXX(354, DATA, End with <CR><LF>.<CR><LF>) \
XXX(500, BAD_SYNTAX, Bad syntax) \
XXX(502, NOT_IMPLEMENTED,Command not implemented) \
XXX(503, BAD_SEQUENCE, Bad sequence of commands) \
XXX(504, UNRECOGNIZED_AUTH_TYPE, Unrecognized authentication type) \
XXX(535, AUTH_FAILED, Authentication failed) \
XXX(553, ERR_MAIL, Mailbox name not allowed) \
XXX(554, ERR_DATA, Transaction failed) \
enum smtp_status {
#define XXX(code, name, string) SMTP_STATUS_##name = code,
SMTP_STATUS_MAP(XXX)
#undef XXX
};
typedef struct mail_s {
char* from;
char* to;
char* subject;
char* body;
} mail_t;
BEGIN_EXTERN_C
HV_EXPORT const char* smtp_command_str(enum smtp_command cmd);
HV_EXPORT const char* smtp_status_str(enum smtp_status status);
// cmd param\r\n
HV_EXPORT int smtp_build_command(enum smtp_command cmd, const char* param, char* buf, int buflen);
// status_code status_message\r\n
HV_EXPORT int sendmail(const char* smtp_server,
const char* username,
const char* password,
mail_t* mail);
END_EXTERN_C
#endif // HV_SMTP_H_