📄 nf_conntrack_ftp.c
字号:
/* FTP extension for connection tracking. *//* (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/netfilter.h>#include <linux/ip.h>#include <linux/ipv6.h>#include <linux/ctype.h>#include <linux/inet.h>#include <net/checksum.h>#include <net/tcp.h>#include <net/netfilter/nf_conntrack.h>#include <net/netfilter/nf_conntrack_expect.h>#include <net/netfilter/nf_conntrack_ecache.h>#include <net/netfilter/nf_conntrack_helper.h>#include <linux/netfilter/nf_conntrack_ftp.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");MODULE_DESCRIPTION("ftp connection tracking helper");MODULE_ALIAS("ip_conntrack_ftp");/* This is slow, but it's simple. --RR */static char *ftp_buffer;static DEFINE_SPINLOCK(nf_ftp_lock);#define MAX_PORTS 8static u_int16_t ports[MAX_PORTS];static unsigned int ports_c;module_param_array(ports, ushort, &ports_c, 0400);static int loose;module_param(loose, bool, 0600);unsigned int (*nf_nat_ftp_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, enum nf_ct_ftp_type type, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp);EXPORT_SYMBOL_GPL(nf_nat_ftp_hook);static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, char);static int try_eprt(const char *, size_t, struct nf_conntrack_man *, char);static int try_epsv_response(const char *, size_t, struct nf_conntrack_man *, char);static struct ftp_search { const char *pattern; size_t plen; char skip; char term; enum nf_ct_ftp_type ftptype; int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char);} search[IP_CT_DIR_MAX][2] = { [IP_CT_DIR_ORIGINAL] = { { .pattern = "PORT", .plen = sizeof("PORT") - 1, .skip = ' ', .term = '\r', .ftptype = NF_CT_FTP_PORT, .getnum = try_rfc959, }, { .pattern = "EPRT", .plen = sizeof("EPRT") - 1, .skip = ' ', .term = '\r', .ftptype = NF_CT_FTP_EPRT, .getnum = try_eprt, }, }, [IP_CT_DIR_REPLY] = { { .pattern = "227 ", .plen = sizeof("227 ") - 1, .skip = '(', .term = ')', .ftptype = NF_CT_FTP_PASV, .getnum = try_rfc959, }, { .pattern = "229 ", .plen = sizeof("229 ") - 1, .skip = '(', .term = ')', .ftptype = NF_CT_FTP_EPSV, .getnum = try_epsv_response, }, },};static intget_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term){ const char *end; int ret = in6_pton(src, min_t(size_t, dlen, 0xffff), (u8 *)dst, term, &end); if (ret > 0) return (int)(end - src); return 0;}static int try_number(const char *data, size_t dlen, u_int32_t array[], int array_size, char sep, char term){ u_int32_t i, len; memset(array, 0, sizeof(array[0])*array_size); /* Keep data pointing at next char. */ for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { if (*data >= '0' && *data <= '9') { array[i] = array[i]*10 + *data - '0'; } else if (*data == sep) i++; else { /* Unexpected character; true if it's the terminator and we're finished. */ if (*data == term && i == array_size - 1) return len; pr_debug("Char %u (got %u nums) `%u' unexpected\n", len, i, *data); return 0; } } pr_debug("Failed to fill %u numbers separated by %c\n", array_size, sep); return 0;}/* Returns 0, or length of numbers: 192,168,1,1,5,6 */static int try_rfc959(const char *data, size_t dlen, struct nf_conntrack_man *cmd, char term){ int length; u_int32_t array[6]; length = try_number(data, dlen, array, 6, ',', term); if (length == 0) return 0; cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]); cmd->u.tcp.port = htons((array[4] << 8) | array[5]); return length;}/* Grab port: number up to delimiter */static int get_port(const char *data, int start, size_t dlen, char delim, __be16 *port){ u_int16_t tmp_port = 0; int i; for (i = start; i < dlen; i++) { /* Finished? */ if (data[i] == delim) { if (tmp_port == 0) break; *port = htons(tmp_port); pr_debug("get_port: return %d\n", tmp_port); return i + 1; } else if (data[i] >= '0' && data[i] <= '9') tmp_port = tmp_port*10 + data[i] - '0'; else { /* Some other crap */ pr_debug("get_port: invalid char.\n"); break; } } return 0;}/* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd, char term){ char delim; int length; /* First character is delimiter, then "1" for IPv4 or "2" for IPv6, then delimiter again. */ if (dlen <= 3) { pr_debug("EPRT: too short\n"); return 0; } delim = data[0]; if (isdigit(delim) || delim < 33 || delim > 126 || data[2] != delim) { pr_debug("try_eprt: invalid delimitter.\n"); return 0; } if ((cmd->l3num == PF_INET && data[1] != '1') || (cmd->l3num == PF_INET6 && data[1] != '2')) { pr_debug("EPRT: invalid protocol number.\n"); return 0; } pr_debug("EPRT: Got %c%c%c\n", delim, data[1], delim); if (data[1] == '1') { u_int32_t array[4]; /* Now we have IP address. */ length = try_number(data + 3, dlen - 3, array, 4, '.', delim); if (length != 0) cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]); } else { /* Now we have IPv6 address. */ length = get_ipv6_addr(data + 3, dlen - 3, (struct in6_addr *)cmd->u3.ip6, delim); } if (length == 0) return 0; pr_debug("EPRT: Got IP address!\n"); /* Start offset includes initial "|1|", and trailing delimiter */ return get_port(data, 3 + length + 1, dlen, delim, &cmd->u.tcp.port);}/* Returns 0, or length of numbers: |||6446| */static int try_epsv_response(const char *data, size_t dlen, struct nf_conntrack_man *cmd, char term){ char delim; /* Three delimiters. */ if (dlen <= 3) return 0; delim = data[0]; if (isdigit(delim) || delim < 33 || delim > 126 || data[1] != delim || data[2] != delim) return 0; return get_port(data, 3, dlen, delim, &cmd->u.tcp.port);}/* Return 1 for match, 0 for accept, -1 for partial. */static int find_pattern(const char *data, size_t dlen, const char *pattern, size_t plen, char skip, char term, unsigned int *numoff, unsigned int *numlen, struct nf_conntrack_man *cmd, int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char)){ size_t i; pr_debug("find_pattern `%s': dlen = %Zu\n", pattern, dlen); if (dlen == 0) return 0; if (dlen <= plen) { /* Short packet: try for partial? */ if (strnicmp(data, pattern, dlen) == 0) return -1; else return 0; } if (strnicmp(data, pattern, plen) != 0) {#if 0 size_t i; pr_debug("ftp: string mismatch\n"); for (i = 0; i < plen; i++) { pr_debug("ftp:char %u `%c'(%u) vs `%c'(%u)\n", i, data[i], data[i], pattern[i], pattern[i]); }#endif return 0; } pr_debug("Pattern matches!\n"); /* Now we've found the constant string, try to skip to the 'skip' character */ for (i = plen; data[i] != skip; i++) if (i == dlen - 1) return -1; /* Skip over the last character */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -