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