308 lines
9.9 KiB
C
308 lines
9.9 KiB
C
#if is(tcc) && is(win32) // @fixme: https lib is broken with tcc. replaced with InternetReadFile() api for now
|
|
|
|
# include <wininet.h>
|
|
# pragma comment(lib,"wininet")
|
|
|
|
int download_file( FILE *out, const char *url ) {
|
|
int ok = false;
|
|
char buffer[ 4096 ];
|
|
DWORD response_size = 0;
|
|
|
|
if( out )
|
|
for( HINTERNET session = InternetOpenA("v4k.download_file", PRE_CONFIG_INTERNET_ACCESS, NULL, INTERNET_INVALID_PORT_NUMBER, 0); session; InternetCloseHandle(session), session = 0 )
|
|
for( HINTERNET request = InternetOpenUrlA(session, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); request; InternetCloseHandle(request), request = 0 )
|
|
for(; InternetReadFile(request, buffer, sizeof(buffer), &response_size) != FALSE && response_size > 0; ) {
|
|
ok = (fwrite(buffer, response_size, 1, out) == 1);
|
|
if(!ok) break;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
array(char) download( const char *url ) {
|
|
int ok = false;
|
|
char buffer[ 4096 ];
|
|
DWORD response_size = 0, pos = 0;
|
|
|
|
array(char) out = 0;
|
|
for( HINTERNET session = InternetOpenA("v4k.download", PRE_CONFIG_INTERNET_ACCESS, NULL, INTERNET_INVALID_PORT_NUMBER, 0); session; InternetCloseHandle(session), session = 0 )
|
|
for( HINTERNET request = InternetOpenUrlA(session, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); request; InternetCloseHandle(request), request = 0 )
|
|
for(; InternetReadFile(request, buffer, sizeof(buffer), &response_size) != FALSE && response_size > 0; ) {
|
|
array_resize(out, pos + response_size);
|
|
ok = !!memcpy(out + (pos += response_size) - response_size, buffer, response_size);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
#else
|
|
int download_file( FILE *out, const char *url ) {
|
|
int ok = false;
|
|
if( out ) for( https_t *h = https_get(url, NULL); h; https_release(h), h = NULL ) {
|
|
while (https_process(h) == HTTPS_STATUS_PENDING) sleep_ms(1);
|
|
//printf("fetch status%d, %d %s\n\n%.*s\n", https_process(h), h->status_code, h->content_type, (int)h->response_size, (char*)h->response_data);
|
|
if(https_process(h) == HTTPS_STATUS_COMPLETED)
|
|
ok = fwrite(h->response_data, h->response_size, 1, out) == 1;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// @fixme: broken with tcc -m64 (our default tcc configuration)
|
|
array(char) download( const char *url ) {
|
|
array(char) out = 0;
|
|
for( https_t *h = https_get(url, NULL); h; https_release(h), h = NULL ) {
|
|
while (https_process(h) == HTTPS_STATUS_PENDING) sleep_ms(1);
|
|
//printf("fetch status:%d, %d %s\n\n%.*s\n", https_process(h), h->status_code, h->content_type, (int)h->response_size, (char*)h->response_data);
|
|
if( https_process(h) == HTTPS_STATUS_COMPLETED ) {
|
|
array_resize(out, h->response_size);
|
|
memcpy(out, h->response_data, h->response_size);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
#endif
|
|
|
|
bool network_tests() {
|
|
// network test (https)
|
|
array(char) webfile = download("https://www.google.com/");
|
|
printf("Network test: %d bytes downloaded from google.com\n", array_count(webfile));
|
|
// array_push(webfile, '\0'); puts(webfile);
|
|
return true;
|
|
}
|
|
|
|
int portname( const char *service_name, unsigned retries ) {
|
|
// Determine port for a given service based on hash of its name.
|
|
// If port cant be reached, client should retry with next hash.
|
|
// Algorithm: fnv1a(name of service) -> splitmix64 num retries -> remap bucket as [min..max] ports.
|
|
|
|
// hash64
|
|
uint64_t hash = 14695981039346656037ULL;
|
|
while( *service_name ) {
|
|
hash = ( (unsigned char)*service_name++ ^ hash ) * 0x100000001b3ULL;
|
|
}
|
|
|
|
// splitmix64
|
|
for( unsigned i = 0; i < retries; ++i ) {
|
|
uint64_t h = (hash += UINT64_C(0x9E3779B97F4A7C15));
|
|
h = (h ^ (h >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);
|
|
h = (h ^ (h >> 27)) * UINT64_C(0x94D049BB133111EB);
|
|
h = (h ^ (h >> 31));
|
|
hash = h;
|
|
}
|
|
|
|
// See dynamic ports: https://en.wikipedia.org/wiki/Ephemeral_port
|
|
// So, excluded ranges: 32768..60999 (linux), 49152..65535 (freebsd+vista+win7), 1024..5000 (winsrv2003+bsd)
|
|
// Output range: [5001..32724], in 4096 steps
|
|
return ((hash & 0xFFF) * 677 / 100 + 5001);
|
|
}
|
|
|
|
static
|
|
void netdump( const void *ptr, int len ) {
|
|
char hexbuf[256] = {0}, strbuf[256] = {0}, *data = (char*)ptr, width = 16;
|
|
for( int jt = 0; jt < len; jt += width ) {
|
|
char *hex = hexbuf, *str = strbuf;
|
|
for( int it = jt, next = it + width; it < len && it < next; ++it, ++data ) {
|
|
hex += sprintf( hex, "%02x ", (unsigned char)*data);
|
|
str += sprintf( str, "%c", *data >= 32 && *data != '\\' ? *data : '.');
|
|
}
|
|
printf("%06x %-*s%s\n", jt, width*3, hexbuf, strbuf);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
#define UDP_DEBUG 0
|
|
|
|
static int udp_init() {
|
|
do_once {
|
|
int rc = swrapInit(); // atexit(swrapTerminate);
|
|
if( rc ) PANIC("udp_init: swrapInit error");
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int udp_open(const char *address, const char *port) {
|
|
do_once udp_init();
|
|
int fd = swrapSocket(SWRAP_UDP, SWRAP_CONNECT, 0, address, port);
|
|
// if( fd == -1 ) PANIC("udp_open: swrapSocket error");
|
|
return fd;
|
|
}
|
|
|
|
int udp_bind(const char *address, const char *port) {
|
|
do_once udp_init();
|
|
int fd = swrapSocket(SWRAP_UDP, SWRAP_BIND, 0, address, port);
|
|
// if( fd == -1 ) PANIC("udp_bind: swrapSocket error");
|
|
return fd;
|
|
}
|
|
|
|
int udp_send( int fd, const void *buf, int len ) { // returns bytes sent, or -1 if error
|
|
int rc = -1;
|
|
if( fd >= 0 ) for( ;; ) {
|
|
rc = swrapSend(fd, (const char *)buf, len);
|
|
#if is(win32)
|
|
if( rc == -1 && WSAGetLastError() == WSAEINTR ) continue;
|
|
else break;
|
|
#else
|
|
if( rc == -1 && errno == EINTR ) continue;
|
|
else break;
|
|
#endif
|
|
}
|
|
#if UDP_DEBUG
|
|
if( rc > 0 ) {
|
|
char host[128], serv[128];
|
|
int rc2 = swrapAddressInfo(&sa, host, 128, serv, 128 );
|
|
if( rc2 != 0 ) PANIC("swrapAddressInfo error");
|
|
printf("udp_send: %d bytes to %s:%s : %.*s\n", rc, host, serv, rc, buf );
|
|
netdump(buf, rc);
|
|
}
|
|
#endif
|
|
return rc;
|
|
}
|
|
|
|
int udp_close( int fd ) { // @todo: expose me? needed?
|
|
#if is(win32)
|
|
// closesocket(fd);
|
|
#else
|
|
// close(fd);
|
|
#endif
|
|
fd = -1; // noop
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
// use socket to send data to another address
|
|
int udp_sendto( int fd, const char *ip, const char *port, const void *buf, int len ) { // return number of bytes sent
|
|
#if 0
|
|
int rc = swrapSendTo(fd, struct swrap_addr*, (const char*)buf, len);
|
|
if( rc == -1 ) return -1; //PANIC("udp_send: swrapSend error");
|
|
return rc;
|
|
#else
|
|
struct sockaddr_in addr = {0};
|
|
addr.sin_family = AF_INET;
|
|
// use inet_addr. tcc(win32) wont work otherwise.
|
|
addr.sin_addr.s_addr = inet_addr(ip); // inet_pton(AF_INET, ip, &addr.sin_addr);
|
|
addr.sin_port = htons(atoi(port));
|
|
|
|
int n = sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr));
|
|
return n < 0 ? -1 : n;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
int udp_peek( int fd ) { // <0 error, 0 timeout, >0 data
|
|
int rc = swrapSelect(fd, 0.00001);
|
|
if( rc < 0 ) return -1; // PANIC("udp_peek: swrapSelect error");
|
|
if( rc == 0 ) return 0; // timeout
|
|
return 1; //> 0: new data is available
|
|
}
|
|
|
|
int udp_recv( int fd, void *buf, int len ) { // <0 error, 0 orderly shutdown, >0 received bytes
|
|
struct swrap_addr sa = {0};
|
|
int rc = swrapReceiveFrom(fd, &sa, buf, len);
|
|
if( rc < 0 ) return -1; // PANIC("udp_recv: swrapReceiveFrom error");
|
|
if( rc == 0 ) return 0; // orderly shutdown
|
|
|
|
#if UDP_DEBUG
|
|
char host[128], serv[128];
|
|
int rc2 = swrapAddressInfo(&sa, host, 128, serv, 128 );
|
|
if( rc2 != 0 ) PANIC("swrapAddressInfo error");
|
|
printf("udp_recv: %d bytes from %s:%s : %.*s\n", rc, host, serv, rc, buf );
|
|
netdump(buf, rc);
|
|
#endif
|
|
|
|
return rc;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
#define TCP_DEBUG 1
|
|
|
|
#if TCP_DEBUG
|
|
static set(int) tcp_set;
|
|
#endif
|
|
|
|
void tcp_init(void) {
|
|
do_once {
|
|
udp_init();
|
|
#if TCP_DEBUG
|
|
set_init(tcp_set, less_int, hash_int);
|
|
#endif
|
|
}
|
|
}
|
|
int tcp_open(const char *address, const char *port) {
|
|
do_once tcp_init();
|
|
int fd = swrapSocket(SWRAP_TCP, SWRAP_CONNECT, 0/*|SWRAP_NODELAY*/, address, port);
|
|
return fd;
|
|
}
|
|
int tcp_bind(const char *interface_, const char *port, int backlog) {
|
|
do_once tcp_init();
|
|
int fd = swrapSocket(SWRAP_TCP, SWRAP_BIND, 0/*|SWRAP_NODELAY*//*|SWRAP_NOBLOCK*/, interface_, port);
|
|
if( fd >= 0 ) swrapListen(fd, backlog);
|
|
return fd;
|
|
}
|
|
int tcp_peek(int fd, int(*callback)(int)) {
|
|
struct swrap_addr sa;
|
|
int fd2 = swrapAccept(fd, &sa);
|
|
if( fd2 >= 0 ) return callback(fd2);
|
|
return -1;
|
|
}
|
|
int tcp_send(int fd, const void *buf, int len) {
|
|
int rc = swrapSend(fd, (const char *)buf, len);
|
|
#if TCP_DEBUG
|
|
if( set_find(tcp_set, fd) ) {
|
|
printf("send -> %11d (status: %d) %s:%s\n", len, rc, tcp_host(fd), tcp_port(fd));
|
|
if( rc > 0 ) netdump(buf, rc);
|
|
}
|
|
#endif
|
|
return rc;
|
|
}
|
|
int tcp_recv(int fd, void *buf, int len) {
|
|
int rc = swrapReceive(fd, (char*)buf, len);
|
|
#if TCP_DEBUG
|
|
if( rc != 0 && set_find(tcp_set, fd) ) {
|
|
printf("recv <- %11d (status: %d) %s:%s\n", len, rc, tcp_host(fd), tcp_port(fd));
|
|
if( rc > 0 ) netdump(buf, rc);
|
|
}
|
|
#endif
|
|
return rc;
|
|
}
|
|
char* tcp_host(int fd) {
|
|
char buf[1024];
|
|
struct swrap_addr sa;
|
|
swrapAddress(fd, &sa);
|
|
swrapAddressInfo(&sa, buf, 512, buf+512, 512);
|
|
return va("%s", buf);
|
|
}
|
|
char* tcp_port(int fd) {
|
|
char buf[1024];
|
|
struct swrap_addr sa;
|
|
swrapAddress(fd, &sa);
|
|
swrapAddressInfo(&sa, buf, 512, buf+512, 512);
|
|
return va("%s", buf+512);
|
|
}
|
|
int tcp_close(int fd) {
|
|
swrapClose(fd);
|
|
return 0;
|
|
}
|
|
int tcp_debug(int fd) {
|
|
#if TCP_DEBUG
|
|
if( set_find(tcp_set, fd) ) {
|
|
set_erase(tcp_set, fd);
|
|
return 0;
|
|
} else {
|
|
set_insert(tcp_set, fd);
|
|
return 1;
|
|
}
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static void network_init() {
|
|
do_once {
|
|
udp_init();
|
|
tcp_init();
|
|
}
|
|
}
|