1145522Sdarrenr/* $FreeBSD$ */ 2145522Sdarrenr 357126Sguido/* 457126Sguido * $FreeBSD$ 5145522Sdarrenr * Copyright (C) 1998-2003 by Darren Reed 6145522Sdarrenr * 7145522Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 8145522Sdarrenr * 9170268Sdarrenr * $Id: ip_raudio_pxy.c,v 1.40.2.4 2006/07/14 06:12:17 darrenr Exp $ 1057126Sguido */ 1153642Sguido 1253642Sguido#define IPF_RAUDIO_PROXY 1353642Sguido 1453642Sguido 1553642Sguidoint ippr_raudio_init __P((void)); 16145522Sdarrenrvoid ippr_raudio_fini __P((void)); 17145522Sdarrenrint ippr_raudio_new __P((fr_info_t *, ap_session_t *, nat_t *)); 18145522Sdarrenrint ippr_raudio_in __P((fr_info_t *, ap_session_t *, nat_t *)); 19145522Sdarrenrint ippr_raudio_out __P((fr_info_t *, ap_session_t *, nat_t *)); 2053642Sguido 2153642Sguidostatic frentry_t raudiofr; 2253642Sguido 23145522Sdarrenrint raudio_proxy_init = 0; 2453642Sguido 25145522Sdarrenr 2653642Sguido/* 2753642Sguido * Real Audio application proxy initialization. 2853642Sguido */ 2953642Sguidoint ippr_raudio_init() 3053642Sguido{ 3153642Sguido bzero((char *)&raudiofr, sizeof(raudiofr)); 3253642Sguido raudiofr.fr_ref = 1; 3353642Sguido raudiofr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 34145522Sdarrenr MUTEX_INIT(&raudiofr.fr_lock, "Real Audio proxy rule lock"); 35145522Sdarrenr raudio_proxy_init = 1; 36145522Sdarrenr 3753642Sguido return 0; 3853642Sguido} 3953642Sguido 4053642Sguido 41145522Sdarrenrvoid ippr_raudio_fini() 42145522Sdarrenr{ 43145522Sdarrenr if (raudio_proxy_init == 1) { 44145522Sdarrenr MUTEX_DESTROY(&raudiofr.fr_lock); 45145522Sdarrenr raudio_proxy_init = 0; 46145522Sdarrenr } 47145522Sdarrenr} 48145522Sdarrenr 49145522Sdarrenr 5053642Sguido/* 5153642Sguido * Setup for a new proxy to handle Real Audio. 5253642Sguido */ 53145522Sdarrenrint ippr_raudio_new(fin, aps, nat) 5453642Sguidofr_info_t *fin; 5553642Sguidoap_session_t *aps; 5653642Sguidonat_t *nat; 5753642Sguido{ 5853642Sguido raudio_t *rap; 5953642Sguido 6053642Sguido KMALLOCS(aps->aps_data, void *, sizeof(raudio_t)); 6160857Sdarrenr if (aps->aps_data == NULL) 6260857Sdarrenr return -1; 6360857Sdarrenr 64145522Sdarrenr fin = fin; /* LINT */ 65145522Sdarrenr nat = nat; /* LINT */ 66145522Sdarrenr 6760857Sdarrenr bzero(aps->aps_data, sizeof(raudio_t)); 6860857Sdarrenr rap = aps->aps_data; 6960857Sdarrenr aps->aps_psiz = sizeof(raudio_t); 7060857Sdarrenr rap->rap_mode = RAP_M_TCP; /* default is for TCP */ 7153642Sguido return 0; 7253642Sguido} 7353642Sguido 7453642Sguido 7553642Sguido 76145522Sdarrenrint ippr_raudio_out(fin, aps, nat) 7753642Sguidofr_info_t *fin; 7853642Sguidoap_session_t *aps; 7953642Sguidonat_t *nat; 8053642Sguido{ 8153642Sguido raudio_t *rap = aps->aps_data; 8255929Sguido unsigned char membuf[512 + 1], *s; 8355929Sguido u_short id = 0; 84145522Sdarrenr tcphdr_t *tcp; 8580482Sdarrenr int off, dlen; 8653642Sguido int len = 0; 8753642Sguido mb_t *m; 8853642Sguido 89145522Sdarrenr nat = nat; /* LINT */ 90145522Sdarrenr 9153642Sguido /* 9253642Sguido * If we've already processed the start messages, then nothing left 9353642Sguido * for the proxy to do. 9453642Sguido */ 9553642Sguido if (rap->rap_eos == 1) 9653642Sguido return 0; 9753642Sguido 98145522Sdarrenr m = fin->fin_m; 9953642Sguido tcp = (tcphdr_t *)fin->fin_dp; 100145522Sdarrenr off = (char *)tcp - (char *)fin->fin_ip; 101145522Sdarrenr off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 10253642Sguido 103145522Sdarrenr#ifdef __sgi 104145522Sdarrenr dlen = fin->fin_plen - off; 105145522Sdarrenr#else 106145522Sdarrenr dlen = MSGDSIZE(m) - off; 107145522Sdarrenr#endif 10853642Sguido if (dlen <= 0) 10953642Sguido return 0; 11053642Sguido 111145522Sdarrenr if (dlen > sizeof(membuf)) 112145522Sdarrenr dlen = sizeof(membuf); 113145522Sdarrenr 114145522Sdarrenr bzero((char *)membuf, sizeof(membuf)); 115145522Sdarrenr COPYDATA(m, off, dlen, (char *)membuf); 11653642Sguido /* 11753642Sguido * In all the startup parsing, ensure that we don't go outside 11853642Sguido * the packet buffer boundary. 11953642Sguido */ 12053642Sguido /* 12153642Sguido * Look for the start of connection "PNA" string if not seen yet. 12253642Sguido */ 12353642Sguido if (rap->rap_seenpna == 0) { 12455929Sguido s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); 12553642Sguido if (s == NULL) 12653642Sguido return 0; 12753642Sguido s += 3; 12853642Sguido rap->rap_seenpna = 1; 12953642Sguido } else 13053642Sguido s = membuf; 13153642Sguido 13253642Sguido /* 13353642Sguido * Directly after the PNA will be the version number of this 13453642Sguido * connection. 13553642Sguido */ 13653642Sguido if (rap->rap_seenpna == 1 && rap->rap_seenver == 0) { 13753642Sguido if ((s + 1) - membuf < dlen) { 13853642Sguido rap->rap_version = (*s << 8) | *(s + 1); 13953642Sguido s += 2; 14053642Sguido rap->rap_seenver = 1; 14153642Sguido } else 14253642Sguido return 0; 14353642Sguido } 14453642Sguido 14553642Sguido /* 14653642Sguido * Now that we've been past the PNA and version number, we're into the 14753642Sguido * startup messages block. This ends when a message with an ID of 0. 14853642Sguido */ 14953642Sguido while ((rap->rap_eos == 0) && ((s + 1) - membuf < dlen)) { 15053642Sguido if (rap->rap_gotid == 0) { 15153642Sguido id = (*s << 8) | *(s + 1); 15253642Sguido s += 2; 15353642Sguido rap->rap_gotid = 1; 15453642Sguido if (id == RA_ID_END) { 15553642Sguido rap->rap_eos = 1; 15653642Sguido break; 15753642Sguido } 15853642Sguido } else if (rap->rap_gotlen == 0) { 15953642Sguido len = (*s << 8) | *(s + 1); 16053642Sguido s += 2; 16153642Sguido rap->rap_gotlen = 1; 16253642Sguido } 16353642Sguido 16453642Sguido if (rap->rap_gotid == 1 && rap->rap_gotlen == 1) { 16553642Sguido if (id == RA_ID_UDP) { 16653642Sguido rap->rap_mode &= ~RAP_M_TCP; 16753642Sguido rap->rap_mode |= RAP_M_UDP; 16853642Sguido rap->rap_plport = (*s << 8) | *(s + 1); 16953642Sguido } else if (id == RA_ID_ROBUST) { 17053642Sguido rap->rap_mode |= RAP_M_ROBUST; 17153642Sguido rap->rap_prport = (*s << 8) | *(s + 1); 17253642Sguido } 17353642Sguido s += len; 17453642Sguido rap->rap_gotlen = 0; 17553642Sguido rap->rap_gotid = 0; 17653642Sguido } 17753642Sguido } 17855929Sguido return 0; 17953642Sguido} 18053642Sguido 18153642Sguido 182145522Sdarrenrint ippr_raudio_in(fin, aps, nat) 18353642Sguidofr_info_t *fin; 18453642Sguidoap_session_t *aps; 18553642Sguidonat_t *nat; 18653642Sguido{ 18755929Sguido unsigned char membuf[IPF_MAXPORTLEN + 1], *s; 18855929Sguido tcphdr_t *tcp, tcph, *tcp2 = &tcph; 18953642Sguido raudio_t *rap = aps->aps_data; 19055929Sguido struct in_addr swa, swb; 191145522Sdarrenr int off, dlen, slen; 19267614Sdarrenr int a1, a2, a3, a4; 19355929Sguido u_short sp, dp; 19455929Sguido fr_info_t fi; 19553642Sguido tcp_seq seq; 196145522Sdarrenr nat_t *nat2; 19755929Sguido u_char swp; 198145522Sdarrenr ip_t *ip; 19953642Sguido mb_t *m; 20053642Sguido 20155929Sguido /* 20255929Sguido * Wait until we've seen the end of the start messages and even then 20355929Sguido * only proceed further if we're using UDP. If they want to use TCP 20455929Sguido * then data is sent back on the same channel that is already open. 20555929Sguido */ 20655929Sguido if (rap->rap_sdone != 0) 20753642Sguido return 0; 20853642Sguido 209145522Sdarrenr m = fin->fin_m; 21053642Sguido tcp = (tcphdr_t *)fin->fin_dp; 211145522Sdarrenr off = (char *)tcp - (char *)fin->fin_ip; 212145522Sdarrenr off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 21353642Sguido 214145522Sdarrenr#ifdef __sgi 215145522Sdarrenr dlen = fin->fin_plen - off; 21653642Sguido#else 217145522Sdarrenr dlen = MSGDSIZE(m) - off; 218145522Sdarrenr#endif 21953642Sguido if (dlen <= 0) 22053642Sguido return 0; 22153642Sguido 222145522Sdarrenr if (dlen > sizeof(membuf)) 223145522Sdarrenr dlen = sizeof(membuf); 224145522Sdarrenr 225145522Sdarrenr bzero((char *)membuf, sizeof(membuf)); 226145522Sdarrenr COPYDATA(m, off, dlen, (char *)membuf); 227145522Sdarrenr 22853642Sguido seq = ntohl(tcp->th_seq); 22953642Sguido /* 23053642Sguido * Check to see if the data in this packet is of interest to us. 23153642Sguido * We only care for the first 19 bytes coming back from the server. 23253642Sguido */ 23353642Sguido if (rap->rap_sseq == 0) { 234145522Sdarrenr s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); 23553642Sguido if (s == NULL) 23653642Sguido return 0; 23753642Sguido a1 = s - membuf; 23853642Sguido dlen -= a1; 23953642Sguido a1 = 0; 24053642Sguido rap->rap_sseq = seq; 24153642Sguido a2 = MIN(dlen, sizeof(rap->rap_svr)); 24253642Sguido } else if (seq <= rap->rap_sseq + sizeof(rap->rap_svr)) { 24353642Sguido /* 24453642Sguido * seq # which is the start of data and from that the offset 24553642Sguido * into the buffer array. 24653642Sguido */ 24753642Sguido a1 = seq - rap->rap_sseq; 24853642Sguido a2 = MIN(dlen, sizeof(rap->rap_svr)); 24953642Sguido a2 -= a1; 25053642Sguido s = membuf; 25153642Sguido } else 25253642Sguido return 0; 25353642Sguido 25455929Sguido for (a3 = a1, a4 = a2; (a4 > 0) && (a3 < 19) && (a3 >= 0); a4--,a3++) { 25553642Sguido rap->rap_sbf |= (1 << a3); 25653642Sguido rap->rap_svr[a3] = *s++; 25753642Sguido } 25855929Sguido 25955929Sguido if ((rap->rap_sbf != 0x7ffff) || (!rap->rap_eos)) /* 19 bits */ 26055929Sguido return 0; 26155929Sguido rap->rap_sdone = 1; 26255929Sguido 26355929Sguido s = (u_char *)rap->rap_svr + 11; 26455929Sguido if (((*s << 8) | *(s + 1)) == RA_ID_ROBUST) { 26555929Sguido s += 2; 26653642Sguido rap->rap_srport = (*s << 8) | *(s + 1); 26753642Sguido } 26855929Sguido 269145522Sdarrenr ip = fin->fin_ip; 27055929Sguido swp = ip->ip_p; 27155929Sguido swa = ip->ip_src; 27255929Sguido swb = ip->ip_dst; 27355929Sguido 27455929Sguido ip->ip_p = IPPROTO_UDP; 27555929Sguido ip->ip_src = nat->nat_inip; 27655929Sguido ip->ip_dst = nat->nat_oip; 27755929Sguido 27855929Sguido bcopy((char *)fin, (char *)&fi, sizeof(fi)); 27955929Sguido bzero((char *)tcp2, sizeof(*tcp2)); 280145522Sdarrenr TCP_OFF_A(tcp2, 5); 281145522Sdarrenr fi.fin_state = NULL; 282145522Sdarrenr fi.fin_nat = NULL; 283145522Sdarrenr fi.fin_flx |= FI_IGNORE; 28455929Sguido fi.fin_dp = (char *)tcp2; 28555929Sguido fi.fin_fr = &raudiofr; 28667853Sdarrenr fi.fin_dlen = sizeof(*tcp2); 287145522Sdarrenr fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 28855929Sguido tcp2->th_win = htons(8192); 28960857Sdarrenr slen = ip->ip_len; 29060857Sdarrenr ip->ip_len = fin->fin_hlen + sizeof(*tcp); 29155929Sguido 29255929Sguido if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && 29355929Sguido (rap->rap_srport != 0)) { 29455929Sguido dp = rap->rap_srport; 29555929Sguido sp = rap->rap_prport; 29655929Sguido tcp2->th_sport = htons(sp); 29755929Sguido tcp2->th_dport = htons(dp); 29855929Sguido fi.fin_data[0] = dp; 29955929Sguido fi.fin_data[1] = sp; 30080482Sdarrenr fi.fin_out = 0; 301145522Sdarrenr nat2 = nat_new(&fi, nat->nat_ptr, NULL, 302145522Sdarrenr NAT_SLAVE|IPN_UDP | (sp ? 0 : SI_W_SPORT), 303145522Sdarrenr NAT_OUTBOUND); 304145522Sdarrenr if (nat2 != NULL) { 305145522Sdarrenr (void) nat_proto(&fi, nat2, IPN_UDP); 306145522Sdarrenr nat_update(&fi, nat2, nat2->nat_ptr); 307145522Sdarrenr 308145522Sdarrenr (void) fr_addstate(&fi, NULL, (sp ? 0 : SI_W_SPORT)); 309145522Sdarrenr if (fi.fin_state != NULL) 310170268Sdarrenr fr_statederef((ipstate_t **)&fi.fin_state); 31155929Sguido } 31255929Sguido } 31355929Sguido 31455929Sguido if ((rap->rap_mode & RAP_M_UDP) == RAP_M_UDP) { 31555929Sguido sp = rap->rap_plport; 31655929Sguido tcp2->th_sport = htons(sp); 31755929Sguido tcp2->th_dport = 0; /* XXX - don't specify remote port */ 31855929Sguido fi.fin_data[0] = sp; 31955929Sguido fi.fin_data[1] = 0; 32080482Sdarrenr fi.fin_out = 1; 321145522Sdarrenr nat2 = nat_new(&fi, nat->nat_ptr, NULL, 322145522Sdarrenr NAT_SLAVE|IPN_UDP|SI_W_DPORT, 323145522Sdarrenr NAT_OUTBOUND); 324145522Sdarrenr if (nat2 != NULL) { 325145522Sdarrenr (void) nat_proto(&fi, nat2, IPN_UDP); 326145522Sdarrenr nat_update(&fi, nat2, nat2->nat_ptr); 327145522Sdarrenr 328145522Sdarrenr (void) fr_addstate(&fi, NULL, SI_W_DPORT); 329145522Sdarrenr if (fi.fin_state != NULL) 330170268Sdarrenr fr_statederef((ipstate_t **)&fi.fin_state); 33155929Sguido } 33255929Sguido } 33360857Sdarrenr 33455929Sguido ip->ip_p = swp; 33560857Sdarrenr ip->ip_len = slen; 33655929Sguido ip->ip_src = swa; 33755929Sguido ip->ip_dst = swb; 33853642Sguido return 0; 33953642Sguido} 340