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