]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/contrib/ipfilter/netinet/ip_ftp_pxy.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / contrib / ipfilter / netinet / ip_ftp_pxy.c
1 /*      $FreeBSD$       */
2
3 /*
4  * Copyright (C) 1997-2003 by Darren Reed
5  *
6  * See the IPFILTER.LICENCE file for details on licencing.
7  *
8  * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
9  * code.
10  *
11  * $FreeBSD$
12  * Id: ip_ftp_pxy.c,v 2.88.2.19 2006/04/01 10:14:53 darrenr Exp $
13  */
14
15 #define IPF_FTP_PROXY
16
17 #define IPF_MINPORTLEN  18
18 #define IPF_MAXPORTLEN  30
19 #define IPF_MIN227LEN   39
20 #define IPF_MAX227LEN   51
21 #define IPF_MIN229LEN   47
22 #define IPF_MAX229LEN   51
23
24 #define FTPXY_GO        0
25 #define FTPXY_INIT      1
26 #define FTPXY_USER_1    2
27 #define FTPXY_USOK_1    3
28 #define FTPXY_PASS_1    4
29 #define FTPXY_PAOK_1    5
30 #define FTPXY_AUTH_1    6
31 #define FTPXY_AUOK_1    7
32 #define FTPXY_ADAT_1    8
33 #define FTPXY_ADOK_1    9
34 #define FTPXY_ACCT_1    10
35 #define FTPXY_ACOK_1    11
36 #define FTPXY_USER_2    12
37 #define FTPXY_USOK_2    13
38 #define FTPXY_PASS_2    14
39 #define FTPXY_PAOK_2    15
40
41 /*
42  * Values for FTP commands.  Numerics cover 0-999
43  */
44 #define FTPXY_C_PASV    1000
45
46 int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
47 int ippr_ftp_complete __P((char *, size_t));
48 int ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *));
49 int ippr_ftp_init __P((void));
50 void ippr_ftp_fini __P((void));
51 int ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *));
52 int ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *));
53 int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
54 int ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
55 int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int));
56 int ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int));
57 int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int));
58 int ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t));
59 int ippr_ftp_server_valid __P((ftpside_t *, char *, size_t));
60 int ippr_ftp_client_valid __P((ftpside_t *, char *, size_t));
61 u_short ippr_ftp_atoi __P((char **));
62 int ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *,
63                             u_int, char *, char *, u_int));
64
65
66 int     ftp_proxy_init = 0;
67 int     ippr_ftp_pasvonly = 0;
68 int     ippr_ftp_insecure = 0;  /* Do not require logins before transfers */
69 int     ippr_ftp_pasvrdr = 0;
70 int     ippr_ftp_forcepasv = 0; /* PASV must be last command prior to 227 */
71 #if defined(_KERNEL)
72 int     ippr_ftp_debug = 0;
73 #else
74 int     ippr_ftp_debug = 2;
75 #endif
76 /*
77  * 1 - security
78  * 2 - errors
79  * 3 - error debugging
80  * 4 - parsing errors
81  * 5 - parsing info
82  * 6 - parsing debug
83  */
84
85 static  frentry_t       ftppxyfr;
86 static  ipftuneable_t   ftptune = {
87         { &ippr_ftp_debug },
88         "ippr_ftp_debug",
89         0,
90         10,
91         sizeof(ippr_ftp_debug),
92         0,
93         NULL
94 };
95
96
97 /*
98  * Initialize local structures.
99  */
100 int ippr_ftp_init()
101 {
102         bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
103         ftppxyfr.fr_ref = 1;
104         ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
105         MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex");
106         ftp_proxy_init = 1;
107         (void) fr_addipftune(&ftptune);
108
109         return 0;
110 }
111
112
113 void ippr_ftp_fini()
114 {
115         (void) fr_delipftune(&ftptune);
116
117         if (ftp_proxy_init == 1) {
118                 MUTEX_DESTROY(&ftppxyfr.fr_lock);
119                 ftp_proxy_init = 0;
120         }
121 }
122
123
124 int ippr_ftp_new(fin, aps, nat)
125 fr_info_t *fin;
126 ap_session_t *aps;
127 nat_t *nat;
128 {
129         ftpinfo_t *ftp;
130         ftpside_t *f;
131
132         KMALLOC(ftp, ftpinfo_t *);
133         if (ftp == NULL)
134                 return -1;
135
136         fin = fin;      /* LINT */
137         nat = nat;      /* LINT */
138
139         aps->aps_data = ftp;
140         aps->aps_psiz = sizeof(ftpinfo_t);
141
142         bzero((char *)ftp, sizeof(*ftp));
143         f = &ftp->ftp_side[0];
144         f->ftps_rptr = f->ftps_buf;
145         f->ftps_wptr = f->ftps_buf;
146         f = &ftp->ftp_side[1];
147         f->ftps_rptr = f->ftps_buf;
148         f->ftps_wptr = f->ftps_buf;
149         ftp->ftp_passok = FTPXY_INIT;
150         ftp->ftp_incok = 0;
151         return 0;
152 }
153
154
155 int ippr_ftp_port(fin, ip, nat, f, dlen)
156 fr_info_t *fin;
157 ip_t *ip;
158 nat_t *nat;
159 ftpside_t *f;
160 int dlen;
161 {
162         tcphdr_t *tcp, tcph, *tcp2 = &tcph;
163         char newbuf[IPF_FTPBUFSZ], *s;
164         struct in_addr swip, swip2;
165         u_int a1, a2, a3, a4;
166         int inc, off, flags;
167         u_short a5, a6, sp;
168         size_t nlen, olen;
169         fr_info_t fi;
170         nat_t *nat2;
171         mb_t *m;
172
173         m = fin->fin_m;
174         tcp = (tcphdr_t *)fin->fin_dp;
175         off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
176
177         /*
178          * Check for client sending out PORT message.
179          */
180         if (dlen < IPF_MINPORTLEN) {
181                 if (ippr_ftp_debug > 1)
182                         printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
183                                dlen);
184                 return 0;
185         }
186         /*
187          * Skip the PORT command + space
188          */
189         s = f->ftps_rptr + 5;
190         /*
191          * Pick out the address components, two at a time.
192          */
193         a1 = ippr_ftp_atoi(&s);
194         if (s == NULL) {
195                 if (ippr_ftp_debug > 1)
196                         printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1);
197                 return 0;
198         }
199         a2 = ippr_ftp_atoi(&s);
200         if (s == NULL) {
201                 if (ippr_ftp_debug > 1)
202                         printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2);
203                 return 0;
204         }
205
206         /*
207          * Check that IP address in the PORT/PASV reply is the same as the
208          * sender of the command - prevents using PORT for port scanning.
209          */
210         a1 <<= 16;
211         a1 |= a2;
212         if (((nat->nat_dir == NAT_OUTBOUND) &&
213              (a1 != ntohl(nat->nat_inip.s_addr))) ||
214             ((nat->nat_dir == NAT_INBOUND) &&
215              (a1 != ntohl(nat->nat_oip.s_addr)))) {
216                 if (ippr_ftp_debug > 0)
217                         printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1");
218                 return APR_ERR(1);
219         }
220
221         a5 = ippr_ftp_atoi(&s);
222         if (s == NULL) {
223                 if (ippr_ftp_debug > 1)
224                         printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3);
225                 return 0;
226         }
227         if (*s == ')')
228                 s++;
229
230         /*
231          * check for CR-LF at the end.
232          */
233         if (*s == '\n')
234                 s--;
235         if ((*s == '\r') && (*(s + 1) == '\n')) {
236                 s += 2;
237                 a6 = a5 & 0xff;
238         } else {
239                 if (ippr_ftp_debug > 1)
240                         printf("ippr_ftp_port:missing %s\n", "cr-lf");
241                 return 0;
242         }
243
244         a5 >>= 8;
245         a5 &= 0xff;
246         sp = a5 << 8 | a6;
247         /*
248          * Don't allow the PORT command to specify a port < 1024 due to
249          * security crap.
250          */
251         if (sp < 1024) {
252                 if (ippr_ftp_debug > 0)
253                         printf("ippr_ftp_port:sp(%d) < 1024\n", sp);
254                 return 0;
255         }
256         /*
257          * Calculate new address parts for PORT command
258          */
259         if (nat->nat_dir == NAT_INBOUND)
260                 a1 = ntohl(nat->nat_oip.s_addr);
261         else
262                 a1 = ntohl(ip->ip_src.s_addr);
263         a2 = (a1 >> 16) & 0xff;
264         a3 = (a1 >> 8) & 0xff;
265         a4 = a1 & 0xff;
266         a1 >>= 24;
267         olen = s - f->ftps_rptr;
268         /* DO NOT change this to snprintf! */
269 #if defined(SNPRINTF) && defined(_KERNEL)
270         SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
271                  "PORT", a1, a2, a3, a4, a5, a6);
272 #else
273         (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
274                        "PORT", a1, a2, a3, a4, a5, a6);
275 #endif
276
277         nlen = strlen(newbuf);
278         inc = nlen - olen;
279         if ((inc + ip->ip_len) > 65535) {
280                 if (ippr_ftp_debug > 0)
281                         printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n",
282                                inc);
283                 return 0;
284         }
285
286 #if !defined(_KERNEL)
287         bcopy(newbuf, MTOD(m, char *) + off, nlen);
288 #else
289 # if defined(MENTAT)
290         if (inc < 0)
291                 (void)adjmsg(m, inc);
292 # else /* defined(MENTAT) */
293         /*
294          * m_adj takes care of pkthdr.len, if required and treats inc<0 to
295          * mean remove -len bytes from the end of the packet.
296          * The mbuf chain will be extended if necessary by m_copyback().
297          */
298         if (inc < 0)
299                 m_adj(m, inc);
300 # endif /* defined(MENTAT) */
301 #endif /* !defined(_KERNEL) */
302         COPYBACK(m, off, nlen, newbuf);
303
304         if (inc != 0) {
305                 ip->ip_len += inc;
306                 fin->fin_dlen += inc;
307                 fin->fin_plen += inc;
308         }
309
310         /*
311          * The server may not make the connection back from port 20, but
312          * it is the most likely so use it here to check for a conflicting
313          * mapping.
314          */
315         bcopy((char *)fin, (char *)&fi, sizeof(fi));
316         fi.fin_state = NULL;
317         fi.fin_nat = NULL;
318         fi.fin_flx |= FI_IGNORE;
319         fi.fin_data[0] = sp;
320         fi.fin_data[1] = fin->fin_data[1] - 1;
321         /*
322          * Add skeleton NAT entry for connection which will come back the
323          * other way.
324          */
325         if (nat->nat_dir == NAT_OUTBOUND)
326                 nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
327                                      nat->nat_inip, nat->nat_oip);
328         else
329                 nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
330                                     nat->nat_inip, nat->nat_oip);
331         if (nat2 == NULL) {
332                 int slen;
333
334                 slen = ip->ip_len;
335                 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
336                 bzero((char *)tcp2, sizeof(*tcp2));
337                 tcp2->th_win = htons(8192);
338                 tcp2->th_sport = htons(sp);
339                 TCP_OFF_A(tcp2, 5);
340                 tcp2->th_flags = TH_SYN;
341                 tcp2->th_dport = 0; /* XXX - don't specify remote port */
342                 fi.fin_data[1] = 0;
343                 fi.fin_dlen = sizeof(*tcp2);
344                 fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
345                 fi.fin_dp = (char *)tcp2;
346                 fi.fin_fr = &ftppxyfr;
347                 fi.fin_out = nat->nat_dir;
348                 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
349                 swip = ip->ip_src;
350                 swip2 = ip->ip_dst;
351                 if (nat->nat_dir == NAT_OUTBOUND) {
352                         fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
353                         ip->ip_src = nat->nat_inip;
354                 } else if (nat->nat_dir == NAT_INBOUND) {
355                         fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
356                         ip->ip_src = nat->nat_oip;
357                 }
358
359                 flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT;
360                 if (nat->nat_dir == NAT_INBOUND)
361                         flags |= NAT_NOTRULEPORT;
362                 nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir);
363
364                 if (nat2 != NULL) {
365                         (void) nat_proto(&fi, nat2, IPN_TCP);
366                         nat_update(&fi, nat2, nat->nat_ptr);
367                         fi.fin_ifp = NULL;
368                         if (nat->nat_dir == NAT_INBOUND) {
369                                 fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
370                                 ip->ip_dst = nat->nat_inip;
371                         }
372                         (void) fr_addstate(&fi, NULL, SI_W_DPORT);
373                         if (fi.fin_state != NULL)
374                                 fr_statederef((ipstate_t **)&fi.fin_state);
375                 }
376                 ip->ip_len = slen;
377                 ip->ip_src = swip;
378                 ip->ip_dst = swip2;
379         }
380         return APR_INC(inc);
381 }
382
383
384 int ippr_ftp_client(fin, ip, nat, ftp, dlen)
385 fr_info_t *fin;
386 nat_t *nat;
387 ftpinfo_t *ftp;
388 ip_t *ip;
389 int dlen;
390 {
391         char *rptr, *wptr, cmd[6], c;
392         ftpside_t *f;
393         int inc, i;
394
395         inc = 0;
396         f = &ftp->ftp_side[0];
397         rptr = f->ftps_rptr;
398         wptr = f->ftps_wptr;
399
400         for (i = 0; (i < 5) && (i < dlen); i++) {
401                 c = rptr[i];
402                 if (ISALPHA(c)) {
403                         cmd[i] = TOUPPER(c);
404                 } else {
405                         cmd[i] = c;
406                 }
407         }
408         cmd[i] = '\0';
409
410         ftp->ftp_incok = 0;
411         if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
412                 if (ftp->ftp_passok == FTPXY_ADOK_1 ||
413                     ftp->ftp_passok == FTPXY_AUOK_1) {
414                         ftp->ftp_passok = FTPXY_USER_2;
415                         ftp->ftp_incok = 1;
416                 } else {
417                         ftp->ftp_passok = FTPXY_USER_1;
418                         ftp->ftp_incok = 1;
419                 }
420         } else if (!strncmp(cmd, "AUTH ", 5)) {
421                 ftp->ftp_passok = FTPXY_AUTH_1;
422                 ftp->ftp_incok = 1;
423         } else if (!strncmp(cmd, "PASS ", 5)) {
424                 if (ftp->ftp_passok == FTPXY_USOK_1) {
425                         ftp->ftp_passok = FTPXY_PASS_1;
426                         ftp->ftp_incok = 1;
427                 } else if (ftp->ftp_passok == FTPXY_USOK_2) {
428                         ftp->ftp_passok = FTPXY_PASS_2;
429                         ftp->ftp_incok = 1;
430                 }
431         } else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
432                    !strncmp(cmd, "ADAT ", 5)) {
433                 ftp->ftp_passok = FTPXY_ADAT_1;
434                 ftp->ftp_incok = 1;
435         } else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
436                     ftp->ftp_passok == FTPXY_PAOK_2) &&
437                  !strncmp(cmd, "ACCT ", 5)) {
438                 ftp->ftp_passok = FTPXY_ACCT_1;
439                 ftp->ftp_incok = 1;
440         } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly &&
441                  !strncmp(cmd, "PORT ", 5)) {
442                 inc = ippr_ftp_port(fin, ip, nat, f, dlen);
443         } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly &&
444                    !strncmp(cmd, "PORT ", 5)) {
445                 inc = ippr_ftp_port(fin, ip, nat, f, dlen);
446         }
447
448         while ((*rptr++ != '\n') && (rptr < wptr))
449                 ;
450         f->ftps_rptr = rptr;
451         return inc;
452 }
453
454
455 int ippr_ftp_pasv(fin, ip, nat, ftp, dlen)
456 fr_info_t *fin;
457 ip_t *ip;
458 nat_t *nat;
459 ftpinfo_t *ftp;
460 int dlen;
461 {
462         u_int a1, a2, a3, a4, data_ip;
463         char newbuf[IPF_FTPBUFSZ];
464         const char *brackets[2];
465         u_short a5, a6;
466         ftpside_t *f;
467         char *s;
468
469         if (ippr_ftp_forcepasv != 0 &&
470             ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) {
471                 if (ippr_ftp_debug > 0)
472                         printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n",
473                                ftp->ftp_side[0].ftps_cmds);
474                 return 0;
475         }
476
477         f = &ftp->ftp_side[1];
478
479 #define PASV_REPLEN     24
480         /*
481          * Check for PASV reply message.
482          */
483         if (dlen < IPF_MIN227LEN) {
484                 if (ippr_ftp_debug > 1)
485                         printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
486                                dlen);
487                 return 0;
488         } else if (strncmp(f->ftps_rptr,
489                            "227 Entering Passive Mod", PASV_REPLEN)) {
490                 if (ippr_ftp_debug > 0)
491                         printf("ippr_ftp_pasv:%d reply wrong\n", 227);
492                 return 0;
493         }
494
495         brackets[0] = "";
496         brackets[1] = "";
497         /*
498          * Skip the PASV reply + space
499          */
500         s = f->ftps_rptr + PASV_REPLEN;
501         while (*s && !ISDIGIT(*s)) {
502                 if (*s == '(') {
503                         brackets[0] = "(";
504                         brackets[1] = ")";
505                 }
506                 s++;
507         }
508
509         /*
510          * Pick out the address components, two at a time.
511          */
512         a1 = ippr_ftp_atoi(&s);
513         if (s == NULL) {
514                 if (ippr_ftp_debug > 1)
515                         printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1);
516                 return 0;
517         }
518         a2 = ippr_ftp_atoi(&s);
519         if (s == NULL) {
520                 if (ippr_ftp_debug > 1)
521                         printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2);
522                 return 0;
523         }
524
525         /*
526          * check that IP address in the PASV reply is the same as the
527          * sender of the command - prevents using PASV for port scanning.
528          */
529         a1 <<= 16;
530         a1 |= a2;
531
532         if (((nat->nat_dir == NAT_INBOUND) &&
533              (a1 != ntohl(nat->nat_inip.s_addr))) ||
534             ((nat->nat_dir == NAT_OUTBOUND) &&
535              (a1 != ntohl(nat->nat_oip.s_addr)))) {
536                 if (ippr_ftp_debug > 0)
537                         printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1");
538                 return 0;
539         }
540
541         a5 = ippr_ftp_atoi(&s);
542         if (s == NULL) {
543                 if (ippr_ftp_debug > 1)
544                         printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3);
545                 return 0;
546         }
547
548         if (*s == ')')
549                 s++;
550         if (*s == '.')
551                 s++;
552         if (*s == '\n')
553                 s--;
554         /*
555          * check for CR-LF at the end.
556          */
557         if ((*s == '\r') && (*(s + 1) == '\n')) {
558                 s += 2;
559         } else {
560                 if (ippr_ftp_debug > 1)
561                         printf("ippr_ftp_pasv:missing %s", "cr-lf\n");
562                 return 0;
563         }
564
565         a6 = a5 & 0xff;
566         a5 >>= 8;
567         /*
568          * Calculate new address parts for 227 reply
569          */
570         if (nat->nat_dir == NAT_INBOUND) {
571                 data_ip = nat->nat_outip.s_addr;
572                 a1 = ntohl(data_ip);
573         } else
574                 data_ip = htonl(a1);
575
576         a2 = (a1 >> 16) & 0xff;
577         a3 = (a1 >> 8) & 0xff;
578         a4 = a1 & 0xff;
579         a1 >>= 24;
580
581 #if defined(SNPRINTF) && defined(_KERNEL)
582         SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
583                 "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
584                 a5, a6, brackets[1]);
585 #else
586         (void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
587                 "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
588                 a5, a6, brackets[1]);
589 #endif
590         return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6),
591                                   newbuf, s, data_ip);
592 }
593
594 int ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip)
595 fr_info_t *fin;
596 ip_t *ip;
597 nat_t *nat;
598 ftpside_t *f;
599 u_int port;
600 char *newmsg;
601 char *s;
602 u_int data_ip;
603 {
604         int inc, off, nflags, sflags;
605         tcphdr_t *tcp, tcph, *tcp2;
606         struct in_addr swip, swip2;
607         struct in_addr data_addr;
608         size_t nlen, olen;
609         fr_info_t fi;
610         nat_t *nat2;
611         mb_t *m;
612
613         m = fin->fin_m;
614         tcp = (tcphdr_t *)fin->fin_dp;
615         off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
616
617         data_addr.s_addr = data_ip;
618         tcp2 = &tcph;
619         inc = 0;
620
621
622         olen = s - f->ftps_rptr;
623         nlen = strlen(newmsg);
624         inc = nlen - olen;
625         if ((inc + ip->ip_len) > 65535) {
626                 if (ippr_ftp_debug > 0)
627                         printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
628                                inc);
629                 return 0;
630         }
631
632 #if !defined(_KERNEL)
633         bcopy(newmsg, MTOD(m, char *) + off, nlen);
634 #else
635 # if defined(MENTAT)
636         if (inc < 0)
637                 (void)adjmsg(m, inc);
638 # else /* defined(MENTAT) */
639         /*
640          * m_adj takes care of pkthdr.len, if required and treats inc<0 to
641          * mean remove -len bytes from the end of the packet.
642          * The mbuf chain will be extended if necessary by m_copyback().
643          */
644         if (inc < 0)
645                 m_adj(m, inc);
646 # endif /* defined(MENTAT) */
647 #endif /* !defined(_KERNEL) */
648         COPYBACK(m, off, nlen, newmsg);
649
650         if (inc != 0) {
651                 ip->ip_len += inc;
652                 fin->fin_dlen += inc;
653                 fin->fin_plen += inc;
654         }
655
656         /*
657          * Add skeleton NAT entry for connection which will come back the
658          * other way.
659          */
660         bcopy((char *)fin, (char *)&fi, sizeof(fi));
661         fi.fin_state = NULL;
662         fi.fin_nat = NULL;
663         fi.fin_flx |= FI_IGNORE;
664         fi.fin_data[0] = 0;
665         fi.fin_data[1] = port;
666         nflags = IPN_TCP|SI_W_SPORT;
667         if (ippr_ftp_pasvrdr && f->ftps_ifp)
668                 nflags |= SI_W_DPORT;
669         if (nat->nat_dir == NAT_OUTBOUND)
670                 nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH,
671                                      nat->nat_p, nat->nat_inip, nat->nat_oip);
672         else
673                 nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH,
674                                     nat->nat_p, nat->nat_inip, nat->nat_oip);
675         if (nat2 == NULL) {
676                 int slen;
677
678                 slen = ip->ip_len;
679                 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
680                 bzero((char *)tcp2, sizeof(*tcp2));
681                 tcp2->th_win = htons(8192);
682                 tcp2->th_sport = 0;             /* XXX - fake it for nat_new */
683                 TCP_OFF_A(tcp2, 5);
684                 tcp2->th_flags = TH_SYN;
685                 fi.fin_data[1] = port;
686                 fi.fin_dlen = sizeof(*tcp2);
687                 tcp2->th_dport = htons(port);
688                 fi.fin_data[0] = 0;
689                 fi.fin_dp = (char *)tcp2;
690                 fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
691                 fi.fin_fr = &ftppxyfr;
692                 fi.fin_out = nat->nat_dir;
693                 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
694                 swip = ip->ip_src;
695                 swip2 = ip->ip_dst;
696                 if (nat->nat_dir == NAT_OUTBOUND) {
697                         fi.fin_fi.fi_daddr = data_addr.s_addr;
698                         fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
699                         ip->ip_dst = data_addr;
700                         ip->ip_src = nat->nat_inip;
701                 } else if (nat->nat_dir == NAT_INBOUND) {
702                         fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
703                         fi.fin_fi.fi_daddr = nat->nat_outip.s_addr;
704                         ip->ip_src = nat->nat_oip;
705                         ip->ip_dst = nat->nat_outip;
706                 }
707
708                 sflags = nflags;
709                 nflags |= NAT_SLAVE;
710                 if (nat->nat_dir == NAT_INBOUND)
711                         nflags |= NAT_NOTRULEPORT;
712                 nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir);
713                 if (nat2 != NULL) {
714                         (void) nat_proto(&fi, nat2, IPN_TCP);
715                         nat_update(&fi, nat2, nat->nat_ptr);
716                         fi.fin_ifp = NULL;
717                         if (nat->nat_dir == NAT_INBOUND) {
718                                 fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
719                                 ip->ip_dst = nat->nat_inip;
720                         }
721                         (void) fr_addstate(&fi, NULL, sflags);
722                         if (fi.fin_state != NULL)
723                                 fr_statederef((ipstate_t **)&fi.fin_state);
724                 }
725
726                 ip->ip_len = slen;
727                 ip->ip_src = swip;
728                 ip->ip_dst = swip2;
729         }
730         return inc;
731 }
732
733
734 int ippr_ftp_server(fin, ip, nat, ftp, dlen)
735 fr_info_t *fin;
736 ip_t *ip;
737 nat_t *nat;
738 ftpinfo_t *ftp;
739 int dlen;
740 {
741         char *rptr, *wptr;
742         ftpside_t *f;
743         int inc;
744
745         inc = 0;
746         f = &ftp->ftp_side[1];
747         rptr = f->ftps_rptr;
748         wptr = f->ftps_wptr;
749
750         if (*rptr == ' ')
751                 goto server_cmd_ok;
752         if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
753                 return 0;
754         if (ftp->ftp_passok == FTPXY_GO) {
755                 if (!strncmp(rptr, "227 ", 4))
756                         inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen);
757                 else if (!strncmp(rptr, "229 ", 4))
758                         inc = ippr_ftp_epsv(fin, ip, nat, f, dlen);
759         } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
760                 inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen);
761         } else if (ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
762                 inc = ippr_ftp_epsv(fin, ip, nat, f, dlen);
763         } else if (*rptr == '5' || *rptr == '4')
764                 ftp->ftp_passok = FTPXY_INIT;
765         else if (ftp->ftp_incok) {
766                 if (*rptr == '3') {
767                         if (ftp->ftp_passok == FTPXY_ACCT_1)
768                                 ftp->ftp_passok = FTPXY_GO;
769                         else
770                                 ftp->ftp_passok++;
771                 } else if (*rptr == '2') {
772                         switch (ftp->ftp_passok)
773                         {
774                         case FTPXY_USER_1 :
775                         case FTPXY_USER_2 :
776                         case FTPXY_PASS_1 :
777                         case FTPXY_PASS_2 :
778                         case FTPXY_ACCT_1 :
779                                 ftp->ftp_passok = FTPXY_GO;
780                                 break;
781                         default :
782                                 ftp->ftp_passok += 3;
783                                 break;
784                         }
785                 }
786         }
787 server_cmd_ok:
788         ftp->ftp_incok = 0;
789
790         while ((*rptr++ != '\n') && (rptr < wptr))
791                 ;
792         f->ftps_rptr = rptr;
793         return inc;
794 }
795
796
797 /*
798  * Look to see if the buffer starts with something which we recognise as
799  * being the correct syntax for the FTP protocol.
800  */
801 int ippr_ftp_client_valid(ftps, buf, len)
802 ftpside_t *ftps;
803 char *buf;
804 size_t len;
805 {
806         register char *s, c, pc;
807         register size_t i = len;
808         char cmd[5];
809
810         s = buf;
811
812         if (ftps->ftps_junk == 1)
813                 return 1;
814
815         if (i < 5) {
816                 if (ippr_ftp_debug > 3)
817                         printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i);
818                 return 2;
819         }
820
821         i--;
822         c = *s++;
823
824         if (ISALPHA(c)) {
825                 cmd[0] = TOUPPER(c);
826                 c = *s++;
827                 i--;
828                 if (ISALPHA(c)) {
829                         cmd[1] = TOUPPER(c);
830                         c = *s++;
831                         i--;
832                         if (ISALPHA(c)) {
833                                 cmd[2] = TOUPPER(c);
834                                 c = *s++;
835                                 i--;
836                                 if (ISALPHA(c)) {
837                                         cmd[3] = TOUPPER(c);
838                                         c = *s++;
839                                         i--;
840                                         if ((c != ' ') && (c != '\r'))
841                                                 goto bad_client_command;
842                                 } else if ((c != ' ') && (c != '\r'))
843                                         goto bad_client_command;
844                         } else
845                                 goto bad_client_command;
846                 } else
847                         goto bad_client_command;
848         } else {
849 bad_client_command:
850                 if (ippr_ftp_debug > 3)
851                         printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
852                                "ippr_ftp_client_valid",
853                                ftps->ftps_junk, (int)len, (int)i, c,
854                                (int)len, (int)len, buf);
855                 return 1;
856         }
857
858         for (; i; i--) {
859                 pc = c;
860                 c = *s++;
861                 if ((pc == '\r') && (c == '\n')) {
862                         cmd[4] = '\0';
863                         if (!strcmp(cmd, "PASV"))
864                                 ftps->ftps_cmds = FTPXY_C_PASV;
865                         else
866                                 ftps->ftps_cmds = 0;
867                         return 0;
868                 }
869         }
870 #if !defined(_KERNEL)
871         printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n",
872                (int)len, (int)len, buf);
873 #endif
874         return 2;
875 }
876
877
878 int ippr_ftp_server_valid(ftps, buf, len)
879 ftpside_t *ftps;
880 char *buf;
881 size_t len;
882 {
883         register char *s, c, pc;
884         register size_t i = len;
885         int cmd;
886
887         s = buf;
888         cmd = 0;
889
890         if (ftps->ftps_junk == 1)
891                 return 1;
892
893         if (i < 5) {
894                 if (ippr_ftp_debug > 3)
895                         printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i);
896                 return 2;
897         }
898
899         c = *s++;
900         i--;
901         if (c == ' ')
902                 goto search_eol;
903
904         if (ISDIGIT(c)) {
905                 cmd = (c - '0') * 100;
906                 c = *s++;
907                 i--;
908                 if (ISDIGIT(c)) {
909                         cmd += (c - '0') * 10;
910                         c = *s++;
911                         i--;
912                         if (ISDIGIT(c)) {
913                                 cmd += (c - '0');
914                                 c = *s++;
915                                 i--;
916                                 if ((c != '-') && (c != ' '))
917                                         goto bad_server_command;
918                         } else
919                                 goto bad_server_command;
920                 } else
921                         goto bad_server_command;
922         } else {
923 bad_server_command:
924                 if (ippr_ftp_debug > 3)
925                         printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
926                                "ippr_ftp_server_valid",
927                                ftps->ftps_junk, (int)len, (int)i,
928                                c, (int)len, (int)len, buf);
929                 return 1;
930         }
931 search_eol:
932         for (; i; i--) {
933                 pc = c;
934                 c = *s++;
935                 if ((pc == '\r') && (c == '\n')) {
936                         ftps->ftps_cmds = cmd;
937                         return 0;
938                 }
939         }
940         if (ippr_ftp_debug > 3)
941                 printf("ippr_ftp_server_valid:junk after cmd[%*.*s]\n",
942                        (int)len, (int)len, buf);
943         return 2;
944 }
945
946
947 int ippr_ftp_valid(ftp, side, buf, len)
948 ftpinfo_t *ftp;
949 int side;
950 char *buf;
951 size_t len;
952 {
953         ftpside_t *ftps;
954         int ret;
955
956         ftps = &ftp->ftp_side[side];
957
958         if (side == 0)
959                 ret = ippr_ftp_client_valid(ftps, buf, len);
960         else
961                 ret = ippr_ftp_server_valid(ftps, buf, len);
962         return ret;
963 }
964
965
966 /*
967  * For map rules, the following applies:
968  * rv == 0 for outbound processing,
969  * rv == 1 for inbound processing.
970  * For rdr rules, the following applies:
971  * rv == 0 for inbound processing,
972  * rv == 1 for outbound processing.
973  */
974 int ippr_ftp_process(fin, nat, ftp, rv)
975 fr_info_t *fin;
976 nat_t *nat;
977 ftpinfo_t *ftp;
978 int rv;
979 {
980         int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff;
981         char *rptr, *wptr, *s;
982         u_32_t thseq, thack;
983         ap_session_t *aps;
984         ftpside_t *f, *t;
985         tcphdr_t *tcp;
986         ip_t *ip;
987         mb_t *m;
988
989         m = fin->fin_m;
990         ip = fin->fin_ip;
991         tcp = (tcphdr_t *)fin->fin_dp;
992         off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
993
994         f = &ftp->ftp_side[rv];
995         t = &ftp->ftp_side[1 - rv];
996         thseq = ntohl(tcp->th_seq);
997         thack = ntohl(tcp->th_ack);
998
999 #ifdef __sgi
1000         mlen = fin->fin_plen - off;
1001 #else
1002         mlen = MSGDSIZE(m) - off;
1003 #endif
1004         if (ippr_ftp_debug > 4)
1005                 printf("ippr_ftp_process: mlen %d\n", mlen);
1006
1007         if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) {
1008                 f->ftps_seq[0] = thseq + 1;
1009                 t->ftps_seq[0] = thack;
1010                 return 0;
1011         } else if (mlen < 0) {
1012                 return 0;
1013         }
1014
1015         aps = nat->nat_aps;
1016
1017         sel = aps->aps_sel[1 - rv];
1018         sel2 = aps->aps_sel[rv];
1019         if (rv == 0) {
1020                 seqoff = aps->aps_seqoff[sel];
1021                 if (aps->aps_seqmin[sel] > seqoff + thseq)
1022                         seqoff = aps->aps_seqoff[!sel];
1023                 ackoff = aps->aps_ackoff[sel2];
1024                 if (aps->aps_ackmin[sel2] > ackoff + thack)
1025                         ackoff = aps->aps_ackoff[!sel2];
1026         } else {
1027                 seqoff = aps->aps_ackoff[sel];
1028                 if (ippr_ftp_debug > 2)
1029                         printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1030                                aps->aps_ackmin[sel]);
1031                 if (aps->aps_ackmin[sel] > seqoff + thseq)
1032                         seqoff = aps->aps_ackoff[!sel];
1033
1034                 ackoff = aps->aps_seqoff[sel2];
1035                 if (ippr_ftp_debug > 2)
1036                         printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1037                                aps->aps_seqmin[sel2]);
1038                 if (ackoff > 0) {
1039                         if (aps->aps_seqmin[sel2] > ackoff + thack)
1040                                 ackoff = aps->aps_seqoff[!sel2];
1041                 } else {
1042                         if (aps->aps_seqmin[sel2] > thack)
1043                                 ackoff = aps->aps_seqoff[!sel2];
1044                 }
1045         }
1046         if (ippr_ftp_debug > 2) {
1047                 printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1048                        rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1049                        thack, ackoff, mlen, fin->fin_plen, off);
1050                 printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1051                        aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1052                        aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1053                 printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1054                        aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1055                        aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1056         }
1057
1058         /*
1059          * XXX - Ideally, this packet should get dropped because we now know
1060          * that it is out of order (and there is no real danger in doing so
1061          * apart from causing packets to go through here ordered).
1062          */
1063         if (ippr_ftp_debug > 2) {
1064                 printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1065                        rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1066         }
1067
1068         ok = 0;
1069         if (t->ftps_seq[0] == 0) {
1070                 t->ftps_seq[0] = thack;
1071                 ok = 1;
1072         } else {
1073                 if (ackoff == 0) {
1074                         if (t->ftps_seq[0] == thack)
1075                                 ok = 1;
1076                         else if (t->ftps_seq[1] == thack) {
1077                                 t->ftps_seq[0] = thack;
1078                                 ok = 1;
1079                         }
1080                 } else {
1081                         if (t->ftps_seq[0] + ackoff == thack)
1082                                 ok = 1;
1083                         else if (t->ftps_seq[0] == thack + ackoff)
1084                                 ok = 1;
1085                         else if (t->ftps_seq[1] + ackoff == thack) {
1086                                 t->ftps_seq[0] = thack - ackoff;
1087                                 ok = 1;
1088                         } else if (t->ftps_seq[1] == thack + ackoff) {
1089                                 t->ftps_seq[0] = thack - ackoff;
1090                                 ok = 1;
1091                         }
1092                 }
1093         }
1094
1095         if (ippr_ftp_debug > 2) {
1096                 if (!ok)
1097                         printf("%s ok\n", "not");
1098         }
1099
1100         if (!mlen) {
1101                 if (t->ftps_seq[0] + ackoff != thack) {
1102                         if (ippr_ftp_debug > 1) {
1103                                 printf("%s:seq[0](%x) + (%x) != (%x)\n",
1104                                        "ippr_ftp_process", t->ftps_seq[0],
1105                                        ackoff, thack);
1106                         }
1107                         return APR_ERR(1);
1108                 }
1109
1110                 if (ippr_ftp_debug > 2) {
1111                         printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n",
1112                                 f->ftps_seq[0], f->ftps_seq[1]);
1113                 }
1114
1115                 if (tcp->th_flags & TH_FIN) {
1116                         if (thseq == f->ftps_seq[1]) {
1117                                 f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1118                                 f->ftps_seq[1] = thseq + 1 - seqoff;
1119                         } else {
1120                                 if (ippr_ftp_debug > 1) {
1121                                         printf("FIN: thseq %x seqoff %d ftps_seq %x %x\n",
1122                                                thseq, seqoff, f->ftps_seq[0], f->ftps_seq[1]);
1123                                 }
1124                                 return APR_ERR(1);
1125                         }
1126                 }
1127                 f->ftps_len = 0;
1128                 return 0;
1129         }
1130
1131         ok = 0;
1132         if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1133                 ok = 1;
1134         /*
1135          * Retransmitted data packet.
1136          */
1137         } else if ((thseq + mlen == f->ftps_seq[0]) ||
1138                    (thseq + mlen == f->ftps_seq[1])) {
1139                 ok = 1;
1140         }
1141
1142         if (ok == 0) {
1143                 inc = thseq - f->ftps_seq[0];
1144                 if (ippr_ftp_debug > 1) {
1145                         printf("inc %d sel %d rv %d\n", inc, sel, rv);
1146                         printf("th_seq %x ftps_seq %x/%x\n",
1147                                thseq, f->ftps_seq[0], f->ftps_seq[1]);
1148                         printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1149                                aps->aps_ackoff[sel]);
1150                         printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1151                                aps->aps_seqoff[sel]);
1152                 }
1153
1154                 return APR_ERR(1);
1155         }
1156
1157         inc = 0;
1158         rptr = f->ftps_rptr;
1159         wptr = f->ftps_wptr;
1160         f->ftps_seq[0] = thseq;
1161         f->ftps_seq[1] = f->ftps_seq[0] + mlen;
1162         f->ftps_len = mlen;
1163
1164         while (mlen > 0) {
1165                 len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
1166                 COPYDATA(m, off, len, wptr);
1167                 mlen -= len;
1168                 off += len;
1169                 wptr += len;
1170
1171                 if (ippr_ftp_debug > 3)
1172                         printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n",
1173                                "ippr_ftp_process",
1174                                len, mlen, off, (u_long)wptr, f->ftps_junk,
1175                                len, len, rptr);
1176
1177                 f->ftps_wptr = wptr;
1178                 if (f->ftps_junk != 0) {
1179                         i = f->ftps_junk;
1180                         f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1181                                                       wptr - rptr);
1182
1183                         if (ippr_ftp_debug > 5)
1184                                 printf("%s:junk %d -> %d\n",
1185                                        "ippr_ftp_process", i, f->ftps_junk);
1186
1187                         if (f->ftps_junk != 0) {
1188                                 if (wptr - rptr == sizeof(f->ftps_buf)) {
1189                                         if (ippr_ftp_debug > 4)
1190                                                 printf("%s:full buffer\n",
1191                                                        "ippr_ftp_process");
1192                                         f->ftps_rptr = f->ftps_buf;
1193                                         f->ftps_wptr = f->ftps_buf;
1194                                         rptr = f->ftps_rptr;
1195                                         wptr = f->ftps_wptr;
1196                                         /*
1197                                          * Because we throw away data here that
1198                                          * we would otherwise parse, set the
1199                                          * junk flag to indicate just ignore
1200                                          * any data upto the next CRLF.
1201                                          */
1202                                         f->ftps_junk = 1;
1203                                         continue;
1204                                 }
1205                         }
1206                 }
1207
1208                 while ((f->ftps_junk == 0) && (wptr > rptr)) {
1209                         len = wptr - rptr;
1210                         f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len);
1211
1212                         if (ippr_ftp_debug > 3) {
1213                                 printf("%s=%d len %d rv %d ptr %lx/%lx ",
1214                                        "ippr_ftp_valid",
1215                                        f->ftps_junk, len, rv, (u_long)rptr,
1216                                        (u_long)wptr);
1217                                 printf("buf [%*.*s]\n", len, len, rptr);
1218                         }
1219
1220                         if (f->ftps_junk == 0) {
1221                                 f->ftps_rptr = rptr;
1222                                 if (rv)
1223                                         inc += ippr_ftp_server(fin, ip, nat,
1224                                                                ftp, len);
1225                                 else
1226                                         inc += ippr_ftp_client(fin, ip, nat,
1227                                                                ftp, len);
1228                                 rptr = f->ftps_rptr;
1229                                 wptr = f->ftps_wptr;
1230                         }
1231                 }
1232
1233                 /*
1234                  * Off to a bad start so lets just forget about using the
1235                  * ftp proxy for this connection.
1236                  */
1237                 if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) {
1238                         /* f->ftps_seq[1] += inc; */
1239
1240                         if (ippr_ftp_debug > 1)
1241                                 printf("%s:cmds == 0 junk == 1\n",
1242                                        "ippr_ftp_process");
1243                         return APR_ERR(2);
1244                 }
1245
1246                 if ((f->ftps_junk != 0) && (rptr < wptr)) {
1247                         for (s = rptr; s < wptr; s++) {
1248                                 if ((*s == '\r') && (s + 1 < wptr) &&
1249                                     (*(s + 1) == '\n')) {
1250                                         rptr = s + 2;
1251                                         f->ftps_junk = 0;
1252                                         break;
1253                                 }
1254                         }
1255                 }
1256
1257                 if (rptr == wptr) {
1258                         rptr = wptr = f->ftps_buf;
1259                 } else {
1260                         /*
1261                          * Compact the buffer back to the start.  The junk
1262                          * flag should already be set and because we're not
1263                          * throwing away any data, it is preserved from its
1264                          * current state.
1265                          */
1266                         if (rptr > f->ftps_buf) {
1267                                 bcopy(rptr, f->ftps_buf, len);
1268                                 wptr -= rptr - f->ftps_buf;
1269                                 rptr = f->ftps_buf;
1270                         }
1271                 }
1272                 f->ftps_rptr = rptr;
1273                 f->ftps_wptr = wptr;
1274         }
1275
1276         /* f->ftps_seq[1] += inc; */
1277         if (tcp->th_flags & TH_FIN)
1278                 f->ftps_seq[1]++;
1279         if (ippr_ftp_debug > 3) {
1280 #ifdef __sgi
1281                 mlen = fin->fin_plen;
1282 #else
1283                 mlen = MSGDSIZE(m);
1284 #endif
1285                 mlen -= off;
1286                 printf("ftps_seq[1] = %x inc %d len %d\n",
1287                        f->ftps_seq[1], inc, mlen);
1288         }
1289
1290         f->ftps_rptr = rptr;
1291         f->ftps_wptr = wptr;
1292         return APR_INC(inc);
1293 }
1294
1295
1296 int ippr_ftp_out(fin, aps, nat)
1297 fr_info_t *fin;
1298 ap_session_t *aps;
1299 nat_t *nat;
1300 {
1301         ftpinfo_t *ftp;
1302         int rev;
1303
1304         ftp = aps->aps_data;
1305         if (ftp == NULL)
1306                 return 0;
1307
1308         rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1309         if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
1310                 ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
1311
1312         return ippr_ftp_process(fin, nat, ftp, rev);
1313 }
1314
1315
1316 int ippr_ftp_in(fin, aps, nat)
1317 fr_info_t *fin;
1318 ap_session_t *aps;
1319 nat_t *nat;
1320 {
1321         ftpinfo_t *ftp;
1322         int rev;
1323
1324         ftp = aps->aps_data;
1325         if (ftp == NULL)
1326                 return 0;
1327
1328         rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1329         if (ftp->ftp_side[rev].ftps_ifp == NULL)
1330                 ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
1331
1332         return ippr_ftp_process(fin, nat, ftp, 1 - rev);
1333 }
1334
1335
1336 /*
1337  * ippr_ftp_atoi - implement a version of atoi which processes numbers in
1338  * pairs separated by commas (which are expected to be in the range 0 - 255),
1339  * returning a 16 bit number combining either side of the , as the MSB and
1340  * LSB.
1341  */
1342 u_short ippr_ftp_atoi(ptr)
1343 char **ptr;
1344 {
1345         register char *s = *ptr, c;
1346         register u_char i = 0, j = 0;
1347
1348         while (((c = *s++) != '\0') && ISDIGIT(c)) {
1349                 i *= 10;
1350                 i += c - '0';
1351         }
1352         if (c != ',') {
1353                 *ptr = NULL;
1354                 return 0;
1355         }
1356         while (((c = *s++) != '\0') && ISDIGIT(c)) {
1357                 j *= 10;
1358                 j += c - '0';
1359         }
1360         *ptr = s;
1361         i &= 0xff;
1362         j &= 0xff;
1363         return (i << 8) | j;
1364 }
1365
1366
1367 int ippr_ftp_epsv(fin, ip, nat, f, dlen)
1368 fr_info_t *fin;
1369 ip_t *ip;
1370 nat_t *nat;
1371 ftpside_t *f;
1372 int dlen;
1373 {
1374         char newbuf[IPF_FTPBUFSZ];
1375         char *s;
1376         u_short ap = 0;
1377
1378 #define EPSV_REPLEN     33
1379         /*
1380          * Check for EPSV reply message.
1381          */
1382         if (dlen < IPF_MIN229LEN)
1383                 return (0);
1384         else if (strncmp(f->ftps_rptr,
1385                          "229 Entering Extended Passive Mode", EPSV_REPLEN))
1386                 return (0);
1387
1388         /*
1389          * Skip the EPSV command + space
1390          */
1391         s = f->ftps_rptr + 33;
1392         while (*s && !ISDIGIT(*s))
1393                 s++;
1394
1395         /*
1396          * As per RFC 2428, there are no addres components in the EPSV
1397          * response.  So we'll go straight to getting the port.
1398          */
1399         while (*s && ISDIGIT(*s)) {
1400                 ap *= 10;
1401                 ap += *s++ - '0';
1402         }
1403
1404         if (!*s)
1405                 return 0;
1406
1407         if (*s == '|')
1408                 s++;
1409         if (*s == ')')
1410                 s++;
1411         if (*s == '\n')
1412                 s--;
1413         /*
1414          * check for CR-LF at the end.
1415          */
1416         if ((*s == '\r') && (*(s + 1) == '\n')) {
1417                 s += 2;
1418         } else
1419                 return 0;
1420
1421 #if defined(SNPRINTF) && defined(_KERNEL)
1422         SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1423                  "229 Entering Extended Passive Mode", ap);
1424 #else
1425         (void) sprintf(newbuf, "%s (|||%u|)\r\n",
1426                        "229 Entering Extended Passive Mode", ap);
1427 #endif
1428
1429         return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s,
1430                                   ip->ip_src.s_addr);
1431 }