/* ==================================================================== * The Kannel Software License, Version 1.0 * * Copyright (c) 2001-2005 Kannel Group * Copyright (c) 1998-2001 WapIT Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Kannel Group (http://www.kannel.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Kannel" and "Kannel Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact org@kannel.org. * * 5. Products derived from this software may not be called "Kannel", * nor may "Kannel" appear in their name, without prior written * permission of the Kannel Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Kannel Group. For more information on * the Kannel Group, please see . * * Portions of this software are based upon software originally written at * WapIT Ltd., Helsinki, Finland for the Kannel project. */ /* * wapproxy.c - an WDP, WSP, WTP layer proxy * * This module contains the main program for the WAP proxy box. * It's intention is to sit between a WTP initiator and WTP repsonder * and log all the UDP traffic that is send in a session. * * The architecture looks like this: * * ---------- UDP -------- UDP ------ * wap device ---> wapproxy ---> wap gw * ---------- <--- -------- <--- ------ * port 51000 p 9201 p 51000 port 9201 * (a) (b) * * This means wapproxy gets the UDP/WDP packets that are actually to * be transmitted to the real wap gw. It changes the source addr within * that packet to reflect wapproxy has send it and binds to the port the * wap device was sending the packet. Then the packet is send to the real * wap gw and wapproxy listens on the client source port (i.e. 51000) for * packets from the wap gw. When those are received the communication is * inverted, which means wapproxy changes again the source addr from the * value of wap gw to it's own and forwards the packet to the client source * addr port. * * Hence the wap device uses wapproxy transparently without knowing that * it is only a proxy and the packets are forwarded to other boxes. * * Stipe Tolj */ #include #include #include #include #include #include #include #include #include #include #include #include "gwlib/gwlib.h" #include "msg.h" //#include "bearerbox.h" #include "shared.h" #include "wap/wap.h" #include "wap/wtp.h" #include "wap/wtp_pdu.h" /* globals */ static volatile sig_atomic_t udp_running; static List *udpc_list; static Octstr *interface_name = NULL; static Octstr *wapgw; static int verbose = 0; static int server_port = 0; List *incoming_wdp; List *outgoing_wdp; List *flow_threads; Counter *incoming_wdp_counter; Counter *outgoing_wdp_counter; enum { CONNECTIONLESS_PORT = 9200, CONNECTION_ORIENTED_PORT = 9201, WTLS_CONNECTIONLESS_PORT = 9202, WTLS_CONNECTION_ORIENTED_PORT = 9203 }; /* structure for a UDP connection */ typedef struct _udpc { int fd; Octstr *addr; Octstr *map_addr; List *outgoing_list; long receiver; } Udpc; /* forward declarations */ static void udpc_destroy(Udpc *udpc); /*------------------------------------------------------------- * analyze and dump functions * */ static WAPEvent *wdp_msg2event(Msg *msg) { WAPEvent *dgram = NULL; gw_assert(msg_type(msg) == wdp_datagram); if (msg->wdp_datagram.destination_port == server_port || msg->wdp_datagram.source_port == server_port || msg->wdp_datagram.destination_port == CONNECTION_ORIENTED_PORT || msg->wdp_datagram.source_port == CONNECTION_ORIENTED_PORT) { dgram = wap_event_create(T_DUnitdata_Ind); dgram->u.T_DUnitdata_Ind.addr_tuple = wap_addr_tuple_create( msg->wdp_datagram.source_address, msg->wdp_datagram.source_port, msg->wdp_datagram.destination_address, msg->wdp_datagram.destination_port); dgram->u.T_DUnitdata_Ind.user_data = octstr_duplicate(msg->wdp_datagram.user_data); } return dgram; } static void wdp_event_dump(Msg *msg) { WAPEvent *dgram; if ((dgram = wdp_msg2event(msg)) != NULL) /* wap_dispatch_datagram(dgram); */ wap_event_dump(dgram); wap_event_destroy(dgram); } static void wtp_event_dump(Msg *msg) { WAPEvent *dgram; List *events; long i, n; dgram = wdp_msg2event(msg); if (dgram == NULL) error(0, "dgram is null"); /* pdu = wtp_pdu_unpack(dgram->u.T_DUnitdata_Ind.user_data); if (pdu == NULL) { error(0, "WTP PDU unpacking failed, WAP event is:"); wap_event_dump(dgram); } else { wtp_pdu_dump(pdu, 0); wtp_pdu_destroy(pdu); } */ events = wtp_unpack_wdp_datagram(dgram); n = gwlist_len(events); debug("wap.proxy",0,"datagram contains %ld events", n); i = 1; while (gwlist_len(events) > 0) { WAPEvent *event; event = gwlist_extract_first(events); info(0, "WTP: %ld/%ld event %s.", i, n, wap_event_name(event->type)); if (wtp_event_is_for_responder(event)) /* wtp_resp_dispatch_event(event); */ debug("",0,"datagram is for WTP responder"); else /* wtp_initiator_dispatch_event(event); */ debug("",0,"datagram is for WTP initiator"); wap_event_dump(event); /* switch (event->type) { RcvInvoke: debug("",0,"XXX invoke"); break; RcvResult: debug("",0,"XXX result"); break; default: error(0,"unkown WTP event type while unpacking"); break; } */ i++; } wap_event_destroy(dgram); gwlist_destroy(events, NULL); } static void dump(Msg *msg) { switch (verbose) { case 0: break; case 1: msg_dump(msg, 0); break; case 2: wdp_event_dump(msg); break; case 3: msg_dump(msg, 0); wdp_event_dump(msg); break; case 4: wtp_event_dump(msg); break; case 5: msg_dump(msg, 0); wtp_event_dump(msg); break; case 6: wdp_event_dump(msg); wtp_event_dump(msg); break; case 7: msg_dump(msg, 0); wdp_event_dump(msg); wtp_event_dump(msg); break; } } /*------------------------------------------------- * receiver thread */ static void udp_receiver(void *arg) { Octstr *datagram, *cliaddr; int ret; Msg *msg; Udpc *conn = arg; Octstr *ip; gwlist_add_producer(incoming_wdp); gwlist_add_producer(flow_threads); gwthread_wakeup(MAIN_THREAD_ID); /* remove messages from socket until it is closed */ while (1) { if (read_available(conn->fd, 100000) < 1) continue; ret = udp_recvfrom(conn->fd, &datagram, &cliaddr); if (ret == -1) { if (errno == EAGAIN) /* No datagram available, don't block. */ continue; error(errno, "Failed to receive an UDP"); continue; } ip = udp_get_ip(cliaddr); msg = msg_create(wdp_datagram); msg->wdp_datagram.source_address = udp_get_ip(cliaddr); msg->wdp_datagram.source_port = udp_get_port(cliaddr); msg->wdp_datagram.destination_address = udp_get_ip(conn->addr); msg->wdp_datagram.destination_port = udp_get_port(conn->addr); msg->wdp_datagram.user_data = datagram; info(0, "datagram received <%s:%d> -> <%s:%d>", octstr_get_cstr(udp_get_ip(cliaddr)), udp_get_port(cliaddr), octstr_get_cstr(udp_get_ip(conn->addr)), udp_get_port(conn->addr)); dump(msg); /* * Descide if this is (a) or (b) UDP packet and add them to the * corresponding queues */ if (octstr_compare(conn->addr, conn->map_addr) == 0) { gwlist_produce(incoming_wdp, msg); counter_increase(incoming_wdp_counter); } else { gwlist_produce(outgoing_wdp, msg); counter_increase(outgoing_wdp_counter); } octstr_destroy(cliaddr); octstr_destroy(ip); } gwlist_remove_producer(incoming_wdp); gwlist_remove_producer(flow_threads); } /*--------------------------------------------- * sender thread */ static int send_udp(int fd, Msg *msg) { Octstr *cliaddr; int ret; cliaddr = udp_create_address(msg->wdp_datagram.destination_address, msg->wdp_datagram.destination_port); ret = udp_sendto(fd, msg->wdp_datagram.user_data, cliaddr); if (ret == -1) error(0, "could not send UDP datagram"); octstr_destroy(cliaddr); return ret; } static void udp_sender(void *arg) { Msg *msg; Udpc *conn = arg; gwlist_add_producer(flow_threads); while (1) { if ((msg = gwlist_consume(conn->outgoing_list)) == NULL) break; info(0, "sending datagram <%s:%ld> -> <%s:%ld>", octstr_get_cstr(msg->wdp_datagram.source_address), msg->wdp_datagram.source_port, octstr_get_cstr(msg->wdp_datagram.destination_address), msg->wdp_datagram.destination_port); dump(msg); if (send_udp(conn->fd, msg) == -1) { msg_destroy(msg); continue; } counter_increase(outgoing_wdp_counter); msg_destroy(msg); } gwthread_join(conn->receiver); udpc_destroy(conn); gwlist_remove_producer(flow_threads); } /*--------------------------------------------------------------- * create UDP connection */ static Udpc *udpc_create(int port, char *interface_name, Octstr *map_addr) { Udpc *udpc; Octstr *os; int fl; udpc = gw_malloc(sizeof(Udpc)); udpc->fd = udp_bind(port, interface_name); os = octstr_create(interface_name); udpc->addr = udp_create_address(os, port); udpc->map_addr = map_addr ? map_addr : udpc->addr; octstr_destroy(os); if (udpc->addr == NULL) { error(0, "updc_create: could not resolve interface <%s>", interface_name); close(udpc->fd); gw_free(udpc); return NULL; } fl = fcntl(udpc->fd, F_GETFL); fcntl(udpc->fd, F_SETFL, fl | O_NONBLOCK); os = udp_get_ip(udpc->addr); debug("wap.proxy",0, "bound to UDP <%s:%d>", octstr_get_cstr(os), udp_get_port(udpc->addr)); octstr_destroy(os); udpc->outgoing_list = gwlist_create(); return udpc; } static void udpc_destroy(Udpc *udpc) { if (udpc == NULL) return; if (udpc->fd >= 0) close(udpc->fd); octstr_destroy(udpc->addr); gw_assert(gwlist_len(udpc->outgoing_list) == 0); gwlist_destroy(udpc->outgoing_list, NULL); gw_free(udpc); } static int add_service(int port, char *interface_name, Octstr *map_addr) { Udpc *udpc; if ((udpc = udpc_create(port, interface_name, map_addr)) == NULL) goto error; gwlist_add_producer(udpc->outgoing_list); udpc->receiver = gwthread_create(udp_receiver, udpc); if (udpc->receiver == -1) goto error; if (gwthread_create(udp_sender, udpc) == -1) goto error; gwlist_append(udpc_list, udpc); return 0; error: error(0, "Failed to start UDP receiver/sender thread"); udpc_destroy(udpc); return -1; } /*------------------------------------------------------------- * main calling functions * */ static int udp_start(Cfg *cfg) { if (udp_running) return -1; debug("wap.proxy", 0, "starting UDP sender/receiver module"); udpc_list = gwlist_create(); /* have a list of running systems */ add_service(server_port, octstr_get_cstr(interface_name), NULL); /* wsp/wtp */ gwlist_add_producer(incoming_wdp); udp_running = 1; return 0; } static Udpc *udpc_find_mapping(Msg *msg, int inbound) { int i; Udpc *udpc; Octstr *addr; /* check if there is allready a bound UDP port */ gwlist_lock(udpc_list); for (i=0; i < gwlist_len(udpc_list); i++) { udpc = gwlist_get(udpc_list, i); /* decide if we compare against inbound or outbound traffic mapping */ addr = inbound ? udpc->map_addr : udpc->addr; if (msg->wdp_datagram.source_port == udp_get_port(addr) && octstr_compare(msg->wdp_datagram.source_address, udp_get_ip(addr)) == 0) { gwlist_unlock(udpc_list); return udpc; } } gwlist_unlock(udpc_list); return NULL; } /* * this function receives an WDP message and adds it to * corresponding outgoing_list. */ static int udp_addwdp_from_server(Msg *msg) { Udpc *udpc; Octstr *os; Octstr *source; if (!udp_running) return -1; assert(msg != NULL); assert(msg_type(msg) == wdp_datagram); octstr_destroy(msg->wdp_datagram.source_address); msg->wdp_datagram.source_address = octstr_create(octstr_get_cstr(msg->wdp_datagram.destination_address)); msg->wdp_datagram.source_port = msg->wdp_datagram.destination_port; if ((udpc = udpc_find_mapping(msg, 0)) == NULL) /* there should have been one */ panic(0,"Could not find UDP mapping, internal error"); /* insert the found mapped destination */ octstr_destroy(msg->wdp_datagram.source_address); octstr_destroy(msg->wdp_datagram.destination_address); msg->wdp_datagram.destination_address = udp_get_ip(udpc->map_addr); msg->wdp_datagram.destination_port = udp_get_port(udpc->map_addr); /* now search for our inbound UDP socket */ os = octstr_duplicate(interface_name); source = udp_create_address(os, server_port); msg->wdp_datagram.source_address = udp_get_ip(source); msg->wdp_datagram.source_port = udp_get_port(source); if ((udpc = udpc_find_mapping(msg, 0)) == NULL) panic(0,"Could not find main inbound UDP socket, internal error"); /* * ok, got the destination, got the socket, * now put it on the outbound queue */ gwlist_produce(udpc->outgoing_list, msg); octstr_destroy(os); return 0; } /* * this function receives an WDP message and checks if a UDP * service for this client has to be created */ static int udp_addwdp_from_client(Msg *msg) { Udpc *udpc; Octstr *map_addr; Octstr *os; Octstr *source; if (!udp_running) return -1; assert(msg != NULL); assert(msg_type(msg) == wdp_datagram); /* * Check if there is allready a bound UDP port for this mapping. * If not create a mapping and bind the mapped UDP port * The mapped port is simply 2x of the client port. */ if ((udpc = udpc_find_mapping(msg, 1)) == NULL) { info(0, "Creating UDP mapping <%s:%ld> <-> <%s:%ld>", octstr_get_cstr(msg->wdp_datagram.source_address), msg->wdp_datagram.source_port, octstr_get_cstr(msg->wdp_datagram.destination_address), msg->wdp_datagram.source_port*2); map_addr = udp_create_address(msg->wdp_datagram.source_address, msg->wdp_datagram.source_port); add_service(msg->wdp_datagram.source_port * 2, octstr_get_cstr(interface_name), map_addr); /* now we should find it in the udpc_list */ if ((udpc = udpc_find_mapping(msg, 1)) == NULL) panic(0,"Could not find UDP mapping, internal error"); } /* now swap the message addressing */ octstr_destroy(msg->wdp_datagram.source_address); octstr_destroy(msg->wdp_datagram.destination_address); os = octstr_duplicate(interface_name); source = udp_create_address(os, msg->wdp_datagram.source_port * 2); msg->wdp_datagram.source_address = udp_get_ip(source); msg->wdp_datagram.source_port = udp_get_port(source); msg->wdp_datagram.destination_address = octstr_duplicate(wapgw); msg->wdp_datagram.destination_port = CONNECTION_ORIENTED_PORT; octstr_destroy(os); gwlist_produce(udpc->outgoing_list, msg); return -1; } static int udp_shutdown(void) { if (!udp_running) return -1; debug("bb.thread", 0, "udp_shutdown: Starting avalanche"); gwlist_remove_producer(incoming_wdp); return 0; } static int udp_die(void) { Udpc *udpc; if (!udp_running) return -1; /* * remove producers from all outgoing lists. */ debug("bb.udp", 0, "udp_die: removing producers from udp-lists"); while ((udpc = gwlist_consume(udpc_list)) != NULL) { gwlist_remove_producer(udpc->outgoing_list); } gwlist_destroy(udpc_list, NULL); udp_running = 0; return 0; } /*------------------------------------------------------------- * main consumer threads * */ static void wdp_router(void *arg) { Msg *msg; gwlist_add_producer(flow_threads); while (1) { if ((msg = gwlist_consume(outgoing_wdp)) == NULL) break; gw_assert(msg_type(msg) == wdp_datagram); udp_addwdp_from_server(msg); } udp_die(); gwlist_remove_producer(flow_threads); } static void service_router(void *arg) { Msg *msg; gwlist_add_producer(flow_threads); while (1) { if ((msg = gwlist_consume(incoming_wdp)) == NULL) break; gw_assert(msg_type(msg) == wdp_datagram); udp_addwdp_from_client(msg); } udp_die(); gwlist_remove_producer(flow_threads); } /*------------------------------------------------------------- * main functions * */ static void help(void) { info(0, "Usage: wapproxy [options] host ..."); info(0, "where host is the real wap gw to forward to and options are:"); info(0, "-v number"); info(0, " set log level for stderr logging"); info(0, "-i interface"); info(0, " bind to the given interface for UDP server port (default: 0.0.0.0)"); info(0, "-p port"); info(0, " bind to the given port for UDP server port (default: 9201)"); info(0, "-m"); info(0, " dump WDP/UDP packets, msg_dump()"); info(0, "-e"); info(0, " dump WAP event packets, wap_event_dump()"); info(0, "-t"); info(0, " dump WTP PDUs, wtp_pdu_dump()"); } int main(int argc, char **argv) { int opt; Cfg *cfg = NULL; gwlib_init(); server_port = CONNECTION_ORIENTED_PORT; while ((opt = getopt(argc, argv, "v:meti:p:")) != EOF) { switch (opt) { case 'v': log_set_output_level(atoi(optarg)); break; case 'm': verbose += 1; break; case 'e': verbose += 2; break; case 't': verbose += 4; break; case 'h': help(); exit(0); case 'i': interface_name = octstr_create(optarg); break; case 'p': server_port = atoi(optarg); break; case '?': default: error(0, "Invalid option %c", opt); help(); panic(0, "Stopping."); } } if (optind == argc) { help(); exit(0); } /* get the host or IP of the real wap gw to forward the WDP packets */ wapgw = octstr_create(argv[optind]); /* if no interface was given use 0.0.0.0 */ if (!interface_name) interface_name = octstr_create("*"); report_versions("wapproxy"); /* initialize main inbound and outbound queues */ outgoing_wdp = gwlist_create(); incoming_wdp = gwlist_create(); flow_threads = gwlist_create(); outgoing_wdp_counter = counter_create(); incoming_wdp_counter = counter_create(); /* start the main UDP listening threads */ udp_start(cfg); gwlist_add_producer(outgoing_wdp); debug("bb", 0, "starting WDP routers"); if (gwthread_create(service_router, NULL) == -1) panic(0, "Failed to start a new thread for inbound WDP routing"); if (gwthread_create(wdp_router, NULL) == -1) panic(0, "Failed to start a new thread for outbound WDP routing"); gwthread_sleep(5.0); /* give time to threads to register themselves */ while (gwlist_consume(flow_threads) != NULL) ; udp_shutdown(); gwlist_remove_producer(outgoing_wdp); gwlist_destroy(flow_threads, NULL); gwlist_destroy(incoming_wdp, NULL); gwlist_destroy(outgoing_wdp, NULL); counter_destroy(incoming_wdp_counter); counter_destroy(outgoing_wdp_counter); octstr_destroy(interface_name); octstr_destroy(wapgw); gwlib_shutdown(); return 0; }