#include <u.h>
#include <libc.h>
#include <mp.h>
#include <auth.h>
#include <libsec.h>

enum            /* internal debugging flags */
{
       DBG=                    1<<0,
       DBG_CRYPTO=             1<<1,
       DBG_PACKET=             1<<2,
       DBG_AUTH=               1<<3,
       DBG_PROC=               1<<4,
       DBG_PROTO=              1<<5,
       DBG_IO=                 1<<6,
       DBG_SCP=                1<<7,
};

enum            /* protocol packet types */
{
/* 0 */
       SSH_MSG_NONE=0,
       SSH_MSG_DISCONNECT,
       SSH_SMSG_PUBLIC_KEY,
       SSH_CMSG_SESSION_KEY,
       SSH_CMSG_USER,
       SSH_CMSG_AUTH_RHOSTS,
       SSH_CMSG_AUTH_RSA,
       SSH_SMSG_AUTH_RSA_CHALLENGE,
       SSH_CMSG_AUTH_RSA_RESPONSE,
       SSH_CMSG_AUTH_PASSWORD,

/* 10 */
       SSH_CMSG_REQUEST_PTY,
       SSH_CMSG_WINDOW_SIZE,
       SSH_CMSG_EXEC_SHELL,
       SSH_CMSG_EXEC_CMD,
       SSH_SMSG_SUCCESS,
       SSH_SMSG_FAILURE,
       SSH_CMSG_STDIN_DATA,
       SSH_SMSG_STDOUT_DATA,
       SSH_SMSG_STDERR_DATA,
       SSH_CMSG_EOF,

/* 20 */
       SSH_SMSG_EXITSTATUS,
       SSH_MSG_CHANNEL_OPEN_CONFIRMATION,
       SSH_MSG_CHANNEL_OPEN_FAILURE,
       SSH_MSG_CHANNEL_DATA,
       SSH_MSG_CHANNEL_CLOSE,
       SSH_MSG_CHANNEL_CLOSE_CONFIRMATION,
       SSH_MSG_UNIX_DOMAIN_X11_FORWARDING,     /* obsolete */
       SSH_SMSG_X11_OPEN,
       SSH_CMSG_PORT_FORWARD_REQUEST,
       SSH_MSG_PORT_OPEN,

/* 30 */
       SSH_CMSG_AGENT_REQUEST_FORWARDING,
       SSH_SMSG_AGENT_OPEN,
       SSH_MSG_IGNORE,
       SSH_CMSG_EXIT_CONFIRMATION,
       SSH_CMSG_X11_REQUEST_FORWARDING,
       SSH_CMSG_AUTH_RHOSTS_RSA,
       SSH_MSG_DEBUG,
       SSH_CMSG_REQUEST_COMPRESSION,
       SSH_CMSG_MAX_PACKET_SIZE,
       SSH_CMSG_AUTH_TIS,

/* 40 */
       SSH_SMSG_AUTH_TIS_CHALLENGE,
       SSH_CMSG_AUTH_TIS_RESPONSE,
       SSH_CMSG_AUTH_KERBEROS,
       SSH_SMSG_AUTH_KERBEROS_RESPONSE,
       SSH_CMSG_HAVE_KERBEROS_TGT,
};

enum            /* protocol flags */
{
       SSH_PROTOFLAG_SCREEN_NUMBER=1<<0,
       SSH_PROTOFLAG_HOST_IN_FWD_OPEN=1<<1,
};

enum            /* protocol constants */
{
       SSH_MAX_DATA = 256*1024,
       SSH_MAX_MSG = SSH_MAX_DATA+4,

       SESSKEYLEN = 32,
       SESSIDLEN = 16,

       COOKIELEN = 8,
};

enum            /* crypto ids */
{
       SSH_CIPHER_NONE = 0,
       SSH_CIPHER_IDEA,
       SSH_CIPHER_DES,
       SSH_CIPHER_3DES,
       SSH_CIPHER_TSS,
       SSH_CIPHER_RC4,
       SSH_CIPHER_BLOWFISH,
       SSH_CIPHER_TWIDDLE,             /* for debugging */
};

enum            /* auth method ids */
{
       SSH_AUTH_RHOSTS = 1,
       SSH_AUTH_RSA = 2,
       SSH_AUTH_PASSWORD = 3,
       SSH_AUTH_RHOSTS_RSA = 4,
       SSH_AUTH_TIS = 5,
       SSH_AUTH_USER_RSA = 6,
};

typedef struct Auth Auth;
typedef struct Authsrv Authsrv;
typedef struct Cipher Cipher;
typedef struct CipherState CipherState;
typedef struct Conn Conn;
typedef struct Msg Msg;

struct Auth
{
       int id;
       char *name;
       int (*fn)(Conn*);
};

struct Authsrv
{
       int id;
       char *name;
       int firstmsg;
       AuthInfo *(*fn)(Conn*, Msg*);
};

struct Cipher
{
       int id;
       char *name;
       CipherState *(*init)(Conn*, int isserver);
       void (*encrypt)(CipherState*, uchar*, int);
       void (*decrypt)(CipherState*, uchar*, int);
};

struct Conn
{
       int fd[2];
       CipherState *cstate;
       uchar cookie[COOKIELEN];
       uchar sessid[SESSIDLEN];
       uchar sesskey[SESSKEYLEN];
       RSApub *serverkey;
       RSApub *hostkey;
       ulong flags;
       ulong ciphermask;
       Cipher *cipher;         /* chosen cipher */
       Cipher **okcipher;      /* list of acceptable ciphers */
       int nokcipher;
       ulong authmask;
       Auth **okauth;
       int nokauth;
       char *user;
       char *host;
       char *aliases;
       int interactive;

       RSApriv *serverpriv;            /* server only */
       RSApriv *hostpriv;
       Authsrv **okauthsrv;
       int nokauthsrv;
};

struct Msg
{
       Conn *c;
       uchar type;
       ulong len;              /* output: #bytes before pos, input: #bytes after pos */
       uchar *bp;      /* beginning of allocated space */
       uchar *rp;              /* read pointer */
       uchar *wp;      /* write pointer */
       uchar *ep;      /* end of allocated space */
       Msg *link;              /* for sshnet */
};

#define LONG(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|((p)[3]))
#define PLONG(p, l) \
       (((p)[0]=(l)>>24),((p)[1]=(l)>>16),\
        ((p)[2]=(l)>>8),((p)[3]=(l)))
#define SHORT(p) (((p)[0]<<8)|(p)[1])
#define PSHORT(p,l) \
       (((p)[0]=(l)>>8),((p)[1]=(l)))

extern char Edecode[];
extern char Eencode[];
extern char Ememory[];
extern char Ehangup[];
extern int doabort;
extern int debuglevel;

extern Auth authpassword;
extern Auth authrsa;
extern Auth authtis;

extern Authsrv authsrvpassword;
extern Authsrv authsrvtis;

extern Cipher cipher3des;
extern Cipher cipherblowfish;
extern Cipher cipherdes;
extern Cipher cipherrc4;
extern Cipher ciphernone;
extern Cipher ciphertwiddle;

/* msg.c */
Msg*    allocmsg(Conn*, int, int);
void            badmsg(Msg*, int);
Msg*    recvmsg(Conn*, int);
int             sendmsg(Msg*);
uchar   getbyte(Msg*);
ushort  getshort(Msg*);
ulong   getlong(Msg*);
char*   getstring(Msg*);
void*   getbytes(Msg*, int);
mpint*  getmpint(Msg*);
RSApub* getRSApub(Msg*);
void            putbyte(Msg*, uchar);
void            putshort(Msg*, ushort);
void            putlong(Msg*, ulong);
void            putstring(Msg*, char*);
void            putbytes(Msg*, void*, long);
void            putmpint(Msg*, mpint*);
void            putRSApub(Msg*, RSApub*);
mpint*  rsapad(mpint*, int);
mpint*  rsaunpad(mpint*);
void            mptoberjust(mpint*, uchar*, int);
mpint*  rsaencryptbuf(RSApub*, uchar*, int);

/* cmsg.c */
void            sshclienthandshake(Conn*);
void            requestpty(Conn*);
int             readgeom(int*, int*, int*, int*);
void            sendwindowsize(Conn*, int, int, int, int);

/* smsg.c */
void            sshserverhandshake(Conn*);

/* pubkey.c */
enum
{
       KeyOk,
       KeyWrong,
       NoKey,
       NoKeyFile,
};
int             appendkey(char*, char*, RSApub*);
int             findkey(char*, char*, RSApub*);
int             replacekey(char*, char*, RSApub*);

/* util.c */
void            debug(int, char*, ...);
void*   emalloc(long);
void            error(char*, ...);
RSApriv*        readsecretkey(char*);
int             readstrnl(int, char*, int);
void            atexitkill(int);
void            atexitkiller(void);
void            calcsessid(Conn*);
void            sshlog(char*, ...);
void            setaliases(Conn*, char*);
void            privatefactotum(void);

#pragma varargck argpos error 1
#pragma varargck argpos sshlog 2