1 | /* $NetBSD: ip_raudio_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (C) 2012 by Darren Reed. |
5 | * |
6 | * See the IPFILTER.LICENCE file for details on licencing. |
7 | * |
8 | * Id: ip_raudio_pxy.c,v 1.1.1.2 2012/07/22 13:45:33 darrenr Exp |
9 | */ |
10 | |
11 | #include <sys/cdefs.h> |
12 | __KERNEL_RCSID(1, "$NetBSD: ip_raudio_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $" ); |
13 | |
14 | #define IPF_RAUDIO_PROXY |
15 | |
16 | |
17 | void ipf_p_raudio_main_load(void); |
18 | void ipf_p_raudio_main_unload(void); |
19 | int ipf_p_raudio_new(void *, fr_info_t *, ap_session_t *, nat_t *); |
20 | int ipf_p_raudio_in(void *, fr_info_t *, ap_session_t *, nat_t *); |
21 | int ipf_p_raudio_out(void *, fr_info_t *, ap_session_t *, nat_t *); |
22 | |
23 | static frentry_t raudiofr; |
24 | |
25 | int raudio_proxy_init = 0; |
26 | |
27 | |
28 | /* |
29 | * Real Audio application proxy initialization. |
30 | */ |
31 | void |
32 | ipf_p_raudio_main_load(void) |
33 | { |
34 | bzero((char *)&raudiofr, sizeof(raudiofr)); |
35 | raudiofr.fr_ref = 1; |
36 | raudiofr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; |
37 | MUTEX_INIT(&raudiofr.fr_lock, "Real Audio proxy rule lock" ); |
38 | raudio_proxy_init = 1; |
39 | } |
40 | |
41 | |
42 | void |
43 | ipf_p_raudio_main_unload(void) |
44 | { |
45 | if (raudio_proxy_init == 1) { |
46 | MUTEX_DESTROY(&raudiofr.fr_lock); |
47 | raudio_proxy_init = 0; |
48 | } |
49 | } |
50 | |
51 | |
52 | /* |
53 | * Setup for a new proxy to handle Real Audio. |
54 | */ |
55 | int |
56 | ipf_p_raudio_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) |
57 | { |
58 | raudio_t *rap; |
59 | |
60 | nat = nat; /* LINT */ |
61 | |
62 | if (fin->fin_v != 4) |
63 | return -1; |
64 | |
65 | KMALLOCS(aps->aps_data, void *, sizeof(raudio_t)); |
66 | if (aps->aps_data == NULL) |
67 | return -1; |
68 | |
69 | bzero(aps->aps_data, sizeof(raudio_t)); |
70 | rap = aps->aps_data; |
71 | aps->aps_psiz = sizeof(raudio_t); |
72 | rap->rap_mode = RAP_M_TCP; /* default is for TCP */ |
73 | return 0; |
74 | } |
75 | |
76 | |
77 | |
78 | int |
79 | ipf_p_raudio_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) |
80 | { |
81 | raudio_t *rap = aps->aps_data; |
82 | unsigned char membuf[512 + 1], *s; |
83 | u_short id = 0; |
84 | tcphdr_t *tcp; |
85 | int off, dlen; |
86 | int len = 0; |
87 | mb_t *m; |
88 | |
89 | nat = nat; /* LINT */ |
90 | |
91 | /* |
92 | * If we've already processed the start messages, then nothing left |
93 | * for the proxy to do. |
94 | */ |
95 | if (rap->rap_eos == 1) |
96 | return 0; |
97 | |
98 | m = fin->fin_m; |
99 | tcp = (tcphdr_t *)fin->fin_dp; |
100 | off = (char *)tcp - (char *)fin->fin_ip; |
101 | off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; |
102 | |
103 | #ifdef __sgi |
104 | dlen = fin->fin_plen - off; |
105 | #else |
106 | dlen = MSGDSIZE(m) - off; |
107 | #endif |
108 | if (dlen <= 0) |
109 | return 0; |
110 | |
111 | if (dlen > sizeof(membuf)) |
112 | dlen = sizeof(membuf); |
113 | |
114 | bzero((char *)membuf, sizeof(membuf)); |
115 | COPYDATA(m, off, dlen, (char *)membuf); |
116 | /* |
117 | * In all the startup parsing, ensure that we don't go outside |
118 | * the packet buffer boundary. |
119 | */ |
120 | /* |
121 | * Look for the start of connection "PNA" string if not seen yet. |
122 | */ |
123 | if (rap->rap_seenpna == 0) { |
124 | s = (u_char *)memstr("PNA" , (char *)membuf, 3, dlen); |
125 | if (s == NULL) |
126 | return 0; |
127 | s += 3; |
128 | rap->rap_seenpna = 1; |
129 | } else |
130 | s = membuf; |
131 | |
132 | /* |
133 | * Directly after the PNA will be the version number of this |
134 | * connection. |
135 | */ |
136 | if (rap->rap_seenpna == 1 && rap->rap_seenver == 0) { |
137 | if ((s + 1) - membuf < dlen) { |
138 | rap->rap_version = (*s << 8) | *(s + 1); |
139 | s += 2; |
140 | rap->rap_seenver = 1; |
141 | } else |
142 | return 0; |
143 | } |
144 | |
145 | /* |
146 | * Now that we've been past the PNA and version number, we're into the |
147 | * startup messages block. This ends when a message with an ID of 0. |
148 | */ |
149 | while ((rap->rap_eos == 0) && ((s + 1) - membuf < dlen)) { |
150 | if (rap->rap_gotid == 0) { |
151 | id = (*s << 8) | *(s + 1); |
152 | s += 2; |
153 | rap->rap_gotid = 1; |
154 | if (id == RA_ID_END) { |
155 | rap->rap_eos = 1; |
156 | break; |
157 | } |
158 | } else if (rap->rap_gotlen == 0) { |
159 | len = (*s << 8) | *(s + 1); |
160 | s += 2; |
161 | rap->rap_gotlen = 1; |
162 | } |
163 | |
164 | if (rap->rap_gotid == 1 && rap->rap_gotlen == 1) { |
165 | if (id == RA_ID_UDP) { |
166 | rap->rap_mode &= ~RAP_M_TCP; |
167 | rap->rap_mode |= RAP_M_UDP; |
168 | rap->rap_plport = (*s << 8) | *(s + 1); |
169 | } else if (id == RA_ID_ROBUST) { |
170 | rap->rap_mode |= RAP_M_ROBUST; |
171 | rap->rap_prport = (*s << 8) | *(s + 1); |
172 | } |
173 | s += len; |
174 | rap->rap_gotlen = 0; |
175 | rap->rap_gotid = 0; |
176 | } |
177 | } |
178 | return 0; |
179 | } |
180 | |
181 | |
182 | int |
183 | ipf_p_raudio_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) |
184 | { |
185 | unsigned char membuf[IPF_MAXPORTLEN + 1], *s; |
186 | tcphdr_t *tcp, tcph, *tcp2 = &tcph; |
187 | raudio_t *rap = aps->aps_data; |
188 | ipf_main_softc_t *softc; |
189 | ipf_nat_softc_t *softn; |
190 | struct in_addr swa, swb; |
191 | int off, dlen, slen; |
192 | int a1, a2, a3, a4; |
193 | u_short sp, dp; |
194 | fr_info_t fi; |
195 | tcp_seq seq; |
196 | nat_t *nat2; |
197 | u_char swp; |
198 | ip_t *ip; |
199 | mb_t *m; |
200 | |
201 | softc = fin->fin_main_soft; |
202 | softn = softc->ipf_nat_soft; |
203 | /* |
204 | * Wait until we've seen the end of the start messages and even then |
205 | * only proceed further if we're using UDP. If they want to use TCP |
206 | * then data is sent back on the same channel that is already open. |
207 | */ |
208 | if (rap->rap_sdone != 0) |
209 | return 0; |
210 | |
211 | m = fin->fin_m; |
212 | tcp = (tcphdr_t *)fin->fin_dp; |
213 | off = (char *)tcp - (char *)fin->fin_ip; |
214 | off += (TCP_OFF(tcp) << 2) + fin->fin_ipoff; |
215 | |
216 | #ifdef __sgi |
217 | dlen = fin->fin_plen - off; |
218 | #else |
219 | dlen = MSGDSIZE(m) - off; |
220 | #endif |
221 | if (dlen <= 0) |
222 | return 0; |
223 | |
224 | if (dlen > sizeof(membuf)) |
225 | dlen = sizeof(membuf); |
226 | |
227 | bzero((char *)membuf, sizeof(membuf)); |
228 | COPYDATA(m, off, dlen, (char *)membuf); |
229 | |
230 | seq = ntohl(tcp->th_seq); |
231 | /* |
232 | * Check to see if the data in this packet is of interest to us. |
233 | * We only care for the first 19 bytes coming back from the server. |
234 | */ |
235 | if (rap->rap_sseq == 0) { |
236 | s = (u_char *)memstr("PNA" , (char *)membuf, 3, dlen); |
237 | if (s == NULL) |
238 | return 0; |
239 | a1 = s - membuf; |
240 | dlen -= a1; |
241 | a1 = 0; |
242 | rap->rap_sseq = seq; |
243 | a2 = MIN(dlen, sizeof(rap->rap_svr)); |
244 | } else if (seq <= rap->rap_sseq + sizeof(rap->rap_svr)) { |
245 | /* |
246 | * seq # which is the start of data and from that the offset |
247 | * into the buffer array. |
248 | */ |
249 | a1 = seq - rap->rap_sseq; |
250 | a2 = MIN(dlen, sizeof(rap->rap_svr)); |
251 | a2 -= a1; |
252 | s = membuf; |
253 | } else |
254 | return 0; |
255 | |
256 | for (a3 = a1, a4 = a2; (a4 > 0) && (a3 < 19) && (a3 >= 0); a4--,a3++) { |
257 | rap->rap_sbf |= (1 << a3); |
258 | rap->rap_svr[a3] = *s++; |
259 | } |
260 | |
261 | if ((rap->rap_sbf != 0x7ffff) || (!rap->rap_eos)) /* 19 bits */ |
262 | return 0; |
263 | rap->rap_sdone = 1; |
264 | |
265 | s = (u_char *)rap->rap_svr + 11; |
266 | if (((*s << 8) | *(s + 1)) == RA_ID_ROBUST) { |
267 | s += 2; |
268 | rap->rap_srport = (*s << 8) | *(s + 1); |
269 | } |
270 | |
271 | ip = fin->fin_ip; |
272 | swp = ip->ip_p; |
273 | swa = ip->ip_src; |
274 | swb = ip->ip_dst; |
275 | |
276 | ip->ip_p = IPPROTO_UDP; |
277 | ip->ip_src = nat->nat_ndstip; |
278 | ip->ip_dst = nat->nat_odstip; |
279 | |
280 | bcopy((char *)fin, (char *)&fi, sizeof(fi)); |
281 | bzero((char *)tcp2, sizeof(*tcp2)); |
282 | TCP_OFF_A(tcp2, 5); |
283 | fi.fin_flx |= FI_IGNORE; |
284 | fi.fin_dp = (char *)tcp2; |
285 | fi.fin_fr = &raudiofr; |
286 | fi.fin_dlen = sizeof(*tcp2); |
287 | fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); |
288 | tcp2->th_win = htons(8192); |
289 | slen = ip->ip_len; |
290 | ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); |
291 | |
292 | if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && |
293 | (rap->rap_srport != 0)) { |
294 | dp = rap->rap_srport; |
295 | sp = rap->rap_prport; |
296 | tcp2->th_sport = htons(sp); |
297 | tcp2->th_dport = htons(dp); |
298 | fi.fin_data[0] = dp; |
299 | fi.fin_data[1] = sp; |
300 | fi.fin_out = 0; |
301 | MUTEX_ENTER(&softn->ipf_nat_new); |
302 | nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, |
303 | NAT_SLAVE|IPN_UDP | (sp ? 0 : SI_W_SPORT), |
304 | NAT_OUTBOUND); |
305 | MUTEX_EXIT(&softn->ipf_nat_new); |
306 | if (nat2 != NULL) { |
307 | (void) ipf_nat_proto(&fi, nat2, IPN_UDP); |
308 | MUTEX_ENTER(&nat2->nat_lock); |
309 | ipf_nat_update(&fi, nat2); |
310 | MUTEX_EXIT(&nat2->nat_lock); |
311 | |
312 | (void) ipf_state_add(softc, &fi, NULL, |
313 | (sp ? 0 : SI_W_SPORT)); |
314 | } |
315 | } |
316 | |
317 | if ((rap->rap_mode & RAP_M_UDP) == RAP_M_UDP) { |
318 | sp = rap->rap_plport; |
319 | tcp2->th_sport = htons(sp); |
320 | tcp2->th_dport = 0; /* XXX - don't specify remote port */ |
321 | fi.fin_data[0] = sp; |
322 | fi.fin_data[1] = 0; |
323 | fi.fin_out = 1; |
324 | MUTEX_ENTER(&softn->ipf_nat_new); |
325 | nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, |
326 | NAT_SLAVE|IPN_UDP|SI_W_DPORT, |
327 | NAT_OUTBOUND); |
328 | MUTEX_EXIT(&softn->ipf_nat_new); |
329 | if (nat2 != NULL) { |
330 | (void) ipf_nat_proto(&fi, nat2, IPN_UDP); |
331 | MUTEX_ENTER(&nat2->nat_lock); |
332 | ipf_nat_update(&fi, nat2); |
333 | MUTEX_EXIT(&nat2->nat_lock); |
334 | |
335 | (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); |
336 | } |
337 | } |
338 | |
339 | ip->ip_p = swp; |
340 | ip->ip_len = slen; |
341 | ip->ip_src = swa; |
342 | ip->ip_dst = swb; |
343 | return 0; |
344 | } |
345 | |