ip_ftp_pxy.c revision 145522
1145522Sdarrenr/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 145522 2005-04-25 18:43:14Z darrenr $ */ 2145522Sdarrenr 353642Sguido/* 4145522Sdarrenr * Copyright (C) 1997-2003 by Darren Reed 5145522Sdarrenr * 6145522Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 7145522Sdarrenr * 853642Sguido * Simple FTP transparent proxy for in-kernel use. For use with the NAT 953642Sguido * code. 1072006Sdarrenr * 1157126Sguido * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 145522 2005-04-25 18:43:14Z darrenr $ 12145522Sdarrenr * Id: ip_ftp_pxy.c,v 2.88.2.15 2005/03/19 19:38:10 darrenr Exp 1353642Sguido */ 1453642Sguido 1553642Sguido#define IPF_FTP_PROXY 1653642Sguido 1753642Sguido#define IPF_MINPORTLEN 18 1853642Sguido#define IPF_MAXPORTLEN 30 1953642Sguido#define IPF_MIN227LEN 39 2053642Sguido#define IPF_MAX227LEN 51 21145522Sdarrenr#define IPF_MIN229LEN 47 22145522Sdarrenr#define IPF_MAX229LEN 51 2353642Sguido 2480482Sdarrenr#define FTPXY_GO 0 2580482Sdarrenr#define FTPXY_INIT 1 2680482Sdarrenr#define FTPXY_USER_1 2 2780482Sdarrenr#define FTPXY_USOK_1 3 2880482Sdarrenr#define FTPXY_PASS_1 4 2980482Sdarrenr#define FTPXY_PAOK_1 5 3080482Sdarrenr#define FTPXY_AUTH_1 6 3180482Sdarrenr#define FTPXY_AUOK_1 7 3280482Sdarrenr#define FTPXY_ADAT_1 8 3380482Sdarrenr#define FTPXY_ADOK_1 9 3480482Sdarrenr#define FTPXY_ACCT_1 10 3580482Sdarrenr#define FTPXY_ACOK_1 11 3680482Sdarrenr#define FTPXY_USER_2 12 3780482Sdarrenr#define FTPXY_USOK_2 13 3880482Sdarrenr#define FTPXY_PASS_2 14 3980482Sdarrenr#define FTPXY_PAOK_2 15 4053642Sguido 41110916Sdarrenr/* 42110916Sdarrenr * Values for FTP commands. Numerics cover 0-999 43110916Sdarrenr */ 44110916Sdarrenr#define FTPXY_C_PASV 1000 45110916Sdarrenr 4660857Sdarrenrint ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 4760857Sdarrenrint ippr_ftp_complete __P((char *, size_t)); 48145522Sdarrenrint ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *)); 4953642Sguidoint ippr_ftp_init __P((void)); 50145522Sdarrenrvoid ippr_ftp_fini __P((void)); 51145522Sdarrenrint ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *)); 52145522Sdarrenrint ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *)); 53110916Sdarrenrint ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 54145522Sdarrenrint ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); 5560857Sdarrenrint ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); 56145522Sdarrenrint ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int)); 5760857Sdarrenrint ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 58110916Sdarrenrint ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t)); 59110916Sdarrenrint ippr_ftp_server_valid __P((ftpside_t *, char *, size_t)); 60110916Sdarrenrint ippr_ftp_client_valid __P((ftpside_t *, char *, size_t)); 6160857Sdarrenru_short ippr_ftp_atoi __P((char **)); 62145522Sdarrenrint ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, 63145522Sdarrenr u_int, char *, char *, u_int)); 6453642Sguido 65145522Sdarrenr 66145522Sdarrenrint ftp_proxy_init = 0; 6760857Sdarrenrint ippr_ftp_pasvonly = 0; 68145522Sdarrenrint ippr_ftp_insecure = 0; /* Do not require logins before transfers */ 69145522Sdarrenrint ippr_ftp_pasvrdr = 0; 70145522Sdarrenrint ippr_ftp_forcepasv = 0; /* PASV must be last command prior to 227 */ 71145522Sdarrenr#if defined(_KERNEL) 72145522Sdarrenrint ippr_ftp_debug = 0; 73145522Sdarrenr#else 74145522Sdarrenrint ippr_ftp_debug = 2; 75145522Sdarrenr#endif 76145522Sdarrenr/* 77145522Sdarrenr * 1 - security 78145522Sdarrenr * 2 - errors 79145522Sdarrenr * 3 - error debugging 80145522Sdarrenr * 4 - parsing errors 81145522Sdarrenr * 5 - parsing info 82145522Sdarrenr * 6 - parsing debug 83145522Sdarrenr */ 8453642Sguido 85145522Sdarrenrstatic frentry_t ftppxyfr; 86145522Sdarrenrstatic ipftuneable_t ftptune = { 87145522Sdarrenr { &ippr_ftp_debug }, 88145522Sdarrenr "ippr_ftp_debug", 89145522Sdarrenr 0, 90145522Sdarrenr 10, 91145522Sdarrenr sizeof(ippr_ftp_debug), 92145522Sdarrenr 0, 93145522Sdarrenr NULL 94145522Sdarrenr}; 9553642Sguido 96145522Sdarrenr 9753642Sguido/* 9853642Sguido * Initialize local structures. 9953642Sguido */ 10053642Sguidoint ippr_ftp_init() 10153642Sguido{ 10292685Sdarrenr bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); 10392685Sdarrenr ftppxyfr.fr_ref = 1; 10492685Sdarrenr ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 105145522Sdarrenr MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); 106145522Sdarrenr ftp_proxy_init = 1; 107145522Sdarrenr (void) fr_addipftune(&ftptune); 108145522Sdarrenr 10953642Sguido return 0; 11053642Sguido} 11153642Sguido 11253642Sguido 113145522Sdarrenrvoid ippr_ftp_fini() 114145522Sdarrenr{ 115145522Sdarrenr (void) fr_delipftune(&ftptune); 116145522Sdarrenr 117145522Sdarrenr if (ftp_proxy_init == 1) { 118145522Sdarrenr MUTEX_DESTROY(&ftppxyfr.fr_lock); 119145522Sdarrenr ftp_proxy_init = 0; 120145522Sdarrenr } 121145522Sdarrenr} 122145522Sdarrenr 123145522Sdarrenr 124145522Sdarrenrint ippr_ftp_new(fin, aps, nat) 12560857Sdarrenrfr_info_t *fin; 12660857Sdarrenrap_session_t *aps; 12760857Sdarrenrnat_t *nat; 12853642Sguido{ 12960857Sdarrenr ftpinfo_t *ftp; 13060857Sdarrenr ftpside_t *f; 13153642Sguido 13260857Sdarrenr KMALLOC(ftp, ftpinfo_t *); 13360857Sdarrenr if (ftp == NULL) 13460857Sdarrenr return -1; 135145522Sdarrenr 136145522Sdarrenr fin = fin; /* LINT */ 137145522Sdarrenr nat = nat; /* LINT */ 138145522Sdarrenr 13960857Sdarrenr aps->aps_data = ftp; 14060857Sdarrenr aps->aps_psiz = sizeof(ftpinfo_t); 14160857Sdarrenr 14260857Sdarrenr bzero((char *)ftp, sizeof(*ftp)); 14360857Sdarrenr f = &ftp->ftp_side[0]; 14460857Sdarrenr f->ftps_rptr = f->ftps_buf; 14560857Sdarrenr f->ftps_wptr = f->ftps_buf; 14660857Sdarrenr f = &ftp->ftp_side[1]; 14760857Sdarrenr f->ftps_rptr = f->ftps_buf; 14860857Sdarrenr f->ftps_wptr = f->ftps_buf; 14980482Sdarrenr ftp->ftp_passok = FTPXY_INIT; 150145522Sdarrenr ftp->ftp_incok = 0; 15160857Sdarrenr return 0; 15253642Sguido} 15353642Sguido 15453642Sguido 15560857Sdarrenrint ippr_ftp_port(fin, ip, nat, f, dlen) 15653642Sguidofr_info_t *fin; 15753642Sguidoip_t *ip; 15853642Sguidonat_t *nat; 15960857Sdarrenrftpside_t *f; 16060857Sdarrenrint dlen; 16153642Sguido{ 16253642Sguido tcphdr_t *tcp, tcph, *tcp2 = &tcph; 16360857Sdarrenr char newbuf[IPF_FTPBUFSZ], *s; 164145522Sdarrenr struct in_addr swip, swip2; 16553642Sguido u_int a1, a2, a3, a4; 166145522Sdarrenr int inc, off, flags; 16792685Sdarrenr u_short a5, a6, sp; 16860857Sdarrenr size_t nlen, olen; 16953642Sguido fr_info_t fi; 170145522Sdarrenr nat_t *nat2; 17153642Sguido mb_t *m; 17253642Sguido 173145522Sdarrenr m = fin->fin_m; 17453642Sguido tcp = (tcphdr_t *)fin->fin_dp; 175145522Sdarrenr off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 176145522Sdarrenr 17760857Sdarrenr /* 17860857Sdarrenr * Check for client sending out PORT message. 17960857Sdarrenr */ 180110916Sdarrenr if (dlen < IPF_MINPORTLEN) { 181145522Sdarrenr if (ippr_ftp_debug > 1) 182145522Sdarrenr printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", 183145522Sdarrenr dlen); 18453642Sguido return 0; 185110916Sdarrenr } 18660857Sdarrenr /* 18753642Sguido * Skip the PORT command + space 18853642Sguido */ 18960857Sdarrenr s = f->ftps_rptr + 5; 19053642Sguido /* 19153642Sguido * Pick out the address components, two at a time. 19253642Sguido */ 19360857Sdarrenr a1 = ippr_ftp_atoi(&s); 194110916Sdarrenr if (s == NULL) { 195145522Sdarrenr if (ippr_ftp_debug > 1) 196145522Sdarrenr printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1); 19753642Sguido return 0; 198110916Sdarrenr } 19960857Sdarrenr a2 = ippr_ftp_atoi(&s); 200110916Sdarrenr if (s == NULL) { 201145522Sdarrenr if (ippr_ftp_debug > 1) 202145522Sdarrenr printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2); 20353642Sguido return 0; 204110916Sdarrenr } 205145522Sdarrenr 20653642Sguido /* 207145522Sdarrenr * Check that IP address in the PORT/PASV reply is the same as the 20853642Sguido * sender of the command - prevents using PORT for port scanning. 20953642Sguido */ 21053642Sguido a1 <<= 16; 21153642Sguido a1 |= a2; 212145522Sdarrenr if (((nat->nat_dir == NAT_OUTBOUND) && 213145522Sdarrenr (a1 != ntohl(nat->nat_inip.s_addr))) || 214145522Sdarrenr ((nat->nat_dir == NAT_INBOUND) && 215145522Sdarrenr (a1 != ntohl(nat->nat_oip.s_addr)))) { 216145522Sdarrenr if (ippr_ftp_debug > 0) 217145522Sdarrenr printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1"); 218145522Sdarrenr return APR_ERR(1); 219110916Sdarrenr } 22053642Sguido 22160857Sdarrenr a5 = ippr_ftp_atoi(&s); 222110916Sdarrenr if (s == NULL) { 223145522Sdarrenr if (ippr_ftp_debug > 1) 224145522Sdarrenr printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3); 22553642Sguido return 0; 226110916Sdarrenr } 22753642Sguido if (*s == ')') 22853642Sguido s++; 22953642Sguido 23053642Sguido /* 23153642Sguido * check for CR-LF at the end. 23253642Sguido */ 23353642Sguido if (*s == '\n') 23453642Sguido s--; 23553642Sguido if ((*s == '\r') && (*(s + 1) == '\n')) { 23653642Sguido s += 2; 23753642Sguido a6 = a5 & 0xff; 238110916Sdarrenr } else { 239145522Sdarrenr if (ippr_ftp_debug > 1) 240145522Sdarrenr printf("ippr_ftp_port:missing %s\n", "cr-lf"); 24153642Sguido return 0; 242110916Sdarrenr } 243145522Sdarrenr 24453642Sguido a5 >>= 8; 24567614Sdarrenr a5 &= 0xff; 246145522Sdarrenr sp = a5 << 8 | a6; 24753642Sguido /* 248145522Sdarrenr * Don't allow the PORT command to specify a port < 1024 due to 249145522Sdarrenr * security crap. 250145522Sdarrenr */ 251145522Sdarrenr if (sp < 1024) { 252145522Sdarrenr if (ippr_ftp_debug > 0) 253145522Sdarrenr printf("ippr_ftp_port:sp(%d) < 1024\n", sp); 254145522Sdarrenr return 0; 255145522Sdarrenr } 256145522Sdarrenr /* 25753642Sguido * Calculate new address parts for PORT command 25853642Sguido */ 259145522Sdarrenr if (nat->nat_dir == NAT_INBOUND) 260145522Sdarrenr a1 = ntohl(nat->nat_oip.s_addr); 261145522Sdarrenr else 262145522Sdarrenr a1 = ntohl(ip->ip_src.s_addr); 26353642Sguido a2 = (a1 >> 16) & 0xff; 26453642Sguido a3 = (a1 >> 8) & 0xff; 26553642Sguido a4 = a1 & 0xff; 26653642Sguido a1 >>= 24; 26760857Sdarrenr olen = s - f->ftps_rptr; 26892685Sdarrenr /* DO NOT change this to snprintf! */ 269145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL) 270145522Sdarrenr SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n", 271145522Sdarrenr "PORT", a1, a2, a3, a4, a5, a6); 272130886Sdarrenr#else 27355929Sguido (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", 27453642Sguido "PORT", a1, a2, a3, a4, a5, a6); 275130886Sdarrenr#endif 27653642Sguido 27753642Sguido nlen = strlen(newbuf); 27853642Sguido inc = nlen - olen; 279110916Sdarrenr if ((inc + ip->ip_len) > 65535) { 280145522Sdarrenr if (ippr_ftp_debug > 0) 281145522Sdarrenr printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n", 282145522Sdarrenr inc); 28360857Sdarrenr return 0; 284110916Sdarrenr } 28560857Sdarrenr 28695563Sdarrenr#if !defined(_KERNEL) 287145522Sdarrenr bcopy(newbuf, MTOD(m, char *) + off, nlen); 28895563Sdarrenr#else 289145522Sdarrenr# if defined(MENTAT) 29053642Sguido if (inc < 0) 291145522Sdarrenr (void)adjmsg(m, inc); 292145522Sdarrenr# else /* defined(MENTAT) */ 293145522Sdarrenr /* 294145522Sdarrenr * m_adj takes care of pkthdr.len, if required and treats inc<0 to 295145522Sdarrenr * mean remove -len bytes from the end of the packet. 296145522Sdarrenr * The mbuf chain will be extended if necessary by m_copyback(). 297145522Sdarrenr */ 298145522Sdarrenr if (inc < 0) 29953642Sguido m_adj(m, inc); 300145522Sdarrenr# endif /* defined(MENTAT) */ 301145522Sdarrenr#endif /* !defined(_KERNEL) */ 302145522Sdarrenr COPYBACK(m, off, nlen, newbuf); 303145522Sdarrenr 30453642Sguido if (inc != 0) { 30553642Sguido ip->ip_len += inc; 306145522Sdarrenr fin->fin_dlen += inc; 307145522Sdarrenr fin->fin_plen += inc; 30853642Sguido } 30953642Sguido 31053642Sguido /* 31153642Sguido * The server may not make the connection back from port 20, but 31253642Sguido * it is the most likely so use it here to check for a conflicting 31353642Sguido * mapping. 31453642Sguido */ 31592685Sdarrenr bcopy((char *)fin, (char *)&fi, sizeof(fi)); 316145522Sdarrenr fi.fin_state = NULL; 317145522Sdarrenr fi.fin_nat = NULL; 318145522Sdarrenr fi.fin_flx |= FI_IGNORE; 31992685Sdarrenr fi.fin_data[0] = sp; 32092685Sdarrenr fi.fin_data[1] = fin->fin_data[1] - 1; 321145522Sdarrenr /* 322145522Sdarrenr * Add skeleton NAT entry for connection which will come back the 323145522Sdarrenr * other way. 324145522Sdarrenr */ 325145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 326145522Sdarrenr nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, 327145522Sdarrenr nat->nat_inip, nat->nat_oip); 328145522Sdarrenr else 329145522Sdarrenr nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, 330145522Sdarrenr nat->nat_inip, nat->nat_oip); 331145522Sdarrenr if (nat2 == NULL) { 33260857Sdarrenr int slen; 33360857Sdarrenr 33460857Sdarrenr slen = ip->ip_len; 33560857Sdarrenr ip->ip_len = fin->fin_hlen + sizeof(*tcp2); 33653642Sguido bzero((char *)tcp2, sizeof(*tcp2)); 33753642Sguido tcp2->th_win = htons(8192); 33892685Sdarrenr tcp2->th_sport = htons(sp); 339145522Sdarrenr TCP_OFF_A(tcp2, 5); 34095563Sdarrenr tcp2->th_flags = TH_SYN; 34153642Sguido tcp2->th_dport = 0; /* XXX - don't specify remote port */ 34253642Sguido fi.fin_data[1] = 0; 34367853Sdarrenr fi.fin_dlen = sizeof(*tcp2); 344145522Sdarrenr fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 34553642Sguido fi.fin_dp = (char *)tcp2; 34692685Sdarrenr fi.fin_fr = &ftppxyfr; 347145522Sdarrenr fi.fin_out = nat->nat_dir; 348145522Sdarrenr fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; 34953642Sguido swip = ip->ip_src; 350145522Sdarrenr swip2 = ip->ip_dst; 351145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) { 352145522Sdarrenr fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; 353145522Sdarrenr ip->ip_src = nat->nat_inip; 354145522Sdarrenr } else if (nat->nat_dir == NAT_INBOUND) { 355145522Sdarrenr fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; 356145522Sdarrenr ip->ip_src = nat->nat_oip; 35753642Sguido } 358145522Sdarrenr 359145522Sdarrenr flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT; 360145522Sdarrenr if (nat->nat_dir == NAT_INBOUND) 361145522Sdarrenr flags |= NAT_NOTRULEPORT; 362145522Sdarrenr nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir); 363145522Sdarrenr 364145522Sdarrenr if (nat2 != NULL) { 365145522Sdarrenr (void) nat_proto(&fi, nat2, IPN_TCP); 366145522Sdarrenr nat_update(&fi, nat2, nat->nat_ptr); 367145522Sdarrenr fi.fin_ifp = NULL; 368145522Sdarrenr if (nat->nat_dir == NAT_INBOUND) { 369145522Sdarrenr fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; 370145522Sdarrenr ip->ip_dst = nat->nat_inip; 371145522Sdarrenr } 372145522Sdarrenr (void) fr_addstate(&fi, &nat2->nat_state, SI_W_DPORT); 373145522Sdarrenr if (fi.fin_state != NULL) 374145522Sdarrenr fr_statederef(&fi, (ipstate_t **)&fi.fin_state); 375145522Sdarrenr } 37660857Sdarrenr ip->ip_len = slen; 37753642Sguido ip->ip_src = swip; 378145522Sdarrenr ip->ip_dst = swip2; 379145522Sdarrenr } else { 380145522Sdarrenr ipstate_t *is; 381145522Sdarrenr 382145522Sdarrenr nat_update(&fi, nat2, nat->nat_ptr); 383145522Sdarrenr READ_ENTER(&ipf_state); 384145522Sdarrenr is = nat2->nat_state; 385145522Sdarrenr if (is != NULL) { 386145522Sdarrenr MUTEX_ENTER(&is->is_lock); 387145522Sdarrenr (void)fr_tcp_age(&is->is_sti, &fi, ips_tqtqb, 388145522Sdarrenr is->is_flags); 389145522Sdarrenr MUTEX_EXIT(&is->is_lock); 390145522Sdarrenr } 391145522Sdarrenr RWLOCK_EXIT(&ipf_state); 39253642Sguido } 393145522Sdarrenr return APR_INC(inc); 39453642Sguido} 39553642Sguido 39653642Sguido 39760857Sdarrenrint ippr_ftp_client(fin, ip, nat, ftp, dlen) 39853642Sguidofr_info_t *fin; 39960857Sdarrenrnat_t *nat; 40060857Sdarrenrftpinfo_t *ftp; 40153642Sguidoip_t *ip; 40260857Sdarrenrint dlen; 40353642Sguido{ 40463523Sdarrenr char *rptr, *wptr, cmd[6], c; 40560857Sdarrenr ftpside_t *f; 40663523Sdarrenr int inc, i; 40760857Sdarrenr 40860857Sdarrenr inc = 0; 40960857Sdarrenr f = &ftp->ftp_side[0]; 41060857Sdarrenr rptr = f->ftps_rptr; 41160857Sdarrenr wptr = f->ftps_wptr; 41260857Sdarrenr 41363523Sdarrenr for (i = 0; (i < 5) && (i < dlen); i++) { 41463523Sdarrenr c = rptr[i]; 415145522Sdarrenr if (ISALPHA(c)) { 416145522Sdarrenr cmd[i] = TOUPPER(c); 41763523Sdarrenr } else { 41863523Sdarrenr cmd[i] = c; 41963523Sdarrenr } 42063523Sdarrenr } 42163523Sdarrenr cmd[i] = '\0'; 42263523Sdarrenr 42380482Sdarrenr ftp->ftp_incok = 0; 42480482Sdarrenr if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) { 42580482Sdarrenr if (ftp->ftp_passok == FTPXY_ADOK_1 || 42680482Sdarrenr ftp->ftp_passok == FTPXY_AUOK_1) { 42780482Sdarrenr ftp->ftp_passok = FTPXY_USER_2; 42880482Sdarrenr ftp->ftp_incok = 1; 42980482Sdarrenr } else { 43080482Sdarrenr ftp->ftp_passok = FTPXY_USER_1; 43180482Sdarrenr ftp->ftp_incok = 1; 43280482Sdarrenr } 43380482Sdarrenr } else if (!strncmp(cmd, "AUTH ", 5)) { 43480482Sdarrenr ftp->ftp_passok = FTPXY_AUTH_1; 43580482Sdarrenr ftp->ftp_incok = 1; 43680482Sdarrenr } else if (!strncmp(cmd, "PASS ", 5)) { 43780482Sdarrenr if (ftp->ftp_passok == FTPXY_USOK_1) { 43880482Sdarrenr ftp->ftp_passok = FTPXY_PASS_1; 43980482Sdarrenr ftp->ftp_incok = 1; 44080482Sdarrenr } else if (ftp->ftp_passok == FTPXY_USOK_2) { 44180482Sdarrenr ftp->ftp_passok = FTPXY_PASS_2; 44280482Sdarrenr ftp->ftp_incok = 1; 44380482Sdarrenr } 44480482Sdarrenr } else if ((ftp->ftp_passok == FTPXY_AUOK_1) && 44580482Sdarrenr !strncmp(cmd, "ADAT ", 5)) { 44680482Sdarrenr ftp->ftp_passok = FTPXY_ADAT_1; 44780482Sdarrenr ftp->ftp_incok = 1; 44892685Sdarrenr } else if ((ftp->ftp_passok == FTPXY_PAOK_1 || 44992685Sdarrenr ftp->ftp_passok == FTPXY_PAOK_2) && 45080482Sdarrenr !strncmp(cmd, "ACCT ", 5)) { 45180482Sdarrenr ftp->ftp_passok = FTPXY_ACCT_1; 45280482Sdarrenr ftp->ftp_incok = 1; 45380482Sdarrenr } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly && 45463523Sdarrenr !strncmp(cmd, "PORT ", 5)) { 45560857Sdarrenr inc = ippr_ftp_port(fin, ip, nat, f, dlen); 45663523Sdarrenr } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly && 45763523Sdarrenr !strncmp(cmd, "PORT ", 5)) { 45863523Sdarrenr inc = ippr_ftp_port(fin, ip, nat, f, dlen); 45960857Sdarrenr } 46060857Sdarrenr 46160857Sdarrenr while ((*rptr++ != '\n') && (rptr < wptr)) 46260857Sdarrenr ; 46360857Sdarrenr f->ftps_rptr = rptr; 46460857Sdarrenr return inc; 46553642Sguido} 46653642Sguido 46753642Sguido 468110916Sdarrenrint ippr_ftp_pasv(fin, ip, nat, ftp, dlen) 46953642Sguidofr_info_t *fin; 47053642Sguidoip_t *ip; 47153642Sguidonat_t *nat; 472110916Sdarrenrftpinfo_t *ftp; 47360857Sdarrenrint dlen; 47453642Sguido{ 475145522Sdarrenr u_int a1, a2, a3, a4, data_ip; 476145522Sdarrenr char newbuf[IPF_FTPBUFSZ]; 477145522Sdarrenr char *s, *brackets[2]; 478145522Sdarrenr u_short a5, a6; 479110916Sdarrenr ftpside_t *f; 48053642Sguido 481110916Sdarrenr if (ippr_ftp_forcepasv != 0 && 482110916Sdarrenr ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) { 483145522Sdarrenr if (ippr_ftp_debug > 0) 484145522Sdarrenr printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n", 485145522Sdarrenr ftp->ftp_side[0].ftps_cmds); 486110916Sdarrenr return 0; 487110916Sdarrenr } 488110916Sdarrenr 489110916Sdarrenr f = &ftp->ftp_side[1]; 490110916Sdarrenr 49180482Sdarrenr#define PASV_REPLEN 24 49260857Sdarrenr /* 49360857Sdarrenr * Check for PASV reply message. 49460857Sdarrenr */ 495110916Sdarrenr if (dlen < IPF_MIN227LEN) { 496145522Sdarrenr if (ippr_ftp_debug > 1) 497145522Sdarrenr printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", 498145522Sdarrenr dlen); 49960857Sdarrenr return 0; 500110916Sdarrenr } else if (strncmp(f->ftps_rptr, 501110916Sdarrenr "227 Entering Passive Mod", PASV_REPLEN)) { 502145522Sdarrenr if (ippr_ftp_debug > 0) 503145522Sdarrenr printf("ippr_ftp_pasv:%d reply wrong\n", 227); 50460857Sdarrenr return 0; 505110916Sdarrenr } 50660857Sdarrenr 507145522Sdarrenr brackets[0] = ""; 508145522Sdarrenr brackets[1] = ""; 50953642Sguido /* 510110916Sdarrenr * Skip the PASV reply + space 51153642Sguido */ 51280482Sdarrenr s = f->ftps_rptr + PASV_REPLEN; 513145522Sdarrenr while (*s && !ISDIGIT(*s)) { 514145522Sdarrenr if (*s == '(') { 515145522Sdarrenr brackets[0] = "("; 516145522Sdarrenr brackets[1] = ")"; 517145522Sdarrenr } 51853642Sguido s++; 519145522Sdarrenr } 520145522Sdarrenr 52153642Sguido /* 52253642Sguido * Pick out the address components, two at a time. 52353642Sguido */ 52460857Sdarrenr a1 = ippr_ftp_atoi(&s); 525110916Sdarrenr if (s == NULL) { 526145522Sdarrenr if (ippr_ftp_debug > 1) 527145522Sdarrenr printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1); 52853642Sguido return 0; 529110916Sdarrenr } 53060857Sdarrenr a2 = ippr_ftp_atoi(&s); 531110916Sdarrenr if (s == NULL) { 532145522Sdarrenr if (ippr_ftp_debug > 1) 533145522Sdarrenr printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2); 53453642Sguido return 0; 535110916Sdarrenr } 53653642Sguido 53753642Sguido /* 538145522Sdarrenr * check that IP address in the PASV reply is the same as the 539145522Sdarrenr * sender of the command - prevents using PASV for port scanning. 54053642Sguido */ 54153642Sguido a1 <<= 16; 54253642Sguido a1 |= a2; 543145522Sdarrenr 544145522Sdarrenr if (((nat->nat_dir == NAT_INBOUND) && 545145522Sdarrenr (a1 != ntohl(nat->nat_inip.s_addr))) || 546145522Sdarrenr ((nat->nat_dir == NAT_OUTBOUND) && 547145522Sdarrenr (a1 != ntohl(nat->nat_oip.s_addr)))) { 548145522Sdarrenr if (ippr_ftp_debug > 0) 549145522Sdarrenr printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1"); 55053642Sguido return 0; 551110916Sdarrenr } 55253642Sguido 55360857Sdarrenr a5 = ippr_ftp_atoi(&s); 554110916Sdarrenr if (s == NULL) { 555145522Sdarrenr if (ippr_ftp_debug > 1) 556145522Sdarrenr printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3); 55753642Sguido return 0; 558110916Sdarrenr } 55953642Sguido 56053642Sguido if (*s == ')') 56153642Sguido s++; 56280482Sdarrenr if (*s == '.') 56380482Sdarrenr s++; 56453642Sguido if (*s == '\n') 56553642Sguido s--; 56653642Sguido /* 56753642Sguido * check for CR-LF at the end. 56853642Sguido */ 56953642Sguido if ((*s == '\r') && (*(s + 1) == '\n')) { 57053642Sguido s += 2; 571110916Sdarrenr } else { 572145522Sdarrenr if (ippr_ftp_debug > 1) 573145522Sdarrenr printf("ippr_ftp_pasv:missing %s", "cr-lf\n"); 57453642Sguido return 0; 575110916Sdarrenr } 576145522Sdarrenr 577145522Sdarrenr a6 = a5 & 0xff; 57853642Sguido a5 >>= 8; 57953642Sguido /* 58053642Sguido * Calculate new address parts for 227 reply 58153642Sguido */ 582145522Sdarrenr if (nat->nat_dir == NAT_INBOUND) { 583145522Sdarrenr data_ip = nat->nat_outip.s_addr; 584145522Sdarrenr a1 = ntohl(data_ip); 585145522Sdarrenr } else 586145522Sdarrenr data_ip = htonl(a1); 587145522Sdarrenr 58853642Sguido a2 = (a1 >> 16) & 0xff; 58953642Sguido a3 = (a1 >> 8) & 0xff; 59053642Sguido a4 = a1 & 0xff; 59153642Sguido a1 >>= 24; 592145522Sdarrenr 593145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL) 594145522Sdarrenr SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n", 595145522Sdarrenr "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, 596145522Sdarrenr a5, a6, brackets[1]); 597145522Sdarrenr#else 598145522Sdarrenr (void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n", 599145522Sdarrenr "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, 600145522Sdarrenr a5, a6, brackets[1]); 601145522Sdarrenr#endif 602145522Sdarrenr return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6), 603145522Sdarrenr newbuf, s, data_ip); 604145522Sdarrenr} 605145522Sdarrenr 606145522Sdarrenrint ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip) 607145522Sdarrenrfr_info_t *fin; 608145522Sdarrenrip_t *ip; 609145522Sdarrenrnat_t *nat; 610145522Sdarrenrftpside_t *f; 611145522Sdarrenru_int port; 612145522Sdarrenrchar *newmsg; 613145522Sdarrenrchar *s; 614145522Sdarrenru_int data_ip; 615145522Sdarrenr{ 616145522Sdarrenr int inc, off, nflags, sflags; 617145522Sdarrenr tcphdr_t *tcp, tcph, *tcp2; 618145522Sdarrenr struct in_addr swip, swip2; 619145522Sdarrenr struct in_addr data_addr; 620145522Sdarrenr size_t nlen, olen; 621145522Sdarrenr fr_info_t fi; 622145522Sdarrenr nat_t *nat2; 623145522Sdarrenr mb_t *m; 624145522Sdarrenr 625145522Sdarrenr m = fin->fin_m; 626145522Sdarrenr tcp = (tcphdr_t *)fin->fin_dp; 627145522Sdarrenr off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 628145522Sdarrenr 629145522Sdarrenr data_addr.s_addr = data_ip; 630145522Sdarrenr tcp2 = &tcph; 63160857Sdarrenr inc = 0; 632145522Sdarrenr 633145522Sdarrenr 63460857Sdarrenr olen = s - f->ftps_rptr; 635145522Sdarrenr nlen = strlen(newmsg); 63653642Sguido inc = nlen - olen; 637145522Sdarrenr if ((inc + ip->ip_len) > 65535) { 638145522Sdarrenr if (ippr_ftp_debug > 0) 639145522Sdarrenr printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", 640145522Sdarrenr inc); 64160857Sdarrenr return 0; 642145522Sdarrenr } 64360857Sdarrenr 64495563Sdarrenr#if !defined(_KERNEL) 645145522Sdarrenr bcopy(newmsg, MTOD(m, char *) + off, nlen); 64695563Sdarrenr#else 647145522Sdarrenr# if defined(MENTAT) 64853642Sguido if (inc < 0) 649145522Sdarrenr (void)adjmsg(m, inc); 650145522Sdarrenr# else /* defined(MENTAT) */ 651145522Sdarrenr /* 652145522Sdarrenr * m_adj takes care of pkthdr.len, if required and treats inc<0 to 653145522Sdarrenr * mean remove -len bytes from the end of the packet. 654145522Sdarrenr * The mbuf chain will be extended if necessary by m_copyback(). 655145522Sdarrenr */ 656145522Sdarrenr if (inc < 0) 65753642Sguido m_adj(m, inc); 658145522Sdarrenr# endif /* defined(MENTAT) */ 659145522Sdarrenr#endif /* !defined(_KERNEL) */ 660145522Sdarrenr COPYBACK(m, off, nlen, newmsg); 661145522Sdarrenr 66253642Sguido if (inc != 0) { 66353642Sguido ip->ip_len += inc; 664145522Sdarrenr fin->fin_dlen += inc; 665145522Sdarrenr fin->fin_plen += inc; 66653642Sguido } 66753642Sguido 66853642Sguido /* 66953642Sguido * Add skeleton NAT entry for connection which will come back the 67053642Sguido * other way. 67153642Sguido */ 67292685Sdarrenr bcopy((char *)fin, (char *)&fi, sizeof(fi)); 673145522Sdarrenr fi.fin_state = NULL; 674145522Sdarrenr fi.fin_nat = NULL; 675145522Sdarrenr fi.fin_flx |= FI_IGNORE; 67692685Sdarrenr fi.fin_data[0] = 0; 677145522Sdarrenr fi.fin_data[1] = port; 678145522Sdarrenr nflags = IPN_TCP|SI_W_SPORT; 679145522Sdarrenr if (ippr_ftp_pasvrdr && f->ftps_ifp) 680145522Sdarrenr nflags |= SI_W_DPORT; 681145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) 682145522Sdarrenr nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH, 683145522Sdarrenr nat->nat_p, nat->nat_inip, nat->nat_oip); 684145522Sdarrenr else 685145522Sdarrenr nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH, 686145522Sdarrenr nat->nat_p, nat->nat_inip, nat->nat_oip); 687145522Sdarrenr if (nat2 == NULL) { 68860857Sdarrenr int slen; 68960857Sdarrenr 69060857Sdarrenr slen = ip->ip_len; 69160857Sdarrenr ip->ip_len = fin->fin_hlen + sizeof(*tcp2); 69253642Sguido bzero((char *)tcp2, sizeof(*tcp2)); 69353642Sguido tcp2->th_win = htons(8192); 69453642Sguido tcp2->th_sport = 0; /* XXX - fake it for nat_new */ 695145522Sdarrenr TCP_OFF_A(tcp2, 5); 69695563Sdarrenr tcp2->th_flags = TH_SYN; 697145522Sdarrenr fi.fin_data[1] = port; 69867853Sdarrenr fi.fin_dlen = sizeof(*tcp2); 699145522Sdarrenr tcp2->th_dport = htons(port); 70092685Sdarrenr fi.fin_data[0] = 0; 70153642Sguido fi.fin_dp = (char *)tcp2; 702145522Sdarrenr fi.fin_plen = fi.fin_hlen + sizeof(*tcp); 70392685Sdarrenr fi.fin_fr = &ftppxyfr; 704145522Sdarrenr fi.fin_out = nat->nat_dir; 705145522Sdarrenr fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; 70653642Sguido swip = ip->ip_src; 70753642Sguido swip2 = ip->ip_dst; 708145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) { 709145522Sdarrenr fi.fin_fi.fi_daddr = data_addr.s_addr; 710145522Sdarrenr fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; 711145522Sdarrenr ip->ip_dst = data_addr; 712145522Sdarrenr ip->ip_src = nat->nat_inip; 713145522Sdarrenr } else if (nat->nat_dir == NAT_INBOUND) { 714145522Sdarrenr fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; 715145522Sdarrenr fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; 716145522Sdarrenr ip->ip_src = nat->nat_oip; 717145522Sdarrenr ip->ip_dst = nat->nat_outip; 71853642Sguido } 719145522Sdarrenr 720145522Sdarrenr sflags = nflags; 721145522Sdarrenr nflags |= NAT_SLAVE; 722145522Sdarrenr if (nat->nat_dir == NAT_INBOUND) 723145522Sdarrenr nflags |= NAT_NOTRULEPORT; 724145522Sdarrenr nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir); 725145522Sdarrenr if (nat2 != NULL) { 726145522Sdarrenr (void) nat_proto(&fi, nat2, IPN_TCP); 727145522Sdarrenr nat_update(&fi, nat2, nat->nat_ptr); 728145522Sdarrenr fi.fin_ifp = NULL; 729145522Sdarrenr if (nat->nat_dir == NAT_INBOUND) { 730145522Sdarrenr fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; 731145522Sdarrenr ip->ip_dst = nat->nat_inip; 732145522Sdarrenr } 733145522Sdarrenr (void) fr_addstate(&fi, &nat2->nat_state, sflags); 734145522Sdarrenr if (fi.fin_state != NULL) 735145522Sdarrenr fr_statederef(&fi, (ipstate_t **)&fi.fin_state); 736145522Sdarrenr } 737145522Sdarrenr 73860857Sdarrenr ip->ip_len = slen; 73953642Sguido ip->ip_src = swip; 74053642Sguido ip->ip_dst = swip2; 741145522Sdarrenr } else { 742145522Sdarrenr ipstate_t *is; 743145522Sdarrenr 744145522Sdarrenr nat_update(&fi, nat2, nat->nat_ptr); 745145522Sdarrenr READ_ENTER(&ipf_state); 746145522Sdarrenr is = nat2->nat_state; 747145522Sdarrenr if (is != NULL) { 748145522Sdarrenr MUTEX_ENTER(&is->is_lock); 749145522Sdarrenr (void)fr_tcp_age(&is->is_sti, &fi, ips_tqtqb, 750145522Sdarrenr is->is_flags); 751145522Sdarrenr MUTEX_EXIT(&is->is_lock); 752145522Sdarrenr } 753145522Sdarrenr RWLOCK_EXIT(&ipf_state); 75453642Sguido } 75553642Sguido return inc; 75653642Sguido} 75753642Sguido 75853642Sguido 75960857Sdarrenrint ippr_ftp_server(fin, ip, nat, ftp, dlen) 76060857Sdarrenrfr_info_t *fin; 76160857Sdarrenrip_t *ip; 76260857Sdarrenrnat_t *nat; 76360857Sdarrenrftpinfo_t *ftp; 76460857Sdarrenrint dlen; 76560857Sdarrenr{ 76660857Sdarrenr char *rptr, *wptr; 76760857Sdarrenr ftpside_t *f; 76860857Sdarrenr int inc; 76960857Sdarrenr 77060857Sdarrenr inc = 0; 77160857Sdarrenr f = &ftp->ftp_side[1]; 77260857Sdarrenr rptr = f->ftps_rptr; 77360857Sdarrenr wptr = f->ftps_wptr; 77460857Sdarrenr 775145522Sdarrenr if (*rptr == ' ') 776145522Sdarrenr goto server_cmd_ok; 777145522Sdarrenr if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2))) 778102520Sdarrenr return 0; 77980482Sdarrenr if (ftp->ftp_passok == FTPXY_GO) { 78080482Sdarrenr if (!strncmp(rptr, "227 ", 4)) 781110916Sdarrenr inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen); 782145522Sdarrenr else if (!strncmp(rptr, "229 ", 4)) 783145522Sdarrenr inc = ippr_ftp_epsv(fin, ip, nat, f, dlen); 78463523Sdarrenr } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) { 785110916Sdarrenr inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen); 786145522Sdarrenr } else if (ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) { 787145522Sdarrenr inc = ippr_ftp_epsv(fin, ip, nat, f, dlen); 78880482Sdarrenr } else if (*rptr == '5' || *rptr == '4') 78980482Sdarrenr ftp->ftp_passok = FTPXY_INIT; 79080482Sdarrenr else if (ftp->ftp_incok) { 79180482Sdarrenr if (*rptr == '3') { 79280482Sdarrenr if (ftp->ftp_passok == FTPXY_ACCT_1) 79380482Sdarrenr ftp->ftp_passok = FTPXY_GO; 79480482Sdarrenr else 79580482Sdarrenr ftp->ftp_passok++; 79680482Sdarrenr } else if (*rptr == '2') { 79780482Sdarrenr switch (ftp->ftp_passok) 79880482Sdarrenr { 79980482Sdarrenr case FTPXY_USER_1 : 80080482Sdarrenr case FTPXY_USER_2 : 80180482Sdarrenr case FTPXY_PASS_1 : 80280482Sdarrenr case FTPXY_PASS_2 : 80380482Sdarrenr case FTPXY_ACCT_1 : 80480482Sdarrenr ftp->ftp_passok = FTPXY_GO; 80580482Sdarrenr break; 80680482Sdarrenr default : 80780482Sdarrenr ftp->ftp_passok += 3; 80880482Sdarrenr break; 80980482Sdarrenr } 81080482Sdarrenr } 81160857Sdarrenr } 812145522Sdarrenrserver_cmd_ok: 81380482Sdarrenr ftp->ftp_incok = 0; 814110916Sdarrenr 81560857Sdarrenr while ((*rptr++ != '\n') && (rptr < wptr)) 81660857Sdarrenr ; 81760857Sdarrenr f->ftps_rptr = rptr; 81860857Sdarrenr return inc; 81960857Sdarrenr} 82060857Sdarrenr 82160857Sdarrenr 82260857Sdarrenr/* 82360857Sdarrenr * Look to see if the buffer starts with something which we recognise as 82460857Sdarrenr * being the correct syntax for the FTP protocol. 82560857Sdarrenr */ 826110916Sdarrenrint ippr_ftp_client_valid(ftps, buf, len) 827110916Sdarrenrftpside_t *ftps; 82860857Sdarrenrchar *buf; 82960857Sdarrenrsize_t len; 83060857Sdarrenr{ 831145522Sdarrenr register char *s, c, pc; 83260857Sdarrenr register size_t i = len; 833110916Sdarrenr char cmd[5]; 83460857Sdarrenr 835145522Sdarrenr s = buf; 836145522Sdarrenr 837145522Sdarrenr if (ftps->ftps_junk == 1) 838145522Sdarrenr return 1; 839145522Sdarrenr 840110916Sdarrenr if (i < 5) { 841145522Sdarrenr if (ippr_ftp_debug > 3) 842145522Sdarrenr printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i); 84360857Sdarrenr return 2; 844110916Sdarrenr } 845145522Sdarrenr 846145522Sdarrenr i--; 84760857Sdarrenr c = *s++; 84860857Sdarrenr 849145522Sdarrenr if (ISALPHA(c)) { 850145522Sdarrenr cmd[0] = TOUPPER(c); 85160857Sdarrenr c = *s++; 85260857Sdarrenr i--; 853145522Sdarrenr if (ISALPHA(c)) { 854145522Sdarrenr cmd[1] = TOUPPER(c); 85560857Sdarrenr c = *s++; 85660857Sdarrenr i--; 857145522Sdarrenr if (ISALPHA(c)) { 858145522Sdarrenr cmd[2] = TOUPPER(c); 85960857Sdarrenr c = *s++; 86060857Sdarrenr i--; 861145522Sdarrenr if (ISALPHA(c)) { 862145522Sdarrenr cmd[3] = TOUPPER(c); 86392685Sdarrenr c = *s++; 86492685Sdarrenr i--; 86592685Sdarrenr if ((c != ' ') && (c != '\r')) 866110916Sdarrenr goto bad_client_command; 86792685Sdarrenr } else if ((c != ' ') && (c != '\r')) 868110916Sdarrenr goto bad_client_command; 86960857Sdarrenr } else 870110916Sdarrenr goto bad_client_command; 87160857Sdarrenr } else 872110916Sdarrenr goto bad_client_command; 873110916Sdarrenr } else { 874110916Sdarrenrbad_client_command: 875145522Sdarrenr if (ippr_ftp_debug > 3) 876145522Sdarrenr printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", 877145522Sdarrenr "ippr_ftp_client_valid", 878145522Sdarrenr ftps->ftps_junk, (int)len, (int)i, c, 879145522Sdarrenr (int)len, (int)len, buf); 88092685Sdarrenr return 1; 881110916Sdarrenr } 882110916Sdarrenr 88392685Sdarrenr for (; i; i--) { 884145522Sdarrenr pc = c; 88560857Sdarrenr c = *s++; 886145522Sdarrenr if ((pc == '\r') && (c == '\n')) { 887110916Sdarrenr cmd[4] = '\0'; 888110916Sdarrenr if (!strcmp(cmd, "PASV")) 889110916Sdarrenr ftps->ftps_cmds = FTPXY_C_PASV; 890110916Sdarrenr else 891110916Sdarrenr ftps->ftps_cmds = 0; 89292685Sdarrenr return 0; 893110916Sdarrenr } 89492685Sdarrenr } 895145522Sdarrenr#if !defined(_KERNEL) 896145522Sdarrenr printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n", 897145522Sdarrenr (int)len, (int)len, buf); 898110916Sdarrenr#endif 89992685Sdarrenr return 2; 90092685Sdarrenr} 90192685Sdarrenr 90292685Sdarrenr 903110916Sdarrenrint ippr_ftp_server_valid(ftps, buf, len) 904110916Sdarrenrftpside_t *ftps; 90592685Sdarrenrchar *buf; 90692685Sdarrenrsize_t len; 90792685Sdarrenr{ 908145522Sdarrenr register char *s, c, pc; 90992685Sdarrenr register size_t i = len; 910110916Sdarrenr int cmd; 91192685Sdarrenr 912145522Sdarrenr s = buf; 913145522Sdarrenr cmd = 0; 914145522Sdarrenr 915145522Sdarrenr if (ftps->ftps_junk == 1) 916145522Sdarrenr return 1; 917145522Sdarrenr 918145522Sdarrenr if (i < 5) { 919145522Sdarrenr if (ippr_ftp_debug > 3) 920145522Sdarrenr printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i); 92192685Sdarrenr return 2; 922145522Sdarrenr } 923145522Sdarrenr 92492685Sdarrenr c = *s++; 92592685Sdarrenr i--; 926145522Sdarrenr if (c == ' ') 927145522Sdarrenr goto search_eol; 92892685Sdarrenr 929145522Sdarrenr if (ISDIGIT(c)) { 930110916Sdarrenr cmd = (c - '0') * 100; 93192685Sdarrenr c = *s++; 93260857Sdarrenr i--; 933145522Sdarrenr if (ISDIGIT(c)) { 934110916Sdarrenr cmd += (c - '0') * 10; 93560857Sdarrenr c = *s++; 93660857Sdarrenr i--; 937145522Sdarrenr if (ISDIGIT(c)) { 938110916Sdarrenr cmd += (c - '0'); 93960857Sdarrenr c = *s++; 94060857Sdarrenr i--; 94192685Sdarrenr if ((c != '-') && (c != ' ')) 942110916Sdarrenr goto bad_server_command; 94360857Sdarrenr } else 944110916Sdarrenr goto bad_server_command; 94560857Sdarrenr } else 946110916Sdarrenr goto bad_server_command; 947110916Sdarrenr } else { 948110916Sdarrenrbad_server_command: 949145522Sdarrenr if (ippr_ftp_debug > 3) 950145522Sdarrenr printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", 951145522Sdarrenr "ippr_ftp_server_valid", 952145522Sdarrenr ftps->ftps_junk, (int)len, (int)i, 953145522Sdarrenr c, (int)len, (int)len, buf); 95460857Sdarrenr return 1; 955110916Sdarrenr } 956145522Sdarrenrsearch_eol: 95760857Sdarrenr for (; i; i--) { 958145522Sdarrenr pc = c; 95960857Sdarrenr c = *s++; 960145522Sdarrenr if ((pc == '\r') && (c == '\n')) { 961110916Sdarrenr ftps->ftps_cmds = cmd; 96260857Sdarrenr return 0; 963110916Sdarrenr } 96460857Sdarrenr } 965145522Sdarrenr if (ippr_ftp_debug > 3) 966145522Sdarrenr printf("ippr_ftp_server_valid:junk after cmd[%*.*s]\n", 967145522Sdarrenr (int)len, (int)len, buf); 96860857Sdarrenr return 2; 96960857Sdarrenr} 97060857Sdarrenr 97160857Sdarrenr 972110916Sdarrenrint ippr_ftp_valid(ftp, side, buf, len) 973110916Sdarrenrftpinfo_t *ftp; 97492685Sdarrenrint side; 97592685Sdarrenrchar *buf; 97692685Sdarrenrsize_t len; 97792685Sdarrenr{ 978110916Sdarrenr ftpside_t *ftps; 97992685Sdarrenr int ret; 98092685Sdarrenr 981110916Sdarrenr ftps = &ftp->ftp_side[side]; 982110916Sdarrenr 98392685Sdarrenr if (side == 0) 984110916Sdarrenr ret = ippr_ftp_client_valid(ftps, buf, len); 98592685Sdarrenr else 986110916Sdarrenr ret = ippr_ftp_server_valid(ftps, buf, len); 98792685Sdarrenr return ret; 98892685Sdarrenr} 98992685Sdarrenr 99092685Sdarrenr 991102520Sdarrenr/* 992145522Sdarrenr * For map rules, the following applies: 993102520Sdarrenr * rv == 0 for outbound processing, 994102520Sdarrenr * rv == 1 for inbound processing. 995145522Sdarrenr * For rdr rules, the following applies: 996145522Sdarrenr * rv == 0 for inbound processing, 997145522Sdarrenr * rv == 1 for outbound processing. 998102520Sdarrenr */ 999145522Sdarrenrint ippr_ftp_process(fin, nat, ftp, rv) 100060857Sdarrenrfr_info_t *fin; 100160857Sdarrenrnat_t *nat; 100260857Sdarrenrftpinfo_t *ftp; 100360857Sdarrenrint rv; 100460857Sdarrenr{ 1005102520Sdarrenr int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff; 1006145522Sdarrenr char *rptr, *wptr, *s; 1007102520Sdarrenr u_32_t thseq, thack; 1008102520Sdarrenr ap_session_t *aps; 100963523Sdarrenr ftpside_t *f, *t; 101060857Sdarrenr tcphdr_t *tcp; 1011145522Sdarrenr ip_t *ip; 101260857Sdarrenr mb_t *m; 101360857Sdarrenr 1014145522Sdarrenr m = fin->fin_m; 1015145522Sdarrenr ip = fin->fin_ip; 101660857Sdarrenr tcp = (tcphdr_t *)fin->fin_dp; 1017145522Sdarrenr off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 101860857Sdarrenr 1019145522Sdarrenr f = &ftp->ftp_side[rv]; 1020145522Sdarrenr t = &ftp->ftp_side[1 - rv]; 1021145522Sdarrenr thseq = ntohl(tcp->th_seq); 1022145522Sdarrenr thack = ntohl(tcp->th_ack); 1023145522Sdarrenr 1024145522Sdarrenr#ifdef __sgi 1025145522Sdarrenr mlen = fin->fin_plen - off; 102660857Sdarrenr#else 1027145522Sdarrenr mlen = MSGDSIZE(m) - off; 102860857Sdarrenr#endif 1029145522Sdarrenr if (ippr_ftp_debug > 4) 1030145522Sdarrenr printf("ippr_ftp_process: mlen %d\n", mlen); 103172006Sdarrenr 1032145522Sdarrenr if (mlen <= 0) { 1033145522Sdarrenr if ((tcp->th_flags & TH_OPENING) == TH_OPENING) { 1034145522Sdarrenr f->ftps_seq[0] = thseq + 1; 1035145522Sdarrenr t->ftps_seq[0] = thack; 1036145522Sdarrenr } 1037145522Sdarrenr return 0; 1038145522Sdarrenr } 1039102520Sdarrenr aps = nat->nat_aps; 104060857Sdarrenr 1041102520Sdarrenr sel = aps->aps_sel[1 - rv]; 1042102520Sdarrenr sel2 = aps->aps_sel[rv]; 1043102520Sdarrenr if (rv == 0) { 1044102520Sdarrenr seqoff = aps->aps_seqoff[sel]; 1045102520Sdarrenr if (aps->aps_seqmin[sel] > seqoff + thseq) 1046102520Sdarrenr seqoff = aps->aps_seqoff[!sel]; 1047102520Sdarrenr ackoff = aps->aps_ackoff[sel2]; 1048102520Sdarrenr if (aps->aps_ackmin[sel2] > ackoff + thack) 1049102520Sdarrenr ackoff = aps->aps_ackoff[!sel2]; 1050102520Sdarrenr } else { 1051102520Sdarrenr seqoff = aps->aps_ackoff[sel]; 1052145522Sdarrenr if (ippr_ftp_debug > 2) 1053145522Sdarrenr printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq, 1054145522Sdarrenr aps->aps_ackmin[sel]); 1055102520Sdarrenr if (aps->aps_ackmin[sel] > seqoff + thseq) 1056102520Sdarrenr seqoff = aps->aps_ackoff[!sel]; 105760857Sdarrenr 1058102520Sdarrenr ackoff = aps->aps_seqoff[sel2]; 1059145522Sdarrenr if (ippr_ftp_debug > 2) 1060145522Sdarrenr printf("ackoff %d thack %x seqmin %x\n", ackoff, thack, 1061145522Sdarrenr aps->aps_seqmin[sel2]); 1062102520Sdarrenr if (ackoff > 0) { 1063102520Sdarrenr if (aps->aps_seqmin[sel2] > ackoff + thack) 1064102520Sdarrenr ackoff = aps->aps_seqoff[!sel2]; 1065102520Sdarrenr } else { 1066102520Sdarrenr if (aps->aps_seqmin[sel2] > thack) 1067102520Sdarrenr ackoff = aps->aps_seqoff[!sel2]; 1068102520Sdarrenr } 106995563Sdarrenr } 1070145522Sdarrenr if (ippr_ftp_debug > 2) { 1071145522Sdarrenr printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n", 1072145522Sdarrenr rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff, 1073145522Sdarrenr thack, ackoff, mlen, fin->fin_plen, off); 1074145522Sdarrenr printf("sel %d seqmin %x/%x offset %d/%d\n", sel, 1075145522Sdarrenr aps->aps_seqmin[sel], aps->aps_seqmin[sel2], 1076145522Sdarrenr aps->aps_seqoff[sel], aps->aps_seqoff[sel2]); 1077145522Sdarrenr printf("sel %d ackmin %x/%x offset %d/%d\n", sel2, 1078145522Sdarrenr aps->aps_ackmin[sel], aps->aps_ackmin[sel2], 1079145522Sdarrenr aps->aps_ackoff[sel], aps->aps_ackoff[sel2]); 1080145522Sdarrenr } 1081102520Sdarrenr 108260857Sdarrenr /* 108360857Sdarrenr * XXX - Ideally, this packet should get dropped because we now know 108460857Sdarrenr * that it is out of order (and there is no real danger in doing so 108560857Sdarrenr * apart from causing packets to go through here ordered). 108660857Sdarrenr */ 1087145522Sdarrenr if (ippr_ftp_debug > 2) { 1088145522Sdarrenr printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n", 1089145522Sdarrenr rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff); 1090145522Sdarrenr } 1091102520Sdarrenr 1092102520Sdarrenr ok = 0; 1093110916Sdarrenr if (t->ftps_seq[0] == 0) { 1094110916Sdarrenr t->ftps_seq[0] = thack; 1095110916Sdarrenr ok = 1; 1096110916Sdarrenr } else { 1097102520Sdarrenr if (ackoff == 0) { 1098102520Sdarrenr if (t->ftps_seq[0] == thack) 1099102520Sdarrenr ok = 1; 1100102520Sdarrenr else if (t->ftps_seq[1] == thack) { 1101102520Sdarrenr t->ftps_seq[0] = thack; 1102102520Sdarrenr ok = 1; 1103102520Sdarrenr } 1104102520Sdarrenr } else { 1105102520Sdarrenr if (t->ftps_seq[0] + ackoff == thack) 1106102520Sdarrenr ok = 1; 1107102520Sdarrenr else if (t->ftps_seq[0] == thack + ackoff) 1108102520Sdarrenr ok = 1; 1109102520Sdarrenr else if (t->ftps_seq[1] + ackoff == thack) { 1110102520Sdarrenr t->ftps_seq[0] = thack - ackoff; 1111102520Sdarrenr ok = 1; 1112102520Sdarrenr } else if (t->ftps_seq[1] == thack + ackoff) { 1113102520Sdarrenr t->ftps_seq[0] = thack - ackoff; 1114102520Sdarrenr ok = 1; 1115102520Sdarrenr } 1116102520Sdarrenr } 1117102520Sdarrenr } 1118102520Sdarrenr 1119145522Sdarrenr if (ippr_ftp_debug > 2) { 1120145522Sdarrenr if (!ok) 1121145522Sdarrenr printf("%s ok\n", "not"); 1122145522Sdarrenr } 1123102520Sdarrenr 1124102520Sdarrenr if (!mlen) { 1125110916Sdarrenr if (t->ftps_seq[0] + ackoff != thack) { 1126145522Sdarrenr if (ippr_ftp_debug > 1) { 1127145522Sdarrenr printf("%s:seq[0](%x) + (%x) != (%x)\n", 1128145522Sdarrenr "ippr_ftp_process", t->ftps_seq[0], 1129145522Sdarrenr ackoff, thack); 1130145522Sdarrenr } 113195563Sdarrenr return APR_ERR(1); 1132110916Sdarrenr } 1133102520Sdarrenr 1134145522Sdarrenr if (ippr_ftp_debug > 2) { 1135145522Sdarrenr printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n", 1136145522Sdarrenr f->ftps_seq[0], f->ftps_seq[1]); 1137145522Sdarrenr } 1138145522Sdarrenr 1139102520Sdarrenr if (tcp->th_flags & TH_FIN) { 1140110916Sdarrenr if (thseq == f->ftps_seq[1]) { 1141110916Sdarrenr f->ftps_seq[0] = f->ftps_seq[1] - seqoff; 1142110916Sdarrenr f->ftps_seq[1] = thseq + 1 - seqoff; 1143110916Sdarrenr } else { 1144145522Sdarrenr if (ippr_ftp_debug > 1) { 1145145522Sdarrenr printf("FIN: thseq %x seqoff %d ftps_seq %x\n", 1146145522Sdarrenr thseq, seqoff, f->ftps_seq[0]); 1147145522Sdarrenr } 1148102520Sdarrenr return APR_ERR(1); 1149102520Sdarrenr } 115095563Sdarrenr } 1151102520Sdarrenr f->ftps_len = 0; 1152102520Sdarrenr return 0; 115360857Sdarrenr } 1154102520Sdarrenr 1155102520Sdarrenr ok = 0; 1156110916Sdarrenr if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) { 1157102520Sdarrenr ok = 1; 1158102520Sdarrenr /* 1159102520Sdarrenr * Retransmitted data packet. 1160102520Sdarrenr */ 1161110916Sdarrenr } else if ((thseq + mlen == f->ftps_seq[0]) || 1162110916Sdarrenr (thseq + mlen == f->ftps_seq[1])) { 1163102520Sdarrenr ok = 1; 1164110916Sdarrenr } 1165110916Sdarrenr 1166102520Sdarrenr if (ok == 0) { 1167102520Sdarrenr inc = thseq - f->ftps_seq[0]; 1168145522Sdarrenr if (ippr_ftp_debug > 1) { 1169145522Sdarrenr printf("inc %d sel %d rv %d\n", inc, sel, rv); 1170145522Sdarrenr printf("th_seq %x ftps_seq %x/%x\n", 1171145522Sdarrenr thseq, f->ftps_seq[0], f->ftps_seq[1]); 1172145522Sdarrenr printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel], 1173145522Sdarrenr aps->aps_ackoff[sel]); 1174145522Sdarrenr printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel], 1175145522Sdarrenr aps->aps_seqoff[sel]); 1176145522Sdarrenr } 1177102520Sdarrenr 1178102520Sdarrenr return APR_ERR(1); 1179102520Sdarrenr } 1180102520Sdarrenr 118195563Sdarrenr inc = 0; 1182102520Sdarrenr rptr = f->ftps_rptr; 1183102520Sdarrenr wptr = f->ftps_wptr; 1184102520Sdarrenr f->ftps_seq[0] = thseq; 1185102520Sdarrenr f->ftps_seq[1] = f->ftps_seq[0] + mlen; 118672006Sdarrenr f->ftps_len = mlen; 118760857Sdarrenr 118860857Sdarrenr while (mlen > 0) { 1189145522Sdarrenr len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr)); 1190145522Sdarrenr COPYDATA(m, off, len, wptr); 119160857Sdarrenr mlen -= len; 119260857Sdarrenr off += len; 119360857Sdarrenr wptr += len; 1194145522Sdarrenr 1195145522Sdarrenr if (ippr_ftp_debug > 3) 1196145522Sdarrenr printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n", 1197145522Sdarrenr "ippr_ftp_process", 1198145522Sdarrenr len, mlen, off, (u_long)wptr, f->ftps_junk, 1199145522Sdarrenr len, len, rptr); 1200145522Sdarrenr 120160857Sdarrenr f->ftps_wptr = wptr; 1202145522Sdarrenr if (f->ftps_junk != 0) { 1203145522Sdarrenr i = f->ftps_junk; 1204110916Sdarrenr f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, 1205110916Sdarrenr wptr - rptr); 120660857Sdarrenr 1207145522Sdarrenr if (ippr_ftp_debug > 5) 1208145522Sdarrenr printf("%s:junk %d -> %d\n", 1209145522Sdarrenr "ippr_ftp_process", i, f->ftps_junk); 1210145522Sdarrenr 1211145522Sdarrenr if (f->ftps_junk != 0) { 1212145522Sdarrenr if (wptr - rptr == sizeof(f->ftps_buf)) { 1213145522Sdarrenr if (ippr_ftp_debug > 4) 1214145522Sdarrenr printf("%s:full buffer\n", 1215145522Sdarrenr "ippr_ftp_process"); 1216145522Sdarrenr f->ftps_rptr = f->ftps_buf; 1217145522Sdarrenr f->ftps_wptr = f->ftps_buf; 1218145522Sdarrenr rptr = f->ftps_rptr; 1219145522Sdarrenr wptr = f->ftps_wptr; 1220145522Sdarrenr /* 1221145522Sdarrenr * Because we throw away data here that 1222145522Sdarrenr * we would otherwise parse, set the 1223145522Sdarrenr * junk flag to indicate just ignore 1224145522Sdarrenr * any data upto the next CRLF. 1225145522Sdarrenr */ 1226145522Sdarrenr f->ftps_junk = 1; 1227145522Sdarrenr continue; 1228145522Sdarrenr } 1229145522Sdarrenr } 1230145522Sdarrenr } 1231145522Sdarrenr 123260857Sdarrenr while ((f->ftps_junk == 0) && (wptr > rptr)) { 1233145522Sdarrenr len = wptr - rptr; 1234145522Sdarrenr f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len); 1235145522Sdarrenr 1236145522Sdarrenr if (ippr_ftp_debug > 3) { 1237145522Sdarrenr printf("%s=%d len %d rv %d ptr %lx/%lx ", 1238145522Sdarrenr "ippr_ftp_valid", 1239145522Sdarrenr f->ftps_junk, len, rv, (u_long)rptr, 1240145522Sdarrenr (u_long)wptr); 1241145522Sdarrenr printf("buf [%*.*s]\n", len, len, rptr); 1242145522Sdarrenr } 1243145522Sdarrenr 124460857Sdarrenr if (f->ftps_junk == 0) { 124560857Sdarrenr f->ftps_rptr = rptr; 124660857Sdarrenr if (rv) 124760857Sdarrenr inc += ippr_ftp_server(fin, ip, nat, 124860857Sdarrenr ftp, len); 124960857Sdarrenr else 125060857Sdarrenr inc += ippr_ftp_client(fin, ip, nat, 125160857Sdarrenr ftp, len); 125260857Sdarrenr rptr = f->ftps_rptr; 125392685Sdarrenr wptr = f->ftps_wptr; 125460857Sdarrenr } 125560857Sdarrenr } 125660857Sdarrenr 125792685Sdarrenr /* 125892685Sdarrenr * Off to a bad start so lets just forget about using the 125992685Sdarrenr * ftp proxy for this connection. 126092685Sdarrenr */ 126195563Sdarrenr if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) { 1262102520Sdarrenr /* f->ftps_seq[1] += inc; */ 1263145522Sdarrenr 1264145522Sdarrenr if (ippr_ftp_debug > 1) 1265145522Sdarrenr printf("%s:cmds == 0 junk == 1\n", 1266145522Sdarrenr "ippr_ftp_process"); 126792685Sdarrenr return APR_ERR(2); 126895563Sdarrenr } 126992685Sdarrenr 1270145522Sdarrenr if ((f->ftps_junk != 0) && (rptr < wptr)) { 1271145522Sdarrenr for (s = rptr; s < wptr; s++) { 1272145522Sdarrenr if ((*s == '\r') && (s + 1 < wptr) && 1273145522Sdarrenr (*(s + 1) == '\n')) { 1274145522Sdarrenr rptr = s + 2; 1275145522Sdarrenr f->ftps_junk = 0; 127667614Sdarrenr break; 1277145522Sdarrenr } 127860857Sdarrenr } 127960857Sdarrenr } 128060857Sdarrenr 128160857Sdarrenr if (rptr == wptr) { 128260857Sdarrenr rptr = wptr = f->ftps_buf; 128360857Sdarrenr } else { 1284145522Sdarrenr /* 1285145522Sdarrenr * Compact the buffer back to the start. The junk 1286145522Sdarrenr * flag should already be set and because we're not 1287145522Sdarrenr * throwing away any data, it is preserved from its 1288145522Sdarrenr * current state. 1289145522Sdarrenr */ 1290145522Sdarrenr if (rptr > f->ftps_buf) { 1291145522Sdarrenr bcopy(rptr, f->ftps_buf, len); 1292145522Sdarrenr wptr -= rptr - f->ftps_buf; 1293145522Sdarrenr rptr = f->ftps_buf; 129460857Sdarrenr } 129560857Sdarrenr } 1296145522Sdarrenr f->ftps_rptr = rptr; 1297145522Sdarrenr f->ftps_wptr = wptr; 129860857Sdarrenr } 129960857Sdarrenr 1300102520Sdarrenr /* f->ftps_seq[1] += inc; */ 1301102520Sdarrenr if (tcp->th_flags & TH_FIN) 1302102520Sdarrenr f->ftps_seq[1]++; 1303145522Sdarrenr if (ippr_ftp_debug > 3) { 1304145522Sdarrenr#ifdef __sgi 1305145522Sdarrenr mlen = fin->fin_plen; 1306145522Sdarrenr#else 1307145522Sdarrenr mlen = MSGDSIZE(m); 1308102520Sdarrenr#endif 1309145522Sdarrenr mlen -= off; 1310145522Sdarrenr printf("ftps_seq[1] = %x inc %d len %d\n", 1311145522Sdarrenr f->ftps_seq[1], inc, mlen); 1312145522Sdarrenr } 1313102520Sdarrenr 131460857Sdarrenr f->ftps_rptr = rptr; 131560857Sdarrenr f->ftps_wptr = wptr; 131664580Sdarrenr return APR_INC(inc); 131760857Sdarrenr} 131860857Sdarrenr 131960857Sdarrenr 1320145522Sdarrenrint ippr_ftp_out(fin, aps, nat) 132160857Sdarrenrfr_info_t *fin; 132260857Sdarrenrap_session_t *aps; 132360857Sdarrenrnat_t *nat; 132460857Sdarrenr{ 132560857Sdarrenr ftpinfo_t *ftp; 1326145522Sdarrenr int rev; 132760857Sdarrenr 132860857Sdarrenr ftp = aps->aps_data; 132960857Sdarrenr if (ftp == NULL) 133060857Sdarrenr return 0; 1331145522Sdarrenr 1332145522Sdarrenr rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1; 1333145522Sdarrenr if (ftp->ftp_side[1 - rev].ftps_ifp == NULL) 1334145522Sdarrenr ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp; 1335145522Sdarrenr 1336145522Sdarrenr return ippr_ftp_process(fin, nat, ftp, rev); 133760857Sdarrenr} 133860857Sdarrenr 133960857Sdarrenr 1340145522Sdarrenrint ippr_ftp_in(fin, aps, nat) 134153642Sguidofr_info_t *fin; 134253642Sguidoap_session_t *aps; 134353642Sguidonat_t *nat; 134453642Sguido{ 134560857Sdarrenr ftpinfo_t *ftp; 1346145522Sdarrenr int rev; 134753642Sguido 134860857Sdarrenr ftp = aps->aps_data; 134960857Sdarrenr if (ftp == NULL) 135060857Sdarrenr return 0; 1351145522Sdarrenr 1352145522Sdarrenr rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1; 1353145522Sdarrenr if (ftp->ftp_side[rev].ftps_ifp == NULL) 1354145522Sdarrenr ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp; 1355145522Sdarrenr 1356145522Sdarrenr return ippr_ftp_process(fin, nat, ftp, 1 - rev); 135753642Sguido} 135860857Sdarrenr 135960857Sdarrenr 136060857Sdarrenr/* 136160857Sdarrenr * ippr_ftp_atoi - implement a version of atoi which processes numbers in 136260857Sdarrenr * pairs separated by commas (which are expected to be in the range 0 - 255), 136360857Sdarrenr * returning a 16 bit number combining either side of the , as the MSB and 136460857Sdarrenr * LSB. 136560857Sdarrenr */ 136660857Sdarrenru_short ippr_ftp_atoi(ptr) 136760857Sdarrenrchar **ptr; 136860857Sdarrenr{ 136960857Sdarrenr register char *s = *ptr, c; 137060857Sdarrenr register u_char i = 0, j = 0; 137160857Sdarrenr 1372145522Sdarrenr while (((c = *s++) != '\0') && ISDIGIT(c)) { 137360857Sdarrenr i *= 10; 137460857Sdarrenr i += c - '0'; 137560857Sdarrenr } 137660857Sdarrenr if (c != ',') { 137760857Sdarrenr *ptr = NULL; 137860857Sdarrenr return 0; 137960857Sdarrenr } 1380145522Sdarrenr while (((c = *s++) != '\0') && ISDIGIT(c)) { 138160857Sdarrenr j *= 10; 138260857Sdarrenr j += c - '0'; 138360857Sdarrenr } 138460857Sdarrenr *ptr = s; 138567614Sdarrenr i &= 0xff; 138667614Sdarrenr j &= 0xff; 138760857Sdarrenr return (i << 8) | j; 138860857Sdarrenr} 1389145522Sdarrenr 1390145522Sdarrenr 1391145522Sdarrenrint ippr_ftp_epsv(fin, ip, nat, f, dlen) 1392145522Sdarrenrfr_info_t *fin; 1393145522Sdarrenrip_t *ip; 1394145522Sdarrenrnat_t *nat; 1395145522Sdarrenrftpside_t *f; 1396145522Sdarrenrint dlen; 1397145522Sdarrenr{ 1398145522Sdarrenr char newbuf[IPF_FTPBUFSZ]; 1399145522Sdarrenr char *s; 1400145522Sdarrenr u_short ap = 0; 1401145522Sdarrenr 1402145522Sdarrenr#define EPSV_REPLEN 33 1403145522Sdarrenr /* 1404145522Sdarrenr * Check for EPSV reply message. 1405145522Sdarrenr */ 1406145522Sdarrenr if (dlen < IPF_MIN229LEN) 1407145522Sdarrenr return (0); 1408145522Sdarrenr else if (strncmp(f->ftps_rptr, 1409145522Sdarrenr "229 Entering Extended Passive Mode", EPSV_REPLEN)) 1410145522Sdarrenr return (0); 1411145522Sdarrenr 1412145522Sdarrenr /* 1413145522Sdarrenr * Skip the EPSV command + space 1414145522Sdarrenr */ 1415145522Sdarrenr s = f->ftps_rptr + 33; 1416145522Sdarrenr while (*s && !ISDIGIT(*s)) 1417145522Sdarrenr s++; 1418145522Sdarrenr 1419145522Sdarrenr /* 1420145522Sdarrenr * As per RFC 2428, there are no addres components in the EPSV 1421145522Sdarrenr * response. So we'll go straight to getting the port. 1422145522Sdarrenr */ 1423145522Sdarrenr while (*s && ISDIGIT(*s)) { 1424145522Sdarrenr ap *= 10; 1425145522Sdarrenr ap += *s++ - '0'; 1426145522Sdarrenr } 1427145522Sdarrenr 1428145522Sdarrenr if (!s) 1429145522Sdarrenr return 0; 1430145522Sdarrenr 1431145522Sdarrenr if (*s == '|') 1432145522Sdarrenr s++; 1433145522Sdarrenr if (*s == ')') 1434145522Sdarrenr s++; 1435145522Sdarrenr if (*s == '\n') 1436145522Sdarrenr s--; 1437145522Sdarrenr /* 1438145522Sdarrenr * check for CR-LF at the end. 1439145522Sdarrenr */ 1440145522Sdarrenr if ((*s == '\r') && (*(s + 1) == '\n')) { 1441145522Sdarrenr s += 2; 1442145522Sdarrenr } else 1443145522Sdarrenr return 0; 1444145522Sdarrenr 1445145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL) 1446145522Sdarrenr SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n", 1447145522Sdarrenr "229 Entering Extended Passive Mode", ap); 1448145522Sdarrenr#else 1449145522Sdarrenr (void) sprintf(newbuf, "%s (|||%u|)\r\n", 1450145522Sdarrenr "229 Entering Extended Passive Mode", ap); 1451145522Sdarrenr#endif 1452145522Sdarrenr 1453145522Sdarrenr return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s, 1454145522Sdarrenr ip->ip_src.s_addr); 1455145522Sdarrenr} 1456