Major cleanup of the base IPFilter:
[dragonfly.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  * $FreeBSD: src/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c,v 1.17.2.6 2004/07/04 09:24:39 darrenr Exp $
5  * $DragonFly: src/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c,v 1.5 2004/07/28 00:22:37 hmp Exp $
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 /*
43  * Values for FTP commands.  Numerics cover 0-999
44  */
45 #define FTPXY_C_PASV    1000
46
47 int ippr_ftp_client (fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int);
48 int ippr_ftp_complete (char *, size_t);
49 int ippr_ftp_in (fr_info_t *, ip_t *, ap_session_t *, nat_t *);
50 int ippr_ftp_init (void);
51 int ippr_ftp_new (fr_info_t *, ip_t *, ap_session_t *, nat_t *);
52 int ippr_ftp_out (fr_info_t *, ip_t *, ap_session_t *, nat_t *);
53 int ippr_ftp_pasv (fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int);
54 int ippr_ftp_port (fr_info_t *, ip_t *, nat_t *, ftpside_t *, int);
55 int ippr_ftp_process (fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int);
56 int ippr_ftp_server (fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int);
57 int ippr_ftp_valid (ftpinfo_t *, int, char *, size_t);
58 int ippr_ftp_server_valid (ftpside_t *, char *, size_t);
59 int ippr_ftp_client_valid (ftpside_t *, char *, size_t);
60 u_short ippr_ftp_atoi (char **);
61
62 static  frentry_t       ftppxyfr;
63 int     ippr_ftp_pasvonly = 0;
64 int     ippr_ftp_insecure = 0;
65 int     ippr_ftp_forcepasv = 0;
66
67
68 /*
69  * Initialize local structures.
70  */
71 int ippr_ftp_init()
72 {
73         bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
74         ftppxyfr.fr_ref = 1;
75         ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
76         return 0;
77 }
78
79
80 int ippr_ftp_new(fin, ip, aps, nat)
81 fr_info_t *fin;
82 ip_t *ip;
83 ap_session_t *aps;
84 nat_t *nat;
85 {
86         ftpinfo_t *ftp;
87         ftpside_t *f;
88
89         KMALLOC(ftp, ftpinfo_t *);
90         if (ftp == NULL)
91                 return -1;
92         aps->aps_data = ftp;
93         aps->aps_psiz = sizeof(ftpinfo_t);
94
95         bzero((char *)ftp, sizeof(*ftp));
96         f = &ftp->ftp_side[0];
97         f->ftps_rptr = f->ftps_buf;
98         f->ftps_wptr = f->ftps_buf;
99         f = &ftp->ftp_side[1];
100         f->ftps_rptr = f->ftps_buf;
101         f->ftps_wptr = f->ftps_buf;
102         ftp->ftp_passok = FTPXY_INIT;
103         return 0;
104 }
105
106
107 int ippr_ftp_port(fin, ip, nat, f, dlen)
108 fr_info_t *fin;
109 ip_t *ip;
110 nat_t *nat;
111 ftpside_t *f;
112 int dlen;
113 {
114         tcphdr_t *tcp, tcph, *tcp2 = &tcph;
115         char newbuf[IPF_FTPBUFSZ], *s;
116         u_int a1, a2, a3, a4;
117         struct in_addr swip;
118         u_short a5, a6, sp;
119         size_t nlen, olen;
120         fr_info_t fi;
121         int inc, off;
122         nat_t *ipn;
123         mb_t *m;
124 #if     SOLARIS && defined(_KERNEL)
125         mb_t *m1;
126 #endif
127
128         tcp = (tcphdr_t *)fin->fin_dp;
129         /*
130          * Check for client sending out PORT message.
131          */
132         if (dlen < IPF_MINPORTLEN) {
133 #if !defined(_KERNEL) && !defined(KERNEL)
134                 fprintf(stdout,
135                         "ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", dlen);
136 #endif
137                 return 0;
138         }
139         off = fin->fin_hlen + (tcp->th_off << 2);
140         /*
141          * Skip the PORT command + space
142          */
143         s = f->ftps_rptr + 5;
144         /*
145          * Pick out the address components, two at a time.
146          */
147         a1 = ippr_ftp_atoi(&s);
148         if (s == NULL) {
149 #if !defined(_KERNEL) && !defined(KERNEL)
150                 fprintf(stdout, "ippr_ftp_port:ippr_ftp_atoi(1) failed\n");
151 #endif
152                 return 0;
153         }
154         a2 = ippr_ftp_atoi(&s);
155         if (s == NULL) {
156 #if !defined(_KERNEL) && !defined(KERNEL)
157                 fprintf(stdout, "ippr_ftp_port:ippr_ftp_atoi(2) failed\n");
158 #endif
159                 return 0;
160         }
161         /*
162          * check that IP address in the PORT/PASV reply is the same as the
163          * sender of the command - prevents using PORT for port scanning.
164          */
165         a1 <<= 16;
166         a1 |= a2;
167         if (a1 != ntohl(nat->nat_inip.s_addr)) {
168 #if !defined(_KERNEL) && !defined(KERNEL)
169                 fprintf(stdout, "ippr_ftp_port:a1 != nat->nat_inip\n");
170 #endif
171                 return 0;
172         }
173
174         a5 = ippr_ftp_atoi(&s);
175         if (s == NULL) {
176 #if !defined(_KERNEL) && !defined(KERNEL)
177                 fprintf(stdout, "ippr_ftp_port:ippr_ftp_atoi(3) failed\n");
178 #endif
179                 return 0;
180         }
181         if (*s == ')')
182                 s++;
183
184         /*
185          * check for CR-LF at the end.
186          */
187         if (*s == '\n')
188                 s--;
189         if ((*s == '\r') && (*(s + 1) == '\n')) {
190                 s += 2;
191                 a6 = a5 & 0xff;
192         } else {
193 #if !defined(_KERNEL) && !defined(KERNEL)
194                 fprintf(stdout, "ippr_ftp_port:missing cr-lf\n");
195 #endif
196                 return 0;
197         }
198         a5 >>= 8;
199         a5 &= 0xff;
200         /*
201          * Calculate new address parts for PORT command
202          */
203         a1 = ntohl(ip->ip_src.s_addr);
204         a2 = (a1 >> 16) & 0xff;
205         a3 = (a1 >> 8) & 0xff;
206         a4 = a1 & 0xff;
207         a1 >>= 24;
208         olen = s - f->ftps_rptr;
209         /* DO NOT change this to snprintf! */
210 #if defined(OpenBSD) && (200311 >= 200311)
211         (void) snprintf(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
212                         "PORT", a1, a2, a3, a4, a5, a6);
213 #else
214         (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
215                        "PORT", a1, a2, a3, a4, a5, a6);
216 #endif
217
218         nlen = strlen(newbuf);
219         inc = nlen - olen;
220         if ((inc + ip->ip_len) > 65535) {
221 #if !defined(_KERNEL) && !defined(KERNEL)
222                 fprintf(stdout,
223                         "ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n", inc);
224 #endif
225                 return 0;
226         }
227
228 #if !defined(_KERNEL)
229         m = *fin->fin_mp;
230         bcopy(newbuf, (char *)m + off, nlen);
231 #else
232 # if SOLARIS
233         m = fin->fin_qfm;
234         for (m1 = m; m1->b_cont; m1 = m1->b_cont)
235                 ;
236         if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
237                 mblk_t *nm;
238
239                 /* alloc enough to keep same trailer space for lower driver */
240                 nm = allocb(nlen, BPRI_MED);
241                 PANIC((!nm),("ippr_ftp_out: allocb failed"));
242
243                 nm->b_band = m1->b_band;
244                 nm->b_wptr += nlen;
245
246                 m1->b_wptr -= olen;
247                 PANIC((m1->b_wptr < m1->b_rptr),
248                       ("ippr_ftp_out: cannot handle fragmented data block"));
249
250                 linkb(m1, nm);
251         } else {
252                 if (m1->b_datap->db_struiolim == m1->b_wptr)
253                         m1->b_datap->db_struiolim += inc;
254                 m1->b_datap->db_struioflag &= ~STRUIO_IP;
255                 m1->b_wptr += inc;
256         }
257         copyin_mblk(m, off, nlen, newbuf);
258 # else
259         m = *fin->fin_mp;
260         if (inc < 0)
261                 m_adj(m, inc);
262         /* the mbuf chain will be extended if necessary by m_copyback() */
263         m_copyback(m, off, nlen, newbuf);
264 #  ifdef        M_PKTHDR
265         if (!(m->m_flags & M_PKTHDR))
266                 m->m_pkthdr.len += inc;
267 #  endif
268 # endif
269 #endif
270         if (inc != 0) {
271 #if ((SOLARIS || defined(__sgi)) && defined(_KERNEL)) || !defined(_KERNEL)
272                 u_32_t  sum1, sum2;
273
274                 sum1 = ip->ip_len;
275                 sum2 = ip->ip_len + inc;
276
277                 /* Because ~1 == -2, We really need ~1 == -1 */
278                 if (sum1 > sum2)
279                         sum2--;
280                 sum2 -= sum1;
281                 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
282
283                 fix_outcksum(fin, &ip->ip_sum, sum2);
284 #endif
285                 ip->ip_len += inc;
286         }
287
288         /*
289          * Add skeleton NAT entry for connection which will come back the
290          * other way.
291          */
292         sp = (a5 << 8 | a6);
293         /*
294          * Don't allow the PORT command to specify a port < 1024 due to
295          * security crap.
296          */
297         if (sp < 1024) {
298 #if !defined(_KERNEL) && !defined(KERNEL)
299                 fprintf(stdout, "ippr_ftp_port:sp(%d) < 1024\n", sp);
300 #endif
301                 return 0;
302         }
303
304         /*
305          * The server may not make the connection back from port 20, but
306          * it is the most likely so use it here to check for a conflicting
307          * mapping.
308          */
309         bcopy((char *)fin, (char *)&fi, sizeof(fi));
310         fi.fin_data[0] = sp;
311         fi.fin_data[1] = fin->fin_data[1] - 1;
312         ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip,
313                             ip->ip_dst, 0);
314         if (ipn == NULL) {
315                 int slen;
316
317                 slen = ip->ip_len;
318                 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
319                 bzero((char *)tcp2, sizeof(*tcp2));
320                 tcp2->th_win = htons(8192);
321                 tcp2->th_sport = htons(sp);
322                 tcp2->th_off = 5;
323                 tcp2->th_flags = TH_SYN;
324                 tcp2->th_dport = 0; /* XXX - don't specify remote port */
325                 fi.fin_data[1] = 0;
326                 fi.fin_dlen = sizeof(*tcp2);
327                 fi.fin_dp = (char *)tcp2;
328                 fi.fin_fr = &ftppxyfr;
329                 fi.fin_out = 1;
330                 swip = ip->ip_src;
331                 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
332                 ip->ip_src = nat->nat_inip;
333                 ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_DPORT,
334                               NAT_OUTBOUND);
335                 if (ipn != NULL) {
336                         ipn->nat_age = fr_defnatage;
337                         (void) fr_addstate(ip, &fi, NULL,
338                                            FI_W_DPORT|FI_IGNOREPKT);
339                 }
340                 ip->ip_len = slen;
341                 ip->ip_src = swip;
342         }
343         return inc;
344 }
345
346
347 int ippr_ftp_client(fin, ip, nat, ftp, dlen)
348 fr_info_t *fin;
349 nat_t *nat;
350 ftpinfo_t *ftp;
351 ip_t *ip;
352 int dlen;
353 {
354         char *rptr, *wptr, cmd[6], c;
355         ftpside_t *f;
356         int inc, i;
357
358         inc = 0;
359         f = &ftp->ftp_side[0];
360         rptr = f->ftps_rptr;
361         wptr = f->ftps_wptr;
362
363         for (i = 0; (i < 5) && (i < dlen); i++) {
364                 c = rptr[i];
365                 if (isalpha(c)) {
366                         cmd[i] = toupper(c);
367                 } else {
368                         cmd[i] = c;
369                 }
370         }
371         cmd[i] = '\0';
372
373         ftp->ftp_incok = 0;
374         if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
375                 if (ftp->ftp_passok == FTPXY_ADOK_1 ||
376                     ftp->ftp_passok == FTPXY_AUOK_1) {
377                         ftp->ftp_passok = FTPXY_USER_2;
378                         ftp->ftp_incok = 1;
379                 } else {
380                         ftp->ftp_passok = FTPXY_USER_1;
381                         ftp->ftp_incok = 1;
382                 }
383         } else if (!strncmp(cmd, "AUTH ", 5)) {
384                 ftp->ftp_passok = FTPXY_AUTH_1;
385                 ftp->ftp_incok = 1;
386         } else if (!strncmp(cmd, "PASS ", 5)) {
387                 if (ftp->ftp_passok == FTPXY_USOK_1) {
388                         ftp->ftp_passok = FTPXY_PASS_1;
389                         ftp->ftp_incok = 1;
390                 } else if (ftp->ftp_passok == FTPXY_USOK_2) {
391                         ftp->ftp_passok = FTPXY_PASS_2;
392                         ftp->ftp_incok = 1;
393                 }
394         } else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
395                    !strncmp(cmd, "ADAT ", 5)) {
396                 ftp->ftp_passok = FTPXY_ADAT_1;
397                 ftp->ftp_incok = 1;
398         } else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
399                     ftp->ftp_passok == FTPXY_PAOK_2) &&
400                  !strncmp(cmd, "ACCT ", 5)) {
401                 ftp->ftp_passok = FTPXY_ACCT_1;
402                 ftp->ftp_incok = 1;
403         } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly &&
404                  !strncmp(cmd, "PORT ", 5)) {
405                 inc = ippr_ftp_port(fin, ip, nat, f, dlen);
406         } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly &&
407                    !strncmp(cmd, "PORT ", 5)) {
408                 inc = ippr_ftp_port(fin, ip, nat, f, dlen);
409         }
410
411         while ((*rptr++ != '\n') && (rptr < wptr))
412                 ;
413         f->ftps_rptr = rptr;
414         return inc;
415 }
416
417
418 int ippr_ftp_pasv(fin, ip, nat, ftp, dlen)
419 fr_info_t *fin;
420 ip_t *ip;
421 nat_t *nat;
422 ftpinfo_t *ftp;
423 int dlen;
424 {
425         tcphdr_t *tcp, tcph, *tcp2 = &tcph;
426         struct in_addr swip, swip2;
427         u_int a1, a2, a3, a4;
428         u_short a5, a6, dp;
429         fr_info_t fi;
430         ftpside_t *f;
431         nat_t *ipn;
432         int inc;
433         char *s;
434
435         if (ippr_ftp_forcepasv != 0 &&
436             ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) {
437 #if !defined(_KERNEL) && !defined(KERNEL)
438                 fprintf(stdout,
439                         "ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n",
440                         ftp->ftp_side[0].ftps_cmds);
441 #endif
442                 return 0;
443         }
444
445         f = &ftp->ftp_side[1];
446
447 #define PASV_REPLEN     24
448         /*
449          * Check for PASV reply message.
450          */
451         if (dlen < IPF_MIN227LEN) {
452 #if !defined(_KERNEL) && !defined(KERNEL)
453                 fprintf(stdout,
454                         "ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", dlen);
455 #endif
456                 return 0;
457         } else if (strncmp(f->ftps_rptr,
458                            "227 Entering Passive Mod", PASV_REPLEN)) {
459 #if !defined(_KERNEL) && !defined(KERNEL)
460                 fprintf(stdout, "ippr_ftp_pasv:227 reply wrong\n");
461 #endif
462                 return 0;
463         }
464
465         tcp = (tcphdr_t *)fin->fin_dp;
466
467         /*
468          * Skip the PASV reply + space
469          */
470         s = f->ftps_rptr + PASV_REPLEN;
471         while (*s && !isdigit(*s))
472                 s++;
473         /*
474          * Pick out the address components, two at a time.
475          */
476         a1 = ippr_ftp_atoi(&s);
477         if (s == NULL) {
478 #if !defined(_KERNEL) && !defined(KERNEL)
479                 fprintf(stdout, "ippr_ftp_pasv:ippr_ftp_atoi(1) failed\n");
480 #endif
481                 return 0;
482         }
483         a2 = ippr_ftp_atoi(&s);
484         if (s == NULL) {
485 #if !defined(_KERNEL) && !defined(KERNEL)
486                 fprintf(stdout, "ippr_ftp_pasv:ippr_ftp_atoi(2) failed\n");
487 #endif
488                 return 0;
489         }
490
491         /*
492          * check that IP address in the PORT/PASV reply is the same as the
493          * sender of the command - prevents using PORT for port scanning.
494          */
495         a1 <<= 16;
496         a1 |= a2;
497         if (a1 != ntohl(nat->nat_oip.s_addr)) {
498 #if !defined(_KERNEL) && !defined(KERNEL)
499                 fprintf(stdout, "ippr_ftp_pasv:a1 != nat->nat_oip\n");
500 #endif
501                 return 0;
502         }
503
504         a5 = ippr_ftp_atoi(&s);
505         if (s == NULL) {
506 #if !defined(_KERNEL) && !defined(KERNEL)
507                 fprintf(stdout, "ippr_ftp_pasv:ippr_ftp_atoi(3) failed\n");
508 #endif
509                 return 0;
510         }
511
512         if (*s == ')')
513                 s++;
514         if (*s == '.')
515                 s++;
516         if (*s == '\n')
517                 s--;
518         /*
519          * check for CR-LF at the end.
520          */
521         if ((*s == '\r') && (*(s + 1) == '\n')) {
522                 s += 2;
523                 a6 = a5 & 0xff;
524         } else {
525 #if !defined(_KERNEL) && !defined(KERNEL)
526                 fprintf(stdout, "ippr_ftp_pasv:missing cr-lf\n");
527 #endif
528                 return 0;
529         }
530         a5 >>= 8;
531         /*
532          * Calculate new address parts for 227 reply
533          */
534         a1 = ntohl(ip->ip_src.s_addr);
535         a2 = (a1 >> 16) & 0xff;
536         a3 = (a1 >> 8) & 0xff;
537         a4 = a1 & 0xff;
538         a1 >>= 24;
539         inc = 0;
540 #if 0
541         olen = s - f->ftps_rptr;
542         (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
543                        "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6);
544         nlen = strlen(newbuf);
545         inc = nlen - olen;
546         if ((inc + ip->ip_len) > 65535)
547                 return 0;
548
549 #if !defined(_KERNEL)
550         m = *fin->fin_mp;
551         m_copyback(m, off, nlen, newbuf);
552 #else
553 # if SOLARIS
554         m = fin->fin_qfm;
555         for (m1 = m; m1->b_cont; m1 = m1->b_cont)
556                 ;
557         if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
558                 mblk_t *nm;
559
560                 /* alloc enough to keep same trailer space for lower driver */
561                 nm = allocb(nlen, BPRI_MED);
562                 PANIC((!nm),("ippr_ftp_out: allocb failed"));
563
564                 nm->b_band = m1->b_band;
565                 nm->b_wptr += nlen;
566
567                 m1->b_wptr -= olen;
568                 PANIC((m1->b_wptr < m1->b_rptr),
569                       ("ippr_ftp_out: cannot handle fragmented data block"));
570
571                 linkb(m1, nm);
572         } else {
573                 m1->b_wptr += inc;
574         }
575         /*copyin_mblk(m, off, nlen, newbuf);*/
576 # else /* SOLARIS */
577         m = *fin->fin_mp;
578         if (inc < 0)
579                 m_adj(m, inc);
580         /* the mbuf chain will be extended if necessary by m_copyback() */
581         /*m_copyback(m, off, nlen, newbuf);*/
582 # endif /* SOLARIS */
583 #endif /* _KERNEL */
584         if (inc != 0) {
585 #if ((SOLARIS || defined(__sgi)) && defined(_KERNEL)) || !defined(_KERNEL)
586                 u_32_t  sum1, sum2;
587
588                 sum1 = ip->ip_len;
589                 sum2 = ip->ip_len + inc;
590
591                 /* Because ~1 == -2, We really need ~1 == -1 */
592                 if (sum1 > sum2)
593                         sum2--;
594                 sum2 -= sum1;
595                 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
596
597                 fix_outcksum(fin, &ip->ip_sum, sum2);
598 #endif /* SOLARIS || defined(__sgi) */
599                 ip->ip_len += inc;
600         }
601 #endif /* 0 */
602
603         /*
604          * Add skeleton NAT entry for connection which will come back the
605          * other way.
606          */
607         bcopy((char *)fin, (char *)&fi, sizeof(fi));
608         fi.fin_data[0] = 0;
609         dp = htons(fin->fin_data[1] - 1);
610         fi.fin_data[1] = ntohs(dp);
611         ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip,
612                             ip->ip_dst, 0);
613         if (ipn == NULL) {
614                 int slen;
615
616                 slen = ip->ip_len;
617                 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
618                 bzero((char *)tcp2, sizeof(*tcp2));
619                 tcp2->th_win = htons(8192);
620                 tcp2->th_sport = 0;             /* XXX - fake it for nat_new */
621                 tcp2->th_off = 5;
622                 tcp2->th_flags = TH_SYN;
623                 fi.fin_data[1] = a5 << 8 | a6;
624                 fi.fin_dlen = sizeof(*tcp2);
625                 tcp2->th_dport = htons(fi.fin_data[1]);
626                 fi.fin_data[0] = 0;
627                 fi.fin_dp = (char *)tcp2;
628                 fi.fin_fr = &ftppxyfr;
629                 fi.fin_out = 1;
630                 swip = ip->ip_src;
631                 swip2 = ip->ip_dst;
632                 fi.fin_fi.fi_daddr = ip->ip_src.s_addr;
633                 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
634                 ip->ip_dst = ip->ip_src;
635                 ip->ip_src = nat->nat_inip;
636                 ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_SPORT,
637                               NAT_OUTBOUND);
638                 if (ipn != NULL) {
639                         ipn->nat_age = fr_defnatage;
640                         (void) fr_addstate(ip, &fi, NULL,
641                                            FI_W_SPORT|FI_IGNOREPKT);
642                 }
643                 ip->ip_len = slen;
644                 ip->ip_src = swip;
645                 ip->ip_dst = swip2;
646         }
647         return inc;
648 }
649
650
651 int ippr_ftp_server(fin, ip, nat, ftp, dlen)
652 fr_info_t *fin;
653 ip_t *ip;
654 nat_t *nat;
655 ftpinfo_t *ftp;
656 int dlen;
657 {
658         char *rptr, *wptr;
659         ftpside_t *f;
660         int inc;
661
662         inc = 0;
663         f = &ftp->ftp_side[1];
664         rptr = f->ftps_rptr;
665         wptr = f->ftps_wptr;
666
667         if (!isdigit(*rptr) || !isdigit(*(rptr + 1)) || !isdigit(*(rptr + 2)))
668                 return 0;
669         if (ftp->ftp_passok == FTPXY_GO) {
670                 if (!strncmp(rptr, "227 ", 4))
671                         inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen);
672         } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
673                 inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen);
674         } else if (*rptr == '5' || *rptr == '4')
675                 ftp->ftp_passok = FTPXY_INIT;
676         else if (ftp->ftp_incok) {
677                 if (*rptr == '3') {
678                         if (ftp->ftp_passok == FTPXY_ACCT_1)
679                                 ftp->ftp_passok = FTPXY_GO;
680                         else
681                                 ftp->ftp_passok++;
682                 } else if (*rptr == '2') {
683                         switch (ftp->ftp_passok)
684                         {
685                         case FTPXY_USER_1 :
686                         case FTPXY_USER_2 :
687                         case FTPXY_PASS_1 :
688                         case FTPXY_PASS_2 :
689                         case FTPXY_ACCT_1 :
690                                 ftp->ftp_passok = FTPXY_GO;
691                                 break;
692                         default :
693                                 ftp->ftp_passok += 3;
694                                 break;
695                         }
696                 }
697         }
698         ftp->ftp_incok = 0;
699
700         while ((*rptr++ != '\n') && (rptr < wptr))
701                 ;
702         f->ftps_rptr = rptr;
703         return inc;
704 }
705
706
707 /*
708  * Look to see if the buffer starts with something which we recognise as
709  * being the correct syntax for the FTP protocol.
710  */
711 int ippr_ftp_client_valid(ftps, buf, len)
712 ftpside_t *ftps;
713 char *buf;
714 size_t len;
715 {
716         char *s, c;
717         size_t i = len;
718         char cmd[5];
719
720         if (i < 5) {
721 #if !defined(_KERNEL) && !defined(KERNEL)
722                 fprintf(stdout, "ippr_ftp_client_valid:i(%lu) < 5\n",
723                         (u_long)i);
724 #endif
725                 return 2;
726         }
727         s = buf;
728         c = *s++;
729         i--;
730
731         if (isalpha(c)) {
732                 cmd[0] = toupper(c);
733                 c = *s++;
734                 i--;
735                 if (isalpha(c)) {
736                         cmd[1] = toupper(c);
737                         c = *s++;
738                         i--;
739                         if (isalpha(c)) {
740                                 cmd[2] = toupper(c);
741                                 c = *s++;
742                                 i--;
743                                 if (isalpha(c)) {
744                                         cmd[3] = toupper(c);
745                                         c = *s++;
746                                         i--;
747                                         if ((c != ' ') && (c != '\r'))
748                                                 goto bad_client_command;
749                                 } else if ((c != ' ') && (c != '\r'))
750                                         goto bad_client_command;
751                         } else
752                                 goto bad_client_command;
753                 } else
754                         goto bad_client_command;
755         } else {
756 bad_client_command:
757 #if !defined(_KERNEL) && !defined(KERNEL)
758                 fprintf(stdout,
759                         "ippr_ftp_client_valid:bad cmd:len %lu i %lu c 0x%x\n",
760                         (u_long)i, (u_long)len, c);
761 #endif
762                 return 1;
763         }
764
765         for (; i; i--) {
766                 c = *s++;
767                 if (c == '\n') {
768                         cmd[4] = '\0';
769                         if (!strcmp(cmd, "PASV"))
770                                 ftps->ftps_cmds = FTPXY_C_PASV;
771                         else
772                                 ftps->ftps_cmds = 0;
773                         return 0;
774                 }
775         }
776 #if !defined(_KERNEL) && !defined(KERNEL)
777         fprintf(stdout, "ippr_ftp_client_valid:junk after cmd[%s]\n", buf);
778 #endif
779         return 2;
780 }
781
782
783 int ippr_ftp_server_valid(ftps, buf, len)
784 ftpside_t *ftps;
785 char *buf;
786 size_t len;
787 {
788         char *s, c;
789         size_t i = len;
790         int cmd;
791
792         if (i < 5)
793                 return 2;
794         s = buf;
795         c = *s++;
796         cmd = 0;
797         i--;
798
799         if (isdigit(c)) {
800                 cmd = (c - '0') * 100;
801                 c = *s++;
802                 i--;
803                 if (isdigit(c)) {
804                         cmd += (c - '0') * 10;
805                         c = *s++;
806                         i--;
807                         if (isdigit(c)) {
808                                 cmd += (c - '0');
809                                 c = *s++;
810                                 i--;
811                                 if ((c != '-') && (c != ' '))
812                                         goto bad_server_command;
813                         } else
814                                 goto bad_server_command;
815                 } else
816                         goto bad_server_command;
817         } else {
818 bad_server_command:
819 #if !defined(_KERNEL) && !defined(KERNEL)
820                 fprintf(stdout,
821                         "ippr_ftp_server_valid:bad cmd:len %lu i %lu c 0x%x\n",
822                         (u_long)i, (u_long)len, c);
823 #endif
824                 return 1;
825         }
826
827         for (; i; i--) {
828                 c = *s++;
829                 if (c == '\n') {
830                         ftps->ftps_cmds = cmd;
831                         return 0;
832                 }
833         }
834 #if !defined(_KERNEL) && !defined(KERNEL)
835         fprintf(stdout, "ippr_ftp_server_valid:junk after cmd[%s]\n", buf);
836 #endif
837         return 2;
838 }
839
840
841 int ippr_ftp_valid(ftp, side, buf, len)
842 ftpinfo_t *ftp;
843 int side;
844 char *buf;
845 size_t len;
846 {
847         ftpside_t *ftps;
848         int ret;
849
850         ftps = &ftp->ftp_side[side];
851
852         if (side == 0)
853                 ret = ippr_ftp_client_valid(ftps, buf, len);
854         else
855                 ret = ippr_ftp_server_valid(ftps, buf, len);
856         return ret;
857 }
858
859
860 /*
861  * rv == 0 for outbound processing,
862  * rv == 1 for inbound processing.
863  */
864 int ippr_ftp_process(fin, ip, nat, ftp, rv)
865 fr_info_t *fin;
866 ip_t *ip;
867 nat_t *nat;
868 ftpinfo_t *ftp;
869 int rv;
870 {
871         int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff;
872         u_32_t thseq, thack;
873         char *rptr, *wptr;
874         ap_session_t *aps;
875         ftpside_t *f, *t;
876         tcphdr_t *tcp;
877         mb_t *m;
878
879         tcp = (tcphdr_t *)fin->fin_dp;
880         off = fin->fin_hlen + (tcp->th_off << 2);
881 #if     SOLARIS && defined(_KERNEL)
882         m = fin->fin_qfm;
883 #else
884         m = *fin->fin_mp;
885 #endif
886
887 #ifndef _KERNEL
888         mlen = mbuflen(m);
889 #else
890 # if    SOLARIS
891         mlen = msgdsize(m);
892 # else
893         mlen = mbufchainlen(m);
894 # endif
895 #endif
896         mlen -= off;
897
898         aps = nat->nat_aps;
899         t = &ftp->ftp_side[1 - rv];
900         f = &ftp->ftp_side[rv];
901         thseq = ntohl(tcp->th_seq);
902         thack = ntohl(tcp->th_ack);
903
904         sel = aps->aps_sel[1 - rv];
905         sel2 = aps->aps_sel[rv];
906         if (rv == 0) {
907                 seqoff = aps->aps_seqoff[sel];
908                 if (aps->aps_seqmin[sel] > seqoff + thseq)
909                         seqoff = aps->aps_seqoff[!sel];
910                 ackoff = aps->aps_ackoff[sel2];
911                 if (aps->aps_ackmin[sel2] > ackoff + thack)
912                         ackoff = aps->aps_ackoff[!sel2];
913         } else {
914 #if PROXY_DEBUG
915                 printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
916                         aps->aps_ackmin[sel]);
917 #endif
918                 seqoff = aps->aps_ackoff[sel];
919                 if (aps->aps_ackmin[sel] > seqoff + thseq)
920                         seqoff = aps->aps_ackoff[!sel];
921
922 #if PROXY_DEBUG
923                 printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
924                         aps->aps_seqmin[sel2]);
925 #endif
926                 ackoff = aps->aps_seqoff[sel2];
927                 if (ackoff > 0) {
928                         if (aps->aps_seqmin[sel2] > ackoff + thack)
929                                 ackoff = aps->aps_seqoff[!sel2];
930                 } else {
931                         if (aps->aps_seqmin[sel2] > thack)
932                                 ackoff = aps->aps_seqoff[!sel2];
933                 }
934         }
935 #if PROXY_DEBUG
936         printf("%s: %x seq %x/%d ack %x/%d len %d\n", rv ? "IN" : "OUT",
937                 tcp->th_flags, thseq, seqoff, thack, ackoff, mlen);
938         printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
939                 aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
940                 aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
941         printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
942                 aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
943                 aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
944 #endif
945
946         /*
947          * XXX - Ideally, this packet should get dropped because we now know
948          * that it is out of order (and there is no real danger in doing so
949          * apart from causing packets to go through here ordered).
950          */
951 #if PROXY_DEBUG
952         printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
953                 rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
954 #endif
955
956         ok = 0;
957         if (t->ftps_seq[0] == 0) {
958                 t->ftps_seq[0] = thack;
959                 ok = 1;
960         } else {
961                 if (ackoff == 0) {
962                         if (t->ftps_seq[0] == thack)
963                                 ok = 1;
964                         else if (t->ftps_seq[1] == thack) {
965                                 t->ftps_seq[0] = thack;
966                                 ok = 1;
967                         }
968                 } else {
969                         if (t->ftps_seq[0] + ackoff == thack)
970                                 ok = 1;
971                         else if (t->ftps_seq[0] == thack + ackoff)
972                                 ok = 1;
973                         else if (t->ftps_seq[1] + ackoff == thack) {
974                                 t->ftps_seq[0] = thack - ackoff;
975                                 ok = 1;
976                         } else if (t->ftps_seq[1] == thack + ackoff) {
977                                 t->ftps_seq[0] = thack - ackoff;
978                                 ok = 1;
979                         }
980                 }
981         }
982
983 #if PROXY_DEBUG
984         if (!ok)
985                 printf("not  ok\n");
986 #endif
987
988         if (!mlen) {
989                 if (t->ftps_seq[0] + ackoff != thack) {
990 #if !defined(_KERNEL) && !defined(KERNEL)
991                         fprintf(stdout,
992                 "ippr_ftp_process:seq[0](%x) + ackoff(%x) != thack(%x)\n",
993                                 t->ftps_seq[0], ackoff, thack);
994 #endif
995                         return APR_ERR(1);
996                 }
997
998 #if PROXY_DEBUG
999         printf("f:seq[0] %x seq[1] %x\n", f->ftps_seq[0], f->ftps_seq[1]);
1000 #endif
1001                 if (tcp->th_flags & TH_FIN) {
1002                         if (thseq == f->ftps_seq[1]) {
1003                                 f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1004                                 f->ftps_seq[1] = thseq + 1 - seqoff;
1005                         } else {
1006 #if PROXY_DEBUG || (!defined(_KERNEL) && !defined(KERNEL))
1007                                 printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1008                                         thseq, seqoff, f->ftps_seq[0]);
1009 #endif
1010                                 return APR_ERR(1);
1011                         }
1012                 }
1013                 f->ftps_len = 0;
1014                 return 0;
1015         }
1016
1017         ok = 0;
1018         if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1019                 ok = 1;
1020         /*
1021          * Retransmitted data packet.
1022          */
1023         } else if ((thseq + mlen == f->ftps_seq[0]) ||
1024                    (thseq + mlen == f->ftps_seq[1])) {
1025                 ok = 1;
1026         }
1027
1028         if (ok == 0) {
1029                 inc = thseq - f->ftps_seq[0];
1030 #if PROXY_DEBUG || (!defined(_KERNEL) && !defined(KERNEL))
1031                 printf("inc %d sel %d rv %d\n", inc, sel, rv);
1032                 printf("th_seq %x ftps_seq %x/%x\n", thseq, f->ftps_seq[0],
1033                         f->ftps_seq[1]);
1034                 printf("ackmin %x ackoff %d\n", (u_int)aps->aps_ackmin[sel],
1035                         aps->aps_ackoff[sel]);
1036                 printf("seqmin %x seqoff %d\n", (u_int)aps->aps_seqmin[sel],
1037                         aps->aps_seqoff[sel]);
1038 #endif
1039
1040                 return APR_ERR(1);
1041         }
1042
1043         inc = 0;
1044         rptr = f->ftps_rptr;
1045         wptr = f->ftps_wptr;
1046         f->ftps_seq[0] = thseq;
1047         f->ftps_seq[1] = f->ftps_seq[0] + mlen;
1048         f->ftps_len = mlen;
1049
1050         while (mlen > 0) {
1051                 len = MIN(mlen, FTP_BUFSZ / 2);
1052
1053 #if !defined(_KERNEL)
1054                 bcopy((char *)m + off, wptr, len);
1055 #else
1056 # if SOLARIS
1057                 copyout_mblk(m, off, len, wptr);
1058 # else
1059                 m_copydata(m, off, len, wptr);
1060 # endif
1061 #endif
1062                 mlen -= len;
1063                 off += len;
1064                 wptr += len;
1065                 f->ftps_wptr = wptr;
1066                 if (f->ftps_junk == 2)
1067                         f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1068                                                       wptr - rptr);
1069
1070                 while ((f->ftps_junk == 0) && (wptr > rptr)) {
1071                         f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1072                                                       wptr - rptr);
1073                         if (f->ftps_junk == 0) {
1074                                 f->ftps_cmds++;
1075                                 len = wptr - rptr;
1076                                 f->ftps_rptr = rptr;
1077                                 if (rv)
1078                                         inc += ippr_ftp_server(fin, ip, nat,
1079                                                                ftp, len);
1080                                 else
1081                                         inc += ippr_ftp_client(fin, ip, nat,
1082                                                                ftp, len);
1083                                 rptr = f->ftps_rptr;
1084                                 wptr = f->ftps_wptr;
1085                         }
1086                 }
1087
1088                 /*
1089                  * Off to a bad start so lets just forget about using the
1090                  * ftp proxy for this connection.
1091                  */
1092                 if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) {
1093                         /* f->ftps_seq[1] += inc; */
1094 #if !defined(_KERNEL) && !defined(KERNEL)
1095                         fprintf(stdout,
1096                                 "ippr_ftp_process:cmds == 0 junk == 1\n");
1097 #endif
1098                         return APR_ERR(2);
1099                 }
1100
1101                 while ((f->ftps_junk == 1) && (rptr < wptr)) {
1102                         while ((rptr < wptr) && (*rptr != '\r'))
1103                                 rptr++;
1104
1105                         if (*rptr == '\r') {
1106                                 if (rptr + 1 < wptr) {
1107                                         if (*(rptr + 1) == '\n') {
1108                                                 rptr += 2;
1109                                                 f->ftps_junk = 0;
1110                                         } else
1111                                                 rptr++;
1112                                 } else
1113                                         break;
1114                         }
1115                 }
1116                 f->ftps_rptr = rptr;
1117
1118                 if (rptr == wptr) {
1119                         rptr = wptr = f->ftps_buf;
1120                 } else {
1121                         if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) {
1122                                 i = wptr - rptr;
1123                                 if ((rptr == f->ftps_buf) ||
1124                                     (wptr - rptr > FTP_BUFSZ / 2)) {
1125                                         f->ftps_junk = 1;
1126                                         rptr = wptr = f->ftps_buf;
1127                                 } else {
1128                                         bcopy(rptr, f->ftps_buf, i);
1129                                         wptr = f->ftps_buf + i;
1130                                         rptr = f->ftps_buf;
1131                                 }
1132                         }
1133                         f->ftps_rptr = rptr;
1134                         f->ftps_wptr = wptr;
1135                 }
1136         }
1137
1138         /* f->ftps_seq[1] += inc; */
1139         if (tcp->th_flags & TH_FIN)
1140                 f->ftps_seq[1]++;
1141 #if PROXY_DEBUG
1142 # ifndef        _KERNEL
1143         mlen = mbuflen(m);
1144 # else
1145 #  if   SOLARIS
1146         mlen = msgdsize(m);
1147 #  else
1148         mlen = mbufchainlen(m);
1149 #  endif
1150 # endif
1151         mlen -= off;
1152         printf("ftps_seq[1] = %x inc %d len %d\n", f->ftps_seq[1], inc, mlen);
1153 #endif
1154
1155         f->ftps_rptr = rptr;
1156         f->ftps_wptr = wptr;
1157         return APR_INC(inc);
1158 }
1159
1160
1161 int ippr_ftp_out(fin, ip, aps, nat)
1162 fr_info_t *fin;
1163 ip_t *ip;
1164 ap_session_t *aps;
1165 nat_t *nat;
1166 {
1167         ftpinfo_t *ftp;
1168
1169         ftp = aps->aps_data;
1170         if (ftp == NULL)
1171                 return 0;
1172         return ippr_ftp_process(fin, ip, nat, ftp, 0);
1173 }
1174
1175
1176 int ippr_ftp_in(fin, ip, aps, nat)
1177 fr_info_t *fin;
1178 ip_t *ip;
1179 ap_session_t *aps;
1180 nat_t *nat;
1181 {
1182         ftpinfo_t *ftp;
1183
1184         ftp = aps->aps_data;
1185         if (ftp == NULL)
1186                 return 0;
1187         return ippr_ftp_process(fin, ip, nat, ftp, 1);
1188 }
1189
1190
1191 /*
1192  * ippr_ftp_atoi - implement a version of atoi which processes numbers in
1193  * pairs separated by commas (which are expected to be in the range 0 - 255),
1194  * returning a 16 bit number combining either side of the , as the MSB and
1195  * LSB.
1196  */
1197 u_short ippr_ftp_atoi(ptr)
1198 char **ptr;
1199 {
1200         char *s = *ptr, c;
1201         u_char i = 0, j = 0;
1202
1203         while ((c = *s++) && isdigit(c)) {
1204                 i *= 10;
1205                 i += c - '0';
1206         }
1207         if (c != ',') {
1208                 *ptr = NULL;
1209                 return 0;
1210         }
1211         while ((c = *s++) && isdigit(c)) {
1212                 j *= 10;
1213                 j += c - '0';
1214         }
1215         *ptr = s;
1216         i &= 0xff;
1217         j &= 0xff;
1218         return (i << 8) | j;
1219 }