Deleted Added
sdiff udiff text old ( 130886 ) new ( 145522 )
full compact
1/*
2 * Simple FTP transparent proxy for in-kernel use. For use with the NAT
3 * code.
4 *
5 * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 130886 2004-06-21 22:46:36Z darrenr $
6 */
7#if SOLARIS && defined(_KERNEL)
8extern kmutex_t ipf_rw;
9#endif
10
11#define isdigit(x) ((x) >= '0' && (x) <= '9')
12#define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z'))
13#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
14#define isalpha(x) (isupper(x) || islower(x))
15#define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A')
16
17#define IPF_FTP_PROXY
18
19#define IPF_MINPORTLEN 18
20#define IPF_MAXPORTLEN 30
21#define IPF_MIN227LEN 39
22#define IPF_MAX227LEN 51
23#define IPF_FTPBUFSZ 96 /* This *MUST* be >= 53! */
24
25#define FTPXY_GO 0
26#define FTPXY_INIT 1
27#define FTPXY_USER_1 2
28#define FTPXY_USOK_1 3
29#define FTPXY_PASS_1 4
30#define FTPXY_PAOK_1 5
31#define FTPXY_AUTH_1 6

--- 9 unchanged lines hidden (view full) ---

41
42/*
43 * Values for FTP commands. Numerics cover 0-999
44 */
45#define FTPXY_C_PASV 1000
46
47int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
48int ippr_ftp_complete __P((char *, size_t));
49int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
50int ippr_ftp_init __P((void));
51int ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
52int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
53int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
54int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
55int ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
56int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
57int ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t));
58int ippr_ftp_server_valid __P((ftpside_t *, char *, size_t));
59int ippr_ftp_client_valid __P((ftpside_t *, char *, size_t));
60u_short ippr_ftp_atoi __P((char **));
61
62static frentry_t ftppxyfr;
63int ippr_ftp_pasvonly = 0;
64int ippr_ftp_insecure = 0;
65int ippr_ftp_forcepasv = 0;
66
67
68/*
69 * Initialize local structures.
70 */
71int ippr_ftp_init()
72{
73 bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
74 ftppxyfr.fr_ref = 1;
75 ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
76 return 0;
77}
78
79
80int ippr_ftp_new(fin, ip, aps, nat)
81fr_info_t *fin;
82ip_t *ip;
83ap_session_t *aps;
84nat_t *nat;
85{
86 ftpinfo_t *ftp;
87 ftpside_t *f;
88
89 KMALLOC(ftp, ftpinfo_t *);
90 if (ftp == NULL)
91 return -1;
92 aps->aps_data = ftp;
93 aps->aps_psiz = sizeof(ftpinfo_t);
94
95 bzero((char *)ftp, sizeof(*ftp));
96 f = &ftp->ftp_side[0];
97 f->ftps_rptr = f->ftps_buf;
98 f->ftps_wptr = f->ftps_buf;
99 f = &ftp->ftp_side[1];
100 f->ftps_rptr = f->ftps_buf;
101 f->ftps_wptr = f->ftps_buf;
102 ftp->ftp_passok = FTPXY_INIT;
103 return 0;
104}
105
106
107int ippr_ftp_port(fin, ip, nat, f, dlen)
108fr_info_t *fin;
109ip_t *ip;
110nat_t *nat;
111ftpside_t *f;
112int dlen;
113{
114 tcphdr_t *tcp, tcph, *tcp2 = &tcph;
115 char newbuf[IPF_FTPBUFSZ], *s;
116 u_int a1, a2, a3, a4;
117 struct in_addr swip;
118 u_short a5, a6, sp;
119 size_t nlen, olen;
120 fr_info_t fi;
121 int inc, off;
122 nat_t *ipn;
123 mb_t *m;
124#if SOLARIS && defined(_KERNEL)
125 mb_t *m1;
126#endif
127
128 tcp = (tcphdr_t *)fin->fin_dp;
129 /*
130 * Check for client sending out PORT message.
131 */
132 if (dlen < IPF_MINPORTLEN) {
133#if !defined(_KERNEL) && !defined(KERNEL)
134 fprintf(stdout,
135 "ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", dlen);
136#endif
137 return 0;
138 }
139 off = fin->fin_hlen + (tcp->th_off << 2);
140 /*
141 * Skip the PORT command + space
142 */
143 s = f->ftps_rptr + 5;
144 /*
145 * Pick out the address components, two at a time.
146 */
147 a1 = ippr_ftp_atoi(&s);
148 if (s == NULL) {
149#if !defined(_KERNEL) && !defined(KERNEL)
150 fprintf(stdout, "ippr_ftp_port:ippr_ftp_atoi(1) failed\n");
151#endif
152 return 0;
153 }
154 a2 = ippr_ftp_atoi(&s);
155 if (s == NULL) {
156#if !defined(_KERNEL) && !defined(KERNEL)
157 fprintf(stdout, "ippr_ftp_port:ippr_ftp_atoi(2) failed\n");
158#endif
159 return 0;
160 }
161 /*
162 * check that IP address in the PORT/PASV reply is the same as the
163 * sender of the command - prevents using PORT for port scanning.
164 */
165 a1 <<= 16;
166 a1 |= a2;
167 if (a1 != ntohl(nat->nat_inip.s_addr)) {
168#if !defined(_KERNEL) && !defined(KERNEL)
169 fprintf(stdout, "ippr_ftp_port:a1 != nat->nat_inip\n");
170#endif
171 return 0;
172 }
173
174 a5 = ippr_ftp_atoi(&s);
175 if (s == NULL) {
176#if !defined(_KERNEL) && !defined(KERNEL)
177 fprintf(stdout, "ippr_ftp_port:ippr_ftp_atoi(3) failed\n");
178#endif
179 return 0;
180 }
181 if (*s == ')')
182 s++;
183
184 /*
185 * check for CR-LF at the end.
186 */
187 if (*s == '\n')
188 s--;
189 if ((*s == '\r') && (*(s + 1) == '\n')) {
190 s += 2;
191 a6 = a5 & 0xff;
192 } else {
193#if !defined(_KERNEL) && !defined(KERNEL)
194 fprintf(stdout, "ippr_ftp_port:missing cr-lf\n");
195#endif
196 return 0;
197 }
198 a5 >>= 8;
199 a5 &= 0xff;
200 /*
201 * Calculate new address parts for PORT command
202 */
203 a1 = ntohl(ip->ip_src.s_addr);
204 a2 = (a1 >> 16) & 0xff;
205 a3 = (a1 >> 8) & 0xff;
206 a4 = a1 & 0xff;
207 a1 >>= 24;
208 olen = s - f->ftps_rptr;
209 /* DO NOT change this to snprintf! */
210#if defined(OpenBSD) && (200311 >= 200311)
211 (void) snprintf(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
212 "PORT", a1, a2, a3, a4, a5, a6);
213#else
214 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
215 "PORT", a1, a2, a3, a4, a5, a6);
216#endif
217
218 nlen = strlen(newbuf);
219 inc = nlen - olen;
220 if ((inc + ip->ip_len) > 65535) {
221#if !defined(_KERNEL) && !defined(KERNEL)
222 fprintf(stdout,
223 "ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n", inc);
224#endif
225 return 0;
226 }
227
228#if !defined(_KERNEL)
229 m = *fin->fin_mp;
230 bcopy(newbuf, (char *)m + off, nlen);
231#else
232# if SOLARIS
233 m = fin->fin_qfm;
234 for (m1 = m; m1->b_cont; m1 = m1->b_cont)
235 ;
236 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
237 mblk_t *nm;
238
239 /* alloc enough to keep same trailer space for lower driver */
240 nm = allocb(nlen, BPRI_MED);
241 PANIC((!nm),("ippr_ftp_out: allocb failed"));
242
243 nm->b_band = m1->b_band;
244 nm->b_wptr += nlen;
245
246 m1->b_wptr -= olen;
247 PANIC((m1->b_wptr < m1->b_rptr),
248 ("ippr_ftp_out: cannot handle fragmented data block"));
249
250 linkb(m1, nm);
251 } else {
252 if (m1->b_datap->db_struiolim == m1->b_wptr)
253 m1->b_datap->db_struiolim += inc;
254 m1->b_datap->db_struioflag &= ~STRUIO_IP;
255 m1->b_wptr += inc;
256 }
257 copyin_mblk(m, off, nlen, newbuf);
258# else
259 m = *fin->fin_mp;
260 if (inc < 0)
261 m_adj(m, inc);
262 /* the mbuf chain will be extended if necessary by m_copyback() */
263 m_copyback(m, off, nlen, newbuf);
264# ifdef M_PKTHDR
265 if (!(m->m_flags & M_PKTHDR))
266 m->m_pkthdr.len += inc;
267# endif
268# endif
269#endif
270 if (inc != 0) {
271#if ((SOLARIS || defined(__sgi)) && defined(_KERNEL)) || !defined(_KERNEL)
272 register u_32_t sum1, sum2;
273
274 sum1 = ip->ip_len;
275 sum2 = ip->ip_len + inc;
276
277 /* Because ~1 == -2, We really need ~1 == -1 */
278 if (sum1 > sum2)
279 sum2--;
280 sum2 -= sum1;
281 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
282
283 fix_outcksum(fin, &ip->ip_sum, sum2);
284#endif
285 ip->ip_len += inc;
286 }
287
288 /*
289 * Add skeleton NAT entry for connection which will come back the
290 * other way.
291 */
292 sp = (a5 << 8 | a6);
293 /*
294 * Don't allow the PORT command to specify a port < 1024 due to
295 * security crap.
296 */
297 if (sp < 1024) {
298#if !defined(_KERNEL) && !defined(KERNEL)
299 fprintf(stdout, "ippr_ftp_port:sp(%d) < 1024\n", sp);
300#endif
301 return 0;
302 }
303
304 /*
305 * The server may not make the connection back from port 20, but
306 * it is the most likely so use it here to check for a conflicting
307 * mapping.
308 */
309 bcopy((char *)fin, (char *)&fi, sizeof(fi));
310 fi.fin_data[0] = sp;
311 fi.fin_data[1] = fin->fin_data[1] - 1;
312 ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip,
313 ip->ip_dst, 0);
314 if (ipn == NULL) {
315 int slen;
316
317 slen = ip->ip_len;
318 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
319 bzero((char *)tcp2, sizeof(*tcp2));
320 tcp2->th_win = htons(8192);
321 tcp2->th_sport = htons(sp);
322 tcp2->th_off = 5;
323 tcp2->th_flags = TH_SYN;
324 tcp2->th_dport = 0; /* XXX - don't specify remote port */
325 fi.fin_data[1] = 0;
326 fi.fin_dlen = sizeof(*tcp2);
327 fi.fin_dp = (char *)tcp2;
328 fi.fin_fr = &ftppxyfr;
329 fi.fin_out = 1;
330 swip = ip->ip_src;
331 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
332 ip->ip_src = nat->nat_inip;
333 ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_DPORT,
334 NAT_OUTBOUND);
335 if (ipn != NULL) {
336 ipn->nat_age = fr_defnatage;
337 (void) fr_addstate(ip, &fi, NULL,
338 FI_W_DPORT|FI_IGNOREPKT);
339 }
340 ip->ip_len = slen;
341 ip->ip_src = swip;
342 }
343 return inc;
344}
345
346
347int ippr_ftp_client(fin, ip, nat, ftp, dlen)
348fr_info_t *fin;
349nat_t *nat;
350ftpinfo_t *ftp;
351ip_t *ip;

--- 5 unchanged lines hidden (view full) ---

357
358 inc = 0;
359 f = &ftp->ftp_side[0];
360 rptr = f->ftps_rptr;
361 wptr = f->ftps_wptr;
362
363 for (i = 0; (i < 5) && (i < dlen); i++) {
364 c = rptr[i];
365 if (isalpha(c)) {
366 cmd[i] = toupper(c);
367 } else {
368 cmd[i] = c;
369 }
370 }
371 cmd[i] = '\0';
372
373 ftp->ftp_incok = 0;
374 if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {

--- 42 unchanged lines hidden (view full) ---

417
418int ippr_ftp_pasv(fin, ip, nat, ftp, dlen)
419fr_info_t *fin;
420ip_t *ip;
421nat_t *nat;
422ftpinfo_t *ftp;
423int dlen;
424{
425 tcphdr_t *tcp, tcph, *tcp2 = &tcph;
426 struct in_addr swip, swip2;
427 u_int a1, a2, a3, a4;
428 u_short a5, a6, dp;
429 fr_info_t fi;
430 ftpside_t *f;
431 nat_t *ipn;
432 int inc;
433 char *s;
434
435 if (ippr_ftp_forcepasv != 0 &&
436 ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) {
437#if !defined(_KERNEL) && !defined(KERNEL)
438 fprintf(stdout,
439 "ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n",
440 ftp->ftp_side[0].ftps_cmds);
441#endif
442 return 0;
443 }
444
445 f = &ftp->ftp_side[1];
446
447#define PASV_REPLEN 24
448 /*
449 * Check for PASV reply message.
450 */
451 if (dlen < IPF_MIN227LEN) {
452#if !defined(_KERNEL) && !defined(KERNEL)
453 fprintf(stdout,
454 "ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", dlen);
455#endif
456 return 0;
457 } else if (strncmp(f->ftps_rptr,
458 "227 Entering Passive Mod", PASV_REPLEN)) {
459#if !defined(_KERNEL) && !defined(KERNEL)
460 fprintf(stdout, "ippr_ftp_pasv:227 reply wrong\n");
461#endif
462 return 0;
463 }
464
465 tcp = (tcphdr_t *)fin->fin_dp;
466
467 /*
468 * Skip the PASV reply + space
469 */
470 s = f->ftps_rptr + PASV_REPLEN;
471 while (*s && !isdigit(*s))
472 s++;
473 /*
474 * Pick out the address components, two at a time.
475 */
476 a1 = ippr_ftp_atoi(&s);
477 if (s == NULL) {
478#if !defined(_KERNEL) && !defined(KERNEL)
479 fprintf(stdout, "ippr_ftp_pasv:ippr_ftp_atoi(1) failed\n");
480#endif
481 return 0;
482 }
483 a2 = ippr_ftp_atoi(&s);
484 if (s == NULL) {
485#if !defined(_KERNEL) && !defined(KERNEL)
486 fprintf(stdout, "ippr_ftp_pasv:ippr_ftp_atoi(2) failed\n");
487#endif
488 return 0;
489 }
490
491 /*
492 * check that IP address in the PORT/PASV reply is the same as the
493 * sender of the command - prevents using PORT for port scanning.
494 */
495 a1 <<= 16;
496 a1 |= a2;
497 if (a1 != ntohl(nat->nat_oip.s_addr)) {
498#if !defined(_KERNEL) && !defined(KERNEL)
499 fprintf(stdout, "ippr_ftp_pasv:a1 != nat->nat_oip\n");
500#endif
501 return 0;
502 }
503
504 a5 = ippr_ftp_atoi(&s);
505 if (s == NULL) {
506#if !defined(_KERNEL) && !defined(KERNEL)
507 fprintf(stdout, "ippr_ftp_pasv:ippr_ftp_atoi(3) failed\n");
508#endif
509 return 0;
510 }
511
512 if (*s == ')')
513 s++;
514 if (*s == '.')
515 s++;
516 if (*s == '\n')
517 s--;
518 /*
519 * check for CR-LF at the end.
520 */
521 if ((*s == '\r') && (*(s + 1) == '\n')) {
522 s += 2;
523 a6 = a5 & 0xff;
524 } else {
525#if !defined(_KERNEL) && !defined(KERNEL)
526 fprintf(stdout, "ippr_ftp_pasv:missing cr-lf\n");
527#endif
528 return 0;
529 }
530 a5 >>= 8;
531 /*
532 * Calculate new address parts for 227 reply
533 */
534 a1 = ntohl(ip->ip_src.s_addr);
535 a2 = (a1 >> 16) & 0xff;
536 a3 = (a1 >> 8) & 0xff;
537 a4 = a1 & 0xff;
538 a1 >>= 24;
539 inc = 0;
540#if 0
541 olen = s - f->ftps_rptr;
542 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
543 "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6);
544 nlen = strlen(newbuf);
545 inc = nlen - olen;
546 if ((inc + ip->ip_len) > 65535)
547 return 0;
548
549#if !defined(_KERNEL)
550 m = *fin->fin_mp;
551 m_copyback(m, off, nlen, newbuf);
552#else
553# if SOLARIS
554 m = fin->fin_qfm;
555 for (m1 = m; m1->b_cont; m1 = m1->b_cont)
556 ;
557 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
558 mblk_t *nm;
559
560 /* alloc enough to keep same trailer space for lower driver */
561 nm = allocb(nlen, BPRI_MED);
562 PANIC((!nm),("ippr_ftp_out: allocb failed"));
563
564 nm->b_band = m1->b_band;
565 nm->b_wptr += nlen;
566
567 m1->b_wptr -= olen;
568 PANIC((m1->b_wptr < m1->b_rptr),
569 ("ippr_ftp_out: cannot handle fragmented data block"));
570
571 linkb(m1, nm);
572 } else {
573 m1->b_wptr += inc;
574 }
575 /*copyin_mblk(m, off, nlen, newbuf);*/
576# else /* SOLARIS */
577 m = *fin->fin_mp;
578 if (inc < 0)
579 m_adj(m, inc);
580 /* the mbuf chain will be extended if necessary by m_copyback() */
581 /*m_copyback(m, off, nlen, newbuf);*/
582# endif /* SOLARIS */
583#endif /* _KERNEL */
584 if (inc != 0) {
585#if ((SOLARIS || defined(__sgi)) && defined(_KERNEL)) || !defined(_KERNEL)
586 register u_32_t sum1, sum2;
587
588 sum1 = ip->ip_len;
589 sum2 = ip->ip_len + inc;
590
591 /* Because ~1 == -2, We really need ~1 == -1 */
592 if (sum1 > sum2)
593 sum2--;
594 sum2 -= sum1;
595 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
596
597 fix_outcksum(fin, &ip->ip_sum, sum2);
598#endif /* SOLARIS || defined(__sgi) */
599 ip->ip_len += inc;
600 }
601#endif /* 0 */
602
603 /*
604 * Add skeleton NAT entry for connection which will come back the
605 * other way.
606 */
607 bcopy((char *)fin, (char *)&fi, sizeof(fi));
608 fi.fin_data[0] = 0;
609 dp = htons(fin->fin_data[1] - 1);
610 fi.fin_data[1] = ntohs(dp);
611 ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip,
612 ip->ip_dst, 0);
613 if (ipn == NULL) {
614 int slen;
615
616 slen = ip->ip_len;
617 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
618 bzero((char *)tcp2, sizeof(*tcp2));
619 tcp2->th_win = htons(8192);
620 tcp2->th_sport = 0; /* XXX - fake it for nat_new */
621 tcp2->th_off = 5;
622 tcp2->th_flags = TH_SYN;
623 fi.fin_data[1] = a5 << 8 | a6;
624 fi.fin_dlen = sizeof(*tcp2);
625 tcp2->th_dport = htons(fi.fin_data[1]);
626 fi.fin_data[0] = 0;
627 fi.fin_dp = (char *)tcp2;
628 fi.fin_fr = &ftppxyfr;
629 fi.fin_out = 1;
630 swip = ip->ip_src;
631 swip2 = ip->ip_dst;
632 fi.fin_fi.fi_daddr = ip->ip_src.s_addr;
633 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
634 ip->ip_dst = ip->ip_src;
635 ip->ip_src = nat->nat_inip;
636 ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_SPORT,
637 NAT_OUTBOUND);
638 if (ipn != NULL) {
639 ipn->nat_age = fr_defnatage;
640 (void) fr_addstate(ip, &fi, NULL,
641 FI_W_SPORT|FI_IGNOREPKT);
642 }
643 ip->ip_len = slen;
644 ip->ip_src = swip;
645 ip->ip_dst = swip2;
646 }
647 return inc;
648}
649
650
651int ippr_ftp_server(fin, ip, nat, ftp, dlen)
652fr_info_t *fin;
653ip_t *ip;

--- 5 unchanged lines hidden (view full) ---

659 ftpside_t *f;
660 int inc;
661
662 inc = 0;
663 f = &ftp->ftp_side[1];
664 rptr = f->ftps_rptr;
665 wptr = f->ftps_wptr;
666
667 if (!isdigit(*rptr) || !isdigit(*(rptr + 1)) || !isdigit(*(rptr + 2)))
668 return 0;
669 if (ftp->ftp_passok == FTPXY_GO) {
670 if (!strncmp(rptr, "227 ", 4))
671 inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen);
672 } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
673 inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen);
674 } else if (*rptr == '5' || *rptr == '4')
675 ftp->ftp_passok = FTPXY_INIT;
676 else if (ftp->ftp_incok) {
677 if (*rptr == '3') {
678 if (ftp->ftp_passok == FTPXY_ACCT_1)
679 ftp->ftp_passok = FTPXY_GO;
680 else
681 ftp->ftp_passok++;

--- 8 unchanged lines hidden (view full) ---

690 ftp->ftp_passok = FTPXY_GO;
691 break;
692 default :
693 ftp->ftp_passok += 3;
694 break;
695 }
696 }
697 }
698 ftp->ftp_incok = 0;
699
700 while ((*rptr++ != '\n') && (rptr < wptr))
701 ;
702 f->ftps_rptr = rptr;
703 return inc;
704}
705
706
707/*
708 * Look to see if the buffer starts with something which we recognise as
709 * being the correct syntax for the FTP protocol.
710 */
711int ippr_ftp_client_valid(ftps, buf, len)
712ftpside_t *ftps;
713char *buf;
714size_t len;
715{
716 register char *s, c;
717 register size_t i = len;
718 char cmd[5];
719
720 if (i < 5) {
721#if !defined(_KERNEL) && !defined(KERNEL)
722 fprintf(stdout, "ippr_ftp_client_valid:i(%lu) < 5\n",
723 (u_long)i);
724#endif
725 return 2;
726 }
727 s = buf;
728 c = *s++;
729 i--;
730
731 if (isalpha(c)) {
732 cmd[0] = toupper(c);
733 c = *s++;
734 i--;
735 if (isalpha(c)) {
736 cmd[1] = toupper(c);
737 c = *s++;
738 i--;
739 if (isalpha(c)) {
740 cmd[2] = toupper(c);
741 c = *s++;
742 i--;
743 if (isalpha(c)) {
744 cmd[3] = toupper(c);
745 c = *s++;
746 i--;
747 if ((c != ' ') && (c != '\r'))
748 goto bad_client_command;
749 } else if ((c != ' ') && (c != '\r'))
750 goto bad_client_command;
751 } else
752 goto bad_client_command;
753 } else
754 goto bad_client_command;
755 } else {
756bad_client_command:
757#if !defined(_KERNEL) && !defined(KERNEL)
758 fprintf(stdout,
759 "ippr_ftp_client_valid:bad cmd:len %lu i %lu c 0x%x\n",
760 (u_long)i, (u_long)len, c);
761#endif
762 return 1;
763 }
764
765 for (; i; i--) {
766 c = *s++;
767 if (c == '\n') {
768 cmd[4] = '\0';
769 if (!strcmp(cmd, "PASV"))
770 ftps->ftps_cmds = FTPXY_C_PASV;
771 else
772 ftps->ftps_cmds = 0;
773 return 0;
774 }
775 }
776#if !defined(_KERNEL) && !defined(KERNEL)
777 fprintf(stdout, "ippr_ftp_client_valid:junk after cmd[%s]\n", buf);
778#endif
779 return 2;
780}
781
782
783int ippr_ftp_server_valid(ftps, buf, len)
784ftpside_t *ftps;
785char *buf;
786size_t len;
787{
788 register char *s, c;
789 register size_t i = len;
790 int cmd;
791
792 if (i < 5)
793 return 2;
794 s = buf;
795 c = *s++;
796 cmd = 0;
797 i--;
798
799 if (isdigit(c)) {
800 cmd = (c - '0') * 100;
801 c = *s++;
802 i--;
803 if (isdigit(c)) {
804 cmd += (c - '0') * 10;
805 c = *s++;
806 i--;
807 if (isdigit(c)) {
808 cmd += (c - '0');
809 c = *s++;
810 i--;
811 if ((c != '-') && (c != ' '))
812 goto bad_server_command;
813 } else
814 goto bad_server_command;
815 } else
816 goto bad_server_command;
817 } else {
818bad_server_command:
819#if !defined(_KERNEL) && !defined(KERNEL)
820 fprintf(stdout,
821 "ippr_ftp_server_valid:bad cmd:len %lu i %lu c 0x%x\n",
822 (u_long)i, (u_long)len, c);
823#endif
824 return 1;
825 }
826
827 for (; i; i--) {
828 c = *s++;
829 if (c == '\n') {
830 ftps->ftps_cmds = cmd;
831 return 0;
832 }
833 }
834#if !defined(_KERNEL) && !defined(KERNEL)
835 fprintf(stdout, "ippr_ftp_server_valid:junk after cmd[%s]\n", buf);
836#endif
837 return 2;
838}
839
840
841int ippr_ftp_valid(ftp, side, buf, len)
842ftpinfo_t *ftp;
843int side;
844char *buf;

--- 8 unchanged lines hidden (view full) ---

853 ret = ippr_ftp_client_valid(ftps, buf, len);
854 else
855 ret = ippr_ftp_server_valid(ftps, buf, len);
856 return ret;
857}
858
859
860/*
861 * rv == 0 for outbound processing,
862 * rv == 1 for inbound processing.
863 */
864int ippr_ftp_process(fin, ip, nat, ftp, rv)
865fr_info_t *fin;
866ip_t *ip;
867nat_t *nat;
868ftpinfo_t *ftp;
869int rv;
870{
871 int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff;
872 u_32_t thseq, thack;
873 char *rptr, *wptr;
874 ap_session_t *aps;
875 ftpside_t *f, *t;
876 tcphdr_t *tcp;
877 mb_t *m;
878
879 tcp = (tcphdr_t *)fin->fin_dp;
880 off = fin->fin_hlen + (tcp->th_off << 2);
881#if SOLARIS && defined(_KERNEL)
882 m = fin->fin_qfm;
883#else
884 m = *fin->fin_mp;
885#endif
886
887#ifndef _KERNEL
888 mlen = mbuflen(m);
889#else
890# if SOLARIS
891 mlen = msgdsize(m);
892# else
893 mlen = mbufchainlen(m);
894# endif
895#endif
896 mlen -= off;
897
898 aps = nat->nat_aps;
899 t = &ftp->ftp_side[1 - rv];
900 f = &ftp->ftp_side[rv];
901 thseq = ntohl(tcp->th_seq);
902 thack = ntohl(tcp->th_ack);
903
904 sel = aps->aps_sel[1 - rv];
905 sel2 = aps->aps_sel[rv];
906 if (rv == 0) {
907 seqoff = aps->aps_seqoff[sel];
908 if (aps->aps_seqmin[sel] > seqoff + thseq)
909 seqoff = aps->aps_seqoff[!sel];
910 ackoff = aps->aps_ackoff[sel2];
911 if (aps->aps_ackmin[sel2] > ackoff + thack)
912 ackoff = aps->aps_ackoff[!sel2];
913 } else {
914#if PROXY_DEBUG
915 printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
916 aps->aps_ackmin[sel]);
917#endif
918 seqoff = aps->aps_ackoff[sel];
919 if (aps->aps_ackmin[sel] > seqoff + thseq)
920 seqoff = aps->aps_ackoff[!sel];
921
922#if PROXY_DEBUG
923 printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
924 aps->aps_seqmin[sel2]);
925#endif
926 ackoff = aps->aps_seqoff[sel2];
927 if (ackoff > 0) {
928 if (aps->aps_seqmin[sel2] > ackoff + thack)
929 ackoff = aps->aps_seqoff[!sel2];
930 } else {
931 if (aps->aps_seqmin[sel2] > thack)
932 ackoff = aps->aps_seqoff[!sel2];
933 }
934 }
935#if PROXY_DEBUG
936 printf("%s: %x seq %x/%d ack %x/%d len %d\n", rv ? "IN" : "OUT",
937 tcp->th_flags, thseq, seqoff, thack, ackoff, mlen);
938 printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
939 aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
940 aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
941 printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
942 aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
943 aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
944#endif
945
946 /*
947 * XXX - Ideally, this packet should get dropped because we now know
948 * that it is out of order (and there is no real danger in doing so
949 * apart from causing packets to go through here ordered).
950 */
951#if PROXY_DEBUG
952 printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
953 rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
954#endif
955
956 ok = 0;
957 if (t->ftps_seq[0] == 0) {
958 t->ftps_seq[0] = thack;
959 ok = 1;
960 } else {
961 if (ackoff == 0) {
962 if (t->ftps_seq[0] == thack)

--- 12 unchanged lines hidden (view full) ---

975 ok = 1;
976 } else if (t->ftps_seq[1] == thack + ackoff) {
977 t->ftps_seq[0] = thack - ackoff;
978 ok = 1;
979 }
980 }
981 }
982
983#if PROXY_DEBUG
984 if (!ok)
985 printf("not ok\n");
986#endif
987
988 if (!mlen) {
989 if (t->ftps_seq[0] + ackoff != thack) {
990#if !defined(_KERNEL) && !defined(KERNEL)
991 fprintf(stdout,
992 "ippr_ftp_process:seq[0](%x) + ackoff(%x) != thack(%x)\n",
993 t->ftps_seq[0], ackoff, thack);
994#endif
995 return APR_ERR(1);
996 }
997
998#if PROXY_DEBUG
999 printf("f:seq[0] %x seq[1] %x\n", f->ftps_seq[0], f->ftps_seq[1]);
1000#endif
1001 if (tcp->th_flags & TH_FIN) {
1002 if (thseq == f->ftps_seq[1]) {
1003 f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1004 f->ftps_seq[1] = thseq + 1 - seqoff;
1005 } else {
1006#if PROXY_DEBUG || (!defined(_KERNEL) && !defined(KERNEL))
1007 printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1008 thseq, seqoff, f->ftps_seq[0]);
1009#endif
1010 return APR_ERR(1);
1011 }
1012 }
1013 f->ftps_len = 0;
1014 return 0;
1015 }
1016
1017 ok = 0;

--- 4 unchanged lines hidden (view full) ---

1022 */
1023 } else if ((thseq + mlen == f->ftps_seq[0]) ||
1024 (thseq + mlen == f->ftps_seq[1])) {
1025 ok = 1;
1026 }
1027
1028 if (ok == 0) {
1029 inc = thseq - f->ftps_seq[0];
1030#if PROXY_DEBUG || (!defined(_KERNEL) && !defined(KERNEL))
1031 printf("inc %d sel %d rv %d\n", inc, sel, rv);
1032 printf("th_seq %x ftps_seq %x/%x\n", thseq, f->ftps_seq[0],
1033 f->ftps_seq[1]);
1034 printf("ackmin %x ackoff %d\n", (u_int)aps->aps_ackmin[sel],
1035 aps->aps_ackoff[sel]);
1036 printf("seqmin %x seqoff %d\n", (u_int)aps->aps_seqmin[sel],
1037 aps->aps_seqoff[sel]);
1038#endif
1039
1040 return APR_ERR(1);
1041 }
1042
1043 inc = 0;
1044 rptr = f->ftps_rptr;
1045 wptr = f->ftps_wptr;
1046 f->ftps_seq[0] = thseq;
1047 f->ftps_seq[1] = f->ftps_seq[0] + mlen;
1048 f->ftps_len = mlen;
1049
1050 while (mlen > 0) {
1051 len = MIN(mlen, FTP_BUFSZ / 2);
1052
1053#if !defined(_KERNEL)
1054 bcopy((char *)m + off, wptr, len);
1055#else
1056# if SOLARIS
1057 copyout_mblk(m, off, len, wptr);
1058# else
1059 m_copydata(m, off, len, wptr);
1060# endif
1061#endif
1062 mlen -= len;
1063 off += len;
1064 wptr += len;
1065 f->ftps_wptr = wptr;
1066 if (f->ftps_junk == 2)
1067 f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1068 wptr - rptr);
1069
1070 while ((f->ftps_junk == 0) && (wptr > rptr)) {
1071 f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1072 wptr - rptr);
1073 if (f->ftps_junk == 0) {
1074 f->ftps_cmds++;
1075 len = wptr - rptr;
1076 f->ftps_rptr = rptr;
1077 if (rv)
1078 inc += ippr_ftp_server(fin, ip, nat,
1079 ftp, len);
1080 else
1081 inc += ippr_ftp_client(fin, ip, nat,
1082 ftp, len);
1083 rptr = f->ftps_rptr;
1084 wptr = f->ftps_wptr;
1085 }
1086 }
1087
1088 /*
1089 * Off to a bad start so lets just forget about using the
1090 * ftp proxy for this connection.
1091 */
1092 if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) {
1093 /* f->ftps_seq[1] += inc; */
1094#if !defined(_KERNEL) && !defined(KERNEL)
1095 fprintf(stdout,
1096 "ippr_ftp_process:cmds == 0 junk == 1\n");
1097#endif
1098 return APR_ERR(2);
1099 }
1100
1101 while ((f->ftps_junk == 1) && (rptr < wptr)) {
1102 while ((rptr < wptr) && (*rptr != '\r'))
1103 rptr++;
1104
1105 if (*rptr == '\r') {
1106 if (rptr + 1 < wptr) {
1107 if (*(rptr + 1) == '\n') {
1108 rptr += 2;
1109 f->ftps_junk = 0;
1110 } else
1111 rptr++;
1112 } else
1113 break;
1114 }
1115 }
1116 f->ftps_rptr = rptr;
1117
1118 if (rptr == wptr) {
1119 rptr = wptr = f->ftps_buf;
1120 } else {
1121 if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) {
1122 i = wptr - rptr;
1123 if ((rptr == f->ftps_buf) ||
1124 (wptr - rptr > FTP_BUFSZ / 2)) {
1125 f->ftps_junk = 1;
1126 rptr = wptr = f->ftps_buf;
1127 } else {
1128 bcopy(rptr, f->ftps_buf, i);
1129 wptr = f->ftps_buf + i;
1130 rptr = f->ftps_buf;
1131 }
1132 }
1133 f->ftps_rptr = rptr;
1134 f->ftps_wptr = wptr;
1135 }
1136 }
1137
1138 /* f->ftps_seq[1] += inc; */
1139 if (tcp->th_flags & TH_FIN)
1140 f->ftps_seq[1]++;
1141#if PROXY_DEBUG
1142# ifndef _KERNEL
1143 mlen = mbuflen(m);
1144# else
1145# if SOLARIS
1146 mlen = msgdsize(m);
1147# else
1148 mlen = mbufchainlen(m);
1149# endif
1150# endif
1151 mlen -= off;
1152 printf("ftps_seq[1] = %x inc %d len %d\n", f->ftps_seq[1], inc, mlen);
1153#endif
1154
1155 f->ftps_rptr = rptr;
1156 f->ftps_wptr = wptr;
1157 return APR_INC(inc);
1158}
1159
1160
1161int ippr_ftp_out(fin, ip, aps, nat)
1162fr_info_t *fin;
1163ip_t *ip;
1164ap_session_t *aps;
1165nat_t *nat;
1166{
1167 ftpinfo_t *ftp;
1168
1169 ftp = aps->aps_data;
1170 if (ftp == NULL)
1171 return 0;
1172 return ippr_ftp_process(fin, ip, nat, ftp, 0);
1173}
1174
1175
1176int ippr_ftp_in(fin, ip, aps, nat)
1177fr_info_t *fin;
1178ip_t *ip;
1179ap_session_t *aps;
1180nat_t *nat;
1181{
1182 ftpinfo_t *ftp;
1183
1184 ftp = aps->aps_data;
1185 if (ftp == NULL)
1186 return 0;
1187 return ippr_ftp_process(fin, ip, nat, ftp, 1);
1188}
1189
1190
1191/*
1192 * ippr_ftp_atoi - implement a version of atoi which processes numbers in
1193 * pairs separated by commas (which are expected to be in the range 0 - 255),
1194 * returning a 16 bit number combining either side of the , as the MSB and
1195 * LSB.
1196 */
1197u_short ippr_ftp_atoi(ptr)
1198char **ptr;
1199{
1200 register char *s = *ptr, c;
1201 register u_char i = 0, j = 0;
1202
1203 while ((c = *s++) && isdigit(c)) {
1204 i *= 10;
1205 i += c - '0';
1206 }
1207 if (c != ',') {
1208 *ptr = NULL;
1209 return 0;
1210 }
1211 while ((c = *s++) && isdigit(c)) {
1212 j *= 10;
1213 j += c - '0';
1214 }
1215 *ptr = s;
1216 i &= 0xff;
1217 j &= 0xff;
1218 return (i << 8) | j;
1219}