BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /*
2 * Copyright (C) Ambroz Bizjak <ambrop7@gmail.com>
3 * Contributions:
4 * Transparent DNS: Copyright (C) Kerem Hadimli <kerem.hadimli@gmail.com>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the
14 * names of its contributors may be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28  
29 #include <stdlib.h>
30 #include <string.h>
31  
32 #include <misc/offset.h>
33 #include <misc/byteorder.h>
34 #include <misc/compare.h>
35 #include <base/BLog.h>
36  
37 #include <udpgw_client/UdpGwClient.h>
38  
39 #include <generated/blog_channel_UdpGwClient.h>
40  
41 static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2);
42 static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2);
43 static void free_server (UdpGwClient *o);
44 static void decoder_handler_error (UdpGwClient *o);
45 static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len);
46 static void send_monitor_handler (UdpGwClient *o);
47 static void keepalive_if_handler_done (UdpGwClient *o);
48 static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr);
49 static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid);
50 static uint16_t find_unused_conid (UdpGwClient *o);
51 static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len);
52 static void connection_free (struct UdpGwClient_connection *con);
53 static void connection_first_job_handler (struct UdpGwClient_connection *con);
54 static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len);
55 static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr);
56  
57 static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2)
58 {
59 return B_COMPARE(*v1, *v2);
60 }
61  
62 static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2)
63 {
64 int r = BAddr_CompareOrder(&v1->remote_addr, &v2->remote_addr);
65 if (r) {
66 return r;
67 }
68 return BAddr_CompareOrder(&v1->local_addr, &v2->local_addr);
69 }
70  
71 static void free_server (UdpGwClient *o)
72 {
73 // disconnect send connector
74 PacketPassConnector_DisconnectOutput(&o->send_connector);
75  
76 // free send sender
77 PacketStreamSender_Free(&o->send_sender);
78  
79 // free receive decoder
80 PacketProtoDecoder_Free(&o->recv_decoder);
81  
82 // free receive interface
83 PacketPassInterface_Free(&o->recv_if);
84 }
85  
86 static void decoder_handler_error (UdpGwClient *o)
87 {
88 DebugObject_Access(&o->d_obj);
89 ASSERT(o->have_server)
90  
91 BLog(BLOG_ERROR, "decoder error");
92  
93 // report error
94 o->handler_servererror(o->user);
95 return;
96 }
97  
98 static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len)
99 {
100 DebugObject_Access(&o->d_obj);
101 ASSERT(o->have_server)
102 ASSERT(data_len >= 0)
103 ASSERT(data_len <= o->udpgw_mtu)
104  
105 // accept packet
106 PacketPassInterface_Done(&o->recv_if);
107  
108 // check header
109 if (data_len < sizeof(struct udpgw_header)) {
110 BLog(BLOG_ERROR, "missing header");
111 return;
112 }
113 struct udpgw_header header;
114 memcpy(&header, data, sizeof(header));
115 data += sizeof(header);
116 data_len -= sizeof(header);
117 uint8_t flags = ltoh8(header.flags);
118 uint16_t conid = ltoh16(header.conid);
119  
120 // parse address
121 BAddr remote_addr;
122 if ((flags & UDPGW_CLIENT_FLAG_IPV6)) {
123 if (data_len < sizeof(struct udpgw_addr_ipv6)) {
124 BLog(BLOG_ERROR, "missing ipv6 address");
125 return;
126 }
127 struct udpgw_addr_ipv6 addr_ipv6;
128 memcpy(&addr_ipv6, data, sizeof(addr_ipv6));
129 data += sizeof(addr_ipv6);
130 data_len -= sizeof(addr_ipv6);
131 BAddr_InitIPv6(&remote_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port);
132 } else {
133 if (data_len < sizeof(struct udpgw_addr_ipv4)) {
134 BLog(BLOG_ERROR, "missing ipv4 address");
135 return;
136 }
137 struct udpgw_addr_ipv4 addr_ipv4;
138 memcpy(&addr_ipv4, data, sizeof(addr_ipv4));
139 data += sizeof(addr_ipv4);
140 data_len -= sizeof(addr_ipv4);
141 BAddr_InitIPv4(&remote_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port);
142 }
143  
144 // check remaining data
145 if (data_len > o->udp_mtu) {
146 BLog(BLOG_ERROR, "too much data");
147 return;
148 }
149  
150 // find connection
151 struct UdpGwClient_connection *con = find_connection_by_conid(o, conid);
152 if (!con) {
153 BLog(BLOG_ERROR, "unknown conid");
154 return;
155 }
156  
157 // check remote address
158 if (BAddr_CompareOrder(&con->conaddr.remote_addr, &remote_addr) != 0) {
159 BLog(BLOG_ERROR, "wrong remote address");
160 return;
161 }
162  
163 // move connection to front of the list
164 LinkedList1_Remove(&o->connections_list, &con->connections_list_node);
165 LinkedList1_Append(&o->connections_list, &con->connections_list_node);
166  
167 // pass packet to user
168 o->handler_received(o->user, con->conaddr.local_addr, con->conaddr.remote_addr, data, data_len);
169 return;
170 }
171  
172 static void send_monitor_handler (UdpGwClient *o)
173 {
174 DebugObject_Access(&o->d_obj);
175  
176 if (o->keepalive_sending) {
177 return;
178 }
179  
180 BLog(BLOG_INFO, "keepalive");
181  
182 // send keepalive
183 PacketPassInterface_Sender_Send(o->keepalive_if, (uint8_t *)&o->keepalive_packet, sizeof(o->keepalive_packet));
184  
185 // set sending keep-alive
186 o->keepalive_sending = 1;
187 }
188  
189 static void keepalive_if_handler_done (UdpGwClient *o)
190 {
191 DebugObject_Access(&o->d_obj);
192 ASSERT(o->keepalive_sending)
193  
194 // set not sending keepalive
195 o->keepalive_sending = 0;
196 }
197  
198 static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr)
199 {
200 BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conaddr, &conaddr);
201 if (!tree_node) {
202 return NULL;
203 }
204  
205 return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conaddr_node);
206 }
207  
208 static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid)
209 {
210 BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conid, &conid);
211 if (!tree_node) {
212 return NULL;
213 }
214  
215 return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conid_node);
216 }
217  
218 static uint16_t find_unused_conid (UdpGwClient *o)
219 {
220 ASSERT(o->num_connections < o->max_connections)
221  
222 while (1) {
223 if (!find_connection_by_conid(o, o->next_conid)) {
224 return o->next_conid;
225 }
226  
227 if (o->next_conid == o->max_connections - 1) {
228 o->next_conid = 0;
229 } else {
230 o->next_conid++;
231 }
232 }
233 }
234  
235 static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len)
236 {
237 ASSERT(o->num_connections < o->max_connections)
238 ASSERT(!find_connection_by_conaddr(o, conaddr))
239 ASSERT(data_len >= 0)
240 ASSERT(data_len <= o->udp_mtu)
241  
242 // allocate structure
243 struct UdpGwClient_connection *con = (struct UdpGwClient_connection *)malloc(sizeof(*con));
244 if (!con) {
245 BLog(BLOG_ERROR, "malloc failed");
246 goto fail0;
247 }
248  
249 // init arguments
250 con->client = o;
251 con->conaddr = conaddr;
252 con->first_flags = flags;
253 con->first_data = data;
254 con->first_data_len = data_len;
255  
256 // allocate conid
257 con->conid = find_unused_conid(o);
258  
259 // init first job
260 BPending_Init(&con->first_job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_first_job_handler, con);
261 BPending_Set(&con->first_job);
262  
263 // init queue flow
264 PacketPassFairQueueFlow_Init(&con->send_qflow, &o->send_queue);
265  
266 // init PacketProtoFlow
267 if (!PacketProtoFlow_Init(&con->send_ppflow, o->udpgw_mtu, o->send_buffer_size, PacketPassFairQueueFlow_GetInput(&con->send_qflow), BReactor_PendingGroup(o->reactor))) {
268 BLog(BLOG_ERROR, "PacketProtoFlow_Init failed");
269 goto fail1;
270 }
271 con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow);
272  
273 // insert to connections tree by conaddr
274 ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL))
275  
276 // insert to connections tree by conid
277 ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node, NULL))
278  
279 // insert to connections list
280 LinkedList1_Append(&o->connections_list, &con->connections_list_node);
281  
282 // increment number of connections
283 o->num_connections++;
284  
285 return;
286  
287 fail1:
288 PacketPassFairQueueFlow_Free(&con->send_qflow);
289 BPending_Free(&con->first_job);
290 free(con);
291 fail0:
292 return;
293 }
294  
295 static void connection_free (struct UdpGwClient_connection *con)
296 {
297 UdpGwClient *o = con->client;
298 PacketPassFairQueueFlow_AssertFree(&con->send_qflow);
299  
300 // decrement number of connections
301 o->num_connections--;
302  
303 // remove from connections list
304 LinkedList1_Remove(&o->connections_list, &con->connections_list_node);
305  
306 // remove from connections tree by conid
307 BAVL_Remove(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node);
308  
309 // remove from connections tree by conaddr
310 BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node);
311  
312 // free PacketProtoFlow
313 PacketProtoFlow_Free(&con->send_ppflow);
314  
315 // free queue flow
316 PacketPassFairQueueFlow_Free(&con->send_qflow);
317  
318 // free first job
319 BPending_Free(&con->first_job);
320  
321 // free structure
322 free(con);
323 }
324  
325 static void connection_first_job_handler (struct UdpGwClient_connection *con)
326 {
327 connection_send(con, UDPGW_CLIENT_FLAG_REBIND|con->first_flags, con->first_data, con->first_data_len);
328 }
329  
330 static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len)
331 {
332 UdpGwClient *o = con->client;
333 B_USE(o)
334 ASSERT(data_len >= 0)
335 ASSERT(data_len <= o->udp_mtu)
336  
337 // get buffer location
338 uint8_t *out;
339 if (!BufferWriter_StartPacket(con->send_if, &out)) {
340 BLog(BLOG_ERROR, "out of buffer");
341 return;
342 }
343 int out_pos = 0;
344  
345 if (con->conaddr.remote_addr.type == BADDR_TYPE_IPV6) {
346 flags |= UDPGW_CLIENT_FLAG_IPV6;
347 }
348  
349 // write header
350 struct udpgw_header header;
351 header.flags = ltoh8(flags);
352 header.conid = ltoh16(con->conid);
353 memcpy(out + out_pos, &header, sizeof(header));
354 out_pos += sizeof(header);
355  
356 // write address
357 switch (con->conaddr.remote_addr.type) {
358 case BADDR_TYPE_IPV4: {
359 struct udpgw_addr_ipv4 addr_ipv4;
360 addr_ipv4.addr_ip = con->conaddr.remote_addr.ipv4.ip;
361 addr_ipv4.addr_port = con->conaddr.remote_addr.ipv4.port;
362 memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4));
363 out_pos += sizeof(addr_ipv4);
364 } break;
365 case BADDR_TYPE_IPV6: {
366 struct udpgw_addr_ipv6 addr_ipv6;
367 memcpy(addr_ipv6.addr_ip, con->conaddr.remote_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip));
368 addr_ipv6.addr_port = con->conaddr.remote_addr.ipv6.port;
369 memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6));
370 out_pos += sizeof(addr_ipv6);
371 } break;
372 }
373  
374 // write packet to buffer
375 memcpy(out + out_pos, data, data_len);
376 out_pos += data_len;
377  
378 // submit packet to buffer
379 BufferWriter_EndPacket(con->send_if, out_pos);
380 }
381  
382 static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr)
383 {
384 ASSERT(!find_connection_by_conaddr(o, conaddr))
385 ASSERT(o->num_connections > 0)
386  
387 // get least recently used connection
388 struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node);
389  
390 // remove from connections tree by conaddr
391 BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node);
392  
393 // set new conaddr
394 con->conaddr = conaddr;
395  
396 // insert to connections tree by conaddr
397 ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL))
398  
399 return con;
400 }
401  
402 int UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user,
403 UdpGwClient_handler_servererror handler_servererror,
404 UdpGwClient_handler_received handler_received)
405 {
406 ASSERT(udp_mtu >= 0)
407 ASSERT(udpgw_compute_mtu(udp_mtu) >= 0)
408 ASSERT(udpgw_compute_mtu(udp_mtu) <= PACKETPROTO_MAXPAYLOAD)
409 ASSERT(max_connections > 0)
410 ASSERT(send_buffer_size > 0)
411  
412 // init arguments
413 o->udp_mtu = udp_mtu;
414 o->max_connections = max_connections;
415 o->send_buffer_size = send_buffer_size;
416 o->keepalive_time = keepalive_time;
417 o->reactor = reactor;
418 o->user = user;
419 o->handler_servererror = handler_servererror;
420 o->handler_received = handler_received;
421  
422 // limit max connections to number of conid's
423 if (o->max_connections > UINT16_MAX + 1) {
424 o->max_connections = UINT16_MAX + 1;
425 }
426  
427 // compute MTUs
428 o->udpgw_mtu = udpgw_compute_mtu(o->udp_mtu);
429 o->pp_mtu = o->udpgw_mtu + sizeof(struct packetproto_header);
430  
431 // init connections tree by conaddr
432 BAVL_Init(&o->connections_tree_by_conaddr, OFFSET_DIFF(struct UdpGwClient_connection, conaddr, connections_tree_by_conaddr_node), (BAVL_comparator)conaddr_comparator, NULL);
433  
434 // init connections tree by conid
435 BAVL_Init(&o->connections_tree_by_conid, OFFSET_DIFF(struct UdpGwClient_connection, conid, connections_tree_by_conid_node), (BAVL_comparator)uint16_comparator, NULL);
436  
437 // init connections list
438 LinkedList1_Init(&o->connections_list);
439  
440 // set zero connections
441 o->num_connections = 0;
442  
443 // set next conid
444 o->next_conid = 0;
445  
446 // init send connector
447 PacketPassConnector_Init(&o->send_connector, o->pp_mtu, BReactor_PendingGroup(o->reactor));
448  
449 // init send monitor
450 PacketPassInactivityMonitor_Init(&o->send_monitor, PacketPassConnector_GetInput(&o->send_connector), o->reactor, o->keepalive_time, (PacketPassInactivityMonitor_handler)send_monitor_handler, o);
451  
452 // init send queue
453 if (!PacketPassFairQueue_Init(&o->send_queue, PacketPassInactivityMonitor_GetInput(&o->send_monitor), BReactor_PendingGroup(o->reactor), 0, 1)) {
454 goto fail0;
455 }
456  
457 // construct keepalive packet
458 o->keepalive_packet.pp.len = sizeof(o->keepalive_packet.udpgw);
459 memset(&o->keepalive_packet.udpgw, 0, sizeof(o->keepalive_packet.udpgw));
460 o->keepalive_packet.udpgw.flags = UDPGW_CLIENT_FLAG_KEEPALIVE;
461  
462 // init keepalive queue flow
463 PacketPassFairQueueFlow_Init(&o->keepalive_qflow, &o->send_queue);
464 o->keepalive_if = PacketPassFairQueueFlow_GetInput(&o->keepalive_qflow);
465  
466 // init keepalive output
467 PacketPassInterface_Sender_Init(o->keepalive_if, (PacketPassInterface_handler_done)keepalive_if_handler_done, o);
468  
469 // set not sending keepalive
470 o->keepalive_sending = 0;
471  
472 // set have no server
473 o->have_server = 0;
474  
475 DebugObject_Init(&o->d_obj);
476 return 1;
477  
478 fail0:
479 PacketPassInactivityMonitor_Free(&o->send_monitor);
480 PacketPassConnector_Free(&o->send_connector);
481 return 0;
482 }
483  
484 void UdpGwClient_Free (UdpGwClient *o)
485 {
486 DebugObject_Free(&o->d_obj);
487  
488 // allow freeing send queue flows
489 PacketPassFairQueue_PrepareFree(&o->send_queue);
490  
491 // free connections
492 while (!LinkedList1_IsEmpty(&o->connections_list)) {
493 struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node);
494 connection_free(con);
495 }
496  
497 // free server
498 if (o->have_server) {
499 free_server(o);
500 }
501  
502 // free keepalive queue flow
503 PacketPassFairQueueFlow_Free(&o->keepalive_qflow);
504  
505 // free send queue
506 PacketPassFairQueue_Free(&o->send_queue);
507  
508 // free send
509 PacketPassInactivityMonitor_Free(&o->send_monitor);
510  
511 // free send connector
512 PacketPassConnector_Free(&o->send_connector);
513 }
514  
515 void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len)
516 {
517 DebugObject_Access(&o->d_obj);
518 ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6)
519 ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6)
520 ASSERT(data_len >= 0)
521 ASSERT(data_len <= o->udp_mtu)
522  
523 // build conaddr
524 struct UdpGwClient_conaddr conaddr;
525 conaddr.local_addr = local_addr;
526 conaddr.remote_addr = remote_addr;
527  
528 // lookup connection
529 struct UdpGwClient_connection *con = find_connection_by_conaddr(o, conaddr);
530  
531 uint8_t flags = 0;
532  
533 if (is_dns) {
534 // route to remote DNS server instead of provided address
535 flags |= UDPGW_CLIENT_FLAG_DNS;
536 }
537  
538 // if no connection and can't create a new one, reuse the least recently used une
539 if (!con && o->num_connections == o->max_connections) {
540 con = reuse_connection(o, conaddr);
541 flags |= UDPGW_CLIENT_FLAG_REBIND;
542 }
543  
544 if (!con) {
545 // create new connection
546 connection_init(o, conaddr, flags, data, data_len);
547 } else {
548 // move connection to front of the list
549 LinkedList1_Remove(&o->connections_list, &con->connections_list_node);
550 LinkedList1_Append(&o->connections_list, &con->connections_list_node);
551  
552 // send packet to existing connection
553 connection_send(con, flags, data, data_len);
554 }
555 }
556  
557 int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if)
558 {
559 DebugObject_Access(&o->d_obj);
560 ASSERT(!o->have_server)
561  
562 // init receive interface
563 PacketPassInterface_Init(&o->recv_if, o->udpgw_mtu, (PacketPassInterface_handler_send)recv_interface_handler_send, o, BReactor_PendingGroup(o->reactor));
564  
565 // init receive decoder
566 if (!PacketProtoDecoder_Init(&o->recv_decoder, recv_if, &o->recv_if, BReactor_PendingGroup(o->reactor), o, (PacketProtoDecoder_handler_error)decoder_handler_error)) {
567 BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed");
568 goto fail1;
569 }
570  
571 // init send sender
572 PacketStreamSender_Init(&o->send_sender, send_if, o->pp_mtu, BReactor_PendingGroup(o->reactor));
573  
574 // connect send connector
575 PacketPassConnector_ConnectOutput(&o->send_connector, PacketStreamSender_GetInput(&o->send_sender));
576  
577 // set have server
578 o->have_server = 1;
579  
580 return 1;
581  
582 fail1:
583 PacketPassInterface_Free(&o->recv_if);
584 return 0;
585 }
586  
587 void UdpGwClient_DisconnectServer (UdpGwClient *o)
588 {
589 DebugObject_Access(&o->d_obj);
590 ASSERT(o->have_server)
591  
592 // free server
593 free_server(o);
594  
595 // set have no server
596 o->have_server = 0;
597 }