| /* |
| * asynchronous winsock services |
| * |
| * (C) 1996 Alex Korobka. |
| * |
| * FIXME: telftp16 (ftp part) stalls on AsyncSelect with FD_ACCEPT. |
| */ |
| #include <unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| #include <sys/msg.h> |
| #include <sys/wait.h> |
| #include <errno.h> |
| |
| extern int h_errno; |
| |
| #include "windows.h" |
| #include "winsock.h" |
| #include "debug.h" |
| |
| #define __WS_ASYNC_DEBUG 0 |
| |
| static int __async_io_max_fd = 0; |
| static fd_set __async_io_fdset; |
| static ws_async_op* __async_op_list = NULL; |
| |
| extern ws_async_ctl async_ctl; |
| extern int async_qid; |
| |
| fd_set fd_read, fd_write, fd_excp; |
| |
| /* ----------------------------------- async/non-blocking I/O */ |
| |
| int WINSOCK_async_io(int fd, int async) |
| { |
| int fd_flags; |
| |
| fcntl(fd, F_SETOWN, getpid()); |
| |
| fd_flags = fcntl(fd, F_GETFL, 0); |
| if (fcntl(fd, F_SETFL, (async)? fd_flags | FASYNC |
| : fd_flags & ~FASYNC ) != -1) return 0; |
| return -1; |
| } |
| |
| int WINSOCK_unblock_io(int fd, int noblock) |
| { |
| int fd_flags; |
| |
| fd_flags = fcntl(fd, F_GETFL, 0); |
| if (fcntl(fd, F_SETFL, (noblock)? fd_flags | O_NONBLOCK |
| : fd_flags & ~O_NONBLOCK ) != -1) return 0; |
| return -1; |
| } |
| |
| int WINSOCK_check_async_op(ws_async_op* p_aop) |
| { |
| ws_async_op* p = __async_op_list; |
| while( p ) if( p == p_aop ) return 1; |
| else p = p->next; |
| return 0; |
| } |
| |
| void WINSOCK_cancel_async_op(HTASK16 hTask) |
| { |
| ws_async_op* p = __async_op_list; |
| while( p ) |
| if(hTask == GetWindowTask16(p->hWnd)) |
| p->flags = 0; |
| } |
| |
| void WINSOCK_link_async_op(ws_async_op* p_aop) |
| { |
| if( __async_op_list ) __async_op_list->prev = p_aop; |
| else FD_ZERO(&__async_io_fdset); |
| |
| p_aop->next = __async_op_list; |
| p_aop->prev = NULL; |
| __async_op_list = p_aop; |
| |
| FD_SET(p_aop->fd[0], &__async_io_fdset); |
| if( p_aop->fd[0] > __async_io_max_fd ) |
| __async_io_max_fd = p_aop->fd[0]; |
| } |
| |
| void WINSOCK_unlink_async_op(ws_async_op* p_aop) |
| { |
| if( p_aop == __async_op_list ) __async_op_list = p_aop->next; |
| else |
| { p_aop->prev->next = p_aop->next; |
| if( p_aop->next ) p_aop->next->prev = p_aop->prev; } |
| FD_CLR(p_aop->fd[0], &__async_io_fdset); |
| if( p_aop->fd[0] == __async_io_max_fd ) |
| __async_io_max_fd--; |
| } |
| |
| /* ----------------------------------- SIGIO handler - |
| * |
| * link_async_op/unlink_async_op allow to install generic |
| * async IO handlers (provided that aop_control function is defined). |
| * |
| * Note: AsyncGetXbyY expilicitly raise it. |
| */ |
| |
| void WINSOCK_sigio(int signal) |
| { |
| struct timeval timeout; |
| fd_set check_set; |
| ws_async_op* p_aop; |
| |
| check_set = __async_io_fdset; |
| bzero(&timeout,sizeof(timeout)); |
| |
| while( select(__async_io_max_fd + 1, |
| &check_set, NULL, NULL, &timeout) > 0) |
| { |
| for( p_aop = __async_op_list; |
| p_aop ; p_aop = p_aop->next ) |
| if( FD_ISSET(p_aop->fd[0], &check_set) ) |
| if( p_aop->aop_control(p_aop, AOP_IO) == AOP_CONTROL_REMOVE ) |
| { |
| if( p_aop->pid ) |
| { |
| kill(p_aop->pid, SIGKILL); |
| waitpid(p_aop->pid, NULL, WNOHANG); |
| } |
| WINSOCK_unlink_async_op( p_aop ); |
| } |
| check_set = __async_io_fdset; |
| } |
| } |
| |
| /* ----------------------------------- child process IPC */ |
| |
| static void _sigusr1_handler_child(int sig) |
| { |
| /* read message queue to decide which |
| * async_ctl parameters to update |
| * |
| * Note: we don't want to have SA_RESTART on this signal |
| * handler, otherwise select() won't notice changed fd sets. |
| */ |
| |
| signal( SIGUSR1, _sigusr1_handler_child ); |
| while( msgrcv(async_qid, (struct msgbuf*)&async_ctl.ip, |
| MTYPE_PARENT_SIZE, MTYPE_PARENT, IPC_NOWAIT) != -1 ) |
| { |
| /* only ip.lParam is updated */ |
| #if __WS_ASYNC_DEBUG |
| printf("handler - event %08x\n", async_ctl.ip.lParam ); |
| #endif |
| |
| switch( async_ctl.ip.lParam ) |
| { |
| /* These are events we are notified of. |
| */ |
| |
| case WS_FD_CONNECTED: async_ctl.lEvent &= ~WS_FD_CONNECT; |
| FD_SET(async_ctl.ws_sock->fd, &fd_read); |
| FD_SET(async_ctl.ws_sock->fd, &fd_write); |
| break; |
| |
| case WS_FD_ACCEPT: async_ctl.ws_sock->flags |= WS_FD_ACCEPT; |
| FD_SET(async_ctl.ws_sock->fd, &fd_read); |
| FD_SET(async_ctl.ws_sock->fd, &fd_write); |
| break; |
| case WS_FD_OOB: async_ctl.lEvent |= WS_FD_OOB; |
| FD_SET(async_ctl.ws_sock->fd, &fd_excp); |
| break; |
| case WS_FD_READ: async_ctl.lEvent |= WS_FD_READ; |
| FD_SET(async_ctl.ws_sock->fd, &fd_read); |
| break; |
| case WS_FD_WRITE: async_ctl.lEvent |= WS_FD_WRITE; |
| FD_SET(async_ctl.ws_sock->fd, &fd_write); |
| break; |
| default: |
| } |
| } |
| } |
| |
| static int notify_parent( unsigned flag ) |
| { |
| if( flag & WSMSG_ASYNC_SELECT ) |
| { |
| async_ctl.ip.mtype = MTYPE_CLIENT; |
| while( msgsnd(async_qid, (struct msgbuf*)&(async_ctl.ip), |
| MTYPE_CLIENT_SIZE, 0) == -1 ) |
| { |
| if( errno == EINTR ) continue; |
| #ifdef EIDRM |
| else if( errno == EIDRM ) _exit(0); |
| #endif |
| else |
| { |
| perror("AsyncSelect(child)"); |
| return 0; |
| } |
| } |
| kill(getppid(), SIGUSR1); |
| |
| #if __WS_ASYNC_DEBUG |
| printf("handler - notify [%08x]\n", async_ctl.ip.lParam); |
| #endif |
| } |
| else /* use half-duplex pipe to handle variable length packets */ |
| { |
| write(async_ctl.ws_aop->fd[1], &async_ctl.lLength, sizeof(unsigned) ); |
| write(async_ctl.ws_aop->fd[1], async_ctl.buffer, async_ctl.lLength ); |
| kill(getppid(), SIGIO); /* simulate async I/O */ |
| #if __WS_ASYNC_DEBUG |
| printf("handler - notify aop [%d, buf %d]\n", async_ctl.lLength, async_ctl.ws_aop->buflen); |
| #endif |
| pause(); |
| } |
| return 1; |
| } |
| |
| /* ----------------------------------- async select */ |
| |
| static void setup_fd_sets() |
| { |
| FD_ZERO(&fd_read); FD_ZERO(&fd_write); FD_ZERO(&fd_excp); |
| |
| if( async_ctl.lEvent & WS_FD_OOB) |
| FD_SET(async_ctl.ws_sock->fd, &fd_excp); |
| if( async_ctl.lEvent & (WS_FD_ACCEPT | WS_FD_READ | |
| WS_FD_CONNECT | WS_FD_CLOSE) ) |
| FD_SET(async_ctl.ws_sock->fd, &fd_read); |
| if( async_ctl.lEvent & (WS_FD_WRITE | WS_FD_CONNECT) ) |
| FD_SET(async_ctl.ws_sock->fd, &fd_write); |
| } |
| |
| static void setup_sig_sets(sigset_t* sig_block) |
| { |
| sigemptyset(sig_block); |
| sigaddset(sig_block, SIGUSR1); |
| sigprocmask( SIG_BLOCK, sig_block, NULL); |
| signal( SIGUSR1, _sigusr1_handler_child ); |
| } |
| |
| void WINSOCK_do_async_select() |
| { |
| sigset_t sig_block; |
| int sock_type, bytes; |
| |
| getsockopt(async_ctl.ws_sock->fd, SOL_SOCKET, SO_TYPE, &sock_type, &bytes); |
| setup_sig_sets(&sig_block); |
| setup_fd_sets(); |
| |
| while(1) |
| { |
| int val; |
| |
| if( sock_type != SOCK_STREAM ) |
| async_ctl.lEvent &= ~(WS_FD_ACCEPT | WS_FD_CONNECT); |
| sigprocmask( SIG_UNBLOCK, &sig_block, NULL); |
| |
| #if __WS_ASYNC_DEBUG |
| printf("select(2)[%i,%i,%i]... ", |
| FD_ISSET(async_ctl.ws_sock->fd, &fd_read), |
| FD_ISSET(async_ctl.ws_sock->fd, &fd_write), |
| FD_ISSET(async_ctl.ws_sock->fd, &fd_excp)); |
| #endif |
| if( (val = select(async_ctl.ws_sock->fd + 1, |
| &fd_read, &fd_write, &fd_excp, NULL)) == -1 ) |
| if( errno == EINTR ) continue; |
| #if __WS_ASYNC_DEBUG |
| printf("got %i events\n", val); |
| #endif |
| |
| #if __WS_ASYNC_DEBUG |
| if( FD_ISSET(async_ctl.ws_sock->fd, &fd_read) ) |
| printf("handler - read is READY! [%08x]\n", async_ctl.lEvent & (WS_FD_READ | WS_FD_CLOSE)); |
| #endif |
| |
| sigprocmask( SIG_BLOCK, &sig_block, NULL); |
| async_ctl.ip.lParam = 0; |
| if( async_ctl.ws_sock->flags & WS_FD_ACCEPT ) |
| { |
| /* listening socket */ |
| |
| FD_CLR(async_ctl.ws_sock->fd, &fd_read); |
| FD_CLR(async_ctl.ws_sock->fd, &fd_write); |
| |
| async_ctl.ip.lParam = WSAMAKESELECTREPLY( WS_FD_ACCEPT, 0 ); |
| notify_parent( WSMSG_ASYNC_SELECT ); |
| continue; |
| } |
| else /* I/O socket */ |
| { |
| if( async_ctl.lEvent & WS_FD_CONNECT ) |
| { |
| if( FD_ISSET(async_ctl.ws_sock->fd, &fd_write) ) |
| { |
| /* success - reinit fd sets to start I/O */ |
| |
| if( async_ctl.lEvent & (WS_FD_READ | WS_FD_CLOSE)) |
| FD_SET(async_ctl.ws_sock->fd, &fd_read); |
| else FD_CLR(async_ctl.ws_sock->fd, &fd_read); |
| if( async_ctl.lEvent & WS_FD_WRITE ) |
| FD_SET(async_ctl.ws_sock->fd, &fd_write); |
| else FD_CLR(async_ctl.ws_sock->fd, &fd_write); |
| |
| async_ctl.ip.lParam = WSAMAKESELECTREPLY( WS_FD_CONNECT, 0 ); |
| async_ctl.lEvent &= ~WS_FD_CONNECT; /* one-shot */ |
| } |
| else if( FD_ISSET(async_ctl.ws_sock->fd, &fd_read) ) |
| { |
| /* failure - do read() to get correct errno */ |
| |
| if( read(async_ctl.ws_sock->fd, &bytes, 4) == -1 ) |
| async_ctl.ip.lParam = WSAMAKESELECTREPLY( WS_FD_CONNECT, wsaErrno() ); |
| else continue; |
| } else continue; /* OOB?? */ |
| |
| notify_parent( WSMSG_ASYNC_SELECT ); |
| } |
| else /* connected socket */ |
| { |
| |
| if( async_ctl.lEvent & WS_FD_OOB ) |
| if( FD_ISSET(async_ctl.ws_sock->fd, &fd_excp) ) |
| { |
| async_ctl.ip.lParam = WSAMAKESELECTREPLY( WS_FD_OOB, 0 ); |
| async_ctl.lEvent &= ~WS_FD_OOB; |
| FD_CLR(async_ctl.ws_sock->fd, &fd_excp); |
| notify_parent( WSMSG_ASYNC_SELECT ); |
| } |
| else FD_SET(async_ctl.ws_sock->fd, &fd_excp); |
| |
| if( async_ctl.lEvent & WS_FD_WRITE ) |
| if( FD_ISSET( async_ctl.ws_sock->fd, &fd_write ) ) |
| { |
| async_ctl.ip.lParam = WSAMAKESELECTREPLY( WS_FD_WRITE, 0 ); |
| async_ctl.lEvent &= ~WS_FD_WRITE; |
| FD_CLR(async_ctl.ws_sock->fd, &fd_write); |
| notify_parent( WSMSG_ASYNC_SELECT ); |
| } |
| else FD_SET(async_ctl.ws_sock->fd, &fd_write); |
| |
| if( async_ctl.lEvent & (WS_FD_READ | WS_FD_CLOSE) ) |
| if( FD_ISSET(async_ctl.ws_sock->fd, &fd_read) ) |
| { |
| int ok = 0; |
| |
| if( sock_type == SOCK_RAW ) ok = 1; |
| else if( ioctl( async_ctl.ws_sock->fd, FIONREAD, (char*)&bytes) == -1 ) |
| { |
| async_ctl.ip.lParam = WSAMAKESELECTREPLY( WS_FD_READ, wsaErrno() ); |
| FD_CLR( async_ctl.ws_sock->fd, &fd_read ); |
| bytes = 0; |
| } |
| |
| if( bytes || ok ) /* got data */ |
| { |
| #if __WS_ASYNC_DEBUG |
| if( ok ) printf("\traw/datagram read pending\n"); |
| else printf("\t%i bytes pending\n", bytes ); |
| #endif |
| if( async_ctl.lEvent & WS_FD_READ ) |
| { |
| async_ctl.ip.lParam = WSAMAKESELECTREPLY( WS_FD_READ, 0 ); |
| async_ctl.lEvent &= ~WS_FD_READ; |
| if( !(async_ctl.lEvent & WS_FD_CLOSE) ) |
| FD_CLR( async_ctl.ws_sock->fd, &fd_read ); |
| } |
| else if( !(async_ctl.lEvent & (WS_FD_WRITE | WS_FD_OOB)) ) |
| { |
| sigprocmask( SIG_UNBLOCK, &sig_block, NULL); |
| pause(); |
| sigprocmask( SIG_BLOCK, &sig_block, NULL); |
| } |
| else continue; |
| } |
| else /* 0 bytes to read */ |
| { |
| val = read( async_ctl.ws_sock->fd, (char*)&bytes, 4); |
| if( errno == EWOULDBLOCK || errno == EINTR ) |
| { |
| #if __WS_ASYNC_DEBUG |
| printf("\twould block..\n"); |
| #endif |
| continue; |
| } |
| switch( val ) |
| { |
| case 0: errno = ENETDOWN; /* ENETDOWN */ |
| case -1: /* ECONNRESET */ |
| async_ctl.ip.lParam = WSAMAKESELECTREPLY( WS_FD_CLOSE, wsaErrno() ); |
| break; |
| default: continue; |
| } |
| async_ctl.lEvent &= ~(WS_FD_CLOSE | WS_FD_READ); /* one-shot */ |
| FD_ZERO(&fd_read); FD_ZERO(&fd_write); |
| } |
| |
| notify_parent( WSMSG_ASYNC_SELECT ); |
| } |
| else FD_SET(async_ctl.ws_sock->fd, &fd_read); |
| |
| } /* connected socket */ |
| } /* I/O socket */ |
| } /* while */ |
| } |
| |
| |
| /* ----------------------------------- getXbyY requests */ |
| |
| static void _async_fail() |
| { |
| async_ctl.lLength = |
| (h_errno < 0) ? (unsigned)WSAMAKEASYNCREPLY( 0, wsaErrno() ) |
| : (unsigned)WSAMAKEASYNCREPLY( 0, wsaHerrno() ); |
| write(async_ctl.ws_aop->fd[1], &async_ctl.lLength, sizeof(unsigned) ); |
| kill(getppid(), SIGIO); /* simulate async I/O */ |
| pause(); |
| } |
| |
| void dump_ws_hostent_offset(struct ws_hostent* wshe) |
| { |
| int i; |
| char* base = (char*)wshe; |
| unsigned* ptr; |
| |
| printf("h_name = %08x\t[%s]\n", (unsigned)wshe->h_name, base + (unsigned)wshe->h_name); |
| printf("h_aliases = %08x\t[%08x]\n", (unsigned)wshe->h_aliases, |
| (unsigned)(base + (unsigned)wshe->h_aliases)); |
| ptr = (unsigned*)(base + (unsigned)wshe->h_aliases); |
| for(i = 0; ptr[i]; i++ ) |
| { |
| printf("%i - %08x ", i + 1, ptr[i]); |
| printf(" [%s]\n", ((char*)base) + ptr[i]); |
| } |
| printf("h_length = %i\n", wshe->h_length); |
| } |
| |
| void WS_do_async_gethost(LPWSINFO pwsi, unsigned flag ) |
| { |
| int size = 0; |
| struct hostent* p_he; |
| |
| close(async_ctl.ws_aop->fd[0]); |
| p_he = (flag & WSMSG_ASYNC_HOSTBYNAME) |
| ? gethostbyname(async_ctl.init) |
| : gethostbyaddr(async_ctl.init, |
| async_ctl.lLength, async_ctl.lEvent); |
| if( p_he ) size = WS_dup_he(pwsi, p_he, WS_DUP_SEGPTR | WS_DUP_OFFSET ); |
| if( size ) |
| { |
| async_ctl.buffer = pwsi->buffer; |
| async_ctl.lLength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 ); |
| notify_parent( flag ); |
| } |
| else _async_fail(); |
| } |
| |
| void WS_do_async_getproto(LPWSINFO pwsi, unsigned flag ) |
| { |
| int size = 0; |
| struct protoent* p_pe; |
| |
| close(async_ctl.ws_aop->fd[0]); |
| p_pe = (flag & WSMSG_ASYNC_PROTOBYNAME) |
| ? getprotobyname(async_ctl.init) |
| : getprotobynumber(async_ctl.lEvent); |
| if( p_pe ) size = WS_dup_pe(pwsi, p_pe, WS_DUP_SEGPTR | WS_DUP_OFFSET ); |
| if( size ) |
| { |
| async_ctl.buffer = pwsi->buffer; |
| async_ctl.lLength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 ); |
| notify_parent( flag ); |
| } |
| else _async_fail(); |
| } |
| |
| void WS_do_async_getserv(LPWSINFO pwsi, unsigned flag ) |
| { |
| int size = 0; |
| struct servent* p_se; |
| |
| close(async_ctl.ws_aop->fd[0]); |
| p_se = (flag & WSMSG_ASYNC_SERVBYNAME) |
| ? getservbyname(async_ctl.init, async_ctl.buffer) |
| : getservbyport(async_ctl.lEvent, async_ctl.init); |
| if( p_se ) size = WS_dup_se(pwsi, p_se, WS_DUP_SEGPTR | WS_DUP_OFFSET ); |
| if( size ) |
| { |
| async_ctl.buffer = pwsi->buffer; |
| async_ctl.lLength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 ); |
| notify_parent( flag ); |
| } |
| else _async_fail(); |
| } |
| |