/* * UAE - The Un*x Amiga Emulator * * bsdsocket.library emulation - Unix * * Copyright 2000-2001 Carl Drougge * 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 #include #include #include #ifdef HAVE_SYS_FILIO_H # include #endif #include #include #include #include #include #include //#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