/* ** QUICKB.C - Quick B Protocol Support routines ** ** converted to C by Paul M. Resch ** adapted to LiteComm(tm) ToolBox by Information Technology, Ltd. */ /* ** This module implements the B-Protocol Functions. ** ** bp_DLE should be invoked whenever a is received. ** bp_ENQ should be called whenever an is received. ** bp_ESC_I should be called when the sequence is received. ** ** This source was originally derived from QUICKB.INC, written by ** Russ Ranshaw, CompuServe Incorporated. ** */ #include #ifdef M_I86 #include #include #include #include #include #endif #ifdef __TURBOC__ #include #include #include #include #endif #include "litecomm.h" #include "litexm.h" #include "cxlwin.h" extern unsigned int port; /* defined in TTL main */ #define TRUE 1 #define FALSE 0 #undef DLE #undef NAK #define DLE 16 #define ETX 03 #define NAK 21 #define ENQ 05 #define CR 0x0D #define LF 0x0A #define MAX_BUF_SIZE 1032 /* Largest data block we can handle */ #define MAX_SA 2 /* Maximum number of waiting packets */ #define DEF_BUF_SIZE 511 /* Default data block */ #define DEF_WS 1 /* I can send 2 packets ahead */ #define DEF_WR 1 /* I can receive single send-ahead */ #define DEF_BS 8 /* I can handle 1024 bytes */ #define DEF_CM 1 /* I can handle CRC */ #define DEF_DQ 1 /* I can handle non-quoted NUL */ #define MAX_ERRORS 10 #define incr_seq(v) (v == 9 ? 0 : v+1) /* macro to incr seq number */ #define incr_SA(v) (v == MAX_SA ? 0 : v + 1 ) #define send_enq() (lc_put(port, ENQ)) /* ** Receive States */ #define R_GET_DLE 0 #define R_GET_B 1 #define R_GET_SEQ 2 #define R_GET_DATA 3 #define R_GET_CHECKSUM 4 #define R_SEND_ACK 5 #define R_TIMED_OUT 6 #define R_SUCCESS 7 /* ** Send States */ #define S_GET_DLE 1 #define S_GET_NUM 2 #define S_HAVE_ACK 3 #define S_GET_PACKET 4 #define S_TIMED_OUT 5 #define S_SEND_NAK 6 #define S_SEND_ENQ 7 #define S_SEND_DATA 8 typedef struct PACKETB { int seq; /* Packet's sequence number */ int num; /* Number of bytes in packet */ unsigned char buf[MAX_BUF_SIZE]; /* Actual packet data */ } PACKET; static PACKET SA_Buf[MAX_SA+1]; /* Send-ahead buffers */ /* ** Table of control characters that need to be masked */ static char mask_table[] = { 0, 0, 0, 1, 0, 1, 0, 0, /* NUL SOH SOB ETX EOT ENQ SYN BEL */ 0, 0, 0, 0, 0, 0, 0, 0, /* BS HT LF VT FF CR SO SI */ 1, 1, 0, 1, 0, 1, 0, 0, /* DLE DC1 DC2 DC3 DC4 NAK ^V ^W */ 0, 0, 0, 0, 0, 0, 0, 0 /* CAN ^Y ^Z ESC ? ? ? ? */ }; static char hex_digit[] = "0123456789ABCDEF"; static int seq_num; /* Current Sequence Number */ static int lchecksm; /* May hold CRC */ static int r_size; /* size of receiver buffer */ static unsigned int s_counter, r_counter; static int timed_out; /* we timed out before receiving */ static int cchar; /* current character */ static int masked; /* TRUE if ctrl character 'masked' */ static int packet_received; /* True if a packet was received */ static unsigned char r_buffer[MAX_BUF_SIZE]; /* ** Other End's Parameters */ static char His_WS; /* Sender's Window Send */ static char His_WR; /* Sender's Window Receive */ static char His_BS; /* Sender's Block Size */ static char His_CM; /* Sender's Check Method */ /* ** Negotiated Parameters */ static char Our_WS; /* Negotiated Window Send */ static char Our_WR; /* Negotiated Window Receive */ static char Our_BS; /* Negotiated Block Size */ static char Our_CM; /* Negotiated Check Method */ static int Quick_B; /* True if Quick B in effect */ static int Use_CRC; /* True if CRC in effect */ static int buffer_size; /* Our_BS * 4 */ static int SA_Max; /* 1 if SA not enabled, else MAX_SA */ static int SA_Enabled; /* True if Send-Ahead is permitted */ static int ack_SA; /* Which SA_Buf is waiting for ACK */ static int fill_SA; /* Which SA_Buf is ready, new data */ static int SA_Waiting; /* Num of SA_Buf's waiting for ACK */ static int blkct; /* block counter for display */ extern char strbuf[]; /* defined in qbttl */ static void do_transport_parameters(void); static int send_packet(); static int SA_Flush(); /* ** crc ** ** Calculates XMODEM-style CRC (uses the CCITT V.41 polynomial but ** completely backwards from the normal bit ordering). */ static unsigned crc_table[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 }; static unsigned int crc_16; /* ** Upd_CRC updates crc_16 and returns the updated value. */ static unsigned int upd_CRC (value) unsigned int value; { crc_16 = crc_table [((crc_16 >> 8) ^ (value)) & 0xff] ^ (crc_16 << 8); return( crc_16 ); } /* ** Update the checksum/CRC */ static void do_checksum(c) int c; { if (Quick_B && Use_CRC) lchecksm = upd_CRC (c); else { lchecksm = lchecksm << 1; if (lchecksm > 255) lchecksm = (lchecksm & 0xFF) + 1; lchecksm = lchecksm + c; if (lchecksm > 255) lchecksm = (lchecksm & 0xFF) + 1; } } static void send_failure( code ) int code; { register PACKET *p; ack_SA = 0; fill_SA = 0; SA_Waiting = 0; p = &SA_Buf [0]; p->buf[0] = 'F'; p->buf[1] = code; if ( send_packet (1)) SA_Flush(); /* Gotta wait for the host to ACK it */ } /* ** bp_ENQ is called when the terminal emulator receives the character ** from the host. Its purpose is to initialize for B Protocol and tell the ** host that we support Quick B. */ void bp_ENQ() { seq_num = 0; buffer_size = 511; /* Set up defaults */ Quick_B = FALSE; /* Not Quick B Protocol */ Use_CRC = FALSE; /* Not CRC_16 */ SA_Enabled = FALSE; /* No Send-Ahead by us */ SA_Max = 1; /* = single packet sent */ lc_put (port, DLE); lc_put (port, '+'); lc_put (port, DLE); lc_put (port, '0'); } /* ** bp_ESC_I is called when is received by the terminal emulator. ** Note that Quick B allows +XX to be added to the end of the response, where ** XX is the two hex digits of the standard B Protocol checksum of the ** preceeding characters in the response. The Qbttl program also supports ** standard VIDTEX cursor control. */ static char esc_I_response[] = "#VCO,CC,PB,DT,+"; void bp_ESC_I() { int save_Use_CRC; register int i; save_Use_CRC = Use_CRC; Use_CRC = FALSE; lchecksm = 0; i = 0; while( esc_I_response[i] ) { lc_put(port, esc_I_response [i]); do_checksum (esc_I_response [i]); i++; } /* ** Append two hex digits of checksum to response */ lc_put(port, hex_digit[ (lchecksm >> 4) & 0x0F ]); lc_put(port, hex_digit[ lchecksm & 0x0F ] ); lc_put(port, CR); Use_CRC = save_Use_CRC; while (lc_ocnt(port) > 0) ; } static void send_masked_byte(c) int c; { c = c & 0xFF; if (c < 32) { if (mask_table [c] != 0) { lc_put(port, DLE); lc_put(port, c + '@'); } else lc_put(port, c); } else lc_put(port, c); s_counter = (s_counter + 1) % 512; } static void send_ack() { lc_put(port, DLE); lc_put(port, seq_num + '0'); } static int read_byte() { timed_out = FALSE; cchar = wait(port, 10, 0x00); if (cchar < 0 ) return( FALSE ); r_counter = (r_counter + 1) % 512; return( TRUE ); } static int read_masked_byte() { masked = FALSE; if (read_byte() == FALSE) return( FALSE ); if (cchar == DLE) { if (read_byte() == FALSE) return( FALSE ); cchar &= 0x1F; masked = TRUE; } return( TRUE ); } static int read_packet (lead_in_seen, from_send_packet) int lead_in_seen, from_send_packet; /* ** Lead_in_Seen is TRUE if the has been seen already. ** from_send_packet is TRUE if called from Send_Packet ** (causes exit on first error detected) ** ** Returns True if packet is available from host. */ { int State, next_seq, block_num, errors, new_cks; int i; packet_received = FALSE; for(i=0; i MAX_ERRORS) || (from_send_packet)) return( FALSE ); lc_put(port, NAK); if (from_send_packet) return( FALSE ); State = R_GET_DLE; break; case R_SEND_ACK : send_ack(); State = R_GET_DLE; /* wait for the next block */ break; case R_SUCCESS : seq_num = block_num; r_size = i; packet_received = TRUE; return( TRUE ); } } } static void send_data (Buffer_Number) int Buffer_Number; { int i; register PACKET *p; s_counter = 0; p = &SA_Buf [Buffer_Number]; if (Quick_B && Use_CRC) lchecksm = crc_16 = -1; else lchecksm = 0; lc_put(port, DLE); lc_put(port, 'B'); lc_put(port, p->seq + '0'); do_checksum(p->seq + '0'); for (i = 0; i<=p->num; i++ ) { send_masked_byte(p->buf[i]); do_checksum(p->buf[i]); } lc_put(port, ETX); do_checksum (ETX); if (Quick_B && Use_CRC) send_masked_byte (lchecksm >> 8); send_masked_byte(lchecksm); } /* ** ReSync is called to restablish syncronism with the remote. This is ** accomplished by sending and waiting for the sequence ** to be received, ignoring everything else. ** ** Return is -1 on time out, else the digit . */ #define GET_FIRST_DLE 1 #define GET_FIRST_DIGIT 2 #define GET_SECOND_DLE 3 #define GET_SECOND_DIGIT 4 static int ReSync() { int State, Digit_1; lc_put(port, ENQ); /* Send */ lc_put(port, ENQ); State = GET_FIRST_DLE; while(1) { switch (State) { case GET_FIRST_DLE : if( !read_byte() ) return( -1 ); if( cchar == DLE ) State = GET_FIRST_DIGIT; break; case GET_FIRST_DIGIT : if( !read_byte() ) return( -1 ); if( (cchar >= '0') && (cchar <= '9') ) { Digit_1 = cchar; State = GET_SECOND_DLE; } break; case GET_SECOND_DLE : if( !read_byte() ) return( -1 ); if( cchar == DLE ) State = GET_SECOND_DIGIT; break; case GET_SECOND_DIGIT : if( !read_byte() ) return( -1 ); if( (cchar >= '0') && (cchar <= '9') ) { if( Digit_1 == cchar ) return( cchar ); else { Digit_1 = cchar; State = GET_SECOND_DLE; } } else State = GET_SECOND_DLE; break; } } } /* ** get_ACK is called to wait until the SA_Buf indicated by ack_SA ** has been ACKed by the host. */ static int get_ACK() { int State, errors, block_num, i; int Sent_ENQ; int SA_Index; packet_received = FALSE; errors = 0; Sent_ENQ = FALSE; State = S_GET_DLE; while( TRUE ) { switch (State) { case S_GET_DLE : /* if (_abort_flag) { send_failure ('A'); return( FALSE ); } */ if (!read_byte()) State = S_TIMED_OUT; else if (cchar == DLE) State = S_GET_NUM; else if (cchar == NAK) { if (++errors > MAX_ERRORS) return( FALSE ); State = S_SEND_ENQ; } else if (cchar == ETX) State = S_SEND_NAK; break; case S_GET_NUM : if (!read_byte()) State = S_TIMED_OUT; else if ((cchar >= '0') && (cchar <= '9')) State = S_HAVE_ACK; /* Received ACK */ else if (cchar == 'B') State = S_GET_PACKET; /* Try to get packet */ else if (cchar == NAK) { if (++errors > MAX_ERRORS) return( FALSE ); State = S_SEND_ENQ; } else State = S_TIMED_OUT; break; case S_GET_PACKET : if (read_packet (TRUE, TRUE)) { if (r_buffer [0] == 'F') { send_ack(); return( FALSE ); } else return( TRUE ); } State = S_TIMED_OUT; /* On a bad receive, try again */ break; case S_HAVE_ACK: block_num = cchar - '0'; if (SA_Buf [ack_SA].seq == block_num) { /* This is the one we're waiting for */ ack_SA = incr_SA(ack_SA); SA_Waiting--; return( TRUE ); } else if (SA_Buf [incr_SA (ack_SA)].seq == block_num) { /* Must have missed an ACK */ ack_SA = incr_SA (ack_SA); ack_SA = incr_SA (ack_SA); SA_Waiting -= 2; return( TRUE ); } else if (SA_Buf [ack_SA].seq == incr_seq (block_num)) { if( Sent_ENQ ) State = S_SEND_DATA; else State = S_GET_DLE; } else State = S_TIMED_OUT; Sent_ENQ = FALSE; break; case S_TIMED_OUT : if (++errors > MAX_ERRORS) return( FALSE ); State = S_SEND_ENQ; break; case S_SEND_NAK : if (++errors > MAX_ERRORS) return( FALSE ); lc_put(port, NAK); State = S_GET_DLE; break; case S_SEND_ENQ : if (++errors > MAX_ERRORS) return( FALSE ); cchar = ReSync(); if( cchar == -1 ) State = S_SEND_ENQ; else State = S_HAVE_ACK; Sent_ENQ = TRUE; break; case S_SEND_DATA : SA_Index = ack_SA; for (i = 1; i<=SA_Waiting; i++ ) { send_data (SA_Index); SA_Index = incr_SA (SA_Index); } State = S_GET_DLE; Sent_ENQ = FALSE; break; } } } /* get_ACK */ static int send_packet (size) int size; { if (SA_Waiting == SA_Max) if (!get_ACK()) return( FALSE ); seq_num = incr_seq (seq_num); SA_Buf [fill_SA].seq = seq_num; SA_Buf [fill_SA].num = size; send_data (fill_SA); fill_SA = incr_SA (fill_SA); SA_Waiting = SA_Waiting + 1; return( TRUE ); } /* ** SA_Flush is called after sending the last packet to get host's ** ACKs on outstanding packets. */ static int SA_Flush() { while( SA_Waiting != 0 ) if (!get_ACK()) return( FALSE ); return( TRUE ); } /* Send_File is called to send a file to the host */ static int send_file(name) char name[]; { int fd; int n; register PACKET *p; fd = open(name, (O_BINARY | O_RDONLY)); if (fd < 0) { send_failure('E'); wperror("** Cannot find that file **"); return( FALSE ); } do { p = &SA_Buf [fill_SA]; p->buf[0] = 'N'; n = read(fd, &p->buf[1], buffer_size); if (n > 0) { if (send_packet (n) == FALSE) return( FALSE ); wgotoxy(4, 1); wprintf("Sent Block: %d", blkct++); } } while( n == buffer_size ); close (fd); if (n < 0) { send_failure ('E'); wperror("** Read failure...aborting **"); return(FALSE); } /* Inform host that the file was sent */ p = &SA_Buf [fill_SA]; p->buf[0] = 'T'; p->buf[1] = 'C'; if (send_packet(2) == FALSE) return( FALSE ); else { wputs( "Waiting for host..." ); if (!SA_Flush()) return( FALSE ); return( TRUE ); } } /* ** do_transport_parameters is called when a Packet type of + is received. ** It sends a packet of our local Quick B parameters and sets the Our_xx ** parameters to the minimum of the sender's and our own parameters. */ static void do_transport_parameters() { register PACKET *p; His_WS = r_buffer [1]; /* Pick out Sender's parameters */ His_WR = r_buffer [2]; His_BS = r_buffer [3]; His_CM = r_buffer [4]; p = &SA_Buf [fill_SA]; p->buf [0] = '+'; /* Prepare to return our own parameters */ p->buf [1] = DEF_WS; p->buf [2] = DEF_WR; p->buf [3] = DEF_BS; p->buf [4] = DEF_CM; p->buf [5] = DEF_DQ; if (!send_packet (5)) return; if (SA_Flush()) /* Wait for host's ACK on our packet */ { /* Take minimal subset of Transport Params. */ /* If he can send ahead, we can receive it. */ Our_WR = (His_WS < DEF_WR) ? His_WS : DEF_WR; /* If he can receive send ahead, we can send it. */ Our_WS = (His_WR < DEF_WS) ? His_WR : DEF_WS; Our_BS = His_BS < DEF_BS ? His_BS : DEF_BS; Our_CM = His_CM < DEF_CM ? His_CM : DEF_CM; if (Our_BS == 0) Our_BS = 4; /* Default */ buffer_size = Our_BS * 128; Quick_B = TRUE; if (Our_CM == 1) Use_CRC = TRUE; if (Our_WS != 0) { SA_Enabled = TRUE; SA_Max = MAX_SA; } } } /* do_application_parameters is called when a ? packet is received. This version ignores the host's packet and returns a ? packet saying that normal B Protocol File Transfer is supported. (Well, actually it says that no extended application packets are supported. The T packet is assumed to be standard.) */ static void do_application_parameters() { register PACKET *p; p = &SA_Buf [fill_SA]; p->buf[0] = '?'; /* Build the ? packet */ p->buf[1] = 1; /* The T packet flag */ if (send_packet (1)) /* Send the packet */ SA_Flush(); } /* Receive_File is called to receive a file from the host */ static int receive_file (name) char name[]; { int fd; unsigned bytes; _fmode = O_BINARY; fd = creat(name,(S_IREAD | S_IWRITE) ); if (fd < 0) { wperror("** Cannot open file...aborting **"); send_failure('E'); return( FALSE ); } send_ack(); /* Process each incoming packet until 'TC' packet received or failure */ while( TRUE ) { if (read_packet (FALSE, FALSE)) { switch (r_buffer[0]) { case 'N' : bytes = r_size - 1; if (write(fd, &r_buffer[1], bytes) != bytes ) { wperror("** Write failure...aborting **"); close (fd); send_failure ('E'); return( FALSE ); } send_ack(); wgotoxy(4,1); wprintf("Received Block: %d", blkct++); break; case 'T' : if (r_buffer[1] == 'C') { close(fd); send_ack(); return( TRUE ); } else { wperror("** Invalid termination packet...aborting **"); close (fd); send_failure ('N'); return( FALSE ); } case 'F' : send_ack(); wperror("** Failure packet received...aborting **"); close (fd); return( FALSE ); } } else { wperror("** Failed to receive packet...aborting **"); close (fd); return( FALSE ); } } } /* ** bp_DLE is called from the main program when the character is ** received from the host. ** ** This routine calls read_packet and dispatches to the appropriate ** handler for the incoming packet. */ void bp_DLE() { int i; int j; char filename[255]; char str[2]; /* ** Begin by getting the next character. If it is then enter the ** B_Protocol state. Otherwise simply return. */ j = wait(port, 10, 0x00); if (j != 'B') return; strcpy( str, " " ); ack_SA = 0; /* Initialize Send-ahead variables */ fill_SA = 0; SA_Waiting = 0; blkct = 0; /* received; begin B Protocol */ r_counter = 0; s_counter = 0; if (Quick_B) { wputs ("*** Quick B is in effect ***\n"); if (Use_CRC) wputs ("*** Using CRC ***\n"); if (Our_WS != 0) /* Allow send-ahead if other end agrees */ wputs ("*** Send-Ahead enabled ***\n"); } if (read_packet (TRUE, FALSE)) { /* Dispatch on the type of packet just received */ switch (r_buffer[0]) { case 'T': /* File Transfer Application */ switch (r_buffer[1]) { case 'D' : /* downloading */ break; case 'U' : /* uploading */ break; default : send_failure('N'); return; } switch (r_buffer[2]) { case 'A': /* ascii file */ break; case 'B': /* binary file */ break; default : send_failure('N'); /* not implemented */ return; } i = 2; strcpy( filename, "" ); while( (r_buffer[i] != 0) && (i < r_size) ) { i = i + 1; str[0] = r_buffer[i]; strcat( filename, str ); } if (r_buffer[1] == 'U') { if( send_file(filename) ) wperror("Transfer completed!" ); } else { if( receive_file(filename) ) wperror("Transfer completed!" ); } break; case '+': /* Received Transport Parameters Packet */ do_transport_parameters(); break; case '?': /* Received Application Parameters Packet */ do_application_parameters(); break; default: /* Unknown packet; tell host we don't know */ send_failure ('N'); break; } /* of case */ } /* of if read_packet the */ }