Now a perl script parses our headers and generates glue code, export tables, etc. This automates all the macro nonsense and bridge code, and separates out all the stuff you need for binary compat with OS/2, so you can remove it all from the build if you want to just build native libraries that supply the OS/2 API to native apps (like Winelib vs Wine). This makes upkeep details less nasty, and easier to build native binaries when implementing and improving APIs, so you can debug directly without a lot of drama.
411 lines
12 KiB
C
411 lines
12 KiB
C
/**
|
|
* 2ine; an OS/2 emulator for Linux.
|
|
*
|
|
* Please see the file LICENSE.txt in the source's root directory.
|
|
*
|
|
* This file written by Ryan C. Gordon.
|
|
*/
|
|
|
|
#include "os2native.h"
|
|
#include "tcpip32.h"
|
|
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/select.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
|
|
#include "tcpip32-lx.h"
|
|
|
|
int OS2_sock_init(void)
|
|
{
|
|
TRACE_NATIVE("sock_init()");
|
|
return 1; // always succeeds.
|
|
} // OS2_sock_init
|
|
|
|
// !!! FIXME: currently expects you're doing IPv4
|
|
static void mapOS2SockAddrIn(const OS2_sockaddr *os2addr, struct sockaddr_in *bsdaddr)
|
|
{
|
|
const OS2_sockaddr_in *os2addrin = (const OS2_sockaddr_in *) os2addr;
|
|
memset(bsdaddr, '\0', sizeof (*bsdaddr));
|
|
bsdaddr->sin_family = AF_INET;
|
|
bsdaddr->sin_port = os2addrin->sin_port;
|
|
bsdaddr->sin_addr.s_addr = os2addrin->sin_addr.s_addr;
|
|
} // mapOS2SockAddrIn
|
|
|
|
// !!! FIXME: currently expects you're doing IPv4
|
|
static void mapBSDSockAddrIn(const struct sockaddr_in *bsdaddr, OS2_sockaddr *os2addr)
|
|
{
|
|
OS2_sockaddr_in *os2addrin = (OS2_sockaddr_in *) os2addr;
|
|
memset(os2addrin, '\0', sizeof (*os2addrin));
|
|
os2addrin->sin_family = OS2_AF_INET;
|
|
os2addrin->sin_port = bsdaddr->sin_port;
|
|
os2addrin->sin_addr.s_addr = bsdaddr->sin_addr.s_addr;
|
|
} // mapBSDSockAddrIn
|
|
|
|
static int mapOS2MsgFlags(const int os2flags)
|
|
{
|
|
int bsdflags = 0;
|
|
|
|
#define MAPSOCKFLAGS(f) if (os2flags & OS2_##f) { bsdflags |= f; }
|
|
MAPSOCKFLAGS(MSG_OOB);
|
|
MAPSOCKFLAGS(MSG_PEEK);
|
|
MAPSOCKFLAGS(MSG_DONTROUTE);
|
|
MAPSOCKFLAGS(MSG_EOR);
|
|
MAPSOCKFLAGS(MSG_TRUNC);
|
|
MAPSOCKFLAGS(MSG_CTRUNC);
|
|
MAPSOCKFLAGS(MSG_WAITALL);
|
|
MAPSOCKFLAGS(MSG_DONTWAIT);
|
|
#undef MAPSOCKFLAGS
|
|
|
|
// !!! FIXME: no such flag on Linux
|
|
//MAPSOCKFLAGS(MSG_FULLREAD);
|
|
|
|
return bsdflags;
|
|
} // mapOS2MsgFlags
|
|
|
|
int OS2_recv(int sock, void *buf, size_t len, int os2flags)
|
|
{
|
|
TRACE_NATIVE("recv(%d, %p, %d, %d)", sock, buf, (int) len, os2flags);
|
|
FIXME("this should fail if not a socket"); // otherwise you might be a file descriptor...
|
|
return recv(sock, buf, len, mapOS2MsgFlags(os2flags));
|
|
} // OS2_recv
|
|
|
|
int OS2_connect(int sock, const struct OS2_sockaddr *os2addr, int addrlen)
|
|
{
|
|
TRACE_NATIVE("connect(%d, %p, %d)", sock, os2addr, addrlen);
|
|
|
|
FIXME("this should fail if not a socket"); // otherwise you might be a file descriptor...
|
|
|
|
if (os2addr->sa_family != OS2_AF_INET) {
|
|
FIXME("currently demands IPv4 socket");
|
|
return -1;
|
|
}
|
|
|
|
struct sockaddr_in bsdaddr;
|
|
mapOS2SockAddrIn(os2addr, &bsdaddr);
|
|
return connect(sock, &bsdaddr, (socklen_t) sizeof (bsdaddr));
|
|
} // OS2_connect
|
|
|
|
int OS2_shutdown(int sock, int kind)
|
|
{
|
|
TRACE_NATIVE("shutdown(%d, %d)", sock, kind);
|
|
FIXME("this should fail if not a socket"); // otherwise you might be a file descriptor...
|
|
return shutdown(sock, kind);
|
|
} // OS2_shutdown
|
|
|
|
int OS2_socket(int os2family, int os2socktype, int protocol)
|
|
{
|
|
TRACE_NATIVE("socket(%d, %d, %d)", os2family, os2socktype, protocol);
|
|
if (os2family != OS2_AF_INET) {
|
|
FIXME("currently demands IPv4 socket");
|
|
return -1;
|
|
}
|
|
|
|
if (protocol != 0) {
|
|
FIXME("Only protocol 0 supported at the moment");
|
|
return -1;
|
|
}
|
|
|
|
int bsdsocktype;
|
|
if (os2socktype == OS2_SOCK_STREAM) {
|
|
bsdsocktype = SOCK_STREAM;
|
|
} else if (os2socktype == OS2_SOCK_DGRAM) {
|
|
bsdsocktype = SOCK_DGRAM;
|
|
} else {
|
|
FIXME("Only SOCK_STREAM and SOCK_DGRAM supported at the moment");
|
|
return -1;
|
|
}
|
|
|
|
return socket(AF_INET, bsdsocktype, 0);
|
|
} // OS2_socket
|
|
|
|
ssize_t OS2_send(int sock, const void *buf, size_t len, int os2flags)
|
|
{
|
|
TRACE_NATIVE("send(%d, %p, %d, %d)", sock, buf, (int) len, os2flags);
|
|
FIXME("this should fail if not a socket"); // otherwise you might be a file descriptor...
|
|
return send(sock, buf, len, mapOS2MsgFlags(os2flags));
|
|
} // OS2_send
|
|
|
|
int OS2_soclose(int sock)
|
|
{
|
|
TRACE_NATIVE("soclose(%d)", sock);
|
|
FIXME("this should fail if not a socket"); // otherwise you might close a file descriptor...
|
|
return close(sock);
|
|
} // OS2_soclose
|
|
|
|
int OS2_sock_errno(void)
|
|
{
|
|
TRACE_NATIVE("sock_errno()");
|
|
FIXME("this should cache errno when an OS/2 socket function fails");
|
|
return errno;
|
|
} // OS2_sock_errno
|
|
|
|
unsigned long OS2_inet_addr(const char *name)
|
|
{
|
|
TRACE_NATIVE("inet_addr('%s')", name);
|
|
return inet_addr(name);
|
|
} // OS2_inet_addr
|
|
|
|
struct OS2_hostent *OS2_gethostbyname(const char *name)
|
|
{
|
|
TRACE_NATIVE("gethostbyname('%s')", name);
|
|
struct hostent *hent = gethostbyname(name);
|
|
return (OS2_hostent *) hent; // these happen to match up between Linux and OS/2.
|
|
} // OS2_gethostbyname
|
|
|
|
unsigned short OS2_htons(unsigned short val)
|
|
{
|
|
TRACE_NATIVE("htons(%u)", (unsigned int) val);
|
|
return htons(val);
|
|
} // OS2_htons
|
|
|
|
char *OS2_inet_ntoa(struct OS2_in_addr os2inaddr)
|
|
{
|
|
TRACE_NATIVE("inet_ntoa(%u)", (unsigned int) os2inaddr.s_addr);
|
|
struct in_addr bsdaddr;
|
|
bsdaddr.s_addr = os2inaddr.s_addr;
|
|
return inet_ntoa(bsdaddr);
|
|
} // OS2_inet_ntoa
|
|
|
|
struct OS2_servent *OS2_getservbyname(const char *name, const char *proto)
|
|
{
|
|
TRACE_NATIVE("getservbyname('%s', '%s')", name, proto);
|
|
struct servent *sent = getservbyname(name, proto);
|
|
return (OS2_servent *) sent; // these happen to match up between Linux and OS/2.
|
|
} // OS2_getservbyname
|
|
|
|
static void mapOS2FdSet(const OS2_fd_set *os2set, fd_set *bsdset)
|
|
{
|
|
FD_ZERO(bsdset);
|
|
const int total = os2set->fd_count;
|
|
for (int i = 0; i < total; i++) {
|
|
FD_SET(os2set->fd_array[i], bsdset);
|
|
}
|
|
} // mapOS2FdSet
|
|
|
|
int OS2_select(int ndfs, OS2_fd_set *os2readfds, OS2_fd_set *os2writefds, OS2_fd_set *os2errorfds, OS2_timeval *os2timeout)
|
|
{
|
|
TRACE_NATIVE("select(%d, %p, %p, %p, %p)", ndfs, os2readfds, os2writefds, os2errorfds, os2timeout);
|
|
#define MAPFDS(t) \
|
|
fd_set bsd##t##fds; \
|
|
fd_set *t##fds = NULL; \
|
|
if (os2##t##fds) { \
|
|
t##fds = &bsd##t##fds; \
|
|
mapOS2FdSet(os2##t##fds, t##fds); \
|
|
}
|
|
MAPFDS(read);
|
|
MAPFDS(write);
|
|
MAPFDS(error);
|
|
#undef MAPFDS
|
|
|
|
struct timeval bsdtv;
|
|
struct timeval *pbsdtv = NULL;
|
|
if (os2timeout) {
|
|
pbsdtv = &bsdtv;
|
|
bsdtv.tv_sec = os2timeout->tv_sec;
|
|
bsdtv.tv_usec = os2timeout->tv_usec;
|
|
}
|
|
const int rc = select(ndfs, readfds, writefds, errorfds, pbsdtv);
|
|
FIXME("fill in OS/2 fds");
|
|
return rc;
|
|
} // OS2_select
|
|
|
|
int OS2_os2_select(int *socks, int noreads, int nowrites, int noexcept, long timeout)
|
|
{
|
|
TRACE_NATIVE("os2_select(%p, %d, %d, %d, %d)", socks, noreads, nowrites, noexcept, (int) timeout);
|
|
const int nfds = noreads + nowrites + noexcept;
|
|
struct pollfd *fds = (struct pollfd *) alloca(sizeof (struct pollfd) * nfds);
|
|
struct pollfd *origfds = fds;
|
|
for (int i = 0; i < noreads; i++) {
|
|
fds->fd = *(socks++);
|
|
fds->events = POLLIN;
|
|
fds++;
|
|
}
|
|
|
|
for (int i = 0; i < nowrites; i++) {
|
|
fds->fd = *(socks++);
|
|
fds->events = POLLOUT;
|
|
fds++;
|
|
}
|
|
|
|
for (int i = 0; i < noexcept; i++) {
|
|
fds->fd = *(socks++);
|
|
fds->events = POLLERR;
|
|
fds++;
|
|
}
|
|
|
|
return poll(origfds, nfds, timeout * 1000);
|
|
} // OS2_os2_select
|
|
|
|
int OS2_getsockname(int sock, OS2_sockaddr *os2name, int *os2namelen)
|
|
{
|
|
TRACE_NATIVE("getsockname(%d, %p, %p)", sock, os2name, os2namelen);
|
|
|
|
struct sockaddr_in bsdaddr;
|
|
socklen_t namelen = sizeof (bsdaddr);
|
|
const int rc = getsockname(sock, &bsdaddr, &namelen);
|
|
if (rc == -1) {
|
|
return -1;
|
|
} else if (bsdaddr.sin_family != AF_INET) {
|
|
FIXME("this only deals with IPv4 at the moment");
|
|
return -1;
|
|
} else if (*os2namelen < sizeof (OS2_sockaddr_in)) {
|
|
return -1;
|
|
}
|
|
|
|
mapBSDSockAddrIn(&bsdaddr, os2name);
|
|
*os2namelen = sizeof (OS2_sockaddr_in);
|
|
return rc;
|
|
} // OS2_getsockname
|
|
|
|
int OS2_bind(int sock, const OS2_sockaddr *os2name, int os2namelen)
|
|
{
|
|
TRACE_NATIVE("bind(%d, %p, %d)", sock, os2name, os2namelen);
|
|
struct sockaddr_in bsdaddr;
|
|
mapOS2SockAddrIn(os2name, &bsdaddr);
|
|
return bind(sock, (struct sockaddr *) &bsdaddr, sizeof (bsdaddr));
|
|
} // OS2_bind
|
|
|
|
int OS2_listen(int sock, int backlog)
|
|
{
|
|
TRACE_NATIVE("listen(%d, %d)", sock, backlog);
|
|
return listen(sock, backlog);
|
|
} // OS2_listen
|
|
|
|
int OS2_accept(int sock, OS2_sockaddr *os2name, int *os2namelen)
|
|
{
|
|
TRACE_NATIVE("bind(%d, %p, %p)", sock, os2name, os2namelen);
|
|
struct sockaddr_in bsdaddr;
|
|
socklen_t bsdaddrlen = sizeof (bsdaddr);
|
|
const int retval = accept(sock, (struct sockaddr *) &bsdaddr, &bsdaddrlen);
|
|
|
|
if (os2name != NULL) {
|
|
mapBSDSockAddrIn(&bsdaddr, os2name);
|
|
}
|
|
|
|
if (os2namelen != NULL) {
|
|
*os2namelen = sizeof (OS2_sockaddr_in);
|
|
}
|
|
|
|
return retval;
|
|
} // OS2_accept
|
|
|
|
static int mapOS2SocketOption(const int os2name)
|
|
{
|
|
switch (os2name) {
|
|
#define MAPSOOPT(f) case OS2_##f: return f
|
|
MAPSOOPT(SO_DEBUG);
|
|
MAPSOOPT(SO_ACCEPTCONN);
|
|
MAPSOOPT(SO_REUSEADDR);
|
|
MAPSOOPT(SO_KEEPALIVE);
|
|
MAPSOOPT(SO_DONTROUTE);
|
|
MAPSOOPT(SO_BROADCAST);
|
|
//MAPSOOPT(SO_USELOOPBACK);
|
|
MAPSOOPT(SO_LINGER);
|
|
MAPSOOPT(SO_OOBINLINE);
|
|
//MAPSOOPT(SO_L_BROADCAST);
|
|
//MAPSOOPT(SO_RCV_SHUTDOWN);
|
|
//MAPSOOPT(SO_SND_SHUTDOWN);
|
|
MAPSOOPT(SO_REUSEPORT);
|
|
//MAPSOOPT(SO_TTCP);
|
|
MAPSOOPT(SO_SNDBUF);
|
|
MAPSOOPT(SO_RCVBUF);
|
|
MAPSOOPT(SO_SNDLOWAT);
|
|
MAPSOOPT(SO_RCVLOWAT);
|
|
MAPSOOPT(SO_SNDTIMEO);
|
|
MAPSOOPT(SO_RCVTIMEO);
|
|
MAPSOOPT(SO_ERROR);
|
|
MAPSOOPT(SO_TYPE);
|
|
//MAPSOOPT(SO_OPTIONS);
|
|
#undef MAPSOOPT
|
|
default: break;
|
|
}
|
|
|
|
FIXME("unsupported OS/2 socket option");
|
|
return 0;
|
|
} // mapOS2SocketOption
|
|
|
|
int OS2_setsockopt(int sock, int os2level, int os2name, const void *value, int len)
|
|
{
|
|
const int bsdname = mapOS2SocketOption(os2name);
|
|
const int bsdlevel = SOL_SOCKET;
|
|
if (!bsdname) {
|
|
return -1;
|
|
} else if (os2level != OS2_SOL_SOCKET) {
|
|
return -1;
|
|
}
|
|
return setsockopt(sock, bsdlevel, bsdname, value, len);
|
|
} // OS2_setsockopt
|
|
|
|
int OS2_gettimeofday(OS2_timeval *os2tv, OS2_timezone *os2tz)
|
|
{
|
|
struct timeval bsdtv;
|
|
struct timezone bsdtz;
|
|
const int retval = gettimeofday(&bsdtv, &bsdtz);
|
|
|
|
if (os2tv) {
|
|
os2tv->tv_sec = bsdtv.tv_sec;
|
|
os2tv->tv_usec = bsdtv.tv_usec;
|
|
}
|
|
|
|
if (os2tz) {
|
|
os2tz->tz_minuteswest = bsdtz.tz_minuteswest;
|
|
os2tz->tz_dsttime = bsdtz.tz_dsttime;
|
|
}
|
|
|
|
return retval;
|
|
} // OS2_gettimeofday
|
|
|
|
// SOCKS support...
|
|
|
|
int OS2_Rgetsockname(int sock, OS2_sockaddr *os2name, int *os2namelen)
|
|
{
|
|
// !!! FIXME: this doesn't talk to a SOCKS proxy at all.
|
|
TRACE_NATIVE("Rgetsockname(%d, %p, %p)", sock, os2name, os2namelen);
|
|
return OS2_getsockname(sock, os2name, os2namelen);
|
|
} // OS2_Rgetsockname
|
|
|
|
int OS2_Rbind(int sock, OS2_sockaddr *os2name, int os2namelen, OS2_sockaddr *os2remote)
|
|
{
|
|
// !!! FIXME: this doesn't talk to a SOCKS proxy at all.
|
|
TRACE_NATIVE("Rbind(%d, %p, %d, %p)", sock, os2name, os2namelen, os2remote);
|
|
return OS2_bind(sock, os2name, os2namelen);
|
|
} // OS2_Rbind
|
|
|
|
int OS2_Raccept(int sock, OS2_sockaddr *os2name, int *os2namelen)
|
|
{
|
|
// !!! FIXME: this doesn't talk to a SOCKS proxy at all.
|
|
TRACE_NATIVE("Raccept(%d, %p, %p)", sock, os2name, os2namelen);
|
|
return OS2_accept(sock, os2name, os2namelen);
|
|
} // OS2_Raccept
|
|
|
|
int OS2_Rconnect(int sock, const OS2_sockaddr *os2name, int os2namelen)
|
|
{
|
|
// !!! FIXME: this doesn't talk to a SOCKS proxy at all.
|
|
TRACE_NATIVE("Rconnect(%d, %p, %d)", sock, os2name, os2namelen);
|
|
return OS2_connect(sock, os2name, os2namelen);
|
|
} // OS2_Rconnect
|
|
|
|
int OS2_Rlisten(int sock, int backlog)
|
|
{
|
|
// !!! FIXME: this doesn't talk to a SOCKS proxy at all.
|
|
TRACE_NATIVE("Rlisten(%d, %d)", sock, backlog);
|
|
return OS2_listen(sock, backlog);
|
|
} // OS2_Rlisten
|
|
|
|
OS2_hostent *OS2_Rgethostbyname(const char *name)
|
|
{
|
|
// !!! FIXME: this doesn't talk to a SOCKS proxy at all.
|
|
TRACE_NATIVE("Rgethostbyname('%s')", name);
|
|
return OS2_gethostbyname(name);
|
|
} // OS2_Rgethostbyname
|
|
|
|
// end of tcpip32.c ...
|
|
|