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