www.pudn.com > tftp.rar > TFTP_SRV.C
/* ** This product contains: ** "Restricted Materials of IBM" ** (c) Copyright IBM Corp. 1987 ** All Rights Reserved ** Licensed Materials-Property of IBM ** ** See Copyright Instructions, G120-2083 ** */ /* Copyright 1984 by the Massachusetts Institute of Technology */ /* See permission and disclaimer notice in file "notice.h" */ #include/* (c) 1986 Copyright IBM Corp.; See file for permissions */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tftp.h" /* the tftp server code */ #define OFF 0 #define ON 1 #define MAXTFTPS 1 UDPCONN tftp; int tfsread(), tfswrit(); /* Forward declarations */ static unsigned numtftps = 1; extern int NBUF; int tfshnd(); int (*tfs_alert)(); int (*tfs_done)(); int tfs_writeEOF; int maxbufsize; int ntftps = 0; int tfstate = OFF; long refusedt = 0; /* time of most recent transfer refusal */ /* tfsinit( alert, done, spool_flag ) - initialize the tftp server. This opens a UDP connection, but does not turn on the server. That needs to done by an explicit call to tfs_on(). alert() is a function which the server will call whenever it receives request for a transfer. This function will be called int he following way: alert(ip_addr, file_name, direction) alert() should return TRUE if it wishes to allow the transfer and FALSE otherwise. done() is a function that the server will call to inform the invoker that this file transfer is complete or aborted. */ tfsinit(alert, done, spool_flag, writeEOF) int (*alert)(); int (*done)(); int spool_flag; /* non-zero if no buffering wanted. */ int writeEOF; { tftp = udp_open(0L, 0, TFTPSOCK, tfshnd, 0); if(tftp == NULL) { printf("TFTP server: couldn't open udp connection\n"); exit(); } maxbufsize = (spool_flag ? 1 : MAXBUFSIZE); tfs_done = done; tfs_alert = alert; tfs_writeEOF = writeEOF; } tfsclose() { udp_close( tftp ); } /* turn the tftp server on */ tfs_on() { tfstate = ON; } /* turn the tftp server off */ tfs_off() { tfstate = OFF; } /* Handle an incoming tftp packet. This involves opening a udp connection (immediately so that we can report errors). If the server is OFF then the tftp will be refused; otherwise more checking will be done. Call the alert function and verify that the "user" wishes to allow the tftp. Report an error if not. Finally, spawn a task to oversee the tftp and cleanup when it's done. */ tfshnd( p, len, host ) PACKET p; unsigned len; in_name host; { unsigned nport; struct tfreq *ptreq; register struct tfconn *cn; char *file, *smode, *tmp; unsigned mode; struct udp *pup = udp_head(in_head(p)); UDPCONN tmpudp; /* If there is already a connection like this one, ignore duplicate request. */ if(udp_ckcon(host, pup->ud_srcp)) { if(NDEBUG&APTRACE) printf("TFTP Server: Ignoring probable duplicate request\n"); udp_free(p); return; } /* If we refused a connection since this request got enqueued, assume this is a duplicate and discard it, so we don't bother the user with a duplicate question. (If we are unlucky, this might be a request from somewhere else that arrived while the user was thinking over the previous request. */ if (refusedt > p->nb_tstamp) { if(NDEBUG&APTRACE) printf("TFTP Serve: Ignoring enqueued request\n"); udp_free(p); return; } /* The next question: Do we have room to do this transfer? */ if ( ntftps >= MAXTFTPS ) { if(NDEBUG&APTRACE) printf("TFTP Serve: Ignoring req, too many connections\n"); tfrpyerr( tftp, p, ERRTXT, "Too many connections." ); udp_free(p); return; } ntftps++; /* OK, let's check over the request more carefully. */ ptreq = (struct tfreq *) tftp_head( p ); ptreq->tf_op = bswap( ptreq->tf_op ); if ( ptreq->tf_op > WRQ ) { #ifdef DEBUG if(NDEBUG & INFOMSG|NETERR|PROTERR) printf("TFTP Server: bad tftp opcode %u\n", ptreq->tf_op); #endif udp_free(p); ntftps--; return; } file = ptreq->tf_name; smode = file+strlen(file)+1; /* Find transfer mode */ for(tmp = smode; *tmp; tmp++) /* ... and convert to lower case */ if(*tmp >= 'A' && *tmp <= 'Z') *tmp += 32; if(strcmp(smode, "image") == 0) mode = OCTET; else if(strcmp(smode, "octet") == 0) mode = OCTET; else if(strcmp(smode, "netascii") == 0) mode = ASCII; else { #ifdef DEBUG if(NDEBUG & INFOMSG|NETERR|PROTERR) printf("TFTP Server: Bad mode %s in req\n", smode); #endif tfrpyerr(tftp, p, ERRTXT, "Bad mode"); udp_free(p); ntftps--; return; } if(tfstate == OFF) { tfrpyerr(tftp, p, ERRTXT,"Transfers are not being accepted."); udp_free(p); ntftps--; return; } if((*tfs_alert)(host, file, ptreq->tf_op == RRQ ? PUT : GET) == 0) { tfrpyerr(tftp, p, ERRTXT, "Transfer refused."); refusedt = cticks; udp_free(p); ntftps--; return; } /* It looks safe to try to open a connection. */ /* Direction is a dummy for now */ cn = tfmkcn( PUT, ASCII, maxbufsize ); if(cn == 0) { printf("TFTP Server: Can't make connection, ignoring request\n"); udp_free(p); ntftps--; return; } cn->tf_udp = udp_open( host, pup->ud_srcp, udp_socket(), tftprcv, cn ); if(cn->tf_udp == 0) { printf("TFTP Server: Can't open udp connection, ignoring request\n"); tfcleanup(cn); udp_free(p); ntftps--; return; } cn->tf_mode = mode; if(ptreq->tf_op == RRQ) { cn->tf_dir = PUT; cn->tf_ctrlz = FALSE; cn->tf_fport = 1; cn->tf_fd = fopen(file, "rb"); if(cn->tf_fd == NULL) { #ifdef DEBUG if(NDEBUG & NETERR|PROTERR) printf("TFTP server: couldn't open file\n"); #endif tfudperr(cn->tf_udp, p, FNOTFOUND, " "); refusedt = cticks; tfcleanup(cn); (*tfs_done)(OFF); udp_free(p); ntftps--; return; } cn->tf_task = tk_fork(tk_cur, tfsread, 1200, "tfrd", cn); } else { cn->tf_dir = GET; cn->tf_ctrlz = (mode == ASCII) ? TRUE : FALSE; cn->tf_fport = 1; cn->tf_fd = fopen(file, "wb"); if(cn->tf_fd == NULL) { printf("TFTP Server: couldn't open file\n"); tfudperr(cn->tf_udp, p, ACCESS, " "); refusedt = cticks; tfcleanup(cn); (*tfs_done)(OFF); udp_free(p); ntftps--; return; } cn->tf_task = tk_fork(tk_cur, tfswrit, 1200, "tfwr", cn); } if ( cn->tf_task == NULL ) printf( "TFTP: cannot fork child task.\n" ); udp_free(p); } tfswrit(cn) register struct tfconn *cn; { int i; i = 0; /* Get an output packet buffer */ while((cn->tf_outp = udp_alloc(NORMLEN, 0)) == 0) { if (++i < 100) /* None there, try to free */ { tk_yield(); } else /* Can't free, give up */ { printf("TFTP Server: Couldn't allocate output packet.\n"); tfcleanup(); (*tfs_done)(OFF); ntftps--; tk_exit(); } } cn->tf_expected = 1; tfsndack(cn, 0); /* tm_set(cn->tf_rt, tftptmo, cn, cn->tf_tm); */ while(1) { cn->tf_state = DATAWAIT; while(cn->tf_state == DATAWAIT) tk_block(); if(cn->tf_state == TIMEOUT) { #ifdef DEBUG if(NDEBUG & TMO) printf("TFTP Server: Host not responding, giving up\n"); #endif tfudperr(cn->tf_udp,cn->tf_outp, ERRTXT, "Retry limit exceeded, giving up"); tfcleanup(cn); (*tfs_done)(OFF); break; } if(cn->tf_state == RCVLASTDATA) { tfcleanup(cn); (*tfs_done)(ON); break; } if(cn->tf_state != RCVDATA) { tfcleanup(cn); (*tfs_done)(OFF); break; } } ntftps--; tk_exit(); } tfsread(cn) register struct tfconn *cn; { int flen, done; PACKET pfill, psnd; int i; int temp; unsigned CRSEEN = 0; unsigned HOLDING = 0; char *data; char HELD; i = 0; /* Get an output packet buffer */ while((cn->tf_outp = udp_alloc(NORMLEN, 0)) == 0) { if (++i < 100) /* None there, try to free */ { tk_yield(); } else /* Can't free, give up */ { if(NDEBUG & NETERR|PROTERR) printf("tfsread: couldn't allocate fill packet\n"); tfcleanup(); (*tfs_done)(OFF); ntftps--; tk_exit(); } } i = 0; while((pfill = udp_alloc(NORMLEN, 0)) == 0) /* get second buffer */ { if (++i < 100) { tk_yield(); } else /* Can't get buffer, give up. */ { tfudperr(cn->tf_udp, cn->tf_outp, ERRTXT, "Couldn't allocate packet."); if(NDEBUG & NETERR|PROTERR) printf("tfsread: couldn't allocate fill packet\n"); tfcleanup(cn); (*tfs_done)(OFF); ntftps--; tk_exit(); } } cn->tf_state = RCVACK; /* consider request to first ack */ cn->tf_expected = 0; flen = NORMLEN; done = 0; /* Here's how the main loop for putting data blocks works: We make the request packet and then we enter the main loop. SEND current pkt <-----------------------+ If we haven't filled the data pkt, fill it | Wait for the ACK | If timeout, ------------------------------------+ Swap the pkt buffers | GOTO -------------------------------------------+ */ while(1) { data = (char *)tftp_data(pfill); for(flen=0; flen tf_fd); if(CRSEEN) { CRSEEN = 0; if(temp == 0x0a) { data[flen] = temp; continue; } else { HOLDING = 1; HELD = temp; data[flen] = 0; continue; } } if(cn->tf_mode == ASCII && temp == 0x0d) CRSEEN = 1; if(temp == EOF) break; data[flen] = temp; if(cn->tf_mode == ASCII && temp == DOS_EOF) break; } cn->tf_size += flen; while(cn->tf_state == ACKWAIT) tk_block(); if(cn->tf_state == TIMEOUT) { if(NDEBUG & TMO) printf("TFTP Server: Host not responding, giving up\n"); tfudperr(cn->tf_udp,cn->tf_outp, ERRTXT, "Retry limit exceeded, giving up"); cn->tf_state = DEAD; tfcleanup(cn); udp_free(pfill); (*tfs_done)(OFF); break; } if(cn->tf_state == RCVACK) { cn->tf_expected++; /* Check if we're done */ if(done) { tfcleanup(cn); udp_free(pfill); (*tfs_done)(ON); break; } /* If not, resume the loop */ psnd = pfill; /* prepare to switch */ pfill = cn->tf_outp; /* take tftp_util's buffer */ cn->tf_outp = psnd; /* our buffer to tftp_util */ tfsndata(cn, flen); cn->tf_state = ACKWAIT; if(flen < NORMLEN) done = 1; continue; } tfcleanup(cn); udp_free(pfill); (*tfs_done)(ON); break; } ntftps--; tk_exit(); }