1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <fcntl.h>
28 #include <arpa/tftp.h>
29 #include "snoop.h"
30 
31 struct tftp_options {
32 	int blksize;
33 	int tsize;
34 };
35 
36 extern char *dlc_header;
37 static char *tftperror(unsigned short);
38 static char *show_type(int);
39 static char *tftp_parse_oack(char *, size_t, struct tftp_options *);
40 
41 int
interpret_tftp(int flags,void * data,int fraglen)42 interpret_tftp(int flags, void *data, int fraglen)
43 {
44 	char *name, *mode;
45 	extern int src_port, dst_port;
46 	int blocksize = fraglen - 4;
47 	struct tftp_options opts;
48 	struct ttable *tt;
49 	struct tftphdr *tftp = data;
50 
51 	opts.tsize = 0;
52 	opts.blksize = 512;
53 
54 	switch (ntohs(tftp->th_opcode)) {
55 	case RRQ:
56 	case WRQ:
57 		add_transient(src_port, interpret_tftp);
58 		break;
59 	case ERROR:
60 		del_transient(src_port);
61 		break;
62 	case OACK:
63 		tt = is_transient(dst_port);
64 		if (tt != NULL)
65 			tt->blksize = opts.blksize;
66 		break;
67 	case DATA:
68 		tt = is_transient(dst_port);
69 		if (tt != NULL)
70 			opts.blksize = tt->blksize;
71 		break;
72 	default:
73 		break;
74 	}
75 
76 	if (flags & F_SUM) {
77 		switch (ntohs(tftp->th_opcode)) {
78 		case RRQ:
79 			name = (char *)&tftp->th_stuff;
80 			mode = name + (strlen(name) + 1);
81 			(void) sprintf(get_sum_line(),
82 			    "TFTP Read \"%s\" (%s)", name, mode);
83 			break;
84 		case WRQ:
85 			name = (char *)&tftp->th_stuff;
86 			mode = name + (strlen(name) + 1);
87 			(void) sprintf(get_sum_line(),
88 			    "TFTP Write \"%s\" (%s)", name, mode);
89 			break;
90 		case DATA:
91 			(void) sprintf(get_sum_line(),
92 			    "TFTP Data block %u (%d bytes)%s",
93 			    ntohs(tftp->th_block), blocksize,
94 			    blocksize < opts.blksize ? " (last block)":"");
95 			break;
96 		case ACK:
97 			(void) sprintf(get_sum_line(), "TFTP Ack block %d",
98 			    ntohs(tftp->th_block));
99 			break;
100 		case ERROR:
101 			(void) sprintf(get_sum_line(), "TFTP Error: %s",
102 			    tftperror(ntohs(tftp->th_code)));
103 			break;
104 		case OACK:
105 			(void) sprintf(get_sum_line(), "TFTP OACK: %s",
106 			    tftp_parse_oack((char *)&tftp->th_stuff,
107 			    fraglen - sizeof (tftp->th_opcode), &opts));
108 			if (tt != NULL)
109 				tt->blksize = opts.blksize;
110 			break;
111 		}
112 	}
113 
114 	if (flags & F_DTAIL) {
115 		show_header("TFTP:  ", "Trivial File Transfer Protocol",
116 		    fraglen);
117 		show_space();
118 		(void) sprintf(get_line((char *)(uintptr_t)tftp->th_opcode -
119 		    dlc_header, 2), "Opcode = %d (%s)", ntohs(tftp->th_opcode),
120 		    show_type(ntohs(tftp->th_opcode)));
121 
122 		switch (ntohs(tftp->th_opcode)) {
123 		case RRQ:
124 		case WRQ:
125 			name = (char *)&tftp->th_stuff;
126 			mode = name + (strlen(name) + 1);
127 			(void) sprintf(
128 			    get_line(name - dlc_header, strlen(name) + 1),
129 			    "File name = \"%s\"", name);
130 			(void) sprintf(
131 			    get_line(mode - dlc_header, strlen(mode) + 1),
132 			    "Transfer mode = %s", mode);
133 			break;
134 
135 		case DATA:
136 			(void) sprintf(get_line(
137 			    (char *)(uintptr_t)tftp->th_block - dlc_header, 2),
138 			    "Data block = %d%s", ntohs(tftp->th_block),
139 			    blocksize < opts.blksize ? " (last block)" : "");
140 			(void) sprintf(get_line(
141 			    (char *)(uintptr_t)tftp->th_data - dlc_header,
142 			    blocksize), "[ %d bytes of data ]", blocksize);
143 			break;
144 
145 		case ACK:
146 			(void) sprintf(get_line(
147 			    (char *)(uintptr_t)tftp->th_block - dlc_header, 2),
148 			    "Acknowledge block = %d", ntohs(tftp->th_block));
149 			break;
150 
151 		case ERROR:
152 			(void) sprintf(get_line(
153 			    (char *)(uintptr_t)tftp->th_code - dlc_header, 2),
154 			    "Error = %d (%s)", ntohs(tftp->th_code),
155 			    tftperror(ntohs(tftp->th_code)));
156 			(void) sprintf(get_line(
157 			    (char *)(uintptr_t)tftp->th_data -
158 			    dlc_header, strlen(tftp->th_data) + 1),
159 			    "Error string = \"%s\"", tftp->th_data);
160 			break;
161 		case OACK:
162 			(void) sprintf(get_line(
163 			    (char *)(uintptr_t)tftp->th_code - dlc_header, 2),
164 			    "TFTP OACK: %s",
165 			    tftp_parse_oack((char *)&tftp->th_stuff,
166 			    fraglen - sizeof (tftp->th_opcode), &opts));
167 			if (tt != NULL)
168 				tt->blksize = opts.blksize;
169 			break;
170 		}
171 	}
172 
173 	return (fraglen);
174 }
175 
176 static char *
show_type(int t)177 show_type(int t)
178 {
179 	switch (t) {
180 	case RRQ:	return ("read request");
181 	case WRQ:	return ("write request");
182 	case DATA:	return ("data packet");
183 	case ACK:	return ("acknowledgement");
184 	case ERROR:	return ("error");
185 	case OACK:	return ("option acknowledgement");
186 	}
187 	return ("?");
188 }
189 
190 static char *
tftperror(unsigned short code)191 tftperror(unsigned short code)
192 {
193 	static char buf[128];
194 
195 	switch (code) {
196 	case EUNDEF:	return ("not defined");
197 	case ENOTFOUND:	return ("file not found");
198 	case EACCESS:	return ("access violation");
199 	case ENOSPACE:	return ("disk full or allocation exceeded");
200 	case EBADOP:	return ("illegal TFTP operation");
201 	case EBADID:	return ("unknown transfer ID");
202 	case EEXISTS:	return ("file already exists");
203 	case ENOUSER:	return ("no such user");
204 	}
205 	(void) sprintf(buf, "%d", code);
206 
207 	return (buf);
208 }
209 
210 static char *
tftp_parse_oack(char * buf,size_t size,struct tftp_options * opts)211 tftp_parse_oack(char *buf, size_t size, struct tftp_options *opts)
212 {
213 	static char tftp_options[128];
214 	int i, idx;
215 
216 	tftp_options[0] = '\0';
217 	idx = 0;
218 
219 	while (size > 0 && idx < sizeof (tftp_options)) {
220 		if (idx > 0) {
221 			tftp_options[idx++] = ' ';
222 			tftp_options[idx] = '\0';
223 		}
224 
225 		/* get name */
226 		if (idx + strnlen(buf, size) + 1 > sizeof (tftp_options))
227 			break;
228 		for (i = 0; i < size; i++) {
229 			tftp_options[idx] = buf[i];
230 			if (tftp_options[idx] == '\0') {
231 				i++;
232 				break;
233 			}
234 			idx++;
235 		}
236 		size -= i;
237 		/*
238 		 * RFC 2348 requires this case in-sensitive.
239 		 */
240 		if (strcasecmp(buf, "blksize") == 0) {
241 			int blksize = strtol(buf + i, NULL, 0);
242 
243 			if (blksize >= 8)
244 				opts->blksize = blksize;
245 		}
246 		buf += i;
247 
248 		/* can we store separator? */
249 		if (idx + 3 > sizeof (tftp_options))
250 			break;
251 		strcat(tftp_options, ": ");
252 		idx += 2;
253 
254 		/* get value */
255 		if (idx + strnlen(buf, size) + 1 > sizeof (tftp_options))
256 			break;
257 
258 		for (i = 0; i < size; i++) {
259 			tftp_options[idx] = buf[i];
260 			if (tftp_options[idx] == '\0') {
261 				i++;
262 				break;
263 			}
264 			idx++;
265 		}
266 		size -= i;
267 		buf += i;
268 	}
269 	return (tftp_options);
270 }
271