]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * Simple FTP transparent proxy for in-kernel use. For use with the NAT | |
24 | * code. | |
25 | */ | |
26 | ||
27 | ||
28 | #define isdigit(x) ((x) >= '0' && (x) <= '9') | |
29 | ||
30 | #define IPF_FTP_PROXY | |
31 | ||
32 | #define IPF_MINPORTLEN 18 | |
33 | #define IPF_MAXPORTLEN 30 | |
34 | ||
35 | ||
36 | int ippr_ftp_init __P((fr_info_t *, ip_t *, tcphdr_t *, | |
37 | ap_session_t *, nat_t *)); | |
38 | int ippr_ftp_in __P((fr_info_t *, ip_t *, tcphdr_t *, | |
39 | ap_session_t *, nat_t *)); | |
40 | int ippr_ftp_out __P((fr_info_t *, ip_t *, tcphdr_t *, | |
41 | ap_session_t *, nat_t *)); | |
42 | u_short ipf_ftp_atoi __P((char **)); | |
43 | ||
44 | ||
45 | /* | |
46 | * FTP application proxy initialization. | |
47 | */ | |
48 | int ippr_ftp_init(fin, ip, tcp, aps, nat) | |
49 | fr_info_t *fin; | |
50 | ip_t *ip; | |
51 | tcphdr_t *tcp; | |
52 | ap_session_t *aps; | |
53 | nat_t *nat; | |
54 | { | |
55 | aps->aps_sport = tcp->th_sport; | |
56 | aps->aps_dport = tcp->th_dport; | |
57 | return 0; | |
58 | } | |
59 | ||
60 | ||
61 | int ippr_ftp_in(fin, ip, tcp, aps, nat) | |
62 | fr_info_t *fin; | |
63 | ip_t *ip; | |
64 | tcphdr_t *tcp; | |
65 | ap_session_t *aps; | |
66 | nat_t *nat; | |
67 | { | |
68 | u_32_t sum1, sum2; | |
69 | short sel; | |
70 | ||
71 | if (tcp->th_sport == aps->aps_dport) { | |
72 | sum2 = (u_32_t)ntohl(tcp->th_ack); | |
73 | sel = aps->aps_sel; | |
74 | if ((aps->aps_after[!sel] > aps->aps_after[sel]) && | |
75 | (sum2 > aps->aps_after[!sel])) { | |
76 | sel = aps->aps_sel = !sel; /* switch to other set */ | |
77 | } | |
78 | if (aps->aps_seqoff[sel] && (sum2 > aps->aps_after[sel])) { | |
79 | sum1 = (u_32_t)aps->aps_seqoff[sel]; | |
80 | tcp->th_ack = htonl(sum2 - sum1); | |
81 | return 2; | |
82 | } | |
83 | } | |
84 | return 0; | |
85 | } | |
86 | ||
87 | ||
88 | /* | |
89 | * ipf_ftp_atoi - implement a version of atoi which processes numbers in | |
90 | * pairs separated by commas (which are expected to be in the range 0 - 255), | |
91 | * returning a 16 bit number combining either side of the , as the MSB and | |
92 | * LSB. | |
93 | */ | |
94 | u_short ipf_ftp_atoi(ptr) | |
95 | char **ptr; | |
96 | { | |
97 | register char *s = *ptr, c; | |
98 | register u_char i = 0, j = 0; | |
99 | ||
100 | while ((c = *s++) && isdigit(c)) { | |
101 | i *= 10; | |
102 | i += c - '0'; | |
103 | } | |
104 | if (c != ',') { | |
105 | *ptr = NULL; | |
106 | return 0; | |
107 | } | |
108 | while ((c = *s++) && isdigit(c)) { | |
109 | j *= 10; | |
110 | j += c - '0'; | |
111 | } | |
112 | *ptr = s; | |
113 | return (i << 8) | j; | |
114 | } | |
115 | ||
116 | ||
117 | int ippr_ftp_out(fin, ip, tcp, aps, nat) | |
118 | fr_info_t *fin; | |
119 | ip_t *ip; | |
120 | tcphdr_t *tcp; | |
121 | ap_session_t *aps; | |
122 | nat_t *nat; | |
123 | { | |
124 | register u_32_t sum1, sum2; | |
125 | char newbuf[IPF_MAXPORTLEN+1]; | |
126 | char portbuf[IPF_MAXPORTLEN+1], *s; | |
127 | int ch = 0, off = (ip->ip_hl << 2) + (tcp->th_off << 2); | |
128 | u_int a1, a2, a3, a4; | |
129 | u_short a5, a6; | |
130 | int olen, dlen, nlen = 0, inc = 0; | |
131 | tcphdr_t tcph, *tcp2 = &tcph; | |
132 | void *savep; | |
133 | nat_t *ipn; | |
134 | struct in_addr swip; | |
135 | mb_t *m = *(mb_t **)fin->fin_mp; | |
136 | ||
137 | #if SOLARIS | |
138 | mb_t *m1; | |
139 | ||
140 | /* skip any leading M_PROTOs */ | |
141 | while(m && (MTYPE(m) != M_DATA)) | |
142 | m = m->b_cont; | |
143 | PANIC((!m),("ippr_ftp_out: no M_DATA")); | |
144 | ||
145 | dlen = msgdsize(m) - off; | |
146 | bzero(portbuf, sizeof(portbuf)); | |
147 | copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); | |
148 | #else | |
149 | dlen = mbufchainlen(m) - off; | |
150 | bzero(portbuf, sizeof(portbuf)); | |
151 | m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); | |
152 | #endif | |
153 | portbuf[IPF_MAXPORTLEN] = '\0'; | |
154 | ||
155 | if ((dlen < IPF_MINPORTLEN) || strncmp(portbuf, "PORT ", 5)) | |
156 | goto adjust_seqack; | |
157 | ||
158 | /* | |
159 | * Skip the PORT command + space | |
160 | */ | |
161 | s = portbuf + 5; | |
162 | /* | |
163 | * Pick out the address components, two at a time. | |
164 | */ | |
165 | (void) ipf_ftp_atoi(&s); | |
166 | if (!s) | |
167 | goto adjust_seqack; | |
168 | (void) ipf_ftp_atoi(&s); | |
169 | if (!s) | |
170 | goto adjust_seqack; | |
171 | a5 = ipf_ftp_atoi(&s); | |
172 | if (!s) | |
173 | goto adjust_seqack; | |
174 | /* | |
175 | * check for CR-LF at the end. | |
176 | */ | |
177 | if (*s != '\n' || *(s - 1) != '\r') | |
178 | goto adjust_seqack; | |
179 | a6 = a5 & 0xff; | |
180 | a5 >>= 8; | |
181 | /* | |
182 | * Calculate new address parts for PORT command | |
183 | */ | |
184 | a1 = ntohl(ip->ip_src.s_addr); | |
185 | a2 = (a1 >> 16) & 0xff; | |
186 | a3 = (a1 >> 8) & 0xff; | |
187 | a4 = a1 & 0xff; | |
188 | a1 >>= 24; | |
189 | olen = s - portbuf + 1; | |
190 | (void) snprintf(newbuf, sizeof(newbuf), "PORT %d,%d,%d,%d,%d,%d\r\n", | |
191 | a1, a2, a3, a4, a5, a6); | |
192 | nlen = strlen(newbuf); | |
193 | inc = nlen - olen; | |
194 | #if SOLARIS | |
195 | for (m1 = m; m1->b_cont; m1 = m1->b_cont) | |
196 | ; | |
197 | if (inc > 0) { | |
198 | mblk_t *nm; | |
199 | ||
200 | /* alloc enough to keep same trailer space for lower driver */ | |
201 | nm = allocb(nlen + m1->b_datap->db_lim - m1->b_wptr, BPRI_MED); | |
202 | PANIC((!nm),("ippr_ftp_out: allocb failed")); | |
203 | ||
204 | nm->b_band = m1->b_band; | |
205 | nm->b_wptr += nlen; | |
206 | ||
207 | m1->b_wptr -= olen; | |
208 | PANIC((m1->b_wptr < m1->b_rptr),("ippr_ftp_out: cannot handle fragmented data block")); | |
209 | ||
210 | linkb(m1, nm); | |
211 | } else { | |
212 | m1->b_wptr += inc; | |
213 | } | |
214 | copyin_mblk(m, off, nlen, newbuf); | |
215 | #else | |
216 | if (inc < 0) | |
217 | m_adj(m, inc); | |
218 | /* the mbuf chain will be extended if necessary by m_copyback() */ | |
219 | m_copyback(m, off, nlen, newbuf); | |
220 | #endif | |
221 | if (inc) { | |
222 | #if SOLARIS || defined(__sgi) | |
223 | sum1 = ip->ip_len; | |
224 | sum2 = ip->ip_len + inc; | |
225 | ||
226 | /* Because ~1 == -2, We really need ~1 == -1 */ | |
227 | if (sum1 > sum2) | |
228 | sum2--; | |
229 | sum2 -= sum1; | |
230 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | |
231 | ||
232 | fix_outcksum(&ip->ip_sum, sum2); | |
233 | #endif | |
234 | ip->ip_len += inc; | |
235 | } | |
236 | ch = 1; | |
237 | ||
238 | /* | |
239 | * Add skeleton NAT entry for connection which will come back the | |
240 | * other way. | |
241 | */ | |
242 | savep = fin->fin_dp; | |
243 | fin->fin_dp = (char *)tcp2; | |
244 | bzero((char *)tcp2, sizeof(*tcp2)); | |
245 | tcp2->th_sport = htons(a5 << 8 | a6); | |
246 | tcp2->th_dport = htons(20); | |
247 | swip = ip->ip_src; | |
248 | ip->ip_src = nat->nat_inip; | |
249 | if ((ipn = nat_new(nat->nat_ptr, ip, fin, IPN_TCP, NAT_OUTBOUND))) | |
250 | ipn->nat_age = fr_defnatage; | |
251 | (void) fr_addstate(ip, fin, FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE); | |
252 | ip->ip_src = swip; | |
253 | fin->fin_dp = (char *)savep; | |
254 | ||
255 | adjust_seqack: | |
256 | if (tcp->th_dport == aps->aps_dport) { | |
257 | sum2 = (u_32_t)ntohl(tcp->th_seq); | |
258 | off = aps->aps_sel; | |
259 | if ((aps->aps_after[!off] > aps->aps_after[off]) && | |
260 | (sum2 > aps->aps_after[!off])) { | |
261 | off = aps->aps_sel = !off; /* switch to other set */ | |
262 | } | |
263 | if (aps->aps_seqoff[off]) { | |
264 | sum1 = (u_32_t)aps->aps_after[off] - | |
265 | aps->aps_seqoff[off]; | |
266 | if (sum2 > sum1) { | |
267 | sum1 = (u_32_t)aps->aps_seqoff[off]; | |
268 | sum2 += sum1; | |
269 | tcp->th_seq = htonl(sum2); | |
270 | ch = 1; | |
271 | } | |
272 | } | |
273 | ||
274 | if (inc && (sum2 > aps->aps_after[!off])) { | |
275 | aps->aps_after[!off] = sum2 + nlen - 1; | |
276 | aps->aps_seqoff[!off] = aps->aps_seqoff[off] + inc; | |
277 | } | |
278 | } | |
279 | return ch ? 2 : 0; | |
280 | } |