#pragma module SPOP3_NETIO "SPOP3_NETIO-1-D" #define __MODULE__ "SPOP3_NETIO" /* **++ ** FACILITY: StarLet POP3 server form OpenVMS ** ** MODULE DESCRIPTION: ** ** This module covers network I/O functionality. ** ** AUTHORS: ** ** Ruslan R. Laishev ** ** CREATION DATE: 24-MAR-2005 ** ** DESIGN ISSUES: ** ** MODIFICATION HISTORY: ** ** 7-SEP-2005 RRL Fixed bug in the netio_close(). ** 27-DEC-2005 RRL Fixed possible buffer overflow in the netio_read(). ** 11-OCT-2006 RRL Removed unused stuff from netio_read()/netio_write(). ** 2-FEB-2007 RRL Changed netio_close() according HP TCP docs and ** investigation of Hunter Goatley. ** 5-APR-2007 RRL Added Keep Alive & Send/Receive Buffer Size options to netio_accept(). ** 1-APR-2008 RRL Changed buffers size. ** 11-NOV-2008 RRL Fixed PSC TCPware-TCP 5.8-3 issue see case "PSC131008 - [5.8-2]TCP KeepAlive?": ** setting the KeepAlive option moved from netio_accept() to ** netio_start(). ** ** {@tbs@}... **-- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include $DESCRIPTOR(dsc_ucxdev,"UCX$DEVICE"); $DESCRIPTOR(dsc_tcpipdev,"TCPIP$DEVICE"); #define INIT_DDESC(dsc) {(dsc).dsc$b_dtype = DSC$K_DTYPE_T;\ (dsc).dsc$b_class = DSC$K_CLASS_D;(dsc).dsc$w_length = 0;\ (dsc).dsc$a_pointer = 0;} #define INIT_SDESC(dsc, len, ptr) {(dsc).dsc$b_dtype = DSC$K_DTYPE_T;\ (dsc).dsc$b_class = DSC$K_CLASS_S; (dsc).dsc$w_length = (short) (len);\ (dsc).dsc$a_pointer = (ptr);} #define min(x,y) ((x > y)?y:x) #define max(x,y) ((x < y)?y:x) const unsigned rbfsz=8192,one=1,sbfsz = 32676; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Open a passive socket and listening for incomming TCP-connection request. ** ** FORMAL PARAMETERS: ** ** netchan: A returned network channel ** port: A local port number, 0 - assume default port number ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int netio_start ( unsigned * netchan, unsigned short port ) { int status; const struct { short proto; char type; char domain; } sck_parm = {TCPIP$C_TCP,TCPIP$C_STREAM,TCPIP$C_AF_INET}; iosb iosb; struct sockaddr_in home; const ile2 lhst_adrs = {sizeof(struct sockaddr_in),TCPIP$C_SOCK_NAME,&home}, sock_opt[]={{sizeof(one),TCPIP$C_REUSEADDR,&one}, {sizeof(sbfsz),TCPIP$C_SNDBUF,&sbfsz}, {sizeof(rbfsz),TCPIP$C_RCVBUF,&rbfsz}, {sizeof(one),TCPIP$C_KEEPALIVE,&one}}, options = {sizeof(sock_opt),TCPIP$C_SOCKOPT,&sock_opt}; /* ** Open a network device */ if ( !(1 & (status = sys$assign(&dsc_ucxdev,netchan,0,0))) ) { if ( status != SS$_NOSUCHDEV ) return status; if ( !(1 & (status = sys$assign( &dsc_tcpipdev,netchan,0,0))) ) return status; } /* ** Create connection socket */ if ( !(1 & (status = sys$qiow(EFN$C_ENF,*netchan,IO$_SETMODE,&iosb,0,0, &sck_parm,0,0,0,&options,0))) || !(iosb.iosb$w_status & 1) ) { sys$dassgn (*netchan); return (1 & status)?iosb.iosb$w_status:status; } /* ** Initialize structure for creating main listening sockets */ memset(&home,0,sizeof(struct sockaddr_in)); home.sin_port = htons(port); home.sin_family = INET$C_AF_INET; home.sin_addr.s_addr = TCPIP$C_INADDR_ANY; /* ** Start waiting incomming connection request */ if ( !(1 & (status = sys$qiow(EFN$C_ENF,*netchan,IO$_SETMODE,&iosb,0,0, 0,0,&lhst_adrs,5,0,0))) || !(iosb.iosb$w_status & 1) ) return (1 & status)?iosb.iosb$w_status:status; return status; } int netio_accept ( unsigned netchan, unsigned * chan, struct in_addr * addr, unsigned short *port ) { unsigned status, nchan = 0; unsigned short retlen = 0; iosb iosb; struct sockaddr_in rhost; ile3 rhost_item = {sizeof(struct sockaddr_in),0,&rhost,&retlen}; memset(&rhost,0,sizeof(struct sockaddr_in)); /* ** Create a socket for new connection */ if ( !(1 & (status = sys$assign(&dsc_ucxdev,&nchan,0,0))) ) { if ( status != SS$_NOSUCHDEV ) return status; if ( !(1 & (status = sys$assign(&dsc_tcpipdev,chan,0,0))) ) return status; } /* ** Accept client connection */ if ( !(1 & (status = sys$qiow(EFN$C_ENF,netchan,IO$_ACCESS|IO$M_ACCEPT,&iosb,0,0, 0,0,&rhost_item,&nchan,0,0))) || !(iosb.iosb$w_status & 1) ) { sys$dassgn (nchan); return (1 & status)?iosb.iosb$w_status:status; } *addr = rhost.sin_addr; *port = ntohs(rhost.sin_port); *chan = nchan; return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Close a network I/O channel. ** ** FORMAL PARAMETERS: ** ** chan: A network I/O channel returned by netio_accept() ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int netio_close ( unsigned chan ) { iosb iosb; sys$qiow(EFN$C_ENF,chan,IO$_DEACCESS|IO$M_SHUTDOWN, &iosb, 0, 0, 0, 0, 0, TCPIP$C_DSC_ALL,0, 0); sys$qiow(EFN$C_ENF,chan,IO$_DEACCESS, &iosb, 0, 0, 0, 0, 0, 0, 0, 0); return sys$dassgn (chan); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Canceling all queued and processed request due of expiration timer. ** ** FORMAL PARAMETERS: ** ** reqidt: pointer to a network channel ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** Cancel _all_ request for the given I/O channel. **-- */ static void timer_ast ( unsigned *reqidt ) { sys$cantim(reqidt,0); sys$cancel(*reqidt); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Read a data from the specified network channel with timeout to a specified buffer. ** ** FORMAL PARAMETERS: ** ** chan: a channel number, given from netio_open() or netio_conn() ** buf: a buffer to receiving data ** retlen: a length of a received data in the buffer ** iotmo: a timeout of network I/O, VMS Delta time ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int netio_read ( unsigned chan, struct dsc$descriptor *buf, unsigned short *retlen, int *iotmo ) { int status,reqidt = chan, bufl = buf->dsc$w_length; iosb iosb; /* ** Some initialization */ *retlen = 0; /* ** Setup timer request if it's a TCP I/O channel */ if ( !(1 & (status = sys$setimr (EFN$C_ENF,iotmo, timer_ast,&reqidt,0))) ) return status; /* ** Reading... */ while ( *retlen < bufl ) { if ( !(1 & (status = sys$qiow (EFN$C_ENF,chan,IO$_READVBLK,&iosb,0,0, buf->dsc$a_pointer+(*retlen),bufl-(*retlen),0,0,0,0))) || !(1 & iosb.iosb$w_status) ) break; *retlen += iosb.iosb$w_bcnt; /* ** Check for line termination sequence */ if ( (*retlen >= 2) && (*(buf->dsc$a_pointer+(*retlen) - 2) == '\r') && (*(buf->dsc$a_pointer+(*retlen) - 1) == '\n') ) { *retlen -= 2; break; } } /* ** Cancel a timer request */ sys$cantim(&reqidt,0); return (1 & status)?iosb.iosb$w_status:status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Write a data to a given network channel. ** ** FORMAL PARAMETERS: ** ** chan: a channel number, given from netio_open() or netio_conn() ** buf: a buffer witj data to send ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int netio_write ( unsigned chan, struct dsc$descriptor *buf ) { int status; iosb iosb; struct {unsigned l; char *a;} bvec[] = { {buf->dsc$w_length,buf->dsc$a_pointer},{2,"\r\n"}}; struct dsc$descriptor buf_d = { 0, DSC$K_CLASS_S, DSC$K_DTYPE_T, 0} ; /* ** Setup the buffer descriptor */ buf_d.dsc$a_pointer = (char *) &bvec; buf_d.dsc$w_length = sizeof(bvec); status = sys$qiow (EFN$C_ENF,chan,IO$_WRITEVBLK,&iosb,0,0, 0,0,0,0,&buf_d,0); return (1 & status)?iosb.iosb$w_status:status; }