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