/* * 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 #include #include #include #include #include #ifdef HAVE_SYS_ERRNO_H # include #endif #include #include #include #ifdef HAVE_SYS_SOCKET_H # include #endif #include #ifdef HAVE_SYS_FILIO_H # include #endif #include #include #include "winerror.h" #include "winbase.h" #include "winsock2.h" #include "process.h" #include "handle.h" #include "thread.h" #include "request.h" struct sock { struct object obj; /* object header */ struct select_user select; /* select user */ 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_add_queue( struct object *obj, struct wait_queue_entry *entry ); static void sock_remove_queue( struct object *obj, struct wait_queue_entry *entry ); static int sock_signaled( struct object *obj, struct thread *thread ); static int sock_get_fd( struct object *obj ); static void sock_destroy( struct object *obj ); static void sock_set_error(void); static const struct object_ops sock_ops = { sizeof(struct sock), sock_dump, sock_add_queue, sock_remove_queue, sock_signaled, no_satisfied, sock_get_fd, sock_get_fd, no_flush, no_get_file_info, sock_destroy }; static int sock_event( struct sock *sock ) { unsigned int mask = sock->mask & sock->state & ~sock->hmask; int ev = EXCEPT_EVENT; if (sock->state & WS_FD_CONNECT) /* connecting, wait for writable */ return WRITE_EVENT | EXCEPT_EVENT; if (sock->state & WS_FD_LISTENING) /* listening, wait for readable */ return ((sock->hmask & FD_ACCEPT) ? 0 : READ_EVENT) | EXCEPT_EVENT; if (mask & FD_READ) ev |= READ_EVENT; if (mask & FD_WRITE) ev |= WRITE_EVENT; return ev; } static void sock_reselect( struct sock *sock ) { int ev = sock_event( sock ); if (debug_level) fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->select.fd, ev); set_select_events( &sock->select, ev ); } inline static int sock_error(int s) { unsigned int optval, optlen; optlen = sizeof(optval); getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen); return optval; } static void sock_select_event( int event, void *private ) { struct sock *sock = (struct sock *)private; unsigned int emask; assert( sock->obj.ops == &sock_ops ); if (debug_level) fprintf(stderr, "socket %d select event: %x\n", sock->select.fd, event); if (sock->state & WS_FD_CONNECT) { /* connecting */ if (event & WRITE_EVENT) { /* we got connected */ sock->state |= WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE; sock->state &= ~WS_FD_CONNECT; sock->pmask |= FD_CONNECT; sock->errors[FD_CONNECT_BIT] = 0; if (debug_level) fprintf(stderr, "socket %d connection success\n", sock->select.fd); } else if (event & EXCEPT_EVENT) { /* we didn't get connected? */ sock->state &= ~WS_FD_CONNECT; sock->pmask |= FD_CONNECT; sock->errors[FD_CONNECT_BIT] = sock_error( sock->select.fd ); if (debug_level) fprintf(stderr, "socket %d connection failure\n", sock->select.fd); } } else if (sock->state & WS_FD_LISTENING) { /* listening */ if (event & READ_EVENT) { /* incoming connection */ sock->pmask |= FD_ACCEPT; sock->errors[FD_ACCEPT_BIT] = 0; sock->hmask |= FD_ACCEPT; } else if (event & EXCEPT_EVENT) { /* failed incoming connection? */ sock->pmask |= FD_ACCEPT; sock->errors[FD_ACCEPT_BIT] = sock_error( sock->select.fd ); sock->hmask |= FD_ACCEPT; } } else { /* normal data flow */ if (event & READ_EVENT) { /* make sure there's data here */ int bytes = 0; ioctl(sock->select.fd, FIONREAD, (char*)&bytes); if (bytes) { /* incoming data */ sock->pmask |= FD_READ; sock->hmask |= FD_READ; sock->errors[FD_READ_BIT] = 0; if (debug_level) fprintf(stderr, "socket %d has %d bytes\n", sock->select.fd, bytes); } else { /* 0 bytes readable == socket closed cleanly */ sock->state &= ~(WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE); sock->pmask |= FD_CLOSE; sock->errors[FD_CLOSE_BIT] = 0; if (debug_level) fprintf(stderr, "socket %d is closing\n", sock->select.fd); } } if (event & WRITE_EVENT) { 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->select.fd); } if (event & EXCEPT_EVENT) { sock->errors[FD_CLOSE_BIT] = sock_error( sock->select.fd ); if (sock->errors[FD_CLOSE_BIT]) { /* we got an error, socket closing? */ sock->state &= ~(WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE); sock->pmask |= FD_CLOSE; if (debug_level) fprintf(stderr, "socket %d aborted by error %d\n", sock->select.fd, sock->errors[FD_CLOSE_BIT]); } else { /* no error, OOB data? */ sock->pmask |= FD_OOB; sock->hmask |= FD_OOB; if (debug_level) fprintf(stderr, "socket %d got OOB data\n", sock->select.fd); } } } 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->select.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->select.fd, sock->state, sock->mask, sock->pmask, sock->hmask ); } static int sock_add_queue( struct object *obj, struct wait_queue_entry *entry ) { /* struct sock *sock = (struct sock *)obj; */ assert( obj->ops == &sock_ops ); add_queue( obj, entry ); return 1; } static void sock_remove_queue( struct object *obj, struct wait_queue_entry *entry ) { /* struct sock *sock = (struct sock *)grab_object(obj); */ assert( obj->ops == &sock_ops ); remove_queue( obj, entry ); /* release_object( obj ); */ } 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->select, sock_event( sock ) ); } static int sock_get_fd( struct object *obj ) { struct sock *sock = (struct sock *)obj; int fd; assert( obj->ops == &sock_ops ); fd = dup( sock->select.fd ); if (fd==-1) sock_set_error(); return fd; } static void sock_destroy( struct object *obj ) { struct sock *sock = (struct sock *)obj; assert( obj->ops == &sock_ops ); unregister_select_user( &sock->select ); /* FIXME: special socket shutdown stuff? */ close( sock->select.fd ); 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 & WS_FD_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 ))) { close( sockfd ); return NULL; } sock->select.fd = sockfd; sock->select.func = sock_select_event; sock->select.private = sock; sock->state = (type!=SOCK_STREAM) ? WS_FD_READ|WS_FD_WRITE : 0; sock->mask = 0; sock->hmask = 0; sock->pmask = 0; sock->event = NULL; register_select_user( &sock->select ); sock_reselect( sock ); clear_error(); return &sock->obj; } /* accept a socket (creates a new fd) */ static struct object *accept_socket( int 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->select.fd,&saddr,&slen); if (acceptfd==-1) { sock_set_error(); release_object( sock ); return NULL; } if (!(acceptsock = alloc_object( &sock_ops ))) { close( acceptfd ); release_object( sock ); return NULL; } acceptsock->select.fd = acceptfd; acceptsock->select.func = sock_select_event; acceptsock->select.private = acceptsock; acceptsock->state = WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE; acceptsock->mask = sock->mask; acceptsock->hmask = 0; acceptsock->pmask = 0; acceptsock->event = (struct event *)grab_object( sock->event ); register_select_user( &acceptsock->select ); sock_reselect( acceptsock ); clear_error(); sock->pmask &= ~FD_ACCEPT; sock->hmask &= ~FD_ACCEPT; release_object( sock ); return &acceptsock->obj; } /* set the last error depending on errno */ static void sock_set_error(void) { switch (errno) { case EINTR: set_error(WSAEINTR);break; case EBADF: set_error(WSAEBADF);break; case EPERM: case EACCES: set_error(WSAEACCES);break; case EFAULT: set_error(WSAEFAULT);break; case EINVAL: set_error(WSAEINVAL);break; case EMFILE: set_error(WSAEMFILE);break; case EWOULDBLOCK: set_error(WSAEWOULDBLOCK);break; case EINPROGRESS: set_error(WSAEINPROGRESS);break; case EALREADY: set_error(WSAEALREADY);break; case ENOTSOCK: set_error(WSAENOTSOCK);break; case EDESTADDRREQ: set_error(WSAEDESTADDRREQ);break; case EMSGSIZE: set_error(WSAEMSGSIZE);break; case EPROTOTYPE: set_error(WSAEPROTOTYPE);break; case ENOPROTOOPT: set_error(WSAENOPROTOOPT);break; case EPROTONOSUPPORT: set_error(WSAEPROTONOSUPPORT);break; case ESOCKTNOSUPPORT: set_error(WSAESOCKTNOSUPPORT);break; case EOPNOTSUPP: set_error(WSAEOPNOTSUPP);break; case EPFNOSUPPORT: set_error(WSAEPFNOSUPPORT);break; case EAFNOSUPPORT: set_error(WSAEAFNOSUPPORT);break; case EADDRINUSE: set_error(WSAEADDRINUSE);break; case EADDRNOTAVAIL: set_error(WSAEADDRNOTAVAIL);break; case ENETDOWN: set_error(WSAENETDOWN);break; case ENETUNREACH: set_error(WSAENETUNREACH);break; case ENETRESET: set_error(WSAENETRESET);break; case ECONNABORTED: set_error(WSAECONNABORTED);break; case EPIPE: case ECONNRESET: set_error(WSAECONNRESET);break; case ENOBUFS: set_error(WSAENOBUFS);break; case EISCONN: set_error(WSAEISCONN);break; case ENOTCONN: set_error(WSAENOTCONN);break; case ESHUTDOWN: set_error(WSAESHUTDOWN);break; case ETOOMANYREFS: set_error(WSAETOOMANYREFS);break; case ETIMEDOUT: set_error(WSAETIMEDOUT);break; case ECONNREFUSED: set_error(WSAECONNREFUSED);break; case ELOOP: set_error(WSAELOOP);break; case ENAMETOOLONG: set_error(WSAENAMETOOLONG);break; case EHOSTDOWN: set_error(WSAEHOSTDOWN);break; case EHOSTUNREACH: set_error(WSAEHOSTUNREACH);break; case ENOTEMPTY: set_error(WSAENOTEMPTY);break; #ifdef EPROCLIM case EPROCLIM: set_error(WSAEPROCLIM);break; #endif #ifdef EUSERS case EUSERS: set_error(WSAEUSERS);break; #endif #ifdef EDQUOT case EDQUOT: set_error(WSAEDQUOT);break; #endif #ifdef ESTALE case ESTALE: set_error(WSAESTALE);break; #endif #ifdef EREMOTE case EREMOTE: set_error(WSAEREMOTE);break; #endif default: perror("sock_set_error"); set_error( ERROR_UNKNOWN ); break; } } /* create a socket */ DECL_HANDLER(create_socket) { struct object *obj; int s = -1; if ((obj = create_socket( req->family, req->type, req->protocol )) != NULL) { s = alloc_handle( current->process, obj, req->access, req->inherit ); release_object( obj ); } req->handle = s; } /* accept a socket */ DECL_HANDLER(accept_socket) { struct object *obj; int s = -1; if ((obj = accept_socket( req->lhandle )) != NULL) { s = alloc_handle( current->process, obj, req->access, req->inherit ); release_object( obj ); } req->handle = s; } /* 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 |= WS_FD_NONBLOCKING; if (oevent) { if ((oevent != sock->event) && (omask & WS_FD_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) { req->mask = 0; req->pmask = 0; req->state = 0; set_error(WSAENOTSOCK); return; } req->mask = sock->mask; req->pmask = sock->pmask; req->state = sock->state; memcpy(req->errors, sock->errors, sizeof(sock->errors)); clear_error(); if (req->service) { if (req->s_event) { struct event *sevent = get_event_obj(current->process, req->s_event, 0); if (sevent == sock->event) req->s_event = 0; release_object( sevent ); } if (!req->s_event) { 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 ); release_object( &sock->obj ); }