wine/server/sock.c
Alexandre Julliard 9caa71eef4 Redesign of the server communication protocol to allow arbitrary sized
data to be exchanged.
Split request and reply structures to make backwards compatibility
easier.
Moved many console functions to dlls/kernel, added code page support,
changed a few requests to behave properly with the new protocol.
2001-11-30 18:46:42 +00:00

594 lines
19 KiB
C

/*
* Server-side socket management
*
* Copyright (C) 1999 Marcus Meissner, Ove Kåven
*
* FIXME: we use read|write access in all cases. Shouldn't we depend that
* on the access of the current handle?
*/
#include "config.h"
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#ifdef HAVE_SYS_ERRNO_H
# include <sys/errno.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#include <sys/ioctl.h>
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#include <time.h>
#include <unistd.h>
#include "winerror.h"
#include "winbase.h"
#include "process.h"
#include "handle.h"
#include "thread.h"
#include "request.h"
/* To avoid conflicts with the Unix socket headers. Plus we only need a few
* macros anyway.
*/
#define USE_WS_PREFIX
#include "winsock2.h"
struct sock
{
struct object obj; /* object header */
unsigned int state; /* status bits */
unsigned int mask; /* event mask */
unsigned int hmask; /* held (blocked) events */
unsigned int pmask; /* pending events */
struct event *event; /* event object */
int errors[FD_MAX_EVENTS]; /* event errors */
};
static void sock_dump( struct object *obj, int verbose );
static int sock_signaled( struct object *obj, struct thread *thread );
static int sock_get_poll_events( struct object *obj );
static void sock_poll_event( struct object *obj, int event );
static int sock_get_fd( struct object *obj );
static int sock_get_info( struct object *obj, struct get_file_info_reply *reply );
static void sock_destroy( struct object *obj );
static int sock_get_error( int err );
static void sock_set_error(void);
static const struct object_ops sock_ops =
{
sizeof(struct sock), /* size */
sock_dump, /* dump */
add_queue, /* add_queue */
remove_queue, /* remove_queue */
sock_signaled, /* signaled */
no_satisfied, /* satisfied */
sock_get_poll_events, /* get_poll_events */
sock_poll_event, /* poll_event */
sock_get_fd, /* get_fd */
no_flush, /* flush */
sock_get_info, /* get_file_info */
sock_destroy /* destroy */
};
static void sock_reselect( struct sock *sock )
{
int ev = sock_get_poll_events( &sock->obj );
struct pollfd pfd;
if (debug_level)
fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->obj.fd, ev);
if (sock->obj.select == -1) {
/* previously unconnected socket, is this reselect supposed to connect it? */
if (!(sock->state & ~FD_WINE_NONBLOCKING)) return;
/* ok, it is, attach it to the wineserver's main poll loop */
add_select_user( &sock->obj );
}
/* update condition mask */
set_select_events( &sock->obj, ev );
/* check whether condition is satisfied already */
pfd.fd = sock->obj.fd;
pfd.events = ev;
pfd.revents = 0;
poll( &pfd, 1, 0 );
if (pfd.revents)
sock_poll_event( &sock->obj, pfd.revents);
}
inline static int sock_error(int s)
{
unsigned int optval = 0, optlen;
optlen = sizeof(optval);
getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen);
return optval ? sock_get_error(optval) : 0;
}
static void sock_poll_event( struct object *obj, int event )
{
struct sock *sock = (struct sock *)obj;
unsigned int emask;
assert( sock->obj.ops == &sock_ops );
if (debug_level)
fprintf(stderr, "socket %d select event: %x\n", sock->obj.fd, event);
if (sock->state & FD_CONNECT)
{
/* connecting */
if (event & POLLOUT)
{
/* we got connected */
sock->state |= FD_WINE_CONNECTED|FD_READ|FD_WRITE;
sock->state &= ~FD_CONNECT;
sock->pmask |= FD_CONNECT;
sock->errors[FD_CONNECT_BIT] = 0;
if (debug_level)
fprintf(stderr, "socket %d connection success\n", sock->obj.fd);
}
else if (event & (POLLERR|POLLHUP))
{
/* we didn't get connected? */
sock->state &= ~FD_CONNECT;
sock->pmask |= FD_CONNECT;
sock->errors[FD_CONNECT_BIT] = sock_error( sock->obj.fd );
if (debug_level)
fprintf(stderr, "socket %d connection failure\n", sock->obj.fd);
}
} else
if (sock->state & FD_WINE_LISTENING)
{
/* listening */
if (event & POLLIN)
{
/* incoming connection */
sock->pmask |= FD_ACCEPT;
sock->errors[FD_ACCEPT_BIT] = 0;
sock->hmask |= FD_ACCEPT;
}
else if (event & (POLLERR|POLLHUP))
{
/* failed incoming connection? */
sock->pmask |= FD_ACCEPT;
sock->errors[FD_ACCEPT_BIT] = sock_error( sock->obj.fd );
sock->hmask |= FD_ACCEPT;
}
} else
{
/* normal data flow */
if (event & POLLIN)
{
char dummy;
/* Linux 2.4 doesn't report POLLHUP if only one side of the socket
* has been closed, so we need to check for it explicitly here */
if (!recv( sock->obj.fd, &dummy, 1, MSG_PEEK )) event = POLLHUP;
else
{
/* incoming data */
sock->pmask |= FD_READ;
sock->hmask |= FD_READ;
sock->errors[FD_READ_BIT] = 0;
if (debug_level)
fprintf(stderr, "socket %d is readable\n", sock->obj.fd );
}
}
if (event & POLLOUT)
{
sock->pmask |= FD_WRITE;
sock->hmask |= FD_WRITE;
sock->errors[FD_WRITE_BIT] = 0;
if (debug_level)
fprintf(stderr, "socket %d is writable\n", sock->obj.fd);
}
if (event & POLLPRI)
{
sock->pmask |= FD_OOB;
sock->hmask |= FD_OOB;
sock->errors[FD_OOB_BIT] = 0;
if (debug_level)
fprintf(stderr, "socket %d got OOB data\n", sock->obj.fd);
}
if (((event & POLLERR) || ((event & (POLLIN|POLLHUP)) == POLLHUP))
&& (sock->state & (FD_READ|FD_WRITE))) {
/* socket closing */
sock->errors[FD_CLOSE_BIT] = sock_error( sock->obj.fd );
sock->state &= ~(FD_WINE_CONNECTED|FD_READ|FD_WRITE);
sock->pmask |= FD_CLOSE;
if (debug_level)
fprintf(stderr, "socket %d aborted by error %d\n",
sock->obj.fd, sock->errors[FD_CLOSE_BIT]);
}
}
if (event & (POLLERR|POLLHUP))
set_select_events( &sock->obj, -1 );
else
sock_reselect( sock );
/* wake up anyone waiting for whatever just happened */
emask = sock->pmask & sock->mask;
if (debug_level && emask)
fprintf(stderr, "socket %d pending events: %x\n", sock->obj.fd, emask);
if (emask && sock->event) {
if (debug_level) fprintf(stderr, "signalling event ptr %p\n", sock->event);
set_event(sock->event);
}
/* if anyone is stupid enough to wait on the socket object itself,
* maybe we should wake them up too, just in case? */
wake_up( &sock->obj, 0 );
}
static void sock_dump( struct object *obj, int verbose )
{
struct sock *sock = (struct sock *)obj;
assert( obj->ops == &sock_ops );
printf( "Socket fd=%d, state=%x, mask=%x, pending=%x, held=%x\n",
sock->obj.fd, sock->state,
sock->mask, sock->pmask, sock->hmask );
}
static int sock_signaled( struct object *obj, struct thread *thread )
{
struct sock *sock = (struct sock *)obj;
assert( obj->ops == &sock_ops );
return check_select_events( sock->obj.fd, sock_get_poll_events( &sock->obj ) );
}
static int sock_get_poll_events( struct object *obj )
{
struct sock *sock = (struct sock *)obj;
unsigned int mask = sock->mask & sock->state & ~sock->hmask;
int ev = 0;
assert( obj->ops == &sock_ops );
if (sock->state & FD_CONNECT)
/* connecting, wait for writable */
return POLLOUT;
if (sock->state & FD_WINE_LISTENING)
/* listening, wait for readable */
return (sock->hmask & FD_ACCEPT) ? 0 : POLLIN;
if (mask & FD_READ) ev |= POLLIN | POLLPRI;
if (mask & FD_WRITE) ev |= POLLOUT;
return ev;
}
static int sock_get_fd( struct object *obj )
{
struct sock *sock = (struct sock *)obj;
assert( obj->ops == &sock_ops );
return sock->obj.fd;
}
static int sock_get_info( struct object *obj, struct get_file_info_reply *reply )
{
if (reply)
{
reply->type = FILE_TYPE_PIPE;
reply->attr = 0;
reply->access_time = 0;
reply->write_time = 0;
reply->size_high = 0;
reply->size_low = 0;
reply->links = 0;
reply->index_high = 0;
reply->index_low = 0;
reply->serial = 0;
}
return FD_TYPE_DEFAULT;
}
static void sock_destroy( struct object *obj )
{
struct sock *sock = (struct sock *)obj;
assert( obj->ops == &sock_ops );
/* FIXME: special socket shutdown stuff? */
if (sock->event)
{
/* if the service thread was waiting for the event object,
* we should now signal it, to let the service thread
* object detect that it is now orphaned... */
if (sock->mask & FD_WINE_SERVEVENT)
set_event( sock->event );
/* we're through with it */
release_object( sock->event );
}
}
/* create a new and unconnected socket */
static struct object *create_socket( int family, int type, int protocol )
{
struct sock *sock;
int sockfd;
sockfd = socket( family, type, protocol );
if (debug_level)
fprintf(stderr,"socket(%d,%d,%d)=%d\n",family,type,protocol,sockfd);
if (sockfd == -1) {
sock_set_error();
return NULL;
}
fcntl(sockfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */
if (!(sock = alloc_object( &sock_ops, -1 ))) return NULL;
sock->obj.fd = sockfd;
sock->state = (type != SOCK_STREAM) ? (FD_READ|FD_WRITE) : 0;
sock->mask = 0;
sock->hmask = 0;
sock->pmask = 0;
sock->event = NULL;
sock_reselect( sock );
clear_error();
return &sock->obj;
}
/* accept a socket (creates a new fd) */
static struct object *accept_socket( handle_t handle )
{
struct sock *acceptsock;
struct sock *sock;
int acceptfd;
struct sockaddr saddr;
int slen;
sock=(struct sock*)get_handle_obj(current->process,handle,
GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops);
if (!sock)
return NULL;
/* Try to accept(2). We can't be safe that this an already connected socket
* or that accept() is allowed on it. In those cases we will get -1/errno
* return.
*/
slen = sizeof(saddr);
acceptfd = accept(sock->obj.fd,&saddr,&slen);
if (acceptfd==-1) {
sock_set_error();
release_object( sock );
return NULL;
}
if (!(acceptsock = alloc_object( &sock_ops, -1 )))
{
release_object( sock );
return NULL;
}
/* newly created socket gets the same properties of the listening socket */
fcntl(acceptfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */
acceptsock->obj.fd = acceptfd;
acceptsock->state = FD_WINE_CONNECTED|FD_READ|FD_WRITE;
if (sock->state & FD_WINE_NONBLOCKING)
acceptsock->state |= FD_WINE_NONBLOCKING;
acceptsock->mask = sock->mask;
acceptsock->hmask = 0;
acceptsock->pmask = 0;
acceptsock->event = NULL;
if (sock->event && !(sock->mask & FD_WINE_SERVEVENT))
acceptsock->event = (struct event *)grab_object( sock->event );
sock_reselect( acceptsock );
clear_error();
sock->pmask &= ~FD_ACCEPT;
sock->hmask &= ~FD_ACCEPT;
sock_reselect( sock );
release_object( sock );
return &acceptsock->obj;
}
/* set the last error depending on errno */
static int sock_get_error( int err )
{
switch (err)
{
case EINTR: return WSAEINTR; break;
case EBADF: return WSAEBADF; break;
case EPERM:
case EACCES: return WSAEACCES; break;
case EFAULT: return WSAEFAULT; break;
case EINVAL: return WSAEINVAL; break;
case EMFILE: return WSAEMFILE; break;
case EWOULDBLOCK: return WSAEWOULDBLOCK; break;
case EINPROGRESS: return WSAEINPROGRESS; break;
case EALREADY: return WSAEALREADY; break;
case ENOTSOCK: return WSAENOTSOCK; break;
case EDESTADDRREQ: return WSAEDESTADDRREQ; break;
case EMSGSIZE: return WSAEMSGSIZE; break;
case EPROTOTYPE: return WSAEPROTOTYPE; break;
case ENOPROTOOPT: return WSAENOPROTOOPT; break;
case EPROTONOSUPPORT: return WSAEPROTONOSUPPORT; break;
case ESOCKTNOSUPPORT: return WSAESOCKTNOSUPPORT; break;
case EOPNOTSUPP: return WSAEOPNOTSUPP; break;
case EPFNOSUPPORT: return WSAEPFNOSUPPORT; break;
case EAFNOSUPPORT: return WSAEAFNOSUPPORT; break;
case EADDRINUSE: return WSAEADDRINUSE; break;
case EADDRNOTAVAIL: return WSAEADDRNOTAVAIL; break;
case ENETDOWN: return WSAENETDOWN; break;
case ENETUNREACH: return WSAENETUNREACH; break;
case ENETRESET: return WSAENETRESET; break;
case ECONNABORTED: return WSAECONNABORTED; break;
case EPIPE:
case ECONNRESET: return WSAECONNRESET; break;
case ENOBUFS: return WSAENOBUFS; break;
case EISCONN: return WSAEISCONN; break;
case ENOTCONN: return WSAENOTCONN; break;
case ESHUTDOWN: return WSAESHUTDOWN; break;
case ETOOMANYREFS: return WSAETOOMANYREFS; break;
case ETIMEDOUT: return WSAETIMEDOUT; break;
case ECONNREFUSED: return WSAECONNREFUSED; break;
case ELOOP: return WSAELOOP; break;
case ENAMETOOLONG: return WSAENAMETOOLONG; break;
case EHOSTDOWN: return WSAEHOSTDOWN; break;
case EHOSTUNREACH: return WSAEHOSTUNREACH; break;
case ENOTEMPTY: return WSAENOTEMPTY; break;
#ifdef EPROCLIM
case EPROCLIM: return WSAEPROCLIM; break;
#endif
#ifdef EUSERS
case EUSERS: return WSAEUSERS; break;
#endif
#ifdef EDQUOT
case EDQUOT: return WSAEDQUOT; break;
#endif
#ifdef ESTALE
case ESTALE: return WSAESTALE; break;
#endif
#ifdef EREMOTE
case EREMOTE: return WSAEREMOTE; break;
#endif
default: errno=err; perror("sock_set_error"); return ERROR_UNKNOWN; break;
}
}
/* set the last error depending on errno */
static void sock_set_error(void)
{
set_error( sock_get_error( errno ) );
}
/* create a socket */
DECL_HANDLER(create_socket)
{
struct object *obj;
reply->handle = 0;
if ((obj = create_socket( req->family, req->type, req->protocol )) != NULL)
{
reply->handle = alloc_handle( current->process, obj, req->access, req->inherit );
release_object( obj );
}
}
/* accept a socket */
DECL_HANDLER(accept_socket)
{
struct object *obj;
reply->handle = 0;
if ((obj = accept_socket( req->lhandle )) != NULL)
{
reply->handle = alloc_handle( current->process, obj, req->access, req->inherit );
release_object( obj );
}
}
/* set socket event parameters */
DECL_HANDLER(set_socket_event)
{
struct sock *sock;
struct event *oevent;
unsigned int omask;
sock=(struct sock*)get_handle_obj(current->process,req->handle,GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops);
if (!sock)
return;
oevent = sock->event;
omask = sock->mask;
sock->mask = req->mask;
sock->event = get_event_obj( current->process, req->event, EVENT_MODIFY_STATE );
if (debug_level && sock->event) fprintf(stderr, "event ptr: %p\n", sock->event);
sock_reselect( sock );
if (sock->mask)
sock->state |= FD_WINE_NONBLOCKING;
/* if a network event is pending, signal the event object
it is possible that FD_CONNECT or FD_ACCEPT network events has happened
before a WSAEventSelect() was done on it.
(when dealing with Asynchronous socket) */
if (sock->pmask & sock->mask)
set_event(sock->event);
if (oevent)
{
if ((oevent != sock->event) && (omask & FD_WINE_SERVEVENT))
/* if the service thread was waiting for the old event object,
* we should now signal it, to let the service thread
* object detect that it is now orphaned... */
set_event( oevent );
/* we're through with it */
release_object( oevent );
}
release_object( &sock->obj );
}
/* get socket event parameters */
DECL_HANDLER(get_socket_event)
{
struct sock *sock;
sock=(struct sock*)get_handle_obj(current->process,req->handle,GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops);
if (!sock)
{
reply->mask = 0;
reply->pmask = 0;
reply->state = 0;
set_error( WSAENOTSOCK );
return;
}
reply->mask = sock->mask;
reply->pmask = sock->pmask;
reply->state = sock->state;
set_reply_data( sock->errors, min( get_reply_max_size(), sizeof(sock->errors) ));
if (req->service)
{
handle_t s_event = req->s_event;
if (s_event)
{
struct event *sevent = get_event_obj(current->process, req->s_event, 0);
if (sevent == sock->event) s_event = 0;
release_object( sevent );
}
if (!s_event)
{
if (req->c_event)
{
struct event *cevent = get_event_obj(current->process, req->c_event, EVENT_MODIFY_STATE);
reset_event( cevent );
release_object( cevent );
}
sock->pmask = 0;
sock_reselect( sock );
}
else set_error(WSAEINVAL);
}
release_object( &sock->obj );
}
/* re-enable pending socket events */
DECL_HANDLER(enable_socket_event)
{
struct sock *sock;
sock=(struct sock*)get_handle_obj(current->process,req->handle,GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops);
if (!sock)
return;
sock->pmask &= ~req->mask; /* is this safe? */
sock->hmask &= ~req->mask;
sock->state |= req->sstate;
sock->state &= ~req->cstate;
sock_reselect( sock );
/* service trigger */
if (req->mask & FD_WINE_SERVEVENT)
{
sock->pmask |= FD_WINE_SERVEVENT;
if (sock->event) {
if (debug_level) fprintf(stderr, "signalling service event ptr %p\n", sock->event);
set_event(sock->event);
}
}
release_object( &sock->obj );
}