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