initial commit
This commit is contained in:
10
third_party/libhv/protocol/README.md
vendored
Executable file
10
third_party/libhv/protocol/README.md
vendored
Executable 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
336
third_party/libhv/protocol/dns.c
vendored
Executable 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
105
third_party/libhv/protocol/dns.h
vendored
Executable 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
247
third_party/libhv/protocol/ftp.c
vendored
Executable 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
96
third_party/libhv/protocol/ftp.h
vendored
Executable 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
125
third_party/libhv/protocol/icmp.c
vendored
Executable 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
15
third_party/libhv/protocol/icmp.h
vendored
Executable 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
256
third_party/libhv/protocol/smtp.c
vendored
Executable 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
74
third_party/libhv/protocol/smtp.h
vendored
Executable 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_
|
||||
Reference in New Issue
Block a user