/* $Header: /netrcs/RCS/oracle/network/tns/tnsapi/RCS/sendrecv.c,v 1.3 1995/09/12 22:43:28 yzheng Exp $ */ #include "tftpdef.h" #include #include extern FILE *file_open(); extern void file_close(); extern int file_read(); extern int file_write(); static void send_ERROR(); #ifdef CLIENT /* * Send a RRQ or WRQ to the other side. * These two packets are only sent by the client to the server. */ void send_RQ(opcode, fname, mode) int opcode; /* RRQ or WRQ */ char *fname; int mode; { register int len; char *modestr; DEBUG2("sending RRQ/WRQ for %s, mode =%d", fname, mode); stshort(opcode, sendbuff); strcpy(sendbuff+2, fname); len = 2 + strlen(fname) + 1; /* 1 for null byte at the end of fname */ switch (mode) { case MODE_ASCII: modestr="netascii"; break; case MODE_BINARY: modestr="octet"; break; default: err_dump("unknown mode"); } strcpy(sendbuff + len, modestr); len +=strlen(modestr) + 1; sendlen = len; net_send(sendbuff, sendlen); op_sent = opcode; } /* * Error packet received in response to an RRQ or WRQ. * Usually means the file we are asking for on the other system * can't be accessed for some reason. We need to print the error * message that is returned. * called by finite state machine */ int recv_RQERR(ptr, nbytes) char *ptr; /* points just past received opcode */ int nbytes; /* does not include received opcode */ { register int ecode; ecode = ldshort(ptr); ptr += 2; nbytes -= 2; ptr[nbytes] = 0; DEBUG2("ERROR received, %d bytes, error code %d", nbytes, ecode); fflush(stdout); fprintf(stderr, "Error# %d: %s\n", ecode, ptr); fflush(stderr); return(-1); } /* * Send a disconnect to the other side to inform it to disconnect */ void send_DISRQ() { stshort(OP_DISCONNECT, sendbuff); sendlen = 2; net_send(sendbuff, sendlen); } #endif /* CLIENT */ /* * Send data to the other side * The data must be stored in the "sendbuff" by the caller * called by recv_ACK() */ void send_DATA(blocknum, nbytes) int blocknum; int nbytes; /* #bytes of actual data to send */ { DEBUG2("sending %d bytes of DATA with block# %d", nbytes, blocknum); stshort(OP_DATA, sendbuff); stshort(blocknum, sendbuff+2); sendlen = nbytes + 4; net_send(sendbuff, sendlen); op_sent = OP_DATA; } /* * Send an acknowledgment packet to the other side * called by recv_DATA) and recv_WRQ() */ void send_ACK(blocknum) int blocknum; { DEBUG1("sending ACK for block # %d", blocknum); stshort(OP_ACK, sendbuff); stshort(blocknum, sendbuff+2); sendlen = 4; net_send(sendbuff, sendlen); op_sent = OP_ACK; } /* * Data packet received. Send an acknowledgment. * called by finite state machine. * This fucntion is called by both client and server */ int recv_DATA(ptr, nbytes) char *ptr; /* points just past received opcode */ int nbytes; /* does not include received opcode */ { register int recvblknum; recvblknum = ldshort(ptr); ptr += 2; nbytes -= 2; DEBUG2("data received, %d bytes, block# %d", nbytes, recvblknum); if (nbytes > MAXDATA) { err_dump("data packet received with length = %d bytes", nbytes); } if (recvblknum == nextblknum) { /* * The data packet is the expected one. Increase our expected-block# for * the next packet. */ nextblknum++; totnbytes += nbytes; if (nbytes > 0) { /* * The final data packet can have a data length of zero, so we only * write the data to the local file if there is data. */ file_write(localfp, ptr, nbytes, modetype); } send_ACK(recvblknum); #ifdef SERVER /* * if the length of the data is between 0-511, this is the last data * block. For the server, here is where we have to close the file. * For the client, the "get" command processing will close the file. */ if (nbytes < MAXDATA) { file_close(localfp); op_sent = 0; /* need to reset it for next operation */ } #endif } else { /* something is wrong */ err_dump("data block# and ACK block# mismatch"); } /* * If the length of the data is between 0-511, we have just received * the final data packet, else there is more to come */ /* * For server, it has to stay in fsm_loop() to wait for new incoming * request, for client, it should exit fsm_loop() */ #ifdef SERVER return (0); #else /* client */ return( (nbytes == MAXDATA) ? 0 : -1); #endif } /* * ACK packet received. Send some more data. * Called by finite state machine. Also called by recv_RRQ() to start * the transmission of a file to the client. * This function is called by both the client and the server. */ int recv_ACK(ptr, nbytes) char *ptr; /* pointer to just past received code */ int nbytes; /* does not include received opcode */ { register int recvblknum; recvblknum = ldshort(ptr); if (nbytes != 2) err_dump("ACK packet received with length = %d bytes", nbytes + 2); DEBUG1("ACK received, block# %d", recvblknum); if (recvblknum == nextblknum) { /* * The received acknowledgement is for the expected data packet that * we sent. * Fill the transmit buffer with the next block of data to send. * If there is not more data to send, then we might be finished. * Note that we must send a final data packet containing 0-511 * bytes of data. If the length of the last packet that we sent * was exactly 512 bytes, there we must send a 0-length data packet. */ if (( nbytes = file_read(localfp, sendbuff+4, MAXDATA, modetype)) == 0) { if (lastsend < MAXDATA) { /* * This is the last data packet sent on the server side, server needs to * close the file. Server returns 0 so it will loop in fsm_loop() to wait * for new incoming request. */ #ifdef SERVER file_close(localfp); op_sent = 0; /* reset op_sent for server */ return(0); #else /* client */ return(-1); /* done */ #endif } } lastsend = nbytes; nextblknum++; totnbytes +=nbytes; send_DATA(nextblknum, nbytes); return(0); } else if (recvblknum == (nextblknum - 1)) { /* * We have received a duplicate ACK. This means either * (1) the other side never received our last data packet * (2) the other side's ACK got delayed somehow. * * Ignore the ACK, do NOT retransmit the data packet * * This should not happen in a reliable transport */ return(0); } else { err_dump("data block # and ACK block # mismatch"); } } #ifdef SERVER /* * RRQ received */ int recv_RRQ(ptr, nbytes) char *ptr; int nbytes; { char ackbuff[2]; recv_xRQ(OP_RRQ, ptr, nbytes); /* * set things up so we can just call recv_ACK() and pretend we * have received an ACK, so it will send the first data block to * the client. */ lastsend = MAXDATA; stshort(0, ackbuff); /* pretend it is an ACK of block #0 */ recv_ACK(ackbuff, 2); /* this sends data block #1 */ return(0); /* the state machine takes over from here */ } /* * WRQ received */ int recv_WRQ(ptr, nbytes) char *ptr; int nbytes; { recv_xRQ(OP_WRQ, ptr, nbytes); /* * call send_ACK() to acknowledge block #0, which will cause the client * to send data block #1. */ nextblknum = 1; send_ACK(0); return(0); /* the state machine takes over from here */ } /* * Process the RRQ or WRQ received */ int recv_xRQ(opcode, ptr, nbytes) int opcode; /* RRQ or WRQ */ register char *ptr; /* points just past received opcode */ int nbytes; /* does not include received opcode */ { register int i; register char *saveptr; char filename[MAXFILENAME], dirname[MAXFILENAME], mode[MAXFILENAME]; struct stat statbuff; /* * Assume the file name and mode are present and null-terminated */ saveptr = ptr; /* points to the beginning of file name */ for (i = 0; i < nbytes; i++) if (*ptr++ == '\0') goto FileOK; err_dump("Invalid filename"); FileOK: strcpy(filename, saveptr); saveptr = ptr; for ( ; i < nbytes; i++) if (*ptr++ == '\0') goto ModeOK; err_dump("Invalid Mode"); ModeOK: strlccpy(mode, saveptr); if (strcmp(mode, "netascii") == 0) modetype = MODE_ASCII; else if (strcmp(mode, "octet") == 0) modetype = MODE_BINARY; else send_ERROR(ERR_BADOP, "Mode is not netascii or octet"); /* * validate file name. File must have permission to be publicly accessed * Filename must have its full pathname specified, i.e., it must begin * with a slash */ #ifndef WIN32 if (filename[0] != '/') send_ERROR(ERR_ACCESS, "filename must begin with a '/'"); #endif if (opcode == OP_RRQ) { /* * read request - verify that the file exists and it has world * read permission */ if (stat(filename, &statbuff) < 0) send_ERROR(ERR_ACCESS, sys_err_str()); if ((statbuff.st_mode & (S_IREAD >> 6)) == 0 ) send_ERROR(ERR_ACCESS, "File does not allow world read permission"); } else if ( opcode == OP_WRQ) { /* * write request - verify that the directory that the file is being * written to has world write permission. */ char *strrchr(); strcpy(dirname, filename); #ifdef WIN32 *(strrchr(dirname, '\\') + 1) = '\0'; #else *(strrchr(dirname, '/') + 1) = '\0'; #endif if (stat(dirname, &statbuff) < 0) send_ERROR(ERR_ACCESS, sys_err_str()); if((statbuff.st_mode & (S_IWRITE >> 6)) == 0) send_ERROR(ERR_ACCESS, "Directory does not have world write permission"); } else err_dump("Unknown request mode"); #ifdef WIN32 if( modetype == MODE_ASCII ) localfp = file_open(filename, (opcode == OP_RRQ) ? "r" : "w",0); else localfp = file_open(filename, (opcode == OP_RRQ) ? "rb" : "wb",0); #else localfp = file_open(filename, (opcode == OP_RRQ) ? "r" : "w",0); #endif if (localfp == NULL) send_ERROR(ERR_NOFILE, sys_err_str()); } /* * Send an error packet. */ static void send_ERROR(ecode, string) int ecode; /* error code, ERR_XXX from tftpdef.h */ char *string; /* error message */ { DEBUG2("sending ERROR, code = %d, string = %s", ecode, string); stshort(OP_ERROR, sendbuff); stshort(ecode, sendbuff+2); strcpy(sendbuff+4, string); sendlen = 4 + strlen(sendbuff+4) + 1; /* 1 for null-termination */ net_send(sendbuff, sendlen); } /* * Copy a string and convert it to lower case */ strlccpy(dest, src) register char *dest, *src; { register char c; while ( (c = *src++) != '\0') { if (isupper(c)) c = tolower(c); *dest++ = c; } *dest = '\0'; } #endif /* SERVER */