1
0
Files
2022-09-29 17:59:04 +03:00

207 lines
5.2 KiB
C

/*
* Copyright 1990 Silicon Graphics, Inc. All rights reserved.
*
* Trivial File Transfer Protocol (TFTP), defined in RFC 783.
*/
#include <netdb.h>
#include <sys/types.h>
#include <arpa/tftp.h>
#include "debug.h"
#include "enum.h"
#include "protodefs.h"
#include "protocols/ip.h"
char tftpname[] = "tftp";
#define th_errcode th_code /* use a more discernable name */
#define TFTP_PORT 69 /* in case getservbyname fails */
#define TFTP_HDRSIZE 4 /* header length in bytes */
enum tftpfid { OPCODE, STUFF, BLOCK, ERRCODE };
static ProtoField tftpfields[] = {
PFI_SINT("opcode", "Operation Code", OPCODE, short, PV_TERSE),
PFI_VAR("stuff", "Filename/Mode", STUFF, 2, PV_TERSE),
PFI("block", "Block Number", BLOCK, DS_SIGN_EXTEND,
sizeof(short), 2, EXOP_NUMBER, PV_DEFAULT),
PFI("errcode", "Error Code", ERRCODE, DS_SIGN_EXTEND,
sizeof(short), 2, EXOP_NUMBER, PV_TERSE),
};
#define TFTPFID(pf) ((enum tftpfid) (pf)->pf_id)
#define TFTPFIELD(fid) tftpfields[(int) fid]
static ProtoMacro tftpnicknames[] = {
PMI("TFTP", tftpname),
};
static Enumeration opcode;
static Enumerator opcodevec[] =
{ EI_VAL("RRQ", RRQ), EI_VAL("WRQ", WRQ), EI_VAL("DATA", DATA),
EI_VAL("ACK", ACK), EI_VAL("ERROR", ERROR) };
static Enumeration errcode;
static Enumerator errcodevec[] =
{ EI_VAL("EUNDEF", EUNDEF), EI_VAL("ENOTFOUND", ENOTFOUND),
EI_VAL("EACCESS", EACCESS), EI_VAL("ENOSPACE", ENOSPACE),
EI_VAL("EBADOP", EBADOP), EI_VAL("EBADID", EBADID),
EI_VAL("EEXISTS", EEXISTS), EI_VAL("ENOUSER", ENOUSER) };
/*
* TFTP protocol interface and operations.
*/
#define tftp_setopt pr_stub_setopt
#define tftp_embed pr_stub_embed
DefineProtocol(tftp, tftpname, "Trivial File Transfer Protocol", PRID_TFTP,
DS_BIG_ENDIAN, TFTP_HDRSIZE, 0, PR_MATCHNOTIFY, 0, 0, 0);
static int
tftp_init()
{
struct servent *sp;
if (!pr_register(&tftp_proto, tftpfields, lengthof(tftpfields),
lengthof(opcodevec) + lengthof(errcodevec))) {
return 0;
}
sp = getservbyname(tftpname, "udp");
if (!pr_nest(&tftp_proto, PRID_UDP, (sp ? sp->s_port : TFTP_PORT),
tftpnicknames, lengthof(tftpnicknames))) {
return 0;
}
en_init(&opcode, opcodevec, lengthof(opcodevec), &tftp_proto);
en_init(&errcode, errcodevec, lengthof(errcodevec), &tftp_proto);
return 1;
}
/* ARGSUSED */
static Expr *
tftp_resolve(char *name, int len, struct snooper *sn)
{
return ex_string(name, len);
}
static ExprType
tftp_compile(ProtoField *pf, Expr *mex, Expr *tex, ProtoCompiler *pc)
{
long mask;
if (!pc_intmask(pc, mex, &mask))
return ET_ERROR;
if (TFTPFID(pf) == STUFF) {
pc_badop(pc, tex);
return ET_ERROR;
}
if (!pc_intfield(pc, pf, mask, tex->ex_val, sizeof(struct tftphdr)))
return ET_COMPLEX;
return ET_SIMPLE;
}
static void
tftp_register(ProtoStack *ps)
{
struct ipframe *ipf;
ipf = PS_TOP(ps, struct ipframe);
(void) pr_nestqual(&tftp_proto, PRID_UDP,
ipf->ipf_sport, ipf->ipf_src.s_addr,
tftpnicknames, lengthof(tftpnicknames));
}
/* ARGSUSED */
static int
tftp_match(Expr *pex, DataStream *ds, ProtoStack *ps, Expr *rex)
{
struct tftphdr *th;
th = (struct tftphdr *) ds_inline(ds, TFTP_HDRSIZE, sizeof(u_short));
if (th == 0)
return 0;
th->th_opcode = ntohs(th->th_opcode);
if (th->th_opcode == RRQ || th->th_opcode == WRQ)
tftp_register(ps);
return 1;
}
static int
tftp_fetch(ProtoField *pf, DataStream *ds, ProtoStack *ps, Expr *rex)
{
struct tftphdr *th;
th = (struct tftphdr *) ds_inline(ds, TFTP_HDRSIZE, sizeof(u_short));
if (th == 0)
return 0;
th->th_opcode = ntohs(th->th_opcode);
if (th->th_opcode == RRQ || th->th_opcode == WRQ)
tftp_register(ps);
rex->ex_op = EXOP_NUMBER;
switch (TFTPFID(pf)) {
case OPCODE:
rex->ex_val = th->th_opcode;
break;
case BLOCK:
rex->ex_val = ntohs(th->th_block);
break;
case ERRCODE:
rex->ex_val = ntohs(th->th_errcode);
break;
case STUFF:
rex->ex_op = EXOP_STRING;
rex->ex_str.s_ptr = th->th_stuff;
rex->ex_str.s_len = strlen(th->th_stuff);
}
return 1;
}
static void
tftp_decode(DataStream *ds, ProtoStack *ps, PacketView *pv)
{
struct tftphdr *th;
th = (struct tftphdr *) ds_inline(ds, TFTP_HDRSIZE, sizeof(u_short));
if (th == 0)
return;
th->th_opcode = ntohs(th->th_opcode);
pv_showfield(pv, &TFTPFIELD(OPCODE), &th->th_opcode,
"%-5.5s", en_name(&opcode, th->th_opcode));
switch (th->th_opcode) {
case RRQ:
case WRQ: {
char *mode, *cp;
int size;
tftp_register(ps);
mode = (th->th_stuff[1] == '\0') ? &th->th_stuff[2] : 0;
size = TFTP_HDRSIZE - sizeof(th->th_opcode);
while ((cp = ds_inline(ds, 1, 1)) != 0) {
size++;
if (*cp != '\0')
continue;
if (mode)
break;
mode = cp + 1;
}
pv_showvarfield(pv, &TFTPFIELD(STUFF), th->th_stuff, size,
"%s (%s)", th->th_stuff, mode ? mode : "???");
break;
}
case DATA:
pv_showfield(pv, &TFTPFIELD(BLOCK), &th->th_block,
"%-5u", ntohs(th->th_block));
if (pv->pv_level < PV_VERBOSE)
ds_seek(ds, ds->ds_count, DS_RELATIVE);
else
pv_decodehex(pv, ds, 0, ds->ds_count);
break;
case ACK:
pv_showfield(pv, &TFTPFIELD(BLOCK), &th->th_block,
"%-5u", ntohs(th->th_block));
break;
case ERROR:
pv_showfield(pv, &TFTPFIELD(ERRCODE), &th->th_errcode,
"%-9.9s", en_name(&errcode,ntohs(th->th_errcode)));
break;
}
}