/* ==================================================================== * 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. */ /* * wtp_resp.c - WTP responder implementation * * Aarno Syvänen * Lars Wirzenius */ #include "gwlib/gwlib.h" #include "wtp_resp.h" #include "wtp_pack.h" #include "wtp_tid.h" #include "wtp.h" #include "timers.h" #include "wap.h" /*********************************************************************** * Internal data structures. * * List of responder WTP machines. */ static List *resp_machines = NULL; /* * Counter for responder WTP machine id numbers, to make sure they are unique. */ static Counter *resp_machine_id_counter = NULL; /* * Give the status of wtp responder: * * limbo * not running at all * running * operating normally * terminating * waiting for operations to terminate, returning to limbo */ static enum { limbo, running, terminating } resp_run_status = limbo; wap_dispatch_func_t *dispatch_to_wdp; wap_dispatch_func_t *dispatch_to_wsp; wap_dispatch_func_t *dispatch_to_push; /* * Queue of events to be handled by WTP responder. */ static List *resp_queue = NULL; /* * Timer 'tick'. All wtp responder timer values are multiplies of this one */ static long resp_timer_freq = -1; /***************************************************************************** * * Prototypes of internal functions: * * Create and destroy an uniniatilized wtp responder state machine. */ static WTPRespMachine *resp_machine_create(WAPAddrTuple *tuple, long tid, long tcl); static void resp_machine_destroy(void *sm); /* * Checks whether wtp responser machines data structure includes a specific * machine. * The machine in question is identified with with source and destination * address and port and tid. If the machine does not exist and the event is * RcvInvoke, a new machine is created and added in the machines data * structure. * First test incoming events (WTP 10.2) (Exception is tests nro 4 and 5: if * we have a memory error, we panic. Nro 4 is already checked) If event was * validated and If the event was RcvAck or RcvAbort, the event is ignored. * If the event is RcvErrorPDU, new machine is created. */ static WTPRespMachine *resp_machine_find_or_create(WAPEvent *event); /* * Feed an event to a WTP responder state machine. Handle all errors by * itself, do not report them to the caller. WSP indication or confirmation * is handled by an included state table. */ static void resp_event_handle(WTPRespMachine *machine, WAPEvent *event); /* * Print a wtp responder machine state as a string. */ static unsigned char *name_resp_state(int name); /* * Find the wtp responder machine from the global list of wtp responder * structures that corresponds to the five-tuple of source and destination * addresses and ports and the transaction identifier. Return a pointer to * the machine, or NULL if not found. */ static WTPRespMachine *resp_machine_find(WAPAddrTuple *tuple, long tid, long mid); static void main_thread(void *); /* * Start acknowledgement interval timer */ static void start_timer_A(WTPRespMachine *machine); /* * Start retry interval timer */ static void start_timer_R(WTPRespMachine *machine); /* * Start timeout interval timer. */ static void start_timer_W(WTPRespMachine *machine); static WAPEvent *create_tr_invoke_ind(WTPRespMachine *sm, Octstr *user_data); static WAPEvent *create_tr_abort_ind(WTPRespMachine *sm, long abort_reason); static WAPEvent *create_tr_result_cnf(WTPRespMachine *sm); static int erroneous_field_in(WAPEvent *event); static void handle_wrong_version(WAPEvent *event); /* * SAR related functions. */ static WAPEvent *assembly_sar_event (WTPRespMachine *machine, int last_psn); static int add_sar_transaction (WTPRespMachine *machine, Octstr *data, int psn); /* static int is_wanted_sar_data (void *a, void *b); */ static int process_sar_transaction(WTPRespMachine *machine, WAPEvent **event); static void begin_sar_result(WTPRespMachine *machine, WAPEvent *event); static void continue_sar_result(WTPRespMachine *machine, WAPEvent *event); static void resend_sar_result(WTPRespMachine *resp_machine, WAPEvent *event); static void sar_info_destroy(void *sar_info); static void sardata_destroy(void *sardata); /* * Create a datagram with an Abort PDU and send it to the WDP layer. */ static void send_abort(WTPRespMachine *machine, long type, long reason); /* * Create a datagram with an Ack PDU and send it to the WDP layer. */ static void send_ack(WTPRespMachine *machine, long ack_type, int rid_flag); /****************************************************************************** * * EXTERNAL FUNCTIONS: * */ void wtp_resp_init(wap_dispatch_func_t *datagram_dispatch, wap_dispatch_func_t *session_dispatch, wap_dispatch_func_t *push_dispatch, long timer_freq) { resp_machines = gwlist_create(); resp_machine_id_counter = counter_create(); resp_queue = gwlist_create(); gwlist_add_producer(resp_queue); dispatch_to_wdp = datagram_dispatch; dispatch_to_wsp = session_dispatch; dispatch_to_push = push_dispatch; timers_init(); resp_timer_freq = timer_freq; wtp_tid_cache_init(); gw_assert(resp_run_status == limbo); resp_run_status = running; gwthread_create(main_thread, NULL); } void wtp_resp_shutdown(void) { gw_assert(resp_run_status == running); resp_run_status = terminating; gwlist_remove_producer(resp_queue); gwthread_join_every(main_thread); debug("wap.wtp", 0, "wtp_resp_shutdown: %ld resp_machines left", gwlist_len(resp_machines)); gwlist_destroy(resp_machines, resp_machine_destroy); gwlist_destroy(resp_queue, wap_event_destroy_item); counter_destroy(resp_machine_id_counter); wtp_tid_cache_shutdown(); timers_shutdown(); } void wtp_resp_dispatch_event(WAPEvent *event) { gwlist_produce(resp_queue, event); } /***************************************************************************** * * INTERNAL FUNCTIONS: * */ static void main_thread(void *arg) { WTPRespMachine *sm; WAPEvent *e; while (resp_run_status == running && (e = gwlist_consume(resp_queue)) != NULL) { sm = resp_machine_find_or_create(e); if (sm == NULL) { wap_event_destroy(e); } else { resp_event_handle(sm, e); } } } /* * Give the name of a responder state in a readable form. */ static unsigned char *name_resp_state(int s) { switch (s) { #define STATE_NAME(state) case state: return #state; #define ROW(state, event, condition, action, new_state) #include "wtp_resp_states.def" default: return "unknown state"; } } /* * Feed an event to a WTP responder state machine. Handle all errors yourself, * do not report them to the caller. Note: Do not put {}s of the else block * inside the macro definition. */ static void resp_event_handle(WTPRespMachine *resp_machine, WAPEvent *event) { WAPEvent *wsp_event = NULL; /* * We don't feed sar packets into state machine * until we got the whole message */ if (process_sar_transaction(resp_machine,&event) == 0) { debug("wap.wtp", 0, "SAR event received, wait for continue"); /* For removing state machine in case of incomplete sar */ start_timer_W(resp_machine); if (event != NULL) { wap_event_destroy(event); } return; } debug("wap.wtp", 0, "WTP: resp_machine %ld, state %s, event %s.", resp_machine->mid, name_resp_state(resp_machine->state), wap_event_name(event->type)); #define STATE_NAME(state) #define ROW(wtp_state, event_type, condition, action, next_state) \ if (resp_machine->state == wtp_state && \ event->type == event_type && \ (condition)) { \ action \ resp_machine->state = next_state; \ debug("wap.wtp", 0, "WTP %ld: New state %s", resp_machine->mid, #next_state); \ } else #include "wtp_resp_states.def" { error(0, "WTP: handle_event: unhandled event!"); debug("wap.wtp", 0, "WTP: handle_event: Unhandled event was:"); wap_event_dump(event); wap_event_destroy(event); return; } if (event != NULL) { wap_event_destroy(event); } if (resp_machine->state == LISTEN) resp_machine_destroy(resp_machine); } static void handle_wrong_version(WAPEvent *event) { WAPEvent *ab; if (event->type == RcvInvoke) { ab = wtp_pack_abort(PROVIDER, WTPVERSIONZERO, event->u.RcvInvoke.tid, event->u.RcvInvoke.addr_tuple); dispatch_to_wdp(ab); } } /* * Check for features 7 and 9 in WTP 10.2. */ static int erroneous_field_in(WAPEvent *event) { return event->type == RcvInvoke && event->u.RcvInvoke.version != 0; } /* * React features 7 and 9 in WTP 10.2, by aborting with an appropiate error * message. */ static void handle_erroneous_field_in(WAPEvent *event) { if (event->type == RcvInvoke) { if (event->u.RcvInvoke.version != 0) { debug("wap.wtp_resp", 0, "WTP_RESP: wrong version, aborting" "transaction"); handle_wrong_version(event); } } } /* * Checks whether wtp machines data structure includes a specific machine. * The machine in question is identified with with source and destination * address and port and tid. First test incoming events (WTP 10.2) * (Exception is tests nro 4 and 5: if we have a memory error, we panic. Nro 5 * is already checked) If event was validated and if the machine does not * exist and the event is RcvInvoke, a new machine is created and added in * the machines data structure. If the event was RcvAck or RcvAbort, the * event is ignored (test nro 3). If the event is RcvErrorPDU (test nro 4) * new machine is created for handling this event. If the event is one of WSP * primitives, we have an error. */ static WTPRespMachine *resp_machine_find_or_create(WAPEvent *event) { WTPRespMachine *resp_machine = NULL; long tid, mid; WAPAddrTuple *tuple; tid = -1; tuple = NULL; mid = -1; switch (event->type) { case RcvInvoke: /* check if erroneous fields are given */ if (erroneous_field_in(event)) { handle_erroneous_field_in(event); return NULL; } else { tid = event->u.RcvInvoke.tid; tuple = event->u.RcvInvoke.addr_tuple; } break; case RcvSegInvoke: tid = event->u.RcvSegInvoke.tid; tuple = event->u.RcvSegInvoke.addr_tuple; break; case RcvAck: tid = event->u.RcvAck.tid; tuple = event->u.RcvAck.addr_tuple; break; case RcvNegativeAck: tid = event->u.RcvAck.tid; tuple = event->u.RcvAck.addr_tuple; break; case RcvAbort: tid = event->u.RcvAbort.tid; tuple = event->u.RcvAbort.addr_tuple; break; case RcvErrorPDU: tid = event->u.RcvErrorPDU.tid; tuple = event->u.RcvErrorPDU.addr_tuple; break; case TR_Invoke_Res: mid = event->u.TR_Invoke_Res.handle; break; case TR_Result_Req: mid = event->u.TR_Result_Req.handle; break; case TR_Abort_Req: mid = event->u.TR_Abort_Req.handle; break; case TimerTO_A: mid = event->u.TimerTO_A.handle; break; case TimerTO_R: mid = event->u.TimerTO_R.handle; break; case TimerTO_W: mid = event->u.TimerTO_W.handle; break; default: debug("wap.wtp", 0, "WTP: resp_machine_find_or_create:" "unhandled event"); wap_event_dump(event); return NULL; } gw_assert(tuple != NULL || mid != -1); resp_machine = resp_machine_find(tuple, tid, mid); if (resp_machine == NULL){ switch (event->type) { /* * When PDU with an illegal header is received, its tcl-field is * irrelevant and possibly meaningless). In this case we must create * a new machine, if there is any. There is a machine for all events * handled stateful manner. */ case RcvErrorPDU: debug("wap.wtp_resp", 0, "an erronous pdu received"); wap_event_dump(event); resp_machine = resp_machine_create(tuple, tid, event->u.RcvInvoke.tcl); break; case RcvInvoke: resp_machine = resp_machine_create(tuple, tid, event->u.RcvInvoke.tcl); /* if SAR requested */ if (!event->u.RcvInvoke.gtr || !event->u.RcvInvoke.ttr) { resp_machine->sar = gw_malloc(sizeof(WTPSARData)); resp_machine->sar->nsegm = 0; resp_machine->sar->csegm = 0; resp_machine->sar->lsegm = 0; resp_machine->sar->data = NULL; } break; case RcvSegInvoke: info(0, "WTP_RESP: resp_machine_find_or_create:" " segmented invoke received, yet having no machine"); break; /* * This and the following branch implement test nro 3 in WTP 10.2. */ case RcvAck: info(0, "WTP_RESP: resp_machine_find_or_create:" " ack received, yet having no machine"); break; case RcvNegativeAck: info(0, "WTP_RESP: resp_machine_find_or_create:" " negative ack received, yet having no machine"); break; case RcvAbort: info(0, "WTP_RESP: resp_machine_find_or_create:" " abort received, yet having no machine"); break; case TR_Invoke_Res: case TR_Result_Req: case TR_Abort_Req: error(0, "WTP_RESP: resp_machine_find_or_create: WSP primitive to" " a wrong WTP machine"); break; case TimerTO_A: case TimerTO_R: case TimerTO_W: error(0, "WTP_RESP: resp_machine_find_or_create: timer event" " without a corresponding machine"); break; default: error(0, "WTP_RESP: resp_machine_find_or_create: unhandled event"); wap_event_dump(event); break; } } /* if machine == NULL */ return resp_machine; } static int is_wanted_resp_machine(void *a, void *b) { machine_pattern *pat; WTPRespMachine *m; m = a; pat = b; if (m->mid == pat->mid) return 1; if (pat->mid != -1) return 0; return m->tid == pat->tid && wap_addr_tuple_same(m->addr_tuple, pat->tuple); } static WTPRespMachine *resp_machine_find(WAPAddrTuple *tuple, long tid, long mid) { machine_pattern pat; WTPRespMachine *m; pat.tuple = tuple; pat.tid = tid; pat.mid = mid; m = gwlist_search(resp_machines, &pat, is_wanted_resp_machine); return m; } static WTPRespMachine *resp_machine_create(WAPAddrTuple *tuple, long tid, long tcl) { WTPRespMachine *resp_machine; resp_machine = gw_malloc(sizeof(WTPRespMachine)); #define ENUM(name) resp_machine->name = LISTEN; #define EVENT(name) resp_machine->name = NULL; #define INTEGER(name) resp_machine->name = 0; #define TIMER(name) resp_machine->name = gwtimer_create(resp_queue); #define ADDRTUPLE(name) resp_machine->name = NULL; #define LIST(name) resp_machine->name = NULL; #define SARDATA(name) resp_machine->name = NULL; #define MACHINE(field) field #include "wtp_resp_machine.def" gwlist_append(resp_machines, resp_machine); resp_machine->mid = counter_increase(resp_machine_id_counter); resp_machine->addr_tuple = wap_addr_tuple_duplicate(tuple); resp_machine->tid = tid; resp_machine->tcl = tcl; debug("wap.wtp", 0, "WTP: Created WTPRespMachine %p (%ld)", (void *) resp_machine, resp_machine->mid); return resp_machine; } /* * Destroys a WTPRespMachine. Assumes it is safe to do so. Assumes it has * already been deleted from the machines list. */ static void resp_machine_destroy(void * p) { WTPRespMachine *resp_machine; resp_machine = p; debug("wap.wtp", 0, "WTP: Destroying WTPRespMachine %p (%ld)", (void *) resp_machine, resp_machine->mid); gwlist_delete_equal(resp_machines, resp_machine); #define ENUM(name) resp_machine->name = LISTEN; #define EVENT(name) wap_event_destroy(resp_machine->name); #define INTEGER(name) resp_machine->name = 0; #define TIMER(name) gwtimer_destroy(resp_machine->name); #define ADDRTUPLE(name) wap_addr_tuple_destroy(resp_machine->name); #define LIST(name) gwlist_destroy(resp_machine->name,sar_info_destroy); #define SARDATA(name) sardata_destroy(resp_machine->name); #define MACHINE(field) field #include "wtp_resp_machine.def" gw_free(resp_machine); } /* * Create a TR-Invoke.ind event. */ static WAPEvent *create_tr_invoke_ind(WTPRespMachine *sm, Octstr *user_data) { WAPEvent *event; event = wap_event_create(TR_Invoke_Ind); event->u.TR_Invoke_Ind.ack_type = sm->u_ack; event->u.TR_Invoke_Ind.user_data = octstr_duplicate(user_data); event->u.TR_Invoke_Ind.tcl = sm->tcl; event->u.TR_Invoke_Ind.addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple); event->u.TR_Invoke_Ind.handle = sm->mid; return event; } /* * Create a TR-Result.cnf event. */ static WAPEvent *create_tr_result_cnf(WTPRespMachine *sm) { WAPEvent *event; event = wap_event_create(TR_Result_Cnf); event->u.TR_Result_Cnf.addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple); event->u.TR_Result_Cnf.handle = sm->mid; return event; } /* * Creates TR-Abort.ind event from a responder state machine. In addition, set * the responder indication flag. */ static WAPEvent *create_tr_abort_ind(WTPRespMachine *sm, long abort_reason) { WAPEvent *event; event = wap_event_create(TR_Abort_Ind); event->u.TR_Abort_Ind.abort_code = abort_reason; event->u.TR_Abort_Ind.addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple); event->u.TR_Abort_Ind.handle = sm->mid; event->u.TR_Abort_Ind.ir_flag = RESPONDER_INDICATION; return event; } /* * Start acknowledgement interval timer. Multiply time with * resp_timer_freq. */ static void start_timer_A(WTPRespMachine *machine) { WAPEvent *timer_event; timer_event = wap_event_create(TimerTO_A); timer_event->u.TimerTO_A.handle = machine->mid; gwtimer_start(machine->timer, L_A_WITH_USER_ACK * resp_timer_freq, timer_event); } /* * Start retry interval timer. Multiply time with resp_timer_freq. */ static void start_timer_R(WTPRespMachine *machine) { WAPEvent *timer_event; timer_event = wap_event_create(TimerTO_R); timer_event->u.TimerTO_R.handle = machine->mid; gwtimer_start(machine->timer, L_R_WITH_USER_ACK * resp_timer_freq, timer_event); } /* * Start segmentation timeout interval timer. Multiply time with * resp_timer_freq. */ static void start_timer_W(WTPRespMachine *machine) { WAPEvent *timer_event; timer_event = wap_event_create(TimerTO_W); timer_event->u.TimerTO_W.handle = machine->mid; gwtimer_start(machine->timer, W_WITH_USER_ACK * resp_timer_freq, timer_event); } static void send_abort(WTPRespMachine *machine, long type, long reason) { WAPEvent *e; e = wtp_pack_abort(type, reason, machine->tid, machine->addr_tuple); dispatch_to_wdp(e); } static void send_ack(WTPRespMachine *machine, long ack_type, int rid_flag) { WAPEvent *e; e = wtp_pack_ack(ack_type, rid_flag, machine->tid, machine->addr_tuple); dispatch_to_wdp(e); } /* * Process incoming event, checking for WTP SAR */ static int process_sar_transaction(WTPRespMachine *machine, WAPEvent **event) { WAPEvent *e, *orig_event; int psn; orig_event = *event; if (orig_event->type == RcvInvoke) { if (!orig_event->u.RcvInvoke.ttr || !orig_event->u.RcvInvoke.gtr) { /* SAR */ /* Ericcson set TTR flag even if we have the only part */ if (orig_event->u.RcvInvoke.ttr == 1) { return 1; /* Not SAR although TTR flag was set */ } else { /* save initial event */ machine->sar_invoke = wap_event_duplicate(orig_event); /* save data into list with psn = 0 */ add_sar_transaction(machine, orig_event->u.RcvInvoke.user_data, 0); if (orig_event->u.RcvInvoke.gtr == 1) { /* Need to acknowledge */ e = wtp_pack_sar_ack(ACKNOWLEDGEMENT, machine->tid, machine->addr_tuple, 0); dispatch_to_wdp(e); } return 0; } } else { return 1; /* Not SAR */ } } if (orig_event->type == RcvSegInvoke) { add_sar_transaction(machine, orig_event->u.RcvSegInvoke.user_data, orig_event->u.RcvSegInvoke.psn); if (orig_event->u.RcvSegInvoke.gtr == 1) { /* Need to acknowledge */ e = wtp_pack_sar_ack(ACKNOWLEDGEMENT, machine->tid, machine->addr_tuple, orig_event->u.RcvSegInvoke.psn); dispatch_to_wdp(e); } if (orig_event->u.RcvSegInvoke.ttr == 1) { /* Need to feed to WSP */ /* Create assembled event */ psn = orig_event->u.RcvSegInvoke.psn; wap_event_destroy(orig_event); *event = assembly_sar_event(machine,psn); gw_assert(event != NULL); return 1; } return 0; } /* Not SAR message */ return 1; } static int is_wanted_sar_data(void *a, void *b) { sar_info_t *s; int *i; s = a; i = b; if (*i == s->sar_psn) { return 1; } else { return 0; } } /* * Return 0 if transaction added suscessufully, 1 otherwise. */ static int add_sar_transaction(WTPRespMachine *machine, Octstr *data, int psn) { sar_info_t *sar_info; if (machine->sar_info == NULL) { machine->sar_info = gwlist_create(); } if (gwlist_search(machine->sar_info, &psn, is_wanted_sar_data) == NULL) { sar_info = gw_malloc(sizeof(sar_info_t)); sar_info->sar_psn = psn; sar_info->sar_data = octstr_duplicate(data); gwlist_append(machine->sar_info, sar_info); return 0; } else { debug("wap.wtp", 0, "Duplicated psn found, ignore packet"); return 1; } } static WAPEvent *assembly_sar_event(WTPRespMachine *machine, int last_psn) { WAPEvent *e; int i; sar_info_t *sar_info; e = wap_event_duplicate(machine->sar_invoke); for (i = 1; i <= last_psn; i++) { if ((sar_info = gwlist_search(machine->sar_info, &i, is_wanted_sar_data)) != NULL) { octstr_append(e->u.RcvInvoke.user_data,sar_info->sar_data); } else { debug("wap.wtp", 0, "Packet with psn %d not found", i); return e; } } return e; } static void sar_info_destroy(void *p) { sar_info_t *sar_info; sar_info = p; octstr_destroy(sar_info->sar_data); gw_free(sar_info); } static void sardata_destroy(void *p) { WTPSARData * sardata; if (p) { sardata = p; octstr_destroy(sardata->data); gw_free(sardata); } } static void begin_sar_result(WTPRespMachine *resp_machine, WAPEvent *event) { WAPEvent *result; WTPSARData *sar; int psn; gw_assert(resp_machine->sar != NULL); sar = resp_machine->sar; sar->data = octstr_duplicate(event->u.TR_Result_Req.user_data); sar->nsegm = (octstr_len(sar->data)-1)/SAR_SEGM_SIZE; sar->tr = sar->lsegm = 0; sar->csegm = -1; debug("wap.wtp", 0, "WTP: begin_sar_result(): data len = %lu", octstr_len(sar->data)); for (psn = 0; !sar->tr; psn++) { result = wtp_pack_sar_result(resp_machine, psn); if (sar->tr) resp_machine->result = wap_event_duplicate(result); debug("wap.wtp", 0, "WTP: dispath_to_wdp(): psn = %u", psn); dispatch_to_wdp(result); sar->lsegm = psn; } resp_machine->rid = 1; } static void continue_sar_result(WTPRespMachine *resp_machine, WAPEvent *event) { WAPEvent *result; WTPSARData *sar; int psn; gw_assert(resp_machine->sar != NULL && event->type == RcvAck); sar = resp_machine->sar; debug("wap.wtp", 0, "WTP: continue_sar_result(): lsegm=%d, nsegm=%d, csegm=%d", sar->lsegm, sar->nsegm, sar->csegm); start_timer_R(resp_machine); if (event->u.RcvAck.psn>sar->csegm) { sar->csegm = event->u.RcvAck.psn; } sar->tr = 0; wap_event_destroy(resp_machine->result); resp_machine->result = NULL; for (psn = sar->csegm + 1; !sar->tr; psn++) { result = wtp_pack_sar_result(resp_machine, psn); if (sar->tr) resp_machine->result = wap_event_duplicate(result); debug("wap.wtp", 0, "WTP: dispath_to_wdp(): psn = %u",psn); dispatch_to_wdp(result); sar->lsegm = psn; } } static void resend_sar_result(WTPRespMachine *resp_machine, WAPEvent *event) { WAPEvent *result; WTPSARData *sar; int psn, i; gw_assert(resp_machine->sar != NULL && event->type == RcvNegativeAck); sar = resp_machine->sar; debug("wap.wtp", 0, "WTP: resend_sar_result(): lsegm=%d, nsegm=%d, csegm=%d", sar->lsegm, sar->nsegm, sar->csegm); start_timer_R(resp_machine); if (event->u.RcvNegativeAck.nmissing) { /* if we have a list of missed packets */ for(i = 0; i < event->u.RcvNegativeAck.nmissing; i++) { if ((psn = octstr_get_char(event->u.RcvNegativeAck.missing, i)) >= 0) { result = wtp_pack_sar_result(resp_machine, psn); wtp_pack_set_rid(result, 1); debug("wap.wtp", 0, "WTP: dispath_to_wdp(): psn = %u", psn); dispatch_to_wdp(result); } } } else { /* if we have to resend a whole group */ sar->tr = 0; for (psn = sar->csegm+1; !sar->tr; psn++) { result = wtp_pack_sar_result(resp_machine, psn); wtp_pack_set_rid(result, 1); debug("wap.wtp", 0, "WTP: dispath_to_wdp(): psn = %u", psn); dispatch_to_wdp(result); } } }