1 #ifndef lint 2 static char sccsid[] = "@(#)main.c 4.9 (Berkeley) 04/12/84"; 3 #endif 4 5 /* 6 * TFTP User Program -- Command Interface. 7 */ 8 #include <sys/types.h> 9 #include <sys/socket.h> 10 #include <sys/file.h> 11 12 #include <netinet/in.h> 13 14 #include <signal.h> 15 #include <stdio.h> 16 #include <errno.h> 17 #include <setjmp.h> 18 #include <ctype.h> 19 #include <netdb.h> 20 21 #define TIMEOUT 5 /* secs between rexmt's */ 22 23 struct sockaddr_in sin = { AF_INET }; 24 int f; 25 int trace; 26 int connected; 27 char mode[32]; 28 char line[200]; 29 int margc; 30 char *margv[20]; 31 char *prompt = "tftp"; 32 jmp_buf toplevel; 33 int intr(); 34 struct servent *sp; 35 36 int quit(), help(), settrace(), status(); 37 int get(), put(), setpeer(), setmode(), setrexmt(), settimeout(); 38 39 #define HELPINDENT (sizeof("connect")) 40 41 struct cmd { 42 char *name; 43 char *help; 44 int (*handler)(); 45 }; 46 47 char thelp[] = "toggle packet tracing"; 48 char chelp[] = "connect to remote tftp"; 49 char qhelp[] = "exit tftp"; 50 char hhelp[] = "print help information"; 51 char shelp[] = "send file"; 52 char rhelp[] = "receive file"; 53 char mhelp[] = "set file transfer mode"; 54 char sthelp[] = "show current status"; 55 char xhelp[] = "set per-packet retransmission timeout"; 56 char ihelp[] = "set total retransmission timeout"; 57 58 struct cmd cmdtab[] = { 59 { "connect", chelp, setpeer }, 60 { "mode", mhelp, setmode }, 61 { "put", shelp, put }, 62 { "get", rhelp, get }, 63 { "quit", qhelp, quit }, 64 { "trace", thelp, settrace }, 65 { "status", sthelp, status }, 66 { "rexmt", xhelp, setrexmt }, 67 { "timeout", ihelp, settimeout }, 68 { "?", hhelp, help }, 69 0 70 }; 71 72 struct cmd *getcmd(); 73 char *tail(); 74 char *index(); 75 char *rindex(); 76 77 main(argc, argv) 78 char *argv[]; 79 { 80 int top; 81 82 sp = getservbyname("tftp", "udp"); 83 if (sp == 0) { 84 fprintf(stderr, "tftp: udp/tftp: unknown service\n"); 85 exit(1); 86 } 87 f = socket(AF_INET, SOCK_DGRAM, 0, 0); 88 if (f < 0) { 89 perror("tftp: socket"); 90 exit(3); 91 } 92 if (bind(f, &sin, sizeof (sin)) < 0) { 93 perror("tftp: bind"); 94 exit(1); 95 } 96 strcpy(mode, "octet"); 97 signal(SIGINT, intr); 98 if (argc > 1) { 99 if (setjmp(toplevel) != 0) 100 exit(0); 101 setpeer(argc, argv); 102 } 103 top = setjmp(toplevel) == 0; 104 for (;;) 105 command(top); 106 } 107 108 char *hostname; 109 char hnamebuf[32]; 110 111 setpeer(argc, argv) 112 int argc; 113 char *argv[]; 114 { 115 register int c; 116 struct hostent *host; 117 118 if (argc < 2) { 119 strcpy(line, "Connect "); 120 printf("(to) "); 121 gets(&line[strlen(line)]); 122 makeargv(); 123 argc = margc; 124 argv = margv; 125 } 126 if (argc > 3) { 127 printf("usage: %s host-name [port]\n", argv[0]); 128 return; 129 } 130 host = gethostbyname(argv[1]); 131 if (host) { 132 sin.sin_family = host->h_addrtype; 133 bcopy(host->h_addr, &sin.sin_addr, host->h_length); 134 hostname = host->h_name; 135 } else { 136 sin.sin_family = AF_INET; 137 sin.sin_addr.s_addr = inet_addr(argv[1]); 138 if (sin.sin_addr.s_addr == -1) { 139 connected = 0; 140 printf("%s: unknown host\n", argv[1]); 141 return; 142 } 143 strcpy(hnamebuf, argv[1]); 144 hostname = hnamebuf; 145 } 146 sin.sin_port = sp->s_port; 147 if (argc == 3) { 148 sin.sin_port = atoi(argv[2]); 149 if (sin.sin_port < 0) { 150 printf("%s: bad port number\n", argv[2]); 151 connected = 0; 152 return; 153 } 154 sin.sin_port = htons((u_short)sin.sin_port); 155 } 156 connected = 1; 157 } 158 159 struct modes { 160 char *m_name; 161 char *m_mode; 162 } modes[] = { 163 { "binary", "octet" }, 164 { "image", "octet" }, 165 { "octet", "octet" }, 166 { 0, 0 } 167 }; 168 169 setmode(argc, argv) 170 char *argv[]; 171 { 172 register struct modes *p; 173 174 if (argc > 2) { 175 char *sep; 176 177 printf("usage: %s [", argv[0]); 178 sep = " "; 179 for (p = modes; p->m_name; p++) { 180 printf("%s%s", sep, p->m_name); 181 if (*sep == ' ') 182 sep = " | "; 183 } 184 printf(" ]\n"); 185 return; 186 } 187 if (argc < 2) { 188 printf("Using %s mode to transfer files.\n", mode); 189 return; 190 } 191 for (p = modes; p->m_name; p++) 192 if (strcmp(argv[1], p->m_name) == 0) 193 break; 194 if (p->m_name) 195 strcpy(mode, p->m_mode); 196 else 197 printf("%s: unknown mode\n", argv[1]); 198 } 199 200 /* 201 * Send file(s). 202 */ 203 put(argc, argv) 204 char *argv[]; 205 { 206 int fd; 207 register int n, addr; 208 register char *cp, *targ; 209 210 if (argc < 2) { 211 strcpy(line, "send "); 212 printf("(file) "); 213 gets(&line[strlen(line)]); 214 makeargv(); 215 argc = margc; 216 argv = margv; 217 } 218 if (argc < 2) { 219 putusage(argv[0]); 220 return; 221 } 222 targ = argv[argc - 1]; 223 if (index(argv[argc - 1], ':')) { 224 char *cp; 225 struct hostent *hp; 226 227 for (n = 1; n < argc - 1; n++) 228 if (index(argv[n], ':')) { 229 putusage(argv[0]); 230 return; 231 } 232 cp = argv[argc - 1]; 233 targ = index(cp, ':'); 234 *targ++ = 0; 235 hp = gethostbyname(cp); 236 if (hp == 0) { 237 printf("%s: Unknown host.\n", cp); 238 return; 239 } 240 bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 241 sin.sin_family = hp->h_addrtype; 242 connected = 1; 243 hostname = hp->h_name; 244 } 245 if (!connected) { 246 printf("No target machine specified.\n"); 247 return; 248 } 249 if (argc < 4) { 250 cp = argc == 2 ? tail(targ) : argv[1]; 251 fd = open(cp, O_RDONLY); 252 if (fd < 0) { 253 fprintf(stderr, "tftp: "); perror(cp); 254 return; 255 } 256 (void) sendfile(fd, targ); 257 return; 258 } 259 cp = index(targ, '\0'); 260 *cp++ = '/'; 261 for (n = 1; n < argc - 1; n++) { 262 strcpy(cp, tail(argv[n])); 263 fd = open(argv[n], O_RDONLY); 264 if (fd < 0) { 265 fprintf(stderr, "tftp: "); perror(argv[n]); 266 continue; 267 } 268 (void) sendfile(fd, targ); 269 } 270 } 271 272 putusage(s) 273 char *s; 274 { 275 printf("usage: %s file ... host:target, or\n", s); 276 printf(" %s file ... target (when already connected)\n", s); 277 } 278 279 /* 280 * Receive file(s). 281 */ 282 get(argc, argv) 283 char *argv[]; 284 { 285 int fd; 286 register int n, addr; 287 register char *cp; 288 char *src; 289 290 if (argc < 2) { 291 strcpy(line, "get "); 292 printf("(files) "); 293 gets(&line[strlen(line)]); 294 makeargv(); 295 argc = margc; 296 argv = margv; 297 } 298 if (argc < 2) { 299 getusage(argv[0]); 300 return; 301 } 302 if (!connected) 303 for (n = 1; n < argc - 1; n++) 304 if (index(argv[n], ':') == 0) { 305 getusage(argv[0]); 306 return; 307 } 308 for (n = 1; argc == 2 || n < argc - 1; n++) { 309 src = index(argv[n], ':'); 310 if (src == NULL) 311 src = argv[n]; 312 else { 313 struct hostent *hp; 314 315 *src++ = 0; 316 hp = gethostbyname(argv[n]); 317 if (hp == 0) { 318 printf("%s: Unknown host.\n", argv[n]); 319 continue; 320 } 321 bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 322 sin.sin_family = hp->h_addrtype; 323 connected = 1; 324 hostname = hp->h_name; 325 } 326 if (argc < 4) { 327 cp = argc == 3 ? argv[2] : tail(src); 328 fd = creat(cp, 0644); 329 if (fd < 0) { 330 fprintf(stderr, "tftp: "); perror(cp); 331 return; 332 } 333 (void) recvfile(fd, src); 334 break; 335 } 336 cp = index(argv[argc - 1], '\0'); 337 *cp++ = '/'; 338 strcpy(cp, tail(src)); 339 fd = creat(src, 0644); 340 if (fd < 0) { 341 fprintf(stderr, "tftp: "); perror(src); 342 continue; 343 } 344 (void) recvfile(fd, src); 345 } 346 } 347 348 getusage(s) 349 { 350 printf("usage: %s host:file host:file ... file, or\n", s); 351 printf(" %s file file ... file if connected\n", s); 352 } 353 354 int rexmtval = TIMEOUT; 355 356 setrexmt(argc, argv) 357 char *argv[]; 358 { 359 int t; 360 361 if (argc < 2) { 362 strcpy(line, "Rexmt-timeout "); 363 printf("(value) "); 364 gets(&line[strlen(line)]); 365 makeargv(); 366 argc = margc; 367 argv = margv; 368 } 369 if (argc != 2) { 370 printf("usage: %s value\n", argv[0]); 371 return; 372 } 373 t = atoi(argv[1]); 374 if (t < 0) 375 printf("%s: bad value\n", t); 376 else 377 rexmtval = t; 378 } 379 380 int maxtimeout = 5 * TIMEOUT; 381 382 settimeout(argc, argv) 383 char *argv[]; 384 { 385 int t; 386 387 if (argc < 2) { 388 strcpy(line, "Maximum-timeout "); 389 printf("(value) "); 390 gets(&line[strlen(line)]); 391 makeargv(); 392 argc = margc; 393 argv = margv; 394 } 395 if (argc != 2) { 396 printf("usage: %s value\n", argv[0]); 397 return; 398 } 399 t = atoi(argv[1]); 400 if (t < 0) 401 printf("%s: bad value\n", t); 402 else 403 maxtimeout = t; 404 } 405 406 status(argc, argv) 407 char *argv[]; 408 { 409 if (connected) 410 printf("Connected to %s.\n", hostname); 411 else 412 printf("Not connected.\n"); 413 printf("Mode: %s Tracing: %s\n", mode, trace ? "on" : "off"); 414 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 415 rexmtval, maxtimeout); 416 } 417 418 intr() 419 { 420 421 alarm(0); 422 longjmp(toplevel, -1); 423 } 424 425 char * 426 tail(filename) 427 char *filename; 428 { 429 register char *s; 430 431 while (*filename) { 432 s = rindex(filename, '/'); 433 if (s == NULL) 434 break; 435 if (s[1]) 436 return (s + 1); 437 *s = '\0'; 438 } 439 return (filename); 440 } 441 442 /* 443 * Command parser. 444 */ 445 command(top) 446 int top; 447 { 448 register struct cmd *c; 449 450 if (!top) 451 putchar('\n'); 452 for (;;) { 453 printf("%s> ", prompt); 454 if (gets(line) == 0) 455 continue; 456 if (line[0] == 0) 457 continue; 458 makeargv(); 459 c = getcmd(margv[0]); 460 if (c == (struct cmd *)-1) { 461 printf("?Ambiguous command\n"); 462 continue; 463 } 464 if (c == 0) { 465 printf("?Invalid command\n"); 466 continue; 467 } 468 (*c->handler)(margc, margv); 469 } 470 } 471 472 struct cmd * 473 getcmd(name) 474 register char *name; 475 { 476 register char *p, *q; 477 register struct cmd *c, *found; 478 register int nmatches, longest; 479 480 longest = 0; 481 nmatches = 0; 482 found = 0; 483 for (c = cmdtab; p = c->name; c++) { 484 for (q = name; *q == *p++; q++) 485 if (*q == 0) /* exact match? */ 486 return (c); 487 if (!*q) { /* the name was a prefix */ 488 if (q - name > longest) { 489 longest = q - name; 490 nmatches = 1; 491 found = c; 492 } else if (q - name == longest) 493 nmatches++; 494 } 495 } 496 if (nmatches > 1) 497 return ((struct cmd *)-1); 498 return (found); 499 } 500 501 /* 502 * Slice a string up into argc/argv. 503 */ 504 makeargv() 505 { 506 register char *cp; 507 register char **argp = margv; 508 509 margc = 0; 510 for (cp = line; *cp;) { 511 while (isspace(*cp)) 512 cp++; 513 if (*cp == '\0') 514 break; 515 *argp++ = cp; 516 margc += 1; 517 while (*cp != '\0' && !isspace(*cp)) 518 cp++; 519 if (*cp == '\0') 520 break; 521 *cp++ = '\0'; 522 } 523 *argp++ = 0; 524 } 525 526 /*VARARGS*/ 527 quit() 528 { 529 exit(0); 530 } 531 532 /* 533 * Help command. 534 */ 535 help(argc, argv) 536 int argc; 537 char *argv[]; 538 { 539 register struct cmd *c; 540 541 if (argc == 1) { 542 printf("Commands may be abbreviated. Commands are:\n\n"); 543 for (c = cmdtab; c->name; c++) 544 printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 545 return; 546 } 547 while (--argc > 0) { 548 register char *arg; 549 arg = *++argv; 550 c = getcmd(arg); 551 if (c == (struct cmd *)-1) 552 printf("?Ambiguous help command %s\n", arg); 553 else if (c == (struct cmd *)0) 554 printf("?Invalid help command %s\n", arg); 555 else 556 printf("%s\n", c->help); 557 } 558 } 559 560 /*VARARGS*/ 561 settrace() 562 { 563 trace = !trace; 564 printf("Packet tracing %s.\n", trace ? "on" : "off"); 565 } 566