mirror of
https://github.com/fail0verflow/hbc.git
synced 2024-11-18 23:59:38 +01:00
298 lines
4.8 KiB
C
298 lines
4.8 KiB
C
#include <sys/types.h>
|
|
#include <sys/errno.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <ogcsys.h>
|
|
#include <network.h>
|
|
#include <ogc/mutex.h>
|
|
#include <ogc/lwp_watchdog.h>
|
|
|
|
#include "../config.h"
|
|
#include "panic.h"
|
|
|
|
#include "tcp.h"
|
|
|
|
s32 tcp_socket (void) {
|
|
s32 s, res;
|
|
u32 val;
|
|
|
|
s = net_socket (PF_INET, SOCK_STREAM, 0);
|
|
if (s < 0) {
|
|
gprintf ("net_socket failed: %ld\n", s);
|
|
return s;
|
|
}
|
|
|
|
val = 1;
|
|
net_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
|
|
|
|
res = net_fcntl (s, F_GETFL, 0);
|
|
if (res < 0) {
|
|
gprintf ("F_GETFL failed: %ld\n", res);
|
|
net_close (s);
|
|
return res;
|
|
}
|
|
|
|
res = net_fcntl (s, F_SETFL, res | 4);
|
|
if (res < 0) {
|
|
gprintf ("F_SETFL failed: %ld\n", res);
|
|
net_close (s);
|
|
return res;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
s32 tcp_connect (char *host, u16 port) {
|
|
struct hostent *hp;
|
|
struct sockaddr_in sa;
|
|
s32 s, res;
|
|
s64 t;
|
|
|
|
hp = net_gethostbyname (host);
|
|
if (!hp || !(hp->h_addrtype == PF_INET)) {
|
|
gprintf ("net_gethostbyname failed: %d\n", errno);
|
|
return errno;
|
|
}
|
|
|
|
s = tcp_socket ();
|
|
if (s < 0)
|
|
return s;
|
|
|
|
memset (&sa, 0, sizeof (struct sockaddr_in));
|
|
sa.sin_family= PF_INET;
|
|
sa.sin_len = sizeof (struct sockaddr_in);
|
|
sa.sin_port= htons (port);
|
|
memcpy ((char *) &sa.sin_addr, hp->h_addr_list[0], hp->h_length);
|
|
|
|
t = gettime ();
|
|
while (true) {
|
|
if (ticks_to_millisecs (diff_ticks (t, gettime ())) >
|
|
TCP_CONNECT_TIMEOUT) {
|
|
gprintf ("tcp_connect timeout\n");
|
|
net_close (s);
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
res = net_connect (s, (struct sockaddr *) &sa,
|
|
sizeof (struct sockaddr_in));
|
|
|
|
if (res < 0) {
|
|
if (res == -EISCONN)
|
|
break;
|
|
|
|
if (res == -EINPROGRESS || res == -EALREADY) {
|
|
usleep (20 * 1000);
|
|
|
|
continue;
|
|
}
|
|
|
|
gprintf ("net_connect failed: %ld\n", res);
|
|
net_close (s);
|
|
|
|
return res;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
s32 tcp_listen (u16 port, s32 backlog) {
|
|
s32 s, res;
|
|
struct sockaddr_in sa;
|
|
|
|
s = tcp_socket ();
|
|
if (s < 0)
|
|
return s;
|
|
|
|
memset(&sa, 0, sizeof (struct sockaddr_in));
|
|
sa.sin_family = PF_INET;
|
|
sa.sin_port = htons (port);
|
|
sa.sin_addr.s_addr = net_gethostip ();
|
|
sa.sin_len = sizeof (struct sockaddr_in);
|
|
|
|
res = net_bind (s, (struct sockaddr *) &sa, sizeof (struct sockaddr_in));
|
|
if (res < 0) {
|
|
gprintf ("net_bind failed: %ld\n", res);
|
|
net_close (s);
|
|
return res;
|
|
}
|
|
|
|
res = net_listen (s, backlog);
|
|
if (res < 0) {
|
|
gprintf ("net_listen failed: %ld\n", res);
|
|
net_close (s);
|
|
return res;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
char * tcp_readln (s32 s, u16 max_length, s64 start_time, u16 timeout) {
|
|
char *buf;
|
|
u16 c;
|
|
s32 res;
|
|
char *ret;
|
|
|
|
buf = (char *) pmalloc (max_length);
|
|
|
|
c = 0;
|
|
ret = NULL;
|
|
while (true) {
|
|
if (ticks_to_millisecs (diff_ticks (start_time, gettime ())) > timeout)
|
|
break;
|
|
|
|
res = net_read (s, &buf[c], 1);
|
|
|
|
if ((res == 0) || (res == -EAGAIN)) {
|
|
usleep (20 * 1000);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (res < 0) {
|
|
gprintf ("tcp_readln failed: %ld\n", res);
|
|
|
|
break;
|
|
}
|
|
|
|
if ((c > 0) && (buf[c - 1] == '\r') && (buf[c] == '\n')) {
|
|
if (c == 1) {
|
|
ret = pstrdup ("");
|
|
|
|
break;
|
|
}
|
|
|
|
ret = strndup (buf, c - 1);
|
|
|
|
break;
|
|
}
|
|
|
|
c++;
|
|
|
|
if (c == max_length)
|
|
break;
|
|
}
|
|
|
|
free (buf);
|
|
return ret;
|
|
}
|
|
|
|
bool tcp_read (s32 s, u8 *buffer, u32 length, const mutex_t *mutex, u32 *progress) {
|
|
u32 step, left, block, received;
|
|
s64 t;
|
|
s32 res;
|
|
|
|
step = 0;
|
|
left = length;
|
|
received = 0;
|
|
|
|
t = gettime ();
|
|
while (left) {
|
|
if (ticks_to_millisecs (diff_ticks (t, gettime ())) >
|
|
TCP_BLOCK_RECV_TIMEOUT) {
|
|
gprintf ("tcp_read timeout\n");
|
|
|
|
break;
|
|
}
|
|
|
|
block = left;
|
|
if (block > 2048)
|
|
block = 2048;
|
|
|
|
res = net_read (s, buffer, block);
|
|
|
|
if ((res == 0) || (res == -EAGAIN)) {
|
|
usleep (20 * 1000);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (res < 0) {
|
|
gprintf ("net_read failed: %ld\n", res);
|
|
|
|
break;
|
|
}
|
|
|
|
received += res;
|
|
left -= res;
|
|
buffer += res;
|
|
|
|
if ((received / TCP_BLOCK_SIZE) > step) {
|
|
t = gettime ();
|
|
step++;
|
|
}
|
|
|
|
if (mutex && progress) {
|
|
LWP_MutexLock (*mutex);
|
|
*progress = received;
|
|
LWP_MutexUnlock (*mutex);
|
|
}
|
|
}
|
|
|
|
return left == 0;
|
|
}
|
|
|
|
bool tcp_write (s32 s, const u8 *buffer, u32 length, const mutex_t *mutex,
|
|
u32 *progress) {
|
|
const u8 *p;
|
|
u32 step, left, block, sent;
|
|
s64 t;
|
|
s32 res;
|
|
|
|
step = 0;
|
|
p = buffer;
|
|
left = length;
|
|
sent = 0;
|
|
|
|
t = gettime ();
|
|
while (left) {
|
|
if (ticks_to_millisecs (diff_ticks (t, gettime ())) >
|
|
TCP_BLOCK_SEND_TIMEOUT) {
|
|
|
|
gprintf ("tcp_write timeout\n");
|
|
break;
|
|
}
|
|
|
|
block = left;
|
|
if (block > 2048)
|
|
block = 2048;
|
|
|
|
res = net_write (s, p, block);
|
|
|
|
if ((res == 0) || (res == -EAGAIN)) {
|
|
usleep (20 * 1000);
|
|
continue;
|
|
}
|
|
|
|
if (res < 0) {
|
|
gprintf ("net_write failed: %ld\n", res);
|
|
break;
|
|
}
|
|
|
|
sent += res;
|
|
left -= res;
|
|
p += res;
|
|
|
|
if ((sent / TCP_BLOCK_SIZE) > step) {
|
|
t = gettime ();
|
|
step++;
|
|
}
|
|
|
|
if (mutex && progress) {
|
|
LWP_MutexLock (*mutex);
|
|
*progress = sent;
|
|
LWP_MutexUnlock (*mutex);
|
|
}
|
|
}
|
|
|
|
return left == 0;
|
|
}
|
|
|