diff --git a/ChangeLog b/ChangeLog index 69b046f3..e9570e09 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,9 +3,12 @@ $Id$ v0.39.6 11-Jan-2004 mbcico: - New binkp/1.1 driver fixes for Sun NetBSD. - Removed TCP ignore check in EOB state. - Transmiter file close now right after last block. + New binkp/1.1 driver. Remember to check your Irex links, see + the warning with version 0.39.5. In case of troubles please + report these. In the mbcico directory are 2 files, binkpold.c + and binkpold.h if they are renamed to binkp.c and binkp.h you + get the old 1.0 driver back. After some weeks I hope to remove + these 2 old files. Added support for T-Mail fileboxes, partly written and info provided by Przemyslaw Kwiatkowski (2:480/127). diff --git a/mbcico/Makefile b/mbcico/Makefile index 1c6b3762..4ddf1474 100644 --- a/mbcico/Makefile +++ b/mbcico/Makefile @@ -4,7 +4,7 @@ include ../Makefile.global -SRCS = zmmisc.c zmrle.c zmrecv.c zmsend.c binkp.c binkpnew.c md5b.c \ +SRCS = zmmisc.c zmrle.c zmrecv.c zmsend.c binkp.c md5b.c \ xmsend.c xmrecv.c m7recv.c m7send.c hydra.c inbound.c \ answer.c chat.c dial.c dietifna.c emsidat.c filelist.c \ openfile.c openport.c opentcp.c rdoptions.c yoohoo.c \ @@ -13,7 +13,7 @@ SRCS = zmmisc.c zmrle.c zmrecv.c zmsend.c binkp.c binkpnew.c md5b.c \ ttyio.c lutil.c scanout.c emsi.c ulock.c \ callstat.c session.c call.c mbcico.c \ outstat.c nlinfo.c mbout.c mbtelind.c mbtelout.c -HDRS = zmodem.h binkp.h binkpnew.h config.h statetbl.h md5b.h \ +HDRS = zmodem.h binkp.h config.h statetbl.h md5b.h \ xmsend.h xmrecv.h m7recv.h m7send.h hydra.h inbound.h \ answer.h chat.h dial.h dietifna.h emsidat.h filelist.h \ openfile.h openport.h opentcp.h rdoptions.h yoohoo.h \ @@ -22,7 +22,7 @@ HDRS = zmodem.h binkp.h binkpnew.h config.h statetbl.h md5b.h \ ttyio.h lutil.h scanout.h emsi.h ulock.h \ callstat.h session.h call.h mbcico.h \ outstat.h nlinfo.h mbtelind.h mbtelout.h -MBCICO_OBJS = zmmisc.o zmrle.o zmrecv.o zmsend.o binkp.o binkpnew.o md5b.o \ +MBCICO_OBJS = zmmisc.o zmrle.o zmrecv.o zmsend.o binkp.o md5b.o \ xmsend.o xmrecv.o m7recv.o m7send.o hydra.o inbound.o \ answer.o chat.o dial.o dietifna.o emsidat.o filelist.o \ openfile.o openport.o opentcp.o rdoptions.o yoohoo.o \ @@ -100,7 +100,6 @@ zmrle.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/clcomm.h ../lib/commo zmrecv.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/clcomm.h ../lib/common.h ../lib/nodelist.h lutil.h ttyio.h session.h zmodem.h config.h emsi.h openfile.h filelist.h openport.h zmsend.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/common.h ../lib/clcomm.h ../lib/nodelist.h ttyio.h session.h zmodem.h lutil.h emsi.h filelist.h binkp.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/common.h ../lib/nodelist.h ../lib/dbnode.h ../lib/clcomm.h ../lib/mberrors.h ttyio.h session.h statetbl.h config.h emsi.h openfile.h respfreq.h filelist.h opentcp.h rdoptions.h lutil.h binkp.h config.h md5b.h inbound.h -binkpnew.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/common.h ../lib/nodelist.h ../lib/dbnode.h ../lib/clcomm.h ../lib/mberrors.h ttyio.h session.h statetbl.h config.h emsi.h openfile.h respfreq.h filelist.h opentcp.h rdoptions.h lutil.h binkpnew.h config.h md5b.h inbound.h md5b.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/common.h ../lib/nodelist.h ../lib/clcomm.h lutil.h md5b.h xmsend.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/common.h ../lib/nodelist.h ../lib/clcomm.h session.h ttyio.h statetbl.h xmsend.h m7send.h filelist.h filetime.h xmrecv.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/common.h ../lib/nodelist.h ../lib/clcomm.h session.h ttyio.h statetbl.h config.h lutil.h openfile.h m7recv.h xmrecv.h filelist.h filetime.h @@ -136,7 +135,7 @@ scanout.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/reco emsi.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/common.h ../lib/nodelist.h ../lib/dbnode.h ../lib/clcomm.h ../lib/mberrors.h ttyio.h session.h statetbl.h config.h emsi.h emsidat.h hydra.h rdoptions.h tcp.h wazoo.h inbound.h ulock.o: ../config.h ../lib/libs.h ../lib/clcomm.h callstat.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/clcomm.h ../lib/common.h callstat.h -session.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/common.h ../lib/clcomm.h ../lib/nodelist.h ../lib/mberrors.h ttyio.h statetbl.h emsi.h ftsc.h session.h yoohoo.h mbcico.h binkpnew.h binkp.h callstat.h inbound.h opentcp.h +session.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/common.h ../lib/clcomm.h ../lib/nodelist.h ../lib/mberrors.h ttyio.h statetbl.h emsi.h ftsc.h session.h yoohoo.h mbcico.h binkp.h callstat.h inbound.h opentcp.h call.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/common.h ../lib/nodelist.h ../lib/clcomm.h ../lib/dbnode.h ../lib/mberrors.h session.h callstat.h call.h config.h dial.h lutil.h portsel.h openport.h opentcp.h rdoptions.h inbound.h mbcico.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/common.h ../lib/nodelist.h ../lib/clcomm.h ../lib/dbcfg.h ../lib/dbnode.h ../lib/dbftn.h ../lib/mberrors.h config.h answer.h call.h lutil.h mbcico.h session.h outstat.o: ../config.h ../lib/libs.h ../lib/structs.h ../lib/users.h ../lib/records.h ../lib/common.h ../lib/nodelist.h ../lib/clcomm.h ../lib/dbcfg.h ../lib/dbnode.h ../lib/dbftn.h ../lib/mberrors.h scanout.h callstat.h outstat.h diff --git a/mbcico/binkp.c b/mbcico/binkp.c index 673f3459..eaf0ea14 100644 --- a/mbcico/binkp.c +++ b/mbcico/binkp.c @@ -5,7 +5,7 @@ * Binkp protocol copyright : Dima Maloff. * ***************************************************************************** - * Copyright (C) 1997-2003 + * Copyright (C) 1997-2004 * * Michiel Broek FIDO: 2:280/2802 * Beekmansbos 10 @@ -31,7 +31,7 @@ #include "../config.h" -#ifndef USE_NEWBINKP + #include "../lib/libs.h" #include "../lib/structs.h" @@ -65,38 +65,15 @@ #define BNKCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@&=+%$-_.!()#|" -static char rbuf[MAX_BLKSIZE + 1]; static char *bstate[] = { - (char *)"NUL", (char *)"ADR", (char *)"PWD", (char *)"FILE", (char *)"OK", - (char *)"EOB", (char *)"GOT", (char *)"ERR", (char *)"BSY", (char *)"GET", - (char *)"SKIP" + (char *)"M_NUL", (char *)"M_ADR", (char *)"M_PWD", (char *)"M_FILE", (char *)"M_OK", + (char *)"M_EOB", (char *)"M_GOT", (char *)"M_ERR", (char *)"M_BSY", (char *)"M_GET", + (char *)"M_SKIP" }; -/* - * Local prototypes - */ -void binkp_init(void); -void binkp_deinit(void); -char *unix2binkp(char *); -char *binkp2unix(char *); -int binkp_expired(void); -void b_banner(void); -void b_nul(char *); -void fill_binkp_list(binkp_list **, file_list *, off_t); -void debug_binkp_list(binkp_list **); -void binkp_send_data(char *, int); -void binkp_send_control(int id, ...); -int binkp_recv_frame(char *, int *, int *); -void binkp_settimer(int); -int resync(off_t); -static int orgbinkp(void); -static int ansbinkp(void); -static int binkp_batch(file_list *); - - extern char *tempinbound; extern char *ttystat[]; extern int Loaded; @@ -108,129 +85,152 @@ extern int most_debug; extern unsigned long sentbytes; extern unsigned long rcvdbytes; -typedef enum {RxWaitFile, RxAcceptFile, RxReceData, RxWriteData, RxEndOfBatch, RxDone} RxType; -typedef enum {TxGetNextFile, TxTryRead, TxReadSend, TxWaitLastAck, TxDone} TxType; -typedef enum {InitTransfer, Switch, Receive, Transmit} TransferType; +typedef enum {RxWaitF, RxAccF, RxReceD, RxWriteD, RxEOB, RxDone} RxType; +typedef enum {TxGNF, TxTryR, TxReadS, TxWLA, TxDone} TxType; typedef enum {Ok, Failure, Continue} TrType; typedef enum {No, WeCan, WeWant, TheyWant, Active} OptionState; +typedef enum {NoState, Sending, IsSent, Got, Skipped, Get} FileState; +typedef enum {InitTransfer, Switch, Receive, Transmit, DeinitTransfer} FtType; -static char *rxstate[] = { (char *)"RxWaitFile", (char *)"RxAccpetFile", (char *)"RxReceData", - (char *)"RxWriteData", (char *)"RxEndOfBatch", (char *)"RxDone" }; -static char *txstate[] = { (char *)"TxGetNextFile", (char *)"TryRread", (char *)"ReadSent", - (char *)"WaitLastAck", (char *)"TxDone" }; -//static char *tfstate[] = { (char *)"InitTransfer", (char *)"Switch", (char *)"Receive", (char *)"Transmit" }; -//static char *trstate[] = { (char *)"Ok", (char *)"Failure", (char *)"Continue" }; + +static char *rxstate[] = { (char *)"RxWaitF", (char *)"RxAccF", (char *)"RxReceD", + (char *)"RxWriteD", (char *)"RxEOB", (char *)"RxDone" }; +static char *txstate[] = { (char *)"TxGNF", (char *)"TxTryR", (char *)"TxReadS", + (char *)"TxWLA", (char *)"TxDone" }; +static char *trstate[] = { (char *)"Ok", (char *)"Failure", (char *)"Continue" }; static char *opstate[] = { (char *)"No", (char *)"WeCan", (char *)"WeWant", (char *)"TheyWant", (char *)"Active" }; +static char *lbstate[] = { (char *)"None", (char *)"Sending", (char *)"IsSent", (char *)"Got", (char *)"Skipped", (char *)"Get"}; +static char *ftstate[] = { (char *)"InitTransfer", (char *)"Switch", (char *)"Receive", + (char *)"Transmit", (char *)"DeinitTransfer" }; -//static int TfState; static time_t Timer; -static int CRAMflag = FALSE; /* CRAM option flag */ -static int Secure = FALSE; /* Secure session */ -int transferred = FALSE; /* Anything transferred in batch */ -unsigned char *MD_challenge = NULL; /* Received CRAM challenge data */ int ext_rand = 0; +the_queue *tql = NULL; /* The Queue List */ +file_list *tosend = NULL; /* Files to send */ +file_list *respond = NULL; /* Requests honored */ +binkp_list *bll = NULL; /* Files to send with status */ +static binkp_list *cursend = NULL; /* Current file being transmitted */ +struct timeval txtvstart; /* Transmitter start time */ +struct timeval txtvend; /* Transmitter end time */ +struct timeval rxtvstart; /* Receiver start time */ +struct timeval rxtvend; /* Receiver end time */ + struct binkprec { - int role; /* 1=orig, 0=answer */ + int Role; /* 1=orig, 0=answer */ int RxState; /* Receiver state */ int TxState; /* Transmitter state */ - int DidSendGET; /* Did we send a GET command */ + int FtState; /* File transfer state */ + /* Option flags */ + int CRAMflag; /* CRAM authentication */ + /* Session state */ + int Secure; /* Secure session */ + int Major; /* Major protocol version */ + int Minor; /* Minor protocol version */ + unsigned char *MD_Challenge; /* Received challenge data */ + + /* Receiver buffer */ + char *rxbuf; /* Receiver buffer */ + int GotFrame; /* Frame complete flag */ + int rxlen; /* Frame length */ + int cmd; /* Frame command flag */ + int blklen; /* Frame blocklength */ + unsigned short header; /* Frame header */ + int rc; /* General return code */ + long rsize; /* Receiver filesize */ long roffs; /* Receiver offset */ char *rname; /* Receiver filename */ time_t rtime; /* Receiver filetime */ - long lsize; /* Local filesize */ - char *lname; /* Local filename */ - time_t ltime; /* Local filetime */ - long gsize; /* GET filesize */ - long goffset; /* GET offset */ - char *gname; /* GET filename */ - time_t gtime; /* GET filetime */ - int MBflag; /* MB option flag */ - int Major; /* Remote major protocol version */ - int Minor; /* Remote minor protocol version */ - int rc; /* Protocol return code */ - int rxlen; /* Receive buffer length */ - int txlen; /* Length of transmitted data block */ - char *txbuf; /* Transmitter buffer */ - char *rxbuf; /* Receiver buffer */ - FILE *txfp; /* File in transmitter */ - FILE *rxfp; /* File in receiver */ - int txpos; /* Transmitter position */ - int rxpos; /* Receiver position */ - int stxpos; /* Start transmitter position */ - int cmd; /* Command flag */ - int GotFrame; /* Got Frame flag */ - unsigned short header; /* Frame header */ - int blklen; /* Block length */ + FILE *rxfp; /* Receiver file */ off_t rxbytes; /* Receiver bytecount */ - struct timeval rxtvstart; /* Receive file start */ - struct timeval rxtvend; /* Receiver file end */ - struct timeval txtvstart; /* Transmit file start */ - struct timeval txtvend; /* Transmit file end */ + int rxpos; /* Receiver position */ struct timezone tz; /* Timezone */ + int DidSendGET; /* Receiver send GET status */ + + char *txbuf; /* Transmitter buffer */ + int txlen; /* Transmitter file length */ + FILE *txfp; /* Transmitter file */ + int txpos; /* Transmitter position */ + int stxpos; /* Transmitter start position */ + + int local_EOB; /* Local EOB sent */ + int remote_EOB; /* Got EOB from remote */ + int messages; /* Messages sent + rcvd */ unsigned long nethold; /* Netmail on hold */ - unsigned long mailhold; /* Packed mail (files) on hold */ - int batchnr; /* Batch number */ + unsigned long mailhold; /* Packed mail on hold */ + + int batchnr; + int msgs_on_queue; /* Messages on the queue */ }; + struct binkprec bp; /* Global structure */ +/* + * Prototypes + */ +TrType binkp_receiver(void); /* Receiver routine */ +TrType binkp_transmitter(void); /* Transmitter routine */ +int binkp_send_frame(int, char *, int); /* Send cmd/data frame */ +int binkp_send_command(int, ...); /* Send command frame */ +void binkp_settimer(int); /* Set timeout timer */ +int binkp_expired(void); /* Timer expired? */ +int binkp_banner(void); /* Send system banner */ +int binkp_recv_command(char *, int *, int *); /* Receive command frame */ +void parse_m_nul(char *); /* Parse M_NUL message */ +int binkp_poll_frame(void); /* Poll for a frame */ +void binkp_add_message(char *frame); /* Add cmd frame to queue */ +int binkp_process_messages(void); /* Process the queue */ +int binkp_resync(off_t); /* File resync */ +char *unix2binkp(char *); /* Binkp -> Unix escape */ +char *binkp2unix(char *); /* Unix -> Binkp escape */ +void fill_binkp_list(binkp_list **, file_list *, off_t); /* Build pending files */ +void debug_binkp_list(binkp_list **); /* Debug pending files list */ +int binkp_pendingfiles(void); /* Count pending files */ +void binkp_clear_filelist(void); /* Clear current filelist */ -void binkp_init(void) -{ - bp.rname = calloc(512, sizeof(char)); - bp.lname = calloc(512, sizeof(char)); - bp.gname = calloc(512, sizeof(char)); - bp.MBflag = WeCan; - bp.Major = 1; - bp.Minor = 0; - bp.DidSendGET = FALSE; - bp.rc = 0; - bp.rxlen = 0; - bp.txbuf = calloc(MAX_BLKSIZE + 3, sizeof(unsigned char)); - bp.rxbuf = calloc(MAX_BLKSIZE + 3, sizeof(unsigned char)); - bp.txfp = NULL; - bp.rxfp = NULL; - bp.txpos = 0; - bp.rxpos = 0; - bp.stxpos = 0; - bp.cmd = FALSE; - bp.GotFrame = FALSE; - bp.header = 0; - bp.batchnr = 0; -} +static int orgbinkp(void); /* Originate session state */ +static int ansbinkp(void); /* Answer session state */ +static int file_transfer(void); /* File transfer state */ -void binkp_deinit(void) -{ - if (bp.rname) - free(bp.rname); - if (bp.lname) - free(bp.lname); - if (bp.gname) - free(bp.gname); - if (bp.txbuf) - free(bp.txbuf); - if (bp.rxbuf) - free(bp.rxbuf); -} +/************************************************************************************/ +/* + * General entry point + */ int binkp(int role) { - int rc = MBERR_OK; - fa_list *eff_remote; - file_list *tosend = NULL, *request = NULL, *respond = NULL, *tmpfl; - char *nonhold_mail; - - binkp_init(); + int rc = 0; +#ifdef USE_NEWBINKP most_debug = TRUE; +#endif + + Syslog('+', "Binkp: start session"); + + memset(&bp, 0, sizeof(bp)); + bp.Role = role; + bp.CRAMflag = FALSE; + bp.Secure = FALSE; + bp.Major = 1; + bp.Minor = 0; + bp.MD_Challenge = NULL; + bp.rxbuf = calloc(MAX_BLKSIZE + 3, sizeof(unsigned char)); + bp.txbuf = calloc(MAX_BLKSIZE + 3, sizeof(unsigned char)); + bp.rname = calloc(512, sizeof(char)); + bp.rxfp = NULL; + bp.DidSendGET = FALSE; + bp.local_EOB = FALSE; + bp.remote_EOB = FALSE; + bp.msgs_on_queue = 0; + if (role == 1) { if (orgbinkp()) { rc = MBERR_SESSION_ERROR; @@ -240,107 +240,1770 @@ int binkp(int role) rc = MBERR_SESSION_ERROR; } } - + if (rc) { - Syslog('!', "Binkp: session failed"); - binkp_deinit(); - return rc; + Syslog('!', "Binkp: session handshake failed"); + goto binkpend; } -// if (localoptions & NOFREQS) -// session_flags &= ~SESSION_WAZOO; -// else + if (Loaded && nodes.NoBinkp11 && (bp.Major == 1) && (bp.Minor != 0)) { + Syslog('+', "Binkp: forcing downgrade to binkp/1.0 protocol"); + bp.Major = 1; + bp.Minor = 0; + } + + if (localoptions & NOFREQS) + session_flags &= ~SESSION_WAZOO; + else session_flags |= SESSION_WAZOO; - Syslog('b', "Binkp: WAZOO requests: %s", (session_flags & SESSION_WAZOO) ? "True":"False"); + bp.FtState = InitTransfer; + rc = file_transfer(); - nonhold_mail = (char *)ALL_MAIL; - eff_remote = remote; +binkpend: /* - * If remote doesn't have the 8.3 flag set, allow long filenames. + * Deinit */ - if (!nodes.FNC) - remote_flags &= ~SESSION_FNC; - - tosend = create_filelist(eff_remote, nonhold_mail, 0); + if (bp.rname) + free(bp.rname); + if (bp.MD_Challenge) + free(bp.MD_Challenge); + if (bp.rxbuf) + free(bp.rxbuf); + if (bp.txbuf) + free(bp.txbuf); + rc = abs(rc); - if (request != NULL) { - Syslog('b', "Binkp: inserting request list"); - tmpfl = tosend; - tosend = request; - for (; request->next; request = request->next); - request->next = tmpfl; + Syslog('+', "Binkp: session finished, rc=%d", rc); + return rc; +} - request = NULL; + +/************************************************************************************/ +/* + * Originate Session Setup + */ + +SM_DECL(orgbinkp, (char *)"orgbinkp") +SM_STATES + ConnInit, + WaitConn, + SendPasswd, + WaitAddr, + AuthRemote, + IfSecure, + WaitOk, + Opts +SM_NAMES + (char *)"ConnInit", + (char *)"WaitConn", + (char *)"SendPasswd", + (char *)"WaitAddr", + (char *)"AuthRemote", + (char *)"IfSecure", + (char *)"WaitOk", + (char *)"Opts" +SM_EDECL + faddr *primary; + char *p, *q, *pwd; + int i, rc = 0, bufl, cmd, dupe, SendPass = FALSE; + fa_list **tmp, *tmpa; + faddr *fa, ra; + +SM_START(ConnInit) + +SM_STATE(ConnInit) + + SM_PROCEED(WaitConn) + +SM_STATE(WaitConn) + + Loaded = FALSE; + Syslog('+', "Binkp: node %s", ascfnode(remote->addr, 0x1f)); + IsDoing("Connect binkp %s", ascfnode(remote->addr, 0xf)); + + /* + * Build options we want (Add PLZ etc). + */ + p = xstrcpy((char *)"OPT"); + + if (strcmp(p, (char *)"OPT")) + rc = binkp_send_command(MM_NUL, p); + free(p); + if (rc) { + SM_ERROR; } - rc = binkp_batch(tosend); - tidy_filelist(tosend, (rc == 0)); - tosend = NULL; - - if ((rc == 0) && transferred && (bp.MBflag == Active)) { - /* - * Running Multiple Batch, only if last batch actually - * did transfer some data. - */ - respond = respond_wazoo(); - /* - * Just create the tosend list again, there may be something - * ready again for this node. - */ - tosend = create_filelist(eff_remote, nonhold_mail, 0); - for (tmpfl = tosend; tmpfl->next; tmpfl = tmpfl->next); - tmpfl->next = respond; - rc = binkp_batch(tosend); - tmpfl->next = NULL; + rc = binkp_banner(); + if (rc) { + SM_ERROR; } - Syslog('+', "Binkp: end transfer rc=%d", rc); - closetcp(); + /* + * Build a list of aka's to send, the primary aka first. + */ + ra.zone = remote->addr->zone; + ra.net = remote->addr->net; + ra.node = remote->addr->node; + ra.point = remote->addr->point; - Syslog('b', "2nd batch or not, MB flag is %s", opstate[bp.MBflag]); + primary = bestaka_s(remote->addr); + p = xstrcpy(ascfnode(primary, 0x1f)); - if (bp.MBflag != Active) { - /* - * In singe batch mode we process filerequests after the batch. - * The results will be put on hold for the calling node. - * This method is also known as "dual session mode". - */ - respond = respond_wazoo(); - for (tmpfl = respond; tmpfl; tmpfl = tmpfl->next) { - if (strncmp(tmpfl->local, "/tmp", 4)) { - attach(*remote->addr, tmpfl->local, LEAVE, 'h'); - Syslog('+', "Binkp: put on hold: %s", MBSE_SS(tmpfl->local)); - } else { - file_mv(tmpfl->local, pktname(remote->addr, 'h')); - Syslog('+', "Binkp: new netmail: %s", pktname(remote->addr, 'h')); - } + /* + * Add all other aka's exept primary aka. + */ + for (i = 0; i < 40; i++) { + if ((CFG.aka[i].zone) && (CFG.akavalid[i]) && + ((CFG.aka[i].zone != primary->zone) || (CFG.aka[i].net != primary->net) || + (CFG.aka[i].node != primary->node) || (CFG.aka[i].point!= primary->point))) { + p = xstrcat(p, (char *)" "); + p = xstrcat(p, aka2str(CFG.aka[i])); } } - tidy_filelist(request, (rc == 0)); - tidy_filelist(tosend, (rc == 0)); - tidy_filelist(respond, 0); + rc = binkp_send_command(MM_ADR, "%s", p); + free(p); + tidy_faddr(primary); + if (rc) { + SM_ERROR; + } - binkp_deinit(); - rc = abs(rc); + SM_PROCEED(WaitAddr) + +SM_STATE(WaitAddr) + + for (;;) { + if ((rc = binkp_recv_command(bp.rxbuf, &bufl, &cmd))) { + Syslog('!', "Binkp: error receiving remote info"); + SM_ERROR; + } + if (cmd) { + if (bp.rxbuf[0] == MM_ADR) { + p = xstrcpy(bp.rxbuf + 1); + tidy_falist(&remote); + remote = NULL; + tmp = &remote; + for (q = strtok(p, " "); q; q = strtok(NULL, " ")) { + if ((fa = parsefnode(q))) { + dupe = FALSE; + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) && + (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) && + (strcmp(tmpa->addr->domain, fa->domain) == 0)) { + dupe = TRUE; + Syslog('b', "Binkp: double address %s", ascfnode(tmpa->addr, 0x1f)); + break; + } + } + if (!dupe) { + *tmp = (fa_list*)malloc(sizeof(fa_list)); + (*tmp)->next = NULL; + (*tmp)->addr = fa; + tmp = &((*tmp)->next); + } + } else { + Syslog('!', "Binkp: unparsable remote address: \"%s\"", printable(q, 0)); + binkp_send_command(MM_ERR, "Unparsable address \"%s\"", printable(q, 0)); + SM_ERROR; + } + } + free(p); + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + Syslog('+', "Address : %s", ascfnode(tmpa->addr, 0x1f)); + if (nodelock(tmpa->addr, mypid)) { + binkp_send_command(MM_BSY, "Address %s locked", ascfnode(tmpa->addr, 0x1f)); + SM_ERROR; + } + /* + * With the loaded flag we prevent removing the noderecord + * when the remote presents us an address we don't know about. + */ + if (!Loaded) { + if (noderecord(tmpa->addr)) + Loaded = TRUE; + } + } + + history.aka.zone = remote->addr->zone; + history.aka.net = remote->addr->net; + history.aka.node = remote->addr->node; + history.aka.point = remote->addr->point; + sprintf(history.aka.domain, "%s", remote->addr->domain); + + SM_PROCEED(SendPasswd) + } else if (bp.rxbuf[0] == MM_BSY) { + Syslog('!', "Binkp: M_BSY \"%s\"", printable(bp.rxbuf +1, 0)); + SM_ERROR; + } else if (bp.rxbuf[0] == MM_ERR) { + Syslog('!', "Binkp: M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); + SM_ERROR; + } else if (bp.rxbuf[0] == MM_NUL) { + parse_m_nul(bp.rxbuf +1); + } else { + binkp_send_command(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } else { + binkp_send_command(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } + +SM_STATE(SendPasswd) + + if (Loaded && strlen(nodes.Spasswd)) { + pwd = xstrcpy(nodes.Spasswd); + SendPass = TRUE; + } else { + pwd = xstrcpy((char *)"-"); + } + + if (bp.MD_Challenge) { + char *tp = NULL; + tp = MD_buildDigest(pwd, bp.MD_Challenge); + if (!tp) { + Syslog('!', "Unable to build MD5 digest"); + binkp_send_command(MM_ERR, "CRAM authentication failed, internal error"); + SM_ERROR; + } + bp.CRAMflag = TRUE; + rc = binkp_send_command(MM_PWD, "%s", tp); + free(tp); + } else { + rc = binkp_send_command(MM_PWD, "%s", pwd); + } + + free(pwd); + if (rc) { + SM_ERROR; + } + SM_PROCEED(AuthRemote) + +SM_STATE(AuthRemote) + + rc = 0; + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + if ((tmpa->addr->zone == ra.zone) && (tmpa->addr->net == ra.net) && + (tmpa->addr->node == ra.node) && (tmpa->addr->point == ra.point)) { + rc = 1; + } + } + + if (rc) { + SM_PROCEED(IfSecure) + } else { + Syslog('!', "Binkp: error, the wrong node is reached"); + binkp_send_command(MM_ERR, "No AKAs in common or all AKAs busy"); + SM_ERROR; + } + +SM_STATE(IfSecure) + + SM_PROCEED(WaitOk) + +SM_STATE(WaitOk) + + for (;;) { + if ((rc = binkp_recv_command(bp.rxbuf, &bufl, &cmd))) { + Syslog('!', "Binkp: error waiting for remote acknowledge"); + SM_ERROR; + } + + if (cmd) { + if (bp.rxbuf[0] == MM_OK) { + Syslog('b', "Binkp: M_OK \"%s\"", printable(bp.rxbuf +1, 0)); + if (SendPass) + bp.Secure = TRUE; + Syslog('+', "Binkp: %s%sprotected session", bp.CRAMflag ? "MD5 ":"", bp.Secure ? "":"un"); + SM_PROCEED(Opts) + + } else if (bp.rxbuf[0] == MM_BSY) { + Syslog('!', "Binkp: M_BSY \"%s\"", printable(bp.rxbuf +1, 0)); + SM_ERROR; + + } else if (bp.rxbuf[0] == MM_ERR) { + Syslog('!', "Binkp: M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); + SM_ERROR; + + } else if (bp.rxbuf[0] == MM_NUL) { + parse_m_nul(bp.rxbuf +1); + + } else { + binkp_send_command(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } else { + binkp_send_command(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } + +SM_STATE(Opts) + + /* + * Try to initiate the MB option if the remote is binkp/1.0 + */ +// if ((bp.MBflag == WeCan) && (bp.Major == 1) && (bp.Minor == 0)) { +// bp.MBflag = WeWant; +// Syslog('b', "MBflag WeCan => WeWant"); +// binkp_send_command(MM_NUL, "OPT MB"); +// } + SM_SUCCESS; + +SM_END +SM_RETURN + + + +/************************************************************************************/ +/* + * Answer Session Setup + */ + +SM_DECL(ansbinkp, (char *)"ansbinkp") +SM_STATES + WaitConn, + WaitAddr, + IsPasswd, + WaitPwd, + PwdAck, + Opts +SM_NAMES + (char *)"WaitConn", + (char *)"WaitAddr", + (char *)"IsPasswd", + (char *)"WaitPwd", + (char *)"PwdAck", + (char *)"Opts" +SM_EDECL + char *p, *q, *pw; + int i, rc, bufl, cmd, dupe, we_have_pwd = FALSE; + fa_list **tmp, *tmpa; + faddr *fa; + +SM_START(WaitConn) + +SM_STATE(WaitConn) + + Loaded = FALSE; + + if (strncmp(SockR("SBBS:0;"), "100:2,1", 7) == 0) { + Syslog('+', "Binkp: system is closed, sending M_BSY"); + binkp_send_command(MM_BSY, "This system is closed, try again later"); + SM_ERROR; + } + + if (!CFG.NoMD5 && ((bp.MD_Challenge = MD_getChallenge(NULL, &peeraddr)) != NULL)) { + /* + * Answering site MUST send CRAM message as very first M_NUL + */ + char s[MD5_DIGEST_LEN*2+15]; /* max. length of opt string */ + strcpy(s, "OPT "); + MD_toString(s+4, bp.MD_Challenge[0], bp.MD_Challenge+1); + bp.CRAMflag = TRUE; + if ((rc = binkp_send_command(MM_NUL, "%s", s))) { + SM_ERROR; + } + } + + if ((rc = binkp_banner())) { + SM_ERROR; + } + + p = xstrcpy((char *)""); + for (i = 0; i < 40; i++) { + if ((CFG.aka[i].zone) && (CFG.akavalid[i])) { + p = xstrcat(p, (char *)" "); + p = xstrcat(p, aka2str(CFG.aka[i])); + } + } + + rc = binkp_send_command(MM_ADR, "%s", p); + free(p); + if (rc) { + SM_ERROR; + } + + SM_PROCEED(WaitAddr) + +SM_STATE(WaitAddr) + + for (;;) { + if ((rc = binkp_recv_command(bp.rxbuf, &bufl, &cmd))) { + Syslog('!', "Binkp: error waiting for remote info"); + SM_ERROR; + } + + if (cmd) { + if (bp.rxbuf[0] == MM_ADR) { + p = xstrcpy(bp.rxbuf +1); + tidy_falist(&remote); + remote = NULL; + tmp = &remote; + + for (q = strtok(p, " "); q; q = strtok(NULL, " ")) { + if ((fa = parsefnode(q))) { + dupe = FALSE; + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) && + (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) && + (strcmp(tmpa->addr->domain, fa->domain) == 0)) { + dupe = TRUE; + Syslog('b', "Binkp: double address %s", ascfnode(tmpa->addr, 0x1f)); + break; + } + } + if (!dupe) { + *tmp = (fa_list*)malloc(sizeof(fa_list)); + (*tmp)->next = NULL; + (*tmp)->addr = fa; + tmp = &((*tmp)->next); + } + } else { + Syslog('!', "Binkp: unparsable remote address: \"%s\"", printable(q, 0)); + binkp_send_command(MM_ERR, "Unparsable address \"%s\"", printable(q, 0)); + SM_ERROR; + } + } + + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + Syslog('+', "Address : %s", ascfnode(tmpa->addr, 0x1f)); + if (nodelock(tmpa->addr, mypid)) { + binkp_send_command(MM_BSY, "Address %s locked", ascfnode(tmpa->addr, 0x1f)); + SM_ERROR; + } + /* + * With the loaded flag we prevent removing the noderecord + * when the remote presents us an address we don't know about. + */ + if (!Loaded) { + if (noderecord(tmpa->addr)) + Loaded = TRUE; + } + } + + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + if (((nlent = getnlent(tmpa->addr))) && (nlent->pflag != NL_DUMMY)) { + Syslog('+', "Binkp: remote is a listed system"); + UserCity(mypid, nlent->sysop, nlent->location); + break; + } + } + if (nlent) + rdoptions(Loaded); + + //if (bp.MBflag == TheyWant) { + // Syslog('b', "Binkp: remote supports MB"); + // binkp_send_control(MM_NUL,"OPT MB"); + // bp.MBflag = Active; + //} + history.aka.zone = remote->addr->zone; + history.aka.net = remote->addr->net; + history.aka.node = remote->addr->node; + history.aka.point = remote->addr->point; + sprintf(history.aka.domain, "%s", remote->addr->domain); + + SM_PROCEED(IsPasswd) + + } else if (bp.rxbuf[0] == MM_ERR) { + Syslog('!', "Binkp: M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); + SM_ERROR; + + } else if (bp.rxbuf[0] == MM_NUL) { + parse_m_nul(bp.rxbuf +1); + + } else if (bp.rxbuf[0] <= MM_MAX) { + binkp_send_command(MM_ERR, "Unexpected frame"); + SM_ERROR; + + } + } else { + binkp_send_command(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } + +SM_STATE(IsPasswd) + + if (Loaded && strlen(nodes.Spasswd)) { + we_have_pwd = TRUE; + } + + Syslog('b', "We %s have a password", we_have_pwd ?"do":"don't"); + SM_PROCEED(WaitPwd) + +SM_STATE(WaitPwd) + + for (;;) { + if ((rc = binkp_recv_command(bp.rxbuf, &bufl, &cmd))) { + Syslog('!', "Binkp: error waiting for password"); + SM_ERROR; + } + + if (cmd) { + if (bp.rxbuf[0] == MM_PWD) { + SM_PROCEED(PwdAck) + } else if (bp.rxbuf[0] == MM_ERR) { + Syslog('!', "Binkp: M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); + SM_ERROR; + } else if (bp.rxbuf[0] == MM_NUL) { + parse_m_nul(bp.rxbuf +1); + } else if (bp.rxbuf[0] <= MM_MAX) { + binkp_send_command(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } else { + binkp_send_command(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } + +SM_STATE(PwdAck) + + if (we_have_pwd) { + pw = xstrcpy(nodes.Spasswd); + } else { + pw = xstrcpy((char *)"-"); + } + + if ((strncmp(bp.rxbuf +1, "CRAM-", 5) == 0) && bp.CRAMflag) { + char *sp; + sp = MD_buildDigest(pw, bp.MD_Challenge); + if (sp != NULL) { + if (strcmp(bp.rxbuf +1, sp)) { + Syslog('+', "Binkp: bad MD5 crypted password"); + binkp_send_command(MM_ERR, "Bad password"); + free(sp); + sp = NULL; + free(pw); + SM_ERROR; + } else { + free(sp); + sp = NULL; + if (we_have_pwd) + bp.Secure = TRUE; + } + } else { + free(pw); + Syslog('!', "Binkp: could not build MD5 digest"); + binkp_send_command(MM_ERR, "*** Internal error ***"); + SM_ERROR; + } + } else if ((strcmp(bp.rxbuf +1, pw) == 0)) { + if (we_have_pwd) + bp.Secure = TRUE; + } else { + free(pw); + Syslog('?', "Binkp: password error: expected \"%s\", got \"%s\"", nodes.Spasswd, bp.rxbuf +1); + binkp_send_command(MM_ERR, "Bad password"); + SM_ERROR; + } + free(pw); + Syslog('+', "Binkp: %s%sprotected session", bp.CRAMflag ? "MD5 ":"", bp.Secure ? "":"un"); + inbound_open(remote->addr, bp.Secure); + binkp_send_command(MM_OK, "%ssecure", bp.Secure ? "":"non-"); + SM_PROCEED(Opts) + +SM_STATE(Opts) + + SM_SUCCESS; + +SM_END +SM_RETURN + + + +/************************************************************************************/ +/* + * File Transfer State + */ + +/* + * We do not use the normal state machine because that produces a lot + * of debug logging that will drive up the CPU usage. + * FIXME: Remove these messages!! + */ +int file_transfer(void) +{ + int rc = 0; + TrType Trc = Ok; + + for (;;) { + Syslog('B', "Binkp: FileTransfer state %s", ftstate[bp.FtState]); + switch (bp.FtState) { + case InitTransfer: binkp_settimer(BINKP_TIMEOUT); + bp.RxState = RxWaitF; + bp.TxState = TxGNF; + bp.FtState = Switch; + bp.messages = 0; + break; + + case Switch: if ((bp.RxState == RxDone) && (bp.TxState == TxDone)) { + bp.FtState = DeinitTransfer; + break; + } + + rc = binkp_poll_frame(); + if (rc == -1) { + Syslog('b', "Binkp: receiver error detected"); + bp.rc = rc = MBERR_FTRANSFER; + bp.FtState = DeinitTransfer; + break; + } else if (rc == 1) { + bp.FtState = Receive; + break; + } + + /* + * Check if there is room in the output buffer + */ + if (WAITPUTGET(-1) & 2) { + bp.FtState = Transmit; + break; + } + + if (binkp_expired()) { + Syslog('+', "Binkp: transfer timeout"); + bp.rc = 1; + bp.FtState = DeinitTransfer; + break; + } + + /* + * Nothing done, release + */ + Syslog('b', "Binkp: NOTHING DONE"); + usleep(1); + break; + + case Receive: Trc = binkp_receiver(); + if (Trc == Ok) { + binkp_settimer(BINKP_TIMEOUT); + if (bp.local_EOB && bp.remote_EOB) + bp.FtState = Transmit; + else + bp.FtState = Switch; + } else if (Trc == Failure) { + Syslog('+', "Binkp: receiver failure"); + bp.rc = 1; + bp.FtState = DeinitTransfer; + } + break; + + case Transmit: Trc = binkp_transmitter(); + if (Trc == Ok) { + binkp_settimer(BINKP_TIMEOUT); + bp.FtState = Switch; + } else if (Trc == Failure) { + Syslog('+', "Binkp: transmitter failure"); + bp.rc = 1; + bp.FtState = DeinitTransfer; + } + break; + + case DeinitTransfer:/* + * In case of a transfer error the filelist is not yet cleared + */ + binkp_clear_filelist(); + if (bp.rc) + return MBERR_FTRANSFER; + else + return 0; + break; + } + } +} + + + +/************************************************************************************/ +/* + * Receiver routine + */ + +TrType binkp_receiver(void) +{ + int bcmd, rc = 0; + struct statfs sfs; + long written; + off_t rxbytes; + + Syslog('B', "Binkp: receiver state %s", rxstate[bp.RxState]); + + if (bp.RxState == RxWaitF) { + + if (! bp.GotFrame) + return Ok; + + bp.GotFrame = FALSE; + bp.rxlen = 0; + bp.header = 0; + + if (! bp.cmd) { + Syslog('b', "Binkp: got %d bytes DATA frame in %s state, ignored", bp.blklen, rxstate[bp.RxState]); + bp.blklen = 0; + return Ok; + } + bp.blklen = 0; + + bcmd = bp.rxbuf[0]; + if (bcmd == MM_ERR) { + Syslog('+', "Binkp: got M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); + bp.RxState = RxDone; + return Failure; + } else if ((bcmd == MM_GET) || (bcmd == MM_GOT) || (bcmd == MM_SKIP)) { + binkp_add_message(bp.rxbuf); + return Ok; + } else if (bcmd == MM_NUL) { + parse_m_nul(bp.rxbuf +1); + return Ok; + } else if (bcmd == MM_EOB) { + if ((bp.Major == 1) && (bp.Minor != 0)) { +// Syslog('B', "Binkp: 1.1 check local_EOB=%s remote_EOB=%s messages=%d", +// bp.local_EOB?"True":"False", bp.remote_EOB?"True":"False", bp.messages); + if (bp.local_EOB && bp.remote_EOB) { + Syslog('b', "Binkp: receiver detects both sides in EOB state"); + if ((bp.messages < 3) || binkp_pendingfiles()) { + Syslog('b', "Binkp: receiver detected end of session or pendingfiles, stay in RxWaitF"); + return Ok; + } else { + bp.batchnr++; + bp.local_EOB = FALSE; + bp.remote_EOB = FALSE; + bp.messages = 0; + bp.TxState = TxGNF; + bp.RxState = RxWaitF; + Syslog('+', "Binkp: receiver starts batch %d", bp.batchnr + 1); + binkp_clear_filelist(); + return Ok; + } + } else { + return Ok; + } + } else { + Syslog('b', "Binkp/1.0 mode and got M_EOB, goto RxEOB"); + bp.RxState = RxEOB; + return Ok; + } + } else if (bcmd == MM_FILE) { + bp.RxState = RxAccF; + return Continue; + } else if (bcmd <= MM_MAX) { + Syslog('+', "Binkp: ERROR got unexpected %s frame in %s state", bstate[bcmd], rxstate[bp.RxState]); + bp.RxState = RxDone; + return Failure; + } else { + Syslog('+', "Binkp: got unexpected unknown frame, ignore"); + return Ok; + } + } else if (bp.RxState == RxAccF) { + if (strlen(bp.rxbuf) < 512) { + sprintf(bp.rname, "%s", strtok(bp.rxbuf+1, " \n\r")); + bp.rsize = atoi(strtok(NULL, " \n\r")); + bp.rtime = atoi(strtok(NULL, " \n\r")); + bp.roffs = atoi(strtok(NULL, " \n\r")); + } else { + /* + * Corrupted command, in case this was serious, send the M_GOT back so it's + * deleted at the remote. + */ + Syslog('+', "Binkp: got corrupted FILE frame, size %d bytes", strlen(bp.rxbuf)); + bp.RxState = RxWaitF; + rc = binkp_send_command(MM_GOT, bp.rxbuf +1); + if (rc) + return Failure; + else + return Ok; + } + Syslog('+', "Binkp: receive file \"%s\" date %s size %ld offset %ld", bp.rname, date(bp.rtime), bp.rsize, bp.roffs); + (void)binkp2unix(bp.rname); + rxbytes = bp.rxbytes; + bp.rxfp = openfile(binkp2unix(bp.rname), bp.rtime, bp.rsize, &rxbytes, binkp_resync); + bp.rxbytes = rxbytes; + + if (bp.DidSendGET) { + Syslog('b', "Binkp: DidSendGET is set"); + /* + * FIXME: this was from the old driver, still needed??? + * + * The file was partly received, via the openfile the resync function + * has send a GET command to start this file with a offset. This means + * we will get a new FILE command to open this file with a offset. + */ + bp.RxState = RxReceD; + return Ok; + } + + gettimeofday(&rxtvstart, &bp.tz); + bp.rxpos = bp.roffs; + + if (!diskfree(CFG.freespace)) { + Syslog('+', "Binkp: low diskspace, sending BSY"); + binkp_send_command(MM_BSY, "Low diskspace, try again later"); + bp.RxState = RxDone; + bp.TxState = TxDone; + bp.rc = MBERR_FTRANSFER; + return Failure; + } + + if (statfs(tempinbound, &sfs) == 0) { + if ((bp.rsize / (sfs.f_bsize + 1)) >= sfs.f_bfree) { + Syslog('!', "Binkp: only %lu blocks free (need %lu) in %s for this file", sfs.f_bfree, + (unsigned long)(bp.rsize / (sfs.f_bsize + 1)), tempinbound); + closefile(); + bp.rxfp = NULL; /* Force SKIP command */ + } + } + + if (bp.rsize == bp.rxbytes) { + /* + * We already got this file, send GOT so it will + * be deleted at the remote. + */ + Syslog('+', "Binkp: already got %s, sending GOT", bp.rname); + rc = binkp_send_command(MM_GOT, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); + bp.RxState = RxWaitF; + bp.rxfp = NULL; + if (rc) + return Failure; + else + return Ok; + } else if (!bp.rxfp) { + /* + * Some error, request to skip it + */ + Syslog('+', "Binkp: error file %s, sending SKIP", bp.rname); + rc = binkp_send_command(MM_SKIP, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); + bp.RxState = RxWaitF; + if (rc) + return Failure; + else + return Ok; + } else { + Syslog('b', "rsize=%d, rxbytes=%d, roffs=%d", bp.rsize, bp.rxbytes, bp.roffs); + bp.RxState = RxReceD; + return Ok; + } + + } else if (bp.RxState == RxReceD) { + + if (! bp.GotFrame) + return Ok; + + if (! bp.cmd) { + bp.RxState = RxWriteD; + return Continue; + } + + bp.GotFrame = FALSE; + bp.rxlen = 0; + bp.header = 0; + bp.blklen = 0; + + bcmd = bp.rxbuf[0]; + if (bcmd == MM_ERR) { + Syslog('+', "Binkp: got M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); + bp.RxState = RxDone; + return Failure; + } else if ((bcmd == MM_GET) || (bcmd == MM_GOT) || (bcmd == MM_SKIP)) { + binkp_add_message(bp.rxbuf); + return Ok; + } else if (bcmd == MM_NUL) { + parse_m_nul(bp.rxbuf +1); + return Ok; + } else if (bcmd == MM_FILE) { + Syslog('+', "Binkp: partial received file, saving"); + closefile(); + bp.rxfp = NULL; + bp.RxState = RxAccF; + return Continue; + } else if (bcmd <= MM_MAX) { + Syslog('+', "Binkp: ERROR got unexpected %s frame in %s state", bstate[bcmd], rxstate[bp.RxState]); + bp.RxState = RxDone; + return Failure; + } else { + Syslog('+', "Binkp: got unexpected unknown frame, ignore"); + return Ok; + } + } else if (bp.RxState == RxWriteD) { + written = fwrite(bp.rxbuf, 1, bp.blklen, bp.rxfp); + + bp.GotFrame = FALSE; + bp.rxlen = 0; + bp.header = 0; + + if (bp.blklen && (bp.blklen != written)) { + Syslog('+', "Binkp: ERROR file write error"); + bp.RxState = RxDone; + bp.blklen = 0; + return Failure; + } + bp.blklen = 0; + bp.rxpos += written; + + if (bp.rxpos == bp.rsize) { + rc = binkp_send_command(MM_GOT, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); + closefile(); + bp.rxpos = bp.rxpos - bp.rxbytes; + gettimeofday(&rxtvend, &bp.tz); + Syslog('+', "Binkp: OK %s", transfertime(rxtvstart, rxtvend, bp.rxpos, FALSE)); + rcvdbytes += bp.rxpos; + bp.RxState = RxWaitF; + if (rc) + return Failure; + else + return Ok; + } + bp.RxState = RxReceD; + return Ok; + } else if (bp.RxState == RxEOB) { + + if (! bp.GotFrame) + return Ok; + + bp.GotFrame = FALSE; + bp.rxlen = 0; + bp.header = 0; + bp.blklen = 0; + + if (bp.cmd) { + bcmd = bp.rxbuf[0]; + if (bcmd == MM_ERR) { + Syslog('+', "Binkp: got M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); + bp.RxState = RxDone; + return Failure; + } else if ((bcmd == MM_GET) || (bcmd == MM_GOT) || (bcmd == MM_SKIP)) { + binkp_add_message(bp.rxbuf); + return Ok; + } else if (bcmd == MM_NUL) { + parse_m_nul(bp.rxbuf +1); + return Ok; + } else if (bcmd <= MM_MAX) { + Syslog('+', "Binkp: ERROR got unexpected %s frame in %s state", bstate[bcmd], rxstate[bp.RxState]); + bp.RxState = RxDone; + return Failure; + } else { + Syslog('+', "Binkp: got unexpected unknown frame, ignore"); + return Ok; + } + } else { + Syslog('+', "Binkp: ERROR got unexpected data frame in %s state", rxstate[bp.RxState]); + bp.RxState = RxDone; + return Failure; + } + + } else if (bp.RxState == RxDone) { + return Ok; + } + + /* + * Cannot be here + */ + bp.RxState = RxDone; + return Failure; +} + + +/************************************************************************************/ +/* + * Transmitter routine + */ + +TrType binkp_transmitter(void) +{ + int rc = 0, eof = FALSE; + char *nonhold_mail; + fa_list *eff_remote; + file_list *tsl; + static binkp_list *tmp; + + Syslog('B', "Binkp: transmitter state %s", txstate[bp.TxState]); + + if (bp.TxState == TxGNF) { + /* + * If we do not have a filelist yet, create one. + */ + if (tosend == NULL) { + Syslog('b', "Creating filelist"); + nonhold_mail = (char *)ALL_MAIL; + bp.nethold = bp.mailhold = 0L; + cursend = NULL; + + /* + * If remote doesn't have the 8.3 flag set, allow long names + */ + if (!nodes.FNC) + remote_flags &= ~SESSION_FNC; + + eff_remote = remote; + tosend = create_filelist(eff_remote, nonhold_mail, 0); + if ((respond = respond_wazoo()) != NULL) { + for (tsl = tosend; tsl->next; tsl = tsl ->next); + tsl->next = respond; + Syslog('b', "Binkp: added requested files"); + } + + /* + * Build a new filelist from the existing filelist. + * This one is special for binkp behaviour. + */ + for (tsl = tosend; tsl; tsl = tsl->next) { + if (tsl->remote != NULL) + fill_binkp_list(&bll, tsl, 0L); + } + debug_binkp_list(&bll); + + if ((bp.nethold || bp.mailhold) || (bp.batchnr == 0)) { + Syslog('+', "Binkp: mail %ld, files %ld bytes", bp.nethold, bp.mailhold); + if ((rc = binkp_send_command(MM_NUL, "TRF %ld %ld", bp.nethold, bp.mailhold))) { + bp.TxState = TxDone; + return Failure; + } + } + } + + for (tmp = bll; tmp; tmp = tmp->next) { + if (tmp->state == NoState) { + /* + * There is something to send + */ + struct flock txflock; + txflock.l_type = F_RDLCK; + txflock.l_whence = 0; + txflock.l_start = 0L; + txflock.l_len = 0L; + + bp.txfp = fopen(tmp->local, "r"); + if (bp.txfp == NULL) { + if ((errno == ENOENT) || (errno == EINVAL)) { + Syslog('+', "Binkp: file %s doesn't exist, removing", MBSE_SS(tmp->local)); + tmp->state = Got; + } else { + WriteError("$Binkp: can't open %s, skipping", MBSE_SS(tmp->local)); + tmp->state = Skipped; + } + break; + } + + if (fcntl(fileno(bp.txfp), F_SETLK, &txflock) != 0) { + WriteError("$Binkp: can't lock file %s, skipping", MBSE_SS(tmp->local)); + fclose(bp.txfp); + bp.txfp = NULL; + tmp->state = Skipped; + break; + } + + bp.txpos = bp.stxpos = tmp->offset; + Syslog('+', "Binkp: send \"%s\" as \"%s\"", MBSE_SS(tmp->local), MBSE_SS(tmp->remote)); + Syslog('+', "Binkp: size %lu bytes, dated %s", (unsigned long)tmp->size, date(tmp->date)); + rc = binkp_send_command(MM_FILE, "%s %lu %ld %ld", MBSE_SS(tmp->remote), + (unsigned long)tmp->size, (long)tmp->date, (unsigned long)tmp->offset); + if (rc) { + bp.TxState = TxDone; + return Failure; + } + gettimeofday(&txtvstart, &bp.tz); + tmp->state = Sending; + cursend = tmp; + bp.TxState = TxTryR; + return Continue; + } /* if state == NoState */ + } /* for */ + + if (tmp == NULL) { + /* + * No more files + */ + rc = binkp_send_command(MM_EOB, ""); + bp.TxState = TxWLA; + if (rc) + return Failure; + else + return Continue; + } + return Ok; + + } else if (bp.TxState == TxTryR) { + + if (bp.msgs_on_queue == 0) { + bp.TxState = TxReadS; + return Continue; + } else { + if (binkp_process_messages()) { + bp.TxState = TxDone; + return Failure; + } else + return Continue; + } + + } else if (bp.TxState == TxReadS) { + fseek(bp.txfp, bp.txpos, SEEK_SET); + bp.txlen = fread(bp.txbuf, 1, SND_BLKSIZE, bp.txfp); + eof = feof(bp.txfp); + + if ((bp.txlen == 0) || eof) { + /* + * Nothing read/error or we are at eof + */ + if (ferror(bp.txfp)) { + WriteError("$Binkp: error reading from file"); + bp.TxState = TxDone; + cursend->state = Skipped; + debug_binkp_list(&bll); + return Failure; + } + + /* + * We are at eof, send last datablock + */ + if (eof && bp.txlen) { + bp.txpos += bp.txlen; + sentbytes += bp.txlen; + rc = binkp_send_frame(FALSE, bp.txbuf, bp.txlen); + if (rc) + return Failure; + } + + /* + * calculate time needed and bytes transferred + */ + gettimeofday(&txtvend, &bp.tz); + + /* + * Close transmitter file + */ + fclose(bp.txfp); + bp.txfp = NULL; + + if (bp.txpos >= 0) { + bp.stxpos = bp.txpos - bp.stxpos; + Syslog('+', "Binkp: OK %s", transfertime(txtvstart, txtvend, bp.stxpos, TRUE)); + } else { + Syslog('+', "Binkp: transmitter skipped file after %ld seconds", txtvend.tv_sec - txtvstart.tv_sec); + } + + cursend->state = IsSent; + cursend = NULL; + bp.TxState = TxGNF; + return Ok; + } else { + /* + * regular datablock + */ + bp.txpos += bp.txlen; + sentbytes += bp.txlen; + rc = binkp_send_frame(FALSE, bp.txbuf, bp.txlen); + bp.TxState = TxTryR; + if (rc) + return Failure; + else + return Ok; + } + + } else if (bp.TxState == TxWLA) { + + if ((bp.msgs_on_queue == 0) && (binkp_pendingfiles() == 0)) { + + if ((bp.RxState >= RxEOB) && (bp.Major == 1) && (bp.Minor == 0)) { + bp.TxState = TxDone; + if (bp.local_EOB && bp.remote_EOB) { + Syslog('b', "Binkp: binkp/1.0 session seems complete"); + bp.RxState = RxDone; + } + + binkp_clear_filelist(); + return Ok; + } + + if ((bp.RxState < RxEOB) && (bp.Major == 1) && (bp.Minor == 0)) { + bp.TxState = TxWLA; + return Ok; + } + + if ((bp.Major == 1) && (bp.Minor != 0)) { +// Syslog('B', "Binkp: 1.1 check local_EOB=%s remote_EOB=%s messages=%d", +// bp.local_EOB?"True":"False", bp.remote_EOB?"True":"False", bp.messages); + + if (bp.local_EOB && bp.remote_EOB) { + /* + * We did send EOB and got a EOB + */ + if (bp.messages < 3) { + /* + * Nothing sent anymore, finish + */ + Syslog('b', "Binkp: binkp/1.1 session seems complete"); + bp.RxState = RxDone; + bp.TxState = TxDone; + } else { + /* + * Start new batch + */ + bp.batchnr++; + bp.local_EOB = FALSE; + bp.remote_EOB = FALSE; + bp.messages = 0; + bp.TxState = TxGNF; + bp.RxState = RxWaitF; + Syslog('+', "Binkp: transmitter starts batch %d", bp.batchnr + 1); + binkp_clear_filelist(); + return Ok; /* Continue is not good here, troubles with binkd on slow links. */ + } + } + + binkp_clear_filelist(); + return Ok; + } + } + + if (bp.msgs_on_queue) { + if (binkp_process_messages()) { + return Failure; + } else { + return Continue; + } + } + return Ok; + + } else if (bp.TxState == TxDone) { + return Ok; + } + + /* + * Cannot be here + */ + bp.TxState = TxDone; + return Failure; +} + + +/************************************************************************************/ +/* + * Functions + */ + + +/* + * Send a binkp frame + */ +int binkp_send_frame(int cmd, char *buf, int len) +{ + unsigned short header = 0; + int rc, id; + + if (cmd) { + header = ((BINKP_CONTROL_BLOCK + len) & 0xffff); + bp.messages++; + id = buf[0]; + if (id == MM_EOB) { + bp.local_EOB = TRUE; + } + if (len == 1) + Syslog('b', "Binkp: send %s", bstate[id]); + else + Syslog('b', "Binkp: send %s %s", bstate[id], printable(buf +1, len -1)); + } else { + header = ((BINKP_DATA_BLOCK + len) & 0xffff); + Syslog('b', "Binkp: send data (%d)", len); + } + + rc = PUTCHAR((header >> 8) & 0x00ff); + if (!rc) + rc = PUTCHAR(header & 0x00ff); + if (len && !rc) + rc = PUT(buf, len); + FLUSHOUT(); + binkp_settimer(BINKP_TIMEOUT); return rc; } /* - * Translate string to binkp escaped string, unsafe characters are escaped. + * Send a command message */ +int binkp_send_command(int id, ...) +{ + va_list args; + char *fmt; + static char buf[1024]; + int sz, rc; + + va_start(args, id); + fmt = va_arg(args, char*); + + if (fmt) { + vsprintf(buf, fmt, args); + sz = (strlen(buf) & 0x7fff); + } else { + buf[0]='\0'; + sz = 0; + } + + memmove(buf+1, buf, sz); + buf[0] = id & 0xff; + sz++; + + rc = binkp_send_frame(TRUE, buf, sz); + va_end(args); + return rc; +} + + + +/* + * Set binkp master timer + */ +void binkp_settimer(int interval) +{ + Timer = time((time_t*)NULL) + interval; +} + + + +/* + * Test if master timer is expired + */ +int binkp_expired(void) +{ + time_t now; + + now = time(NULL); + if (now >= Timer) + Syslog('+', "Binkp: timeout"); + return (now >= Timer); +} + + + +/* + * Send system info to remote + */ +int binkp_banner(void) +{ + time_t t; + int rc; + + rc = binkp_send_command(MM_NUL,"SYS %s", CFG.bbs_name); + if (!rc) + rc = binkp_send_command(MM_NUL,"ZYZ %s", CFG.sysop_name); + if (!rc) + rc = binkp_send_command(MM_NUL,"LOC %s", CFG.location); + if (!rc) + rc = binkp_send_command(MM_NUL,"NDL %s", CFG.Flags); + t = time(NULL); + if (!rc) + rc = binkp_send_command(MM_NUL,"TIME %s", rfcdate(t)); + if (!rc) { + if (nodes.NoBinkp11) + rc = binkp_send_command(MM_NUL,"VER mbcico/%s/%s-%s %s/%s", VERSION, OsName(), OsCPU(), PRTCLNAME, PRTCLOLD); + else + rc = binkp_send_command(MM_NUL,"VER mbcico/%s/%s-%s %s/%s", VERSION, OsName(), OsCPU(), PRTCLNAME, PRTCLVER); + } + if (strlen(CFG.Phone) && !rc) + rc = binkp_send_command(MM_NUL,"PHN %s", CFG.Phone); + if (strlen(CFG.comment) && !rc) + rc = binkp_send_command(MM_NUL,"OPM %s", CFG.comment); + + return rc; +} + + + +/* + * Receive command frame + */ +int binkp_recv_command(char *buf, int *len, int *cmd) +{ + int b0, b1; + + *len = *cmd = 0; + + b0 = GETCHAR(BINKP_TIMEOUT); + if (tty_status) + goto to; + if (b0 & 0x80) + *cmd = 1; + + b1 = GETCHAR(1); + if (tty_status) + goto to; + + *len = (b0 & 0x7f) << 8; + *len += b1; + + GET(buf, *len, BINKP_TIMEOUT / 2); + buf[*len] = '\0'; + if (tty_status) + goto to; + +to: + if (tty_status) + WriteError("Binkp: TCP rcv error, tty status: %s", ttystat[tty_status]); + return tty_status; +} + + + +/* + * Parse a received M_NUL message + */ +void parse_m_nul(char *msg) +{ + char *p, *q; + + if (strncmp(msg, "SYS ", 4) == 0) { + Syslog('+', "System : %s", msg+4); + strncpy(history.system_name, msg+4, 35); + + } else if (strncmp(msg, "ZYZ ", 4) == 0) { + Syslog('+', "Sysop : %s", msg+4); + strncpy(history.sysop, msg+4, 35); + + } else if (strncmp(msg, "LOC ", 4) == 0) { + Syslog('+', "Location: %s", msg+4); + strncpy(history.location, msg+4, 35); + + } else if (strncmp(msg, "NDL ", 4) == 0) { + Syslog('+', "Flags : %s", msg+4); + + } else if (strncmp(msg, "TIME ", 5) == 0) { + Syslog('+', "Time : %s", msg+5); + + } else if (strncmp(msg, "VER ", 4) == 0) { + Syslog('+', "Uses : %s", msg+4); + if ((p = strstr(msg+4, PRTCLNAME "/")) && (q = strstr(p, "."))) { + bp.Major = atoi(p + 6); + bp.Minor = atoi(q + 1); + } + } else if (strncmp(msg, "PHN ", 4) == 0) { + Syslog('+', "Phone : %s", msg+4); + + } else if (strncmp(msg, "OPM ", 4) == 0) { + Syslog('+', "Remark : %s", msg+4); + + } else if (strncmp(msg, "TRF ", 4) == 0) { + Syslog('+', "Binkp: remote has %s mail/files for us", msg+4); + + } else if (strncmp(msg, "OPT ", 4) == 0) { + Syslog('+', "Options : %s", msg+4); + if (strstr(msg, (char *)"CRAM-MD5-") != NULL) { /* No SHA-1 support */ + if (CFG.NoMD5) { + Syslog('+', "Binkp: Remote supports MD5, but it's turned off here"); + } else { + if (bp.MD_Challenge) + free(bp.MD_Challenge); + bp.MD_Challenge = MD_getChallenge(msg, NULL); + } + } + } else { + Syslog('+', "Binkp: M_NUL \"%s\"", msg); + } +} + + + +/* + * Poll for a frame, returns: + * -1 = Error + * 0 = Nothing yet + * 1 = Got a frame + * 2 = Frame not processed + */ +int binkp_poll_frame(void) +{ + int c, rc = 0, bcmd; + + for (;;) { + if (bp.GotFrame) { + Syslog('b', "Binkp: WARNING: frame not processed"); + rc = 2; + break; + } else { + c = GETCHAR(0); + if (c < 0) { + c = -c; + if (c == STAT_TIMEOUT) { + usleep(1); + rc = 0; + break; + } + Syslog('?', "Binkp: receiver status %s", ttystat[c]); + bp.rc = (MBERR_TTYIO + (-c)); + rc = -1; + break; + } else { + switch (bp.rxlen) { + case 0: bp.header = c << 8; + rc = 0; + break; + case 1: bp.header += c; + rc = 0; + break; + default:bp.rxbuf[bp.rxlen-2] = c; + } + if (bp.rxlen == 1) { + bp.cmd = bp.header & 0x8000; + bp.blklen = bp.header & 0x7fff; + } + if ((bp.rxlen == (bp.blklen + 1) && (bp.rxlen >= 1))) { + bp.GotFrame = TRUE; + bp.rxbuf[bp.rxlen-1] = '\0'; + if (bp.cmd) { + bp.messages++; + bcmd = bp.rxbuf[0]; + Syslog('b', "Binkp: rcvd %s %s", bstate[bcmd], printable(bp.rxbuf+1, 0)); + if (bcmd == MM_EOB) { + bp.remote_EOB = TRUE; + } + } else { + Syslog('b', "Binkp: rcvd data (%d)", bp.rxlen -1); + } + rc = 1; + break; + } + bp.rxlen++; + } + } + } + + return rc; +} + + + +/* + * Add received command frame to the queue, will be processed by the transmitter. + */ +void binkp_add_message(char *frame) +{ + the_queue **tmpl; + int bcmd; + + bcmd = frame[0]; + Syslog('b', "Binkp: add message %s %s", bstate[bcmd], printable(frame +1, 0)); + + for (tmpl = &tql; *tmpl; tmpl = &((*tmpl)->next)); + *tmpl = (the_queue *)malloc(sizeof(the_queue)); + + (*tmpl)->next = NULL; + (*tmpl)->cmd = frame[0]; + (*tmpl)->data = xstrcpy(frame +1); + + bp.msgs_on_queue++; +} + + + +/* + * Process all messages on the queue, the oldest are on top, after + * processing release memory and reset the messages queue. + */ +int binkp_process_messages(void) +{ + the_queue *tmpq, *oldq; + binkp_list *tmp; + file_list *tsl; + int Found; + char *lname; + time_t ltime; + long lsize, loffs; + + Syslog('b', "Binkp: Process The Messages Queue Start"); + + lname = calloc(512, sizeof(char)); + + for (tmpq = tql; tmpq; tmpq = tmpq->next) { + Syslog('+', "Binkp: %s \"%s\"", bstate[tmpq->cmd], printable(tmpq->data, 0)); + if (tmpq->cmd == MM_GET) { + sprintf(lname, "%s", strtok(tmpq->data, " \n\r")); + lsize = atoi(strtok(NULL, " \n\r")); + ltime = atoi(strtok(NULL, " \n\r")); + loffs = atoi(strtok(NULL, " \n\r")); + Found = FALSE; + for (tmp = bll; tmp; tmp = tmp->next) { + if ((strcmp(lname, tmp->remote) == 0) && (lsize == tmp->size) && (ltime == tmp->date)) { + if (lsize == loffs) { + /* + * Requested offset is filesize, close file. + */ + if (cursend && (strcmp(lname, cursend->remote) == 0) && + (lsize == cursend->size) && (ltime == cursend->date)) { + Syslog('b', "Got M_GET with offset == filesize for current file, close"); + /* + * Close transmitter file + */ + fclose(bp.txfp); + bp.txfp = NULL; + + if (bp.txpos >= 0) { + bp.stxpos = bp.txpos - bp.stxpos; + Syslog('+', "Binkp: OK %s", transfertime(txtvstart, txtvend, bp.stxpos, TRUE)); + } else { + Syslog('+', "Binkp: transmitter skipped file after %ld seconds", + txtvend.tv_sec - txtvstart.tv_sec); + } + + cursend->state = IsSent; + cursend = NULL; + bp.TxState = TxGNF; + } else { + Syslog('+', "Binkp: requested offset = filesize, but is not the current file"); + } + } else if (loffs < lsize) { + tmp->state = NoState; + tmp->offset = loffs; + if (loffs) { + Syslog('+', "Binkp: Remote wants %s for resync at offset %ld", tmp->remote, tmp->offset); + } else { + Syslog('+', "Binkp: Remote wants %s again from start", tmp->remote); + } + bp.TxState = TxGNF; + } else { + Syslog('+', "Binkp: requested offset > filesize, ignore"); + } + Found = TRUE; + break; + } + if (!Found) { + Syslog('+', "Binkp: unexpected M_GET \"%s\"", tmpq->data); /* Ignore frame */ + } + } + } else if (tmpq->cmd == MM_GOT) { + sprintf(lname, "%s", strtok(tmpq->data, " \n\r")); + lsize = atoi(strtok(NULL, " \n\r")); + ltime = atoi(strtok(NULL, " \n\r")); + Found = FALSE; + for (tmp = bll; tmp; tmp = tmp->next) { + if ((strcmp(lname, tmp->remote) == 0) && (lsize == tmp->size) && (ltime == tmp->date)) { + Found = TRUE; + if (tmp->state == Sending) { + Syslog('+', "Binkp: remote refused %s", tmp->remote); + fclose(bp.txfp); + bp.txfp = NULL; + bp.TxState = TxGNF; + cursend = NULL; + } else { + Syslog('+', "Binkp: remote GOT \"%s\"", tmp->remote); + } + tmp->state = Got; + for (tsl = tosend; tsl; tsl = tsl->next) { + if (tsl->remote == NULL) { + execute_disposition(tsl); + } else { + if (strcmp(tmp->local, tsl->local) == 0) { + execute_disposition(tsl); + } + } + } + break; + } + } + if (!Found) { + Syslog('+', "Binkp: unexpected M_GOT \"%s\"", tmpq->data); /* Ignore frame */ + } + } else if (tmpq->cmd == MM_SKIP) { + sprintf(lname, "%s", strtok(tmpq->data, " \n\r")); + lsize = atoi(strtok(NULL, " \n\r")); + ltime = atoi(strtok(NULL, " \n\r")); + Found = FALSE; + for (tmp = bll; tmp; tmp = tmp->next) { + if ((strcmp(lname, tmp->remote) == 0) && (lsize == tmp->size) && (ltime == tmp->date)) { + Found = TRUE; + if (tmp->state == Sending) { + Syslog('+', "Binkp: remote skipped %s, may be accepted later", tmp->remote); + fclose(bp.txfp); + bp.txfp = NULL; + cursend = NULL; + bp.TxState = TxGNF; + } else { + Syslog('+', "Binkp: remote refused %s", tmp->remote); + } + tmp->state = Skipped; + break; + } + } + if (!Found) { + Syslog('+', "Binkp: unexpected M_SKIP \"%s\"", tmpq->data); /* Ignore frame */ + } + } else { + /* Illegal message on the queue */ + } + } + + /* + * Tidy the messages queue and reset. + */ + for (tmpq = tql; tmpq; tmpq = oldq) { + oldq = tmpq->next; + if (tmpq->data) + free(tmpq->data); + free(tmpq); + } + tql = NULL; + free(lname); + bp.msgs_on_queue = 0; + + debug_binkp_list(&bll); + Syslog('b', "Binkp: Process The Messages Queue End"); + return 0; +} + + + +/* + * Count number of pending files + */ +int binkp_pendingfiles(void) +{ + binkp_list *tmpl; + int count = 0; + + for (tmpl = bll; tmpl; tmpl = tmpl->next) { + if ((tmpl->state != Got) && (tmpl->state != Skipped)) + count++; + } + + if (count) + Syslog('b', "Binkp: %d pending files on queue", count); + return count; +} + + + +/* + * This function is called two times if a partial file exists from openfile. + * 1. A partial file is detected, send a GET to the remote, set DidSendGET flag. + * 2. DidSendGET is set, return 0 and let openfile open the file in append mode. + */ +int binkp_resync(off_t off) +{ + Syslog('b', "Binkp: resync(%d) DidSendGET=%s", off, bp.DidSendGET ?"TRUE":"FALSE"); + if (!bp.DidSendGET) { + binkp_send_command(MM_GET, "%s %ld %ld %ld", bp.rname, bp.rsize, bp.rtime, off); + bp.DidSendGET = TRUE; + Syslog('+', "Binkp: already %lu bytes received, requested restart with offset", (unsigned long)off); + return -1; /* Signal openfile not to open the file */ + } + bp.DidSendGET = FALSE; + return 0; /* Signal openfile to open the file in append mode */ +} + + + +/* + * * Translate string to binkp escaped string, unsafe characters are escaped. + * */ char *unix2binkp(char *fn) { - static char buf[PATH_MAX]; - char *p, *q; + static char buf[PATH_MAX]; + char *p, *q; memset(&buf, 0, sizeof(buf)); p = fn; q = buf; - + while (*p) { if (strspn(p, (char *)BNKCHARS)) { *q++ = *p; @@ -357,20 +2020,20 @@ char *unix2binkp(char *fn) p++; } *q = '\0'; - + return buf; } /* - * Translate escaped binkp string to normal string. - */ + * * Translate escaped binkp string to normal string. + * */ char *binkp2unix(char *fn) { static char buf[PATH_MAX]; - char *p, *q, hex[3]; - int c; + char *p, *q, hex[3]; + int c; memset(&buf, 0, sizeof(buf)); p = fn; @@ -416,753 +2079,23 @@ char *binkp2unix(char *fn) /* - * Transmit data frame + * Fill internal binkp filelist */ -void binkp_send_data(char *buf, int len) +void fill_binkp_list(binkp_list **bkll, file_list *fal, off_t offs) { - unsigned short header = 0; - - Syslog('B', "Binkp: send_data len=%d", len); - - header = ((BINKP_DATA_BLOCK + len) & 0xffff); - - PUTCHAR((header >> 8) & 0x00ff); - PUTCHAR(header & 0x00ff); - if (len) - PUT(buf, len); - FLUSHOUT(); - binkp_settimer(BINKP_TIMEOUT); -} - - - -/* - * Transmit control frame - */ -void binkp_send_control(int id,...) -{ - va_list args; - char *fmt, *s; - binkp_frame frame; - static char buf[1024]; - int sz; - - va_start(args, id); - fmt = va_arg(args, char*); - - if (fmt) { - vsprintf(buf, fmt, args); - sz = ((1 + strlen(buf)) & 0x7fff); - } else { - buf[0]='\0'; - sz = 1; - } - - Syslog('b', "Binkp: send_ctl %s \"%s\"", bstate[id], buf); - frame.header = ((BINKP_CONTROL_BLOCK + sz) & 0xffff); - frame.id = (char)id; - frame.data = buf; - - s = (unsigned char *)malloc(sz + 2 + 1); - s[sz + 2] = '\0'; - s[0] = ((frame.header >> 8)&0xff); - s[1] = (frame.header & 0xff); - s[2] = frame.id; - if (frame.data[0]) - strncpy(s + 3, frame.data, sz-1); - - PUT(s, sz+2); - FLUSHOUT(); - - free(s); - va_end(args); - binkp_settimer(BINKP_TIMEOUT); -} - - - -/* - * This function is called two times if a partial file exists from openfile. - * 1. A partial file is detected, send a GET to the remote, set DidSendGET flag. - * 2. DidSendGET is set, return 0 and let openfile open the file in append mode. - */ -int resync(off_t off) -{ - Syslog('b', "Binkp: resync(%d) DidSendGET=%s", off, bp.DidSendGET ?"TRUE":"FALSE"); - if (!bp.DidSendGET) { - binkp_send_control(MM_GET, "%s %ld %ld %ld", bp.rname, bp.rsize, bp.rtime, off); - bp.DidSendGET = TRUE; - Syslog('+', "Binkp: already %lu bytes received, requested restart with offset", (unsigned long)off); - return -1; /* Signal openfile not to open the file */ - } - bp.DidSendGET = FALSE; - return 0; /* Signal openfile to open the file in append mode */ -} - - - -/* - * Receive control frame - */ -int binkp_recv_frame(char *buf, int *len, int *cmd) -{ - int b0, b1; - - *len = *cmd = 0; - - b0 = GETCHAR(BINKP_TIMEOUT); - if (tty_status) - goto to; - if (b0 & 0x80) - *cmd = 1; - - b1 = GETCHAR(1); - if (tty_status) - goto to; - - *len = (b0 & 0x7f) << 8; - *len += b1; - - GET(buf, *len, BINKP_TIMEOUT / 2); - buf[*len] = '\0'; - if (tty_status) - goto to; - -to: - if (tty_status) - WriteError("Binkp: TCP receive error: %d %s", tty_status, ttystat[tty_status]); - return tty_status; -} - - - -void binkp_settimer(int interval) -{ - Timer = time((time_t*)NULL) + interval; -} - - - -int binkp_expired(void) -{ - time_t now; - - now = time(NULL); - if (now >= Timer) - Syslog('+', "Binkp: timeout"); - return (now >= Timer); -} - - - -void b_banner(void) -{ - time_t t; - - binkp_send_control(MM_NUL,"SYS %s", CFG.bbs_name); - binkp_send_control(MM_NUL,"ZYZ %s", CFG.sysop_name); - binkp_send_control(MM_NUL,"LOC %s", CFG.location); - binkp_send_control(MM_NUL,"NDL %s", CFG.Flags); - t = time(NULL); - binkp_send_control(MM_NUL,"TIME %s", rfcdate(t)); - binkp_send_control(MM_NUL,"VER mbcico/%s/%s-%s %s/%s", VERSION, OsName(), OsCPU(), PRTCLNAME, PRTCLVER); - if (strlen(CFG.Phone)) - binkp_send_control(MM_NUL,"PHN %s", CFG.Phone); - if (strlen(CFG.comment)) - binkp_send_control(MM_NUL,"OPM %s", CFG.comment); -} - - - -void b_nul(char *msg) -{ - char *p, *q; - - if (strncmp(msg, "SYS ", 4) == 0) { - Syslog('+', "System : %s", msg+4); - strncpy(history.system_name, msg+4, 35); - } else if (strncmp(msg, "ZYZ ", 4) == 0) { - Syslog('+', "Sysop : %s", msg+4); - strncpy(history.sysop, msg+4, 35); - } else if (strncmp(msg, "LOC ", 4) == 0) { - Syslog('+', "Location: %s", msg+4); - strncpy(history.location, msg+4, 35); - } else if (strncmp(msg, "NDL ", 4) == 0) - Syslog('+', "Flags : %s", msg+4); - else if (strncmp(msg, "TIME ", 5) == 0) - Syslog('+', "Time : %s", msg+5); - else if (strncmp(msg, "VER ", 4) == 0) { - Syslog('+', "Uses : %s", msg+4); - if ((p = strstr(msg+4, PRTCLNAME "/")) && (q = strstr(p, "."))) { - bp.Major = atoi(p + 6); - bp.Minor = atoi(q + 1); - Syslog('b', "Remote protocol version %d.%d", bp.Major, bp.Minor); - /* - * Disable MB if protocol > 1.0 and MB was not yet active. - */ - if ((bp.MBflag != Active) && (((bp.Major * 10) + bp.Minor) > 10)) { - Syslog('b', "MBflag %s => No", opstate[bp.MBflag]); - bp.MBflag = No; - } - } - } - else if (strncmp(msg, "PHN ", 4) == 0) - Syslog('+', "Phone : %s", msg+4); - else if (strncmp(msg, "OPM ", 4) == 0) - Syslog('+', "Remark : %s", msg+4); - else if (strncmp(msg, "TRF ", 4) == 0) - Syslog('+', "Binkp: remote has %s mail/files for us", msg+4); - else if (strncmp(msg, "OPT ", 4) == 0) { - Syslog('+', "Options : %s", msg+4); - if (strstr(msg, (char *)"MB") != NULL) { - Syslog('b', "Remote requests MB, current state = %s", opstate[bp.MBflag]); - if ((bp.MBflag == WeCan) && (bp.Major == 1) && (bp.Minor == 0)) { /* Answering session and do binkp/1.0 */ - bp.MBflag = TheyWant; - Syslog('b', "MBflag WeCan => TheyWant"); - binkp_send_control(MM_NUL,"OPT MB"); - Syslog('b', "MBflag TheyWant => Active"); - bp.MBflag = Active; - } else if ((bp.MBflag == WeWant) && (bp.Major == 1) && (bp.Minor == 0)) { /* Originating session and do binkp/1.0 */ - bp.MBflag = Active; - Syslog('b', "MBflag WeWant => Active"); - } else { - Syslog('b', "MBflag is %s and received MB option", opstate[bp.MBflag]); - } - } - if (strstr(msg, (char *)"CRAM-MD5-") != NULL) { /* No SHA-1 support */ - if (CFG.NoMD5) { - Syslog('+', "Binkp: Remote supports MD5, but it's turned off here"); - } else { - if (MD_challenge) - free(MD_challenge); - MD_challenge = MD_getChallenge(msg, NULL); - } - } - } else - Syslog('+', "Binkp: M_NUL \"%s\"", msg); -} - - - -/* - * Originate a binkp session - */ -SM_DECL(orgbinkp, (char *)"orgbinkp") -SM_STATES - ConnInit, - WaitConn, - SendPasswd, - WaitAddr, - AuthRemote, - IfSecure, - WaitOk, - Opts -SM_NAMES - (char *)"ConnInit", - (char *)"WaitConn", - (char *)"SendPasswd", - (char *)"WaitAddr", - (char *)"AuthRemote", - (char *)"IfSecure", - (char *)"WaitOk", - (char *)"Opts" -SM_EDECL - faddr *primary; - char *p, *q, *pwd; - int i, rc, bufl, cmd, dupe, SendPass = FALSE; - fa_list **tmp, *tmpa; - faddr *fa, ra; - -SM_START(ConnInit) - -SM_STATE(ConnInit) - - SM_PROCEED(WaitConn) - -SM_STATE(WaitConn) - - Loaded = FALSE; - Syslog('+', "Binkp: node %s", ascfnode(remote->addr, 0x1f)); - IsDoing("Connect binkp %s", ascfnode(remote->addr, 0xf)); - - /* - * Build options we want - */ -// p = xstrcpy((char *)"OPT"); -// if ((noderecord(remote->addr)) && nodes.CRC32 && (bp.CRCflag == WeCan)) { -// p = xstrcat(p, (char *)" CRC"); -// bp.CRCflag = WeWant; -// Syslog('b', "CRCflag WeCan => WeWant"); -// } -// if (strcmp(p, (char *)"OPT")) -// binkp_send_control(MM_NUL, p); -// free(p); - b_banner(); - - /* - * Build a list of aka's to send, the primary aka first. - */ - ra.zone = remote->addr->zone; - ra.net = remote->addr->net; - ra.node = remote->addr->node; - ra.point = remote->addr->point; - - primary = bestaka_s(remote->addr); - p = xstrcpy(ascfnode(primary, 0x1f)); - - /* - * Add all other aka's exept primary aka. - */ - for (i = 0; i < 40; i++) - if ((CFG.aka[i].zone) && (CFG.akavalid[i]) && - ((CFG.aka[i].zone != primary->zone) || (CFG.aka[i].net != primary->net) || - (CFG.aka[i].node != primary->node) || (CFG.aka[i].point!= primary->point))) { - p = xstrcat(p, (char *)" "); - p = xstrcat(p, aka2str(CFG.aka[i])); - } - - binkp_send_control(MM_ADR, "%s", p); - free(p); - tidy_faddr(primary); - SM_PROCEED(WaitAddr) - -SM_STATE(WaitAddr) - - for (;;) { - if ((rc = binkp_recv_frame(rbuf, &bufl, &cmd))) { - Syslog('!', "Binkp: error receiving remote info"); - SM_ERROR; - } - - if (cmd) { - if (rbuf[0] == MM_ADR) { - p = xstrcpy(&rbuf[1]); - tidy_falist(&remote); - remote = NULL; - tmp = &remote; - - for (q = strtok(p, " "); q; q = strtok(NULL, " ")) { - if ((fa = parsefnode(q))) { - dupe = FALSE; - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) && - (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) && - (strcmp(tmpa->addr->domain, fa->domain) == 0)) { - dupe = TRUE; - Syslog('b', "Binkp: double address %s", ascfnode(tmpa->addr, 0x1f)); - break; - } - } - if (!dupe) { - *tmp = (fa_list*)malloc(sizeof(fa_list)); - (*tmp)->next = NULL; - (*tmp)->addr = fa; - tmp = &((*tmp)->next); - } - } else { - Syslog('!', "Binkp: bad remote address: \"%s\"", printable(q, 0)); - binkp_send_control(MM_ERR, "Bad address"); - } - } - - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - Syslog('+', "Address : %s", ascfnode(tmpa->addr, 0x1f)); - if (nodelock(tmpa->addr, mypid)) { - binkp_send_control(MM_BSY, "Address %s locked", ascfnode(tmpa->addr, 0x1f)); - SM_ERROR; - } - - /* - * With the loaded flag we prevent removing the noderecord - * when the remote presents us an address we don't know about. - */ - if (!Loaded) { - if (noderecord(tmpa->addr)) - Loaded = TRUE; - } - } - - history.aka.zone = remote->addr->zone; - history.aka.net = remote->addr->net; - history.aka.node = remote->addr->node; - history.aka.point = remote->addr->point; - sprintf(history.aka.domain, "%s", remote->addr->domain); - - SM_PROCEED(SendPasswd) - - } else if (rbuf[0] == MM_BSY) { - Syslog('!', "Binkp: M_BSY \"%s\"", printable(&rbuf[1], 0)); - SM_ERROR; - - } else if (rbuf[0] == MM_ERR) { - Syslog('!', "Binkp: M_ERR \"%s\"", printable(&rbuf[1], 0)); - SM_ERROR; - - } else if (rbuf[0] == MM_NUL) { - b_nul(&rbuf[1]); - - } else { - binkp_send_control(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } - } - -SM_STATE(SendPasswd) - - if (Loaded && strlen(nodes.Spasswd)) { - pwd = xstrcpy(nodes.Spasswd); - SendPass = TRUE; - } else { - pwd = xstrcpy((char *)"-"); - } - - if (MD_challenge) { - char *tp = NULL; - tp = MD_buildDigest(pwd, MD_challenge); - if (!tp) { - Syslog('!', "Unable to build MD5 digest"); - SM_ERROR; - } - CRAMflag = TRUE; - binkp_send_control(MM_PWD, "%s", tp); - free(tp); - } else { - binkp_send_control(MM_PWD, "%s", pwd); - } - - free(pwd); - SM_PROCEED(AuthRemote) - -SM_STATE(AuthRemote) - - rc = 0; - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - if ((tmpa->addr->zone == ra.zone) && (tmpa->addr->net == ra.net) && - (tmpa->addr->node == ra.node) && (tmpa->addr->point == ra.point)) { - rc = 1; - } - } - - if (rc) { - SM_PROCEED(IfSecure) - } else { - Syslog('!', "Binkp: error, the wrong node is reached"); - binkp_send_control(MM_ERR, "No AKAs in common or all AKAs busy"); - SM_ERROR; - } - -SM_STATE(IfSecure) - - SM_PROCEED(WaitOk) - -SM_STATE(WaitOk) - - for (;;) { - if ((rc = binkp_recv_frame(rbuf, &bufl, &cmd))) { - Syslog('!', "Binkp: error waiting for remote acknowledge"); - SM_ERROR; - } - - if (cmd) { - if (rbuf[0] == MM_OK) { - Syslog('b', "Binkp: M_OK \"%s\"", printable(&rbuf[1], 0)); - if (SendPass) - Secure = TRUE; - Syslog('+', "Binkp: %s%sprotected session", CRAMflag ? "MD5 ":"", Secure ? "":"un"); - SM_PROCEED(Opts) - - } else if (rbuf[0] == MM_BSY) { - Syslog('!', "Binkp: M_BSY \"%s\"", printable(&rbuf[1], 0)); - SM_ERROR; - - } else if (rbuf[0] == MM_ERR) { - Syslog('!', "Binkp: M_ERR \"%s\"", printable(&rbuf[1], 0)); - SM_ERROR; - - } else if (rbuf[0] == MM_NUL) { - b_nul(&rbuf[1]); - - } else { - binkp_send_control(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } - } - -SM_STATE(Opts) - - /* - * Try to initiate the MB option if the remote is binkp/1.0 - */ - if ((bp.MBflag == WeCan) && (bp.Major == 1) && (bp.Minor == 0)) { - bp.MBflag = WeWant; - Syslog('b', "MBflag WeCan => WeWant"); - binkp_send_control(MM_NUL, "OPT MB"); - } - SM_SUCCESS; - -SM_END -SM_RETURN - - - -/* - * Answer a binkp session - */ -SM_DECL(ansbinkp, (char *)"ansbinkp") -SM_STATES - WaitConn, - WaitAddr, - IsPasswd, - WaitPwd, - PwdAck, - Opts -SM_NAMES - (char *)"WaitConn", - (char *)"WaitAddr", - (char *)"IsPasswd", - (char *)"WaitPwd", - (char *)"PwdAck", - (char *)"Opts" - -SM_EDECL - char *p, *q, *pw; - int i, rc, bufl, cmd, dupe, we_have_pwd = FALSE; - fa_list **tmp, *tmpa; - faddr *fa; - -SM_START(WaitConn) - -SM_STATE(WaitConn) - - Loaded = FALSE; - - if (strncmp(SockR("SBBS:0;"), "100:2,1", 7) == 0) { - Syslog('+', "Binkp: system is closed, sending M_BSY"); - binkp_send_control(MM_BSY, "This system is closed, try again later"); - SM_ERROR; - } - - if (!CFG.NoMD5 && ((MD_challenge = MD_getChallenge(NULL, &peeraddr)) != NULL)) { - /* - * Answering site MUST send CRAM message as very first M_NUL - */ - char s[MD5_DIGEST_LEN*2+15]; /* max. length of opt string */ - strcpy(s, "OPT "); - MD_toString(s+4, MD_challenge[0], MD_challenge+1); - CRAMflag = TRUE; - binkp_send_control(MM_NUL, "%s", s); - } - b_banner(); - p = xstrcpy((char *)""); - - for (i = 0; i < 40; i++) - if ((CFG.aka[i].zone) && (CFG.akavalid[i])) { - p = xstrcat(p, (char *)" "); - p = xstrcat(p, aka2str(CFG.aka[i])); - } - - binkp_send_control(MM_ADR, "%s", p); - free(p); - SM_PROCEED(WaitAddr) - -SM_STATE(WaitAddr) - - for (;;) { - if ((rc = binkp_recv_frame(rbuf, &bufl, &cmd))) { - Syslog('!', "Binkp: error waiting for remote info"); - SM_ERROR; - } - - if (cmd) { - if (rbuf[0] == MM_ADR) { - p = xstrcpy(&rbuf[1]); - tidy_falist(&remote); - remote = NULL; - tmp = &remote; - - for (q = strtok(p, " "); q; q = strtok(NULL, " ")) - if ((fa = parsefnode(q))) { - dupe = FALSE; - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) && - (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) && - (strcmp(tmpa->addr->domain, fa->domain) == 0)) { - dupe = TRUE; - Syslog('b', "Binkp: double address %s", ascfnode(tmpa->addr, 0x1f)); - break; - } - } - if (!dupe) { - *tmp = (fa_list*)malloc(sizeof(fa_list)); - (*tmp)->next = NULL; - (*tmp)->addr = fa; - tmp = &((*tmp)->next); - } - } else { - Syslog('!', "Binkp: bad remote address: \"%s\"", printable(q, 0)); - binkp_send_control(MM_ERR, "Bad address"); - } - - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - Syslog('+', "Address : %s", ascfnode(tmpa->addr, 0x1f)); - if (nodelock(tmpa->addr, mypid)) { - binkp_send_control(MM_BSY, "Address %s locked", ascfnode(tmpa->addr, 0x1f)); - SM_ERROR; - } - - /* - * With the loaded flag we prevent removing the noderecord - * when the remote presents us an address we don't know about. - */ - if (!Loaded) { - if (noderecord(tmpa->addr)) - Loaded = TRUE; - } - } - - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - if (((nlent = getnlent(tmpa->addr))) && (nlent->pflag != NL_DUMMY)) { - Syslog('+', "Binkp: remote is a listed system"); - UserCity(mypid, nlent->sysop, nlent->location); - break; - } - } - if (nlent) - rdoptions(Loaded); - -// if (bp.MBflag == TheyWant) { -// Syslog('b', "Binkp: remote supports MB"); -// binkp_send_control(MM_NUL,"OPT MB"); -// bp.MBflag = Active; -// } - - history.aka.zone = remote->addr->zone; - history.aka.net = remote->addr->net; - history.aka.node = remote->addr->node; - history.aka.point = remote->addr->point; - sprintf(history.aka.domain, "%s", remote->addr->domain); - - SM_PROCEED(IsPasswd) - - } else if (rbuf[0] == MM_ERR) { - Syslog('!', "Binkp: M_ERR \"%s\"", printable(&rbuf[1], 0)); - SM_ERROR; - - } else if (rbuf[0] == MM_NUL) { - b_nul(&rbuf[1]); - } else if (rbuf[0] <= MM_MAX) { - binkp_send_control(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } - } - -SM_STATE(IsPasswd) - - if (Loaded && strlen(nodes.Spasswd)) { - we_have_pwd = TRUE; - } - - Syslog('b', "We %s have a password", we_have_pwd ?"do":"don't"); - SM_PROCEED(WaitPwd) - -SM_STATE(WaitPwd) - - for (;;) { - if ((rc = binkp_recv_frame(rbuf, &bufl, &cmd))) { - Syslog('!', "Binkp: error waiting for password"); - SM_ERROR; - } - - if (cmd) { - if (rbuf[0] == MM_PWD) { - SM_PROCEED(PwdAck) - - } else if (rbuf[0] == MM_ERR) { - Syslog('!', "Binkp: M_ERR \"%s\"", printable(&rbuf[1], 0)); - SM_ERROR; - } else if (rbuf[0] == MM_NUL) { - b_nul(&rbuf[1]); - } else if (rbuf[0] <= MM_MAX) { - binkp_send_control(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } - } - -SM_STATE(PwdAck) - - if (we_have_pwd) { - pw = xstrcpy(nodes.Spasswd); - } else { - pw = xstrcpy((char *)"-"); - } - - if ((strncmp(&rbuf[1], "CRAM-", 5) == 0) && CRAMflag) { - char *sp; - sp = MD_buildDigest(pw, MD_challenge); - if (sp != NULL) { - if (strcmp(&rbuf[1], sp)) { - Syslog('+', "Binkp: bad MD5 crypted password"); - binkp_send_control(MM_ERR, "Bad password"); - free(sp); - sp = NULL; - free(pw); - SM_ERROR; - } else { - free(sp); - sp = NULL; - if (we_have_pwd) - Secure = TRUE; - } - } else { - free(pw); - Syslog('!', "Binkp: could not build MD5 digest"); - binkp_send_control(MM_ERR, "*** Internal error ***"); - SM_ERROR; - } - } else if ((strcmp(&rbuf[1], pw) == 0)) { - if (we_have_pwd) - Secure = TRUE; - } else { - free(pw); - Syslog('?', "Binkp: password error: expected \"%s\", got \"%s\"", nodes.Spasswd, &rbuf[1]); - binkp_send_control(MM_ERR, "Bad password"); - SM_ERROR; - } - - free(pw); - Syslog('+', "Binkp: %s%sprotected session", CRAMflag ? "MD5 ":"", Secure ? "":"un"); - inbound_open(remote->addr, Secure); - binkp_send_control(MM_OK, "%ssecure", Secure ? "":"non-"); - SM_PROCEED(Opts) - -SM_STATE(Opts) - - SM_SUCCESS; - -SM_END -SM_RETURN - - - -void fill_binkp_list(binkp_list **bll, file_list *fal, off_t offs) -{ - binkp_list **tmpl; - struct stat tstat; + binkp_list **tmpl; + struct stat tstat; if (stat(fal->local, &tstat) != 0) { - Syslog('!', "$Can't add %s to sendlist", fal->local); + Syslog('+', "$Binkp: can't add %s to sendlist", fal->local); return; } if (strstr(fal->remote, (char *)".pkt")) bp.nethold += tstat.st_size; else bp.mailhold += tstat.st_size; - - for (tmpl = bll; *tmpl; tmpl = &((*tmpl)->next)); + + for (tmpl = bkll; *tmpl; tmpl = &((*tmpl)->next)); *tmpl = (binkp_list *)malloc(sizeof(binkp_list)); (*tmpl)->next = NULL; @@ -1177,505 +2110,42 @@ void fill_binkp_list(binkp_list **bll, file_list *fal, off_t offs) -char *lbstat[]={(char *)"None", - (char *)"Sending", - (char *)"IsSent", - (char *)"Got", - (char *)"Skipped", - (char *)"Get"}; - - -void debug_binkp_list(binkp_list **bll) +void debug_binkp_list(binkp_list **bkll) { - binkp_list *tmpl; + binkp_list *tmpl; Syslog('B', "Current filelist:"); - for (tmpl = *bll; tmpl; tmpl = tmpl->next) - Syslog('B', "%s %s %s %ld", MBSE_SS(tmpl->local), MBSE_SS(tmpl->remote), lbstat[tmpl->state], tmpl->offset); + for (tmpl = *bkll; tmpl; tmpl = tmpl->next) + Syslog('B', "%s %s %s %ld", MBSE_SS(tmpl->local), MBSE_SS(tmpl->remote), lbstate[tmpl->state], tmpl->offset); } + /* - * Get a fram for the batch + * Clear current filelist */ -void batch_receive_frame(void); -void batch_receive_frame(void) +void binkp_clear_filelist(void) { - int c; + binkp_list *tmp; - for (;;) { - if (bp.GotFrame) { - Syslog('b', "Binkp: WARNING: frame not processed"); - break; - } else { - c = GETCHAR(0); - if (c < 0) { - c = -c; - if (c == STAT_TIMEOUT) { - usleep(1); - break; - } - Syslog('?', "Binkp: receiver status %s", ttystat[c]); - bp.TxState = TxDone; - bp.RxState = RxDone; - bp.rc = (MBERR_TTYIO + (-c)); - break; - } else { - switch (bp.rxlen) { - case 0: bp.header = c << 8; - break; - case 1: bp.header += c; - break; - default:bp.rxbuf[bp.rxlen-2] = c; - } - if (bp.rxlen == 1) { - bp.cmd = bp.header & 0x8000; - bp.blklen = bp.header & 0x7fff; - } - if ((bp.rxlen == (bp.blklen + 1) && (bp.rxlen >= 1))) { - bp.GotFrame = TRUE; - binkp_settimer(BINKP_TIMEOUT); - bp.rxbuf[bp.rxlen-1] = '\0'; - break; - } - bp.rxlen++; - } + if (tosend != NULL) { + Syslog('b', "Binkp: clear current filelist"); + + for (tmp = bll; bll; bll = tmp) { + tmp = bll->next; + if (bll->local) + free(bll->local); + if (bll->remote) + free(bll->remote); + free(bll); } + + tidy_filelist(tosend, TRUE); + tosend = NULL; + respond = NULL; } } - -int binkp_batch(file_list *to_send) -{ - int NotDone, written, Found = FALSE; - binkp_list *bll = NULL, *tmp, *tmpg, *cursend = NULL; - file_list *tsl; - struct statfs sfs; - - bp.rxtvstart.tv_sec = bp.rxtvstart.tv_usec = 0; - bp.rxtvend.tv_sec = bp.rxtvend.tv_usec = 0; - bp.txtvstart.tv_sec = bp.txtvstart.tv_usec = 0; - bp.txtvend.tv_sec = bp.txtvend.tv_usec = 0; - bp.tz.tz_minuteswest = bp.tz.tz_dsttime = 0; - - bp.batchnr++; - Syslog('+', "Binkp: starting batch %d", bp.batchnr); - IsDoing("Binkp %s %s", (bp.role == 1)?"out":"inb", ascfnode(remote->addr, 0xf)); -// TfState = Switch; - bp.RxState = RxWaitFile; - bp.TxState = TxGetNextFile; - binkp_settimer(BINKP_TIMEOUT); - bp.nethold = bp.mailhold = 0L; - transferred = FALSE; - - /* - * Build a new filelist from the existing filelist. - * This one is special for binkp behaviour. - */ - for (tsl = to_send; tsl; tsl = tsl->next) { - if (tsl->remote != NULL) - fill_binkp_list(&bll, tsl, 0L); - } - debug_binkp_list(&bll); - - Syslog('+', "Binkp: mail %ld, files %ld bytes", bp.nethold, bp.mailhold); - binkp_send_control(MM_NUL, "TRF %ld %ld", bp.nethold, bp.mailhold); - - while ((bp.RxState != RxDone) || (bp.TxState != TxDone)) { - - Nopper(); - if (binkp_expired()) { - Syslog('!', "Binkp: Transfer timeout"); - Syslog('b', "Binkp: TxState=%s, RxState=%s, rxlen=%d", txstate[bp.TxState], rxstate[bp.RxState], bp.rxlen); - bp.RxState = RxDone; - bp.TxState = TxDone; - binkp_send_control(MM_ERR, "Transfer timeout"); - bp.rc = MBERR_FTRANSFER; - break; - } - - /* - * Receiver binkp frame - */ - batch_receive_frame(); - - /* - * Transmitter state machine - */ - switch (bp.TxState) { - case TxGetNextFile: - for (tmp = bll; tmp; tmp = tmp->next) { - if (tmp->state == NoState) { - /* - * There is something to send - */ - struct flock txflock; - - txflock.l_type = F_RDLCK; - txflock.l_whence = 0; - txflock.l_start = 0L; - txflock.l_len = 0L; - - bp.txfp = fopen(tmp->local, "r"); - if (bp.txfp == NULL) { - if ((errno == ENOENT) || (errno == EINVAL)) { - Syslog('+', "Binkp: file %s doesn't exist, removing", MBSE_SS(tmp->local)); - tmp->state = Got; - } else { - WriteError("$Binkp: can't open %s, skipping", MBSE_SS(tmp->local)); - tmp->state = Skipped; - } - break; - } - - if (fcntl(fileno(bp.txfp), F_SETLK, &txflock) != 0) { - WriteError("$Binkp: can't lock file %s, skipping", MBSE_SS(tmp->local)); - fclose(bp.txfp); - bp.txfp = NULL; - tmp->state = Skipped; - break; - } - - bp.txpos = bp.stxpos = tmp->offset; - Syslog('+', "Binkp: send \"%s\" as \"%s\"", MBSE_SS(tmp->local), MBSE_SS(tmp->remote)); - Syslog('+', "Binkp: size %lu bytes, dated %s", (unsigned long)tmp->size, date(tmp->date)); - binkp_send_control(MM_FILE, "%s %lu %ld %ld", MBSE_SS(tmp->remote), - (unsigned long)tmp->size, (long)tmp->date, (unsigned long)tmp->offset); - gettimeofday(&bp.txtvstart, &bp.tz); - tmp->state = Sending; - cursend = tmp; - bp.TxState = TxTryRead; - transferred = TRUE; - break; - } /* if state == NoState */ - } /* for */ - - if (tmp == NULL) { - /* - * No more files to send - */ - bp.TxState = TxWaitLastAck; - Syslog('b', "Binkp: transmitter to WaitLastAck"); - } - break; - - case TxTryRead: - /* - * Check if there is room in the output buffer - */ - if ((WAITPUTGET(-1) & 2) != 0) - bp.TxState = TxReadSend; - break; - - case TxReadSend: - fseek(bp.txfp, bp.txpos, SEEK_SET); - bp.txlen = fread(bp.txbuf, 1, SND_BLKSIZE, bp.txfp); - - if (bp.txlen == 0) { - - if (ferror(bp.txfp)) { - WriteError("$Binkp: error reading from file"); - bp.TxState = TxGetNextFile; - cursend->state = Skipped; - debug_binkp_list(&bll); - break; - } - - /* - * calculate time needed and bytes transferred - */ - gettimeofday(&bp.txtvend, &bp.tz); - - /* - * Close transmitter file - */ - fclose(bp.txfp); - bp.txfp = NULL; - - if (bp.txpos >= 0) { - bp.stxpos = bp.txpos - bp.stxpos; - Syslog('+', "Binkp: OK %s", transfertime(bp.txtvstart, bp.txtvend, bp.stxpos, TRUE)); - } else { - Syslog('+', "Binkp: transmitter skipped file after %ld seconds", bp.txtvend.tv_sec - bp.txtvstart.tv_sec); - } - - cursend->state = IsSent; - bp.TxState = TxGetNextFile; - break; - } else { - bp.txpos += bp.txlen; - sentbytes += bp.txlen; - binkp_send_data(bp.txbuf, bp.txlen); - } - - bp.TxState = TxTryRead; - break; - - case TxWaitLastAck: - debug_binkp_list(&bll); - NotDone = FALSE; - for (tmp = bll; tmp; tmp = tmp->next) - if ((tmp->state != Got) && (tmp->state != Skipped)) { - NotDone = TRUE; - break; - } - if (tmp == NULL) { - bp.TxState = TxDone; - binkp_send_control(MM_EOB, ""); - Syslog('b', "Binkp: sending EOB"); - } - break; - - case TxDone: - break; - } - - /* - * Process received frame - */ - if (bp.GotFrame) { - if (bp.cmd) { - switch (bp.rxbuf[0]) { - case MM_ERR: Syslog('+', "Binkp: got ERR: %s", bp.rxbuf+1); - bp.RxState = RxDone; - bp.TxState = TxDone; - bp.rc = MBERR_FTRANSFER; - break; - - case MM_BSY: Syslog('+', "Binkp: got BSY: %s", bp.rxbuf+1); - bp.RxState = RxDone; - bp.TxState = TxDone; - bp.rc = MBERR_FTRANSFER; - break; - - case MM_SKIP: Syslog('+', "Binkp: got SKIP: %s", bp.rxbuf+1); - break; - - case MM_GET: Syslog('+', "Binkp: got GET: %s", bp.rxbuf+1); - sscanf(bp.rxbuf+1, "%s %ld %ld %ld", bp.gname, &bp.gsize, &bp.gtime, &bp.goffset); - for (tmpg = bll; tmpg; tmpg = tmpg->next) { - if (strcasecmp(tmpg->remote, bp.gname) == 0) { - tmpg->state = NoState; - tmpg->offset = bp.goffset; - if (bp.goffset) { - Syslog('+', "Remote wants %s for resync at offset %ld", bp.gname, bp.goffset); - } else { - Syslog('+', "Remote wants %s again", bp.gname); - } - bp.TxState = TxGetNextFile; - } - } - break; - - case MM_GOT: Syslog('+', "Binkp: got GOT: %s", bp.rxbuf+1); - sscanf(bp.rxbuf+1, "%s %ld %ld", bp.lname, &bp.lsize, &bp.ltime); - Found = FALSE; - for (tmp = bll; tmp; tmp = tmp->next) - if ((strcmp(bp.lname, tmp->remote) == 0) && (bp.lsize == tmp->size) && - (bp.ltime == tmp->date)) { - Syslog('+', "Binkp: remote GOT \"%s\"", tmp->remote); - tmp->state = Got; - Found = TRUE; - } - if (!Found) { - Syslog('!', "Binkp: unexpected GOT \"%s\"", bp.rxbuf+1); - } - break; - - case MM_NUL: b_nul(bp.rxbuf+1); - break; - - case MM_EOB: Syslog('+', "Binkp: got EOB"); - bp.RxState = RxEndOfBatch; - break; - - case MM_FILE: Syslog('b', "Binkp: got FILE: %s", bp.rxbuf+1); - if (bp.RxState == RxReceData ) { - /* - * If we get a m_file command during receive, the current file is - * considered interrupted. Save the partial received file and accept - * the new file. After this file the transmitter should continue - * with the original file and this mailer should send m_get to set - * the offset to the point were we left off. - */ - Syslog('+', "Binkp: new file during receive, saving %s", bp.rname); - closefile(); - bp.rxfp = NULL; - bp.RxState = RxWaitFile; - } - if ((bp.RxState == RxWaitFile) || (bp.RxState == RxEndOfBatch)) { - bp.RxState = RxAcceptFile; - /* - * Check against buffer overflow - */ - if (strlen(bp.rxbuf) < 512) { - sscanf(bp.rxbuf+1, "%s %ld %ld %ld", bp.rname, &bp.rsize, &bp.rtime, &bp.roffs); - } else { - Syslog('+', "Binkp: got corrupted FILE frame, size %d bytes", strlen(bp.rxbuf)); - } - } else { - Syslog('+', "Binkp: got unexpected FILE frame %s", bp.rxbuf+1); - } - break; - - default: Syslog('+', "Binkp: Unexpected frame %d \"%s\"", bp.rxbuf[0], printable(bp.rxbuf+1, 0)); - } - } else { - if (bp.blklen) { - if (bp.RxState == RxReceData) { - written = fwrite(bp.rxbuf, 1, bp.blklen, bp.rxfp); - if (!written && bp.blklen) { - Syslog('+', "Binkp: file write error"); - bp.RxState = RxDone; - } - bp.rxpos += written; - if (bp.rxpos == bp.rsize) { - bp.RxState = RxWaitFile; - binkp_send_control(MM_GOT, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); - closefile(); - bp.rxpos = bp.rxpos - bp.rxbytes; - gettimeofday(&bp.rxtvend, &bp.tz); - Syslog('+', "Binkp: OK %s", transfertime(bp.rxtvstart, bp.rxtvend, bp.rxpos, FALSE)); - rcvdbytes += bp.rxpos; - bp.RxState = RxWaitFile; - transferred = TRUE; - } - } else { - if (!bp.DidSendGET) { - /* - * Do not log after a GET command, there will be data packets - * in the pipeline that must be ignored. - */ - Syslog('+', "Binkp: unexpected DATA frame %d", bp.rxbuf[0]); - } - } - } - } - bp.GotFrame = FALSE; - bp.rxlen = 0; - bp.header = 0; - bp.blklen = 0; - } - - /* - * Receiver state machine - */ - switch (bp.RxState) { - case RxWaitFile: - break; - - case RxAcceptFile: - Syslog('+', "Binkp: receive file \"%s\" date %s size %ld offset %ld", bp.rname, date(bp.rtime), bp.rsize, bp.roffs); - (void)binkp2unix(bp.rname); - bp.rxfp = openfile(binkp2unix(bp.rname), bp.rtime, bp.rsize, &bp.rxbytes, resync); - - if (bp.DidSendGET) { - /* - * The file was partly received, via the openfile the resync function - * has send a GET command to start this file with a offset. This means - * we will get a new FILE command to open this file with a offset. - */ - bp.RxState = RxWaitFile; - break; - } - - gettimeofday(&bp.rxtvstart, &bp.tz); - bp.rxpos = bp.roffs; - - if (!diskfree(CFG.freespace)) { - Syslog('+', "Binkp: low diskspace, sending BSY"); - binkp_send_control(MM_BSY, "Low diskspace, try again later"); - bp.RxState = RxDone; - bp.TxState = TxDone; - bp.rc = MBERR_FTRANSFER; - break; - } - - if (statfs(tempinbound, &sfs) == 0) { - Syslog('b', "blocksize %lu free blocks %lu", sfs.f_bsize, sfs.f_bfree); - Syslog('b', "need %lu blocks", (unsigned long)(bp.rsize / (sfs.f_bsize + 1))); - if ((bp.rsize / (sfs.f_bsize + 1)) >= sfs.f_bfree) { - Syslog('!', "Only %lu blocks free (need %lu) in %s", sfs.f_bfree, - (unsigned long)(bp.rsize / (sfs.f_bsize + 1)), tempinbound); - closefile(); - bp.rxfp = NULL; /* Force SKIP command */ - } - } - - if (bp.rsize == bp.rxbytes) { - /* - * We already got this file, send GOT so it will - * be deleted at the remote. - */ - Syslog('+', "Binkp: already got %s, sending GOT", bp.rname); - binkp_send_control(MM_GOT, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); - bp.RxState = RxWaitFile; - bp.rxfp = NULL; - } else if (!bp.rxfp) { - /* - * Some error, request to skip it - */ - Syslog('+', "Binkp: error file %s, sending SKIP", bp.rname); - binkp_send_control(MM_SKIP, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); - bp.RxState = RxWaitFile; - } else { - Syslog('b', "rsize=%d, rxbytes=%d, roffs=%d", bp.rsize, bp.rxbytes, bp.roffs); - bp.RxState = RxReceData; - } - break; - - case RxReceData: - break; - - case RxEndOfBatch: - if (bp.TxState == TxDone) - bp.RxState = RxDone; - break; - - case RxDone: - break; - } - } - - debug_binkp_list(&bll); - - /* - * Process all send files. - */ - for (tsl = to_send; tsl; tsl = tsl->next) { - if (tsl->remote == NULL) { - execute_disposition(tsl); - } else { - for (tmp = bll; tmp; tmp = tmp->next) { - if ((strcmp(tmp->local, tsl->local) == 0) && (tmp->state == Got)) { - execute_disposition(tsl); - } - } - } - } - - for (tmp = bll; bll; bll = tmp) { - tmp = bll->next; - if (bll->local) - free(bll->local); - if (bll->remote) - free(bll->remote); - free(bll); - } - - /* - * If there was an error, try to close a possible incomplete file in - * the temp inbound so we can resume the next time we have a session - * with this node. - */ - if (bp.rc) - closefile(); - - Syslog('+', "Binkp: batch %d completed rc=%d", bp.batchnr, bp.rc); - return bp.rc; -} - -#endif diff --git a/mbcico/binkp.h b/mbcico/binkp.h index a8b1fc94..19db0a0b 100644 --- a/mbcico/binkp.h +++ b/mbcico/binkp.h @@ -1,7 +1,5 @@ -#ifndef USE_NEWBINKP - -#ifndef _BINKP_H -#define _BINKP_H +#ifndef _BINKPNEW_H +#define _BINKPNEW_H /* $Id$ */ @@ -23,11 +21,8 @@ /* protocol version */ #define PRTCLNAME "binkp" -#ifdef USE_EXPERIMENT -#define PRTCLVER "1.0" -#else -#define PRTCLVER "1.0" -#endif +#define PRTCLVER "1.1" +#define PRTCLOLD "1.0" #define MAX_BLKSIZE 0x7fff /* Don't change! */ #define BLK_HDR_SIZE 2 /* 2 bytes header */ @@ -64,8 +59,6 @@ typedef struct _binkp_frame { /* * Linked list of files to send and responses from the receiver. */ -typedef enum {NoState, Sending, IsSent, Got, Skipped, Get} FileState; - typedef struct _binkp_list { struct _binkp_list *next; char *remote; /* Remote filename */ @@ -78,8 +71,18 @@ typedef struct _binkp_list { } binkp_list; + +/* + * Linked FIFO list of received commands to be processed by the transmitter. + */ +typedef struct _the_queue { + struct _the_queue *next; + int cmd; /* M_xxx command id */ + char *data; /* Frame data in the queue */ +} the_queue; + + + int binkp(int); #endif - -#endif diff --git a/mbcico/binkpnew.c b/mbcico/binkpnew.c deleted file mode 100644 index 6c41227c..00000000 --- a/mbcico/binkpnew.c +++ /dev/null @@ -1,2151 +0,0 @@ -/***************************************************************************** - * - * $Id$ - * Purpose .................: Fidonet binkp protocol - * Binkp protocol copyright : Dima Maloff. - * - ***************************************************************************** - * Copyright (C) 1997-2004 - * - * Michiel Broek FIDO: 2:280/2802 - * Beekmansbos 10 - * 1971 BV IJmuiden - * the Netherlands - * - * This file is part of MBSE BBS. - * - * This BBS is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * MBSE BBS 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. - * - * You should have received a copy of the GNU General Public License - * along with MBSE BBS; see the file COPYING. If not, write to the Free - * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - *****************************************************************************/ - -#include "../config.h" - -#ifdef USE_NEWBINKP - - -#include "../lib/libs.h" -#include "../lib/structs.h" -#include "../lib/users.h" -#include "../lib/records.h" -#include "../lib/common.h" -#include "../lib/nodelist.h" -#include "../lib/dbnode.h" -#include "../lib/clcomm.h" -#include "../lib/mberrors.h" -#include "ttyio.h" -#include "session.h" -#include "statetbl.h" -#include "config.h" -#include "emsi.h" -#include "openfile.h" -#include "respfreq.h" -#include "filelist.h" -#include "opentcp.h" -#include "rdoptions.h" -#include "lutil.h" -#include "binkpnew.h" -#include "config.h" -#include "md5b.h" -#include "inbound.h" - - -/* - * Safe characters for binkp filenames, the rest will be escaped. - */ -#define BNKCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@&=+%$-_.!()#|" - - - -static char *bstate[] = { - (char *)"M_NUL", (char *)"M_ADR", (char *)"M_PWD", (char *)"M_FILE", (char *)"M_OK", - (char *)"M_EOB", (char *)"M_GOT", (char *)"M_ERR", (char *)"M_BSY", (char *)"M_GET", - (char *)"M_SKIP" -}; - - - -extern char *tempinbound; -extern char *ttystat[]; -extern int Loaded; -extern pid_t mypid; -extern struct sockaddr_in peeraddr; -extern int most_debug; - - -extern unsigned long sentbytes; -extern unsigned long rcvdbytes; - -typedef enum {RxWaitF, RxAccF, RxReceD, RxWriteD, RxEOB, RxDone} RxType; -typedef enum {TxGNF, TxTryR, TxReadS, TxWLA, TxDone} TxType; -typedef enum {Ok, Failure, Continue} TrType; -typedef enum {No, WeCan, WeWant, TheyWant, Active} OptionState; -typedef enum {NoState, Sending, IsSent, Got, Skipped, Get} FileState; -typedef enum {InitTransfer, Switch, Receive, Transmit, DeinitTransfer} FtType; - - -static char *rxstate[] = { (char *)"RxWaitF", (char *)"RxAccF", (char *)"RxReceD", - (char *)"RxWriteD", (char *)"RxEOB", (char *)"RxDone" }; -static char *txstate[] = { (char *)"TxGNF", (char *)"TxTryR", (char *)"TxReadS", - (char *)"TxWLA", (char *)"TxDone" }; -static char *trstate[] = { (char *)"Ok", (char *)"Failure", (char *)"Continue" }; -static char *opstate[] = { (char *)"No", (char *)"WeCan", (char *)"WeWant", (char *)"TheyWant", (char *)"Active" }; -static char *lbstate[] = { (char *)"None", (char *)"Sending", (char *)"IsSent", (char *)"Got", (char *)"Skipped", (char *)"Get"}; -static char *ftstate[] = { (char *)"InitTransfer", (char *)"Switch", (char *)"Receive", - (char *)"Transmit", (char *)"DeinitTransfer" }; - - -static time_t Timer; -int ext_rand = 0; - - -the_queue *tql = NULL; /* The Queue List */ -file_list *tosend = NULL; /* Files to send */ -file_list *respond = NULL; /* Requests honored */ -binkp_list *bll = NULL; /* Files to send with status */ -static binkp_list *cursend = NULL; /* Current file being transmitted */ -struct timeval txtvstart; /* Transmitter start time */ -struct timeval txtvend; /* Transmitter end time */ -struct timeval rxtvstart; /* Receiver start time */ -struct timeval rxtvend; /* Receiver end time */ - - -struct binkprec { - int Role; /* 1=orig, 0=answer */ - int RxState; /* Receiver state */ - int TxState; /* Transmitter state */ - int FtState; /* File transfer state */ - /* Option flags */ - int CRAMflag; /* CRAM authentication */ - /* Session state */ - int Secure; /* Secure session */ - int Major; /* Major protocol version */ - int Minor; /* Minor protocol version */ - unsigned char *MD_Challenge; /* Received challenge data */ - - /* Receiver buffer */ - char *rxbuf; /* Receiver buffer */ - int GotFrame; /* Frame complete flag */ - int rxlen; /* Frame length */ - int cmd; /* Frame command flag */ - int blklen; /* Frame blocklength */ - unsigned short header; /* Frame header */ - int rc; /* General return code */ - - long rsize; /* Receiver filesize */ - long roffs; /* Receiver offset */ - char *rname; /* Receiver filename */ - time_t rtime; /* Receiver filetime */ - FILE *rxfp; /* Receiver file */ - off_t rxbytes; /* Receiver bytecount */ - int rxpos; /* Receiver position */ - struct timezone tz; /* Timezone */ - int DidSendGET; /* Receiver send GET status */ - - char *txbuf; /* Transmitter buffer */ - int txlen; /* Transmitter file length */ - FILE *txfp; /* Transmitter file */ - int txpos; /* Transmitter position */ - int stxpos; /* Transmitter start position */ - - int local_EOB; /* Local EOB sent */ - int remote_EOB; /* Got EOB from remote */ - int messages; /* Messages sent + rcvd */ - unsigned long nethold; /* Netmail on hold */ - unsigned long mailhold; /* Packed mail on hold */ - - int batchnr; - int msgs_on_queue; /* Messages on the queue */ -}; - - -struct binkprec bp; /* Global structure */ - - -/* - * Prototypes - */ -TrType binkp_receiver(void); /* Receiver routine */ -TrType binkp_transmitter(void); /* Transmitter routine */ -int binkp_send_frame(int, char *, int); /* Send cmd/data frame */ -int binkp_send_command(int, ...); /* Send command frame */ -void binkp_settimer(int); /* Set timeout timer */ -int binkp_expired(void); /* Timer expired? */ -int binkp_banner(void); /* Send system banner */ -int binkp_recv_command(char *, int *, int *); /* Receive command frame */ -void parse_m_nul(char *); /* Parse M_NUL message */ -int binkp_poll_frame(void); /* Poll for a frame */ -void binkp_add_message(char *frame); /* Add cmd frame to queue */ -int binkp_process_messages(void); /* Process the queue */ -int binkp_resync(off_t); /* File resync */ -char *unix2binkp(char *); /* Binkp -> Unix escape */ -char *binkp2unix(char *); /* Unix -> Binkp escape */ -void fill_binkp_list(binkp_list **, file_list *, off_t); /* Build pending files */ -void debug_binkp_list(binkp_list **); /* Debug pending files list */ -int binkp_pendingfiles(void); /* Count pending files */ -void binkp_clear_filelist(void); /* Clear current filelist */ - -static int orgbinkp(void); /* Originate session state */ -static int ansbinkp(void); /* Answer session state */ -static int file_transfer(void); /* File transfer state */ - - - -/************************************************************************************/ -/* - * General entry point - */ - -int binkp(int role) -{ - int rc = 0; - - most_debug = TRUE; - - Syslog('+', "Binkp: start session"); - - memset(&bp, 0, sizeof(bp)); - bp.Role = role; - bp.CRAMflag = FALSE; - bp.Secure = FALSE; - bp.Major = 1; - bp.Minor = 0; - bp.MD_Challenge = NULL; - bp.rxbuf = calloc(MAX_BLKSIZE + 3, sizeof(unsigned char)); - bp.txbuf = calloc(MAX_BLKSIZE + 3, sizeof(unsigned char)); - bp.rname = calloc(512, sizeof(char)); - bp.rxfp = NULL; - bp.DidSendGET = FALSE; - bp.local_EOB = FALSE; - bp.remote_EOB = FALSE; - bp.msgs_on_queue = 0; - - if (role == 1) { - if (orgbinkp()) { - rc = MBERR_SESSION_ERROR; - } - } else { - if (ansbinkp()) { - rc = MBERR_SESSION_ERROR; - } - } - - if (rc) { - Syslog('!', "Binkp: session handshake failed"); - goto binkpend; - } - - if (Loaded && nodes.NoBinkp11 && (bp.Major == 1) && (bp.Minor != 0)) { - Syslog('+', "Binkp: forcing downgrade to binkp/1.0 protocol"); - bp.Major = 1; - bp.Minor = 0; - } - - if (localoptions & NOFREQS) - session_flags &= ~SESSION_WAZOO; - else - session_flags |= SESSION_WAZOO; - - bp.FtState = InitTransfer; - rc = file_transfer(); - -binkpend: - /* - * Deinit - */ - if (bp.rname) - free(bp.rname); - if (bp.MD_Challenge) - free(bp.MD_Challenge); - if (bp.rxbuf) - free(bp.rxbuf); - if (bp.txbuf) - free(bp.txbuf); - rc = abs(rc); - - Syslog('+', "Binkp: session finished, rc=%d", rc); - return rc; -} - - -/************************************************************************************/ -/* - * Originate Session Setup - */ - -SM_DECL(orgbinkp, (char *)"orgbinkp") -SM_STATES - ConnInit, - WaitConn, - SendPasswd, - WaitAddr, - AuthRemote, - IfSecure, - WaitOk, - Opts -SM_NAMES - (char *)"ConnInit", - (char *)"WaitConn", - (char *)"SendPasswd", - (char *)"WaitAddr", - (char *)"AuthRemote", - (char *)"IfSecure", - (char *)"WaitOk", - (char *)"Opts" -SM_EDECL - faddr *primary; - char *p, *q, *pwd; - int i, rc = 0, bufl, cmd, dupe, SendPass = FALSE; - fa_list **tmp, *tmpa; - faddr *fa, ra; - -SM_START(ConnInit) - -SM_STATE(ConnInit) - - SM_PROCEED(WaitConn) - -SM_STATE(WaitConn) - - Loaded = FALSE; - Syslog('+', "Binkp: node %s", ascfnode(remote->addr, 0x1f)); - IsDoing("Connect binkp %s", ascfnode(remote->addr, 0xf)); - - /* - * Build options we want (Add PLZ etc). - */ - p = xstrcpy((char *)"OPT"); - - if (strcmp(p, (char *)"OPT")) - rc = binkp_send_command(MM_NUL, p); - free(p); - if (rc) { - SM_ERROR; - } - - rc = binkp_banner(); - if (rc) { - SM_ERROR; - } - - /* - * Build a list of aka's to send, the primary aka first. - */ - ra.zone = remote->addr->zone; - ra.net = remote->addr->net; - ra.node = remote->addr->node; - ra.point = remote->addr->point; - - primary = bestaka_s(remote->addr); - p = xstrcpy(ascfnode(primary, 0x1f)); - - /* - * Add all other aka's exept primary aka. - */ - for (i = 0; i < 40; i++) { - if ((CFG.aka[i].zone) && (CFG.akavalid[i]) && - ((CFG.aka[i].zone != primary->zone) || (CFG.aka[i].net != primary->net) || - (CFG.aka[i].node != primary->node) || (CFG.aka[i].point!= primary->point))) { - p = xstrcat(p, (char *)" "); - p = xstrcat(p, aka2str(CFG.aka[i])); - } - } - - rc = binkp_send_command(MM_ADR, "%s", p); - free(p); - tidy_faddr(primary); - if (rc) { - SM_ERROR; - } - - SM_PROCEED(WaitAddr) - -SM_STATE(WaitAddr) - - for (;;) { - if ((rc = binkp_recv_command(bp.rxbuf, &bufl, &cmd))) { - Syslog('!', "Binkp: error receiving remote info"); - SM_ERROR; - } - if (cmd) { - if (bp.rxbuf[0] == MM_ADR) { - p = xstrcpy(bp.rxbuf + 1); - tidy_falist(&remote); - remote = NULL; - tmp = &remote; - for (q = strtok(p, " "); q; q = strtok(NULL, " ")) { - if ((fa = parsefnode(q))) { - dupe = FALSE; - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) && - (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) && - (strcmp(tmpa->addr->domain, fa->domain) == 0)) { - dupe = TRUE; - Syslog('b', "Binkp: double address %s", ascfnode(tmpa->addr, 0x1f)); - break; - } - } - if (!dupe) { - *tmp = (fa_list*)malloc(sizeof(fa_list)); - (*tmp)->next = NULL; - (*tmp)->addr = fa; - tmp = &((*tmp)->next); - } - } else { - Syslog('!', "Binkp: unparsable remote address: \"%s\"", printable(q, 0)); - binkp_send_command(MM_ERR, "Unparsable address \"%s\"", printable(q, 0)); - SM_ERROR; - } - } - free(p); - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - Syslog('+', "Address : %s", ascfnode(tmpa->addr, 0x1f)); - if (nodelock(tmpa->addr, mypid)) { - binkp_send_command(MM_BSY, "Address %s locked", ascfnode(tmpa->addr, 0x1f)); - SM_ERROR; - } - /* - * With the loaded flag we prevent removing the noderecord - * when the remote presents us an address we don't know about. - */ - if (!Loaded) { - if (noderecord(tmpa->addr)) - Loaded = TRUE; - } - } - - history.aka.zone = remote->addr->zone; - history.aka.net = remote->addr->net; - history.aka.node = remote->addr->node; - history.aka.point = remote->addr->point; - sprintf(history.aka.domain, "%s", remote->addr->domain); - - SM_PROCEED(SendPasswd) - } else if (bp.rxbuf[0] == MM_BSY) { - Syslog('!', "Binkp: M_BSY \"%s\"", printable(bp.rxbuf +1, 0)); - SM_ERROR; - } else if (bp.rxbuf[0] == MM_ERR) { - Syslog('!', "Binkp: M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); - SM_ERROR; - } else if (bp.rxbuf[0] == MM_NUL) { - parse_m_nul(bp.rxbuf +1); - } else { - binkp_send_command(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } else { - binkp_send_command(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } - -SM_STATE(SendPasswd) - - if (Loaded && strlen(nodes.Spasswd)) { - pwd = xstrcpy(nodes.Spasswd); - SendPass = TRUE; - } else { - pwd = xstrcpy((char *)"-"); - } - - if (bp.MD_Challenge) { - char *tp = NULL; - tp = MD_buildDigest(pwd, bp.MD_Challenge); - if (!tp) { - Syslog('!', "Unable to build MD5 digest"); - binkp_send_command(MM_ERR, "CRAM authentication failed, internal error"); - SM_ERROR; - } - bp.CRAMflag = TRUE; - rc = binkp_send_command(MM_PWD, "%s", tp); - free(tp); - } else { - rc = binkp_send_command(MM_PWD, "%s", pwd); - } - - free(pwd); - if (rc) { - SM_ERROR; - } - SM_PROCEED(AuthRemote) - -SM_STATE(AuthRemote) - - rc = 0; - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - if ((tmpa->addr->zone == ra.zone) && (tmpa->addr->net == ra.net) && - (tmpa->addr->node == ra.node) && (tmpa->addr->point == ra.point)) { - rc = 1; - } - } - - if (rc) { - SM_PROCEED(IfSecure) - } else { - Syslog('!', "Binkp: error, the wrong node is reached"); - binkp_send_command(MM_ERR, "No AKAs in common or all AKAs busy"); - SM_ERROR; - } - -SM_STATE(IfSecure) - - SM_PROCEED(WaitOk) - -SM_STATE(WaitOk) - - for (;;) { - if ((rc = binkp_recv_command(bp.rxbuf, &bufl, &cmd))) { - Syslog('!', "Binkp: error waiting for remote acknowledge"); - SM_ERROR; - } - - if (cmd) { - if (bp.rxbuf[0] == MM_OK) { - Syslog('b', "Binkp: M_OK \"%s\"", printable(bp.rxbuf +1, 0)); - if (SendPass) - bp.Secure = TRUE; - Syslog('+', "Binkp: %s%sprotected session", bp.CRAMflag ? "MD5 ":"", bp.Secure ? "":"un"); - SM_PROCEED(Opts) - - } else if (bp.rxbuf[0] == MM_BSY) { - Syslog('!', "Binkp: M_BSY \"%s\"", printable(bp.rxbuf +1, 0)); - SM_ERROR; - - } else if (bp.rxbuf[0] == MM_ERR) { - Syslog('!', "Binkp: M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); - SM_ERROR; - - } else if (bp.rxbuf[0] == MM_NUL) { - parse_m_nul(bp.rxbuf +1); - - } else { - binkp_send_command(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } else { - binkp_send_command(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } - -SM_STATE(Opts) - - /* - * Try to initiate the MB option if the remote is binkp/1.0 - */ -// if ((bp.MBflag == WeCan) && (bp.Major == 1) && (bp.Minor == 0)) { -// bp.MBflag = WeWant; -// Syslog('b', "MBflag WeCan => WeWant"); -// binkp_send_command(MM_NUL, "OPT MB"); -// } - SM_SUCCESS; - -SM_END -SM_RETURN - - - -/************************************************************************************/ -/* - * Answer Session Setup - */ - -SM_DECL(ansbinkp, (char *)"ansbinkp") -SM_STATES - WaitConn, - WaitAddr, - IsPasswd, - WaitPwd, - PwdAck, - Opts -SM_NAMES - (char *)"WaitConn", - (char *)"WaitAddr", - (char *)"IsPasswd", - (char *)"WaitPwd", - (char *)"PwdAck", - (char *)"Opts" -SM_EDECL - char *p, *q, *pw; - int i, rc, bufl, cmd, dupe, we_have_pwd = FALSE; - fa_list **tmp, *tmpa; - faddr *fa; - -SM_START(WaitConn) - -SM_STATE(WaitConn) - - Loaded = FALSE; - - if (strncmp(SockR("SBBS:0;"), "100:2,1", 7) == 0) { - Syslog('+', "Binkp: system is closed, sending M_BSY"); - binkp_send_command(MM_BSY, "This system is closed, try again later"); - SM_ERROR; - } - - if (!CFG.NoMD5 && ((bp.MD_Challenge = MD_getChallenge(NULL, &peeraddr)) != NULL)) { - /* - * Answering site MUST send CRAM message as very first M_NUL - */ - char s[MD5_DIGEST_LEN*2+15]; /* max. length of opt string */ - strcpy(s, "OPT "); - MD_toString(s+4, bp.MD_Challenge[0], bp.MD_Challenge+1); - bp.CRAMflag = TRUE; - if ((rc = binkp_send_command(MM_NUL, "%s", s))) { - SM_ERROR; - } - } - - if ((rc = binkp_banner())) { - SM_ERROR; - } - - p = xstrcpy((char *)""); - for (i = 0; i < 40; i++) { - if ((CFG.aka[i].zone) && (CFG.akavalid[i])) { - p = xstrcat(p, (char *)" "); - p = xstrcat(p, aka2str(CFG.aka[i])); - } - } - - rc = binkp_send_command(MM_ADR, "%s", p); - free(p); - if (rc) { - SM_ERROR; - } - - SM_PROCEED(WaitAddr) - -SM_STATE(WaitAddr) - - for (;;) { - if ((rc = binkp_recv_command(bp.rxbuf, &bufl, &cmd))) { - Syslog('!', "Binkp: error waiting for remote info"); - SM_ERROR; - } - - if (cmd) { - if (bp.rxbuf[0] == MM_ADR) { - p = xstrcpy(bp.rxbuf +1); - tidy_falist(&remote); - remote = NULL; - tmp = &remote; - - for (q = strtok(p, " "); q; q = strtok(NULL, " ")) { - if ((fa = parsefnode(q))) { - dupe = FALSE; - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) && - (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) && - (strcmp(tmpa->addr->domain, fa->domain) == 0)) { - dupe = TRUE; - Syslog('b', "Binkp: double address %s", ascfnode(tmpa->addr, 0x1f)); - break; - } - } - if (!dupe) { - *tmp = (fa_list*)malloc(sizeof(fa_list)); - (*tmp)->next = NULL; - (*tmp)->addr = fa; - tmp = &((*tmp)->next); - } - } else { - Syslog('!', "Binkp: unparsable remote address: \"%s\"", printable(q, 0)); - binkp_send_command(MM_ERR, "Unparsable address \"%s\"", printable(q, 0)); - SM_ERROR; - } - } - - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - Syslog('+', "Address : %s", ascfnode(tmpa->addr, 0x1f)); - if (nodelock(tmpa->addr, mypid)) { - binkp_send_command(MM_BSY, "Address %s locked", ascfnode(tmpa->addr, 0x1f)); - SM_ERROR; - } - /* - * With the loaded flag we prevent removing the noderecord - * when the remote presents us an address we don't know about. - */ - if (!Loaded) { - if (noderecord(tmpa->addr)) - Loaded = TRUE; - } - } - - for (tmpa = remote; tmpa; tmpa = tmpa->next) { - if (((nlent = getnlent(tmpa->addr))) && (nlent->pflag != NL_DUMMY)) { - Syslog('+', "Binkp: remote is a listed system"); - UserCity(mypid, nlent->sysop, nlent->location); - break; - } - } - if (nlent) - rdoptions(Loaded); - - //if (bp.MBflag == TheyWant) { - // Syslog('b', "Binkp: remote supports MB"); - // binkp_send_control(MM_NUL,"OPT MB"); - // bp.MBflag = Active; - //} - history.aka.zone = remote->addr->zone; - history.aka.net = remote->addr->net; - history.aka.node = remote->addr->node; - history.aka.point = remote->addr->point; - sprintf(history.aka.domain, "%s", remote->addr->domain); - - SM_PROCEED(IsPasswd) - - } else if (bp.rxbuf[0] == MM_ERR) { - Syslog('!', "Binkp: M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); - SM_ERROR; - - } else if (bp.rxbuf[0] == MM_NUL) { - parse_m_nul(bp.rxbuf +1); - - } else if (bp.rxbuf[0] <= MM_MAX) { - binkp_send_command(MM_ERR, "Unexpected frame"); - SM_ERROR; - - } - } else { - binkp_send_command(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } - -SM_STATE(IsPasswd) - - if (Loaded && strlen(nodes.Spasswd)) { - we_have_pwd = TRUE; - } - - Syslog('b', "We %s have a password", we_have_pwd ?"do":"don't"); - SM_PROCEED(WaitPwd) - -SM_STATE(WaitPwd) - - for (;;) { - if ((rc = binkp_recv_command(bp.rxbuf, &bufl, &cmd))) { - Syslog('!', "Binkp: error waiting for password"); - SM_ERROR; - } - - if (cmd) { - if (bp.rxbuf[0] == MM_PWD) { - SM_PROCEED(PwdAck) - } else if (bp.rxbuf[0] == MM_ERR) { - Syslog('!', "Binkp: M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); - SM_ERROR; - } else if (bp.rxbuf[0] == MM_NUL) { - parse_m_nul(bp.rxbuf +1); - } else if (bp.rxbuf[0] <= MM_MAX) { - binkp_send_command(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } else { - binkp_send_command(MM_ERR, "Unexpected frame"); - SM_ERROR; - } - } - -SM_STATE(PwdAck) - - if (we_have_pwd) { - pw = xstrcpy(nodes.Spasswd); - } else { - pw = xstrcpy((char *)"-"); - } - - if ((strncmp(bp.rxbuf +1, "CRAM-", 5) == 0) && bp.CRAMflag) { - char *sp; - sp = MD_buildDigest(pw, bp.MD_Challenge); - if (sp != NULL) { - if (strcmp(bp.rxbuf +1, sp)) { - Syslog('+', "Binkp: bad MD5 crypted password"); - binkp_send_command(MM_ERR, "Bad password"); - free(sp); - sp = NULL; - free(pw); - SM_ERROR; - } else { - free(sp); - sp = NULL; - if (we_have_pwd) - bp.Secure = TRUE; - } - } else { - free(pw); - Syslog('!', "Binkp: could not build MD5 digest"); - binkp_send_command(MM_ERR, "*** Internal error ***"); - SM_ERROR; - } - } else if ((strcmp(bp.rxbuf +1, pw) == 0)) { - if (we_have_pwd) - bp.Secure = TRUE; - } else { - free(pw); - Syslog('?', "Binkp: password error: expected \"%s\", got \"%s\"", nodes.Spasswd, bp.rxbuf +1); - binkp_send_command(MM_ERR, "Bad password"); - SM_ERROR; - } - free(pw); - Syslog('+', "Binkp: %s%sprotected session", bp.CRAMflag ? "MD5 ":"", bp.Secure ? "":"un"); - inbound_open(remote->addr, bp.Secure); - binkp_send_command(MM_OK, "%ssecure", bp.Secure ? "":"non-"); - SM_PROCEED(Opts) - -SM_STATE(Opts) - - SM_SUCCESS; - -SM_END -SM_RETURN - - - -/************************************************************************************/ -/* - * File Transfer State - */ - -/* - * We do not use the normal state machine because that produces a lot - * of debug logging that will drive up the CPU usage. - * FIXME: Remove these messages!! - */ -int file_transfer(void) -{ - int rc = 0; - TrType Trc = Ok; - - for (;;) { - Syslog('B', "Binkp: FileTransfer state %s", ftstate[bp.FtState]); - switch (bp.FtState) { - case InitTransfer: binkp_settimer(BINKP_TIMEOUT); - bp.RxState = RxWaitF; - bp.TxState = TxGNF; - bp.FtState = Switch; - bp.messages = 0; - break; - - case Switch: if ((bp.RxState == RxDone) && (bp.TxState == TxDone)) { - bp.FtState = DeinitTransfer; - break; - } - - rc = binkp_poll_frame(); - if (rc == -1) { - Syslog('b', "Binkp: receiver error detected"); - bp.rc = rc = MBERR_FTRANSFER; - bp.FtState = DeinitTransfer; - break; - } else if (rc == 1) { - bp.FtState = Receive; - break; - } - - /* - * Check if there is room in the output buffer - */ - if (WAITPUTGET(-1) & 2) { - bp.FtState = Transmit; - break; - } - - if (binkp_expired()) { - Syslog('+', "Binkp: transfer timeout"); - bp.rc = 1; - bp.FtState = DeinitTransfer; - break; - } - - /* - * Nothing done, release - */ - Syslog('b', "Binkp: NOTHING DONE"); - usleep(1); - break; - - case Receive: Trc = binkp_receiver(); - if (Trc == Ok) { - binkp_settimer(BINKP_TIMEOUT); - if (bp.local_EOB && bp.remote_EOB) - bp.FtState = Transmit; - else - bp.FtState = Switch; - } else if (Trc == Failure) { - Syslog('+', "Binkp: receiver failure"); - bp.rc = 1; - bp.FtState = DeinitTransfer; - } - break; - - case Transmit: Trc = binkp_transmitter(); - if (Trc == Ok) { - binkp_settimer(BINKP_TIMEOUT); - bp.FtState = Switch; - } else if (Trc == Failure) { - Syslog('+', "Binkp: transmitter failure"); - bp.rc = 1; - bp.FtState = DeinitTransfer; - } - break; - - case DeinitTransfer:/* - * In case of a transfer error the filelist is not yet cleared - */ - binkp_clear_filelist(); - if (bp.rc) - return MBERR_FTRANSFER; - else - return 0; - break; - } - } -} - - - -/************************************************************************************/ -/* - * Receiver routine - */ - -TrType binkp_receiver(void) -{ - int bcmd, rc = 0; - struct statfs sfs; - long written; - off_t rxbytes; - - Syslog('B', "Binkp: receiver state %s", rxstate[bp.RxState]); - - if (bp.RxState == RxWaitF) { - - if (! bp.GotFrame) - return Ok; - - bp.GotFrame = FALSE; - bp.rxlen = 0; - bp.header = 0; - - if (! bp.cmd) { - Syslog('b', "Binkp: got %d bytes DATA frame in %s state, ignored", bp.blklen, rxstate[bp.RxState]); - bp.blklen = 0; - return Ok; - } - bp.blklen = 0; - - bcmd = bp.rxbuf[0]; - if (bcmd == MM_ERR) { - Syslog('+', "Binkp: got M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); - bp.RxState = RxDone; - return Failure; - } else if ((bcmd == MM_GET) || (bcmd == MM_GOT) || (bcmd == MM_SKIP)) { - binkp_add_message(bp.rxbuf); - return Ok; - } else if (bcmd == MM_NUL) { - parse_m_nul(bp.rxbuf +1); - return Ok; - } else if (bcmd == MM_EOB) { - if ((bp.Major == 1) && (bp.Minor != 0)) { -// Syslog('B', "Binkp: 1.1 check local_EOB=%s remote_EOB=%s messages=%d", -// bp.local_EOB?"True":"False", bp.remote_EOB?"True":"False", bp.messages); - if (bp.local_EOB && bp.remote_EOB) { - Syslog('b', "Binkp: receiver detects both sides in EOB state"); - if ((bp.messages < 3) || binkp_pendingfiles()) { - Syslog('b', "Binkp: receiver detected end of session or pendingfiles, stay in RxWaitF"); - return Ok; - } else { - bp.batchnr++; - bp.local_EOB = FALSE; - bp.remote_EOB = FALSE; - bp.messages = 0; - bp.TxState = TxGNF; - bp.RxState = RxWaitF; - Syslog('+', "Binkp: receiver starts batch %d", bp.batchnr + 1); - binkp_clear_filelist(); - return Ok; - } - } else { - return Ok; - } - } else { - Syslog('b', "Binkp/1.0 mode and got M_EOB, goto RxEOB"); - bp.RxState = RxEOB; - return Ok; - } - } else if (bcmd == MM_FILE) { - bp.RxState = RxAccF; - return Continue; - } else if (bcmd <= MM_MAX) { - Syslog('+', "Binkp: ERROR got unexpected %s frame in %s state", bstate[bcmd], rxstate[bp.RxState]); - bp.RxState = RxDone; - return Failure; - } else { - Syslog('+', "Binkp: got unexpected unknown frame, ignore"); - return Ok; - } - } else if (bp.RxState == RxAccF) { - if (strlen(bp.rxbuf) < 512) { - sprintf(bp.rname, "%s", strtok(bp.rxbuf+1, " \n\r")); - bp.rsize = atoi(strtok(NULL, " \n\r")); - bp.rtime = atoi(strtok(NULL, " \n\r")); - bp.roffs = atoi(strtok(NULL, " \n\r")); - } else { - /* - * Corrupted command, in case this was serious, send the M_GOT back so it's - * deleted at the remote. - */ - Syslog('+', "Binkp: got corrupted FILE frame, size %d bytes", strlen(bp.rxbuf)); - bp.RxState = RxWaitF; - rc = binkp_send_command(MM_GOT, bp.rxbuf +1); - if (rc) - return Failure; - else - return Ok; - } - Syslog('+', "Binkp: receive file \"%s\" date %s size %ld offset %ld", bp.rname, date(bp.rtime), bp.rsize, bp.roffs); - (void)binkp2unix(bp.rname); - rxbytes = bp.rxbytes; - bp.rxfp = openfile(binkp2unix(bp.rname), bp.rtime, bp.rsize, &rxbytes, binkp_resync); - bp.rxbytes = rxbytes; - - if (bp.DidSendGET) { - Syslog('b', "Binkp: DidSendGET is set"); - /* - * FIXME: this was from the old driver, still needed??? - * - * The file was partly received, via the openfile the resync function - * has send a GET command to start this file with a offset. This means - * we will get a new FILE command to open this file with a offset. - */ - bp.RxState = RxReceD; - return Ok; - } - - gettimeofday(&rxtvstart, &bp.tz); - bp.rxpos = bp.roffs; - - if (!diskfree(CFG.freespace)) { - Syslog('+', "Binkp: low diskspace, sending BSY"); - binkp_send_command(MM_BSY, "Low diskspace, try again later"); - bp.RxState = RxDone; - bp.TxState = TxDone; - bp.rc = MBERR_FTRANSFER; - return Failure; - } - - if (statfs(tempinbound, &sfs) == 0) { - if ((bp.rsize / (sfs.f_bsize + 1)) >= sfs.f_bfree) { - Syslog('!', "Binkp: only %lu blocks free (need %lu) in %s for this file", sfs.f_bfree, - (unsigned long)(bp.rsize / (sfs.f_bsize + 1)), tempinbound); - closefile(); - bp.rxfp = NULL; /* Force SKIP command */ - } - } - - if (bp.rsize == bp.rxbytes) { - /* - * We already got this file, send GOT so it will - * be deleted at the remote. - */ - Syslog('+', "Binkp: already got %s, sending GOT", bp.rname); - rc = binkp_send_command(MM_GOT, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); - bp.RxState = RxWaitF; - bp.rxfp = NULL; - if (rc) - return Failure; - else - return Ok; - } else if (!bp.rxfp) { - /* - * Some error, request to skip it - */ - Syslog('+', "Binkp: error file %s, sending SKIP", bp.rname); - rc = binkp_send_command(MM_SKIP, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); - bp.RxState = RxWaitF; - if (rc) - return Failure; - else - return Ok; - } else { - Syslog('b', "rsize=%d, rxbytes=%d, roffs=%d", bp.rsize, bp.rxbytes, bp.roffs); - bp.RxState = RxReceD; - return Ok; - } - - } else if (bp.RxState == RxReceD) { - - if (! bp.GotFrame) - return Ok; - - if (! bp.cmd) { - bp.RxState = RxWriteD; - return Continue; - } - - bp.GotFrame = FALSE; - bp.rxlen = 0; - bp.header = 0; - bp.blklen = 0; - - bcmd = bp.rxbuf[0]; - if (bcmd == MM_ERR) { - Syslog('+', "Binkp: got M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); - bp.RxState = RxDone; - return Failure; - } else if ((bcmd == MM_GET) || (bcmd == MM_GOT) || (bcmd == MM_SKIP)) { - binkp_add_message(bp.rxbuf); - return Ok; - } else if (bcmd == MM_NUL) { - parse_m_nul(bp.rxbuf +1); - return Ok; - } else if (bcmd == MM_FILE) { - Syslog('+', "Binkp: partial received file, saving"); - closefile(); - bp.rxfp = NULL; - bp.RxState = RxAccF; - return Continue; - } else if (bcmd <= MM_MAX) { - Syslog('+', "Binkp: ERROR got unexpected %s frame in %s state", bstate[bcmd], rxstate[bp.RxState]); - bp.RxState = RxDone; - return Failure; - } else { - Syslog('+', "Binkp: got unexpected unknown frame, ignore"); - return Ok; - } - } else if (bp.RxState == RxWriteD) { - written = fwrite(bp.rxbuf, 1, bp.blklen, bp.rxfp); - - bp.GotFrame = FALSE; - bp.rxlen = 0; - bp.header = 0; - - if (bp.blklen && (bp.blklen != written)) { - Syslog('+', "Binkp: ERROR file write error"); - bp.RxState = RxDone; - bp.blklen = 0; - return Failure; - } - bp.blklen = 0; - bp.rxpos += written; - - if (bp.rxpos == bp.rsize) { - rc = binkp_send_command(MM_GOT, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); - closefile(); - bp.rxpos = bp.rxpos - bp.rxbytes; - gettimeofday(&rxtvend, &bp.tz); - Syslog('+', "Binkp: OK %s", transfertime(rxtvstart, rxtvend, bp.rxpos, FALSE)); - rcvdbytes += bp.rxpos; - bp.RxState = RxWaitF; - if (rc) - return Failure; - else - return Ok; - } - bp.RxState = RxReceD; - return Ok; - } else if (bp.RxState == RxEOB) { - - if (! bp.GotFrame) - return Ok; - - bp.GotFrame = FALSE; - bp.rxlen = 0; - bp.header = 0; - bp.blklen = 0; - - if (bp.cmd) { - bcmd = bp.rxbuf[0]; - if (bcmd == MM_ERR) { - Syslog('+', "Binkp: got M_ERR \"%s\"", printable(bp.rxbuf +1, 0)); - bp.RxState = RxDone; - return Failure; - } else if ((bcmd == MM_GET) || (bcmd == MM_GOT) || (bcmd == MM_SKIP)) { - binkp_add_message(bp.rxbuf); - return Ok; - } else if (bcmd == MM_NUL) { - parse_m_nul(bp.rxbuf +1); - return Ok; - } else if (bcmd <= MM_MAX) { - Syslog('+', "Binkp: ERROR got unexpected %s frame in %s state", bstate[bcmd], rxstate[bp.RxState]); - bp.RxState = RxDone; - return Failure; - } else { - Syslog('+', "Binkp: got unexpected unknown frame, ignore"); - return Ok; - } - } else { - Syslog('+', "Binkp: ERROR got unexpected data frame in %s state", rxstate[bp.RxState]); - bp.RxState = RxDone; - return Failure; - } - - } else if (bp.RxState == RxDone) { - return Ok; - } - - /* - * Cannot be here - */ - bp.RxState = RxDone; - return Failure; -} - - -/************************************************************************************/ -/* - * Transmitter routine - */ - -TrType binkp_transmitter(void) -{ - int rc = 0, eof = FALSE; - char *nonhold_mail; - fa_list *eff_remote; - file_list *tsl; - static binkp_list *tmp; - - Syslog('B', "Binkp: transmitter state %s", txstate[bp.TxState]); - - if (bp.TxState == TxGNF) { - /* - * If we do not have a filelist yet, create one. - */ - if (tosend == NULL) { - Syslog('b', "Creating filelist"); - nonhold_mail = (char *)ALL_MAIL; - bp.nethold = bp.mailhold = 0L; - cursend = NULL; - - /* - * If remote doesn't have the 8.3 flag set, allow long names - */ - if (!nodes.FNC) - remote_flags &= ~SESSION_FNC; - - eff_remote = remote; - tosend = create_filelist(eff_remote, nonhold_mail, 0); - if ((respond = respond_wazoo()) != NULL) { - for (tsl = tosend; tsl->next; tsl = tsl ->next); - tsl->next = respond; - Syslog('b', "Binkp: added requested files"); - } - - /* - * Build a new filelist from the existing filelist. - * This one is special for binkp behaviour. - */ - for (tsl = tosend; tsl; tsl = tsl->next) { - if (tsl->remote != NULL) - fill_binkp_list(&bll, tsl, 0L); - } - debug_binkp_list(&bll); - - if ((bp.nethold || bp.mailhold) || (bp.batchnr == 0)) { - Syslog('+', "Binkp: mail %ld, files %ld bytes", bp.nethold, bp.mailhold); - if ((rc = binkp_send_command(MM_NUL, "TRF %ld %ld", bp.nethold, bp.mailhold))) { - bp.TxState = TxDone; - return Failure; - } - } - } - - for (tmp = bll; tmp; tmp = tmp->next) { - if (tmp->state == NoState) { - /* - * There is something to send - */ - struct flock txflock; - txflock.l_type = F_RDLCK; - txflock.l_whence = 0; - txflock.l_start = 0L; - txflock.l_len = 0L; - - bp.txfp = fopen(tmp->local, "r"); - if (bp.txfp == NULL) { - if ((errno == ENOENT) || (errno == EINVAL)) { - Syslog('+', "Binkp: file %s doesn't exist, removing", MBSE_SS(tmp->local)); - tmp->state = Got; - } else { - WriteError("$Binkp: can't open %s, skipping", MBSE_SS(tmp->local)); - tmp->state = Skipped; - } - break; - } - - if (fcntl(fileno(bp.txfp), F_SETLK, &txflock) != 0) { - WriteError("$Binkp: can't lock file %s, skipping", MBSE_SS(tmp->local)); - fclose(bp.txfp); - bp.txfp = NULL; - tmp->state = Skipped; - break; - } - - bp.txpos = bp.stxpos = tmp->offset; - Syslog('+', "Binkp: send \"%s\" as \"%s\"", MBSE_SS(tmp->local), MBSE_SS(tmp->remote)); - Syslog('+', "Binkp: size %lu bytes, dated %s", (unsigned long)tmp->size, date(tmp->date)); - rc = binkp_send_command(MM_FILE, "%s %lu %ld %ld", MBSE_SS(tmp->remote), - (unsigned long)tmp->size, (long)tmp->date, (unsigned long)tmp->offset); - if (rc) { - bp.TxState = TxDone; - return Failure; - } - gettimeofday(&txtvstart, &bp.tz); - tmp->state = Sending; - cursend = tmp; - bp.TxState = TxTryR; - return Continue; - } /* if state == NoState */ - } /* for */ - - if (tmp == NULL) { - /* - * No more files - */ - rc = binkp_send_command(MM_EOB, ""); - bp.TxState = TxWLA; - if (rc) - return Failure; - else - return Continue; - } - return Ok; - - } else if (bp.TxState == TxTryR) { - - if (bp.msgs_on_queue == 0) { - bp.TxState = TxReadS; - return Continue; - } else { - if (binkp_process_messages()) { - bp.TxState = TxDone; - return Failure; - } else - return Continue; - } - - } else if (bp.TxState == TxReadS) { - fseek(bp.txfp, bp.txpos, SEEK_SET); - bp.txlen = fread(bp.txbuf, 1, SND_BLKSIZE, bp.txfp); - eof = feof(bp.txfp); - - if ((bp.txlen == 0) || eof) { - /* - * Nothing read/error or we are at eof - */ - if (ferror(bp.txfp)) { - WriteError("$Binkp: error reading from file"); - bp.TxState = TxDone; - cursend->state = Skipped; - debug_binkp_list(&bll); - return Failure; - } - - /* - * We are at eof, send last datablock - */ - if (eof && bp.txlen) { - bp.txpos += bp.txlen; - sentbytes += bp.txlen; - rc = binkp_send_frame(FALSE, bp.txbuf, bp.txlen); - if (rc) - return Failure; - } - - /* - * calculate time needed and bytes transferred - */ - gettimeofday(&txtvend, &bp.tz); - - /* - * Close transmitter file - */ - fclose(bp.txfp); - bp.txfp = NULL; - - if (bp.txpos >= 0) { - bp.stxpos = bp.txpos - bp.stxpos; - Syslog('+', "Binkp: OK %s", transfertime(txtvstart, txtvend, bp.stxpos, TRUE)); - } else { - Syslog('+', "Binkp: transmitter skipped file after %ld seconds", txtvend.tv_sec - txtvstart.tv_sec); - } - - cursend->state = IsSent; - cursend = NULL; - bp.TxState = TxGNF; - return Ok; - } else { - /* - * regular datablock - */ - bp.txpos += bp.txlen; - sentbytes += bp.txlen; - rc = binkp_send_frame(FALSE, bp.txbuf, bp.txlen); - bp.TxState = TxTryR; - if (rc) - return Failure; - else - return Ok; - } - - } else if (bp.TxState == TxWLA) { - - if ((bp.msgs_on_queue == 0) && (binkp_pendingfiles() == 0)) { - - if ((bp.RxState >= RxEOB) && (bp.Major == 1) && (bp.Minor == 0)) { - bp.TxState = TxDone; - if (bp.local_EOB && bp.remote_EOB) { - Syslog('b', "Binkp: binkp/1.0 session seems complete"); - bp.RxState = RxDone; - } - - binkp_clear_filelist(); - return Ok; - } - - if ((bp.RxState < RxEOB) && (bp.Major == 1) && (bp.Minor == 0)) { - bp.TxState = TxWLA; - return Ok; - } - - if ((bp.Major == 1) && (bp.Minor != 0)) { -// Syslog('B', "Binkp: 1.1 check local_EOB=%s remote_EOB=%s messages=%d", -// bp.local_EOB?"True":"False", bp.remote_EOB?"True":"False", bp.messages); - - if (bp.local_EOB && bp.remote_EOB) { - /* - * We did send EOB and got a EOB - */ - if (bp.messages < 3) { - /* - * Nothing sent anymore, finish - */ - Syslog('b', "Binkp: binkp/1.1 session seems complete"); - bp.RxState = RxDone; - bp.TxState = TxDone; - } else { - /* - * Start new batch - */ - bp.batchnr++; - bp.local_EOB = FALSE; - bp.remote_EOB = FALSE; - bp.messages = 0; - bp.TxState = TxGNF; - bp.RxState = RxWaitF; - Syslog('+', "Binkp: transmitter starts batch %d", bp.batchnr + 1); - binkp_clear_filelist(); - return Ok; /* Continue is not good here, troubles with binkd on slow links. */ - } - } - - binkp_clear_filelist(); - return Ok; - } - } - - if (bp.msgs_on_queue) { - if (binkp_process_messages()) { - return Failure; - } else { - return Continue; - } - } - return Ok; - - } else if (bp.TxState == TxDone) { - return Ok; - } - - /* - * Cannot be here - */ - bp.TxState = TxDone; - return Failure; -} - - -/************************************************************************************/ -/* - * Functions - */ - - -/* - * Send a binkp frame - */ -int binkp_send_frame(int cmd, char *buf, int len) -{ - unsigned short header = 0; - int rc, id; - - if (cmd) { - header = ((BINKP_CONTROL_BLOCK + len) & 0xffff); - bp.messages++; - id = buf[0]; - if (id == MM_EOB) { - bp.local_EOB = TRUE; - } - if (len == 1) - Syslog('b', "Binkp: send %s", bstate[id]); - else - Syslog('b', "Binkp: send %s %s", bstate[id], printable(buf +1, len -1)); - } else { - header = ((BINKP_DATA_BLOCK + len) & 0xffff); - Syslog('b', "Binkp: send data (%d)", len); - } - - rc = PUTCHAR((header >> 8) & 0x00ff); - if (!rc) - rc = PUTCHAR(header & 0x00ff); - if (len && !rc) - rc = PUT(buf, len); - FLUSHOUT(); - binkp_settimer(BINKP_TIMEOUT); - return rc; -} - - - -/* - * Send a command message - */ -int binkp_send_command(int id, ...) -{ - va_list args; - char *fmt; - static char buf[1024]; - int sz, rc; - - va_start(args, id); - fmt = va_arg(args, char*); - - if (fmt) { - vsprintf(buf, fmt, args); - sz = (strlen(buf) & 0x7fff); - } else { - buf[0]='\0'; - sz = 0; - } - - memmove(buf+1, buf, sz); - buf[0] = id & 0xff; - sz++; - - rc = binkp_send_frame(TRUE, buf, sz); - va_end(args); - return rc; -} - - - -/* - * Set binkp master timer - */ -void binkp_settimer(int interval) -{ - Timer = time((time_t*)NULL) + interval; -} - - - -/* - * Test if master timer is expired - */ -int binkp_expired(void) -{ - time_t now; - - now = time(NULL); - if (now >= Timer) - Syslog('+', "Binkp: timeout"); - return (now >= Timer); -} - - - -/* - * Send system info to remote - */ -int binkp_banner(void) -{ - time_t t; - int rc; - - rc = binkp_send_command(MM_NUL,"SYS %s", CFG.bbs_name); - if (!rc) - rc = binkp_send_command(MM_NUL,"ZYZ %s", CFG.sysop_name); - if (!rc) - rc = binkp_send_command(MM_NUL,"LOC %s", CFG.location); - if (!rc) - rc = binkp_send_command(MM_NUL,"NDL %s", CFG.Flags); - t = time(NULL); - if (!rc) - rc = binkp_send_command(MM_NUL,"TIME %s", rfcdate(t)); - if (!rc) { - if (nodes.NoBinkp11) - rc = binkp_send_command(MM_NUL,"VER mbcico/%s/%s-%s %s/%s", VERSION, OsName(), OsCPU(), PRTCLNAME, PRTCLOLD); - else - rc = binkp_send_command(MM_NUL,"VER mbcico/%s/%s-%s %s/%s", VERSION, OsName(), OsCPU(), PRTCLNAME, PRTCLVER); - } - if (strlen(CFG.Phone) && !rc) - rc = binkp_send_command(MM_NUL,"PHN %s", CFG.Phone); - if (strlen(CFG.comment) && !rc) - rc = binkp_send_command(MM_NUL,"OPM %s", CFG.comment); - - return rc; -} - - - -/* - * Receive command frame - */ -int binkp_recv_command(char *buf, int *len, int *cmd) -{ - int b0, b1; - - *len = *cmd = 0; - - b0 = GETCHAR(BINKP_TIMEOUT); - if (tty_status) - goto to; - if (b0 & 0x80) - *cmd = 1; - - b1 = GETCHAR(1); - if (tty_status) - goto to; - - *len = (b0 & 0x7f) << 8; - *len += b1; - - GET(buf, *len, BINKP_TIMEOUT / 2); - buf[*len] = '\0'; - if (tty_status) - goto to; - -to: - if (tty_status) - WriteError("Binkp: TCP rcv error, tty status: %s", ttystat[tty_status]); - return tty_status; -} - - - -/* - * Parse a received M_NUL message - */ -void parse_m_nul(char *msg) -{ - char *p, *q; - - if (strncmp(msg, "SYS ", 4) == 0) { - Syslog('+', "System : %s", msg+4); - strncpy(history.system_name, msg+4, 35); - - } else if (strncmp(msg, "ZYZ ", 4) == 0) { - Syslog('+', "Sysop : %s", msg+4); - strncpy(history.sysop, msg+4, 35); - - } else if (strncmp(msg, "LOC ", 4) == 0) { - Syslog('+', "Location: %s", msg+4); - strncpy(history.location, msg+4, 35); - - } else if (strncmp(msg, "NDL ", 4) == 0) { - Syslog('+', "Flags : %s", msg+4); - - } else if (strncmp(msg, "TIME ", 5) == 0) { - Syslog('+', "Time : %s", msg+5); - - } else if (strncmp(msg, "VER ", 4) == 0) { - Syslog('+', "Uses : %s", msg+4); - if ((p = strstr(msg+4, PRTCLNAME "/")) && (q = strstr(p, "."))) { - bp.Major = atoi(p + 6); - bp.Minor = atoi(q + 1); - } - } else if (strncmp(msg, "PHN ", 4) == 0) { - Syslog('+', "Phone : %s", msg+4); - - } else if (strncmp(msg, "OPM ", 4) == 0) { - Syslog('+', "Remark : %s", msg+4); - - } else if (strncmp(msg, "TRF ", 4) == 0) { - Syslog('+', "Binkp: remote has %s mail/files for us", msg+4); - - } else if (strncmp(msg, "OPT ", 4) == 0) { - Syslog('+', "Options : %s", msg+4); - if (strstr(msg, (char *)"CRAM-MD5-") != NULL) { /* No SHA-1 support */ - if (CFG.NoMD5) { - Syslog('+', "Binkp: Remote supports MD5, but it's turned off here"); - } else { - if (bp.MD_Challenge) - free(bp.MD_Challenge); - bp.MD_Challenge = MD_getChallenge(msg, NULL); - } - } - } else { - Syslog('+', "Binkp: M_NUL \"%s\"", msg); - } -} - - - -/* - * Poll for a frame, returns: - * -1 = Error - * 0 = Nothing yet - * 1 = Got a frame - * 2 = Frame not processed - */ -int binkp_poll_frame(void) -{ - int c, rc = 0, bcmd; - - for (;;) { - if (bp.GotFrame) { - Syslog('b', "Binkp: WARNING: frame not processed"); - rc = 2; - break; - } else { - c = GETCHAR(0); - if (c < 0) { - c = -c; - if (c == STAT_TIMEOUT) { - usleep(1); - rc = 0; - break; - } - Syslog('?', "Binkp: receiver status %s", ttystat[c]); - bp.rc = (MBERR_TTYIO + (-c)); - rc = -1; - break; - } else { - switch (bp.rxlen) { - case 0: bp.header = c << 8; - rc = 0; - break; - case 1: bp.header += c; - rc = 0; - break; - default:bp.rxbuf[bp.rxlen-2] = c; - } - if (bp.rxlen == 1) { - bp.cmd = bp.header & 0x8000; - bp.blklen = bp.header & 0x7fff; - } - if ((bp.rxlen == (bp.blklen + 1) && (bp.rxlen >= 1))) { - bp.GotFrame = TRUE; - bp.rxbuf[bp.rxlen-1] = '\0'; - if (bp.cmd) { - bp.messages++; - bcmd = bp.rxbuf[0]; - Syslog('b', "Binkp: rcvd %s %s", bstate[bcmd], printable(bp.rxbuf+1, 0)); - if (bcmd == MM_EOB) { - bp.remote_EOB = TRUE; - } - } else { - Syslog('b', "Binkp: rcvd data (%d)", bp.rxlen -1); - } - rc = 1; - break; - } - bp.rxlen++; - } - } - } - - return rc; -} - - - -/* - * Add received command frame to the queue, will be processed by the transmitter. - */ -void binkp_add_message(char *frame) -{ - the_queue **tmpl; - int bcmd; - - bcmd = frame[0]; - Syslog('b', "Binkp: add message %s %s", bstate[bcmd], printable(frame +1, 0)); - - for (tmpl = &tql; *tmpl; tmpl = &((*tmpl)->next)); - *tmpl = (the_queue *)malloc(sizeof(the_queue)); - - (*tmpl)->next = NULL; - (*tmpl)->cmd = frame[0]; - (*tmpl)->data = xstrcpy(frame +1); - - bp.msgs_on_queue++; -} - - - -/* - * Process all messages on the queue, the oldest are on top, after - * processing release memory and reset the messages queue. - */ -int binkp_process_messages(void) -{ - the_queue *tmpq, *oldq; - binkp_list *tmp; - file_list *tsl; - int Found; - char *lname; - time_t ltime; - long lsize, loffs; - - Syslog('b', "Binkp: Process The Messages Queue Start"); - - lname = calloc(512, sizeof(char)); - - for (tmpq = tql; tmpq; tmpq = tmpq->next) { - Syslog('+', "Binkp: %s \"%s\"", bstate[tmpq->cmd], printable(tmpq->data, 0)); - if (tmpq->cmd == MM_GET) { - sprintf(lname, "%s", strtok(tmpq->data, " \n\r")); - lsize = atoi(strtok(NULL, " \n\r")); - ltime = atoi(strtok(NULL, " \n\r")); - loffs = atoi(strtok(NULL, " \n\r")); - Found = FALSE; - for (tmp = bll; tmp; tmp = tmp->next) { - if ((strcmp(lname, tmp->remote) == 0) && (lsize == tmp->size) && (ltime == tmp->date)) { - if (lsize == loffs) { - /* - * Requested offset is filesize, close file. - */ - if (cursend && (strcmp(lname, cursend->remote) == 0) && - (lsize == cursend->size) && (ltime == cursend->date)) { - Syslog('b', "Got M_GET with offset == filesize for current file, close"); - /* - * Close transmitter file - */ - fclose(bp.txfp); - bp.txfp = NULL; - - if (bp.txpos >= 0) { - bp.stxpos = bp.txpos - bp.stxpos; - Syslog('+', "Binkp: OK %s", transfertime(txtvstart, txtvend, bp.stxpos, TRUE)); - } else { - Syslog('+', "Binkp: transmitter skipped file after %ld seconds", - txtvend.tv_sec - txtvstart.tv_sec); - } - - cursend->state = IsSent; - cursend = NULL; - bp.TxState = TxGNF; - } else { - Syslog('+', "Binkp: requested offset = filesize, but is not the current file"); - } - } else if (loffs < lsize) { - tmp->state = NoState; - tmp->offset = loffs; - if (loffs) { - Syslog('+', "Binkp: Remote wants %s for resync at offset %ld", tmp->remote, tmp->offset); - } else { - Syslog('+', "Binkp: Remote wants %s again from start", tmp->remote); - } - bp.TxState = TxGNF; - } else { - Syslog('+', "Binkp: requested offset > filesize, ignore"); - } - Found = TRUE; - break; - } - if (!Found) { - Syslog('+', "Binkp: unexpected M_GET \"%s\"", tmpq->data); /* Ignore frame */ - } - } - } else if (tmpq->cmd == MM_GOT) { - sprintf(lname, "%s", strtok(tmpq->data, " \n\r")); - lsize = atoi(strtok(NULL, " \n\r")); - ltime = atoi(strtok(NULL, " \n\r")); - Found = FALSE; - for (tmp = bll; tmp; tmp = tmp->next) { - if ((strcmp(lname, tmp->remote) == 0) && (lsize == tmp->size) && (ltime == tmp->date)) { - Found = TRUE; - if (tmp->state == Sending) { - Syslog('+', "Binkp: remote refused %s", tmp->remote); - fclose(bp.txfp); - bp.txfp = NULL; - bp.TxState = TxGNF; - cursend = NULL; - } else { - Syslog('+', "Binkp: remote GOT \"%s\"", tmp->remote); - } - tmp->state = Got; - for (tsl = tosend; tsl; tsl = tsl->next) { - if (tsl->remote == NULL) { - execute_disposition(tsl); - } else { - if (strcmp(tmp->local, tsl->local) == 0) { - execute_disposition(tsl); - } - } - } - break; - } - } - if (!Found) { - Syslog('+', "Binkp: unexpected M_GOT \"%s\"", tmpq->data); /* Ignore frame */ - } - } else if (tmpq->cmd == MM_SKIP) { - sprintf(lname, "%s", strtok(tmpq->data, " \n\r")); - lsize = atoi(strtok(NULL, " \n\r")); - ltime = atoi(strtok(NULL, " \n\r")); - Found = FALSE; - for (tmp = bll; tmp; tmp = tmp->next) { - if ((strcmp(lname, tmp->remote) == 0) && (lsize == tmp->size) && (ltime == tmp->date)) { - Found = TRUE; - if (tmp->state == Sending) { - Syslog('+', "Binkp: remote skipped %s, may be accepted later", tmp->remote); - fclose(bp.txfp); - bp.txfp = NULL; - cursend = NULL; - bp.TxState = TxGNF; - } else { - Syslog('+', "Binkp: remote refused %s", tmp->remote); - } - tmp->state = Skipped; - break; - } - } - if (!Found) { - Syslog('+', "Binkp: unexpected M_SKIP \"%s\"", tmpq->data); /* Ignore frame */ - } - } else { - /* Illegal message on the queue */ - } - } - - /* - * Tidy the messages queue and reset. - */ - for (tmpq = tql; tmpq; tmpq = oldq) { - oldq = tmpq->next; - if (tmpq->data) - free(tmpq->data); - free(tmpq); - } - tql = NULL; - free(lname); - bp.msgs_on_queue = 0; - - debug_binkp_list(&bll); - Syslog('b', "Binkp: Process The Messages Queue End"); - return 0; -} - - - -/* - * Count number of pending files - */ -int binkp_pendingfiles(void) -{ - binkp_list *tmpl; - int count = 0; - - for (tmpl = bll; tmpl; tmpl = tmpl->next) { - if ((tmpl->state != Got) && (tmpl->state != Skipped)) - count++; - } - - if (count) - Syslog('b', "Binkp: %d pending files on queue", count); - return count; -} - - - -/* - * This function is called two times if a partial file exists from openfile. - * 1. A partial file is detected, send a GET to the remote, set DidSendGET flag. - * 2. DidSendGET is set, return 0 and let openfile open the file in append mode. - */ -int binkp_resync(off_t off) -{ - Syslog('b', "Binkp: resync(%d) DidSendGET=%s", off, bp.DidSendGET ?"TRUE":"FALSE"); - if (!bp.DidSendGET) { - binkp_send_command(MM_GET, "%s %ld %ld %ld", bp.rname, bp.rsize, bp.rtime, off); - bp.DidSendGET = TRUE; - Syslog('+', "Binkp: already %lu bytes received, requested restart with offset", (unsigned long)off); - return -1; /* Signal openfile not to open the file */ - } - bp.DidSendGET = FALSE; - return 0; /* Signal openfile to open the file in append mode */ -} - - - -/* - * * Translate string to binkp escaped string, unsafe characters are escaped. - * */ -char *unix2binkp(char *fn) -{ - static char buf[PATH_MAX]; - char *p, *q; - - memset(&buf, 0, sizeof(buf)); - p = fn; - q = buf; - - while (*p) { - if (strspn(p, (char *)BNKCHARS)) { - *q++ = *p; - *q = '\0'; - } else { - if (nodes.WrongEscape) { - sprintf(q, "\\%2x", p[0]); - } else { - sprintf(q, "\\x%2x", p[0]); - } - } - while (*q) - q++; - p++; - } - *q = '\0'; - - return buf; -} - - - -/* - * * Translate escaped binkp string to normal string. - * */ -char *binkp2unix(char *fn) -{ - static char buf[PATH_MAX]; - char *p, *q, hex[3]; - int c; - - memset(&buf, 0, sizeof(buf)); - p = fn; - q = buf; - - while (*p) { - if (p[0] == '\\') { - p++; - if (*p == '\\') { - /* - * A backslash is transmitted - */ - *q++ = '\\'; - *q = '\0'; - } else { - /* - * If remote sends \x0a method instead of \0a, eat the x character. - * Remotes should send the x character, But some (Argus and Irex) don't. - */ - if ((*p == 'x') || (*p == 'X')) - p++; - /* - * Decode hex characters - */ - hex[0] = *p++; - hex[1] = *p; - hex[2] = '\0'; - sscanf(hex, "%2x", &c); - *q++ = c; - *q = '\0'; - } - } else { - *q++ = *p; - *q = '\0'; - } - p++; - } - *q = '\0'; - - return buf; -} - - - -/* - * Fill internal binkp filelist - */ -void fill_binkp_list(binkp_list **bkll, file_list *fal, off_t offs) -{ - binkp_list **tmpl; - struct stat tstat; - - if (stat(fal->local, &tstat) != 0) { - Syslog('+', "$Binkp: can't add %s to sendlist", fal->local); - return; - } - if (strstr(fal->remote, (char *)".pkt")) - bp.nethold += tstat.st_size; - else - bp.mailhold += tstat.st_size; - - for (tmpl = bkll; *tmpl; tmpl = &((*tmpl)->next)); - *tmpl = (binkp_list *)malloc(sizeof(binkp_list)); - - (*tmpl)->next = NULL; - (*tmpl)->state = NoState; - (*tmpl)->get = FALSE; - (*tmpl)->local = xstrcpy(fal->local); - (*tmpl)->remote = xstrcpy(unix2binkp(fal->remote)); - (*tmpl)->offset = offs; - (*tmpl)->size = tstat.st_size; - (*tmpl)->date = tstat.st_mtime; -} - - - - -void debug_binkp_list(binkp_list **bkll) -{ - binkp_list *tmpl; - - Syslog('B', "Current filelist:"); - - for (tmpl = *bkll; tmpl; tmpl = tmpl->next) - Syslog('B', "%s %s %s %ld", MBSE_SS(tmpl->local), MBSE_SS(tmpl->remote), lbstate[tmpl->state], tmpl->offset); -} - - - -/* - * Clear current filelist - */ -void binkp_clear_filelist(void) -{ - binkp_list *tmp; - - if (tosend != NULL) { - Syslog('b', "Binkp: clear current filelist"); - - for (tmp = bll; bll; bll = tmp) { - tmp = bll->next; - if (bll->local) - free(bll->local); - if (bll->remote) - free(bll->remote); - free(bll); - } - - tidy_filelist(tosend, TRUE); - tosend = NULL; - respond = NULL; - } -} - - -#endif diff --git a/mbcico/binkpold.c b/mbcico/binkpold.c new file mode 100644 index 00000000..0827a3b8 --- /dev/null +++ b/mbcico/binkpold.c @@ -0,0 +1,1679 @@ +/***************************************************************************** + * + * $Id$ + * Purpose .................: Fidonet binkp protocol + * Binkp protocol copyright : Dima Maloff. + * + ***************************************************************************** + * Copyright (C) 1997-2003 + * + * Michiel Broek FIDO: 2:280/2802 + * Beekmansbos 10 + * 1971 BV IJmuiden + * the Netherlands + * + * This file is part of MBSE BBS. + * + * This BBS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * MBSE BBS 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. + * + * You should have received a copy of the GNU General Public License + * along with MBSE BBS; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "../config.h" + + +#include "../lib/libs.h" +#include "../lib/structs.h" +#include "../lib/users.h" +#include "../lib/records.h" +#include "../lib/common.h" +#include "../lib/nodelist.h" +#include "../lib/dbnode.h" +#include "../lib/clcomm.h" +#include "../lib/mberrors.h" +#include "ttyio.h" +#include "session.h" +#include "statetbl.h" +#include "config.h" +#include "emsi.h" +#include "openfile.h" +#include "respfreq.h" +#include "filelist.h" +#include "opentcp.h" +#include "rdoptions.h" +#include "lutil.h" +#include "binkp.h" +#include "config.h" +#include "md5b.h" +#include "inbound.h" + + +/* + * Safe characters for binkp filenames, the rest will be escaped. + */ +#define BNKCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@&=+%$-_.!()#|" + + +static char rbuf[MAX_BLKSIZE + 1]; + +static char *bstate[] = { + (char *)"NUL", (char *)"ADR", (char *)"PWD", (char *)"FILE", (char *)"OK", + (char *)"EOB", (char *)"GOT", (char *)"ERR", (char *)"BSY", (char *)"GET", + (char *)"SKIP" +}; + + + +/* + * Local prototypes + */ +void binkp_init(void); +void binkp_deinit(void); +char *unix2binkp(char *); +char *binkp2unix(char *); +int binkp_expired(void); +void b_banner(void); +void b_nul(char *); +void fill_binkp_list(binkp_list **, file_list *, off_t); +void debug_binkp_list(binkp_list **); +void binkp_send_data(char *, int); +void binkp_send_control(int id, ...); +int binkp_recv_frame(char *, int *, int *); +void binkp_settimer(int); +int resync(off_t); +static int orgbinkp(void); +static int ansbinkp(void); +static int binkp_batch(file_list *); + + +extern char *tempinbound; +extern char *ttystat[]; +extern int Loaded; +extern pid_t mypid; +extern struct sockaddr_in peeraddr; +extern int most_debug; + + +extern unsigned long sentbytes; +extern unsigned long rcvdbytes; + +typedef enum {RxWaitFile, RxAcceptFile, RxReceData, RxWriteData, RxEndOfBatch, RxDone} RxType; +typedef enum {TxGetNextFile, TxTryRead, TxReadSend, TxWaitLastAck, TxDone} TxType; +typedef enum {InitTransfer, Switch, Receive, Transmit} TransferType; +typedef enum {Ok, Failure, Continue} TrType; +typedef enum {No, WeCan, WeWant, TheyWant, Active} OptionState; + +static char *rxstate[] = { (char *)"RxWaitFile", (char *)"RxAccpetFile", (char *)"RxReceData", + (char *)"RxWriteData", (char *)"RxEndOfBatch", (char *)"RxDone" }; +static char *txstate[] = { (char *)"TxGetNextFile", (char *)"TryRread", (char *)"ReadSent", + (char *)"WaitLastAck", (char *)"TxDone" }; +//static char *tfstate[] = { (char *)"InitTransfer", (char *)"Switch", (char *)"Receive", (char *)"Transmit" }; +//static char *trstate[] = { (char *)"Ok", (char *)"Failure", (char *)"Continue" }; +static char *opstate[] = { (char *)"No", (char *)"WeCan", (char *)"WeWant", (char *)"TheyWant", (char *)"Active" }; + + +//static int TfState; +static time_t Timer; +static int CRAMflag = FALSE; /* CRAM option flag */ +static int Secure = FALSE; /* Secure session */ +int transferred = FALSE; /* Anything transferred in batch */ +unsigned char *MD_challenge = NULL; /* Received CRAM challenge data */ +int ext_rand = 0; + + + +struct binkprec { + int role; /* 1=orig, 0=answer */ + int RxState; /* Receiver state */ + int TxState; /* Transmitter state */ + int DidSendGET; /* Did we send a GET command */ + long rsize; /* Receiver filesize */ + long roffs; /* Receiver offset */ + char *rname; /* Receiver filename */ + time_t rtime; /* Receiver filetime */ + long lsize; /* Local filesize */ + char *lname; /* Local filename */ + time_t ltime; /* Local filetime */ + long gsize; /* GET filesize */ + long goffset; /* GET offset */ + char *gname; /* GET filename */ + time_t gtime; /* GET filetime */ + int MBflag; /* MB option flag */ + int Major; /* Remote major protocol version */ + int Minor; /* Remote minor protocol version */ + int rc; /* Protocol return code */ + int rxlen; /* Receive buffer length */ + int txlen; /* Length of transmitted data block */ + char *txbuf; /* Transmitter buffer */ + char *rxbuf; /* Receiver buffer */ + FILE *txfp; /* File in transmitter */ + FILE *rxfp; /* File in receiver */ + int txpos; /* Transmitter position */ + int rxpos; /* Receiver position */ + int stxpos; /* Start transmitter position */ + int cmd; /* Command flag */ + int GotFrame; /* Got Frame flag */ + unsigned short header; /* Frame header */ + int blklen; /* Block length */ + off_t rxbytes; /* Receiver bytecount */ + struct timeval rxtvstart; /* Receive file start */ + struct timeval rxtvend; /* Receiver file end */ + struct timeval txtvstart; /* Transmit file start */ + struct timeval txtvend; /* Transmit file end */ + struct timezone tz; /* Timezone */ + unsigned long nethold; /* Netmail on hold */ + unsigned long mailhold; /* Packed mail (files) on hold */ + int batchnr; /* Batch number */ +}; + +struct binkprec bp; /* Global structure */ + + + +void binkp_init(void) +{ + bp.rname = calloc(512, sizeof(char)); + bp.lname = calloc(512, sizeof(char)); + bp.gname = calloc(512, sizeof(char)); + bp.MBflag = WeCan; + bp.Major = 1; + bp.Minor = 0; + bp.DidSendGET = FALSE; + bp.rc = 0; + bp.rxlen = 0; + bp.txbuf = calloc(MAX_BLKSIZE + 3, sizeof(unsigned char)); + bp.rxbuf = calloc(MAX_BLKSIZE + 3, sizeof(unsigned char)); + bp.txfp = NULL; + bp.rxfp = NULL; + bp.txpos = 0; + bp.rxpos = 0; + bp.stxpos = 0; + bp.cmd = FALSE; + bp.GotFrame = FALSE; + bp.header = 0; + bp.batchnr = 0; +} + + +void binkp_deinit(void) +{ + if (bp.rname) + free(bp.rname); + if (bp.lname) + free(bp.lname); + if (bp.gname) + free(bp.gname); + if (bp.txbuf) + free(bp.txbuf); + if (bp.rxbuf) + free(bp.rxbuf); +} + + +int binkp(int role) +{ + int rc = MBERR_OK; + fa_list *eff_remote; + file_list *tosend = NULL, *request = NULL, *respond = NULL, *tmpfl; + char *nonhold_mail; + + binkp_init(); + + most_debug = TRUE; + if (role == 1) { + if (orgbinkp()) { + rc = MBERR_SESSION_ERROR; + } + } else { + if (ansbinkp()) { + rc = MBERR_SESSION_ERROR; + } + } + + if (rc) { + Syslog('!', "Binkp: session failed"); + binkp_deinit(); + return rc; + } + +// if (localoptions & NOFREQS) +// session_flags &= ~SESSION_WAZOO; +// else + session_flags |= SESSION_WAZOO; + + Syslog('b', "Binkp: WAZOO requests: %s", (session_flags & SESSION_WAZOO) ? "True":"False"); + + nonhold_mail = (char *)ALL_MAIL; + eff_remote = remote; + /* + * If remote doesn't have the 8.3 flag set, allow long filenames. + */ + if (!nodes.FNC) + remote_flags &= ~SESSION_FNC; + + tosend = create_filelist(eff_remote, nonhold_mail, 0); + + if (request != NULL) { + Syslog('b', "Binkp: inserting request list"); + tmpfl = tosend; + tosend = request; + for (; request->next; request = request->next); + request->next = tmpfl; + + request = NULL; + } + + rc = binkp_batch(tosend); + tidy_filelist(tosend, (rc == 0)); + tosend = NULL; + + if ((rc == 0) && transferred && (bp.MBflag == Active)) { + /* + * Running Multiple Batch, only if last batch actually + * did transfer some data. + */ + respond = respond_wazoo(); + /* + * Just create the tosend list again, there may be something + * ready again for this node. + */ + tosend = create_filelist(eff_remote, nonhold_mail, 0); + for (tmpfl = tosend; tmpfl->next; tmpfl = tmpfl->next); + tmpfl->next = respond; + rc = binkp_batch(tosend); + tmpfl->next = NULL; + } + + Syslog('+', "Binkp: end transfer rc=%d", rc); + closetcp(); + + Syslog('b', "2nd batch or not, MB flag is %s", opstate[bp.MBflag]); + + if (bp.MBflag != Active) { + /* + * In singe batch mode we process filerequests after the batch. + * The results will be put on hold for the calling node. + * This method is also known as "dual session mode". + */ + respond = respond_wazoo(); + for (tmpfl = respond; tmpfl; tmpfl = tmpfl->next) { + if (strncmp(tmpfl->local, "/tmp", 4)) { + attach(*remote->addr, tmpfl->local, LEAVE, 'h'); + Syslog('+', "Binkp: put on hold: %s", MBSE_SS(tmpfl->local)); + } else { + file_mv(tmpfl->local, pktname(remote->addr, 'h')); + Syslog('+', "Binkp: new netmail: %s", pktname(remote->addr, 'h')); + } + } + } + + tidy_filelist(request, (rc == 0)); + tidy_filelist(tosend, (rc == 0)); + tidy_filelist(respond, 0); + + binkp_deinit(); + rc = abs(rc); + return rc; +} + + + +/* + * Translate string to binkp escaped string, unsafe characters are escaped. + */ +char *unix2binkp(char *fn) +{ + static char buf[PATH_MAX]; + char *p, *q; + + memset(&buf, 0, sizeof(buf)); + p = fn; + q = buf; + + while (*p) { + if (strspn(p, (char *)BNKCHARS)) { + *q++ = *p; + *q = '\0'; + } else { + if (nodes.WrongEscape) { + sprintf(q, "\\%2x", p[0]); + } else { + sprintf(q, "\\x%2x", p[0]); + } + } + while (*q) + q++; + p++; + } + *q = '\0'; + + return buf; +} + + + +/* + * Translate escaped binkp string to normal string. + */ +char *binkp2unix(char *fn) +{ + static char buf[PATH_MAX]; + char *p, *q, hex[3]; + int c; + + memset(&buf, 0, sizeof(buf)); + p = fn; + q = buf; + + while (*p) { + if (p[0] == '\\') { + p++; + if (*p == '\\') { + /* + * A backslash is transmitted + */ + *q++ = '\\'; + *q = '\0'; + } else { + /* + * If remote sends \x0a method instead of \0a, eat the x character. + * Remotes should send the x character, But some (Argus and Irex) don't. + */ + if ((*p == 'x') || (*p == 'X')) + p++; + /* + * Decode hex characters + */ + hex[0] = *p++; + hex[1] = *p; + hex[2] = '\0'; + sscanf(hex, "%2x", &c); + *q++ = c; + *q = '\0'; + } + } else { + *q++ = *p; + *q = '\0'; + } + p++; + } + *q = '\0'; + + return buf; +} + + + +/* + * Transmit data frame + */ +void binkp_send_data(char *buf, int len) +{ + unsigned short header = 0; + + Syslog('B', "Binkp: send_data len=%d", len); + + header = ((BINKP_DATA_BLOCK + len) & 0xffff); + + PUTCHAR((header >> 8) & 0x00ff); + PUTCHAR(header & 0x00ff); + if (len) + PUT(buf, len); + FLUSHOUT(); + binkp_settimer(BINKP_TIMEOUT); +} + + + +/* + * Transmit control frame + */ +void binkp_send_control(int id,...) +{ + va_list args; + char *fmt, *s; + binkp_frame frame; + static char buf[1024]; + int sz; + + va_start(args, id); + fmt = va_arg(args, char*); + + if (fmt) { + vsprintf(buf, fmt, args); + sz = ((1 + strlen(buf)) & 0x7fff); + } else { + buf[0]='\0'; + sz = 1; + } + + Syslog('b', "Binkp: send_ctl %s \"%s\"", bstate[id], buf); + frame.header = ((BINKP_CONTROL_BLOCK + sz) & 0xffff); + frame.id = (char)id; + frame.data = buf; + + s = (unsigned char *)malloc(sz + 2 + 1); + s[sz + 2] = '\0'; + s[0] = ((frame.header >> 8)&0xff); + s[1] = (frame.header & 0xff); + s[2] = frame.id; + if (frame.data[0]) + strncpy(s + 3, frame.data, sz-1); + + PUT(s, sz+2); + FLUSHOUT(); + + free(s); + va_end(args); + binkp_settimer(BINKP_TIMEOUT); +} + + + +/* + * This function is called two times if a partial file exists from openfile. + * 1. A partial file is detected, send a GET to the remote, set DidSendGET flag. + * 2. DidSendGET is set, return 0 and let openfile open the file in append mode. + */ +int resync(off_t off) +{ + Syslog('b', "Binkp: resync(%d) DidSendGET=%s", off, bp.DidSendGET ?"TRUE":"FALSE"); + if (!bp.DidSendGET) { + binkp_send_control(MM_GET, "%s %ld %ld %ld", bp.rname, bp.rsize, bp.rtime, off); + bp.DidSendGET = TRUE; + Syslog('+', "Binkp: already %lu bytes received, requested restart with offset", (unsigned long)off); + return -1; /* Signal openfile not to open the file */ + } + bp.DidSendGET = FALSE; + return 0; /* Signal openfile to open the file in append mode */ +} + + + +/* + * Receive control frame + */ +int binkp_recv_frame(char *buf, int *len, int *cmd) +{ + int b0, b1; + + *len = *cmd = 0; + + b0 = GETCHAR(BINKP_TIMEOUT); + if (tty_status) + goto to; + if (b0 & 0x80) + *cmd = 1; + + b1 = GETCHAR(1); + if (tty_status) + goto to; + + *len = (b0 & 0x7f) << 8; + *len += b1; + + GET(buf, *len, BINKP_TIMEOUT / 2); + buf[*len] = '\0'; + if (tty_status) + goto to; + +to: + if (tty_status) + WriteError("Binkp: TCP receive error: %d %s", tty_status, ttystat[tty_status]); + return tty_status; +} + + + +void binkp_settimer(int interval) +{ + Timer = time((time_t*)NULL) + interval; +} + + + +int binkp_expired(void) +{ + time_t now; + + now = time(NULL); + if (now >= Timer) + Syslog('+', "Binkp: timeout"); + return (now >= Timer); +} + + + +void b_banner(void) +{ + time_t t; + + binkp_send_control(MM_NUL,"SYS %s", CFG.bbs_name); + binkp_send_control(MM_NUL,"ZYZ %s", CFG.sysop_name); + binkp_send_control(MM_NUL,"LOC %s", CFG.location); + binkp_send_control(MM_NUL,"NDL %s", CFG.Flags); + t = time(NULL); + binkp_send_control(MM_NUL,"TIME %s", rfcdate(t)); + binkp_send_control(MM_NUL,"VER mbcico/%s/%s-%s %s/%s", VERSION, OsName(), OsCPU(), PRTCLNAME, PRTCLVER); + if (strlen(CFG.Phone)) + binkp_send_control(MM_NUL,"PHN %s", CFG.Phone); + if (strlen(CFG.comment)) + binkp_send_control(MM_NUL,"OPM %s", CFG.comment); +} + + + +void b_nul(char *msg) +{ + char *p, *q; + + if (strncmp(msg, "SYS ", 4) == 0) { + Syslog('+', "System : %s", msg+4); + strncpy(history.system_name, msg+4, 35); + } else if (strncmp(msg, "ZYZ ", 4) == 0) { + Syslog('+', "Sysop : %s", msg+4); + strncpy(history.sysop, msg+4, 35); + } else if (strncmp(msg, "LOC ", 4) == 0) { + Syslog('+', "Location: %s", msg+4); + strncpy(history.location, msg+4, 35); + } else if (strncmp(msg, "NDL ", 4) == 0) + Syslog('+', "Flags : %s", msg+4); + else if (strncmp(msg, "TIME ", 5) == 0) + Syslog('+', "Time : %s", msg+5); + else if (strncmp(msg, "VER ", 4) == 0) { + Syslog('+', "Uses : %s", msg+4); + if ((p = strstr(msg+4, PRTCLNAME "/")) && (q = strstr(p, "."))) { + bp.Major = atoi(p + 6); + bp.Minor = atoi(q + 1); + Syslog('b', "Remote protocol version %d.%d", bp.Major, bp.Minor); + /* + * Disable MB if protocol > 1.0 and MB was not yet active. + */ + if ((bp.MBflag != Active) && (((bp.Major * 10) + bp.Minor) > 10)) { + Syslog('b', "MBflag %s => No", opstate[bp.MBflag]); + bp.MBflag = No; + } + } + } + else if (strncmp(msg, "PHN ", 4) == 0) + Syslog('+', "Phone : %s", msg+4); + else if (strncmp(msg, "OPM ", 4) == 0) + Syslog('+', "Remark : %s", msg+4); + else if (strncmp(msg, "TRF ", 4) == 0) + Syslog('+', "Binkp: remote has %s mail/files for us", msg+4); + else if (strncmp(msg, "OPT ", 4) == 0) { + Syslog('+', "Options : %s", msg+4); + if (strstr(msg, (char *)"MB") != NULL) { + Syslog('b', "Remote requests MB, current state = %s", opstate[bp.MBflag]); + if ((bp.MBflag == WeCan) && (bp.Major == 1) && (bp.Minor == 0)) { /* Answering session and do binkp/1.0 */ + bp.MBflag = TheyWant; + Syslog('b', "MBflag WeCan => TheyWant"); + binkp_send_control(MM_NUL,"OPT MB"); + Syslog('b', "MBflag TheyWant => Active"); + bp.MBflag = Active; + } else if ((bp.MBflag == WeWant) && (bp.Major == 1) && (bp.Minor == 0)) { /* Originating session and do binkp/1.0 */ + bp.MBflag = Active; + Syslog('b', "MBflag WeWant => Active"); + } else { + Syslog('b', "MBflag is %s and received MB option", opstate[bp.MBflag]); + } + } + if (strstr(msg, (char *)"CRAM-MD5-") != NULL) { /* No SHA-1 support */ + if (CFG.NoMD5) { + Syslog('+', "Binkp: Remote supports MD5, but it's turned off here"); + } else { + if (MD_challenge) + free(MD_challenge); + MD_challenge = MD_getChallenge(msg, NULL); + } + } + } else + Syslog('+', "Binkp: M_NUL \"%s\"", msg); +} + + + +/* + * Originate a binkp session + */ +SM_DECL(orgbinkp, (char *)"orgbinkp") +SM_STATES + ConnInit, + WaitConn, + SendPasswd, + WaitAddr, + AuthRemote, + IfSecure, + WaitOk, + Opts +SM_NAMES + (char *)"ConnInit", + (char *)"WaitConn", + (char *)"SendPasswd", + (char *)"WaitAddr", + (char *)"AuthRemote", + (char *)"IfSecure", + (char *)"WaitOk", + (char *)"Opts" +SM_EDECL + faddr *primary; + char *p, *q, *pwd; + int i, rc, bufl, cmd, dupe, SendPass = FALSE; + fa_list **tmp, *tmpa; + faddr *fa, ra; + +SM_START(ConnInit) + +SM_STATE(ConnInit) + + SM_PROCEED(WaitConn) + +SM_STATE(WaitConn) + + Loaded = FALSE; + Syslog('+', "Binkp: node %s", ascfnode(remote->addr, 0x1f)); + IsDoing("Connect binkp %s", ascfnode(remote->addr, 0xf)); + + /* + * Build options we want + */ +// p = xstrcpy((char *)"OPT"); +// if ((noderecord(remote->addr)) && nodes.CRC32 && (bp.CRCflag == WeCan)) { +// p = xstrcat(p, (char *)" CRC"); +// bp.CRCflag = WeWant; +// Syslog('b', "CRCflag WeCan => WeWant"); +// } +// if (strcmp(p, (char *)"OPT")) +// binkp_send_control(MM_NUL, p); +// free(p); + b_banner(); + + /* + * Build a list of aka's to send, the primary aka first. + */ + ra.zone = remote->addr->zone; + ra.net = remote->addr->net; + ra.node = remote->addr->node; + ra.point = remote->addr->point; + + primary = bestaka_s(remote->addr); + p = xstrcpy(ascfnode(primary, 0x1f)); + + /* + * Add all other aka's exept primary aka. + */ + for (i = 0; i < 40; i++) + if ((CFG.aka[i].zone) && (CFG.akavalid[i]) && + ((CFG.aka[i].zone != primary->zone) || (CFG.aka[i].net != primary->net) || + (CFG.aka[i].node != primary->node) || (CFG.aka[i].point!= primary->point))) { + p = xstrcat(p, (char *)" "); + p = xstrcat(p, aka2str(CFG.aka[i])); + } + + binkp_send_control(MM_ADR, "%s", p); + free(p); + tidy_faddr(primary); + SM_PROCEED(WaitAddr) + +SM_STATE(WaitAddr) + + for (;;) { + if ((rc = binkp_recv_frame(rbuf, &bufl, &cmd))) { + Syslog('!', "Binkp: error receiving remote info"); + SM_ERROR; + } + + if (cmd) { + if (rbuf[0] == MM_ADR) { + p = xstrcpy(&rbuf[1]); + tidy_falist(&remote); + remote = NULL; + tmp = &remote; + + for (q = strtok(p, " "); q; q = strtok(NULL, " ")) { + if ((fa = parsefnode(q))) { + dupe = FALSE; + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) && + (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) && + (strcmp(tmpa->addr->domain, fa->domain) == 0)) { + dupe = TRUE; + Syslog('b', "Binkp: double address %s", ascfnode(tmpa->addr, 0x1f)); + break; + } + } + if (!dupe) { + *tmp = (fa_list*)malloc(sizeof(fa_list)); + (*tmp)->next = NULL; + (*tmp)->addr = fa; + tmp = &((*tmp)->next); + } + } else { + Syslog('!', "Binkp: bad remote address: \"%s\"", printable(q, 0)); + binkp_send_control(MM_ERR, "Bad address"); + } + } + + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + Syslog('+', "Address : %s", ascfnode(tmpa->addr, 0x1f)); + if (nodelock(tmpa->addr, mypid)) { + binkp_send_control(MM_BSY, "Address %s locked", ascfnode(tmpa->addr, 0x1f)); + SM_ERROR; + } + + /* + * With the loaded flag we prevent removing the noderecord + * when the remote presents us an address we don't know about. + */ + if (!Loaded) { + if (noderecord(tmpa->addr)) + Loaded = TRUE; + } + } + + history.aka.zone = remote->addr->zone; + history.aka.net = remote->addr->net; + history.aka.node = remote->addr->node; + history.aka.point = remote->addr->point; + sprintf(history.aka.domain, "%s", remote->addr->domain); + + SM_PROCEED(SendPasswd) + + } else if (rbuf[0] == MM_BSY) { + Syslog('!', "Binkp: M_BSY \"%s\"", printable(&rbuf[1], 0)); + SM_ERROR; + + } else if (rbuf[0] == MM_ERR) { + Syslog('!', "Binkp: M_ERR \"%s\"", printable(&rbuf[1], 0)); + SM_ERROR; + + } else if (rbuf[0] == MM_NUL) { + b_nul(&rbuf[1]); + + } else { + binkp_send_control(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } + } + +SM_STATE(SendPasswd) + + if (Loaded && strlen(nodes.Spasswd)) { + pwd = xstrcpy(nodes.Spasswd); + SendPass = TRUE; + } else { + pwd = xstrcpy((char *)"-"); + } + + if (MD_challenge) { + char *tp = NULL; + tp = MD_buildDigest(pwd, MD_challenge); + if (!tp) { + Syslog('!', "Unable to build MD5 digest"); + SM_ERROR; + } + CRAMflag = TRUE; + binkp_send_control(MM_PWD, "%s", tp); + free(tp); + } else { + binkp_send_control(MM_PWD, "%s", pwd); + } + + free(pwd); + SM_PROCEED(AuthRemote) + +SM_STATE(AuthRemote) + + rc = 0; + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + if ((tmpa->addr->zone == ra.zone) && (tmpa->addr->net == ra.net) && + (tmpa->addr->node == ra.node) && (tmpa->addr->point == ra.point)) { + rc = 1; + } + } + + if (rc) { + SM_PROCEED(IfSecure) + } else { + Syslog('!', "Binkp: error, the wrong node is reached"); + binkp_send_control(MM_ERR, "No AKAs in common or all AKAs busy"); + SM_ERROR; + } + +SM_STATE(IfSecure) + + SM_PROCEED(WaitOk) + +SM_STATE(WaitOk) + + for (;;) { + if ((rc = binkp_recv_frame(rbuf, &bufl, &cmd))) { + Syslog('!', "Binkp: error waiting for remote acknowledge"); + SM_ERROR; + } + + if (cmd) { + if (rbuf[0] == MM_OK) { + Syslog('b', "Binkp: M_OK \"%s\"", printable(&rbuf[1], 0)); + if (SendPass) + Secure = TRUE; + Syslog('+', "Binkp: %s%sprotected session", CRAMflag ? "MD5 ":"", Secure ? "":"un"); + SM_PROCEED(Opts) + + } else if (rbuf[0] == MM_BSY) { + Syslog('!', "Binkp: M_BSY \"%s\"", printable(&rbuf[1], 0)); + SM_ERROR; + + } else if (rbuf[0] == MM_ERR) { + Syslog('!', "Binkp: M_ERR \"%s\"", printable(&rbuf[1], 0)); + SM_ERROR; + + } else if (rbuf[0] == MM_NUL) { + b_nul(&rbuf[1]); + + } else { + binkp_send_control(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } + } + +SM_STATE(Opts) + + /* + * Try to initiate the MB option if the remote is binkp/1.0 + */ + if ((bp.MBflag == WeCan) && (bp.Major == 1) && (bp.Minor == 0)) { + bp.MBflag = WeWant; + Syslog('b', "MBflag WeCan => WeWant"); + binkp_send_control(MM_NUL, "OPT MB"); + } + SM_SUCCESS; + +SM_END +SM_RETURN + + + +/* + * Answer a binkp session + */ +SM_DECL(ansbinkp, (char *)"ansbinkp") +SM_STATES + WaitConn, + WaitAddr, + IsPasswd, + WaitPwd, + PwdAck, + Opts +SM_NAMES + (char *)"WaitConn", + (char *)"WaitAddr", + (char *)"IsPasswd", + (char *)"WaitPwd", + (char *)"PwdAck", + (char *)"Opts" + +SM_EDECL + char *p, *q, *pw; + int i, rc, bufl, cmd, dupe, we_have_pwd = FALSE; + fa_list **tmp, *tmpa; + faddr *fa; + +SM_START(WaitConn) + +SM_STATE(WaitConn) + + Loaded = FALSE; + + if (strncmp(SockR("SBBS:0;"), "100:2,1", 7) == 0) { + Syslog('+', "Binkp: system is closed, sending M_BSY"); + binkp_send_control(MM_BSY, "This system is closed, try again later"); + SM_ERROR; + } + + if (!CFG.NoMD5 && ((MD_challenge = MD_getChallenge(NULL, &peeraddr)) != NULL)) { + /* + * Answering site MUST send CRAM message as very first M_NUL + */ + char s[MD5_DIGEST_LEN*2+15]; /* max. length of opt string */ + strcpy(s, "OPT "); + MD_toString(s+4, MD_challenge[0], MD_challenge+1); + CRAMflag = TRUE; + binkp_send_control(MM_NUL, "%s", s); + } + b_banner(); + p = xstrcpy((char *)""); + + for (i = 0; i < 40; i++) + if ((CFG.aka[i].zone) && (CFG.akavalid[i])) { + p = xstrcat(p, (char *)" "); + p = xstrcat(p, aka2str(CFG.aka[i])); + } + + binkp_send_control(MM_ADR, "%s", p); + free(p); + SM_PROCEED(WaitAddr) + +SM_STATE(WaitAddr) + + for (;;) { + if ((rc = binkp_recv_frame(rbuf, &bufl, &cmd))) { + Syslog('!', "Binkp: error waiting for remote info"); + SM_ERROR; + } + + if (cmd) { + if (rbuf[0] == MM_ADR) { + p = xstrcpy(&rbuf[1]); + tidy_falist(&remote); + remote = NULL; + tmp = &remote; + + for (q = strtok(p, " "); q; q = strtok(NULL, " ")) + if ((fa = parsefnode(q))) { + dupe = FALSE; + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) && + (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) && + (strcmp(tmpa->addr->domain, fa->domain) == 0)) { + dupe = TRUE; + Syslog('b', "Binkp: double address %s", ascfnode(tmpa->addr, 0x1f)); + break; + } + } + if (!dupe) { + *tmp = (fa_list*)malloc(sizeof(fa_list)); + (*tmp)->next = NULL; + (*tmp)->addr = fa; + tmp = &((*tmp)->next); + } + } else { + Syslog('!', "Binkp: bad remote address: \"%s\"", printable(q, 0)); + binkp_send_control(MM_ERR, "Bad address"); + } + + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + Syslog('+', "Address : %s", ascfnode(tmpa->addr, 0x1f)); + if (nodelock(tmpa->addr, mypid)) { + binkp_send_control(MM_BSY, "Address %s locked", ascfnode(tmpa->addr, 0x1f)); + SM_ERROR; + } + + /* + * With the loaded flag we prevent removing the noderecord + * when the remote presents us an address we don't know about. + */ + if (!Loaded) { + if (noderecord(tmpa->addr)) + Loaded = TRUE; + } + } + + for (tmpa = remote; tmpa; tmpa = tmpa->next) { + if (((nlent = getnlent(tmpa->addr))) && (nlent->pflag != NL_DUMMY)) { + Syslog('+', "Binkp: remote is a listed system"); + UserCity(mypid, nlent->sysop, nlent->location); + break; + } + } + if (nlent) + rdoptions(Loaded); + +// if (bp.MBflag == TheyWant) { +// Syslog('b', "Binkp: remote supports MB"); +// binkp_send_control(MM_NUL,"OPT MB"); +// bp.MBflag = Active; +// } + + history.aka.zone = remote->addr->zone; + history.aka.net = remote->addr->net; + history.aka.node = remote->addr->node; + history.aka.point = remote->addr->point; + sprintf(history.aka.domain, "%s", remote->addr->domain); + + SM_PROCEED(IsPasswd) + + } else if (rbuf[0] == MM_ERR) { + Syslog('!', "Binkp: M_ERR \"%s\"", printable(&rbuf[1], 0)); + SM_ERROR; + + } else if (rbuf[0] == MM_NUL) { + b_nul(&rbuf[1]); + } else if (rbuf[0] <= MM_MAX) { + binkp_send_control(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } + } + +SM_STATE(IsPasswd) + + if (Loaded && strlen(nodes.Spasswd)) { + we_have_pwd = TRUE; + } + + Syslog('b', "We %s have a password", we_have_pwd ?"do":"don't"); + SM_PROCEED(WaitPwd) + +SM_STATE(WaitPwd) + + for (;;) { + if ((rc = binkp_recv_frame(rbuf, &bufl, &cmd))) { + Syslog('!', "Binkp: error waiting for password"); + SM_ERROR; + } + + if (cmd) { + if (rbuf[0] == MM_PWD) { + SM_PROCEED(PwdAck) + + } else if (rbuf[0] == MM_ERR) { + Syslog('!', "Binkp: M_ERR \"%s\"", printable(&rbuf[1], 0)); + SM_ERROR; + } else if (rbuf[0] == MM_NUL) { + b_nul(&rbuf[1]); + } else if (rbuf[0] <= MM_MAX) { + binkp_send_control(MM_ERR, "Unexpected frame"); + SM_ERROR; + } + } + } + +SM_STATE(PwdAck) + + if (we_have_pwd) { + pw = xstrcpy(nodes.Spasswd); + } else { + pw = xstrcpy((char *)"-"); + } + + if ((strncmp(&rbuf[1], "CRAM-", 5) == 0) && CRAMflag) { + char *sp; + sp = MD_buildDigest(pw, MD_challenge); + if (sp != NULL) { + if (strcmp(&rbuf[1], sp)) { + Syslog('+', "Binkp: bad MD5 crypted password"); + binkp_send_control(MM_ERR, "Bad password"); + free(sp); + sp = NULL; + free(pw); + SM_ERROR; + } else { + free(sp); + sp = NULL; + if (we_have_pwd) + Secure = TRUE; + } + } else { + free(pw); + Syslog('!', "Binkp: could not build MD5 digest"); + binkp_send_control(MM_ERR, "*** Internal error ***"); + SM_ERROR; + } + } else if ((strcmp(&rbuf[1], pw) == 0)) { + if (we_have_pwd) + Secure = TRUE; + } else { + free(pw); + Syslog('?', "Binkp: password error: expected \"%s\", got \"%s\"", nodes.Spasswd, &rbuf[1]); + binkp_send_control(MM_ERR, "Bad password"); + SM_ERROR; + } + + free(pw); + Syslog('+', "Binkp: %s%sprotected session", CRAMflag ? "MD5 ":"", Secure ? "":"un"); + inbound_open(remote->addr, Secure); + binkp_send_control(MM_OK, "%ssecure", Secure ? "":"non-"); + SM_PROCEED(Opts) + +SM_STATE(Opts) + + SM_SUCCESS; + +SM_END +SM_RETURN + + + +void fill_binkp_list(binkp_list **bll, file_list *fal, off_t offs) +{ + binkp_list **tmpl; + struct stat tstat; + + if (stat(fal->local, &tstat) != 0) { + Syslog('!', "$Can't add %s to sendlist", fal->local); + return; + } + if (strstr(fal->remote, (char *)".pkt")) + bp.nethold += tstat.st_size; + else + bp.mailhold += tstat.st_size; + + for (tmpl = bll; *tmpl; tmpl = &((*tmpl)->next)); + *tmpl = (binkp_list *)malloc(sizeof(binkp_list)); + + (*tmpl)->next = NULL; + (*tmpl)->state = NoState; + (*tmpl)->get = FALSE; + (*tmpl)->local = xstrcpy(fal->local); + (*tmpl)->remote = xstrcpy(unix2binkp(fal->remote)); + (*tmpl)->offset = offs; + (*tmpl)->size = tstat.st_size; + (*tmpl)->date = tstat.st_mtime; +} + + + +char *lbstat[]={(char *)"None", + (char *)"Sending", + (char *)"IsSent", + (char *)"Got", + (char *)"Skipped", + (char *)"Get"}; + + + +void debug_binkp_list(binkp_list **bll) +{ + binkp_list *tmpl; + + Syslog('B', "Current filelist:"); + + for (tmpl = *bll; tmpl; tmpl = tmpl->next) + Syslog('B', "%s %s %s %ld", MBSE_SS(tmpl->local), MBSE_SS(tmpl->remote), lbstat[tmpl->state], tmpl->offset); +} + + +/* + * Get a fram for the batch + */ +void batch_receive_frame(void); +void batch_receive_frame(void) +{ + int c; + + for (;;) { + if (bp.GotFrame) { + Syslog('b', "Binkp: WARNING: frame not processed"); + break; + } else { + c = GETCHAR(0); + if (c < 0) { + c = -c; + if (c == STAT_TIMEOUT) { + usleep(1); + break; + } + Syslog('?', "Binkp: receiver status %s", ttystat[c]); + bp.TxState = TxDone; + bp.RxState = RxDone; + bp.rc = (MBERR_TTYIO + (-c)); + break; + } else { + switch (bp.rxlen) { + case 0: bp.header = c << 8; + break; + case 1: bp.header += c; + break; + default:bp.rxbuf[bp.rxlen-2] = c; + } + if (bp.rxlen == 1) { + bp.cmd = bp.header & 0x8000; + bp.blklen = bp.header & 0x7fff; + } + if ((bp.rxlen == (bp.blklen + 1) && (bp.rxlen >= 1))) { + bp.GotFrame = TRUE; + binkp_settimer(BINKP_TIMEOUT); + bp.rxbuf[bp.rxlen-1] = '\0'; + break; + } + bp.rxlen++; + } + } + } +} + + + +int binkp_batch(file_list *to_send) +{ + int NotDone, written, Found = FALSE; + binkp_list *bll = NULL, *tmp, *tmpg, *cursend = NULL; + file_list *tsl; + struct statfs sfs; + + bp.rxtvstart.tv_sec = bp.rxtvstart.tv_usec = 0; + bp.rxtvend.tv_sec = bp.rxtvend.tv_usec = 0; + bp.txtvstart.tv_sec = bp.txtvstart.tv_usec = 0; + bp.txtvend.tv_sec = bp.txtvend.tv_usec = 0; + bp.tz.tz_minuteswest = bp.tz.tz_dsttime = 0; + + bp.batchnr++; + Syslog('+', "Binkp: starting batch %d", bp.batchnr); + IsDoing("Binkp %s %s", (bp.role == 1)?"out":"inb", ascfnode(remote->addr, 0xf)); +// TfState = Switch; + bp.RxState = RxWaitFile; + bp.TxState = TxGetNextFile; + binkp_settimer(BINKP_TIMEOUT); + bp.nethold = bp.mailhold = 0L; + transferred = FALSE; + + /* + * Build a new filelist from the existing filelist. + * This one is special for binkp behaviour. + */ + for (tsl = to_send; tsl; tsl = tsl->next) { + if (tsl->remote != NULL) + fill_binkp_list(&bll, tsl, 0L); + } + debug_binkp_list(&bll); + + Syslog('+', "Binkp: mail %ld, files %ld bytes", bp.nethold, bp.mailhold); + binkp_send_control(MM_NUL, "TRF %ld %ld", bp.nethold, bp.mailhold); + + while ((bp.RxState != RxDone) || (bp.TxState != TxDone)) { + + Nopper(); + if (binkp_expired()) { + Syslog('!', "Binkp: Transfer timeout"); + Syslog('b', "Binkp: TxState=%s, RxState=%s, rxlen=%d", txstate[bp.TxState], rxstate[bp.RxState], bp.rxlen); + bp.RxState = RxDone; + bp.TxState = TxDone; + binkp_send_control(MM_ERR, "Transfer timeout"); + bp.rc = MBERR_FTRANSFER; + break; + } + + /* + * Receiver binkp frame + */ + batch_receive_frame(); + + /* + * Transmitter state machine + */ + switch (bp.TxState) { + case TxGetNextFile: + for (tmp = bll; tmp; tmp = tmp->next) { + if (tmp->state == NoState) { + /* + * There is something to send + */ + struct flock txflock; + + txflock.l_type = F_RDLCK; + txflock.l_whence = 0; + txflock.l_start = 0L; + txflock.l_len = 0L; + + bp.txfp = fopen(tmp->local, "r"); + if (bp.txfp == NULL) { + if ((errno == ENOENT) || (errno == EINVAL)) { + Syslog('+', "Binkp: file %s doesn't exist, removing", MBSE_SS(tmp->local)); + tmp->state = Got; + } else { + WriteError("$Binkp: can't open %s, skipping", MBSE_SS(tmp->local)); + tmp->state = Skipped; + } + break; + } + + if (fcntl(fileno(bp.txfp), F_SETLK, &txflock) != 0) { + WriteError("$Binkp: can't lock file %s, skipping", MBSE_SS(tmp->local)); + fclose(bp.txfp); + bp.txfp = NULL; + tmp->state = Skipped; + break; + } + + bp.txpos = bp.stxpos = tmp->offset; + Syslog('+', "Binkp: send \"%s\" as \"%s\"", MBSE_SS(tmp->local), MBSE_SS(tmp->remote)); + Syslog('+', "Binkp: size %lu bytes, dated %s", (unsigned long)tmp->size, date(tmp->date)); + binkp_send_control(MM_FILE, "%s %lu %ld %ld", MBSE_SS(tmp->remote), + (unsigned long)tmp->size, (long)tmp->date, (unsigned long)tmp->offset); + gettimeofday(&bp.txtvstart, &bp.tz); + tmp->state = Sending; + cursend = tmp; + bp.TxState = TxTryRead; + transferred = TRUE; + break; + } /* if state == NoState */ + } /* for */ + + if (tmp == NULL) { + /* + * No more files to send + */ + bp.TxState = TxWaitLastAck; + Syslog('b', "Binkp: transmitter to WaitLastAck"); + } + break; + + case TxTryRead: + /* + * Check if there is room in the output buffer + */ + if ((WAITPUTGET(-1) & 2) != 0) + bp.TxState = TxReadSend; + break; + + case TxReadSend: + fseek(bp.txfp, bp.txpos, SEEK_SET); + bp.txlen = fread(bp.txbuf, 1, SND_BLKSIZE, bp.txfp); + + if (bp.txlen == 0) { + + if (ferror(bp.txfp)) { + WriteError("$Binkp: error reading from file"); + bp.TxState = TxGetNextFile; + cursend->state = Skipped; + debug_binkp_list(&bll); + break; + } + + /* + * calculate time needed and bytes transferred + */ + gettimeofday(&bp.txtvend, &bp.tz); + + /* + * Close transmitter file + */ + fclose(bp.txfp); + bp.txfp = NULL; + + if (bp.txpos >= 0) { + bp.stxpos = bp.txpos - bp.stxpos; + Syslog('+', "Binkp: OK %s", transfertime(bp.txtvstart, bp.txtvend, bp.stxpos, TRUE)); + } else { + Syslog('+', "Binkp: transmitter skipped file after %ld seconds", bp.txtvend.tv_sec - bp.txtvstart.tv_sec); + } + + cursend->state = IsSent; + bp.TxState = TxGetNextFile; + break; + } else { + bp.txpos += bp.txlen; + sentbytes += bp.txlen; + binkp_send_data(bp.txbuf, bp.txlen); + } + + bp.TxState = TxTryRead; + break; + + case TxWaitLastAck: + debug_binkp_list(&bll); + NotDone = FALSE; + for (tmp = bll; tmp; tmp = tmp->next) + if ((tmp->state != Got) && (tmp->state != Skipped)) { + NotDone = TRUE; + break; + } + if (tmp == NULL) { + bp.TxState = TxDone; + binkp_send_control(MM_EOB, ""); + Syslog('b', "Binkp: sending EOB"); + } + break; + + case TxDone: + break; + } + + /* + * Process received frame + */ + if (bp.GotFrame) { + if (bp.cmd) { + switch (bp.rxbuf[0]) { + case MM_ERR: Syslog('+', "Binkp: got ERR: %s", bp.rxbuf+1); + bp.RxState = RxDone; + bp.TxState = TxDone; + bp.rc = MBERR_FTRANSFER; + break; + + case MM_BSY: Syslog('+', "Binkp: got BSY: %s", bp.rxbuf+1); + bp.RxState = RxDone; + bp.TxState = TxDone; + bp.rc = MBERR_FTRANSFER; + break; + + case MM_SKIP: Syslog('+', "Binkp: got SKIP: %s", bp.rxbuf+1); + break; + + case MM_GET: Syslog('+', "Binkp: got GET: %s", bp.rxbuf+1); + sscanf(bp.rxbuf+1, "%s %ld %ld %ld", bp.gname, &bp.gsize, &bp.gtime, &bp.goffset); + for (tmpg = bll; tmpg; tmpg = tmpg->next) { + if (strcasecmp(tmpg->remote, bp.gname) == 0) { + tmpg->state = NoState; + tmpg->offset = bp.goffset; + if (bp.goffset) { + Syslog('+', "Remote wants %s for resync at offset %ld", bp.gname, bp.goffset); + } else { + Syslog('+', "Remote wants %s again", bp.gname); + } + bp.TxState = TxGetNextFile; + } + } + break; + + case MM_GOT: Syslog('+', "Binkp: got GOT: %s", bp.rxbuf+1); + sscanf(bp.rxbuf+1, "%s %ld %ld", bp.lname, &bp.lsize, &bp.ltime); + Found = FALSE; + for (tmp = bll; tmp; tmp = tmp->next) + if ((strcmp(bp.lname, tmp->remote) == 0) && (bp.lsize == tmp->size) && + (bp.ltime == tmp->date)) { + Syslog('+', "Binkp: remote GOT \"%s\"", tmp->remote); + tmp->state = Got; + Found = TRUE; + } + if (!Found) { + Syslog('!', "Binkp: unexpected GOT \"%s\"", bp.rxbuf+1); + } + break; + + case MM_NUL: b_nul(bp.rxbuf+1); + break; + + case MM_EOB: Syslog('+', "Binkp: got EOB"); + bp.RxState = RxEndOfBatch; + break; + + case MM_FILE: Syslog('b', "Binkp: got FILE: %s", bp.rxbuf+1); + if (bp.RxState == RxReceData ) { + /* + * If we get a m_file command during receive, the current file is + * considered interrupted. Save the partial received file and accept + * the new file. After this file the transmitter should continue + * with the original file and this mailer should send m_get to set + * the offset to the point were we left off. + */ + Syslog('+', "Binkp: new file during receive, saving %s", bp.rname); + closefile(); + bp.rxfp = NULL; + bp.RxState = RxWaitFile; + } + if ((bp.RxState == RxWaitFile) || (bp.RxState == RxEndOfBatch)) { + bp.RxState = RxAcceptFile; + /* + * Check against buffer overflow + */ + if (strlen(bp.rxbuf) < 512) { + sscanf(bp.rxbuf+1, "%s %ld %ld %ld", bp.rname, &bp.rsize, &bp.rtime, &bp.roffs); + } else { + Syslog('+', "Binkp: got corrupted FILE frame, size %d bytes", strlen(bp.rxbuf)); + } + } else { + Syslog('+', "Binkp: got unexpected FILE frame %s", bp.rxbuf+1); + } + break; + + default: Syslog('+', "Binkp: Unexpected frame %d \"%s\"", bp.rxbuf[0], printable(bp.rxbuf+1, 0)); + } + } else { + if (bp.blklen) { + if (bp.RxState == RxReceData) { + written = fwrite(bp.rxbuf, 1, bp.blklen, bp.rxfp); + if (!written && bp.blklen) { + Syslog('+', "Binkp: file write error"); + bp.RxState = RxDone; + } + bp.rxpos += written; + if (bp.rxpos == bp.rsize) { + bp.RxState = RxWaitFile; + binkp_send_control(MM_GOT, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); + closefile(); + bp.rxpos = bp.rxpos - bp.rxbytes; + gettimeofday(&bp.rxtvend, &bp.tz); + Syslog('+', "Binkp: OK %s", transfertime(bp.rxtvstart, bp.rxtvend, bp.rxpos, FALSE)); + rcvdbytes += bp.rxpos; + bp.RxState = RxWaitFile; + transferred = TRUE; + } + } else { + if (!bp.DidSendGET) { + /* + * Do not log after a GET command, there will be data packets + * in the pipeline that must be ignored. + */ + Syslog('+', "Binkp: unexpected DATA frame %d", bp.rxbuf[0]); + } + } + } + } + bp.GotFrame = FALSE; + bp.rxlen = 0; + bp.header = 0; + bp.blklen = 0; + } + + /* + * Receiver state machine + */ + switch (bp.RxState) { + case RxWaitFile: + break; + + case RxAcceptFile: + Syslog('+', "Binkp: receive file \"%s\" date %s size %ld offset %ld", bp.rname, date(bp.rtime), bp.rsize, bp.roffs); + (void)binkp2unix(bp.rname); + bp.rxfp = openfile(binkp2unix(bp.rname), bp.rtime, bp.rsize, &bp.rxbytes, resync); + + if (bp.DidSendGET) { + /* + * The file was partly received, via the openfile the resync function + * has send a GET command to start this file with a offset. This means + * we will get a new FILE command to open this file with a offset. + */ + bp.RxState = RxWaitFile; + break; + } + + gettimeofday(&bp.rxtvstart, &bp.tz); + bp.rxpos = bp.roffs; + + if (!diskfree(CFG.freespace)) { + Syslog('+', "Binkp: low diskspace, sending BSY"); + binkp_send_control(MM_BSY, "Low diskspace, try again later"); + bp.RxState = RxDone; + bp.TxState = TxDone; + bp.rc = MBERR_FTRANSFER; + break; + } + + if (statfs(tempinbound, &sfs) == 0) { + Syslog('b', "blocksize %lu free blocks %lu", sfs.f_bsize, sfs.f_bfree); + Syslog('b', "need %lu blocks", (unsigned long)(bp.rsize / (sfs.f_bsize + 1))); + if ((bp.rsize / (sfs.f_bsize + 1)) >= sfs.f_bfree) { + Syslog('!', "Only %lu blocks free (need %lu) in %s", sfs.f_bfree, + (unsigned long)(bp.rsize / (sfs.f_bsize + 1)), tempinbound); + closefile(); + bp.rxfp = NULL; /* Force SKIP command */ + } + } + + if (bp.rsize == bp.rxbytes) { + /* + * We already got this file, send GOT so it will + * be deleted at the remote. + */ + Syslog('+', "Binkp: already got %s, sending GOT", bp.rname); + binkp_send_control(MM_GOT, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); + bp.RxState = RxWaitFile; + bp.rxfp = NULL; + } else if (!bp.rxfp) { + /* + * Some error, request to skip it + */ + Syslog('+', "Binkp: error file %s, sending SKIP", bp.rname); + binkp_send_control(MM_SKIP, "%s %ld %ld", bp.rname, bp.rsize, bp.rtime); + bp.RxState = RxWaitFile; + } else { + Syslog('b', "rsize=%d, rxbytes=%d, roffs=%d", bp.rsize, bp.rxbytes, bp.roffs); + bp.RxState = RxReceData; + } + break; + + case RxReceData: + break; + + case RxEndOfBatch: + if (bp.TxState == TxDone) + bp.RxState = RxDone; + break; + + case RxDone: + break; + } + } + + debug_binkp_list(&bll); + + /* + * Process all send files. + */ + for (tsl = to_send; tsl; tsl = tsl->next) { + if (tsl->remote == NULL) { + execute_disposition(tsl); + } else { + for (tmp = bll; tmp; tmp = tmp->next) { + if ((strcmp(tmp->local, tsl->local) == 0) && (tmp->state == Got)) { + execute_disposition(tsl); + } + } + } + } + + for (tmp = bll; bll; bll = tmp) { + tmp = bll->next; + if (bll->local) + free(bll->local); + if (bll->remote) + free(bll->remote); + free(bll); + } + + /* + * If there was an error, try to close a possible incomplete file in + * the temp inbound so we can resume the next time we have a session + * with this node. + */ + if (bp.rc) + closefile(); + + Syslog('+', "Binkp: batch %d completed rc=%d", bp.batchnr, bp.rc); + return bp.rc; +} + diff --git a/mbcico/binkpnew.h b/mbcico/binkpold.h similarity index 82% rename from mbcico/binkpnew.h rename to mbcico/binkpold.h index d9c638f5..52a6f9f4 100644 --- a/mbcico/binkpnew.h +++ b/mbcico/binkpold.h @@ -1,7 +1,5 @@ -#ifdef USE_NEWBINKP - -#ifndef _BINKPNEW_H -#define _BINKPNEW_H +#ifndef _BINKP_H +#define _BINKP_H /* $Id$ */ @@ -23,8 +21,7 @@ /* protocol version */ #define PRTCLNAME "binkp" -#define PRTCLVER "1.1" -#define PRTCLOLD "1.0" +#define PRTCLVER "1.0" #define MAX_BLKSIZE 0x7fff /* Don't change! */ #define BLK_HDR_SIZE 2 /* 2 bytes header */ @@ -61,6 +58,8 @@ typedef struct _binkp_frame { /* * Linked list of files to send and responses from the receiver. */ +typedef enum {NoState, Sending, IsSent, Got, Skipped, Get} FileState; + typedef struct _binkp_list { struct _binkp_list *next; char *remote; /* Remote filename */ @@ -73,20 +72,6 @@ typedef struct _binkp_list { } binkp_list; - -/* - * Linked FIFO list of received commands to be processed by the transmitter. - */ -typedef struct _the_queue { - struct _the_queue *next; - int cmd; /* M_xxx command id */ - char *data; /* Frame data in the queue */ -} the_queue; - - - int binkp(int); #endif - -#endif diff --git a/mbcico/session.c b/mbcico/session.c index 8f9cf3a7..56efb8ee 100644 --- a/mbcico/session.c +++ b/mbcico/session.c @@ -44,11 +44,7 @@ #include "session.h" #include "yoohoo.h" #include "mbcico.h" -#ifdef USE_NEWBINKP -#include "binkpnew.h" -#else #include "binkp.h" -#endif #include "callstat.h" #include "inbound.h" #include "opentcp.h"