]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/ipfilter/netinet/ip_ftp_pxy.c
This commit was generated by cvs2svn to compensate for changes in r95415,
[FreeBSD/FreeBSD.git] / sys / contrib / ipfilter / netinet / ip_ftp_pxy.c
1 /*
2  * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
3  * code.
4  *
5  * $FreeBSD$
6  */
7 #if SOLARIS && defined(_KERNEL)
8 extern  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
32 #define FTPXY_AUOK_1    7
33 #define FTPXY_ADAT_1    8
34 #define FTPXY_ADOK_1    9
35 #define FTPXY_ACCT_1    10
36 #define FTPXY_ACOK_1    11
37 #define FTPXY_USER_2    12
38 #define FTPXY_USOK_2    13
39 #define FTPXY_PASS_2    14
40 #define FTPXY_PAOK_2    15
41
42 int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
43 int ippr_ftp_complete __P((char *, size_t));
44 int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
45 int ippr_ftp_init __P((void));
46 int ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
47 int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
48 int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
49 int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
50 int ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
51 int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
52 int ippr_ftp_valid __P((int, char *, size_t));
53 int ippr_ftp_server_valid __P((char *, size_t));
54 int ippr_ftp_client_valid __P((char *, size_t));
55 u_short ippr_ftp_atoi __P((char **));
56
57 static  frentry_t       ftppxyfr;
58 int     ippr_ftp_pasvonly = 0;
59 int     ippr_ftp_insecure = 0;
60
61
62 /*
63  * Initialize local structures.
64  */
65 int ippr_ftp_init()
66 {
67         bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
68         ftppxyfr.fr_ref = 1;
69         ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
70         return 0;
71 }
72
73
74 int ippr_ftp_new(fin, ip, aps, nat)
75 fr_info_t *fin;
76 ip_t *ip;
77 ap_session_t *aps;
78 nat_t *nat;
79 {
80         ftpinfo_t *ftp;
81         ftpside_t *f;
82
83         KMALLOC(ftp, ftpinfo_t *);
84         if (ftp == NULL)
85                 return -1;
86         aps->aps_data = ftp;
87         aps->aps_psiz = sizeof(ftpinfo_t);
88
89         bzero((char *)ftp, sizeof(*ftp));
90         f = &ftp->ftp_side[0];
91         f->ftps_rptr = f->ftps_buf;
92         f->ftps_wptr = f->ftps_buf;
93         f = &ftp->ftp_side[1];
94         f->ftps_rptr = f->ftps_buf;
95         f->ftps_wptr = f->ftps_buf;
96         ftp->ftp_passok = FTPXY_INIT;
97         return 0;
98 }
99
100
101 int ippr_ftp_port(fin, ip, nat, f, dlen)
102 fr_info_t *fin;
103 ip_t *ip;
104 nat_t *nat;
105 ftpside_t *f;
106 int dlen;
107 {
108         tcphdr_t *tcp, tcph, *tcp2 = &tcph;
109         char newbuf[IPF_FTPBUFSZ], *s;
110         u_int a1, a2, a3, a4;
111         struct in_addr swip;
112         u_short a5, a6, sp;
113         size_t nlen, olen;
114         fr_info_t fi;
115         int inc, off;
116         nat_t *ipn;
117         mb_t *m;
118 #if     SOLARIS
119         mb_t *m1;
120 #endif
121
122         tcp = (tcphdr_t *)fin->fin_dp;
123         /*
124          * Check for client sending out PORT message.
125          */
126         if (dlen < IPF_MINPORTLEN)
127                 return 0;
128         off = fin->fin_hlen + (tcp->th_off << 2);
129         /*
130          * Skip the PORT command + space
131          */
132         s = f->ftps_rptr + 5;
133         /*
134          * Pick out the address components, two at a time.
135          */
136         a1 = ippr_ftp_atoi(&s);
137         if (!s)
138                 return 0;
139         a2 = ippr_ftp_atoi(&s);
140         if (!s)
141                 return 0;
142         /*
143          * check that IP address in the PORT/PASV reply is the same as the
144          * sender of the command - prevents using PORT for port scanning.
145          */
146         a1 <<= 16;
147         a1 |= a2;
148         if (a1 != ntohl(nat->nat_inip.s_addr))
149                 return 0;
150
151         a5 = ippr_ftp_atoi(&s);
152         if (!s)
153                 return 0;
154         if (*s == ')')
155                 s++;
156
157         /*
158          * check for CR-LF at the end.
159          */
160         if (*s == '\n')
161                 s--;
162         if ((*s == '\r') && (*(s + 1) == '\n')) {
163                 s += 2;
164                 a6 = a5 & 0xff;
165         } else
166                 return 0;
167         a5 >>= 8;
168         a5 &= 0xff;
169         /*
170          * Calculate new address parts for PORT command
171          */
172         a1 = ntohl(ip->ip_src.s_addr);
173         a2 = (a1 >> 16) & 0xff;
174         a3 = (a1 >> 8) & 0xff;
175         a4 = a1 & 0xff;
176         a1 >>= 24;
177         olen = s - f->ftps_rptr;
178         /* DO NOT change this to snprintf! */
179         (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
180                        "PORT", a1, a2, a3, a4, a5, a6);
181
182         nlen = strlen(newbuf);
183         inc = nlen - olen;
184         if ((inc + ip->ip_len) > 65535)
185                 return 0;
186
187 #if SOLARIS
188         m = fin->fin_qfm;
189         for (m1 = m; m1->b_cont; m1 = m1->b_cont)
190                 ;
191         if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
192                 mblk_t *nm;
193
194                 /* alloc enough to keep same trailer space for lower driver */
195                 nm = allocb(nlen, BPRI_MED);
196                 PANIC((!nm),("ippr_ftp_out: allocb failed"));
197
198                 nm->b_band = m1->b_band;
199                 nm->b_wptr += nlen;
200
201                 m1->b_wptr -= olen;
202                 PANIC((m1->b_wptr < m1->b_rptr),
203                       ("ippr_ftp_out: cannot handle fragmented data block"));
204
205                 linkb(m1, nm);
206         } else {
207                 if (m1->b_datap->db_struiolim == m1->b_wptr)
208                         m1->b_datap->db_struiolim += inc;
209                 m1->b_datap->db_struioflag &= ~STRUIO_IP;
210                 m1->b_wptr += inc;
211         }
212         copyin_mblk(m, off, nlen, newbuf);
213 #else
214         m = *((mb_t **)fin->fin_mp);
215         if (inc < 0)
216                 m_adj(m, inc);
217         /* the mbuf chain will be extended if necessary by m_copyback() */
218         m_copyback(m, off, nlen, newbuf);
219 # ifdef M_PKTHDR
220         if (!(m->m_flags & M_PKTHDR))
221                 m->m_pkthdr.len += inc;
222 # endif
223 #endif
224         if (inc != 0) {
225 #if SOLARIS || defined(__sgi)
226                 register u_32_t sum1, sum2;
227
228                 sum1 = ip->ip_len;
229                 sum2 = ip->ip_len + inc;
230
231                 /* Because ~1 == -2, We really need ~1 == -1 */
232                 if (sum1 > sum2)
233                         sum2--;
234                 sum2 -= sum1;
235                 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
236
237                 fix_outcksum(fin, &ip->ip_sum, sum2);
238 #endif
239                 ip->ip_len += inc;
240         }
241
242         /*
243          * Add skeleton NAT entry for connection which will come back the
244          * other way.
245          */
246         sp = (a5 << 8 | a6);
247         /*
248          * Don't allow the PORT command to specify a port < 1024 due to
249          * security crap.
250          */
251         if (sp < 1024)
252                 return 0;
253         /*
254          * The server may not make the connection back from port 20, but
255          * it is the most likely so use it here to check for a conflicting
256          * mapping.
257          */
258         bcopy((char *)fin, (char *)&fi, sizeof(fi));
259         fi.fin_data[0] = sp;
260         fi.fin_data[1] = fin->fin_data[1] - 1;
261         ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip,
262                             ip->ip_dst, 0);
263         if (ipn == NULL) {
264                 int slen;
265
266                 slen = ip->ip_len;
267                 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
268                 bzero((char *)tcp2, sizeof(*tcp2));
269                 tcp2->th_win = htons(8192);
270                 tcp2->th_sport = htons(sp);
271                 tcp2->th_off = 5;
272                 tcp2->th_dport = 0; /* XXX - don't specify remote port */
273                 fi.fin_data[1] = 0;
274                 fi.fin_dlen = sizeof(*tcp2);
275                 fi.fin_dp = (char *)tcp2;
276                 fi.fin_fr = &ftppxyfr;
277                 fi.fin_out = 1;
278                 swip = ip->ip_src;
279                 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
280                 ip->ip_src = nat->nat_inip;
281                 ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_DPORT,
282                               NAT_OUTBOUND);
283                 if (ipn != NULL) {
284                         ipn->nat_age = fr_defnatage;
285                         (void) fr_addstate(ip, &fi, NULL,
286                                            FI_W_DPORT|FI_IGNOREPKT);
287                 }
288                 ip->ip_len = slen;
289                 ip->ip_src = swip;
290         }
291         return APR_INC(inc);
292 }
293
294
295 int ippr_ftp_client(fin, ip, nat, ftp, dlen)
296 fr_info_t *fin;
297 nat_t *nat;
298 ftpinfo_t *ftp;
299 ip_t *ip;
300 int dlen;
301 {
302         char *rptr, *wptr, cmd[6], c;
303         ftpside_t *f;
304         int inc, i;
305
306         inc = 0;
307         f = &ftp->ftp_side[0];
308         rptr = f->ftps_rptr;
309         wptr = f->ftps_wptr;
310
311         for (i = 0; (i < 5) && (i < dlen); i++) {
312                 c = rptr[i];
313                 if (isalpha(c)) {
314                         cmd[i] = toupper(c);
315                 } else {
316                         cmd[i] = c;
317                 }
318         }
319         cmd[i] = '\0';
320
321         ftp->ftp_incok = 0;
322         if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
323                 if (ftp->ftp_passok == FTPXY_ADOK_1 ||
324                     ftp->ftp_passok == FTPXY_AUOK_1) {
325                         ftp->ftp_passok = FTPXY_USER_2;
326                         ftp->ftp_incok = 1;
327                 } else {
328                         ftp->ftp_passok = FTPXY_USER_1;
329                         ftp->ftp_incok = 1;
330                 }
331         } else if (!strncmp(cmd, "AUTH ", 5)) {
332                 ftp->ftp_passok = FTPXY_AUTH_1;
333                 ftp->ftp_incok = 1;
334         } else if (!strncmp(cmd, "PASS ", 5)) {
335                 if (ftp->ftp_passok == FTPXY_USOK_1) {
336                         ftp->ftp_passok = FTPXY_PASS_1;
337                         ftp->ftp_incok = 1;
338                 } else if (ftp->ftp_passok == FTPXY_USOK_2) {
339                         ftp->ftp_passok = FTPXY_PASS_2;
340                         ftp->ftp_incok = 1;
341                 }
342         } else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
343                    !strncmp(cmd, "ADAT ", 5)) {
344                 ftp->ftp_passok = FTPXY_ADAT_1;
345                 ftp->ftp_incok = 1;
346         } else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
347                     ftp->ftp_passok == FTPXY_PAOK_2) &&
348                  !strncmp(cmd, "ACCT ", 5)) {
349                 ftp->ftp_passok = FTPXY_ACCT_1;
350                 ftp->ftp_incok = 1;
351         } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly &&
352                  !strncmp(cmd, "PORT ", 5)) {
353                 inc = ippr_ftp_port(fin, ip, nat, f, dlen);
354         } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly &&
355                    !strncmp(cmd, "PORT ", 5)) {
356                 inc = ippr_ftp_port(fin, ip, nat, f, dlen);
357         }
358
359         while ((*rptr++ != '\n') && (rptr < wptr))
360                 ;
361         f->ftps_rptr = rptr;
362         return inc;
363 }
364
365
366 int ippr_ftp_pasv(fin, ip, nat, f, dlen)
367 fr_info_t *fin;
368 ip_t *ip;
369 nat_t *nat;
370 ftpside_t *f;
371 int dlen;
372 {
373         tcphdr_t *tcp, tcph, *tcp2 = &tcph;
374         struct in_addr swip, swip2;
375         u_int a1, a2, a3, a4;
376         u_short a5, a6, dp;
377         fr_info_t fi;
378         nat_t *ipn;
379         int inc;
380         char *s;
381
382 #define PASV_REPLEN     24
383         /*
384          * Check for PASV reply message.
385          */
386         if (dlen < IPF_MIN227LEN)
387                 return 0;
388         else if (strncmp(f->ftps_rptr, "227 Entering Passive Mod", PASV_REPLEN))
389                 return 0;
390
391         tcp = (tcphdr_t *)fin->fin_dp;
392
393         /*
394          * Skip the PORT command + space
395          */
396         s = f->ftps_rptr + PASV_REPLEN;
397         while (*s && !isdigit(*s))
398                 s++;
399         /*
400          * Pick out the address components, two at a time.
401          */
402         a1 = ippr_ftp_atoi(&s);
403         if (!s)
404                 return 0;
405         a2 = ippr_ftp_atoi(&s);
406         if (!s)
407                 return 0;
408
409         /*
410          * check that IP address in the PORT/PASV reply is the same as the
411          * sender of the command - prevents using PORT for port scanning.
412          */
413         a1 <<= 16;
414         a1 |= a2;
415         if (a1 != ntohl(nat->nat_oip.s_addr))
416                 return 0;
417
418         a5 = ippr_ftp_atoi(&s);
419         if (!s)
420                 return 0;
421
422         if (*s == ')')
423                 s++;
424         if (*s == '.')
425                 s++;
426         if (*s == '\n')
427                 s--;
428         /*
429          * check for CR-LF at the end.
430          */
431         if ((*s == '\r') && (*(s + 1) == '\n')) {
432                 s += 2;
433                 a6 = a5 & 0xff;
434         } else
435                 return 0;
436         a5 >>= 8;
437         /*
438          * Calculate new address parts for 227 reply
439          */
440         a1 = ntohl(ip->ip_src.s_addr);
441         a2 = (a1 >> 16) & 0xff;
442         a3 = (a1 >> 8) & 0xff;
443         a4 = a1 & 0xff;
444         a1 >>= 24;
445         inc = 0;
446 #if 0
447         olen = s - f->ftps_rptr;
448         (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
449                        "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6);
450         nlen = strlen(newbuf);
451         inc = nlen - olen;
452         if ((inc + ip->ip_len) > 65535)
453                 return 0;
454
455 #if SOLARIS
456         m = fin->fin_qfm;
457         for (m1 = m; m1->b_cont; m1 = m1->b_cont)
458                 ;
459         if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
460                 mblk_t *nm;
461
462                 /* alloc enough to keep same trailer space for lower driver */
463                 nm = allocb(nlen, BPRI_MED);
464                 PANIC((!nm),("ippr_ftp_out: allocb failed"));
465
466                 nm->b_band = m1->b_band;
467                 nm->b_wptr += nlen;
468
469                 m1->b_wptr -= olen;
470                 PANIC((m1->b_wptr < m1->b_rptr),
471                       ("ippr_ftp_out: cannot handle fragmented data block"));
472
473                 linkb(m1, nm);
474         } else {
475                 m1->b_wptr += inc;
476         }
477         /*copyin_mblk(m, off, nlen, newbuf);*/
478 #else /* SOLARIS */
479         m = *((mb_t **)fin->fin_mp);
480         if (inc < 0)
481                 m_adj(m, inc);
482         /* the mbuf chain will be extended if necessary by m_copyback() */
483         /*m_copyback(m, off, nlen, newbuf);*/
484 #endif /* SOLARIS */
485         if (inc != 0) {
486 #if SOLARIS || defined(__sgi)
487                 register u_32_t sum1, sum2;
488
489                 sum1 = ip->ip_len;
490                 sum2 = ip->ip_len + inc;
491
492                 /* Because ~1 == -2, We really need ~1 == -1 */
493                 if (sum1 > sum2)
494                         sum2--;
495                 sum2 -= sum1;
496                 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
497
498                 fix_outcksum(fin, &ip->ip_sum, sum2);
499 #endif /* SOLARIS || defined(__sgi) */
500                 ip->ip_len += inc;
501         }
502 #endif /* 0 */
503
504         /*
505          * Add skeleton NAT entry for connection which will come back the
506          * other way.
507          */
508         bcopy((char *)fin, (char *)&fi, sizeof(fi));
509         fi.fin_data[0] = 0;
510         dp = htons(fin->fin_data[1] - 1);
511         fi.fin_data[1] = ntohs(dp);
512         ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip,
513                             ip->ip_dst, 0);
514         if (ipn == NULL) {
515                 int slen;
516
517                 slen = ip->ip_len;
518                 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
519                 bzero((char *)tcp2, sizeof(*tcp2));
520                 tcp2->th_win = htons(8192);
521                 tcp2->th_sport = 0;             /* XXX - fake it for nat_new */
522                 tcp2->th_off = 5;
523                 fi.fin_data[1] = a5 << 8 | a6;
524                 fi.fin_dlen = sizeof(*tcp2);
525                 tcp2->th_dport = htons(fi.fin_data[1]);
526                 fi.fin_data[0] = 0;
527                 fi.fin_dp = (char *)tcp2;
528                 fi.fin_fr = &ftppxyfr;
529                 fi.fin_out = 1;
530                 swip = ip->ip_src;
531                 swip2 = ip->ip_dst;
532                 fi.fin_fi.fi_daddr = ip->ip_src.s_addr;
533                 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
534                 ip->ip_dst = ip->ip_src;
535                 ip->ip_src = nat->nat_inip;
536                 ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_SPORT,
537                               NAT_OUTBOUND);
538                 if (ipn != NULL) {
539                         ipn->nat_age = fr_defnatage;
540                         (void) fr_addstate(ip, &fi, NULL,
541                                            FI_W_SPORT|FI_IGNOREPKT);
542                 }
543                 ip->ip_len = slen;
544                 ip->ip_src = swip;
545                 ip->ip_dst = swip2;
546         }
547         return inc;
548 }
549
550
551 int ippr_ftp_server(fin, ip, nat, ftp, dlen)
552 fr_info_t *fin;
553 ip_t *ip;
554 nat_t *nat;
555 ftpinfo_t *ftp;
556 int dlen;
557 {
558         char *rptr, *wptr;
559         ftpside_t *f;
560         int inc;
561
562         inc = 0;
563         f = &ftp->ftp_side[1];
564         rptr = f->ftps_rptr;
565         wptr = f->ftps_wptr;
566
567         if (!isdigit(*rptr) || !isdigit(*(rptr + 1)) || !isdigit(*(rptr + 2)))
568                 return inc;
569         if (ftp->ftp_passok == FTPXY_GO) {
570                 if (!strncmp(rptr, "227 ", 4))
571                         inc = ippr_ftp_pasv(fin, ip, nat, f, dlen);
572         } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
573                 inc = ippr_ftp_pasv(fin, ip, nat, f, dlen);
574         } else if (*rptr == '5' || *rptr == '4')
575                 ftp->ftp_passok = FTPXY_INIT;
576         else if (ftp->ftp_incok) {
577                 if (*rptr == '3') {
578                         if (ftp->ftp_passok == FTPXY_ACCT_1)
579                                 ftp->ftp_passok = FTPXY_GO;
580                         else
581                                 ftp->ftp_passok++;
582                 } else if (*rptr == '2') {
583                         switch (ftp->ftp_passok)
584                         {
585                         case FTPXY_USER_1 :
586                         case FTPXY_USER_2 :
587                         case FTPXY_PASS_1 :
588                         case FTPXY_PASS_2 :
589                         case FTPXY_ACCT_1 :
590                                 ftp->ftp_passok = FTPXY_GO;
591                                 break;
592                         default :
593                                 ftp->ftp_passok += 3;
594                                 break;
595                         }
596                 }
597         }
598         ftp->ftp_incok = 0;
599         while ((*rptr++ != '\n') && (rptr < wptr))
600                 ;
601         f->ftps_rptr = rptr;
602         return inc;
603 }
604
605
606 /*
607  * Look to see if the buffer starts with something which we recognise as
608  * being the correct syntax for the FTP protocol.
609  */
610 int ippr_ftp_client_valid(buf, len)
611 char *buf;
612 size_t len;
613 {
614         register char *s, c;
615         register size_t i = len;
616
617         if (i < 5)
618                 return 2;
619         s = buf;
620         c = *s++;
621         i--;
622
623         if (isalpha(c)) {
624                 c = *s++;
625                 i--;
626                 if (isalpha(c)) {
627                         c = *s++;
628                         i--;
629                         if (isalpha(c)) {
630                                 c = *s++;
631                                 i--;
632                                 if (isalpha(c)) {
633                                         c = *s++;
634                                         i--;
635                                         if ((c != ' ') && (c != '\r'))
636                                                 return 1;
637                                 } else if ((c != ' ') && (c != '\r'))
638                                         return 1;
639                         } else
640                                 return 1;
641                 } else
642                         return 1;
643         } else
644                 return 1;
645         for (; i; i--) {
646                 c = *s++;
647                 if (c == '\n')
648                         return 0;
649         }
650         return 2;
651 }
652
653
654 int ippr_ftp_server_valid(buf, len)
655 char *buf;
656 size_t len;
657 {
658         register char *s, c;
659         register size_t i = len;
660
661         if (i < 5)
662                 return 2;
663         s = buf;
664         c = *s++;
665         i--;
666
667         if (isdigit(c)) {
668                 c = *s++;
669                 i--;
670                 if (isdigit(c)) {
671                         c = *s++;
672                         i--;
673                         if (isdigit(c)) {
674                                 c = *s++;
675                                 i--;
676                                 if ((c != '-') && (c != ' '))
677                                         return 1;
678                         } else
679                                 return 1;
680                 } else
681                         return 1;
682         } else
683                 return 1;
684         for (; i; i--) {
685                 c = *s++;
686                 if (c == '\n')
687                         return 0;
688         }
689         return 2;
690 }
691
692
693 int ippr_ftp_valid(side, buf, len)
694 int side;
695 char *buf;
696 size_t len;
697 {
698         int ret;
699
700         if (side == 0)
701                 ret = ippr_ftp_client_valid(buf, len);
702         else
703                 ret = ippr_ftp_server_valid(buf, len);
704         return ret;
705 }
706
707
708 int ippr_ftp_process(fin, ip, nat, ftp, rv)
709 fr_info_t *fin;
710 ip_t *ip;
711 nat_t *nat;
712 ftpinfo_t *ftp;
713 int rv;
714 {
715         int mlen, len, off, inc, i, sel;
716         char *rptr, *wptr;
717         ftpside_t *f, *t;
718         tcphdr_t *tcp;
719         mb_t *m;
720
721         tcp = (tcphdr_t *)fin->fin_dp;
722         off = fin->fin_hlen + (tcp->th_off << 2);
723
724 #if     SOLARIS
725         m = fin->fin_qfm;
726 #else
727         m = *((mb_t **)fin->fin_mp);
728 #endif
729
730 #if     SOLARIS
731         mlen = msgdsize(m) - off;
732 #else
733         mlen = mbufchainlen(m) - off;
734 #endif
735
736         t = &ftp->ftp_side[1 - rv];
737         f = &ftp->ftp_side[rv];
738         if (!mlen) {
739                 if (!t->ftps_seq ||
740                     (int)ntohl(tcp->th_ack) - (int)t->ftps_seq > 0)
741                         t->ftps_seq = ntohl(tcp->th_ack);
742                 f->ftps_len = 0;
743                 return 0;
744         }
745
746         inc = 0;
747         rptr = f->ftps_rptr;
748         wptr = f->ftps_wptr;
749
750         sel = nat->nat_aps->aps_sel[1 - rv];
751         if (rv)
752                 i = nat->nat_aps->aps_ackoff[sel];
753         else
754                 i = nat->nat_aps->aps_seqoff[sel];
755         /*
756          * XXX - Ideally, this packet should get dropped because we now know
757          * that it is out of order (and there is no real danger in doing so
758          * apart from causing packets to go through here ordered).
759          */
760         if (f->ftps_len + f->ftps_seq == ntohl(tcp->th_seq))
761                 f->ftps_seq = ntohl(tcp->th_seq);
762         else if (ntohl(tcp->th_seq) + i != f->ftps_seq) {
763                 return APR_ERR(1);
764         }
765         f->ftps_len = mlen;
766
767         while (mlen > 0) {
768                 len = MIN(mlen, FTP_BUFSZ / 2);
769
770 #if     SOLARIS
771                 copyout_mblk(m, off, len, wptr);
772 #else
773                 m_copydata(m, off, len, wptr);
774 #endif
775                 mlen -= len;
776                 off += len;
777                 wptr += len;
778                 f->ftps_wptr = wptr;
779                 if (f->ftps_junk == 2)
780                         f->ftps_junk = ippr_ftp_valid(rv, rptr, wptr - rptr);
781
782                 while ((f->ftps_junk == 0) && (wptr > rptr)) {
783                         f->ftps_junk = ippr_ftp_valid(rv, rptr, wptr - rptr);
784                         if (f->ftps_junk == 0) {
785                                 f->ftps_cmds++;
786                                 len = wptr - rptr;
787                                 f->ftps_rptr = rptr;
788                                 if (rv)
789                                         inc += ippr_ftp_server(fin, ip, nat,
790                                                                ftp, len);
791                                 else
792                                         inc += ippr_ftp_client(fin, ip, nat,
793                                                                ftp, len);
794                                 rptr = f->ftps_rptr;
795                                 wptr = f->ftps_wptr;
796                         }
797                 }
798
799                 /*
800                  * Off to a bad start so lets just forget about using the
801                  * ftp proxy for this connection.
802                  */
803                 if ((f->ftps_cmds == 0) && (f->ftps_junk == 1))
804                         return APR_ERR(2);
805
806                 while ((f->ftps_junk == 1) && (rptr < wptr)) {
807                         while ((rptr < wptr) && (*rptr != '\r'))
808                                 rptr++;
809
810                         if (*rptr == '\r') {
811                                 if (rptr + 1 < wptr) {
812                                         if (*(rptr + 1) == '\n') {
813                                                 rptr += 2;
814                                                 f->ftps_junk = 0;
815                                         } else
816                                                 rptr++;
817                                 } else
818                                         break;
819                         }
820                 }
821                 f->ftps_rptr = rptr;
822
823                 if (rptr == wptr) {
824                         rptr = wptr = f->ftps_buf;
825                 } else {
826                         if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) {
827                                 i = wptr - rptr;
828                                 if ((rptr == f->ftps_buf) ||
829                                     (wptr - rptr > FTP_BUFSZ / 2)) {
830                                         f->ftps_junk = 1;
831                                         rptr = wptr = f->ftps_buf;
832                                 } else {
833                                         bcopy(rptr, f->ftps_buf, i);
834                                         wptr = f->ftps_buf + i;
835                                         rptr = f->ftps_buf;
836                                 }
837                         }
838                         f->ftps_rptr = rptr;
839                         f->ftps_wptr = wptr;
840                 }
841         }
842
843         t->ftps_seq = ntohl(tcp->th_ack);
844         f->ftps_rptr = rptr;
845         f->ftps_wptr = wptr;
846         return APR_INC(inc);
847 }
848
849
850 int ippr_ftp_out(fin, ip, aps, nat)
851 fr_info_t *fin;
852 ip_t *ip;
853 ap_session_t *aps;
854 nat_t *nat;
855 {
856         ftpinfo_t *ftp;
857
858         ftp = aps->aps_data;
859         if (ftp == NULL)
860                 return 0;
861         return ippr_ftp_process(fin, ip, nat, ftp, 0);
862 }
863
864
865 int ippr_ftp_in(fin, ip, aps, nat)
866 fr_info_t *fin;
867 ip_t *ip;
868 ap_session_t *aps;
869 nat_t *nat;
870 {
871         ftpinfo_t *ftp;
872
873         ftp = aps->aps_data;
874         if (ftp == NULL)
875                 return 0;
876         return ippr_ftp_process(fin, ip, nat, ftp, 1);
877 }
878
879
880 /*
881  * ippr_ftp_atoi - implement a version of atoi which processes numbers in
882  * pairs separated by commas (which are expected to be in the range 0 - 255),
883  * returning a 16 bit number combining either side of the , as the MSB and
884  * LSB.
885  */
886 u_short ippr_ftp_atoi(ptr)
887 char **ptr;
888 {
889         register char *s = *ptr, c;
890         register u_char i = 0, j = 0;
891
892         while ((c = *s++) && isdigit(c)) {
893                 i *= 10;
894                 i += c - '0';
895         }
896         if (c != ',') {
897                 *ptr = NULL;
898                 return 0;
899         }
900         while ((c = *s++) && isdigit(c)) {
901                 j *= 10;
902                 j += c - '0';
903         }
904         *ptr = s;
905         i &= 0xff;
906         j &= 0xff;
907         return (i << 8) | j;
908 }