Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / ipfilter / ip_h323_pxy.c
1 /*
2  * Copyright 2001, QNX Software Systems Ltd. All Rights Reserved
3  * 
4  * This source code has been published by QNX Software Systems Ltd. (QSSL).
5  * However, any use, reproduction, modification, distribution or transfer of
6  * this software, or any software which includes or is based upon any of this
7  * code, is only permitted under the terms of the QNX Open Community License
8  * version 1.0 (see licensing.qnx.com for details) or as otherwise expressly
9  * authorized by a written license agreement from QSSL. For more information,
10  * please email licensing@qnx.com.
11  *
12  * For more details, see QNX_OCL.txt provided with this distribution.
13  */
14
15 /*
16  * Simple H.323 proxy
17  * 
18  *      by xtang@canada.com
19  *      ported to ipfilter 3.4.20 by Michael Grant mg-ipf@grant.org
20  */
21
22 #if __FreeBSD_version >= 220000 && defined(_KERNEL)
23 # include <sys/fcntl.h>
24 # include <sys/filio.h>
25 #else
26 # include <sys/ioctl.h>
27 #endif
28
29 #define IPF_H323_PROXY
30
31 int  ippr_h323_init __P((void));
32 int  ippr_h323_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
33 void ippr_h323_del __P((ap_session_t *));
34 int  ippr_h323_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
35 int  ippr_h323_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
36
37 int  ippr_h245_init __P((void));
38 int  ippr_h245_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
39 int  ippr_h245_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
40 int  ippr_h245_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
41
42 static  frentry_t       h323_fr;
43 #if     (SOLARIS || defined(__sgi)) && defined(_KERNEL)
44 extern  KRWLOCK_T   ipf_nat;
45 #endif
46
47 static int find_port __P((int, u_char *, int datlen, int *, u_short *));
48
49
50 static int find_port(ipaddr, data, datlen, off, port)
51 int ipaddr;
52 unsigned char *data;
53 int datlen, *off;
54 unsigned short *port;
55 {
56         u_32_t addr, netaddr;
57         u_char *dp;
58         int offset;
59
60         if (datlen < 6)
61                 return -1;
62         
63         *port = 0;
64         offset = *off;
65         dp = (u_char *)data;
66         netaddr = ntohl(ipaddr);
67
68         for (offset = 0; offset <= datlen - 6; offset++, dp++) {
69                 addr = (dp[0] << 24) | (dp[1] << 16) | (dp[2] << 8) | dp[3];
70                 if (netaddr == addr)
71                 {
72                         *port = (*(dp + 4) << 8) | *(dp + 5);
73                         break;
74                 }
75         }
76         *off = offset;
77         return (offset > datlen - 6) ? -1 : 0;
78 }
79
80 /*
81  * Initialize local structures.
82  */
83 int ippr_h323_init()
84 {
85         bzero((char *)&h323_fr, sizeof(h323_fr));
86         h323_fr.fr_ref = 1;
87         h323_fr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
88
89         return 0;
90 }
91
92
93 int ippr_h323_new(fin, ip, aps, nat)
94 fr_info_t *fin;
95 ip_t *ip;
96 ap_session_t *aps;
97 nat_t *nat;
98 {
99         aps->aps_data = NULL;
100         aps->aps_psiz = 0;
101
102         return 0;
103 }
104
105
106 void ippr_h323_del(aps)
107 ap_session_t *aps;
108 {
109         int i;
110         ipnat_t *ipn;
111         
112         if (aps->aps_data) {
113                 for (i = 0, ipn = aps->aps_data;
114                      i < (aps->aps_psiz / sizeof(ipnat_t)); 
115                      i++, ipn = (ipnat_t *)((char *)ipn + sizeof(*ipn)))
116                 {
117                         /* 
118                          * Check the comment in ippr_h323_in() function,
119                          * just above nat_ioctl() call.
120                          * We are lucky here because this function is not
121                          * called with ipf_nat locked.
122                          */
123                         if (nat_ioctl((caddr_t)ipn, SIOCRMNAT, NAT_SYSSPACE|
124                                       NAT_LOCKHELD|FWRITE) == -1) {
125                                 /* log the error */
126                         }
127                 }
128                 KFREES(aps->aps_data, aps->aps_psiz);
129                 /* avoid double free */
130                 aps->aps_data = NULL;
131                 aps->aps_psiz = 0;
132         }
133         return;
134 }
135
136
137 int ippr_h323_out(fin, ip, aps, nat)
138 fr_info_t *fin;
139 ip_t *ip;
140 ap_session_t *aps;
141 nat_t *nat;
142 {
143         return 0;
144 }
145
146
147 int ippr_h323_in(fin, ip, aps, nat)
148 fr_info_t *fin;
149 ip_t *ip;
150 ap_session_t *aps;
151 nat_t *nat;
152 {
153         int ipaddr, off, datlen;
154         unsigned short port;
155         unsigned char *data;
156         tcphdr_t *tcp;
157         
158         tcp = (tcphdr_t *)fin->fin_dp;
159         ipaddr = ip->ip_src.s_addr;
160         
161         data = (unsigned char *)tcp + (tcp->th_off << 2);
162         datlen = fin->fin_dlen - (tcp->th_off << 2);
163         if (find_port(ipaddr, data, datlen, &off, &port) == 0) {
164                 ipnat_t *ipn;
165                 char *newarray;
166
167                 /* setup a nat rule to set a h245 proxy on tcp-port "port"
168                  * it's like:
169                  *   map <if> <inter_ip>/<mask> -> <gate_ip>/<mask> proxy port <port> <port>/tcp
170                  */
171                 KMALLOCS(newarray, char *, aps->aps_psiz + sizeof(*ipn));
172                 if (newarray == NULL) {
173                         return -1;
174                 }
175                 ipn = (ipnat_t *)&newarray[aps->aps_psiz];
176                 bcopy(nat->nat_ptr, ipn, sizeof(ipnat_t));
177                 strncpy(ipn->in_plabel, "h245", APR_LABELLEN);
178                 
179                 ipn->in_inip = nat->nat_inip.s_addr;
180                 ipn->in_inmsk = 0xffffffff;
181                 ipn->in_dport = htons(port);
182                 /* 
183                  * we got a problem here. we need to call nat_ioctl() to add
184                  * the h245 proxy rule, but since we already hold (READ locked)
185                  * the nat table rwlock (ipf_nat), if we go into nat_ioctl(),
186                  * it will try to WRITE lock it. This will causing dead lock
187                  * on RTP.
188                  * 
189                  * The quick & dirty solution here is release the read lock,
190                  * call nat_ioctl() and re-lock it.
191                  * A (maybe better) solution is do a UPGRADE(), and instead
192                  * of calling nat_ioctl(), we add the nat rule ourself.
193                  */
194                 RWLOCK_EXIT(&ipf_nat);
195                 if (nat_ioctl((caddr_t)ipn, SIOCADNAT,
196                               NAT_SYSSPACE|FWRITE) == -1) {
197                         READ_ENTER(&ipf_nat);
198                         return -1;
199                 }
200                 READ_ENTER(&ipf_nat);
201                 if (aps->aps_data != NULL && aps->aps_psiz > 0) {
202                         bcopy(aps->aps_data, newarray, aps->aps_psiz);
203                         KFREES(aps->aps_data, aps->aps_psiz);
204                 }
205                 aps->aps_data = newarray;
206                 aps->aps_psiz += sizeof(*ipn);
207         }
208         return 0;
209 }
210
211
212 int ippr_h245_init()
213 {
214         return 0;
215 }
216
217
218 int ippr_h245_new(fin, ip, aps, nat)
219 fr_info_t *fin;
220 ip_t *ip;
221 ap_session_t *aps;
222 nat_t *nat;
223 {
224         aps->aps_data = NULL;
225         aps->aps_psiz = 0;
226         return 0;
227 }
228
229
230 int ippr_h245_out(fin, ip, aps, nat)
231 fr_info_t *fin;
232 ip_t *ip;
233 ap_session_t *aps;
234 nat_t *nat;
235 {
236         int ipaddr, off, datlen;
237         u_short port;
238         unsigned char *data;
239         tcphdr_t *tcp;
240         
241         tcp = (tcphdr_t *)fin->fin_dp;
242         ipaddr = nat->nat_inip.s_addr;
243         data = (unsigned char *)tcp + (tcp->th_off << 2);
244         datlen = ip->ip_len - fin->fin_hlen - (tcp->th_off << 2);
245         if (find_port(ipaddr, data, datlen, &off, &port) == 0) {
246                 fr_info_t fi;
247                 nat_t     *ipn;
248
249 /*              port = htons(port); */
250                 ipn = nat_outlookup(fin->fin_ifp, IPN_UDP, IPPROTO_UDP,
251                                     ip->ip_src, ip->ip_dst, 1);
252                 if (ipn == NULL) {
253                         struct ip newip;
254                         struct udphdr udp;
255                         
256                         bcopy(ip, &newip, sizeof(newip));
257                         newip.ip_len = fin->fin_hlen + sizeof(udp);
258                         newip.ip_p = IPPROTO_UDP;
259                         newip.ip_src = nat->nat_inip;
260                         
261                         bzero(&udp, sizeof(udp));
262                         udp.uh_sport = port;
263                         
264                         bcopy(fin, &fi, sizeof(fi));
265                         fi.fin_fi.fi_p = IPPROTO_UDP;
266                         fi.fin_data[0] = port;
267                         fi.fin_data[1] = 0;
268                         fi.fin_dp = (char *)&udp;
269                         
270                         ipn = nat_new(&fi, &newip, nat->nat_ptr, NULL,
271                                       IPN_UDP|FI_W_DPORT, NAT_OUTBOUND);
272                         if (ipn != NULL) {
273                                 ipn->nat_ptr->in_hits++;
274 #ifdef  IPFILTER_LOG
275                                 nat_log(ipn, (u_int)(nat->nat_ptr->in_redir));
276 #endif
277                                 bcopy((u_char*)&ip->ip_src.s_addr,
278                                       data + off, 4);
279                                 bcopy((u_char*)&ipn->nat_outport,
280                                       data + off + 4, 2);
281                         }
282                 }
283         }
284         return 0;
285 }
286
287
288 int ippr_h245_in(fin, ip, aps, nat)
289 fr_info_t *fin;
290 ip_t *ip;
291 ap_session_t *aps;
292 nat_t *nat;
293 {
294         return 0;
295 }