/***************************************************************************
 *                                                                         *
 *   RLOGIN.C                                                              *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Rlogin client for Worldgroup.  Based on the Telnet client by          *
 *   RNStein.                                                              *
 *                                                                         *
 *                                        - Craig Yap  5/16/95             *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "tcpip.h"
#include "dns.h"
#include "tno.h"
#include "telnetd.h"
#include "rlogin.h"
#include "rlogind.h"
#include "alias.h"
#include "galrlgn.h"
#include "tcport.h"

#define FILREV "$Revision: 25 $"

#ifdef GCWINNT
#define EADDRINUSE WSAEADDRINUSE
#endif // GCWINNT

#ifndef ICO2
#define tcpip_errno ips_errno
#define TNFACCP TNFRECV
#define TNFCONN TNFSEND
#endif

static INT globallgn(VOID);
static GBOOL lgninp(VOID);
static INT rhdlhst(VOID);
static VOID lgncbk(struct dns *);
static GBOOL lgncall(VOID);
static VOID rbgncon(struct lgninf *);
static GBOOL inilgn(VOID);
static VOID rabtcon(VOID);
static VOID declgn(VOID);
static VOID rendcon(INT msgnum,...);
static VOID rfincon(VOID);
static VOID lgnsts(VOID);
static VOID lgnhup(VOID);
static INT lgnsbf(CHAR *bytes,INT nbytes);
static INT sendwin(VOID);
static VOID lgnrcv(struct lgninf *);
static INT lgnsnd(struct lgninf *);
static INT tcpdialr(
struct in_addr inaddr,
UINT port,
UINT fromport,
INT *psocket);

#define LGNWAIT -1                 /* substate wait for output to finish   */
#define LGNEXIT -2                 /* substate to trigger Rlogin exit      */
#define BNDNEXT -3                 /* try to bind next available port      */
#define DSCTMO (5*16)              /* wait up to 5 sec for output to finish*/

INT lgnstt;                        /* Rlogin client module state number    */
struct module lgnmodule={          /* module interface block               */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     lgninp,                       /*    input routine if selected         */
     lgnsts,                       /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     lgnhup,                       /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     NULL,                         /*    finish-up (sys shutdown) routine  */
};

static HMCVFILE lgnmb;             /* --- options in GALLGN.MCV ---------- */

static VOID (*olddec)(VOID);       /* for recording (*decusr)() vector     */
static INT rwconn;                 /* receive window for Rlogin connection */
static INT rbempt;                 /* receive buffer empty (btuoba() val)  */
static INT lgnhys;                 /* hysteresis for rcv window growth     */
static GBOOL lgngo;                /* can users type "/RLOGIN" command?    */
static SHORT contwt;               /* dialing out patience, 1/16 sec units */
static CHAR *argkey;               /* key required to specify params.      */
static CHAR *abtkey;               /* key required to use abort string     */
static CHAR *abtstg;               /* string to abort non-ASCII connection */
static CHAR *bnckey;               /* key required to rlogin out           */
static SHORT abtsec;               /* pause b4 abort stg (1/16 sec units)  */
static INT maxlgn;                 /* max simultaneous outgoing rlogins    */
static GBOOL audcon;               /* audit rlogin connects                */
static GBOOL auddsc;               /* audit rlogin disconnects             */
static SHORT lgnchg;               /* rlogin client surcharge creds/min    */
static LONG lgnkchg;               /* charge / 1K bytes traffic via Rlogin */
static GBOOL canescp;              /* true if sysop allows users to escape */
static CHAR **rendmsgs;            /* end connection messages              */

VOID EXPORT
init__galrlgn(VOID)                /* Rlogin client initialization         */
{
     init__tcpip();
     init__galtntd();
     stzcpy(lgnmodule.descrp,gmdnam("galrlgn.mdf"),MNMSIZ);
     lgnstt=register_module(&lgnmodule);
     lgnmb=opnmsg("galrlgn.mcv");
     rendmsgs=(CHAR **)alczer(nterms*sizeof(CHAR *));
     lgnchg=numopt(LGNCHG,-32767,32767);
     lgnkchg=lngopt(LGNKCHG,-2000000000L,2000000000L);
     abtkey=stgopt(ABTKEY);
     bnckey=stgopt(BNCKEY);
     argkey=stgopt(ARGKEY);
     maxlgn=numopt(MAXLGN,0,250);
     lgngo=ynopt(LGNGO);
     canescp=ynopt(CANESCP);
     contwt=numopt(CONTWT,0,2048);
     abtstg=stgopt(ABTSTGG);
     abtsec=numopt(ABTSEC,0,999);
     audcon=ynopt(AUDCON);
     auddsc=ynopt(AUDDSC);
     olddec=decusr;
     decusr=declgn;
     globalcmd(globallgn);
     dclvda(sizeof(struct lgninf));
     rwconn=OUTSIZ/2-1;
     rbempt=OUTSIZ/2-1;
     lgnhys=OUTSIZ/4;
}

VOID EXPORT
initwc__galrlgn(VOID)
{
     init__galrlgn();
}

static INT
globallgn(VOID)                    /* global "/rlogin <host> <params>" cmd */
{
     CHAR *cp;

     if (lgngo && margc >= 1 && sameas((CHAR *)margv[0],
                                       (CHAR *)"/rlogin")) {
          cp=margv[0];
          rstrin();
          movmem(cp,cp+3,(UINT)(INPSIZ-(cp-input)-3));
          input[INPSIZ-1]='\0';
          movmem("/go ",cp,4);
          parsin();
          return(globalgo());
     }
     return(0);
}

static GBOOL
lgninp(VOID)                       /* Rlogin client user input routine     */
{
     GBOOL rc=TRUE;
     CHAR *cp;

     setmbk(lgnmb);
     if (margc == 1 && sameas((CHAR *)margv[0],(CHAR *)"X")
      && usrptr->substt != CONNED) {
          dnsabt();
          rabtcon();
          return(0);
     }
     do {
          bgncnc();
          switch (usrptr->substt) {
          case 0:
               cncchr();
               if (rendmsgs[usrnum] != NULL) {
                    free(rendmsgs[usrnum]);
                    rendmsgs[usrnum]=NULL;
               }
               setmem(lgnptr,sizeof(struct lgninf),0);
               lgnptr->sockt=-1;
               lgnptr->alwescp=FALSE;
               if ((tntinf[usrnum].flags&TNTCHN
                || lgndinf[usrnum].flags&LGNCHN)
                && !haskey(bnckey)) {
                    prfmsg(CANTBNC);
                    rc=FALSE;
               }
               else if (numonl(lgnstt) > maxlgn) {
                    prfmsg(LGNMANY,maxlgn);
                    rc=FALSE;
               }
               else if ((cp=alsofusr(usaptr->userid)) == NULL) {
                    prfmsg(NOALIAS);
                    rc=FALSE;
               }
               else {
                    stzcpy(lgnptr->luserid,cp,UIDSIZ);
                    prfmsg(usrptr->substt=LGNHOST);
               }
               break;
          case LGNHOST:
               rhdlhst();
               cncall();
               break;
          case CONNED:
               clrprf();
               break;
          case LOOKING:
          case CONNING:
               dnsabt();
               rabtcon();
          case LGNEXIT:
               cncall();
               rc=FALSE;
               break;
          }
     } while (!endcnc());
     outprf(usrnum);
     rstmbk();
     return(rc);
}

static INT
rhdlhst(VOID)                       /* handle host name & other options     */
{
     INT i;
     GBOOL canprm,canabt,ok=TRUE;

     canprm=haskey(argkey);
     if (margc == 0
      || sameas(margv[0],"?")) {
          prfmsg(canprm ? LGNHLPP1 : LGNHLP1);
          dnshelp();
          prfmsg(canprm ? LGNHLPP2 : LGNHLP2);
          return(1);
     }
     canabt=(canescp && haskey(abtkey) && abtsec > 0);
     parsin();
     for (i=0 ; ok && i < margc ; i++) {
          if (*margv[i] == '-') {
               if (canprm) {
                    switch (toupper(margv[i][1])) {
                    case 'A':
                         if (canabt) {
                              lgnptr->alwescp=TRUE;
                         }
                         else {
                              prfmsg(CANTESP);
                              ok=FALSE;
                         }
                         break;
                    case 'L':
                         if ((i+1 == margc) || (*margv[i+1] == '-')) {
                              prfmsg(NOUSR);
                              ok=FALSE;
                         }
                         else {
                              stzcpy(lgnptr->ruserid,margv[++i],USRSIZ);
                         }
                         break;
                    case 'E':
                         if (canabt) {
                              if (i+1 == margc) {
                                   prfmsg(NOESCOD);
                                   ok=FALSE;
                              }
                              else {
                                   stzcpy(lgnptr->escape,margv[++i],ESCSIZ);
                                   lgnptr->alwescp=TRUE;
                              }
                         }
                         else {
                              prfmsg(CANTESP);
                              ok=FALSE;
                         }
                         break;
                    default:
                         prfmsg(UNKSWCH,margv[i]);
                         ok=FALSE;
                         break;
                    }
               }
               else {
                    prfmsg(CANTPRM);
                    ok=FALSE;
               }
          }
          else if (lgnptr->host[0] == '\0') {
               stzcpy(lgnptr->host,margv[i],HSTSIZ);
          }
          else {
               prfmsg(CANTPRM);
               ok=FALSE;
          }
     }
     if (!ok) {
       lgnptr->host[0]='\0';
          prfmsg(LGNHOST);
          return(0);
     }
     if (lgnptr->host[0] == '\0') {
          prfmsg(NOHOST);
          prfmsg(LGNHOST);
     }
     else {
          stzcpy(lgnptr->dns.name,lgnptr->host,DNSNSZ);
          if (lgnptr->ruserid[0] == '\0') {
               stzcpy(lgnptr->ruserid,lgnptr->luserid,USRSIZ);
          }
          if (lgnptr->escape[0] == '\0') {
               stzcpy(lgnptr->escape,abtstg,ESCSIZ);
          }
          lgnptr->rport=LGNPORT;
          lgnptr->dns.numaddr=1;
          lgnptr->dns.callbk=lgncbk;
          if (!dnsn2a(&lgnptr->dns)) {
               prfmsg(usrptr->substt=LOOKING,lgnptr->host);
          }
     }
     return(1);
}

static VOID
lgncbk(                            /* Rlogin DNS lookup callback vector    */
struct dns *dnsptr)
{
     GBOOL goahead;

     setmbk(lgnmb);
     if (dnsptr->status >= 0) {
          prfmsg(CONNING,inet_ntoa(dnsptr->inaddr[0]));
          lgnptr->lport=PRVPRT;
          goahead=lgncall();
     }
     else {
          prfmsg(LUPERR,dnsemg);
          goahead=FALSE;
     }
     outprf(usrnum);
     clrprf();
     if (goahead) {
          btuinj(usrnum,CYCLE);
     }
     else {
          usrptr->substt=LGNEXIT;
          btuinj(usrnum,CRSTG);
     }
     rstmbk();
}

static GBOOL
lgncall(VOID)                      /* try to connect to Rlogin server      */
                                   /* returns 1=working 0=problem reported */
                                   /* lgnptr->dns.inaddr[0], lgnptr->rport,*/
{                                  /* implicit inputs, expects curusr() too*/
     INT rc;

     switch (rc=tcpdialr(lgnptr->dns.inaddr[0],htons(lgnptr->rport),
             lgnptr->lport,&lgnptr->sockt)) {
     case DLERRB:
          if (tcpip_errno == EADDRINUSE) {
               usrptr->substt=BNDNEXT;
               return(TRUE);
          }
          else {
               prfmsg(BINDERR,tcpip_errno,tcpErrStg(tcpip_errno));
          }
          break;
     case DLERRS:
          prfmsg(SOCKERR,tcpip_errno,tcpErrStg(tcpip_errno));
          break;
     case DLERRI:
          prfmsg(IOCLERR,tcpip_errno,tcpErrStg(tcpip_errno));
          break;;
     case DLCNOW:
     case DLCING:
#ifdef GCDOS
          setrcvwin(lgnptr->sockt,lgnptr->rcvwin=rwconn);
#endif // GCDOS
          sktnfy(TNFCONN,lgnptr->sockt,rbgncon,lgnptr,usrnum);
          usrptr->substt=CONNING;
          lgnptr->sttime=btuTicker();
          lgnptr->tckonl=lngtck;
          lgnptr->byttfc=0L;
          return(TRUE);
     case DLERRC:
          prfmsg(CONNERR,rc,tcpip_errno,tcpErrStg(tcpip_errno));
          break;
     default:
          prfmsg(UNKNOWN,rc,tcpip_errno,tcpErrStg(tcpip_errno));
          break;
     }
     lgnptr->sockt=-1;
     return(FALSE);
}

static VOID
rbgncon(                           /* Connection has begun                 */
struct lgninf *lgn)                /* Rlogin client info structure (vda)   */
{                                  /* will corrupt usrnum, etc.            */
     INT sktmp;

     if (btuoba(usrnum) != OUTSIZ-1) {
          return;
     }
     if (audcon) {
          shocst("RLOGIN CLIENT CONNECT","to %s with %s",lgn->dns.name,
                 lgn->ruserid);
     }
     lgn->tckonl=lngtck;
     lgn->byttfc=0L;
     btubsz(usrnum,OUTSIZ/2,OUTSIZ/2);
     btuhwh(usrnum,OUTSIZ/2-40);
     setmbk(lgnmb);
     prfasc(usrptr->substt=CONNED,lgn->dns.name);
     usrptr->flags|=NOINJO;
     btutrg(usrnum,OUTSIZ/2);
     btuech(usrnum,0);
     if (lgnptr->alwescp && abtsec > 0 && haskey(abtkey)) {
          prfasc(DISCHELP,abtsec,lgnptr->escape);
     }
     sktmp=lgn->sockt;
     if (!inilgn()) {
          prfmsg(CONNRFSD);
          usrptr->substt=LGNEXIT;
          btuinj(usrnum,CRSTG);
          rfincon();
          sktcnc(TNFCONN,sktmp);
     }
     else {
          sktnfy(TNFRECV,lgn->sockt,lgnrcv,lgn,usrnum);
          sktcnc(TNFCONN,lgn->sockt);
     }
     outprf(usrnum);
     clrprf();
     rstmbk();
}

static GBOOL
inilgn(VOID)                       /* send initial user logon information  */
{
     CHAR *sprf;

     lgnsbf("",1);
     lgnsbf(lgnptr->luserid,strlen(lgnptr->luserid)+1);
     lgnsbf(lgnptr->ruserid,strlen(lgnptr->ruserid)+1);
     sprf=spr("%s/9600","vt100");
     lgnsbf(sprf,strlen(sprf)+1);
     return(lgnsnd(lgnptr) < 0 ? FALSE : (sendwin() < 0 ? FALSE : TRUE));
}

static VOID
rabtcon(VOID)                       /* abort connection to the server      */
{
     if (lgnptr->sockt != -1) {
          tfcchg(lgnptr->byttfc,lgnkchg);
          if (auddsc) {
               shocst("RLOGIN CLIENT DISCONNECT",
                      "from %s, %s seconds, %s bytes traffic",
                      inet_ntoa(lgnptr->dns.inaddr[0]),
                      ul2as(lngtck-lgnptr->tckonl),
                      ul2as(lgnptr->byttfc));
          }
          clsskt(lgnptr->sockt);
          lgnptr->sockt=-1;
     }
}

static VOID
declgn(VOID)                       /* Rlogin client connect time accting   */
{
     if (decusp->usrcls == ACTUSR && decusp->state == lgnstt) {
          deccst+=(lgnchg>>2)+((decusp->minut4&3) < (lgnchg&3));
     }
     (*olddec)();
}

static VOID
rendcon(                           /* end of connection                    */
INT msgnum,                        /*   notice to user                     */
...)                               /*   parameters                         */
{                                  /*   curusr() expected to be in effect  */
     va_list args;
     CHAR *stg;

     rabtcon();
     lgnptr->bufcnt=0;
     setmbk(lgnmb);
     stg=getmsg(msgnum);
     rstmbk();
     va_start(args,msgnum);
     vsprintf(vdatmp,stg,args);
     va_end(args);
     rendmsgs[usrnum]=alcdup(vdatmp);
     usrptr->substt=LGNWAIT;
     lgnptr->sttime=btuTicker();
}

static VOID
rfincon(VOID)                      /* final restoration of channel         */
{                                  /*   curusr() expected to be in effect  */
     btubsz(usrnum,INPSIZ,OUTSIZ);
     btuhwh(usrnum,OUTSIZ-40);
     btutrg(usrnum,0);
     echon();
     usrptr->flags&=~NOINJO;
}

static VOID
lgnsts(VOID)                       /* Rlogin client status handler routine */
{
     INT nroom,nactual;
     CHAR *newbytes;
     USHORT now;
     INT i;
     GBOOL timout,abt=FALSE,abterr=FALSE,more=TRUE;

     if (status != CYCLE) {
          dfsthn();
          return;
     }
     switch (usrptr->substt) {
     case BNDNEXT:
          setmbk(lgnmb);
          clrprf();
          for (i=0 ; i < MAXLPS ; i++) {
               if (--lgnptr->lport == 0) {
                    abterr=TRUE;
                    break;
               }
               else if ((abt=lgncall()) == TRUE && (lgnptr->sockt >= 0)) {
                    abterr=FALSE;
                    break;
               }
               else if (!abt) {
                    abterr=TRUE;
                    break;
               }
          }
          outprf(usrnum);
          rstmbk();
          if (abterr) {
               more=FALSE;
               usrptr->substt=LGNEXIT;
               btuinj(usrnum,CRSTG);
          }
          break;
     case CONNING:
          if (contwt > 0 && btuTicker()-lgnptr->sttime > contwt) {
               rendcon(CONTMO,contwt);
          }
          break;
     case CONNED:
          now=btuTicker();
          if ((nroom=LGNSIZ-lgnptr->bufcnt) > 0
           && (nactual=btuica(usrnum,(CHAR *)(newbytes=lgnptr->buffer+
                                  lgnptr->bufcnt),nroom)) > 0) {
               timout=(lgnptr->alwescp && now-lgnptr->abtclk >= abtsec);
               if (timout || lgnptr->abtcnt > 0) {
                    if (timout) {
                         lgnptr->abtcnt=0;
                    }
                    for (i=0 ; i < nactual ; i++) {
                         if (newbytes[i] ==
                             (CHAR)lgnptr->escape[lgnptr->abtcnt]) {
                              if (++lgnptr->abtcnt == strlen(lgnptr->escape)) {
                                   abt=TRUE;
                                   break;
                              }
                         }
                         else {
                              lgnptr->abtcnt=0;
                              break;
                         }
                    }
               }
               lgnptr->abtclk=now;
               lgnptr->bufcnt+=nactual;
               lgnsnd(lgnptr);
               if (abt) {
                    rendcon(ABORTED,abtstg);
               }
          }
          else {
               actdet=0;
          }
          if (lgnptr->sockt != -1) {
               rwnadj(lgnptr->sockt,rwconn,btuoba(usrnum),lgnhys,
                      &lgnptr->rcvwin);
          }
          break;
     case LGNWAIT:
          if (btuoba(usrnum) >= rbempt || btuTicker()-lgnptr->sttime > DSCTMO) {
               rfincon();
               if (rendmsgs[usrnum] != NULL) {
                    prf("%s",rendmsgs[usrnum]);
                    free(rendmsgs[usrnum]);
                    rendmsgs[usrnum]=NULL;
               }
               else {
                    prf("");
               }
               outprf(usrnum);
               usrptr->substt=LGNEXIT;
               btuinj(usrnum,CRSTG);
               more=FALSE;
          }
          break;
     default:
          more=FALSE;
          break;
     }
     if (more) {
          btuinj(usrnum,CYCLE);
     }
}

static VOID
lgnhup(VOID)                       /* Rlogin client user hangup routine    */
{
     if (usrptr->state == lgnstt) {
          rabtcon();
     }
     if (rendmsgs[usrnum] != NULL) {
          free(rendmsgs[usrnum]);
          rendmsgs[usrnum]=NULL;
     }
}

VOID
lgnrcv(                            /* Rlogin client receive-from-server    */
struct lgninf *lgn)                /* user's rlogin client info            */
{
     INT nroom,nactual;

     nroom=btuoba(usrnum);
     if (nroom > vdasiz) {
          nroom=vdasiz;
     }
#ifdef GCDOS
     rwnmgr(lgn->sockt,nroom,&lgn->rcvwin);
#endif // GCDOS
     if (nroom == 0) {
          actdet=0;
          return;
     }
     if ((nactual=recv(lgn->sockt,vdatmp,nroom,0)) < 0) {
          if (tcpip_errno == ECONNRESET) {
               rendcon(SVRDISC);
          }
          else {
               rendcon(RCVERR,tcpip_errno,tcpErrStg(tcpip_errno));
          }
     }
     else if (nactual == 0) {
          rendcon(SVREND);
     }
     else {
          lgnptr->byttfc+=nactual;
          btuxct(usrnum,nactual,vdatmp);
          if (lgn->bufcnt > 0) {
               lgnsnd(lgn);
          }
     }
}

static INT
lgnsbf(                            /* put bytes into the send buffer       */
CHAR *bytes,
INT nbytes)
{
     INT nbuf;

     if ((nbuf=min(LGNSIZ-lgnptr->bufcnt,nbytes)) > 0) {
          movmem(bytes,lgnptr->buffer+lgnptr->bufcnt,nbuf);
          lgnptr->bufcnt+=nbuf;
     }
     else {
          nbuf=0;
     }
     return(nbuf);
}

INT
lgnsnd(                            /* try to send data in buffer to server */
struct lgninf *lgn)                /* Rlogin client session info           */
{
     INT rc;

     switch (sndmgr((CHAR *)lgn->buffer,&lgn->bufcnt,lgn->sockt)) {
     case -1:
          rendcon(SNDERR,tcpip_errno,tcpErrStg(tcpip_errno));
          rc=-1;
          break;
     case 0:
          sktcnc(TNFSEND,lgn->sockt);
          rc=0;
          break;
     case 1:
          sktnfy(TNFSEND,lgn->sockt,(VOID (*)())lgnsnd,lgn,usrnum);
          rc=1;
          break;
     }
     lgnptr->byttfc+=sndact;
     return(rc);
}

static INT
sendwin(VOID)                      /* send initial window (screen) size    */
{
     CHAR buffer[4+sizeof(struct Winsize)];
     struct Winsize *ws;

     ws=(struct Winsize *)(buffer+4);

     buffer[0]=0xFF;
     buffer[1]=0xFF;
     buffer[2]='s';
     buffer[3]='s';
     ws->ws_row=htons((INT)usaptr->scnfse);
     ws->ws_col=htons((INT)usaptr->scnwid);
     ws->ws_xpixel=htons(100);
     ws->ws_ypixel=htons(100);
     lgnsbf((CHAR *)buffer,sizeof(buffer));
     return(lgnsnd(lgnptr));
}

/***************************************************************************
 *                                                                         *
 * This function is being added to bind() before setsockopt(), so bind()   *
 * will return an error if the port is in use.  This function should be    *
 * moved to TCPIP.C.                                                       *
 *                                                                         *
 ***************************************************************************/

static INT
tcpdialr(                          /* initiate an rlogin TCP connection    */
struct in_addr inaddr,             /*   host IP address                    */
UINT port,                         /*   port number (network byte order)   */
UINT fromport,                     /*   local port number (0=ephemeral)    */
INT *psocket)                      /*   where to store socket handle (must */
                                   /*   be -1 when this socket not open)   */
{                                  /*   see DLXXXX ret values in TCPIP.H   */
     INT serrno;

     struct sockaddr_in myaddr,rhaddr;
#ifdef UNIX
     INT async=1;
#endif

#ifdef UNIX
     if (*psocket >= 0) {
#else
     if (*psocket != INVALID_SOCKET) {
#endif
          clsskt(*psocket);
     }
#ifdef UNIX
     getpriv();
#endif
     if ((*psocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET) {
          return(DLERRS);
     }
#ifdef GCDOS
     setsndwin(*psocket,sndwin);
     setrcvwin(*psocket,rcvwin);
#endif // GCDOS
     myaddr.sin_port=htons(fromport);
     myaddr.sin_addr.s_addr=ipaddrb;
     myaddr.sin_family=AF_INET;
     setmem(myaddr.sin_zero,sizeof(myaddr.sin_zero),0);
     if (bind(*psocket,(struct sockaddr *)&myaddr,sizeof(myaddr))
                                                            == SOCKET_ERROR) {
          serrno=tcpip_errno;
          clsskt(*psocket);
#ifdef GCWINNT
          WSASetLastError(serrno);
#else
          tcpip_errno=serrno;
#endif
          *psocket=-1;
          return(DLERRB);
     }
#ifdef UNIX
     relpriv();
     if (ioctl(*psocket,FIONBIO,(CHAR *)&async) == -1) {
          clsskt(*psocket);
          *psocket=-1;
          return(DLERRI);
     }
#else
     nonblock(*psocket);
#endif
     rhaddr.sin_addr.s_addr=inaddr.s_addr;
     rhaddr.sin_port=port;
     rhaddr.sin_family=AF_INET;
     setmem(rhaddr.sin_zero,sizeof(rhaddr.sin_zero),0);
     if (connect(*psocket,(struct sockaddr *)&rhaddr,
                                            sizeof(rhaddr)) == -1) {
          if (tcpip_errno != EINPROGRESS          /* (Ipswitch's AOK       */
           && tcpip_errno != EWOULDBLOCK) {       /* (PacSoft's AOK        */
               clsskt(*psocket);
               *psocket=-1;
               return(DLERRC);
          }
          return(DLCING);
     }
     return(DLCNOW);
}
