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