#include #include #include #include #include #include #include #include #include #include #include #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; }