/***************************************************************************
 *                                                                         *
 *   IDENTD.C                                                              *
 *                                                                         *
 *   Copyright (c) 1995-1997 GALACTICOMM, Inc.  All rights reserved.       *
 *                                                                         *
 *   This is the Identification Protocol (Ident) Daemon for Worldgroup,    *
 *   as outlined by RFC-1413.                                              *
 *                                                                         *
 *                                           - C. Dunn 12/29/95            *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "remote.h"
#include "tcpip.h"
#ifdef GCDOS
#include "cman.h"
#endif // GCDOS
#include "alias.h"
#include "tcport.h"
#include "identd.h"
#include "galident.h"

#define FILREV "$Revision: 9 $"

#define MINPRX 32768U              /* minimum value for proxy port         */

                                   /* usrptr->substt codes                 */
#define GETPRT -1                  /*   parsing server and client ports    */
#define LK4PRT -2                  /*   looking for ports on system        */
#define LK4PRX -3                  /*   looking for proxy ports on system  */
#define WT4EMT -4                  /*   wait for output buffers to empty   */

                                   /* getprt() return values               */
#define NOPORT 0                   /*   could not parse port from client   */
#define NRMPRT 1                   /*   normal (non-proxy) port            */
#define PRXPRT 2                   /*   proxy port (port >= 32768)         */

                                   /* fndprt() return values               */
#define NOUSER 0                   /*   no port/user found                 */
#define FNDUSR 1                   /*   port found with real User-ID       */
#define HIDUSR 2                   /*   port found; user hidden (SMTP,etc) */

static VOID idcall(INT gotchn);
static GBOOL idninp(VOID);
static VOID idnsts(VOID);
static VOID idnfin(VOID);
static INT getprt(VOID);
static INT fndprt(VOID);
static VOID enatmo(VOID);
static VOID distmo(VOID);
#ifdef GCDOS
static CHAR *fixuid(CHAR *scpuid);
#endif // GCDOS

static HMCVFILE idnmb;             /* file pointer to GALIDENT.MCV         */

static INT maxpxp;                 /* max number of proxy sessions/user    */

                                   /* options in GALIDENT.MSG              */
static GBOOL idnonl;               /*   is ident server online?            */
static GBOOL idnrej;               /*   send reject message if not online? */
static INT maxidn;                 /*   maximum number of ident channels   */
static GBOOL audidn;               /*   audit successful verifications?    */
static GBOOL audhidn;              /*   audit hidden user verifications?   */
static GBOOL audide;               /*   audit verification errors?         */
static UINT idntmo;                /*   inactivity time-out (in seconds)   */

static INT idnstt;                 /* ident server module state number     */

struct module idmodule={           /* module interface block               */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     idninp,                       /*    input routine if selected         */
     idnsts,                       /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     NULL,                         /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     idnfin                        /*    finish-up (sys shutdown) routine  */
};

VOID EXPORT
init__galident(VOID)               /* ident server initialization routine  */
{
     CHAR *pxpbuf;

     init__tcpip();
     idnmb=opnmsg("galident.mcv");
     idnonl=ynopt(IDNONL);
     idnrej=ynopt(IDNREJ);
     if (idnonl || idnrej) {
          maxidn=numopt(MAXIDN,1,250);
          audidn=ynopt(AUDIDN);
          audhidn=ynopt(AUDHIDN);
          audide=ynopt(AUDIDE);
          idntmo=(UINT)numopt(IDNTMO,60,180);
          maxpxp=0;
          if ((pxpbuf=msgscan("GALSCP.MSG","MAXPXP")) != NULL) {
               maxpxp=atoi(pxpbuf);
          }
          stzcpy(idmodule.descrp,gmdnam("galident.mdf"),MNMSIZ);
          idnstt=register_module(&idmodule);
          regtcpsvr(IDNAME,IDPORT,IDBACK,idcall);
          dclvda(sizeof(struct idnusr));
     }
     else {
          clsmsg(idnmb);
     }
}

VOID EXPORT
initwc__galident(VOID)
{
     init__galident();
}

static VOID
idcall(                            /* ident server connection callback     */
INT gotchn)                        /*   do we have a valid channel?        */
{
     struct tcpipinf *tip;

     setmbk(idnmb);
     clrprf();
     if (!gotchn) {
          sprintf(prfbuf,xlttxv(stpans(getasc(kilipg || errcod != 1 ?
                                SYSSHUT : SYSFULL)),mxmssz),numcdi("TCP/IP"));
          send(clskt,prfbuf,strlen(prfbuf),0);
     }
     else {
          setmem(idnptr,sizeof(struct idnusr),0);
          tip=&tcpipinf[usrnum];
          usrptr->usrcls=BBSPRV;
          usrptr->state=idnstt;
          usrptr->substt=GETPRT;
          usrptr->flags|=NOGLOB+NOINJO;
          btuech(usrnum,0);
          stansi();
          if (!idnonl) {
               byendl(IDNNOL);
               rejinc(IDNAME,"Ident server disabled");
          }
          else if (numonl(idnstt) > maxidn) {
               byendl(IDNMNY,maxidn);
               rejinc(IDNAME,spr("%d ident connections",maxidn));
          }
          else {
               shochl(spr("Ident contact from %s...",inet_ntoa(tip->inaddr)),
                      'I',0x1F);
               sktnfy(TNFRECV,clskt,tcpinc,tip,usrnum);
               sprintf(usaptr->userid,"(%s) ident",inet_ntoa(tip->inaddr));
               btuinj(usrnum,CYCLE);
               enatmo();
          }
     }
}

static GBOOL
idninp(VOID)                       /* ident server input routine           */
{
     setmbk(idnmb);
     switch (usrptr->substt) {
     case GETPRT:
          rstrin();
          switch (getprt()) {
          case NOPORT:
               prfmsg(IDSERR,input);
               usrptr->substt=WT4EMT;
               break;
          case NRMPRT:
               usrptr->substt=LK4PRT;
               distmo();
               break;
          case PRXPRT:
               usrptr->substt=LK4PRX;
               distmo();
               break;
          }
          break;
     }
     outprf(usrnum);
     return(TRUE);
}

static VOID
idnsts(VOID)                       /* ident server status routine          */
{
     GBOOL more;
#    ifdef GCDOS
     INT i,unum;
     cman_proxport *pxp;
     struct usracc *uptr;
#    endif // GCDOS

     setmbk(idnmb);
     if (status == RING) {
          rstchn();
          return;
     }
     if (status != CYCLE) {
          return;
     }
     more=TRUE;
     if (idnptr->sttime > 0U && (USHORT)(hrtval()/65536L)-idnptr->sttime > idntmo) {
          byendl(NO_MESSAGE);
          more=FALSE;
          if (audide) {
               shocst("IDENT TIMEOUT",
                      "Ident client at %s inactive for %d seconds.",
                      inet_ntoa(tcpipinf[usrnum].inaddr),idntmo);
          }
     }
     switch (usrptr->substt) {
     case LK4PRT:
          if (idnptr->curskt >= NUMSOCKETS) {
               prfmsg(IDPERROR,idnptr->cliprt,idnptr->srvprt);
               usrptr->substt=WT4EMT;
               if (audide) {
                    shocst("IDENT VERIFICATION ERROR",
                           "No user using local port %u to remote port %u.",
                           idnptr->cliprt,idnptr->srvprt);
               }
          }
          else {
               switch (fndprt()) {
               case NOUSER:
                    idnptr->curskt++;
                    break;
               case FNDUSR:
                    prfmsg(IDNUSR1,idnptr->cliprt,idnptr->srvprt,idnptr->alias);
                    usrptr->substt=WT4EMT;
                    if (audidn) {
                         shocst("IDENT VERIFICATION SUCCESSFUL",
                                "Alias \"%s\" successfully verified.",
                                idnptr->alias);
                    }
                    break;
               case HIDUSR:
                    prfmsg(IDNHUSER,idnptr->cliprt,idnptr->srvprt);
                    usrptr->substt=WT4EMT;
                    if (audidn && audhidn) {
                         shocst("IDENT VERIFICATION SUCCESSFUL",
                                "Hidden user succesfully verified.");
                    }
                    break;
               }
               break;
          }
          break;
     case LK4PRX:
#         ifdef GCDOS
               if (maxpxp == 0) {
                    prfmsg(IDPERROR,idnptr->cliprt,idnptr->srvprt);
                    usrptr->substt=WT4EMT;
                    break;
               }
               for (i=0 ; i < (nterms*maxpxp)-1 ; i++) {
                    pxp=&proxport[i];
                    if (idnptr->cliprt == pxp->realport
                     && pxp->flags == (PXPUOPN|PXPROPN)) {
                         unum=(i == 0 ? i : i/maxpxp);
                         if ((uptr=uacoff(unum)) != NULL) {
                              stzcpy(idnptr->alias,
                                     (*alsofusr)(fixuid(uptr->userid)),UIDSIZ);
                              prfmsg(IDNUSR1,idnptr->cliprt,idnptr->srvprt,
                                     idnptr->alias);
                              if (audidn) {
                                   shocst("IDENT VERIFICATION SUCCESSFUL",
                                          "Proxy alias \"%s\" successfully "
                                          "verified.",idnptr->alias);
                              }
                         }
                         else {
                              prfmsg(IDNHUSER,idnptr->cliprt,idnptr->srvprt);
                              if (audidn && audhidn) {
                                   shocst("IDENT VERIFICATION SUCCESSFUL",
                                          "Hidden proxy user succesfully "
                                          "verified.");
                              }
                         }
                         break;
                    }
               }
#         endif // GCDOS
          usrptr->substt=WT4EMT;
          break;
     case WT4EMT:
          if (btuoba(usrnum) == OUTSIZ-1
           && tcpipinf[usrnum].outsnk.bufcnt == 0) {
               byendl(NO_MESSAGE);
               more=FALSE;
          }
          break;
     default:
          break;
     }
     outprf(usrnum);
     if (more) {
          btuinj(usrnum,CYCLE);
     }
}

static VOID
idnfin(VOID)                       /* finish-up (system shutdown) routine  */
{
     clsmsg(idnmb);
}

static INT
getprt(VOID)                       /* get server and client port numbers   */
{
     CHAR *ptr;

     strcpy(vdatmp,input);
     if ((ptr=strchr(vdatmp,',')) != NULL) {
          *ptr++='\0';
          ptr=skptwht(ptr);
          if ((idnptr->cliprt=(USHORT)atoi(vdatmp)) != 0
           && (idnptr->srvprt=(USHORT)atoi(ptr)) != 0) {
               return(idnptr->cliprt < MINPRX ? NRMPRT : PRXPRT);
          }
     }
     return(NOPORT);
}

static INT
fndprt(VOID)                       /* find local and remote port           */
{
     INT mylen;
     struct usracc *uptr;
     struct sockaddr_in myaddr;

     setmem(&myaddr,sizeof(struct sockaddr_in),0);
     myaddr.sin_family=AF_INET;
     mylen=sizeof(myaddr);
     if (getsockname(getIdxSkt(idnptr->curskt),(struct sockaddr *)&myaddr,&mylen) != -1) {
          if (idnptr->cliprt == ntohs(myaddr.sin_port)) {
               if (sktunm[idnptr->curskt] != -1
                && (uptr=uacoff(sktunm[idnptr->curskt])) != NULL) {
                    stzcpy(idnptr->alias,(*alsofusr)(uptr->userid),UIDSIZ);
                    return(FNDUSR);
               }
               else {
                    return(HIDUSR);
               }
          }
     }
     return(NOUSER);
}

static VOID
enatmo(VOID)                       /* enable/reset time-out                */
{
     idnptr->sttime=(USHORT)(hrtval()/65536L);
}

static VOID
distmo(VOID)                       /* disable time-out                     */
{
     idnptr->sttime=0U;
}

#ifdef GCDOS
static CHAR *                      /*   returns pointer to valid User-ID   */
fixuid(                            /* fix-up SLIP/CSLIP/PPP User-ID        */
CHAR *scpuid)                      /*   User-ID - (Charles Dunn/PPP)       */
{
     CHAR *ptr;
     static CHAR uid[UIDSIZ];

     stzcpy(uid,scpuid+1,UIDSIZ);
     if ((ptr=strrchr(uid,'/')) != NULL) {
          *ptr='\0';
     }
     return(uid);
}
#endif // GCDOS
