uae-wii/src/bsdsocket-posix-new.c

1717 lines
39 KiB
C

/*
* UAE - The Un*x Amiga Emulator
*
* bsdsocket.library emulation - Unix
*
* Copyright 2000-2001 Carl Drougge <carl.drougge@home.se> <bearded@longhaired.org>
* Copyright 2003-2005 Richard Drummond
* Copyright 2004 Jeff Shepherd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "autoconf.h"
#include "traps.h"
#include "threaddep/thread.h"
#include "native2amiga.h"
#include "bsdsocket.h"
#ifdef BSDSOCKET
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stddef.h>
#include <netdb.h>
#include <signal.h>
#include <arpa/inet.h>
//#define DEBUG_BSDSOCKET
#ifdef DEBUG_BSDSOCKET
#define DEBUG_LOG write_log
#else
#define DEBUG_LOG(...) do ; while(0)
#endif
#define WAITSIGNAL waitsig (context, sb)
/* Sigqueue is unsafe on SMP machines.
* Temporary work-around.
*/
//#define SETSIGNAL addtosigqueue (sb, 0)
#define SETSIGNAL \
do { \
uae_Signal (sb->ownertask, sb->sigstosend | ((uae_u32) 1) << sb->signal); \
sb->dosignal = 1; \
} while (0)
#define SETERRNO bsdsocklib_seterrno (sb,mapErrno (errno))
#define SETHERRNO bsdsocklib_setherrno (sb, h_errno)
/* BSD-systems don't seem to have MSG_NOSIGNAL..
@@@ We need to catch SIGPIPE on those systems! (?) */
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
#define S_GL_result(res) sb->resultval = (res)
uae_u32 bsdthr_Accept_2 (SB);
uae_u32 bsdthr_Recv_2 (SB);
uae_u32 bsdthr_blockingstuff (uae_u32 (*tryfunc)(SB), SB);
uae_u32 bsdthr_SendRecvAcceptConnect (uae_u32 (*tryfunc)(SB), SB);
uae_u32 bsdthr_Send_2 (SB);
uae_u32 bsdthr_Connect_2 (SB);
uae_u32 bsdthr_WaitSelect (SB);
uae_u32 bsdthr_Wait (SB);
void clearsockabort (SB);
static uae_sem_t sem_queue;
/**
** Helper functions
**/
/*
* Map host errno to amiga errno
*/
static int mapErrno (int e)
{
switch (e) {
case EINTR: e = 4; break;
case EDEADLK: e = 11; break;
case EAGAIN: e = 35; break;
case EINPROGRESS: e = 36; break;
case EALREADY: e = 37; break;
case ENOTSOCK: e = 38; break;
case EDESTADDRREQ: e = 39; break;
case EMSGSIZE: e = 40; break;
case EPROTOTYPE: e = 41; break;
case ENOPROTOOPT: e = 42; break;
case EPROTONOSUPPORT: e = 43; break;
case ESOCKTNOSUPPORT: e = 44; break;
case EOPNOTSUPP: e = 45; break;
case EPFNOSUPPORT: e = 46; break;
case EAFNOSUPPORT: e = 47; break;
case EADDRINUSE: e = 48; break;
case EADDRNOTAVAIL: e = 49; break;
case ENETDOWN: e = 50; break;
case ENETUNREACH: e = 51; break;
case ENETRESET: e = 52; break;
case ECONNABORTED: e = 53; break;
case ECONNRESET: e = 54; break;
case ENOBUFS: e = 55; break;
case EISCONN: e = 56; break;
case ENOTCONN: e = 57; break;
case ESHUTDOWN: e = 58; break;
case ETOOMANYREFS: e = 59; break;
case ETIMEDOUT: e = 60; break;
case ECONNREFUSED: e = 61; break;
case ELOOP: e = 62; break;
case ENAMETOOLONG: e = 63; break;
default: break;
}
return e;
}
/*
* Map amiga (s|g)etsockopt level into native one
*/
static int mapsockoptlevel (int level)
{
switch (level) {
case 0xffff:
return SOL_SOCKET;
case 0:
return IPPROTO_IP;
case 1:
return IPPROTO_ICMP;
case 2:
return IPPROTO_IGMP;
#ifdef IPPROTO_IPIP
case 4:
return IPPROTO_IPIP;
#endif
case 6:
return IPPROTO_TCP;
case 8:
return IPPROTO_EGP;
case 12:
return IPPROTO_PUP;
case 17:
return IPPROTO_UDP;
case 22:
return IPPROTO_IDP;
#ifdef IPPROTO_TP
case 29:
return IPPROTO_TP;
#endif
case 98:
return IPPROTO_ENCAP;
default:
DEBUG_LOG ("Unknown sockopt level %d\n", level);
return level;
}
}
/*
* Map amiga (s|g)etsockopt optname into native one
*/
static int mapsockoptname (int level, int optname)
{
switch (level) {
case SOL_SOCKET:
switch (optname) {
case 0x0001:
return SO_DEBUG;
case 0x0002:
return SO_ACCEPTCONN;
case 0x0004:
return SO_REUSEADDR;
case 0x0008:
return SO_KEEPALIVE;
case 0x0010:
return SO_DONTROUTE;
case 0x0020:
return SO_BROADCAST;
#ifdef SO_USELOOPBACK
case 0x0040:
return SO_USELOOPBACK;
#endif
case 0x0080:
return SO_LINGER;
case 0x0100:
return SO_OOBINLINE;
#ifdef SO_REUSEPORT
case 0x0200:
return SO_REUSEPORT;
#endif
case 0x1001:
return SO_SNDBUF;
case 0x1002:
return SO_RCVBUF;
case 0x1003:
return SO_SNDLOWAT;
case 0x1004:
return SO_RCVLOWAT;
case 0x1005:
return SO_SNDTIMEO;
case 0x1006:
return SO_RCVTIMEO;
case 0x1007:
return SO_ERROR;
case 0x1008:
return SO_TYPE;
default:
DEBUG_LOG ("Invalid setsockopt option %x for level %d\n",
optname, level);
return -1;
}
break;
case IPPROTO_IP:
switch (optname) {
case 1:
return IP_OPTIONS;
case 2:
return IP_HDRINCL;
case 3:
return IP_TOS;
case 4:
return IP_TTL;
case 5:
return IP_RECVOPTS;
case 6:
return IP_RECVRETOPTS;
case 8:
return IP_RETOPTS;
case 9:
return IP_MULTICAST_IF;
case 10:
return IP_MULTICAST_TTL;
case 11:
return IP_MULTICAST_LOOP;
case 12:
return IP_ADD_MEMBERSHIP;
default:
DEBUG_LOG ("Invalid setsockopt option %x for level %d\n",
optname, level);
return -1;
}
break;
case IPPROTO_TCP:
switch (optname) {
case 1:
return TCP_NODELAY;
case 2:
return TCP_MAXSEG;
default:
DEBUG_LOG ("Invalid setsockopt option %x for level %d\n",
optname, level);
return -1;
}
break;
default:
DEBUG_LOG ("Unknown level %d\n", level);
return -1;
}
}
/*
* Map amiga (s|g)etsockopt return value into the correct form
*/
static void mapsockoptreturn(int level, int optname, uae_u32 optval, void *buf)
{
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_DEBUG:
case SO_ACCEPTCONN:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
#ifdef SO_USELOOPBACK
case SO_USELOOPBACK:
#endif
case SO_LINGER:
case SO_OOBINLINE:
#ifdef SO_REUSEPORT
case SO_REUSEPORT:
#endif
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
case SO_SNDTIMEO:
case SO_RCVTIMEO:
case SO_TYPE:
put_long(optval, *(int *)buf);
break;
case SO_ERROR:
DEBUG_LOG("New errno is %d\n", mapErrno(*(int *)buf));
put_long(optval, mapErrno(*(int *)buf));
break;
default:
break;
}
break;
case IPPROTO_IP:
switch (optname) {
case IP_OPTIONS:
case IP_HDRINCL:
case IP_TOS:
case IP_TTL:
case IP_RECVOPTS:
//case IP_RECVRETOPTS:
//case IP_RETOPTS:
case IP_MULTICAST_IF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
case IP_ADD_MEMBERSHIP:
put_long(optval, *(int *)buf);
break;
default:
break;
}
break;
case IPPROTO_TCP:
switch (optname) {
case TCP_NODELAY:
case TCP_MAXSEG:
put_long(optval,*(int *)buf);
break;
default:
break;
}
break;
default:
break;
}
}
/*
* Map amiga (s|g)etsockopt value from amiga to the appropriate value
*/
static void mapsockoptvalue(int level, int optname, uae_u32 optval, void *buf)
{
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_DEBUG:
case SO_ACCEPTCONN:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
#ifdef SO_USELOOPBACK
case SO_USELOOPBACK:
#endif
case SO_LINGER:
case SO_OOBINLINE:
#ifdef SO_REUSEPORT
case SO_REUSEPORT:
#endif
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
case SO_SNDTIMEO:
case SO_RCVTIMEO:
case SO_TYPE:
case SO_ERROR:
*((int *)buf) = get_long(optval);
break;
default:
break;
}
break;
case IPPROTO_IP:
switch (optname) {
case IP_OPTIONS:
case IP_HDRINCL:
case IP_TOS:
case IP_TTL:
case IP_RECVOPTS:
//case IP_RECVRETOPTS:
//case IP_RETOPTS:
case IP_MULTICAST_IF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
case IP_ADD_MEMBERSHIP:
*((int *)buf) = get_long(optval);
break;
default:
break;
}
break;
case IPPROTO_TCP:
switch (optname) {
case TCP_NODELAY:
case TCP_MAXSEG:
*((int *)buf) = get_long(optval);
break;
default:
break;
}
break;
default:
break;
}
}
STATIC_INLINE void fd_zero (uae_u32 fdset, uae_u32 nfds)
{
unsigned int i;
for (i = 0; i < nfds; i += 32, fdset += 4)
put_long (fdset, 0);
}
STATIC_INLINE int bsd_amigaside_FD_ISSET (int n, uae_u32 set)
{
uae_u32 foo = get_long (set + (n / 32));
if (foo & (1 << (n % 32)))
return 1;
return 0;
}
STATIC_INLINE void bsd_amigaside_FD_ZERO (uae_u32 set)
{
put_long (set, 0);
put_long (set + 4, 0);
}
STATIC_INLINE void bsd_amigaside_FD_SET (int n, uae_u32 set)
{
set = set + (n / 32);
put_long (set, get_long (set) | (1 << (n % 32)));
}
#ifdef DEBUG_BSDSOCKET
static void printSockAddr (struct sockaddr_in *in)
{
DEBUG_LOG ("Family %d, ", in->sin_family);
DEBUG_LOG ("Port %d,", ntohs (in->sin_port));
DEBUG_LOG ("Address %s,", inet_ntoa (in->sin_addr));
}
#else
#define printSockAddr(sockAddr)
#endif
/*
* Copy a sockaddr object from amiga space to native space
*/
static int copysockaddr_a2n (struct sockaddr_in *addr, uae_u32 a_addr, unsigned int len)
{
if ((len > sizeof (struct sockaddr_in)) || (len < 8))
return 1;
if (a_addr == 0)
return 0;
addr->sin_family = get_byte (a_addr + 1);
addr->sin_port = htons (get_word (a_addr + 2));
addr->sin_addr.s_addr = htonl (get_long (a_addr + 4));
if (len > 8)
memcpy (&addr->sin_zero, get_real_address (a_addr + 8), len - 8); /* Pointless? */
return 0;
}
/*
* Copy a sockaddr object from native space to amiga space
*/
static int copysockaddr_n2a (uae_u32 a_addr, const struct sockaddr_in *addr, unsigned int len)
{
if (len < 8)
return 1;
if (a_addr == 0)
return 0;
put_byte (a_addr, 0); /* Anyone use this field? */
put_byte (a_addr + 1, addr->sin_family);
put_word (a_addr + 2, ntohs (addr->sin_port));
put_long (a_addr + 4, ntohl (addr->sin_addr.s_addr));
if (len > 8)
memset (get_real_address (a_addr + 8), 0, len - 8);
return 0;
}
/*
* Copy a hostent object from native space to amiga space
*/
static void copyHostent (const struct hostent *hostent, SB)
{
int size = 28;
int i;
int numaddr = 0;
int numaliases = 0;
uae_u32 aptr;
if (hostent->h_name != NULL)
size += strlen(hostent->h_name)+1;
if (hostent->h_aliases != NULL)
while (hostent->h_aliases[numaliases])
size += strlen(hostent->h_aliases[numaliases++]) + 5;
if (hostent->h_addr_list != NULL) {
while (hostent->h_addr_list[numaddr])
numaddr++;
size += numaddr*(hostent->h_length+4);
}
aptr = sb->hostent + 28 + numaliases * 4 + numaddr * 4;
// transfer hostent to Amiga memory
put_long (sb->hostent + 4, sb->hostent + 20);
put_long (sb->hostent + 8, hostent->h_addrtype);
put_long (sb->hostent + 12, hostent->h_length);
put_long (sb->hostent + 16, sb->hostent + 24 + numaliases*4);
for (i = 0; i < numaliases; i++)
put_long (sb->hostent + 20 + i * 4, addstr (&aptr, hostent->h_aliases[i]));
put_long (sb->hostent + 20 + numaliases * 4, 0);
for (i = 0; i < numaddr; i++) {
put_long (sb->hostent + 24 + (numaliases + i) * 4,
addmem (&aptr, hostent->h_addr_list[i], hostent->h_length));
}
put_long (sb->hostent + 24 + numaliases * 4 + numaddr * 4, 0);
put_long (sb->hostent, aptr);
addstr (&aptr, hostent->h_name);
TRACE (("OK (%s)\n",hostent->h_name));
bsdsocklib_seterrno (sb,0);
}
/*
* Copy a protoent object from native space to Amiga space
*/
static void copyProtoent (TrapContext *context, SB, const struct protoent *p)
{
size_t size = 16;
int numaliases = 0;
int i;
uae_u32 aptr;
// compute total size of protoent
if (p->p_name != NULL)
size += strlen (p->p_name) + 1;
if (p->p_aliases != NULL)
while (p->p_aliases[numaliases])
size += strlen (p->p_aliases[numaliases++]) + 5;
if (sb->protoent) {
uae_FreeMem (context, sb->protoent, sb->protoentsize);
}
sb->protoent = uae_AllocMem (context, size, 0);
if (!sb->protoent) {
write_log ("BSDSOCK: WARNING - copyProtoent() ran out of Amiga memory (couldn't allocate %d bytes)\n", size);
bsdsocklib_seterrno (sb, 12); // ENOMEM
return;
}
sb->protoentsize = size;
aptr = sb->protoent + 16 + numaliases * 4;
// transfer protoent to Amiga memory
put_long (sb->protoent + 4, sb->protoent + 12);
put_long (sb->protoent + 8, p->p_proto);
for (i = 0; i < numaliases; i++)
put_long (sb->protoent + 12 + i * 4, addstr (&aptr, p->p_aliases[i]));
put_long (sb->protoent + 12 + numaliases * 4, 0);
put_long (sb->protoent, aptr);
addstr (&aptr, p->p_name);
bsdsocklib_seterrno(sb, 0);
}
uae_u32 bsdthr_WaitSelect (SB)
{
fd_set sets [3];
int i, s, set, a_s, max;
uae_u32 a_set;
struct timeval tv;
int r;
DEBUG_LOG ("WaitSelect: %d 0x%x 0x%x 0x%x 0x%x 0x%x\n",
sb->nfds, sb->sets [0], sb->sets [1], sb->sets [2], sb->timeout, sb->sigmp);
if (sb->timeout)
DEBUG_LOG ("WaitSelect: timeout %d %d\n", get_long (sb->timeout),
get_long (sb->timeout + 4));
FD_ZERO (&sets [0]);
FD_ZERO (&sets [1]);
FD_ZERO (&sets [2]);
/* Set up the abort socket */
FD_SET (sb->sockabort[0], &sets[0]);
FD_SET (sb->sockabort[0], &sets[2]);
max = sb->sockabort[0];
for (set = 0; set < 3; set++) {
if (sb->sets [set] != 0) {
a_set = sb->sets [set];
for (i = 0; i < sb->nfds; i++) {
if (bsd_amigaside_FD_ISSET (i, a_set)) {
s = getsock (sb, i + 1);
DEBUG_LOG ("WaitSelect: AmigaSide %d set. NativeSide %d.\n", i, s);
if (s == -1) {
write_log ("BSDSOCK: WaitSelect() called with invalid descriptor %d in set %d.\n", i, set);
} else {
FD_SET (s, &sets [set]);
if (max < s) max = s;
}
}
}
}
}
max++;
if (sb->timeout) {
tv.tv_sec = get_long (sb->timeout);
tv.tv_usec = get_long (sb->timeout + 4);
}
DEBUG_LOG("Select going to select\n");
r = select (max, &sets [0], &sets [1], &sets [2], (sb->timeout == 0) ? NULL : &tv);
DEBUG_LOG("Select returns %d, errno is %d\n", r, errno);
if( r > 0 ) {
/* Socket told us to abort */
if (FD_ISSET (sb->sockabort[0], &sets[0])) {
/* read from the pipe to reset it */
DEBUG_LOG ("WaitSelect aborted from signal\n");
r = 0;
for (set = 0; set < 3; set++)
if (sb->sets [set] != 0)
bsd_amigaside_FD_ZERO (sb->sets [set]);
clearsockabort (sb);
}
else
/* This is perhaps slightly inefficient, but I don't care.. */
for (set = 0; set < 3; set++) {
a_set = sb->sets [set];
if (a_set != 0) {
bsd_amigaside_FD_ZERO (a_set);
for (i = 0; i < sb->nfds; i++) {
a_s = getsock (sb, i + 1);
if (a_s != -1) {
if (FD_ISSET (a_s, &sets [set])) {
DEBUG_LOG ("WaitSelect: NativeSide %d set. AmigaSide %d.\n", a_s, i);
bsd_amigaside_FD_SET (i, a_set);
}
}
}
}
}
} else if (r == 0) { /* Timeout. I think we're supposed to clear the sets.. */
for (set = 0; set < 3; set++)
if (sb->sets [set] != 0)
bsd_amigaside_FD_ZERO (sb->sets [set]);
}
DEBUG_LOG ("WaitSelect: %d(%d)\n", r, errno);
return r;
}
uae_u32 bsdthr_Accept_2 (SB)
{
int foo, s, s2;
long flags;
struct sockaddr_in addr;
socklen_t hlen = sizeof (struct sockaddr_in);
if ((s = accept (sb->s, (struct sockaddr *)&addr, &hlen)) >= 0) {
if ((flags = fcntl (s, F_GETFL)) == -1)
flags = 0;
fcntl (s, F_SETFL, flags & ~O_NONBLOCK); /* @@@ Don't do this if it's supposed to stay nonblocking... */
s2 = getsd (sb, s);
sb->ftable[s2-1] = sb->ftable[sb->len]; /* new socket inherits the old socket's properties */
DEBUG_LOG ("Accept: AmigaSide %d, NativeSide %d, len %d(%d)",
sb->resultval, s, &hlen, get_long(sb->a_addrlen));
printSockAddr (&addr);
foo = get_long (sb->a_addrlen);
if (foo > 16)
put_long (sb->a_addrlen, 16);
copysockaddr_n2a (sb->a_addr, &addr, foo);
return s2 - 1;
} else {
return -1;
}
}
uae_u32 bsdthr_Recv_2 (SB)
{
int foo;
int l, i;
if (sb->from == 0) {
foo = recv (sb->s, sb->buf, sb->len, sb->flags /*| MSG_NOSIGNAL*/);
DEBUG_LOG ("recv2, recv returns %d, errno is %d\n", foo, errno);
} else {
struct sockaddr_in addr;
socklen_t l = sizeof (struct sockaddr_in);
i = get_long (sb->fromlen);
copysockaddr_a2n (&addr, sb->from, i);
foo = recvfrom (sb->s, sb->buf, sb->len, sb->flags | MSG_NOSIGNAL, (struct sockaddr *)&addr, &l);
DEBUG_LOG ("recv2, recvfrom returns %d, errno is %d\n", foo, errno);
if (foo >= 0) {
copysockaddr_n2a (sb->from, &addr, l);
put_long (sb->fromlen, l);
}
}
return foo;
}
uae_u32 bsdthr_Send_2 (SB)
{
if (sb->to == 0) {
return send (sb->s, sb->buf, sb->len, sb->flags | MSG_NOSIGNAL);
} else {
struct sockaddr_in addr;
int l = sizeof (struct sockaddr_in);
copysockaddr_a2n (&addr, sb->to, sb->tolen);
return sendto (sb->s, sb->buf, sb->len, sb->flags | MSG_NOSIGNAL,
(struct sockaddr *)&addr, l);
}
}
uae_u32 bsdthr_Connect_2 (SB)
{
if (sb->action == 1) {
struct sockaddr_in addr;
int len = sizeof (struct sockaddr_in);
int retval;
copysockaddr_a2n (&addr, sb->a_addr, sb->a_addrlen);
retval = connect (sb->s, (struct sockaddr *)&addr, len);
DEBUG_LOG ("Connect returns %d, errno is %d\n", retval, errno);
/* Hack: I need to set the action to something other than
* 1 but I know action == 2 does the correct thing
*/
sb->action = 2;
if (retval == 0) {
errno = 0;
}
return retval;
} else {
int foo;
socklen_t bar;
bar = sizeof (foo);
if (getsockopt (sb->s, SOL_SOCKET, SO_ERROR, &foo, &bar) == 0) {
errno = foo;
DEBUG_LOG("Connect status is %d\n", foo);
return (foo == 0) ? 0 : -1;
}
return -1;
}
}
uae_u32 bsdthr_SendRecvAcceptConnect (uae_u32 (*tryfunc)(SB), SB)
{
return bsdthr_blockingstuff (tryfunc, sb);
}
uae_u32 bsdthr_blockingstuff (uae_u32 (*tryfunc)(SB), SB)
{
int done = 0, foo;
long flags;
int nonblock;
if ((flags = fcntl (sb->s, F_GETFL)) == -1)
flags = 0;
nonblock = (flags & O_NONBLOCK);
fcntl (sb->s, F_SETFL, flags | O_NONBLOCK);
while (!done) {
done = 1;
foo = tryfunc (sb);
if (foo < 0 && !nonblock) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINPROGRESS)) {
fd_set readset, writeset, exceptset;
int maxfd = (sb->s > sb->sockabort[0]) ? sb->s : sb->sockabort[0];
int num;
FD_ZERO (&readset);
FD_ZERO (&writeset);
FD_ZERO (&exceptset);
if (sb->action == 3 || sb->action == 6)
FD_SET (sb->s, &readset);
if (sb->action == 2 || sb->action == 1 || sb->action == 4)
FD_SET (sb->s, &writeset);
FD_SET (sb->sockabort[0], &readset);
num = select (maxfd + 1, &readset, &writeset, &exceptset, NULL);
if (num == -1) {
DEBUG_LOG ("Blocking select(%d) returns -1,errno is %d\n", sb->sockabort[0],errno);
fcntl (sb->s, F_SETFL, flags);
return -1;
}
if (FD_ISSET (sb->sockabort[0], &readset) || FD_ISSET (sb->sockabort[0], &writeset)) {
/* reset sock abort pipe */
/* read from the pipe to reset it */
DEBUG_LOG ("select aborted from signal\n");
clearsockabort (sb);
DEBUG_LOG ("Done read\n");
errno = EINTR;
done = 1;
} else {
done = 0;
}
} else if (errno == EINTR)
done = 1;
}
}
fcntl (sb->s, F_SETFL, flags);
return foo;
}
static void *bsdlib_threadfunc (void *arg)
{
struct socketbase *sb = (struct socketbase *) arg;
DEBUG_LOG ("THREAD_START\n");
while (1) {
uae_sem_wait (&sb->sem);
DEBUG_LOG ("Socket thread got action %d\n", sb->action);
switch (sb->action) {
case 0: /* kill thread (CloseLibrary) */
DEBUG_LOG ("THREAD_END\n");
uae_sem_destroy (&sb->sem);
return NULL;
case 1: /* Connect */
sb->resultval = bsdthr_SendRecvAcceptConnect (bsdthr_Connect_2, sb);
break;
/* @@@ Should check (from|to)len so it's 16.. */
case 2: /* Send[to] */
sb->resultval = bsdthr_SendRecvAcceptConnect (bsdthr_Send_2, sb);
break;
case 3: /* Recv[from] */
sb->resultval = bsdthr_SendRecvAcceptConnect (bsdthr_Recv_2, sb);
break;
case 4: { /* Gethostbyname */
struct hostent *tmphostent = gethostbyname ((char *)get_real_address (sb->name));
if (tmphostent) {
copyHostent (tmphostent, sb);
bsdsocklib_setherrno (sb, 0);
} else
SETHERRNO;
break;
}
case 5: /* WaitSelect */
sb->resultval = bsdthr_WaitSelect (sb);
break;
case 6: /* Accept */
sb->resultval = bsdthr_SendRecvAcceptConnect (bsdthr_Accept_2, sb);
break;
case 7: {
struct hostent *tmphostent = gethostbyaddr (get_real_address (sb->name), sb->a_addrlen, sb->flags);
if (tmphostent) {
copyHostent (tmphostent, sb);
bsdsocklib_setherrno (sb, 0);
} else
SETHERRNO;
break;
}
}
SETERRNO;
SETSIGNAL;
}
return NULL; /* Just to keep GCC happy.. */
}
void host_connect (TrapContext *context, SB, uae_u32 sd, uae_u32 name, uae_u32 namelen)
{
sb->s = getsock (sb, sd + 1);
if (sb->s == -1) {
sb->resultval = -1;
bsdsocklib_seterrno (sb, 9); /* EBADF */
return;
}
sb->a_addr = name;
sb->a_addrlen = namelen;
sb->action = 1;
uae_sem_post (&sb->sem);
WAITSIGNAL;
}
void host_sendto (TrapContext *context, SB, uae_u32 sd, uae_u32 msg, uae_u32 len, uae_u32 flags, uae_u32 to, uae_u32 tolen)
{
sb->s = getsock (sb, sd + 1);
if (sb->s == -1) {
sb->resultval = -1;
bsdsocklib_seterrno (sb, 9); /* EBADF */
return;
}
sb->buf = get_real_address (msg);
sb->len = len;
sb->flags = flags;
sb->to = to;
sb->tolen = tolen;
sb->action = 2;
uae_sem_post (&sb->sem);
WAITSIGNAL;
}
void host_recvfrom (TrapContext *context, SB, uae_u32 sd, uae_u32 msg, uae_u32 len, uae_u32 flags, uae_u32 addr, uae_u32 addrlen)
{
int s = getsock (sb, sd + 1);
DEBUG_LOG ("Recv[from](%lx, %d, %lx, %ld, %lx, %lx, %d)\n",
sb, sd, msg, len, flags, addr, addrlen);
if (s == -1) {
sb->resultval = -1;
bsdsocklib_seterrno (sb, 9); /* EBADF */;
return;
}
sb->s = s;
sb->buf = get_real_address (msg);
sb->len = len;
sb->flags = flags;
sb->from = addr;
sb->fromlen= addrlen;
sb->action = 3;
uae_sem_post (&sb->sem);
WAITSIGNAL;
}
void host_setsockopt (SB, uae_u32 sd, uae_u32 level, uae_u32 optname, uae_u32 optval, uae_u32 optlen)
{
int s = getsock (sb, sd + 1);
int nativelevel = mapsockoptlevel (level);
int nativeoptname = mapsockoptname(nativelevel, optname);
void *buf;
if (s == -1) {
sb->resultval = -1;
bsdsocklib_seterrno (sb, 9); /* EBADF */;
return;
}
if (optval) {
buf = malloc(optlen);
mapsockoptvalue(nativelevel, nativeoptname, optval, buf);
} else {
buf = NULL;
}
sb->resultval = setsockopt (s, nativelevel, nativeoptname, buf, optlen);
if (buf)
free(buf);
SETERRNO;
DEBUG_LOG ("setsockopt: sock %d, level %d, 'name' %d(%d), len %d -> %d, %d\n",
s, level, optname, nativeoptname, optlen,
sb->resultval, errno);
}
uae_u32 host_getsockname (SB, uae_u32 sd, uae_u32 name, uae_u32 namelen)
{
int s;
socklen_t len = sizeof (struct sockaddr_in);
struct sockaddr_in addr;
DEBUG_LOG ("getsockname(%d,0x%lx,%d) -> ", sd, name, len);
s = getsock (sb, sd + 1);
if (s != -1) {
if (getsockname (s, (struct sockaddr *)&addr, &len)) {
SETERRNO;
DEBUG_LOG ("failed (%d)\n", sb->sb_errno);
} else {
int a_nl;
DEBUG_LOG ("okay\n");
a_nl = get_long (namelen);
copysockaddr_n2a (name, &addr, a_nl);
if (a_nl > 16)
put_long (namelen, 16);
return 0;
}
}
return -1;
}
uae_u32 host_getpeername (SB, uae_u32 sd, uae_u32 name, uae_u32 namelen)
{
int s;
socklen_t len = sizeof (struct sockaddr_in);
struct sockaddr_in addr;
DEBUG_LOG ("getpeername(%d,0x%lx,%d) -> ", sd, name, len);
s = getsock (sb, sd + 1);
if (s != -1) {
if (getpeername (s, (struct sockaddr *)&addr, &len)) {
SETERRNO;
DEBUG_LOG ("failed (%d)\n", sb->sb_errno);
} else {
int a_nl;
DEBUG_LOG ("okay\n");
a_nl = get_long (namelen);
copysockaddr_n2a (name, &addr, a_nl);
if (a_nl > 16)
put_long (namelen, 16);
return 0;
}
}
return -1;
}
void host_gethostbynameaddr (TrapContext *context, SB, uae_u32 name, uae_u32 namelen, long addrtype)
{
sb->name = name;
sb->a_addrlen = namelen;
sb->flags = addrtype;
if (addrtype == -1)
sb->action = 4;
else
sb->action = 7;
uae_sem_post (&sb->sem);
WAITSIGNAL;
}
void host_WaitSelect (TrapContext *context, SB, uae_u32 nfds, uae_u32 readfds, uae_u32 writefds, uae_u32 exceptfds,
uae_u32 timeout, uae_u32 sigmp)
{
uae_u32 wssigs = (sigmp) ? get_long (sigmp) : 0;
uae_u32 sigs;
if (wssigs) {
m68k_dreg (&context->regs, 0) = 0;
m68k_dreg (&context->regs, 1) = wssigs;
sigs = CallLib (context, get_long (4), -0x132) & wssigs; // SetSignal()
if (sigs) {
DEBUG_LOG ("WaitSelect preempted by signals 0x%08x\n", sigs & wssigs);
put_long (sigmp, sigs);
// Check for zero address -> otherwise WinUAE crashes
if (readfds) fd_zero (readfds, nfds);
if (writefds) fd_zero (writefds, nfds);
if (exceptfds) fd_zero (exceptfds, nfds);
sb->resultval = 0;
bsdsocklib_seterrno (sb, 0);
return;
}
}
if (nfds == 0) {
/* No sockets - Just wait on signals */
m68k_dreg (&context->regs, 0) = wssigs;
sigs = CallLib (context, get_long (4), -0x13e); // Wait()
if (sigmp)
put_long (sigmp, sigs & wssigs);
if (readfds) fd_zero (readfds, nfds);
if (writefds) fd_zero (writefds, nfds);
if (exceptfds) fd_zero (exceptfds, nfds);
sb->resultval = 0;
return;
}
sb->nfds = nfds;
sb->sets [0] = readfds;
sb->sets [1] = writefds;
sb->sets [2] = exceptfds;
sb->timeout = timeout;
sb->sigmp = wssigs;
sb->action = 5;
uae_sem_post (&sb->sem);
m68k_dreg (&context->regs, 0) = (((uae_u32)1) << sb->signal) | sb->eintrsigs | wssigs;
sigs = CallLib (context, get_long (4), -0x13e); // Wait()
if (sigmp)
put_long (sigmp, sigs & (sb->eintrsigs | wssigs));
if (sigs & wssigs) {
/* Received the signals we were waiting on */
DEBUG_LOG ("WaitSelect: got signal(s) %lx\n", sigs);
if (!(sigs & (((uae_u32)1) << sb->signal))) {
sockabort (sb);
WAITSIGNAL;
}
sb->resultval = 0;
if (readfds) fd_zero (readfds, nfds);
if (writefds) fd_zero (writefds, nfds);
if (exceptfds) fd_zero (exceptfds, nfds);
bsdsocklib_seterrno (sb, 0);
} else if (sigs & sb->eintrsigs) {
/* Wait select was interrupted */
DEBUG_LOG ("WaitSelect: interrupted\n");
if (!(sigs & (((uae_u32)1) << sb->signal))) {
sockabort (sb);
WAITSIGNAL;
}
sb->resultval = -1;
bsdsocklib_seterrno (sb, mapErrno (EINTR));
}
clearsockabort(sb);
}
void host_accept (TrapContext *context, SB, uae_u32 sd, uae_u32 name, uae_u32 namelen)
{
sb->s = getsock (sb, sd + 1);
if (sb->s == -1) {
sb->resultval = -1;
bsdsocklib_seterrno (sb, 9); /* EBADF */
return;
}
DEBUG_LOG ("accept(%d, %lx, %lx)\n", sb->s, name, namelen);
sb->a_addr = name;
sb->a_addrlen = namelen;
sb->action = 6;
sb->len = sd;
uae_sem_post (&sb->sem);
WAITSIGNAL;
DEBUG_LOG ("Accept returns %d\n", sb->resultval);
}
int host_socket (SB, int af, int type, int protocol)
{
int sd;
int s;
DEBUG_LOG ("socket(%s,%s,%d) -> ",af == AF_INET ? "AF_INET" : "AF_other",
type == SOCK_STREAM ? "SOCK_STREAM" : type == SOCK_DGRAM ?
"SOCK_DGRAM " : "SOCK_RAW", protocol);
if ((s = socket (af, type, protocol)) == -1) {
SETERRNO;
DEBUG_LOG ("failed (%d)\n", sb->sb_errno);
return -1;
} else {
int arg = 1;
sd = getsd (sb, s);
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg));
}
sb->ftable[sd-1] = SF_BLOCKING;
DEBUG_LOG ("socket returns Amiga %d, NativeSide %d\n", sd - 1, s);
return sd - 1;
}
uae_u32 host_bind (SB, uae_u32 sd, uae_u32 name, uae_u32 namelen)
{
uae_u32 success = 0;
struct sockaddr_in addr;
int len = sizeof (struct sockaddr_in);
int s;
s = getsock (sb, sd + 1);
if (s == -1) {
sb->resultval = -1;
bsdsocklib_seterrno (sb, 9); /* EBADF */
return -1;
}
DEBUG_LOG ("bind(%d[%d],0x%lx,%d) -> ", sd, s, name, namelen);
copysockaddr_a2n (&addr, name, namelen);
printSockAddr (&addr);
if ((success = bind (s, (struct sockaddr *)&addr, len)) != 0) {
SETERRNO;
DEBUG_LOG ("failed (%d)\n",sb->sb_errno);
} else {
DEBUG_LOG ("OK\n");
}
return success;
}
uae_u32 host_listen (SB, uae_u32 sd, uae_u32 backlog)
{
int s;
uae_u32 success = -1;
DEBUG_LOG ("listen(%d,%d) -> ", sd, backlog);
s = getsock (sb, sd + 1);
if (s == -1) {
bsdsocklib_seterrno (sb, 9);
return -1;
}
if ((success = listen (s, backlog)) != 0) {
SETERRNO;
DEBUG_LOG ("failed (%d)\n", sb->sb_errno);
} else {
DEBUG_LOG ("OK\n");
}
return success;
}
void host_getprotobyname (TrapContext *context, SB, uae_u32 name)
{
struct protoent *p = getprotobyname ((char *)get_real_address (name));
DEBUG_LOG ("Getprotobyname(%s)=%lx\n", get_real_address (name), p);
if (p == NULL) {
SETERRNO;
return;
}
copyProtoent (context, sb, p);
TRACE (("OK (%s, %d)\n", p->p_name, p->p_proto));
}
void host_getprotobynumber (TrapContext *context, SB, uae_u32 number)
{
struct protoent *p = getprotobynumber(number);
DEBUG_LOG("getprotobynumber(%d)=%lx\n", number, p);
if (p == NULL) {
SETERRNO;
return;
}
copyProtoent (context, sb, p);
TRACE (("OK (%s, %d)\n", p->p_name, p->p_proto));
}
void host_getservbynameport (TrapContext *context, SB, uae_u32 name, uae_u32 proto, uae_u32 type)
{
struct servent *s = (type) ?
getservbyport (name, (char *)get_real_address (proto)) :
getservbyname ((char *)get_real_address (name), (char *)get_real_address (proto));
size_t size = 20;
int numaliases = 0;
uae_u32 aptr;
int i;
if (type) {
DEBUG_LOG("Getservbyport(%d, %s) = %lx\n", name, get_real_address(proto), s);
} else {
DEBUG_LOG("Getservbyname(%s, %s) = %lx\n", get_real_address(name), get_real_address(proto), s);
}
if (s == NULL) {
SETERRNO;
return;
}
// compute total size of servent
if (s->s_name != NULL)
size += strlen (s->s_name) + 1;
if (s->s_proto != NULL)
size += strlen (s->s_proto) + 1;
if (s->s_aliases != NULL)
while (s->s_aliases[numaliases])
size += strlen (s->s_aliases[numaliases++]) + 5;
if (sb->servent) {
uae_FreeMem (context, sb->servent, sb->serventsize);
}
sb->servent = uae_AllocMem (context, size, 0);
if (!sb->servent) {
write_log ("BSDSOCK: WARNING - getservby%s() ran out of Amiga memory (couldn't allocate %d bytes)\n",type ? "port" : "name", size);
bsdsocklib_seterrno (sb, 12); // ENOMEM
return;
}
sb->serventsize = size;
aptr = sb->servent + 20 + numaliases * 4;
// transfer servent to Amiga memory
put_long (sb->servent + 4, sb->servent + 16);
put_long (sb->servent + 8, (unsigned short)htons (s->s_port));
for (i = 0; i < numaliases; i++)
put_long (sb->servent + 16 + i * 4, addstr (&aptr, s->s_aliases[i]));
put_long (sb->servent + 16 + numaliases * 4, 0);
put_long (sb->servent, aptr);
addstr (&aptr, s->s_name);
put_long (sb->servent + 12, aptr);
addstr (&aptr, s->s_proto);
TRACE (("OK (%s, %d)\n", s->s_name, (unsigned short)htons (s->s_port)));
bsdsocklib_seterrno (sb,0);
}
int host_sbinit (TrapContext *context, SB)
{
if (pipe (sb->sockabort) < 0) {
return 0;
}
if (fcntl (sb->sockabort[0], F_SETFL, O_NONBLOCK) < 0) {
write_log ("Set nonblock failed %d\n", errno);
}
if (uae_sem_init (&sb->sem, 0, 0)) {
write_log ("BSDSOCK: Failed to create semaphore.\n");
close (sb->sockabort[0]);
close (sb->sockabort[1]);
return 0;
}
/* Alloc hostent buffer */
sb->hostent = uae_AllocMem (context, 1024, 0);
sb->hostentsize = 1024;
/* @@@ The thread should be PTHREAD_CREATE_DETACHED */
if (uae_start_thread (bsdlib_threadfunc, (void *)sb, &sb->thread)) {
write_log ("BSDSOCK: Failed to create thread.\n");
uae_sem_destroy (&sb->sem);
close (sb->sockabort[0]);
close (sb->sockabort[1]);
return 0;
}
return 1;
}
void host_sbcleanup (SB)
{
int i;
uae_thread_id thread = sb->thread;
close (sb->sockabort[0]);
close (sb->sockabort[1]);
for (i = 0; i < sb->dtablesize; i++) {
if (sb->dtable[i] != -1) {
close(sb->dtable[i]);
}
}
sb->action = 0;
uae_sem_post (&sb->sem); /* destroy happens on socket thread */
/* We need to join with the socket thread to allow the thread to die
* and clean up resources when the underlying thread layer is pthreads.
* Ideally, this shouldn't be necessary, but, for example, when SDL uses
* pthreads, it always creates joinable threads - and we can't do anything
* about that. */
uae_wait_thread (thread);
}
void host_sbreset (void)
{
/* TODO */
}
uae_u32 host_Inet_NtoA (TrapContext *context, SB, uae_u32 in)
{
char *addr;
struct in_addr ina;
uae_u32 buf;
*(uae_u32 *)&ina = htonl (in);
TRACE (("Inet_NtoA(%lx) -> ", in));
if ((addr = inet_ntoa(ina)) != NULL) {
buf = m68k_areg (&context->regs, 6) + offsetof (struct UAEBSDBase, scratchbuf);
strncpyha (buf, addr, SCRATCHBUFSIZE);
TRACE (("%s\n", addr));
return buf;
} else
SETERRNO;
TRACE (("failed (%d)\n", sb->sb_errno));
return 0;
}
uae_u32 host_inet_addr (uae_u32 cp)
{
uae_u32 addr;
char *cp_rp;
cp_rp = (char *)get_real_address (cp);
addr = htonl (inet_addr (cp_rp));
TRACE (("inet_addr(%s) -> 0x%08lx\n", cp_rp, addr));
return addr;
}
uae_u32 host_shutdown (SB, uae_u32 sd, uae_u32 how)
{
SOCKET s;
TRACE (("shutdown(%d,%d) -> ", sd, how));
s = getsock (sb, sd + 1);
if (s != -1) {
if (shutdown (s, how)) {
SETERRNO;
TRACE (("failed (%d)\n", sb->sb_errno));
} else {
TRACE (("OK\n"));
return 0;
}
}
return -1;
}
int host_dup2socket (SB, int fd1, int fd2) {
int s1, s2;
TRACE (("dup2socket(%d,%d) -> ", fd1, fd2));
fd1++;
s1 = getsock (sb, fd1);
if (s1 != -1) {
if (fd2 != -1) {
if ((unsigned int) (fd2) >= (unsigned int) sb->dtablesize) {
TRACE (("Bad file descriptor (%d)\n", fd2));
bsdsocklib_seterrno (sb, 9); /* EBADF */
}
fd2++;
s2 = getsock (sb, fd2);
if (s2 != -1) {
close (s2);
}
setsd (sb, fd2, dup (s1));
TRACE (("0(%d)\n", getsock (sb, fd2)));
return 0;
} else {
fd2 = getsd (sb, 1);
if (fd2 != -1) {
setsd (sb, fd2, dup (s1));
TRACE (("%d(%d)\n", fd2, getsock (sb, fd2)));
return (fd2 - 1);
} else {
TRACE(("-1\n"));
return -1;
}
}
}
TRACE (("-1\n"));
return -1;
}
uae_u32 host_getsockopt (SB, uae_u32 sd, uae_u32 level, uae_u32 optname,
uae_u32 optval, uae_u32 optlen)
{
socklen_t len = 0;
int r;
int s;
int nativelevel = mapsockoptlevel(level);
int nativeoptname = mapsockoptname(nativelevel, optname);
void *buf = NULL;
s = getsock (sb, sd + 1);
if (s == -1) {
bsdsocklib_seterrno(sb, 9); /* EBADF */
return -1;
}
if (optlen) {
len = get_long (optlen);
buf = malloc(len);
if (buf == NULL) {
return -1;
}
}
r = getsockopt (s, nativelevel, nativeoptname,
optval ? buf : NULL, optlen ? &len : NULL);
if (optlen)
put_long (optlen, len);
SETERRNO;
DEBUG_LOG ("getsockopt: sock AmigaSide %d NativeSide %d, level %d, 'name' %x(%d), len %d -> %d, %d\n",
sd, s, level, optname, nativeoptname, len, r, errno);
if (optval) {
if (r == 0) {
mapsockoptreturn(nativelevel, nativeoptname, optval, buf);
}
}
if (buf != NULL)
free(buf);
return r;
}
uae_u32 host_IoctlSocket (SB, uae_u32 sd, uae_u32 request, uae_u32 arg)
{
int sock = getsock (sb, sd + 1);
int r, argval = get_long (arg);
long flags;
if (sock == -1) {
sb->resultval = -1;
bsdsocklib_seterrno (sb, 9); /* EBADF */
return -1;
}
if ((flags = fcntl (sock, F_GETFL)) == -1) {
SETERRNO;
return -1;
}
DEBUG_LOG ("Ioctl code is %lx, flags are %d\n", request, flags);
switch (request) {
case 0x8004667B: /* FIOGETOWN */
sb->ownertask = get_long (arg);
return 0;
case 0x8004667C: /* FIOSETOWN */
put_long (arg,sb->ownertask);
return 0;
case 0x8004667D: /* FIOASYNC */
# ifdef O_ASYNC
r = fcntl (sock, F_SETFL, argval ? flags | O_ASYNC : flags & ~O_ASYNC);
return r;
# else
/* O_ASYNC is only available on Linux and BSD systems */
return fcntl (sock, F_GETFL);
# endif
case 0x8004667E: /* FIONBIO */
r = fcntl (sock, F_SETFL, argval ?
flags | O_NONBLOCK : flags & ~O_NONBLOCK);
if (argval) {
DEBUG_LOG ("nonblocking\n");
sb->ftable[sd-1] &= ~SF_BLOCKING;
} else {
DEBUG_LOG ("blocking\n");
sb->ftable[sd-1] |= SF_BLOCKING;
}
return r;
case 0x4004667F: /* FIONREAD */
r = ioctl (sock, FIONREAD, &flags);
if (r >= 0) {
put_long(arg, flags);
}
return r;
} /* end switch */
bsdsocklib_seterrno (sb, EINVAL);
return -1;
}
int host_CloseSocket (SB, int sd)
{
int s = getsock (sb, sd + 1);
int retval;
if (s == -1) {
bsdsocklib_seterrno (sb, 9); /* EBADF */
return -1;
}
/*
if (checksd (sb, sd + 1) == 1) {
return 0;
}
*/
DEBUG_LOG ("CloseSocket Amiga: %d, NativeSide %d\n", sd, s);
retval = close (s);
SETERRNO;
releasesock (sb, sd + 1);
return retval;
}
void host_closesocketquick (int s)
{
struct linger l;
l.l_onoff = 0;
l.l_linger = 0;
if(s != -1) {
setsockopt (s, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
close (s);
}
}
uae_u32 host_gethostname (uae_u32 name, uae_u32 namelen)
{
return gethostname ((char *)get_real_address (name), namelen);
}
int init_socket_layer(void)
{
if (uae_sem_init(&sem_queue, 0, 1) < 0) {
DEBUG_LOG("Can't create sem %d\n", errno);
return 0;
}
return 1;
}
void clearsockabort (SB)
{
int chr;
int num;
while ((num = read (sb->sockabort[0], &chr, sizeof(chr))) >= 0) {
DEBUG_LOG ("Sockabort got %d bytes\n", num);
;
}
}
void sockabort (SB)
{
int chr = 1;
DEBUG_LOG ("Sock abort!!\n");
write (sb->sockabort[1], &chr, sizeof (chr));
}
void locksigqueue (void)
{
uae_sem_wait(&sem_queue);
}
void unlocksigqueue (void)
{
uae_sem_post(&sem_queue);
}
#endif