2 * Simple FTP transparent proxy for in-kernel use. For use with the NAT
6 #if SOLARIS && defined(_KERNEL)
7 extern kmutex_t ipf_rw;
10 #define isdigit(x) ((x) >= '0' && (x) <= '9')
14 #define IPF_MINPORTLEN 18
15 #define IPF_MAXPORTLEN 30
16 #define IPF_MIN227LEN 39
17 #define IPF_MAX227LEN 51
20 int ippr_ftp_init __P((void));
21 int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
22 int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
23 int ippr_ftp_portmsg __P((fr_info_t *, ip_t *, nat_t *));
24 int ippr_ftp_pasvmsg __P((fr_info_t *, ip_t *, nat_t *));
26 u_short ipf_ftp_atoi __P((char **));
28 static frentry_t natfr;
32 * Initialize local structures.
36 bzero((char *)&natfr, sizeof(natfr));
38 natfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
44 * ipf_ftp_atoi - implement a version of atoi which processes numbers in
45 * pairs separated by commas (which are expected to be in the range 0 - 255),
46 * returning a 16 bit number combining either side of the , as the MSB and
49 u_short ipf_ftp_atoi(ptr)
52 register char *s = *ptr, c;
53 register u_char i = 0, j = 0;
55 while ((c = *s++) && isdigit(c)) {
63 while ((c = *s++) && isdigit(c)) {
72 int ippr_ftp_portmsg(fin, ip, nat)
77 char portbuf[IPF_MAXPORTLEN + 1], newbuf[IPF_MAXPORTLEN + 1], *s;
78 tcphdr_t *tcp, tcph, *tcp2 = &tcph;
79 size_t nlen = 0, dlen, olen;
80 u_short a5, a6, sp, dp;
91 tcp = (tcphdr_t *)fin->fin_dp;
92 bzero(portbuf, sizeof(portbuf));
93 off = (ip->ip_hl << 2) + (tcp->th_off << 2);
98 dlen = msgdsize(m) - off;
100 copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf);
102 m = *(mb_t **)fin->fin_mp;
104 dlen = mbufchainlen(m) - off;
106 m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf);
110 portbuf[sizeof(portbuf) - 1] = '\0';
112 if (!strncmp(portbuf, "PORT ", 5)) {
113 if (dlen < IPF_MINPORTLEN)
119 * Skip the PORT command + space
123 * Pick out the address components, two at a time.
125 a1 = ipf_ftp_atoi(&s);
128 a2 = ipf_ftp_atoi(&s);
133 * check that IP address in the PORT/PASV reply is the same as the
134 * sender of the command - prevents using PORT for port scanning.
138 if (a1 != ntohl(nat->nat_inip.s_addr))
141 a5 = ipf_ftp_atoi(&s);
148 * check for CR-LF at the end.
152 if ((*s == '\r') && (*(s + 1) == '\n')) {
159 * Calculate new address parts for PORT command
161 a1 = ntohl(ip->ip_src.s_addr);
162 a2 = (a1 >> 16) & 0xff;
163 a3 = (a1 >> 8) & 0xff;
167 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
168 "PORT", a1, a2, a3, a4, a5, a6);
170 nlen = strlen(newbuf);
173 for (m1 = m; m1->b_cont; m1 = m1->b_cont)
175 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
178 /* alloc enough to keep same trailer space for lower driver */
179 nm = allocb(nlen, BPRI_MED);
180 PANIC((!nm),("ippr_ftp_out: allocb failed"));
182 nm->b_band = m1->b_band;
186 PANIC((m1->b_wptr < m1->b_rptr),
187 ("ippr_ftp_out: cannot handle fragmented data block"));
191 if (m1->b_datap->db_struiolim == m1->b_wptr)
192 m1->b_datap->db_struiolim += inc;
193 m1->b_datap->db_struioflag &= ~STRUIO_IP;
196 copyin_mblk(m, off, nlen, newbuf);
200 /* the mbuf chain will be extended if necessary by m_copyback() */
201 m_copyback(m, off, nlen, newbuf);
204 #if SOLARIS || defined(__sgi)
205 register u_32_t sum1, sum2;
208 sum2 = ip->ip_len + inc;
210 /* Because ~1 == -2, We really need ~1 == -1 */
214 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
216 fix_outcksum(&ip->ip_sum, sum2, 0);
222 * Add skeleton NAT entry for connection which will come back the
225 sp = htons(a5 << 8 | a6);
227 * The server may not make the connection back from port 20, but
228 * it is the most likely so use it here to check for a conflicting
231 dp = htons(fin->fin_data[1] - 1);
232 ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip,
233 ip->ip_dst, (dp << 16) | sp);
235 bcopy((char *)fin, (char *)&fi, sizeof(fi));
236 bzero((char *)tcp2, sizeof(*tcp2));
237 tcp2->th_win = htons(8192);
239 tcp2->th_dport = 0; /* XXX - don't specify remote port */
240 fi.fin_data[0] = ntohs(sp);
242 fi.fin_dp = (char *)tcp2;
244 ip->ip_src = nat->nat_inip;
245 ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT,
248 ipn->nat_age = fr_defnatage;
249 (void) fr_addstate(ip, &fi, FI_W_DPORT);
257 int ippr_ftp_out(fin, ip, aps, nat)
263 return ippr_ftp_portmsg(fin, ip, nat);
267 int ippr_ftp_pasvmsg(fin, ip, nat)
272 char portbuf[IPF_MAX227LEN + 1], newbuf[IPF_MAX227LEN + 1], *s;
273 int off, olen, dlen, nlen = 0, inc = 0;
274 tcphdr_t tcph, *tcp2 = &tcph;
275 struct in_addr swip, swip2;
276 u_short a5, a6, dp, sp;
277 u_int a1, a2, a3, a4;
286 tcp = (tcphdr_t *)fin->fin_dp;
287 off = (ip->ip_hl << 2) + (tcp->th_off << 2);
288 m = *(mb_t **)fin->fin_mp;
289 bzero(portbuf, sizeof(portbuf));
294 dlen = msgdsize(m) - off;
296 copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf);
298 dlen = mbufchainlen(m) - off;
300 m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf);
304 portbuf[sizeof(portbuf) - 1] = '\0';
307 if (!strncmp(portbuf, "227 ", 4)) {
308 if (dlen < IPF_MIN227LEN)
310 else if (strncmp(portbuf, "227 Entering Passive Mode", 25))
315 * Skip the PORT command + space
318 while (*s && !isdigit(*s))
321 * Pick out the address components, two at a time.
323 a1 = ipf_ftp_atoi(&s);
326 a2 = ipf_ftp_atoi(&s);
331 * check that IP address in the PORT/PASV reply is the same as the
332 * sender of the command - prevents using PORT for port scanning.
336 if (a1 != ntohl(nat->nat_oip.s_addr))
339 a5 = ipf_ftp_atoi(&s);
348 * check for CR-LF at the end.
350 if ((*s == '\r') && (*(s + 1) == '\n')) {
357 * Calculate new address parts for 227 reply
359 a1 = ntohl(ip->ip_src.s_addr);
360 a2 = (a1 >> 16) & 0xff;
361 a3 = (a1 >> 8) & 0xff;
365 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
366 "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6);
368 nlen = strlen(newbuf);
371 for (m1 = m; m1->b_cont; m1 = m1->b_cont)
373 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
376 /* alloc enough to keep same trailer space for lower driver */
377 nm = allocb(nlen, BPRI_MED);
378 PANIC((!nm),("ippr_ftp_out: allocb failed"));
380 nm->b_band = m1->b_band;
384 PANIC((m1->b_wptr < m1->b_rptr),
385 ("ippr_ftp_out: cannot handle fragmented data block"));
391 copyin_mblk(m, off, nlen, newbuf);
395 /* the mbuf chain will be extended if necessary by m_copyback() */
396 m_copyback(m, off, nlen, newbuf);
399 #if SOLARIS || defined(__sgi)
400 register u_32_t sum1, sum2;
403 sum2 = ip->ip_len + inc;
405 /* Because ~1 == -2, We really need ~1 == -1 */
409 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
411 fix_outcksum(&ip->ip_sum, sum2, 0);
417 * Add skeleton NAT entry for connection which will come back the
421 dp = htons(fin->fin_data[1] - 1);
422 ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip,
423 ip->ip_dst, (dp << 16) | sp);
425 bcopy((char *)fin, (char *)&fi, sizeof(fi));
426 bzero((char *)tcp2, sizeof(*tcp2));
427 tcp2->th_win = htons(8192);
428 tcp2->th_sport = 0; /* XXX - fake it for nat_new */
429 fi.fin_data[0] = a5 << 8 | a6;
430 tcp2->th_dport = htons(fi.fin_data[0]);
432 fi.fin_dp = (char *)tcp2;
435 ip->ip_dst = ip->ip_src;
436 ip->ip_src = nat->nat_inip;
437 ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_SPORT,
440 ipn->nat_age = fr_defnatage;
441 (void) fr_addstate(ip, &fi, FI_W_SPORT);
450 int ippr_ftp_in(fin, ip, aps, nat)
457 return ippr_ftp_pasvmsg(fin, ip, nat);