📄 wsp_session.c
字号:
/* ==================================================================== * The Kannel Software License, Version 1.0 * * Copyright (c) 2001-2004 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 <http://www.kannel.org/>. * * Portions of this software are based upon software originally written at * WapIT Ltd., Helsinki, Finland for the Kannel project. */ /* * wsp_session.c - Implement WSP session oriented service * * Lars Wirzenius * Stipe Tolj */#include <string.h>#include <limits.h>#include "gwlib/gwlib.h"#include "wsp.h"#include "wsp_pdu.h"#include "wsp_headers.h"#include "wsp_caps.h"#include "wsp_strings.h"#include "cookies.h"#include "wap.h"#include "wtp.h"typedef enum { #define STATE_NAME(name) name, #define ROW(state, event, condition, action, next_state) #include "wsp_server_session_states.def" #define STATE_NAME(name) name, #define ROW(state, event, condition, action, next_state) #include "wsp_server_method_states.def" #define STATE_NAME(name) name, #define ROW(state, event, condition, action, next_state) #include "wsp_server_push_states.def" WSPState_count} WSPState;/* * Give the status the module: * * limbo * not running at all * running * operating normally * terminating * waiting for operations to terminate, returning to limbo */static enum { limbo, running, terminating } run_status = limbo;static wap_dispatch_func_t *dispatch_to_wtp_resp;static wap_dispatch_func_t *dispatch_to_wtp_init;static wap_dispatch_func_t *dispatch_to_appl;static wap_dispatch_func_t *dispatch_to_ota;/* * True iff "Session resume facility" is enabled. This means we are * willing to let sessions go to SUSPENDED state, and later resume them. * Currently we always support it, but this may become configurable * at some point. */static int resume_enabled = 1;static List *queue = NULL;static List *session_machines = NULL;static Counter *session_id_counter = NULL;static WSPMachine *find_session_machine(WAPEvent *event, WSP_PDU *pdu);static void handle_session_event(WSPMachine *machine, WAPEvent *event, WSP_PDU *pdu);static WSPMachine *machine_create(void);static void machine_destroy(void *p);static void handle_method_event(WSPMachine *session, WSPMethodMachine *machine, WAPEvent *event, WSP_PDU *pdu);static void cant_handle_event(WSPMachine *sm, WAPEvent *event);static WSPMethodMachine *method_machine_create(WSPMachine *, long);static void method_machine_destroy(void *msm);static void handle_push_event(WSPMachine *session, WSPPushMachine *machine, WAPEvent *e);static WSPPushMachine *push_machine_create(WSPMachine *session, long id);static void push_machine_destroy(void *p);static char *state_name(WSPState state);static unsigned long next_wsp_session_id(void);static List *make_capabilities_reply(WSPMachine *m);static List *make_reply_headers(WSPMachine *m);static Octstr *make_connectreply_pdu(WSPMachine *m);static Octstr *make_resume_reply_pdu(WSPMachine *m, List *headers);static WSP_PDU *make_confirmedpush_pdu(WAPEvent *e);static WSP_PDU *make_push_pdu(WAPEvent *e);static int transaction_belongs_to_session(void *session, void *tuple);static int find_by_session_id(void *session, void *idp);static int same_client(void *sm1, void *sm2);static WSPMethodMachine *find_method_machine(WSPMachine *, long id);static WSPPushMachine *find_push_machine(WSPMachine *m, long id);static List *unpack_new_headers(WSPMachine *sm, Octstr *hdrs);static void disconnect_other_sessions(WSPMachine *sm);static void send_abort(long reason, long handle);static void indicate_disconnect(WSPMachine *sm, long reason);static void indicate_suspend(WSPMachine *sm, long reason);static void indicate_resume(WSPMachine *sm, WAPAddrTuple *tuple, List *client_headers);static void release_holding_methods(WSPMachine *sm);static void abort_methods(WSPMachine *sm, long reason);static void abort_pushes(WSPMachine *sm, long reason);static void method_abort(WSPMethodMachine *msm, long reason);static void indicate_method_abort(WSPMethodMachine *msm, long reason);static WAPEvent *make_abort(long reason, long handle);static void send_invoke(WSPMachine *session, WSP_PDU *pdu, WAPEvent *e, long class);static void send_abort_to_initiator(long reason, long handle);static void indicate_pushabort(WSPPushMachine *machine, long reason);static void confirm_push(WSPPushMachine *machine);static void main_thread(void *);static int id_belongs_to_session (void *, void *);static int wsp_encoding_string_to_version(Octstr *enc);static Octstr *wsp_encoding_version_to_string(int version);/*********************************************************************** * Public functions. */void wsp_session_init(wap_dispatch_func_t *responder_dispatch, wap_dispatch_func_t *initiator_dispatch, wap_dispatch_func_t *application_dispatch, wap_dispatch_func_t *push_ota_dispatch) { queue = list_create(); list_add_producer(queue); session_machines = list_create(); session_id_counter = counter_create(); dispatch_to_wtp_resp = responder_dispatch; dispatch_to_wtp_init = initiator_dispatch; dispatch_to_appl = application_dispatch; dispatch_to_ota = push_ota_dispatch; wsp_strings_init(); run_status = running; gwthread_create(main_thread, NULL);}void wsp_session_shutdown(void) { gw_assert(run_status == running); run_status = terminating; list_remove_producer(queue); gwthread_join_every(main_thread); list_destroy(queue, wap_event_destroy_item); debug("wap.wsp", 0, "WSP: %ld session machines left.", list_len(session_machines)); list_destroy(session_machines, machine_destroy); counter_destroy(session_id_counter); wsp_strings_shutdown();}void wsp_session_dispatch_event(WAPEvent *event) { wap_event_assert(event); list_produce(queue, event);}/*********************************************************************** * Local functions */static void main_thread(void *arg) { WAPEvent *e; WSPMachine *sm; WSP_PDU *pdu; while (run_status == running && (e = list_consume(queue)) != NULL) { wap_event_assert(e); switch (e->type) { case TR_Invoke_Ind: pdu = wsp_pdu_unpack(e->u.TR_Invoke_Ind.user_data); if (pdu == NULL) { warning(0, "WSP: Broken PDU ignored."); wap_event_destroy(e); continue; } break; default: pdu = NULL; break; } sm = find_session_machine(e, pdu); if (sm == NULL) { wap_event_destroy(e); } else { handle_session_event(sm, e, pdu); } wsp_pdu_destroy(pdu); }}static WSPMachine *find_session_machine(WAPEvent *event, WSP_PDU *pdu) { WSPMachine *sm; long session_id; WAPAddrTuple *tuple; tuple = NULL; session_id = -1; switch (event->type) { case TR_Invoke_Ind: tuple = wap_addr_tuple_duplicate( event->u.TR_Invoke_Ind.addr_tuple); break; case TR_Invoke_Cnf: tuple = wap_addr_tuple_duplicate( event->u.TR_Invoke_Cnf.addr_tuple); break; case TR_Result_Cnf: tuple = wap_addr_tuple_duplicate( event->u.TR_Result_Cnf.addr_tuple); break; case TR_Abort_Ind: tuple = wap_addr_tuple_duplicate( event->u.TR_Abort_Ind.addr_tuple); break; case S_Connect_Res: session_id = event->u.S_Connect_Res.session_id; break; case S_Resume_Res: session_id = event->u.S_Resume_Res.session_id; break; case Disconnect_Event: session_id = event->u.Disconnect_Event.session_handle; break; case Suspend_Event: session_id = event->u.Suspend_Event.session_handle; break; case S_MethodInvoke_Res: session_id = event->u.S_MethodInvoke_Res.session_id; break; case S_MethodResult_Req: session_id = event->u.S_MethodResult_Req.session_id; break; case S_ConfirmedPush_Req: session_id = event->u.S_ConfirmedPush_Req.session_id; break; case S_Push_Req: session_id = event->u.S_Push_Req.session_id; break; default: error(0, "WSP: Cannot find machine for %s event", wap_event_name(event->type)); } gw_assert(tuple != NULL || session_id != -1); /* Pre-state-machine tests, according to 7.1.5. After the tests, * caller will pass the event to sm if sm is not NULL. */ sm = NULL; /* First test is for MRUEXCEEDED, and we don't have a MRU */ /* Second test is for class 2 TR-Invoke.ind with Connect PDU */ if (event->type == TR_Invoke_Ind && event->u.TR_Invoke_Ind.tcl == 2 && pdu->type == Connect) { /* Create a new session, even if there is already * a session open for this address. The new session * will take care of killing the old ones. */ sm = machine_create(); gw_assert(tuple != NULL); sm->addr_tuple = wap_addr_tuple_duplicate(tuple); sm->connect_handle = event->u.TR_Invoke_Ind.handle; /* Third test is for class 2 TR-Invoke.ind with Resume PDU */ } else if (event->type == TR_Invoke_Ind && event->u.TR_Invoke_Ind.tcl == 2 && pdu->type == Resume) { /* Pass to session identified by session id, not * the address tuple. */ session_id = pdu->u.Resume.sessionid; sm = list_search(session_machines, &session_id, find_by_session_id); if (sm == NULL) { /* No session; TR-Abort.req(DISCONNECT) */ send_abort(WSP_ABORT_DISCONNECT, event->u.TR_Invoke_Ind.handle); } /* Fourth test is for a class 1 or 2 TR-Invoke.Ind with no * session for that address tuple. We also handle class 0 * TR-Invoke.ind here by ignoring them; this seems to be * an omission in the spec table. */ } else if (event->type == TR_Invoke_Ind) { sm = list_search(session_machines, tuple, transaction_belongs_to_session); if (sm == NULL && (event->u.TR_Invoke_Ind.tcl == 1 || event->u.TR_Invoke_Ind.tcl == 2)) { send_abort(WSP_ABORT_DISCONNECT, event->u.TR_Invoke_Ind.handle); } /* Other tests are for events not handled by the state tables; * do those later, after we've tried to handle them. */ } else { if (session_id != -1) { sm = list_search(session_machines, &session_id, find_by_session_id); } else { sm = list_search(session_machines, tuple, transaction_belongs_to_session); } /* The table doesn't really say what we should do with * non-Invoke events for which there is no session. But * such a situation means there is an error _somewhere_ * in the gateway. */ if (sm == NULL) { error(0, "WSP: Cannot find session machine for event."); wap_event_dump(event); } } wap_addr_tuple_destroy(tuple); return sm;}static void handle_session_event(WSPMachine *sm, WAPEvent *current_event, WSP_PDU *pdu) { debug("wap.wsp", 0, "WSP: machine %p, state %s, event %s", (void *) sm, state_name(sm->state), wap_event_name(current_event->type)); #define STATE_NAME(name) #define ROW(state_name, event, condition, action, next_state) \ { \ struct event *e; \ e = ¤t_event->u.event; \ if (sm->state == state_name && \ current_event->type == event && \ (condition)) { \ action \ sm->state = next_state; \ debug("wap.wsp", 0, "WSP %ld: New state %s", \ sm->session_id, #next_state); \ goto end; \ } \ } #include "wsp_server_session_states.def" cant_handle_event(sm, current_event);end: wap_event_destroy(current_event); if (sm->state == NULL_SESSION) machine_destroy(sm);}static void cant_handle_event(WSPMachine *sm, WAPEvent *event) { /* We do the rest of the pre-state-machine tests here. The first * four were done in find_session_machine(). The fifth is a * class 1 or 2 TR-Invoke.ind not handled by the state tables. */ if (event->type == TR_Invoke_Ind && (event->u.TR_Invoke_Ind.tcl == 1 || event->u.TR_Invoke_Ind.tcl == 2)) { warning(0, "WSP: Can't handle TR-Invoke.ind, aborting transaction."); debug("wap.wsp", 0, "WSP: The unhandled event:"); wap_event_dump(event); send_abort(WSP_ABORT_PROTOERR, event->u.TR_Invoke_Ind.handle); /* The sixth is a class 0 TR-Invoke.ind not handled by state tables. */ } else if (event->type == TR_Invoke_Ind) { warning(0, "WSP: Can't handle TR-Invoke.ind, ignoring."); debug("wap.wsp", 0, "WSP: The ignored event:"); wap_event_dump(event); /* The seventh is any other event not handled by state tables. */ } else { error(0, "WSP: Can't handle event. Aborting session."); debug("wap.wsp", 0, "WSP: The unhandled event:"); wap_event_dump(event); /* TR-Abort.req(PROTOERR) if it is some other transaction * event than abort. */ /* Currently that means TR-Result.cnf, because we already * tested for Invoke. */ /* FIXME We need a better way to get at event values than * by hardcoding the types. */ if (event->type == TR_Result_Cnf) { send_abort(WSP_ABORT_PROTOERR, event->u.TR_Result_Cnf.handle); } /* Abort(PROTOERR) all method and push transactions */ abort_methods(sm, WSP_ABORT_PROTOERR); abort_pushes(sm, WSP_ABORT_PROTOERR); /* S-Disconnect.ind(PROTOERR) */ indicate_disconnect(sm, WSP_ABORT_PROTOERR); }}static WSPMachine *machine_create(void) { WSPMachine *p; p = gw_malloc(sizeof(WSPMachine)); debug("wap.wsp", 0, "WSP: Created WSPMachine %p", (void *) p); #define INTEGER(name) p->name = 0; #define OCTSTR(name) p->name = NULL; #define HTTPHEADERS(name) p->name = NULL; #define ADDRTUPLE(name) p->name = NULL; #define MACHINESLIST(name) p->name = list_create(); #define CAPABILITIES(name) p->name = NULL; #define COOKIES(name) p->name = NULL; #define REFERER(name) p->name = NULL; #define MACHINE(fields) fields #include "wsp_server_session_machine.def" p->state = NULL_SESSION; /* set capabilities to default values (defined in 1.1) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -