/* BLURB lgpl

                           Coda File System
                              Release 7

          Copyright (c) 1987-2019 Carnegie Mellon University
                  Additional copyrights listed below

This  code  is  distributed "AS IS" without warranty of any kind under
the  terms of the  GNU  Library General Public Licence  Version 2,  as
shown in the file LICENSE. The technical and financial contributors to
Coda are listed in the file CREDITS.

                        Additional copyrights

#*/

/*
                         IBM COPYRIGHT NOTICE

                          Copyright (C) 1986
             International Business Machines Corporation
                         All Rights Reserved

This  file  contains  some  code identical to or derived from the 1986
version of the Andrew File System ("AFS"), which is owned by  the  IBM
Corporation.   This  code is provided "AS IS" and IBM does not warrant
that it is free of infringement of  any  intellectual  rights  of  any
third  party.    IBM  disclaims  liability of any kind for any damages
whatsoever resulting directly or indirectly from use of this  software
or  of  any  derivative work.  Carnegie Mellon University has obtained
permission to  modify,  distribute and sublicense this code,  which is
based on Version 2  of  AFS  and  does  not  contain  the features and
enhancements that are part of  Version 3 of  AFS.  Version 3 of AFS is
commercially   available   and  supported  by   Transarc  Corporation,
Pittsburgh, PA.

*/

#ifndef _SFTP
#define _SFTP

#include <unistd.h>
#include <stdlib.h>
/*
    Features:
    1. Windowing with bit masks to avoid unnecessary retransmissions
    2. Adaptive choice of transmission parameters	(not yet implemented)
    3. Piggybacking of small files with request/response
*/

#define SFTPVERSION 3
/* Changed from 1 on 7 Jan 1988 by Satya (ThisRPCCall check added) */
/* Changed from 2 on 27 Feb 1997 by bnoble */

/* (header+body) of largest sftp packet (2 IP fragments on Ether) */
#define SFTP_MAXPACKETSIZE 2900
#define SFTP_MAXBODYSIZE (SFTP_MAXPACKETSIZE - sizeof(struct RPC2_PacketHeader))

#define SFTP_DEFPACKETSIZE (1024 + sizeof(struct RPC2_PacketHeader))
#define SFTP_DEFWINDOWSIZE 32
#define SFTP_DEFSENDAHEAD 8

/* as above (1 IP fragment on SLIP) */
#define SFTP_MINPACKETSIZE 240
#define SFTP_MINBODYSIZE (SFTP_MINPACKETSIZE - sizeof(struct RPC2_PacketHeader))
#define SFTP_MINWINDOWSIZE 2
#define SFTP_MINSENDAHEAD 1

#define SFTP_DebugLevel RPC2_DebugLevel

/* Packet Format:
   =============

Smart FTP is NOT built on top of RPC, but it does use RPC format packets for
convenience

The interpretation of header fields is similar to that in RPC:

    ProtoVersion        Set to SFTPVERSION; must match value at destination
    RemoteHandle        RPC Handle, at the packet destination, of the connection
                        on whose behalf this file transfer is being performed
    LocalHandle         RPC Handle, at the packet source, of the connection
                        on whose behalf this file transfer is being performed
    Flags               RPC2_RETRY and RPC2_ENCRYPTED have the usual meanings.
                        SFTP_ACKME is also indicated here on data packets.

    <<< Fields below here encrypted on secure connections>>>
    BodyLength          Number of bytes in packet body
    SeqNumber           SFTP Sequence number with the following properties:
                1. Data packets (Opcode SFTP_DATA) and control
                   packets (all other opcodes) form different sequences.
                   Each starts at 1 and monotonically increases by 1.
                2. Every data packet is eventually acknowledged, but control
                   packets need not always be acknowledged.
                3. The client end and server ends each have their own,
                   independent sequences.
                4. There are thus 4 sequences in a connection:
                    Client to Server, Data
                    Client to Server, Control
                    Server to Client, Data
                    Server to Client, Control
                5. The flow of packets is effectively viewed as a series of file
                   transfers from client to server and vice versa, with control
                   packets being merely annotations.  The boundary between file
                   transfers is demarcated by a single data packet with the
                   MOREDATA flag off.  The use of a separate, non-sparse
                   sequence numbers for data is what allows the use of bitmasks.

    Opcode              SFTP Op code
    SEFlags             Flags meaningful to SFTP
    SEDataOffSet        #defined to GotEmAll;
                            Makes sense only with opcode SFTP_ACK
                            Highest seq number up to and including which all
                            preceding data packets have been seen. Says nothing
                            about control packets.
    SubsysId            Set to SMARTFTP always
    ReturnCode          #defined to BitMask0	(only sensible with SFTP_ACK)
                            BitMask0..BitMask1 together form a bit string.
                            1 bits indicate received packets with seq numbers
                            greater than GotEmAll; Read left to right, bits
                            correspond to GotEmAll+1, GotEmAll+2 .... Leftmost
                            bit must be 0, by definition of GotEmAll
    Lamport             #defined to BitMask1    (only sensible with SFTP_ACK)
    Uniquefier          #defined to ThisCall    (RPC call sequence number at
                            sending side of the RPC pertaining to this side
                            effect)
    TimeStamp
    BindTime            #defined to TimeEcho
    Body                Contains actual BodyLength bytes of file data;
                            Used with SFTP_DATA and SFTP_START (for
                            conveying SFTP address)
*/

/* Renaming of RPC packet header fields */
#define GotEmAll SEDataOffset
#define BitMask0 ReturnCode
#define BitMask1 Lamport
#define TimeEcho BindTime
#define ThisRPCCall Uniquefier

/* Values of Flags field in header */
#define SFTP_ACKME 0x80000000
/* on data packets: acknowledge this packet.
   Located in Flags rather than SEFlags so that retransmits
   can turn off this bit without decryption and re-encryption */

/* Values of SEFlags field in header */
#define SFTP_MOREDATA 0x1 /* on data packets, indicates more data to come */
#define SFTP_PIGGY \
    0x2 /* on RPC packets: piggybacked info present on this packet */
#define SFTP_ALLOVER \
    0x4 /* on RPC reply packets: indicates server got all data packets */
#define SFTP_TRIGGER \
    0x8 /* on ack packets: distinguishes a server "triggered" ack from a real
           one. Necessary only for compatibility, triggers now send null
           timestamp echos */
#define SFTP_FIRST \
    0x10 /* on data packets, indicates first of group sent by source */
#define SFTP_COUNTED \
    0x20 /* on data packets: arrived or acked before last round */

/* SFTP Opcodes */
#define SFTP_START \
    1 /* Control: start sending data (flow is from RPC client to server)*/
#define SFTP_ACK 2 /* Control: acknowledgement you had requested */
#define SFTP_DATA \
    3 /* Data: next chunk; MOREDATA flag indicates whether EOF has been seen */
#define SFTP_NAK 4 /* Control: got a bogus packet from you */
#define SFTP_RESET 5 /* Control: reset transmission parameters */
#define SFTP_BUSY 6 /* Control: momentarily busy; reset your timeout counter */

/* Per-connection information: accessible via RPC2_GetSEPointer() and
 * RPC2_SetSEPointer() */
#define SFTPMAGIC 4902057
#define MAXOPACKETS 64 /* Maximum no of outstanding packets; multiple of 32 */
#define BITMASKWIDTH (MAXOPACKETS / 32) /* No of elements in integer array */

struct SFTP_Parms { /* sent in SFTP_START packets, and piggy-backed on very
                       first RPC call on a connection */
    RPC2_PortIdent Port;
    int32_t WindowSize;
    int32_t SendAhead;
    int32_t AckPoint;
    int32_t PacketSize;
    int32_t DupThreshold;
};

struct SFTP_MCParms /* Multicast parameters */
{
    uint32_t PeerSendLastContig;
};

enum SFState
{
    SFSERVER,
    SFCLIENT,
    ERROR,
    DISKERROR
};

struct SFTP_Entry /* per-connection data structure */
{
    long Magic; /* SFTPMAGIC */
    enum SFState WhoAmI;
    RPC2_Handle LocalHandle; /* which RPC2 conn on this side do I
                                correspond to? */
    RPC2_PeerInfo PInfo; /* all the RPC info  about the other side */
    struct timeval LastWord; /* Last time we received something on this SE */
    struct HEntry *HostInfo; /* Connection-independent host info. set by
                                ExaminePacket on client side (if
                                !GotParms), and sftp_ExtractParmsFromPacket
                                on server side */
    uint32_t ThisRPCCall; /* Client-side RPC sequence number of the call
                             in progress. Used to reject outdated SFTP
                             packets that may be floating around after
                             the next RPC has begun. Set on client side
                             in SFTP_MakeRPC1() and on server side on
                             SFTP_GetRequest() */
    uint32_t GotParms; /* FALSE initially; TRUE after I have
                          discovered my peer's parms */
    uint32_t SentParms; /* FALSE initially; TRUE after I have sent my
                           parms to peer */
    SE_Descriptor *SDesc; /* set by SFTP_MakeRPC1 on client side, by
                             SFTP_InitSE and SFTP_CheckSE on server side */
    long openfd; /* file descriptor: valid during actual transfer */
    off_t fd_offset; /* For FILEBYFD transfers, we save the offset
                        within the file after each read/write */
    struct SL_Entry *Sleeper; /* SL_Entry of LWP sleeping on this connection,
                                 or NULL */
    struct rpc2_LinkEntry *RecvQueue; /* queue of received packet buffers */
    long RecvQueueLen;
    uint32_t PacketSize; /* Amount of  data in each packet */
    uint32_t WindowSize; /* Max Number of outstanding packets without
                            acknowledgement <= MAXOPACKETS */
    uint32_t SendAhead; /* How many more packets to send after
                           demanding an ack. Equal to read-ahead  */
    uint32_t AckPoint; /* After how many send ahead packets should an
                          ack be demanded? */
    uint32_t DupThreshold; /* How many duplicate data packets can I see
                              before sending Ack spontaneously? */
    uint32_t RetryCount; /* How many times to retry Ack request */
    uint32_t ReadAheadCount; /* How many packets have been read by read
                                strategy routine */
    uint32_t CtrlSeqNumber; /* Seq number of last control packet sent out */
    uint32_t Retransmitting; /* FALSE initially; TRUE prevents RTT update */
    uint32_t TimeEcho; /* Timestamp to send on next packet (valid
                          when not retransmitting) */
    struct timeval LastSS; /* time SendStrategy was last invoked by an
                              Ack on this connection */
    SE_Descriptor *PiggySDesc; /* malloc()ed copy of SDesc; held on until
                                 SendResponse, if piggybacking might take
                                 place */

    /*  Transmission Parameters:

      INVARIANTs (when XferState = XferInProgress):
      ==========
      1. SendLastContig <= SendWorriedLimit <= SendAckLimit <= SendMostRecent
      2. (SendMostRecent - SendLastContig) <= WindowSize
      3. (SendMostRecent - SendAckLimit) <= SendAhead
      4. RecvLastContig <= RecvMostRecent
      5. (RecvMostRecent - RecvLastContig) <= WindowSize

      INVARIANTs (when XferState = {XferNotStarted,XferAborted,XferCompleted}):
      =========
      1. SendLastContig (at source) = SendMostRecent (at source)
      2. RecvLastContig (at sink) = RecvMostRecent (at sink)
      3. SendLastContig (at source) = RecvLastContig (at sink)
    */
    enum
    {
        XferNotStarted = 0,
        XferInProgress = 1,
        XferCompleted  = 2
    } XferState;

    /* Next block is multicast specific */
    uint32_t RepliedSinceLastSS; /* TRUE iff {ACK,NAK,START} received since last
                                  invocation of SendStrategy */
    uint32_t McastersStarted; /* number of individual conns participating */
    uint32_t McastersFinished; /* number of participating conns which have
                                  finished */
    uint32_t FirstSeqNo;
#define SendFirst FirstSeqNo
    /* Value of SendLastContig (+1) when Multicast MRPC call is initiated */
#define RecvFirst FirstSeqNo
    /* Value of RecvMostRecent (+1) when Multicast MRPC call is initiated */

    uint32_t HitEOF; /* source side: EOF has been seen by read strategy routine
                        sink side: last packet for this transfer has been received */
    uint32_t SendLastContig; /* Seq no. of packet before (and including) which NO
                                state is maintained. This is the most recent
                                packet such that it and all earlier packets
                                are known by me to have been received by
                                the other side */
    uint32_t SendMostRecent; /* SendMostRecent is the latest data packet we have
                                sent out  */

    /* Bit pattern of packets in the range SendLastContig+1..SendMostRecent
       that have successfully been sent by me AND are known by me to have
       been received by other side */
    unsigned int SendTheseBits[BITMASKWIDTH];

    uint32_t SendAckLimit; /* Highest data packet for which an ack has been
                              requested. */
    uint32_t SendWorriedLimit; /* Highest data packet about which we are
                                  worried. */
    uint32_t RecvLastContig; /* Most recent data packet up to which I no longer
                                maintain state */
    uint32_t RecvMostRecent; /* Highest numbered data packet seen so far */
    uint32_t DupsSinceAck; /* Duplicates seen since the last ack I sent */
    uint32_t RecvSinceAck; /* Packets received since the last ack I sent */

    uint32_t RequestTime; /* arrival time of packet, to correct RTT
                             estimates for processing time */

    /* Packets in RecvLastContig+1..RecvMostRecent that I have received */
    unsigned int RecvTheseBits[BITMASKWIDTH];

    RPC2_PacketBuffer *ThesePackets[MAXOPACKETS];
    /* Packets being currently dealt with. There can be at most MAXOPACKETS
     * outstanding, in the range LastContig+1..LastContig+WindowSize. The
     * index of the i'th packet is given by (i % MAXOPACKETS).
     *
     * Some of these pointers, may be NULL for the following reasons:
     * Receiving side:  The packets have not been received, or have
     *			been received and written to disk already.
     * Sending side: The packets have been sent, and an ACK for them has been
     * received.
     */
    struct security_association *sa;
};

extern long SFTP_DebugLevel;

int sftp_XmitPacket(struct SFTP_Entry *sentry, RPC2_PacketBuffer *pb,
                    int confirm);
void sftp_Timer(void);
void sftp_ExaminePacket(RPC2_PacketBuffer *pb);

#define IsSource(sfe)                                                         \
    ((sfe->WhoAmI == SFCLIENT && sfe->SDesc &&                                \
      sfe->SDesc->Value.SmartFTPD.TransmissionDirection == CLIENTTOSERVER) || \
     (sfe->WhoAmI == SFSERVER && sfe->SDesc &&                                \
      sfe->SDesc->Value.SmartFTPD.TransmissionDirection == SERVERTOCLIENT))

#define IsSink(sfe)                                                           \
    ((sfe->WhoAmI == SFCLIENT && sfe->SDesc &&                                \
      sfe->SDesc->Value.SmartFTPD.TransmissionDirection == SERVERTOCLIENT) || \
     (sfe->WhoAmI == SFSERVER && sfe->SDesc &&                                \
      sfe->SDesc->Value.SmartFTPD.TransmissionDirection == CLIENTTOSERVER))

/* Operations on integer array bitmask; leftmost position is 1, rightmost is
   32*BITMASKWIDTH Choice of 1 rather than 0 is deliberate:
   {Send,Recv}LastContig+1 corresponds to leftmost bit */
#define WORDOFFSET(pos) (((pos)-1) >> 5) /* avoid / operator */
#define BITOFFSET(pos) ((((pos)-1) & 31) + 1) /* avoid % operator */
#define PM(pos) (1L << (32 - (BITOFFSET(pos))))
#define SETBIT(mask, pos) ((mask)[WORDOFFSET(pos)] |= PM(pos))
#define TESTBIT(mask, pos) ((mask)[WORDOFFSET(pos)] & PM(pos))
#define CLEARBIT(mask, pos) ((mask)[WORDOFFSET(pos)] &= (~PM(pos)))

/* Packet buffer position */
#define PBUFF(x) ((x) & (MAXOPACKETS - 1)) /* effectively modulo operator */

/* The transmission parameters below are initial values; actual ones are
 * per-connection */
extern long SFTP_PacketSize;
extern long SFTP_WindowSize;
extern long SFTP_EnforceQuota; /* Nonzero to activate ByteQuota in
                                  SE_Descriptors */
extern long SFTP_SendAhead;
extern long SFTP_AckPoint;
extern long SFTP_DupThreshold;
extern long SFTP_DoPiggy; /* FALSE to suppress piggybacking */

/* SE routines invoked by base RPC2 code */
long SFTP_Init();
long SFTP_Bind1(RPC2_Handle ConnHandle, RPC2_CountedBS *ClientIdent);
long SFTP_Bind2(RPC2_Handle ConnHandle, RPC2_Unsigned BindTime);
long SFTP_Unbind(RPC2_Handle ConnHandle);
long SFTP_NewConn(RPC2_Handle ConnHandle, RPC2_CountedBS *ClientIdent);
long SFTP_MakeRPC1(RPC2_Handle ConnHandle, SE_Descriptor *SDesc,
                   RPC2_PacketBuffer **RequestPtr);
long SFTP_MakeRPC2(RPC2_Handle ConnHandle, SE_Descriptor *SDesc,
                   RPC2_PacketBuffer *Reply);
long SFTP_MultiRPC1();
long SFTP_MultiRPC2();
long SFTP_CreateMgrp();
long SFTP_AddToMgrp();
long SFTP_InitMulticast();
long SFTP_DeleteMgrp();
long SFTP_GetRequest(RPC2_Handle ConnHandle, RPC2_PacketBuffer *Request);
long SFTP_InitSE(RPC2_Handle ConnHandle, SE_Descriptor *SDesc);
long SFTP_CheckSE(RPC2_Handle ConnHandle, SE_Descriptor *SDesc, long Flags);
long SFTP_SendResponse(RPC2_Handle ConnHandle, RPC2_PacketBuffer **Reply);
long SFTP_PrintSED(SE_Descriptor *SDesc, FILE *outFile);
void SFTP_SetDefaults(SFTP_Initializer *initPtr);
void SFTP_Activate(SFTP_Initializer *initPtr);
long SFTP_GetTime(RPC2_Handle ConnHandle, struct timeval *Time);
long SFTP_GetHostInfo(RPC2_Handle ConnHandle, struct HEntry **hPtr);

/* Internal SFTP routines */
int sftp_InitIO(struct SFTP_Entry *sEntry);
int sftp_DataArrived(RPC2_PacketBuffer *pBuff, struct SFTP_Entry *sEntry);
int sftp_WriteStrategy(struct SFTP_Entry *sEntry);
int sftp_AckArrived(RPC2_PacketBuffer *pBuff, struct SFTP_Entry *sEntry);
int sftp_SendStrategy(struct SFTP_Entry *sEntry);
int sftp_ReadStrategy(struct SFTP_Entry *sEntry);
int sftp_SendStart(struct SFTP_Entry *sEntry);
int sftp_StartArrived(RPC2_PacketBuffer *pBuff, struct SFTP_Entry *sEntry);
int sftp_SendTrigger(struct SFTP_Entry *sEntry);
void sftp_InitPacket(RPC2_PacketBuffer *pb, struct SFTP_Entry *sfe,
                     long bodylen);
void sftp_InitTrace(void);
int sftp_vfwritefile(struct SFTP_Entry *se, char *buf, int nbytes);
void sftp_vfclose(struct SFTP_Entry *se);
int sftp_piggybackfileread(struct SFTP_Entry *se, char *buf);
off_t sftp_piggybackfilesize(struct SFTP_Entry *se);
void sftp_TraceBogus(long filenum, long linenum);
void sftp_TraceStatus(struct SFTP_Entry *sEntry, int filenum, int linenum);
void sftp_DumpTrace(char *fName);
void sftp_Progress(SE_Descriptor *sdesc, off_t BytesTransferred);

void sftp_UpdateRTT(RPC2_PacketBuffer *pb, struct SFTP_Entry *sEntry,
                    unsigned long inbytes, unsigned long outbytes);

struct SFTP_Entry *sftp_AllocSEntry(void);
void sftp_FreeSEntry(struct SFTP_Entry *se);
void sftp_AllocPiggySDesc(struct SFTP_Entry *se, off_t len,
                          enum WhichWay direction);
void sftp_FreePiggySDesc(struct SFTP_Entry *se);
int sftp_AppendParmsToPacket(struct SFTP_Entry *sEntry,
                             RPC2_PacketBuffer **whichP);
int sftp_ExtractParmsFromPacket(struct SFTP_Entry *sEntry,
                                RPC2_PacketBuffer *whichP);
off_t sftp_AppendFileToPacket(struct SFTP_Entry *sEntry,
                              RPC2_PacketBuffer **whichP);
off_t sftp_ExtractFileFromPacket(struct SFTP_Entry *sEntry,
                                 RPC2_PacketBuffer *whichP);
int sftp_AddPiggy(RPC2_PacketBuffer **whichP, char *dPtr, off_t dSize,
                  unsigned int maxSize);
void sftp_SetError(struct SFTP_Entry *s, enum SFState e);
int sftp_MorePackets(void);

extern long sftp_datas, sftp_datar, sftp_acks, sftp_ackr, sftp_busy,
    sftp_triggers, sftp_starts, sftp_retries, sftp_timeouts, sftp_windowfulls,
    sftp_duplicates, sftp_bogus, sftp_ackslost, sftp_didpiggy, sftp_starved,
    sftp_rttupdates;

extern long sftp_PacketsInUse;
extern long SFTP_MaxPackets;

/* SFTP's version of RPC2_AllocBuffer and RPC2_FreeBuffer */

#define SFTP_AllocBuffer(x, y) (sftp_PacketsInUse++, RPC2_AllocBuffer(x, y))

#define SFTP_FreeBuffer(x) (sftp_PacketsInUse--, RPC2_FreeBuffer(x))

/* For encryption and decryption */
#define sftp_Encrypt(pb, sfe)                                          \
    rpc2_Encrypt((char *)&pb->Header.BodyLength,                       \
                 (char *)&pb->Header.BodyLength,                       \
                 pb->Prefix.LengthOfPacket - 4 * sizeof(RPC2_Integer), \
                 sfe->PInfo.SessionKey, sfe->PInfo.EncryptionType)

#define sftp_Decrypt(pb, sfe)                                          \
    rpc2_Decrypt((char *)&pb->Header.BodyLength,                       \
                 (char *)&pb->Header.BodyLength,                       \
                 pb->Prefix.LengthOfPacket - 4 * sizeof(RPC2_Integer), \
                 sfe->PInfo.SessionKey, sfe->PInfo.EncryptionType)
#endif /* _SFTP */

/* Predicate to test if file is in vm */
#define MEMFILE(s) (s->Value.SmartFTPD.Tag == FILEINVM)
#define BYFDFILE(s) (s->Value.SmartFTPD.Tag == FILEBYFD)

/* test if we can send an TimeEcho response */
#ifdef VERY_FAST_SERVERS
#define VALID_TIMEECHO(se) \
    (!(se)->Retransmitting && (se)->TimeEcho != 0 && (se)->RequestTime != 0)
#else
#define VALID_TIMEECHO(se) (!(se)->Retransmitting && (se)->TimeEcho != 0)
#endif
