2 * Routines for ftp packet dissection
3 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
4 * Copyright 2001, Juan Toledo <toledo@users.sourceforge.net> (Passive FTP)
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * Copied from packet-pop.c
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 #include <epan/packet.h>
38 #include <epan/strutil.h>
39 #include <epan/conversation.h>
40 #include <epan/expert.h>
41 #include <epan/wmem/wmem.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
46 #ifdef HAVE_SYS_SOCKET_H
47 #include <sys/socket.h> /* needed to define AF_ values on UNIX */
49 #ifdef HAVE_WINSOCK2_H
50 #include <winsock2.h> /* needed to define AF_ values on Windows */
52 #ifdef NEED_INET_V6DEFS_H
53 #include "wsutil/inet_v6defs.h" /* if not a *NIX system */
58 static int proto_ftp
= -1;
59 static int proto_ftp_data
= -1;
60 static int hf_ftp_response
= -1;
61 static int hf_ftp_request
= -1;
62 static int hf_ftp_request_command
= -1;
63 static int hf_ftp_request_arg
= -1;
64 static int hf_ftp_response_code
= -1;
65 static int hf_ftp_response_arg
= -1;
66 static int hf_ftp_pasv_ip
= -1 ;
67 static int hf_ftp_pasv_port
= -1;
68 static int hf_ftp_pasv_nat
= -1;
69 static int hf_ftp_active_ip
= -1;
70 static int hf_ftp_active_port
= -1;
71 static int hf_ftp_active_nat
= -1;
72 static int hf_ftp_eprt_af
= -1;
73 static int hf_ftp_eprt_ip
= -1;
74 static int hf_ftp_eprt_ipv6
= -1;
75 static int hf_ftp_eprt_port
= -1;
76 static int hf_ftp_epsv_ip
= -1;
77 static int hf_ftp_epsv_ipv6
= -1;
78 static int hf_ftp_epsv_port
= -1;
80 static gint ett_ftp
= -1;
81 static gint ett_ftp_reqresp
= -1;
83 static expert_field ei_ftp_eprt_args_invalid
= EI_INIT
;
84 static expert_field ei_ftp_epsv_args_invalid
= EI_INIT
;
86 static dissector_handle_t ftpdata_handle
;
88 #define TCP_PORT_FTPDATA 20
89 #define TCP_PORT_FTP 21
91 static const value_string response_table
[] = {
92 { 110, "Restart marker reply" },
93 { 120, "Service ready in nnn minutes" },
94 { 125, "Data connection already open; transfer starting" },
95 { 150, "File status okay; about to open data connection" },
96 { 200, "Command okay" },
97 { 202, "Command not implemented, superfluous at this site" },
98 { 211, "System status, or system help reply" },
99 { 212, "Directory status" },
100 { 213, "File status" },
101 { 214, "Help message" },
102 { 215, "NAME system type" },
103 { 220, "Service ready for new user" },
104 { 221, "Service closing control connection" },
105 { 225, "Data connection open; no transfer in progress" },
106 { 226, "Closing data connection" },
107 { 227, "Entering Passive Mode" },
108 { 229, "Entering Extended Passive Mode" },
109 { 230, "User logged in, proceed" },
110 { 232, "User logged in, authorized by security data exchange" },
111 { 234, "Security data exchange complete" },
112 { 235, "Security data exchange completed successfully" },
113 { 250, "Requested file action okay, completed" },
114 { 257, "PATHNAME created" },
115 { 331, "User name okay, need password" },
116 { 332, "Need account for login" },
117 { 334, "Requested security mechanism is ok" },
118 { 335, "Security data is acceptable, more is required" },
119 { 336, "Username okay, need password. Challenge is ..." },
120 { 350, "Requested file action pending further information" },
121 { 421, "Service not available, closing control connection" },
122 { 425, "Can't open data connection" },
123 { 426, "Connection closed; transfer aborted" },
124 { 431, "Need some unavailable resource to process security" },
125 { 450, "Requested file action not taken" },
126 { 451, "Requested action aborted: local error in processing" },
127 { 452, "Requested action not taken. Insufficient storage space in system" },
128 { 500, "Syntax error, command unrecognized" },
129 { 501, "Syntax error in parameters or arguments" },
130 { 502, "Command not implemented" },
131 { 503, "Bad sequence of commands" },
132 { 504, "Command not implemented for that parameter" },
133 { 522, "Network protocol not supported" },
134 { 530, "Not logged in" },
135 { 532, "Need account for storing files" },
136 { 533, "Command protection level denied for policy reasons" },
137 { 534, "Request denied for policy reasons" },
138 { 535, "Failed security check (hash, sequence, etc)" },
139 { 536, "Requested PROT level not supported by mechanism" },
140 { 537, "Command protection level not supported by security mechanism" },
141 { 550, "Requested action not taken: File unavailable" },
142 { 551, "Requested action aborted: page type unknown" },
143 { 552, "Requested file action aborted: Exceeded storage allocation" },
144 { 553, "Requested action not taken: File name not allowed" },
145 { 631, "Integrity protected reply" },
146 { 632, "Confidentiality and integrity protected reply" },
147 { 633, "Confidentiality protected reply" },
150 static value_string_ext response_table_ext
= VALUE_STRING_EXT_INIT(response_table
);
152 #define EPRT_AF_IPv4 1
153 #define EPRT_AF_IPv6 2
154 static const value_string eprt_af_vals
[] = {
155 { EPRT_AF_IPv4
, "IPv4" },
156 { EPRT_AF_IPv6
, "IPv6" },
162 * Parse the address and port information in a PORT command or in the
163 * response to a PASV command. Return TRUE if we found an address and
164 * port, and supply the address and port; return FALSE if we didn't find
167 * We ignore the IP address in the reply, and use the address from which
170 * XXX - are there cases where they differ? What if the FTP server is
171 * behind a NAT box, so that the address it puts into the reply isn't
172 * the address at which you should contact it? Do all NAT boxes detect
173 * FTP PASV replies and rewrite the address? (I suspect not.)
175 * RFC 959 doesn't say much about the syntax of the 227 reply.
177 * A proposal from Dan Bernstein at
179 * http://cr.yp.to/ftp/retr.html
181 * "recommend[s] that clients use the following strategy to parse the
182 * response line: look for the first digit after the initial space; look
183 * for the fourth comma after that digit; read two (possibly negative)
184 * integers, separated by a comma; the TCP port number is p1*256+p2, where
185 * p1 is the first integer modulo 256 and p2 is the second integer modulo
188 * wget 1.5.3 looks for a digit, although it doesn't handle negative
191 * The FTP code in the source of the cURL library, at
193 * http://curl.haxx.se/lxr/source/lib/ftp.c
195 * says that cURL "now scans for a sequence of six comma-separated numbers
196 * and will take them as IP+port indicators"; it loops, doing "sscanf"s
197 * looking for six numbers separated by commas, stepping the start pointer
198 * in the scanf one character at a time - i.e., it tries rather exhaustively.
200 * An optimization would be to scan for a digit, and start there, and if
201 * the scanf doesn't find six values, scan for the next digit and try
202 * again; this will probably succeed on the first try.
204 * The cURL code also says that "found reply-strings include":
206 * "227 Entering Passive Mode (127,0,0,1,4,51)"
207 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
208 * "227 Entering passive mode. 127,0,0,1,4,51"
210 * so it appears that you can't assume there are parentheses around
211 * the address and port number.
214 parse_port_pasv(const guchar
*line
, int linelen
, guint32
*ftp_ip
, guint16
*ftp_port
,
215 guint32
*pasv_offset
, guint
*ftp_ip_len
, guint
*ftp_port_len
)
221 int ip_address
[4], port
[2];
222 gboolean ret
= FALSE
;
225 * Copy the rest of the line into a null-terminated buffer.
227 args
= wmem_strndup(wmem_packet_scope(), line
, linelen
);
234 while ((c
= *p
) != '\0' && !isdigit(c
))
239 * We ran out of text without finding anything.
245 * See if we have six numbers.
247 i
= sscanf(p
, "%d,%d,%d,%d,%d,%d",
248 &ip_address
[0], &ip_address
[1], &ip_address
[2], &ip_address
[3],
254 *ftp_port
= ((port
[0] & 0xFF)<<8) | (port
[1] & 0xFF);
255 *ftp_ip
= g_htonl((ip_address
[0] << 24) | (ip_address
[1] <<16) | (ip_address
[2] <<8) | ip_address
[3]);
256 *pasv_offset
= (guint32
)(p
- args
);
257 *ftp_port_len
= (port
[0] < 10 ? 1 : (port
[0] < 100 ? 2 : 3 )) + 1 +
258 (port
[1] < 10 ? 1 : (port
[1] < 100 ? 2 : 3 ));
259 *ftp_ip_len
= (ip_address
[0] < 10 ? 1 : (ip_address
[0] < 100 ? 2 : 3)) + 1 +
260 (ip_address
[1] < 10 ? 1 : (ip_address
[1] < 100 ? 2 : 3)) + 1 +
261 (ip_address
[2] < 10 ? 1 : (ip_address
[2] < 100 ? 2 : 3)) + 1 +
262 (ip_address
[3] < 10 ? 1 : (ip_address
[3] < 100 ? 2 : 3));
268 * Well, that didn't work. Skip the first number we found,
271 while ((c
= *p
) != '\0' && isdigit(c
))
279 isvalid_rfc2428_delimiter(const guchar c
)
281 /* RFC2428 sect. 2 states rules for a valid delimiter */
282 static const gchar forbidden
[] = {"0123456789abcdef.:"};
283 if (c
< 33 || c
> 126)
285 else if (strchr(forbidden
, tolower(c
)))
297 * 1 Internet Protocol, Version 4
298 * 2 Internet Protocol, Version 6
300 * AF Number Address Format Example
301 * --------- -------------- -------
302 * 1 dotted decimal 132.235.1.2
303 * 2 IPv6 string 1080::8:800:200C:417A
307 * The following are sample EPRT commands:
308 * EPRT |1|132.235.1.2|6275|
309 * EPRT |2|1080::8:800:200C:417A|5282|
311 * The first command specifies that the server should use IPv4 to open a
312 * data connection to the host "132.235.1.2" on TCP port 6275. The
313 * second command specifies that the server should use the IPv6 network
314 * protocol and the network address "1080::8:800:200C:417A" to open a
315 * TCP data connection on port 5282.
317 * ... which means in fact that RFC2428 is capable to handle both,
318 * IPv4 and IPv6 so we have to care about the address family and properly
319 * act depending on it.
323 parse_eprt_request(const guchar
* line
, gint linelen
, guint32
*eprt_af
,
324 guint32
*eprt_ip
, guint16
*eprt_ipv6
, guint16
*ftp_port
,
325 guint32
*eprt_ip_len
, guint32
*ftp_port_len
)
327 gint delimiters_seen
= 0;
337 /* line contains the EPRT parameters, we need at least the 4 delimiters */
338 if (!line
|| linelen
<4)
341 /* Copy the rest of the line into a null-terminated buffer. */
342 args
= wmem_strndup(wmem_packet_scope(), line
, linelen
);
346 * RFC2428 sect. 2 states ...
348 * The EPRT command keyword MUST be followed by a single space (ASCII
349 * 32). Following the space, a delimiter character (<d>) MUST be
352 * ... the preceding <space> is already stripped so we know that the first
353 * character must be the delimiter and has just to be checked to be valid.
355 if (!isvalid_rfc2428_delimiter(*p
))
356 return FALSE
; /* EPRT command does not follow a vaild delimiter;
357 * malformed EPRT command - immediate escape */
360 /* Validate that the delimiter occurs 4 times in the string */
361 for (n
= 0; n
< linelen
; n
++) {
362 if (*(p
+n
) == delimiter
)
365 if (delimiters_seen
!= 4)
366 return FALSE
; /* delimiter doesn't occur 4 times
367 * probably no EPRT request - immediate escape */
369 /* we know that the first character is a delimiter... */
372 /* ... so we can start searching from the 2nd onwards */
373 for (n
=1; n
< linelen
; n
++) {
375 if (*(p
+n
) != delimiter
)
378 /* we found a delimiter */
381 fieldlen
= n
- lastn
- 1;
383 return FALSE
; /* all fields must have data in them */
384 field
= p
+ lastn
+ 1;
386 if (delimiters_seen
== 2) { /* end of address family field */
388 af_str
= wmem_strndup(wmem_packet_scope(), field
, fieldlen
);
389 *eprt_af
= atoi(af_str
);
391 else if (delimiters_seen
== 3) {/* end of IP address field */
393 ip_str
= wmem_strndup(wmem_packet_scope(), field
, fieldlen
);
395 if (*eprt_af
== EPRT_AF_IPv4
) {
396 if (inet_pton(AF_INET
, ip_str
, eprt_ip
) > 0)
401 else if (*eprt_af
== EPRT_AF_IPv6
) {
402 if (inet_pton(AF_INET6
, ip_str
, eprt_ipv6
) > 0)
408 return FALSE
; /* invalid/unknown address family */
410 *eprt_ip_len
= fieldlen
;
412 else if (delimiters_seen
== 4) {/* end of port field */
414 pt_str
= wmem_strndup(wmem_packet_scope(), field
, fieldlen
);
416 *ftp_port
= atoi(pt_str
);
417 *ftp_port_len
= fieldlen
;
427 * RFC2428 states ....
429 * The first two fields contained in the parenthesis MUST be blank. The
430 * third field MUST be the string representation of the TCP port number
431 * on which the server is listening for a data connection.
433 * The network protocol used by the data connection will be the same network
434 * protocol used by the control connection. In addition, the network
435 * address used to establish the data connection will be the same
436 * network address used for the control connection.
438 * An example response string follows:
440 * Entering Extended Passive Mode (|||6446|)
442 * ... which in fact means that again both address families IPv4 and IPv6
443 * are supported. But gladly it's not necessary to parse because it doesn't
444 * occur in EPSV responses. We can leverage ftp_ip_address which is
445 * protocol independent and already set.
449 parse_extended_pasv_response(const guchar
*line
, gint linelen
, guint16
*ftp_port
,
450 guint
*pasv_offset
, guint
*ftp_port_len
)
457 gboolean ret
= FALSE
;
458 gboolean delimiters_seen
= FALSE
;
461 * Copy the rest of the line into a null-terminated buffer.
463 args
= wmem_strndup(wmem_packet_scope(), line
, linelen
);
467 * Look for ( <d> <d> <d>
468 (Try to cope with '(' in description)
470 for (; !delimiters_seen
;) {
471 guchar delimiter
= '\0';
472 while ((c
= *p
) != '\0' && (c
!= '('))
482 /* Make sure same delimiter is used 3 times */
483 for (n
=0; n
<3; n
++) {
484 if ((c
= *p
) != '\0') {
485 if (delimiter
== '\0' && isvalid_rfc2428_delimiter(c
)) {
488 if (c
!= delimiter
) {
497 delimiters_seen
= TRUE
;
501 * Should now be at digits.
505 * We didn't run out of text without finding anything.
508 *pasv_offset
= (guint32
)(p
- args
);
512 /* get port string length */
513 if ((e
=strchr(p
,')')) == NULL
) {
517 *ftp_port_len
= (guint
)(--e
- p
);
526 dissect_ftp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
529 proto_tree
*ftp_tree
= NULL
;
530 proto_tree
*reqresp_tree
= NULL
;
531 proto_item
*ti
, *hidden_item
;
536 gboolean is_port_request
= FALSE
;
537 gboolean is_eprt_request
= FALSE
;
538 gboolean is_pasv_response
= FALSE
;
539 gboolean is_epasv_response
= FALSE
;
543 const guchar
*next_token
;
551 guint16 eprt_ipv6
[8];
552 guint32 eprt_ip_len
= 0;
554 guint32 ftp_port_len
;
555 address ftp_ip_address
;
557 conversation_t
*conversation
;
559 ftp_ip_address
= pinfo
->src
;
561 if (pinfo
->match_uint
== pinfo
->destport
)
566 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "FTP");
569 * Find the end of the first line.
571 * Note that "tvb_find_line_end()" will return a value that is
572 * not longer than what's in the buffer, so the "tvb_get_ptr()"
573 * call won't throw an exception.
575 linelen
= tvb_find_line_end(tvb
, 0, -1, &next_offset
, FALSE
);
576 line
= tvb_get_ptr(tvb
, 0, linelen
);
579 * Put the first line from the buffer into the summary
580 * (but leave out the line terminator).
582 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s: %s",
583 is_request
? "Request" : "Response",
584 format_text(line
, linelen
));
586 ti
= proto_tree_add_item(tree
, proto_ftp
, tvb
, 0, -1, ENC_NA
);
587 ftp_tree
= proto_item_add_subtree(ti
, ett_ftp
);
590 hidden_item
= proto_tree_add_boolean(ftp_tree
,
591 hf_ftp_request
, tvb
, 0, 0, TRUE
);
592 PROTO_ITEM_SET_HIDDEN(hidden_item
);
593 hidden_item
= proto_tree_add_boolean(ftp_tree
,
594 hf_ftp_response
, tvb
, 0, 0, FALSE
);
595 PROTO_ITEM_SET_HIDDEN(hidden_item
);
597 hidden_item
= proto_tree_add_boolean(ftp_tree
,
598 hf_ftp_request
, tvb
, 0, 0, FALSE
);
599 PROTO_ITEM_SET_HIDDEN(hidden_item
);
600 hidden_item
= proto_tree_add_boolean(ftp_tree
,
601 hf_ftp_response
, tvb
, 0, 0, TRUE
);
602 PROTO_ITEM_SET_HIDDEN(hidden_item
);
605 /* Put the line into the protocol tree. */
606 ti
= proto_tree_add_text(ftp_tree
, tvb
, 0, next_offset
, "%s",
607 tvb_format_text(tvb
, 0, next_offset
));
608 reqresp_tree
= proto_item_add_subtree(ti
, ett_ftp_reqresp
);
612 * Extract the first token, and, if there is a first
613 * token, add it as the request.
615 tokenlen
= get_token_len(line
, line
+ linelen
, &next_token
);
617 proto_tree_add_item(reqresp_tree
, hf_ftp_request_command
,
618 tvb
, 0, tokenlen
, ENC_ASCII
|ENC_NA
);
619 if (strncmp(line
, "PORT", tokenlen
) == 0)
620 is_port_request
= TRUE
;
622 * EPRT request command, as per RFC 2428
624 else if (strncmp(line
, "EPRT", tokenlen
) == 0)
625 is_eprt_request
= TRUE
;
629 * This is a response; the response code is 3 digits,
630 * followed by a space or hyphen, possibly followed by
633 * If the line doesn't start with 3 digits, it's part of
636 * XXX - keep track of state in the first pass, and
637 * treat non-continuation lines not beginning with digits
640 if (linelen
>= 3 && isdigit(line
[0]) && isdigit(line
[1])
641 && isdigit(line
[2])) {
643 * One-line reply, or first or last line
644 * of a multi-line reply.
646 tvb_get_nstringz0(tvb
, 0, sizeof(code_str
), code_str
);
647 code
= (guint32
)strtoul(code_str
, NULL
, 10);
649 proto_tree_add_uint(reqresp_tree
,
650 hf_ftp_response_code
, tvb
, 0, 3, code
);
653 * See if it's a passive-mode response.
655 * XXX - does anybody do FOOBAR, as per RFC
656 * 1639, or has that been supplanted by RFC 2428?
659 is_pasv_response
= TRUE
;
662 * Responses to EPSV command, as per RFC 2428
665 is_epasv_response
= TRUE
;
668 * Skip the 3 digits and, if present, the
672 next_token
= line
+ 4;
674 next_token
= line
+ linelen
;
677 * Line doesn't start with 3 digits; assume it's
678 * a line in the middle of a multi-line reply.
684 offset
= (gint
) (next_token
- line
);
685 linelen
-= (int) (next_token
- line
);
689 * Add the rest of the first line as request or
694 proto_tree_add_item(reqresp_tree
,
695 hf_ftp_request_arg
, tvb
, offset
,
696 linelen
, ENC_ASCII
|ENC_NA
);
698 proto_tree_add_item(reqresp_tree
,
699 hf_ftp_response_arg
, tvb
, offset
,
700 linelen
, ENC_ASCII
|ENC_NA
);
703 offset
= next_offset
;
706 * If this is a PORT request or a PASV response, handle it.
708 if (is_port_request
) {
709 if (parse_port_pasv(line
, linelen
, &ftp_ip
, &ftp_port
, &pasv_offset
, &ftp_ip_len
, &ftp_port_len
)) {
710 proto_tree_add_ipv4(reqresp_tree
, hf_ftp_active_ip
,
711 tvb
, pasv_offset
+ (tokenlen
+1) , ftp_ip_len
, ftp_ip
);
712 proto_tree_add_uint(reqresp_tree
, hf_ftp_active_port
,
713 tvb
, pasv_offset
+ 1 + (tokenlen
+1) + ftp_ip_len
, ftp_port_len
, ftp_port
);
714 SET_ADDRESS(&ftp_ip_address
, AT_IPv4
, 4, (const guint8
*)&ftp_ip
);
715 ftp_nat
= !ADDRESSES_EQUAL(&pinfo
->src
, &ftp_ip_address
);
717 proto_tree_add_boolean(reqresp_tree
, hf_ftp_active_nat
,
723 if (is_pasv_response
) {
726 * This frame contains a PASV response; set up a
727 * conversation for the data.
729 if (parse_port_pasv(line
, linelen
, &pasv_ip
, &ftp_port
, &pasv_offset
, &ftp_ip_len
, &ftp_port_len
)) {
730 proto_tree_add_ipv4(reqresp_tree
, hf_ftp_pasv_ip
,
731 tvb
, pasv_offset
+ 4, ftp_ip_len
, pasv_ip
);
732 proto_tree_add_uint(reqresp_tree
, hf_ftp_pasv_port
,
733 tvb
, pasv_offset
+ 4 + 1 + ftp_ip_len
, ftp_port_len
, ftp_port
);
734 SET_ADDRESS(&ftp_ip_address
, AT_IPv4
, 4,
735 (const guint8
*)&pasv_ip
);
736 ftp_nat
= !ADDRESSES_EQUAL(&pinfo
->src
, &ftp_ip_address
);
738 proto_tree_add_boolean(reqresp_tree
, hf_ftp_pasv_nat
,
743 * We use "ftp_ip_address", so that if
744 * we're NAT'd we look for the un-NAT'd
747 * XXX - should this call to
748 * "find_conversation()" just use
749 * "ftp_ip_address" and "server_port", and
750 * wildcard everything else?
752 conversation
= find_conversation(pinfo
->fd
->num
, &ftp_ip_address
,
753 &pinfo
->dst
, PT_TCP
, ftp_port
, 0,
755 if (conversation
== NULL
) {
757 * XXX - should this call to "conversation_new()"
758 * just use "ftp_ip_address" and "server_port",
759 * and wildcard everything else?
761 * XXX - what if we did find a conversation? As
762 * we create it only on the first pass through the
763 * packets, if we find one, it's presumably an
764 * unrelated conversation. Should we remove the
765 * old one from the hash table and put this one in
766 * its place? Can the conversation code handle
767 * conversations not in the hash table? Or should
768 * we make conversations support start and end
769 * frames, as circuits do, and treat this as an
770 * indication that one conversation was closed and
771 * a new one was opened?
773 conversation
= conversation_new(
774 pinfo
->fd
->num
, &ftp_ip_address
, &pinfo
->dst
,
775 PT_TCP
, ftp_port
, 0, NO_PORT2
);
776 conversation_set_dissector(conversation
, ftpdata_handle
);
782 if (is_eprt_request
) {
785 * This frame contains a EPRT request; let's dissect it and set up a
786 * conversation for the data connection.
788 if (parse_eprt_request(line
, linelen
,
789 &eprt_af
, &eprt_ip
, eprt_ipv6
, &ftp_port
,
790 &eprt_ip_len
, &ftp_port_len
)) {
792 /* since parse_eprt_request() returned TRUE,
793 we know that we have a valid address family */
794 eprt_offset
= tokenlen
+ 1 + 1; /* token, space, 1st delimiter */
795 proto_tree_add_uint(reqresp_tree
, hf_ftp_eprt_af
, tvb
,
796 eprt_offset
, 1, eprt_af
);
797 eprt_offset
+= 1 + 1; /* addr family, 2nd delimiter */
799 if (eprt_af
== EPRT_AF_IPv4
) {
800 proto_tree_add_ipv4(reqresp_tree
, hf_ftp_eprt_ip
,
801 tvb
, eprt_offset
, eprt_ip_len
, eprt_ip
);
802 SET_ADDRESS(&ftp_ip_address
, AT_IPv4
, 4,
803 (const guint8
*)&eprt_ip
);
805 else if (eprt_af
== EPRT_AF_IPv6
) {
806 proto_tree_add_ipv6(reqresp_tree
, hf_ftp_eprt_ipv6
,
807 tvb
, eprt_offset
, eprt_ip_len
, (const guint8
*)eprt_ipv6
);
808 SET_ADDRESS(&ftp_ip_address
, AT_IPv6
, 16,
809 (const guint8
*)&eprt_ipv6
);
811 eprt_offset
+= eprt_ip_len
+ 1; /* addr, 3rd delimiter */
813 proto_tree_add_uint(reqresp_tree
, hf_ftp_eprt_port
,
814 tvb
, eprt_offset
, ftp_port_len
, ftp_port
);
816 /* Find/create conversation for data */
817 conversation
= find_conversation(pinfo
->fd
->num
,
818 &pinfo
->src
, &ftp_ip_address
,
819 PT_TCP
, ftp_port
, 0, NO_PORT_B
);
820 if (conversation
== NULL
) {
821 conversation
= conversation_new(
822 pinfo
->fd
->num
, &pinfo
->src
, &ftp_ip_address
,
823 PT_TCP
, ftp_port
, 0, NO_PORT2
);
824 conversation_set_dissector(conversation
,
830 item
= proto_tree_add_text(reqresp_tree
,
831 tvb
, offset
- linelen
- 1, linelen
, "Invalid EPRT arguments");
832 expert_add_info(pinfo
, item
, &ei_ftp_eprt_args_invalid
);
836 if (is_epasv_response
) {
841 * This frame contains an EPSV response; set up a
842 * conversation for the data.
844 if (parse_extended_pasv_response(line
, linelen
,
845 &ftp_port
, &pasv_offset
, &ftp_port_len
)) {
846 /* Add IP address and port number to tree */
848 if (ftp_ip_address
.type
== AT_IPv4
) {
850 memcpy(&addr
, ftp_ip_address
.data
, 4);
851 addr_it
= proto_tree_add_ipv4(reqresp_tree
,
852 hf_ftp_epsv_ip
, tvb
, 0, 0, addr
);
853 PROTO_ITEM_SET_GENERATED(addr_it
);
855 else if (ftp_ip_address
.type
== AT_IPv6
) {
856 addr_it
= proto_tree_add_ipv6(reqresp_tree
,
857 hf_ftp_epsv_ipv6
, tvb
, 0, 0,
858 (guint8
*)ftp_ip_address
.data
);
859 PROTO_ITEM_SET_GENERATED(addr_it
);
862 proto_tree_add_uint(reqresp_tree
,
863 hf_ftp_epsv_port
, tvb
, pasv_offset
+ 4,
864 ftp_port_len
, ftp_port
);
866 /* Find/create conversation for data */
867 conversation
= find_conversation(pinfo
->fd
->num
, &ftp_ip_address
,
868 &pinfo
->dst
, PT_TCP
, ftp_port
, 0,
870 if (conversation
== NULL
) {
871 conversation
= conversation_new(
872 pinfo
->fd
->num
, &ftp_ip_address
, &pinfo
->dst
,
873 PT_TCP
, ftp_port
, 0, NO_PORT2
);
874 conversation_set_dissector(conversation
,
880 item
= proto_tree_add_text(reqresp_tree
,
881 tvb
, offset
- linelen
- 1, linelen
, "Invalid EPSV arguments");
882 expert_add_info(pinfo
, item
, &ei_ftp_epsv_args_invalid
);
888 * Show the rest of the request or response as text,
890 * XXX - only if there's a continuation indicator?
892 while (tvb_offset_exists(tvb
, offset
)) {
894 * Find the end of the line.
896 tvb_find_line_end(tvb
, offset
, -1, &next_offset
, FALSE
);
901 proto_tree_add_text(ftp_tree
, tvb
, offset
,
902 next_offset
- offset
, "%s",
903 tvb_format_text(tvb
, offset
, next_offset
- offset
));
904 offset
= next_offset
;
909 dissect_ftpdata(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
913 gboolean is_text
= TRUE
;
916 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "FTP-DATA");
918 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "FTP Data: %u bytes",
919 tvb_reported_length(tvb
));
921 data_length
= tvb_length(tvb
);
923 ti
= proto_tree_add_item(tree
, proto_ftp_data
, tvb
, 0, -1, ENC_NA
);
925 /* Check the first few chars to see whether it looks like a text file or not */
926 check_chars
= MIN(10, data_length
);
927 for (i
=0; i
< check_chars
; i
++) {
928 if (!isprint(tvb_get_guint8(tvb
, i
))) {
935 /* Show as string, but don't format more text than will be displayed */
936 proto_item_append_text(ti
, " (%s)", tvb_format_text(tvb
, 0, MIN(data_length
, ITEM_LABEL_LENGTH
)));
939 /* Assume binary, just show the number of bytes */
940 proto_item_append_text(ti
, " (%u bytes data)", data_length
);
945 proto_register_ftp(void)
947 static hf_register_info hf
[] = {
949 { "Response", "ftp.response",
950 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
951 "TRUE if FTP response", HFILL
}},
954 { "Request", "ftp.request",
955 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
956 "TRUE if FTP request", HFILL
}},
958 { &hf_ftp_request_command
,
959 { "Request command", "ftp.request.command",
960 FT_STRING
, BASE_NONE
, NULL
, 0x0,
963 { &hf_ftp_request_arg
,
964 { "Request arg", "ftp.request.arg",
965 FT_STRING
, BASE_NONE
, NULL
, 0x0,
968 { &hf_ftp_response_code
,
969 { "Response code", "ftp.response.code",
970 FT_UINT32
, BASE_DEC
|BASE_EXT_STRING
, &response_table_ext
, 0x0,
973 { &hf_ftp_response_arg
,
974 { "Response arg", "ftp.response.arg",
975 FT_STRING
, BASE_NONE
, NULL
, 0x0,
979 { "Passive IP address", "ftp.passive.ip",
980 FT_IPv4
, BASE_NONE
, NULL
,0x0,
981 "Passive IP address (check NAT)", HFILL
}},
984 { "Passive port", "ftp.passive.port",
985 FT_UINT16
, BASE_DEC
, NULL
,0x0,
986 "Passive FTP server port", HFILL
}},
989 {"Passive IP NAT", "ftp.passive.nat",
990 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
991 "NAT is active SIP and passive IP different", HFILL
}},
994 { "Active IP address", "ftp.active.cip",
995 FT_IPv4
, BASE_NONE
, NULL
, 0x0,
996 "Active FTP client IP address", HFILL
}},
998 { &hf_ftp_active_port
,
999 {"Active port", "ftp.active.port",
1000 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
1001 "Active FTP client port", HFILL
}},
1003 { &hf_ftp_active_nat
,
1004 { "Active IP NAT", "ftp.active.nat",
1005 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
1006 "NAT is active", HFILL
}},
1009 { "Extended active address family", "ftp.eprt.af",
1010 FT_UINT8
, BASE_DEC
, VALS(eprt_af_vals
), 0,
1014 { "Extended active IP address", "ftp.eprt.ip",
1015 FT_IPv4
, BASE_NONE
, NULL
, 0,
1016 "Extended active FTP client IPv4 address", HFILL
}},
1018 { &hf_ftp_eprt_ipv6
,
1019 { "Extended active IPv6 address", "ftp.eprt.ipv6",
1020 FT_IPv6
, BASE_NONE
, NULL
, 0,
1021 "Extended active FTP client IPv6 address", HFILL
}},
1023 { &hf_ftp_eprt_port
,
1024 { "Extended active port", "ftp.eprt.port",
1025 FT_UINT16
, BASE_DEC
, NULL
, 0,
1026 "Extended active FTP client listener port", HFILL
}},
1029 { "Extended passive IPv4 address", "ftp.epsv.ip",
1030 FT_IPv4
, BASE_NONE
, NULL
, 0,
1031 "Extended passive FTP server IPv4 address", HFILL
}},
1033 { &hf_ftp_epsv_ipv6
,
1034 { "Extended passive IPv6 address", "ftp.epsv.ipv6",
1035 FT_IPv6
, BASE_NONE
, NULL
, 0,
1036 "Extended passive FTP server IPv6 address", HFILL
}},
1038 { &hf_ftp_epsv_port
,
1039 { "Extended passive port", "ftp.epsv.port",
1040 FT_UINT16
, BASE_DEC
, NULL
, 0,
1041 "Extended passive FTP server port", HFILL
}}
1044 static gint
*ett
[] = {
1049 static ei_register_info ei
[] = {
1050 { &ei_ftp_eprt_args_invalid
, { "ftp.eprt.args_invalid", PI_MALFORMED
, PI_WARN
, "EPRT arguments must have the form: |<family>|<addr>|<port>|", EXPFILL
}},
1051 { &ei_ftp_epsv_args_invalid
, { "ftp.epsv.args_invalid", PI_MALFORMED
, PI_WARN
, "EPSV arguments must have the form (|||<port>|)", EXPFILL
}},
1054 expert_module_t
* expert_ftp
;
1056 proto_ftp
= proto_register_protocol("File Transfer Protocol (FTP)", "FTP", "ftp");
1058 register_dissector("ftp", dissect_ftp
, proto_ftp
);
1059 proto_ftp_data
= proto_register_protocol("FTP Data", "FTP-DATA", "ftp-data");
1060 register_dissector("ftp-data", dissect_ftpdata
, proto_ftp_data
);
1061 proto_register_field_array(proto_ftp
, hf
, array_length(hf
));
1062 proto_register_subtree_array(ett
, array_length(ett
));
1063 expert_ftp
= expert_register_protocol(proto_ftp
);
1064 expert_register_field_array(expert_ftp
, ei
, array_length(ei
));
1068 proto_reg_handoff_ftp(void)
1070 dissector_handle_t ftp_handle
;
1072 ftpdata_handle
= find_dissector("ftp-data");
1073 dissector_add_uint("tcp.port", TCP_PORT_FTPDATA
, ftpdata_handle
);
1074 ftp_handle
= find_dissector("ftp");
1075 dissector_add_uint("tcp.port", TCP_PORT_FTP
, ftp_handle
);
1079 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1084 * indent-tabs-mode: nil
1087 * vi: set shiftwidth=4 tabstop=4 expandtab:
1088 * :indentSize=4:tabSize=4:noTabs=true: