import of upstream version 1.17
[mw/micromonitor-lm32.git] / umon_main / host / src / ttftp / ttftp.c
1 /* TTFTP:
2         An optional replacement for tftp on sun and pc.
3
4         General notice:
5         This code is part of a boot-monitor package developed as a generic base
6         platform for embedded system designs.  As such, it is likely to be
7         distributed to various projects beyond the control of the original
8         author.  Please notify the author of any enhancements made or bugs found
9         so that all may benefit from the changes.  In addition, notification back
10         to the author will allow the new user to pick up changes that may have
11         been made by other users after this version of the code was distributed.
12
13         Author: Ed Sutter
14         email:  esutter@lucent.com              (home: lesutter@worldnet.att.net)
15         phone:  908-582-2351                    (home: 908-889-5161)
16 */
17 #define WIN32_LEAN_AND_MEAN
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <time.h>
29 #include "ttftp.h"
30 #ifdef BUILD_WITH_VCC
31 #include <io.h>
32 #include <winsock2.h>
33 #define uint32_t unsigned long
34 #else
35 #define Sleep(a)        sleep(a/1000)
36 #include <unistd.h>
37 #include <stdint.h>
38 #ifndef O_BINARY
39 #define O_BINARY 0
40 #endif
41 #include <netdb.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #endif
46
47 #ifndef IPPORT_TFTP
48 #define IPPORT_TFTP 69
49 #endif
50
51 #ifdef BUILD_WITH_VCC
52 typedef unsigned short ushort;
53 #else
54 #define INVALID_SOCKET  -1
55 #endif
56
57 extern  void usage(char *);
58 char    consoleTitle[256];
59 short   tftpPort;
60 int             tftpVerbose, tftpQuiet, RetryCount, tftpSrvrTimeout, tftpPpd;
61 int             tftpRFC2349TsizeEnabled, tftpExitAfterRcv;
62
63 void
64 err(char *msg)
65 {
66         if (msg)
67                 fprintf(stderr,"%s, ",msg);
68 #ifdef BUILD_WITH_VCC
69         fprintf(stderr,"err=%d\n",WSAGetLastError());
70 #else
71         perror("");
72 #endif
73         exit(EXIT_ERROR);
74 }
75
76 void
77 ttftp_init(void)
78 {
79         tftpPort = IPPORT_TFTP;
80         tftpPpd = 0;
81         tftpQuiet = 0;
82         RetryCount = 0;
83         tftpVerbose = 0;
84         tftpSrvrTimeout = 60;
85         tftpRFC2349TsizeEnabled = 1;
86         tftpExitAfterRcv = 0;
87 }
88
89 void
90 tick(void)
91 {
92         static char *hand[] = { "|\b", "/\b", "-\b", "\\\b" };
93         static int tock;
94         
95         if (tftpQuiet < 2) {
96                 if (tock > 3)
97                         tock = 0;
98                 write(1,hand[tock++],2);
99         }
100 }
101
102 void
103 showerr(char *msg,int len)
104 {
105         ushort  errnum;
106
107         errnum = ntohs(*(ushort *)(msg+2));
108         if (len > 5)
109                 printf("\nTFTP ERROR %d: %s\n",errnum,&msg[4]);
110         else 
111                 printf("\nTFTP ERROR %d\n",errnum);
112 }
113
114 void
115 SendTo(int sfd,char *msg,int len,int flags,
116         struct sockaddr *to,int tolen,char *desc,int pass)
117 {
118         static  int     Sfd, MsgLen, Flags, ToLen;
119         static  char    Msg[1024], Desc[128];
120         static  struct sockaddr Saddr;
121
122         /* If this is pass zero, then copy the data to static space in */
123         /* case it is needed for retry. */
124         if (pass == 0) {
125                 RetryCount = 0;
126                 Sfd = sfd;
127                 MsgLen = len;
128                 Flags = flags;
129                 ToLen = tolen;
130                 if ((len > 1024) || (strlen(desc) > 128)) {
131                         fprintf(stderr,"too big\n");
132                         exit(EXIT_ERROR);
133                 }
134                 memcpy(Msg,msg,len);
135                 strcpy(Desc,desc);
136                 Saddr = *to;
137         }
138         if (tftpVerbose)
139                 fprintf(stderr,"Sending %s...\n",Desc);
140
141         if (sendto(Sfd,Msg,MsgLen,Flags,&Saddr,sizeof(Saddr)) < 0)
142                 err("sendto failed");
143 }
144
145 char *
146 strtolower(char *string)
147 {
148         char *base, *cp, *sp;
149
150         sp = string;
151         base = cp = malloc(strlen(string) + 1);
152         while(*sp) 
153                 *cp++ = tolower(*sp++);
154         *cp = 0;
155         return(base);
156 }
157
158 #ifdef BUILD_WITH_VCC
159 /* perror():
160  * Implementation of a discussion in VC++ documentation for displaying
161  * the error string that corresponds to the return value of GetLastError().
162  * A win32 implementation of perror()...
163  */
164 void
165 perror(const char *msg)
166 {
167         int             err;
168         LPVOID  lpMessageBuffer;
169
170         err = GetLastError();
171         FormatMessage(
172                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
173                 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
174                 (LPTSTR) &lpMessageBuffer, 0, NULL);
175         if (!msg)
176                 msg = "";
177         fprintf(stderr,"Error:\t %s: %s",msg,lpMessageBuffer);
178         LocalFree(lpMessageBuffer);
179 }
180 #endif
181
182 int
183 ttftp(char *sysname, char *getorput, char *srcfile,
184         char *destfile, char *xfermode)
185 {
186         unsigned sfd;
187         ushort  opcode;
188         unsigned msglen;
189         struct  stat mstat;
190         uint32_t inaddr;
191         ushort  blockno, lastblockno;
192         struct  hostent *hp, host_info;
193         struct  sockaddr_in server, resp;
194         int             xferlen, filemode, ffd, done, len;
195         char    rcvmsg[1024], sndmsg[1024], *openfile;
196 #ifdef BUILD_WITH_VCC
197         WSADATA WsaData;
198 #endif
199
200         if (!tftpQuiet)
201                 printf("ttftp(%s %s %s %s %s)\n",
202                         sysname,getorput,srcfile,destfile,xfermode);
203
204         if (strcmp(strtolower(getorput),"get") == 0) {
205                 opcode = htons(TFTP_RRQ);
206                 filemode = O_WRONLY | O_BINARY | O_CREAT | O_TRUNC;
207                 openfile = destfile; 
208                 ffd = 0;
209                 /* Don't open the file till we make sure that it exists on
210                  * the system we're going to retrieve it from.
211                  */
212         }
213         else if (strcmp(strtolower(getorput),"put") == 0) {
214                 opcode = htons(TFTP_WRQ);
215                 filemode = O_RDONLY | O_BINARY;
216                 openfile = srcfile; 
217
218                 /* Open the file now and if it doesn't exist, just exit.
219                  */
220                 ffd = open(openfile,filemode,0664);
221                 if (ffd == -1) {
222                         perror(openfile);
223                         exit(EXIT_ERROR);
224                 }
225         }
226         else
227                 usage("specify 'get or put'");
228
229
230         fstat(ffd,&mstat);
231         if (opcode == htons(TFTP_WRQ))
232                 printf("File %s size: %d bytes\n",openfile,mstat.st_size);
233
234         xferlen = mstat.st_size;
235
236 #ifdef BUILD_WITH_VCC
237         if (WSAStartup (0x0101, &WsaData) == SOCKET_ERROR)
238                 err("WSAStartup Failed");
239 #endif
240
241         /* Accept server name as string or internet dotted-decimal address: */
242         memset((char *)&server,0,sizeof(struct sockaddr));
243         if ((inaddr = inet_addr(sysname)) != INADDR_NONE) {
244                 memcpy((char *)&server.sin_addr,(char *)&inaddr,sizeof(inaddr));
245                 host_info.h_name = NULL;
246         }
247         else {
248                 hp = gethostbyname(sysname);
249                 if (hp == NULL)
250                         err("gethostbyname failed");
251                 host_info = *hp;
252                 memcpy((char *)&server.sin_addr,hp->h_addr,hp->h_length);
253         }
254         server.sin_family = AF_INET;
255         server.sin_port = htons(tftpPort);
256
257         sfd = socket(AF_INET,SOCK_DGRAM,0);
258         if (sfd == INVALID_SOCKET)
259                 err("socket failed");
260
261         /* Build the TFTP request: */
262         memcpy(sndmsg,&opcode,2);
263         
264         if (opcode == htons(TFTP_WRQ)) {
265                 strcpy(&sndmsg[2],destfile);
266                 msglen = strlen(destfile) + 2;
267         }
268         else {
269                 strcpy(&sndmsg[2],srcfile);
270                 msglen = strlen(srcfile) + 2;
271         }
272         strcpy(&sndmsg[msglen+1],xfermode);
273         msglen += (strlen(xfermode) + 2);
274         
275         if ((tftpRFC2349TsizeEnabled) && (opcode == htons(TFTP_WRQ))) {
276                 strcpy(sndmsg+msglen,"tsize");
277                 msglen += 6;
278                 msglen += sprintf(sndmsg+msglen,"%d",mstat.st_size);
279                 msglen++;
280         }
281
282         /* Start off the transfer... */
283         SendTo(sfd,sndmsg,msglen,0,(struct sockaddr *)&server,
284                 sizeof(server),"RRQ/WRQ",0);
285
286         done = 0;
287         blockno = lastblockno = 0;
288         while(!done) {
289                 fd_set  fd_read;
290                 int             sval, rcvsz;
291                 struct  timeval tval;
292
293                 tval.tv_sec = 3;
294                 tval.tv_usec = 0;
295
296                 FD_ZERO(&fd_read);
297                 FD_SET(sfd,&fd_read);
298                 sval = select(sfd+1,&fd_read,0,0,&tval);
299                 
300                 if (sval == 1) {                /* Data ready */
301                         msglen = sizeof(struct sockaddr);
302                         rcvsz = recvfrom(sfd,rcvmsg,sizeof(rcvmsg),0,
303                                 (struct sockaddr *)&resp,&msglen);
304                 }
305                 else if (sval == 0) {   /* Timeout */
306                         if (++RetryCount >= RETRY_MAX) {
307                                 fprintf(stderr,"Retries exhausted, giving up!\n");
308                                 exit(EXIT_ERROR);
309                         }
310                         tick();
311                         SendTo(0,0,0,0,0,0,0,1);
312                         continue;
313                 }
314                 else {                                  /* Error */
315                         err("select failed");
316                 }
317
318                 if (rcvsz <= 0) {
319                         fprintf(stderr,"recvfrom size: %d\n",rcvsz);
320                         err("recvfrom error");
321                 }
322
323                 opcode = ntohs(*(ushort *)rcvmsg);
324
325                 if (tftpVerbose)
326                         printf("Rcvd opcode: 0x%x\n",opcode);
327
328                 switch(opcode) {
329                 case TFTP_DAT:
330                         if (tftpVerbose)
331                                 printf("Rcvd TFTP_DAT\n");
332                         else if (tftpQuiet == 0)
333                                 write(1,".",1);
334                         else if (tftpQuiet == 1)
335                                 tick();
336                         rcvsz -= 4;
337                         if (rcvsz) {
338                                 if (ffd == 0) {
339                                         /* Now that we know the file exists, we can open the
340                                          * file here...
341                                          */
342                                         ffd = open(openfile,filemode,0664);
343                                         if (ffd == -1) {
344                                                 perror(openfile);
345                                                 exit(EXIT_ERROR);
346                                         }
347                                 }
348                                 if (write(ffd,&rcvmsg[4],rcvsz) < 0) {
349                                         fprintf(stderr,"\nwrite size=%d\n",rcvsz);
350                                         err("write failed");
351                                 }
352                         }
353                         *(ushort *)sndmsg = htons(TFTP_ACK);
354                         sndmsg[2] = rcvmsg[2];
355                         sndmsg[3] = rcvmsg[3];
356 //#ifdef BUILD_WITH_VCC
357                         testTftp(TFTP_DAT,sndmsg,4);
358 //#endif
359                         SendTo(sfd,sndmsg,4,0,(struct sockaddr *)&resp,
360                                 sizeof(server),"ACK",0);
361                         if (rcvsz < TFTP_DATAMAX)
362                                 done = 1;
363                         break;
364                 case TFTP_ACK:
365                 case TFTP_OACK:
366                         if (opcode == TFTP_ACK) {
367                                 blockno = ntohs((*(ushort *)(rcvmsg+2)));
368                                 if (tftpVerbose)
369                                         printf("Rcvd TFTP_ACK blk#%d\n",blockno);
370                         }
371                         else {
372                                 blockno = 0;
373                                 if (tftpVerbose)
374                                         printf("Rcvd TFTP_OACK\n");
375                         }
376 //#ifdef BUILD_WITH_VCC
377                         if (tftpPpd)
378                                 Sleep(tftpPpd);
379 //#endif
380                         
381                         if (xferlen == -1) {
382                                 done = 1;
383                                 break;
384                         }
385                         *(ushort *)sndmsg = htons(TFTP_DAT);
386                         if (!blockno || (blockno == lastblockno + 1)) {
387                                 if (!tftpVerbose) {
388                                         if (tftpQuiet == 0)
389                                                 write(1,".",1);
390                                         else if (tftpQuiet == 1)
391                                                 tick();
392                                 }
393                                 if (xferlen > TFTP_DATAMAX) {
394                                         len = TFTP_DATAMAX;
395                                         xferlen -= TFTP_DATAMAX;
396                                 }
397                                 else if (xferlen == TFTP_DATAMAX) {
398                                         len = TFTP_DATAMAX;
399                                         xferlen = 0;
400                                 }
401                                 else {
402                                         if (xferlen > 0)
403                                                 len = xferlen;
404                                         else
405                                                 len = 0;
406                                         xferlen = -1;
407                                 }
408                                 *(ushort *)(sndmsg+2) = htons((ushort)(blockno+1));
409                                 if (len) {
410                                         int n;
411                                         n = read(ffd,&sndmsg[4],len);
412                                         if (n != len) {
413                                                 fprintf(stderr,"read=%d,not%d\n",n,len);
414                                                 err("read failed");
415                                         }
416                                 }
417                         }
418                         else {
419                                 if (tftpVerbose) {
420                                         fprintf(stderr,"retry blkno=%d\n",
421                                         blockno);
422                                 }
423                         }
424 //#ifdef BUILD_WITH_VCC
425                         testTftp(TFTP_ACK,sndmsg,len+4);
426 //#endif
427                         SendTo(sfd,sndmsg,len+4,0,(struct sockaddr *)&resp,
428                                 sizeof(server),"DAT",0);
429                         lastblockno = blockno;
430                         break;
431                 case TFTP_ERR:
432                         showerr(rcvmsg,rcvsz);
433                         done = -1;
434                         break;
435                 case TFTP_WRQ:
436                         if (tftpVerbose)
437                                 fprintf(stderr,"Rcvd TFTP_WRQ\n");
438                         break;
439                 case TFTP_RRQ:
440                         if (tftpVerbose)
441                                 fprintf(stderr,"Rcvd TFTP_RRQ\n");
442                         break;
443                 default:
444                         if (tftpVerbose)
445                                 fprintf(stderr,"Rcvd 0x%x ????\n",opcode);
446                         done = 1;
447                         break;
448                 }
449         }
450         if ((tftpVerbose) || (!tftpQuiet))
451                 printf("\n");
452         close(sfd);
453         close(ffd);
454         if (done == -1)
455                 return(EXIT_ERROR);
456         else
457                 return(EXIT_SUCCESS);
458 }