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