2 * Simple FTP transparent proxy for in-kernel use. For use with the NAT
5 #if SOLARIS && defined(_KERNEL)
6 extern kmutex_t ipf_rw;
9 #define isdigit(x) ((x) >= '0' && (x) <= '9')
13 #define IPF_MINPORTLEN 18
14 #define IPF_MAXPORTLEN 30
15 #define IPF_MIN227LEN 39
16 #define IPF_MAX227LEN 51
19 int ippr_ftp_init __P((void));
20 int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
21 int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
22 int ippr_ftp_portmsg __P((fr_info_t *, ip_t *, nat_t *));
23 int ippr_ftp_pasvmsg __P((fr_info_t *, ip_t *, nat_t *));
25 u_short ipf_ftp_atoi __P((char **));
27 static frentry_t natfr;
31 * Initialize local structures.
35 bzero((char *)&natfr, sizeof(natfr));
37 natfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
43 * ipf_ftp_atoi - implement a version of atoi which processes numbers in
44 * pairs separated by commas (which are expected to be in the range 0 - 255),
45 * returning a 16 bit number combining either side of the , as the MSB and
48 u_short ipf_ftp_atoi(ptr)
51 register char *s = *ptr, c;
52 register u_char i = 0, j = 0;
54 while ((c = *s++) && isdigit(c)) {
62 while ((c = *s++) && isdigit(c)) {
71 int ippr_ftp_portmsg(fin, ip, nat)
76 char portbuf[IPF_MAXPORTLEN + 1], newbuf[IPF_MAXPORTLEN + 1], *s;
77 tcphdr_t *tcp, tcph, *tcp2 = &tcph;
78 size_t nlen = 0, dlen, olen;
79 u_short a5, a6, sp, dp;
90 tcp = (tcphdr_t *)fin->fin_dp;
91 bzero(portbuf, sizeof(portbuf));
92 off = (ip->ip_hl << 2) + (tcp->th_off << 2);
97 dlen = msgdsize(m) - off;
99 copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf);
101 m = *(mb_t **)fin->fin_mp;
103 dlen = mbufchainlen(m) - off;
105 m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf);
109 portbuf[sizeof(portbuf) - 1] = '\0';
111 if (!strncmp(portbuf, "PORT ", 5)) {
112 if (dlen < IPF_MINPORTLEN)
118 * Skip the PORT command + space
122 * Pick out the address components, two at a time.
124 a1 = ipf_ftp_atoi(&s);
127 a2 = ipf_ftp_atoi(&s);
132 * check that IP address in the PORT/PASV reply is the same as the
133 * sender of the command - prevents using PORT for port scanning.
137 if (a1 != ntohl(nat->nat_inip.s_addr))
140 a5 = ipf_ftp_atoi(&s);
147 * check for CR-LF at the end.
151 if ((*s == '\r') && (*(s + 1) == '\n')) {
158 * Calculate new address parts for PORT command
160 a1 = ntohl(ip->ip_src.s_addr);
161 a2 = (a1 >> 16) & 0xff;
162 a3 = (a1 >> 8) & 0xff;
166 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
167 "PORT", a1, a2, a3, a4, a5, a6);
169 nlen = strlen(newbuf);
172 for (m1 = m; m1->b_cont; m1 = m1->b_cont)
174 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
177 /* alloc enough to keep same trailer space for lower driver */
178 nm = allocb(nlen, BPRI_MED);
179 PANIC((!nm),("ippr_ftp_out: allocb failed"));
181 nm->b_band = m1->b_band;
185 PANIC((m1->b_wptr < m1->b_rptr),
186 ("ippr_ftp_out: cannot handle fragmented data block"));
190 if (m1->b_datap->db_struiolim == m1->b_wptr)
191 m1->b_datap->db_struiolim += inc;
192 m1->b_datap->db_struioflag &= ~STRUIO_IP;
195 copyin_mblk(m, off, nlen, newbuf);
199 /* the mbuf chain will be extended if necessary by m_copyback() */
200 m_copyback(m, off, nlen, newbuf);
203 #if SOLARIS || defined(__sgi)
204 register u_32_t sum1, sum2;
207 sum2 = ip->ip_len + inc;
209 /* Because ~1 == -2, We really need ~1 == -1 */
213 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
215 fix_outcksum(&ip->ip_sum, sum2);
221 * Add skeleton NAT entry for connection which will come back the
224 sp = htons(a5 << 8 | a6);
226 * The server may not make the connection back from port 20, but
227 * it is the most likely so use it here to check for a conflicting
230 dp = htons(fin->fin_data[1] - 1);
231 ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip,
232 ip->ip_dst, (dp << 16) | sp);
234 bcopy((char *)fin, (char *)&fi, sizeof(fi));
235 bzero((char *)tcp2, sizeof(*tcp2));
236 tcp2->th_win = htons(8192);
238 tcp2->th_dport = 0; /* XXX - don't specify remote port */
239 fi.fin_data[0] = ntohs(sp);
241 fi.fin_dp = (char *)tcp2;
243 ip->ip_src = nat->nat_inip;
244 ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT,
247 ipn->nat_age = fr_defnatage;
248 (void) fr_addstate(ip, &fi, FI_W_DPORT);
256 int ippr_ftp_out(fin, ip, aps, nat)
262 return ippr_ftp_portmsg(fin, ip, nat);
266 int ippr_ftp_pasvmsg(fin, ip, nat)
271 char portbuf[IPF_MAX227LEN + 1], newbuf[IPF_MAX227LEN + 1], *s;
272 int off, olen, dlen, nlen = 0, inc = 0;
273 tcphdr_t tcph, *tcp2 = &tcph;
274 struct in_addr swip, swip2;
275 u_short a5, a6, dp, sp;
276 u_int a1, a2, a3, a4;
285 tcp = (tcphdr_t *)fin->fin_dp;
286 off = (ip->ip_hl << 2) + (tcp->th_off << 2);
287 m = *(mb_t **)fin->fin_mp;
288 bzero(portbuf, sizeof(portbuf));
293 dlen = msgdsize(m) - off;
295 copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf);
297 dlen = mbufchainlen(m) - off;
299 m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf);
303 portbuf[sizeof(portbuf) - 1] = '\0';
306 if (!strncmp(portbuf, "227 ", 4)) {
307 if (dlen < IPF_MIN227LEN)
309 else if (strncmp(portbuf, "227 Entering Passive Mode", 25))
314 * Skip the PORT command + space
317 while (*s && !isdigit(*s))
320 * Pick out the address components, two at a time.
322 a1 = ipf_ftp_atoi(&s);
325 a2 = ipf_ftp_atoi(&s);
330 * check that IP address in the PORT/PASV reply is the same as the
331 * sender of the command - prevents using PORT for port scanning.
335 if (a1 != ntohl(nat->nat_oip.s_addr))
338 a5 = ipf_ftp_atoi(&s);
347 * check for CR-LF at the end.
349 if ((*s == '\r') && (*(s + 1) == '\n')) {
356 * Calculate new address parts for 227 reply
358 a1 = ntohl(ip->ip_src.s_addr);
359 a2 = (a1 >> 16) & 0xff;
360 a3 = (a1 >> 8) & 0xff;
364 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
365 "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6);
367 nlen = strlen(newbuf);
370 for (m1 = m; m1->b_cont; m1 = m1->b_cont)
372 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
375 /* alloc enough to keep same trailer space for lower driver */
376 nm = allocb(nlen, BPRI_MED);
377 PANIC((!nm),("ippr_ftp_out: allocb failed"));
379 nm->b_band = m1->b_band;
383 PANIC((m1->b_wptr < m1->b_rptr),
384 ("ippr_ftp_out: cannot handle fragmented data block"));
390 copyin_mblk(m, off, nlen, newbuf);
394 /* the mbuf chain will be extended if necessary by m_copyback() */
395 m_copyback(m, off, nlen, newbuf);
398 #if SOLARIS || defined(__sgi)
399 register u_32_t sum1, sum2;
402 sum2 = ip->ip_len + inc;
404 /* Because ~1 == -2, We really need ~1 == -1 */
408 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
410 fix_outcksum(&ip->ip_sum, sum2);
416 * Add skeleton NAT entry for connection which will come back the
420 dp = htons(fin->fin_data[1] - 1);
421 ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip,
422 ip->ip_dst, (dp << 16) | sp);
424 bcopy((char *)fin, (char *)&fi, sizeof(fi));
425 bzero((char *)tcp2, sizeof(*tcp2));
426 tcp2->th_win = htons(8192);
427 tcp2->th_sport = 0; /* XXX - fake it for nat_new */
428 fi.fin_data[0] = a5 << 8 | a6;
429 tcp2->th_dport = htons(fi.fin_data[0]);
431 fi.fin_dp = (char *)tcp2;
434 ip->ip_dst = ip->ip_src;
435 ip->ip_src = nat->nat_inip;
436 ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_SPORT,
439 ipn->nat_age = fr_defnatage;
440 (void) fr_addstate(ip, &fi, FI_W_SPORT);
449 int ippr_ftp_in(fin, ip, aps, nat)
456 return ippr_ftp_pasvmsg(fin, ip, nat);