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