/*
                                  NETWOX
                             Network toolbox
                Copyright(c) 1999-2005 Laurent Constantin
                                  -----

  Main server    : http://www.laurentconstantin.com/
  Backup servers : http://go.to/laurentconstantin/
                   http://laurentconstantin.est-la.com/
                   http://laurentconstantin.free.fr/
                   http://membres.lycos.fr/lauconstantin/
  [my current email address is on the web servers]

                                  -----
  This file is part of Netwox.

  Netwox is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  version 2 as published by the Free Software Foundation.

  Netwox is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details (http://www.gnu.org/).

------------------------------------------------------------------------
*/

/*-------------------------------------------------------------*/
#include "../netwox.h"

/*-------------------------------------------------------------*/
netwib_err netwox_smbser_init(netwox_constsmbserver *pcommon,
                              netwib_io *pio,
                              netwox_smbser *psmbser)
{

  psmbser->pcommon = pcommon;
  psmbser->pio = pio;

  netwib_er(netwib_bufpool_buf_init(psmbser->pcommon->pbufpool,
                                    &psmbser->pbufrecv));
  psmbser->pbufrecv->flags |= NETWIB_BUF_FLAGS_CANSLIDE;
  psmbser->smberrtosend = NETWOX_SMBERR_OK;

  psmbser->cmn_treeid = 0;
  psmbser->cmn_processid = 0;
  psmbser->cmn_userid = 0;
  psmbser->cmn_multiplexid = 0;
  psmbser->cmn_secumode = NETWOX_SMBCMD_NEGOTIATE_SECUMODE_USERLEVEL | NETWOX_SMBCMD_NEGOTIATE_SECUMODE_CHALRESP;
  psmbser->cmn_maxmsgsize = 32000;
  psmbser->cmn_vcsessionid = 0;
  psmbser->cmn_capabilities = NETWOX_SMBCMDCMN_NEGOCAPA_UNICODE | NETWOX_SMBCMDCMN_NEGOCAPA_NTERROR;
  netwib_er(netwib_bufpool_buf_init(psmbser->pcommon->pbufpool,
                                    &psmbser->pcmn_challenge));

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbser_close(netwox_smbser *psmbser)
{
  netwib_er(netwib_bufpool_buf_close(psmbser->pcommon->pbufpool,
                                     &psmbser->pcmn_challenge));
  netwib_er(netwib_bufpool_buf_close(psmbser->pcommon->pbufpool,
                                     &psmbser->pbufrecv));
  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbser_smbmsg_setdefault(netwox_smbser *psmbser,
                                           netwox_smbmsg *psmbmsg)
{
  netwib_er(netwox_smbmsg_setdefault(psmbmsg));

  psmbmsg->hdr.flags |= NETWOX_SMBMSG_FLAGS_CASELESSPATHNAMES;
  psmbmsg->hdr.flags |= NETWOX_SMBMSG_FLAGS_REPLY;
  psmbmsg->hdr.flags2 |= NETWOX_SMBMSG_FLAGS2_ACCEPTSLONGPATHNAME;
  psmbmsg->hdr.flags2 |= NETWOX_SMBMSG_FLAGS2_USESLONGPATHNAME;
  if (psmbser->cmn_capabilities & NETWOX_SMBCMDCMN_NEGOCAPA_NTERROR) {
    psmbmsg->hdr.flags2 |= NETWOX_SMBMSG_FLAGS2_NTERROR;
  }
  if (psmbser->cmn_capabilities & NETWOX_SMBCMDCMN_NEGOCAPA_UNICODE) {
    psmbmsg->hdr.flags2 |= NETWOX_SMBMSG_FLAGS2_UNICODESTRINGS;
  }
  psmbmsg->hdr.treeid = psmbser->cmn_treeid;
  psmbmsg->hdr.processid = psmbser->cmn_processid;
  psmbmsg->hdr.userid = psmbser->cmn_userid;
  psmbmsg->hdr.multiplexid = psmbser->cmn_multiplexid;

  return(NETWIB_ERR_OK);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbser_smbmsg_send(netwox_smbser *psmbser,
                                     netwox_smbmsg *psmbmsg)
{
  netwib_buf *pbuf;
  netwib_err ret;

  /* show message */
  if (psmbser->pcommon->debug) {
    netwib_er(netwox_smbmsg_display(psmbser->pcommon->pbufpool, psmbmsg));
  }

  /* generate packet */
  netwib_er(netwib_bufpool_buf_init(psmbser->pcommon->pbufpool, &pbuf));
  netwib_er(netwox_pkt_append_smbmsghdr_begin(psmbser->pcommon->isnaked,
                                              pbuf));
  netwib_er(netwox_pkt_append_smbmsg(psmbmsg, pbuf));
  netwib_er(netwox_pkt_append_smbmsghdr_end(psmbser->pcommon->isnaked, pbuf));

  /* send it */
  ret = netwib_io_write(psmbser->pio, pbuf);
  netwib_er(netwib_bufpool_buf_close(psmbser->pcommon->pbufpool, &pbuf));

  return(ret);
}

/*-------------------------------------------------------------*/
static netwib_err netwox_smbser_smbmsg_recv_nbtss(netwox_smbser *psmbser,
                                                  netwib_uint32 *pskipsize)
{
  netwox_smbnbtss smbnbtssq, smbnbtssr;
  netwib_buf *pbuf;
  netwib_err ret;

  netwib_er(netwox_pkt_decode_smbnbtss(psmbser->pbufrecv, &smbnbtssq,
                                       pskipsize));

  if (smbnbtssq.smbnbtsstype == NETWOX_SMBNBTSSTYPE_KEEPALIVE) {
    /* nothing to do */
    return(NETWIB_ERR_OK);
  }

  if (smbnbtssq.smbnbtsstype != NETWOX_SMBNBTSSTYPE_REQUEST) {
    /* only deal with request */
    return(NETWIB_ERR_NOTCONVERTED);
  }

  /* always accept session, without checking request values */
  smbnbtssr.smbnbtsstype = NETWOX_SMBNBTSSTYPE_RESPPOSITIVE;
  netwib_er(netwib_bufpool_buf_init(psmbser->pcommon->pbufpool, &pbuf));
  netwib_er(netwox_pkt_append_smbnbtss(&smbnbtssr, pbuf));
  ret = netwib_io_write(psmbser->pio, pbuf);
  netwib_er(netwib_bufpool_buf_close(psmbser->pcommon->pbufpool, &pbuf));

  return(ret);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbser_smbmsg_recv(netwox_smbser *psmbser,
                                     netwox_smbmsg *psmbmsg)
{
  netwib_time t;
  netwib_bool event, tryread;
  netwib_bufext smbmsgbuf;
  netwib_uint32 skipsize;
  netwib_err ret=NETWIB_ERR_OK;

  netwib_er(netwib_time_init_now(&t));
  netwib_er(netwib_time_plus_msec(&t, psmbser->pcommon->maxwaitms));
  tryread = NETWIB_FALSE;
  while (NETWIB_TRUE) {
    if (tryread) {
      netwib_er(netwib_io_wait_read(psmbser->pio, &t, &event));
      if (!event) {
        ret = NETWOX_ERR_TIMEOUT;
        break;
      }
      ret = netwib_io_read(psmbser->pio, psmbser->pbufrecv);
      if (ret == NETWIB_ERR_DATANOTAVAIL) {
        continue;
      } else if (ret != NETWIB_ERR_OK) {
        break;
      }
    }
    tryread = NETWIB_TRUE;
    if (!psmbser->pcommon->isnaked) {
      /* special nbtss messages : treat then skip */
      ret = netwox_smbser_smbmsg_recv_nbtss(psmbser, &skipsize);
      if (ret == NETWIB_ERR_OK) {
        psmbser->pbufrecv->beginoffset += skipsize;
        continue;
      }
    }
    ret = netwox_pkt_decode_smbmsghdr(psmbser->pbufrecv,
                                      psmbser->pcommon->isnaked,
                                      &smbmsgbuf, &skipsize);
    if (ret == NETWIB_ERR_DATAMISSING) {
      continue;
    } else if (ret != NETWIB_ERR_OK) {
      break;
    }
    ret = netwox_pkt_decode_smbmsg(&smbmsgbuf, psmbmsg);
    if (ret == NETWIB_ERR_DATAMISSING) {
      continue;
    } else if (ret != NETWIB_ERR_OK) {
      break;
    }
    psmbser->pbufrecv->beginoffset += skipsize;
    break;
  }

  if (ret == NETWIB_ERR_OK) {
    /* show message */
    if (psmbser->pcommon->debug) {
      netwib_er(netwox_smbmsg_display(psmbser->pcommon->pbufpool, psmbmsg));
    }
    /* check values */
    if (psmbmsg->hdr.error != 0) {
      return(NETWOX_ERR_PROTOCOL);
    }
    if (psmbser->cmn_processid != 0) {
      if (psmbser->cmn_processid != psmbmsg->hdr.processid) {
        return(NETWOX_ERR_PROTOCOL);
      }
    } else {
      psmbser->cmn_processid = psmbmsg->hdr.processid;
    }
    if (psmbser->cmn_multiplexid != 0) {
      if (psmbser->cmn_multiplexid != psmbmsg->hdr.multiplexid) {
        return(NETWOX_ERR_PROTOCOL);
      }
    } else {
      psmbser->cmn_multiplexid = psmbmsg->hdr.multiplexid;
    }
    if (psmbser->cmn_userid != 0 && psmbser->cmn_userid!=psmbmsg->hdr.userid) {
      return(NETWOX_ERR_PROTOCOL);
    }
    if (psmbser->cmn_treeid != 0 && psmbser->cmn_treeid!=psmbmsg->hdr.treeid) {
      return(NETWOX_ERR_PROTOCOL);
    }
  }

  return(ret);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbser_smberr_send(netwox_smbser *psmbser,
                                     netwox_smbmsg_cmd smbcmd,
                                     netwox_smberr smberr)
{
  netwox_smbmsg smbmsg;
  netwib_err ret;

  /* init */
  netwib_er(netwox_smbmsg_init(&smbmsg));
  netwib_er(netwox_smbser_smbmsg_setdefault(psmbser, &smbmsg));
  netwib_er(netwox_smberr_init(smberr,
                               smbmsg.hdr.flags2&NETWOX_SMBMSG_FLAGS2_NTERROR,
                               &smbmsg.hdr.error));
  smbmsg.numcmds = 1;
  smbmsg.cmds[0].cmd = smbcmd;

  /* send message */
  ret = netwox_smbser_smbmsg_send(psmbser, &smbmsg);

  /* close */
  netwib_er(netwox_smbmsg_close(&smbmsg));

  return(ret);
}

/*-------------------------------------------------------------*/
netwib_err netwox_smbser_negotiate(netwox_smbser *psmbser,
                                   netwox_constsmbcmd *psmbcmdq)
{
  netwox_smbmsg smbmsgr;
  netwox_smbcmd smbcmdr;
  netwib_time t;
  netwib_localtime lt;
  netwib_err ret;

  if (psmbser->pcommon->verbose) {
    netwib_er(netwib_fmt_display("Function netwox_smbser_negotiate\n"));
  }

  /* check query */
  if (psmbcmdq->value.negotiate_q.ntlm012position == 0) {
    return(NETWOX_ERR_PROTOCOL);
  }

  /* init */
  ret = NETWIB_ERR_OK;
  netwib_er(netwox_smbmsg_init(&smbmsgr));
  netwib_er(netwox_smbcmd_init(&smbcmdr));

  /* send reply */
  netwib_eg(netwox_smbser_smbmsg_setdefault(psmbser, &smbmsgr));
  netwib_eg(netwox_smbcmd_selecttype(&smbcmdr,
                                     NETWOX_SMBCMD_TYPE_NEGOTIATE_R0));
  netwib_eg(netwox_smbcmd_setdefault(&smbcmdr));
  smbcmdr.value.negotiate_r0.dialectindex = psmbcmdq->value.negotiate_q.ntlm012position - 1;
  smbcmdr.value.negotiate_r0.secumode = psmbser->cmn_secumode;
  smbcmdr.value.negotiate_r0.maxmultiplex = 1;
  smbcmdr.value.negotiate_r0.maxvc = 1;
  smbcmdr.value.negotiate_r0.maxmsgsize = psmbser->cmn_maxmsgsize;
  smbcmdr.value.negotiate_r0.vcsessionid = psmbser->cmn_vcsessionid;
  smbcmdr.value.negotiate_r0.capabilities = psmbser->cmn_capabilities;
  netwib_eg(netwib_time_init_now(&t));
  netwib_eg(netwox_time1601_init_time(&t, &smbcmdr.value.negotiate_r0.systemtime));
  netwib_eg(netwib_time_decode_localtime(&t, &lt));
  smbcmdr.value.negotiate_r0.timezoneoffset = (netwib_uint16)(lt.zoneoffset/60);
  netwib_eg(netwox_winauth_lm_chal_gene(&smbcmdr.value.negotiate_r0.challenge));

  netwib_eg(netwox_smbmsg_append_smbcmd(&smbcmdr, &smbmsgr));
  netwib_eg(netwox_smbser_smbmsg_send(psmbser, &smbmsgr));

  /* close */
 netwib_gotolabel:
  netwib_er(netwox_smbcmd_close(&smbcmdr));
  netwib_er(netwox_smbmsg_close(&smbmsgr));
  return(ret);
}
