1145522Sdarrenr/* $FreeBSD$ */ 2145522Sdarrenr 357126Sguido/* 4255332Scy * Copyright (C) 2012 by Darren Reed. 5145522Sdarrenr * 6145522Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 7145522Sdarrenr * 8170268Sdarrenr * $Id: ip_raudio_pxy.c,v 1.40.2.4 2006/07/14 06:12:17 darrenr Exp $ 957126Sguido */ 1053642Sguido 1153642Sguido#define IPF_RAUDIO_PROXY 1253642Sguido 1353642Sguido 14255332Scyvoid ipf_p_raudio_main_load __P((void)); 15255332Scyvoid ipf_p_raudio_main_unload __P((void)); 16255332Scyint ipf_p_raudio_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); 17255332Scyint ipf_p_raudio_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); 18255332Scyint ipf_p_raudio_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); 1953642Sguido 2053642Sguidostatic frentry_t raudiofr; 2153642Sguido 22145522Sdarrenrint raudio_proxy_init = 0; 2353642Sguido 24145522Sdarrenr 2553642Sguido/* 2653642Sguido * Real Audio application proxy initialization. 2753642Sguido */ 28255332Scyvoid 29255332Scyipf_p_raudio_main_load() 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; 3653642Sguido} 3753642Sguido 3853642Sguido 39255332Scyvoid 40255332Scyipf_p_raudio_main_unload() 41145522Sdarrenr{ 42145522Sdarrenr if (raudio_proxy_init == 1) { 43145522Sdarrenr MUTEX_DESTROY(&raudiofr.fr_lock); 44145522Sdarrenr raudio_proxy_init = 0; 45145522Sdarrenr } 46145522Sdarrenr} 47145522Sdarrenr 48145522Sdarrenr 4953642Sguido/* 5053642Sguido * Setup for a new proxy to handle Real Audio. 5153642Sguido */ 52255332Scyint 53255332Scyipf_p_raudio_new(arg, fin, aps, nat) 54255332Scy void *arg; 55255332Scy fr_info_t *fin; 56255332Scy ap_session_t *aps; 57255332Scy nat_t *nat; 5853642Sguido{ 5953642Sguido raudio_t *rap; 6053642Sguido 61255332Scy nat = nat; /* LINT */ 62255332Scy 63255332Scy if (fin->fin_v != 4) 64255332Scy return -1; 65255332Scy 6653642Sguido KMALLOCS(aps->aps_data, void *, sizeof(raudio_t)); 6760857Sdarrenr if (aps->aps_data == NULL) 6860857Sdarrenr return -1; 6960857Sdarrenr 7060857Sdarrenr bzero(aps->aps_data, sizeof(raudio_t)); 7160857Sdarrenr rap = aps->aps_data; 7260857Sdarrenr aps->aps_psiz = sizeof(raudio_t); 7360857Sdarrenr rap->rap_mode = RAP_M_TCP; /* default is for TCP */ 7453642Sguido return 0; 7553642Sguido} 7653642Sguido 7753642Sguido 7853642Sguido 79255332Scyint 80255332Scyipf_p_raudio_out(arg, fin, aps, nat) 81255332Scy void *arg; 82255332Scy fr_info_t *fin; 83255332Scy ap_session_t *aps; 84255332Scy nat_t *nat; 8553642Sguido{ 8653642Sguido raudio_t *rap = aps->aps_data; 8755929Sguido unsigned char membuf[512 + 1], *s; 8855929Sguido u_short id = 0; 89145522Sdarrenr tcphdr_t *tcp; 9080482Sdarrenr int off, dlen; 9153642Sguido int len = 0; 9253642Sguido mb_t *m; 9353642Sguido 94145522Sdarrenr nat = nat; /* LINT */ 95145522Sdarrenr 9653642Sguido /* 9753642Sguido * If we've already processed the start messages, then nothing left 9853642Sguido * for the proxy to do. 9953642Sguido */ 10053642Sguido if (rap->rap_eos == 1) 10153642Sguido return 0; 10253642Sguido 103145522Sdarrenr m = fin->fin_m; 10453642Sguido tcp = (tcphdr_t *)fin->fin_dp; 105145522Sdarrenr off = (char *)tcp - (char *)fin->fin_ip; 106145522Sdarrenr off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 10753642Sguido 108145522Sdarrenr#ifdef __sgi 109145522Sdarrenr dlen = fin->fin_plen - off; 110145522Sdarrenr#else 111145522Sdarrenr dlen = MSGDSIZE(m) - off; 112145522Sdarrenr#endif 11353642Sguido if (dlen <= 0) 11453642Sguido return 0; 11553642Sguido 116145522Sdarrenr if (dlen > sizeof(membuf)) 117145522Sdarrenr dlen = sizeof(membuf); 118145522Sdarrenr 119145522Sdarrenr bzero((char *)membuf, sizeof(membuf)); 120145522Sdarrenr COPYDATA(m, off, dlen, (char *)membuf); 12153642Sguido /* 12253642Sguido * In all the startup parsing, ensure that we don't go outside 12353642Sguido * the packet buffer boundary. 12453642Sguido */ 12553642Sguido /* 12653642Sguido * Look for the start of connection "PNA" string if not seen yet. 12753642Sguido */ 12853642Sguido if (rap->rap_seenpna == 0) { 12955929Sguido s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); 13053642Sguido if (s == NULL) 13153642Sguido return 0; 13253642Sguido s += 3; 13353642Sguido rap->rap_seenpna = 1; 13453642Sguido } else 13553642Sguido s = membuf; 13653642Sguido 13753642Sguido /* 13853642Sguido * Directly after the PNA will be the version number of this 13953642Sguido * connection. 14053642Sguido */ 14153642Sguido if (rap->rap_seenpna == 1 && rap->rap_seenver == 0) { 14253642Sguido if ((s + 1) - membuf < dlen) { 14353642Sguido rap->rap_version = (*s << 8) | *(s + 1); 14453642Sguido s += 2; 14553642Sguido rap->rap_seenver = 1; 14653642Sguido } else 14753642Sguido return 0; 14853642Sguido } 14953642Sguido 15053642Sguido /* 15153642Sguido * Now that we've been past the PNA and version number, we're into the 15253642Sguido * startup messages block. This ends when a message with an ID of 0. 15353642Sguido */ 15453642Sguido while ((rap->rap_eos == 0) && ((s + 1) - membuf < dlen)) { 15553642Sguido if (rap->rap_gotid == 0) { 15653642Sguido id = (*s << 8) | *(s + 1); 15753642Sguido s += 2; 15853642Sguido rap->rap_gotid = 1; 15953642Sguido if (id == RA_ID_END) { 16053642Sguido rap->rap_eos = 1; 16153642Sguido break; 16253642Sguido } 16353642Sguido } else if (rap->rap_gotlen == 0) { 16453642Sguido len = (*s << 8) | *(s + 1); 16553642Sguido s += 2; 16653642Sguido rap->rap_gotlen = 1; 16753642Sguido } 16853642Sguido 16953642Sguido if (rap->rap_gotid == 1 && rap->rap_gotlen == 1) { 17053642Sguido if (id == RA_ID_UDP) { 17153642Sguido rap->rap_mode &= ~RAP_M_TCP; 17253642Sguido rap->rap_mode |= RAP_M_UDP; 17353642Sguido rap->rap_plport = (*s << 8) | *(s + 1); 17453642Sguido } else if (id == RA_ID_ROBUST) { 17553642Sguido rap->rap_mode |= RAP_M_ROBUST; 17653642Sguido rap->rap_prport = (*s << 8) | *(s + 1); 17753642Sguido } 17853642Sguido s += len; 17953642Sguido rap->rap_gotlen = 0; 18053642Sguido rap->rap_gotid = 0; 18153642Sguido } 18253642Sguido } 18355929Sguido return 0; 18453642Sguido} 18553642Sguido 18653642Sguido 187255332Scyint 188255332Scyipf_p_raudio_in(arg, fin, aps, nat) 189255332Scy void *arg; 190255332Scy fr_info_t *fin; 191255332Scy ap_session_t *aps; 192255332Scy nat_t *nat; 19353642Sguido{ 19455929Sguido unsigned char membuf[IPF_MAXPORTLEN + 1], *s; 19555929Sguido tcphdr_t *tcp, tcph, *tcp2 = &tcph; 19653642Sguido raudio_t *rap = aps->aps_data; 197255332Scy ipf_main_softc_t *softc; 198255332Scy ipf_nat_softc_t *softn; 19955929Sguido struct in_addr swa, swb; 200145522Sdarrenr int off, dlen, slen; 20167614Sdarrenr int a1, a2, a3, a4; 20255929Sguido u_short sp, dp; 20355929Sguido fr_info_t fi; 20453642Sguido tcp_seq seq; 205145522Sdarrenr nat_t *nat2; 20655929Sguido u_char swp; 207145522Sdarrenr ip_t *ip; 20853642Sguido mb_t *m; 20953642Sguido 210255332Scy softc = fin->fin_main_soft; 211255332Scy softn = softc->ipf_nat_soft; 21255929Sguido /* 21355929Sguido * Wait until we've seen the end of the start messages and even then 21455929Sguido * only proceed further if we're using UDP. If they want to use TCP 21555929Sguido * then data is sent back on the same channel that is already open. 21655929Sguido */ 21755929Sguido if (rap->rap_sdone != 0) 21853642Sguido return 0; 21953642Sguido 220145522Sdarrenr m = fin->fin_m; 22153642Sguido tcp = (tcphdr_t *)fin->fin_dp; 222145522Sdarrenr off = (char *)tcp - (char *)fin->fin_ip; 223145522Sdarrenr off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 22453642Sguido 225145522Sdarrenr#ifdef __sgi 226145522Sdarrenr dlen = fin->fin_plen - off; 22753642Sguido#else 228145522Sdarrenr dlen = MSGDSIZE(m) - off; 229145522Sdarrenr#endif 23053642Sguido if (dlen <= 0) 23153642Sguido return 0; 23253642Sguido 233145522Sdarrenr if (dlen > sizeof(membuf)) 234145522Sdarrenr dlen = sizeof(membuf); 235145522Sdarrenr 236145522Sdarrenr bzero((char *)membuf, sizeof(membuf)); 237145522Sdarrenr COPYDATA(m, off, dlen, (char *)membuf); 238145522Sdarrenr 23953642Sguido seq = ntohl(tcp->th_seq); 24053642Sguido /* 24153642Sguido * Check to see if the data in this packet is of interest to us. 24253642Sguido * We only care for the first 19 bytes coming back from the server. 24353642Sguido */ 24453642Sguido if (rap->rap_sseq == 0) { 245145522Sdarrenr s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); 24653642Sguido if (s == NULL) 24753642Sguido return 0; 24853642Sguido a1 = s - membuf; 24953642Sguido dlen -= a1; 25053642Sguido a1 = 0; 25153642Sguido rap->rap_sseq = seq; 25253642Sguido a2 = MIN(dlen, sizeof(rap->rap_svr)); 25353642Sguido } else if (seq <= rap->rap_sseq + sizeof(rap->rap_svr)) { 25453642Sguido /* 25553642Sguido * seq # which is the start of data and from that the offset 25653642Sguido * into the buffer array. 25753642Sguido */ 25853642Sguido a1 = seq - rap->rap_sseq; 25953642Sguido a2 = MIN(dlen, sizeof(rap->rap_svr)); 26053642Sguido a2 -= a1; 26153642Sguido s = membuf; 26253642Sguido } else 26353642Sguido return 0; 26453642Sguido 26555929Sguido for (a3 = a1, a4 = a2; (a4 > 0) && (a3 < 19) && (a3 >= 0); a4--,a3++) { 26653642Sguido rap->rap_sbf |= (1 << a3); 26753642Sguido rap->rap_svr[a3] = *s++; 26853642Sguido } 26955929Sguido 27055929Sguido if ((rap->rap_sbf != 0x7ffff) || (!rap->rap_eos)) /* 19 bits */ 27155929Sguido return 0; 27255929Sguido rap->rap_sdone = 1; 27355929Sguido 27455929Sguido s = (u_char *)rap->rap_svr + 11; 27555929Sguido if (((*s << 8) | *(s + 1)) == RA_ID_ROBUST) { 27655929Sguido s += 2; 27753642Sguido rap->rap_srport = (*s << 8) | *(s + 1); 27853642Sguido } 27955929Sguido 280145522Sdarrenr ip = fin->fin_ip; 28155929Sguido swp = ip->ip_p; 28255929Sguido swa = ip->ip_src; 28355929Sguido swb = ip->ip_dst; 28455929Sguido 28555929Sguido ip->ip_p = IPPROTO_UDP; 286255332Scy ip->ip_src = nat->nat_ndstip; 287255332Scy ip->ip_dst = nat->nat_odstip; 28855929Sguido 28955929Sguido bcopy((char *)fin, (char *)&fi, sizeof(fi)); 29055929Sguido bzero((char *)tcp2, sizeof(*tcp2)); 291145522Sdarrenr TCP_OFF_A(tcp2, 5); 292145522Sdarrenr fi.fin_flx |= FI_IGNORE; 29355929Sguido fi.fin_dp = (char *)tcp2; 29455929Sguido fi.fin_fr = &raudiofr; 29567853Sdarrenr fi.fin_dlen = sizeof(*tcp2); 296145522Sdarrenr fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 29755929Sguido tcp2->th_win = htons(8192); 29860857Sdarrenr slen = ip->ip_len; 299255332Scy ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); 30055929Sguido 30155929Sguido if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && 30255929Sguido (rap->rap_srport != 0)) { 30355929Sguido dp = rap->rap_srport; 30455929Sguido sp = rap->rap_prport; 30555929Sguido tcp2->th_sport = htons(sp); 30655929Sguido tcp2->th_dport = htons(dp); 30755929Sguido fi.fin_data[0] = dp; 30855929Sguido fi.fin_data[1] = sp; 30980482Sdarrenr fi.fin_out = 0; 310255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 311255332Scy nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, 312145522Sdarrenr NAT_SLAVE|IPN_UDP | (sp ? 0 : SI_W_SPORT), 313145522Sdarrenr NAT_OUTBOUND); 314255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 315145522Sdarrenr if (nat2 != NULL) { 316255332Scy (void) ipf_nat_proto(&fi, nat2, IPN_UDP); 317255332Scy MUTEX_ENTER(&nat2->nat_lock); 318255332Scy ipf_nat_update(&fi, nat2); 319255332Scy MUTEX_EXIT(&nat2->nat_lock); 320145522Sdarrenr 321255332Scy (void) ipf_state_add(softc, &fi, NULL, 322255332Scy (sp ? 0 : SI_W_SPORT)); 32355929Sguido } 32455929Sguido } 32555929Sguido 32655929Sguido if ((rap->rap_mode & RAP_M_UDP) == RAP_M_UDP) { 32755929Sguido sp = rap->rap_plport; 32855929Sguido tcp2->th_sport = htons(sp); 32955929Sguido tcp2->th_dport = 0; /* XXX - don't specify remote port */ 33055929Sguido fi.fin_data[0] = sp; 33155929Sguido fi.fin_data[1] = 0; 33280482Sdarrenr fi.fin_out = 1; 333255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 334255332Scy nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, 335145522Sdarrenr NAT_SLAVE|IPN_UDP|SI_W_DPORT, 336145522Sdarrenr NAT_OUTBOUND); 337255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 338145522Sdarrenr if (nat2 != NULL) { 339255332Scy (void) ipf_nat_proto(&fi, nat2, IPN_UDP); 340255332Scy MUTEX_ENTER(&nat2->nat_lock); 341255332Scy ipf_nat_update(&fi, nat2); 342255332Scy MUTEX_EXIT(&nat2->nat_lock); 343145522Sdarrenr 344255332Scy (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); 34555929Sguido } 34655929Sguido } 34760857Sdarrenr 34855929Sguido ip->ip_p = swp; 34960857Sdarrenr ip->ip_len = slen; 35055929Sguido ip->ip_src = swa; 35155929Sguido ip->ip_dst = swb; 35253642Sguido return 0; 35353642Sguido} 354