mirror of
https://github.com/Oibaf66/uae-wii.git
synced 2024-11-25 12:06:55 +01:00
1717 lines
39 KiB
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
|