view src/g23m-fad/app/app_core.c @ 52:6d77f6e1670a

bug in the last commit
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 19 Oct 2020 05:27:40 +0000
parents fa8dc04885d8
children
line wrap: on
line source

/*-*- c-basic-offset: 2 -*-
+------------------------------------------------------------------------------
|  File:       app_core.c
+------------------------------------------------------------------------------
|  Copyright 2003 Texas Instruments Berlin, AG
|                 All rights reserved.
|
|                 This file is confidential and a trade secret of Texas
|                 Instruments Berlin, AG
|                 The receipt of or possession of this file does not convey
|                 any rights to reproduce or disclose its contents or to
|                 manufacture, use, or sell anything it may describe, in
|                 whole, or in part, without the specific written consent of
|                 Texas Instruments Berlin, AG.
+-----------------------------------------------------------------------------
|  Purpose :  Example application for TCP/IP and Socket API -- core functions.
+-----------------------------------------------------------------------------
*/


/* This should only be compiled into the entity if TCP/IP is enabled */
#ifdef FF_GPF_TCPIP

#define APP_CORE_C

#define ENTITY_APP

/*==== INCLUDES =============================================================*/

#include <string.h>             /* String functions, e. g. strncpy(). */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef _SIMULATION_
#include "typedefs.h"           /* Condat data types. */
#endif /* _SIMULATION_ */
#include "vsi.h"                /* A lot of macros. */
#ifndef _SIMULATION_
#include "custom.h"
#include "gsm.h"                /* A lot of macros. */
#include "prim.h"               /* Definitions of used SAP and directions. */
#include "pei.h"                /* PEI interface. */
#include "tools.h"              /* Common tools. */
#endif /* _SIMULATION_ */
#include "socket_api.h"             /* Socket API. */
#include "app.h"                /* Global entity definitions. */


/*==== Local data ============================================================*/

#define NPROCS 1                /* Maximum number of application processes. */

#define PORT_CHARGEN 19         /* Chargen service for download. */
#define PORT_ECHO     7         /* Echo port for tcpecho and udpecho. */
#define PORT_DISCARD  9         /* Discard port for upload. */

#define FQDN_LENGTH 255         /* Maximum length of a fully-qualified domain
                                 * name. */

#undef HTONS
#define HTONS(a) ((((a) & 0xff) << 8) | (((a) & 0xff00) >> 8))
#undef NTOHS
#define NTOHS(a) HTONS(a)
#define MIN(a, b) ((a) < (b) ? (a) : (b))

/* We can run different types of application processes, according to the
 * commend sent by the user. */
typedef enum {
  AP_NONE,                      /* For uninitialized process types. */
  AP_TCPDL,                     /* Download some data over TCP. */
  AP_TCPUL,                     /* Upload some data over TCP. */
  AP_UDPDL,                     /* Download some data over UDP. */
  AP_UDPUL,                     /* Upload some data over UDP. */
  AP_TCPECHO,                   /* Send/receive data to/from TCP echo port. */
  AP_UDPECHO,                   /* Send/receive data to/from UDP echo port. */
  AP_TCPSRV,                    /* TCP server application. */
  AP_DNSQRY,                    /* Issue DNS queries and collect result. */
  AP_TCPFORK,                   /* Forked TCP server process. */
  AP_INVALID
} APP_PROCTYPE_T ;

/* Strings for process types; used for debugging and MUST correspond strictly
 * to the process type enum labels defined above. */
static char *proc_type_name[] = {
  "AP_NONE",                    /* 00 */
  "AP_TCPDL",                   /* dl */
  "AP_TCPUL",                   /* ul */
  "AP_UDPDL",
  "AP_UDPUL",
  "AP_TCPECHO",                 /* te */
  "AP_UDPECHO",                 /* ue */
  "AP_TCPSRV",
  "AP_DNSQRY",                  /* dq */
  "AP_TCPFORK",
  "AP_INVALID"
} ;

/* Process states; the state transitions are mostly linear in this order. */
typedef enum {
  PS_IDLE,                      /* Initial state, process not running. */
  PS_W_DCM_OPEN,                /* Waiting for DCM to open connection. */
  PS_W_DCM_OPEN_ONLY,           /* Waiting for DCM to open connection - no further action. */
  PS_W_CREAT,                   /* Waiting for socket create confirmation. */
  PS_W_SCONN,                   /* Waiting for socket connect confirmation. */
  PS_W_BIND,                    /* Waiting for socket bind confirmation. */
  PS_W_LISTN,                   /* Waiting for confirmation of listen call. */
  PS_LISTENS,                   /* Listens for client connections. */
  PS_W_DNS,                     /* Waiting for a DNS query. */
  PS_COMM,                      /* Happily exchanging data. */
  PS_W_SCLOS,                   /* Waiting for socket close confirmation. */
  PS_W_DCLOS,                   /* Waiting for DCM to close connection. */
  PS_W_CONN_INFO,               /* Waiting for connection information */
  PS_DCM_OPEN,                  /* DCM (bearer) connecion opened*/
  PS_SOCK_OPEN,                 /* Socket and bearer open */
  PS_INVALID
} PROC_STAT_T ;

/* Strings for the process states; used for debugging and MUST correspond
 * strictly to the process state enum labels defined above, as the array is
 * indexed by those. */
static char *proc_state_name[] = {
  "PS_IDLE",
  "PS_W_DCM_OPEN",
  "PS_W_DCM_OPEN_ONLY",
  "PS_W_CREAT",
  "PS_W_SCONN",
  "PS_W_BIND",
  "PS_W_LISTN",
  "PS_W_LISTENS",
  "PS_W_DNS",
  "PS_COMM",
  "PS_W_SCLOS",
  "PS_W_DCLOS",
  "PS_W_CONN_INFO",
  "PS_DCM_OPEN",
  "PS_SOCK_OPEN",
  "PS_INVALID"
} ;

/* The data a process holds. May be dynamically allocated in the future. */
typedef struct PROCESS_CONTEXT_S {
  APP_PROCTYPE_T ptype ;        /* Type of application process */
  PROC_STAT_T pstate ;          /* Process status as defined above. */
  int in_shutdown ;             /* Non-zero iff process is being shut down. */
  T_SOCK_EVENTSTRUCT *last_evt; /* Last event passed from the Socket API. */
  T_SOCK_IPPROTO ipproto ;      /* IP protocol number for this process (TCP or
                                 * UDP); unused with dq. */
  char *server_name ;           /* May be a domain name or an IP address in
                                 * dotted decimal notation. */
  T_SOCK_IPADDR server_ipaddr ; /* Server IP address. (Will be IPADDR_ANY in
                                 * case of AP_TCPSRV.) */
  T_SOCK_PORT server_port ;     /* Server port number. (Also in case of
                                 * AP_TCPSRV.) */

  /* The following variables are in use only where appropriate, of course --
   * as indicated in the comment. */

  int f_id ;                    /* Identity of TCP server fork. */
  int spec_items ;              /* Specified number of items to transfer. (The
                                 * items are single bytes for dl and ul.) */
  int spec_reps ;               /* Specified number of repetitions. */

  int data_sent ;               /* Total amount of data sent (ul, te, ue). */
  int data_rcvd ;               /* Total amount of data recvd (dl, te, ue). */
  int items_sent ;              /* Number of blocks/packets/queries sent (ul,
                                 * te, ue, dq). */
  int items_rcvd ;              /* Number of blocks/packets/responses received
                                 * (dl, te, ue, dq). */
  int n_reps ;                  /* Number of repetitions done. */
  int errors ;                  /* Number of errors at all. */
  T_SOCK_SOCKET psocket ;       /* The socket in use by the process. */
  int network_is_open ;         /* Non-zero iff we have an open network
                                 * connection. */
  int psocket_is_open ;         /* Non-zero iff we have an open psocket. */
  BOOL bearer_only;             /* if set, only a Bearer will be opened */ 
} PROC_CONTEXT_T ;

static PROC_CONTEXT_T proc_context ;
static PROC_CONTEXT_T cl_context[APP_N_CLIENTS] ;
static char server_name[FQDN_LENGTH+1] = APP_DEF_SERVER ;
                                /* Global server name. */
static char query_name[FQDN_LENGTH+1] = APP_DEF_DNSQUERY_ADDR ;
static int buffer_size = APP_DEF_BUFFER_SIZE ;
static U16 port_number = 0 ;    /* Port number override if non-zero. */
static int udp_interval = APP_DEF_UDP_INTERVAL ;
static T_SOCK_BEARER_TYPE bearer_select    = SOCK_BEARER_AS_SPECIFIED;
static T_SOCK_BEARER_TYPE sock_bearer_type = SOCK_BEARER_GPRS;
EXTERN BOOL custom_apn_valid;


/* The cache for DNS queries is RNET_RT_RESOLV_CACHE_MAX queries big, i. e. 8
 * in the current configuration. We need to overflow this cache in order to
 * test lookup robustness. */
static char *domain_name[] = {
#ifdef _SIMULATION_              /* Not in the simulation, though. */
  "chuck.berlin.tide.ti.com",
#else  /* _SIMULATION_ */
  "gsmtest.com",
  "w21.org",
  "gw.w21.org",
  "troll.cs.tu-berlin.de",
  "gatekeeper.dec.com",
  "www.mcvax.org",
  "www.mcvaymedia.com",
  "www.vodafone.de",
  "www.ti.com",
  "mailbox.tu-berlin.de",
  "ge-2-3-0.r02.asbnva01.us.bb.verio.net",
  "www.condat.de",
  "www.tellique.de",
  "prz.tu-berlin.de",
#endif /* _SIMULATION_ */
  0
} ;



/*==== Local functions =======================================================*/

/*
 * Utility functions.
 */


static char *sock_bearer_type_string(T_SOCK_BEARER_TYPE btype)
{
  switch (btype)
  {
    case SOCK_BEARER_ANY: return "SOCK_BEARER_ANY" ;
    case SOCK_BEARER_GPRS: return "SOCK_BEARER_GPRS" ;
    case SOCK_BEARER_GSM: return "SOCK_BEARER_GSM" ;
    case SOCK_BEARER_USE_PROFILE: return "SOCK_BEARER_USE_PROFILE" ;
    case SOCK_BEARER_AS_SPECIFIED: return "SOCK_BEARER_AS_SPECIFIED" ;
    default: return "<unknown bearer type>" ;
  }
}

/** Give a print representation for the specified character. This is the
 * character itself for printable characters and a substitution character for
 * others.
 *
 * @param c    the character
 * @return     the print representation
 */
static char p_char(char c)
{
  return isprint(c) ? c : '~' ;
}


#define DUMP_LLENGTH 16         /* Data dump line length. */
#define DBUF_LENGTH (4 * DUMP_LLENGTH + 10) /* See example below. */
#define XDIGIT(n) ("0123456789abcdef"[n])

/** Dump the specified portion of the data as event traces like this:
 * 0000: 27 28 29 2a 2b 2c 2d 2e-2f 30 31 32 33 34 35 36 ['()*+,-./0123456]
 *
 * @param data    Pointer to data area
 * @param size    Size of data to dump
 */
void trace_dump_data(U8 *data, int size)
{
  char dump_buf[DBUF_LENGTH] ;  /* Buffer to dump a line into. */
  int lcount = 0 ;              /* Line count. */
  int i ;                       /* Index into data. */
  char *cp ;                    /* Pointer to current char in dump_buf[]. */

  while (size > 0)
  {
    cp = dump_buf ;
    /* Hex values. */
    for (i = 0; i < DUMP_LLENGTH && i < size; i++)
    {
      *cp++ = XDIGIT(data[i] >> 4) ;
      *cp++ = XDIGIT(data[i] & 0xf) ;
      *cp++ = (i == DUMP_LLENGTH/2 - 1) ? '-' : ' ' ;
    }

    /* Fill up with blanks. */
    for ( ; i < DUMP_LLENGTH; i++)
    {
      *cp++ = ' ' ; *cp++ = ' ' ; *cp++ = ' ' ;
    }

    /* Literal characters with some decoration. */
    *cp++ = '[' ;
    for (i = 0; i < DUMP_LLENGTH && i < size; i++, cp++)
    {
      *cp = p_char(data[i]) ;
    }
    *cp++ = ']' ;
    *cp++ = 0 ;
    TRACE_EVENT_P2("%04x: %s", DUMP_LLENGTH * lcount++, dump_buf) ;
    size -= DUMP_LLENGTH ;
    data += DUMP_LLENGTH ;
  }
}


/** Build a string, characterizing a process, suitable for tracing. The string
 * is statically allocated and will be overwritten with the next call.
 *
 * @param pcont    Pointer to process context.
 * @return         The string.
 */
char *proc_string(PROC_CONTEXT_T *pcont)
{
  /* This string must fit the longest possible process string. */
  static char procstring[sizeof("AP_TCPFORK99(PS_W_DCM_OPEN_ONLY)")] ;

  /*lint -e685 (Warning -- Relational operator always evaluates to true) */
  sprintf(procstring, "%s%d(%s)",
          pcont->ptype <= AP_INVALID  ? proc_type_name[pcont->ptype] : "AP_UNKNOWN",
          pcont->ptype == AP_TCPFORK  ? pcont->f_id : 0,
          pcont->pstate <= PS_INVALID ? proc_state_name[pcont->pstate] : "PS_UNKNOWN") ;
  /*lint +e685 (Warning -- Relational operator always evaluates to true) */
  return procstring ;
}


/** Converts a numeric IP address in network order into an IP address in
 * dotted decimal string notation. The string returned is statically allocated
 * and will be overwritten on the next call.
 *
 * @param ipaddr    The IP address in network order
 * @return String with the IP address in dotted decimal..
 */
static char *inet_ntoa(T_SOCK_IPADDR ipaddr)
{
  U8 *addrbyte ;
  static char addr_string[sizeof("000.000.000.000")] ;

  addrbyte = (U8 *) &ipaddr ;
  sprintf(addr_string, "%u.%u.%u.%u",
          addrbyte[0], addrbyte[1], addrbyte[2], addrbyte[3]) ;
  return addr_string ;
}


/** Converts an IP address in dotted decimal string notation into a numeric IP
 * address in network order.
 *
 * @param addr_string    String with the IP address in dotted decimal.
 * @return The IP address in network order, or SOCK_IPADDR_ANY if the address
 *         string cannot be parsed.
 */
static T_SOCK_IPADDR inet_aton(char *addr_string)
{
  T_SOCK_IPADDR ipaddr ;
  U8 *addrbyte ;
  int o1, o2, o3, o4 ;

  if (sscanf(addr_string, "%d.%d.%d.%d", &o1, &o2, &o3, &o4) != 4)
  {
    TRACE_EVENT_P1("cannot parse '%s' as an IP address", addr_string) ;
    return SOCK_IPADDR_ANY ;
  }
  addrbyte = (U8 *) &ipaddr ;
  addrbyte[0] = (U8) o1 ;
  addrbyte[1] = (U8) o2 ;
  addrbyte[2] = (U8) o3 ;
  addrbyte[3] = (U8) o4 ;

  return ipaddr ;
}


LOCAL char *sock_result_string(T_SOCK_RESULT result)
{
  switch(result)
  {
    case SOCK_RESULT_OK                : return "SOCK_RESULT_OK";
    case SOCK_RESULT_INVALID_PARAMETER : return "SOCK_RESULT_INVALID_PARAMETER"; 
    case SOCK_RESULT_INTERNAL_ERROR    : return "SOCK_RESULT_INTERNAL_ERROR";
    case SOCK_RESULT_ADDR_IN_USE       : return "SOCK_RESULT_ADDR_IN_USE";
    case SOCK_RESULT_OUT_OF_MEMORY     : return "SOCK_RESULT_OUT_OF_MEMORY";
    case SOCK_RESULT_NOT_SUPPORTED     : return "SOCK_RESULT_NOT_SUPPORTED";
    case SOCK_RESULT_UNREACHABLE       : return "SOCK_RESULT_UNREACHABLE";
    case SOCK_RESULT_CONN_REFUSED      : return "SOCK_RESULT_CONN_REFUSED";
    case SOCK_RESULT_TIMEOUT           : return "SOCK_RESULT_TIMEOUT";
    case SOCK_RESULT_IS_CONNECTED      : return "SOCK_RESULT_IS_CONNECTED";
    case SOCK_RESULT_HOST_NOT_FOUND    : return "SOCK_RESULT_HOST_NOT_FOUND";
    case SOCK_RESULT_DNS_TEMP_ERROR    : return "SOCK_RESULT_DNS_TEMP_ERROR";
    case SOCK_RESULT_DNS_PERM_ERROR    : return "SOCK_RESULT_DNS_PERM_ERROR";
    case SOCK_RESULT_NO_IPADDR         : return "SOCK_RESULT_NO_IPADDR";
    case SOCK_RESULT_NOT_CONNECTED     : return "SOCK_RESULT_NOT_CONNECTED";
    case SOCK_RESULT_MSG_TOO_BIG       : return "SOCK_RESULT_MSG_TOO_BIG";
    case SOCK_RESULT_CONN_RESET        : return "SOCK_RESULT_CONN_RESET";
    case SOCK_RESULT_CONN_ABORTED      : return "SOCK_RESULT_CONN_ABORTED";
    case SOCK_RESULT_NO_BUFSPACE       : return "SOCK_RESULT_NO_BUFSPACE";
    case SOCK_RESULT_NETWORK_LOST      : return "SOCK_RESULT_NETWORK_LOST";
    case SOCK_RESULT_NOT_READY         : return "SOCK_RESULT_NOT_READY";
    case SOCK_RESULT_BEARER_NOT_READY  : return "SOCK_RESULT_BEARER_NOT_READY";
    case SOCK_RESULT_IN_PROGRESS       : return "SOCK_RESULT_IN_PROGRESS";
    case SOCK_RESULT_BEARER_ACTIVE     : return "SOCK_RESULT_BEARER_ACTIVE";
    default                            : return "<INVALID SOCKET RESULT!>";
  }
}



/** Trace a specific socket API result code with some context.
 *
 * @param pcont        Pointer to process context.
 * @param function     The function or event that reported the error.
 * @param result       Socket API result code.
 * @return
 */
static void sock_trace_result(PROC_CONTEXT_T *pcont, char *function,
                              T_SOCK_RESULT result)
{
  if(result NEQ SOCK_RESULT_OK)
  {
    TRACE_ERROR("Sock Result Error");
  }
  TRACE_EVENT_P3("%s: %s for %s", function, 
                 sock_result_string(result), proc_string(pcont));
}


/** Return the string for a Socket API event type. We don't have the values
 * under our (i. e. APP's) own control, so we rather do a switch than indexing
 * an array.
 *
 * @param event_type    Type of the event.
 * @return              String for the event type.
 */
char *sock_event_string(T_SOCK_EVENTTYPE event_type)
{
  switch (event_type)
  {
    case SOCK_CREATE_CNF:        return "SOCK_CREATE_CNF" ;
    case SOCK_CLOSE_CNF:         return "SOCK_CLOSE_CNF" ;
    case SOCK_BIND_CNF:          return "SOCK_BIND_CNF" ;
    case SOCK_LISTEN_CNF:        return "SOCK_LISTEN_CNF" ;
    case SOCK_CONNECT_CNF:       return "SOCK_CONNECT_CNF" ;
    case SOCK_SOCKNAME_CNF:      return "SOCK_SOCKNAME_CNF" ;
    case SOCK_PEERNAME_CNF:      return "SOCK_PEERNAME_CNF" ;
    case SOCK_HOSTINFO_CNF:      return "SOCK_HOSTINFO_CNF" ;
    case SOCK_MTU_SIZE_CNF:      return "SOCK_MTU_SIZE_CNF" ;
    case SOCK_RECV_IND:          return "SOCK_RECV_IND" ;
    case SOCK_CONNECT_IND:       return "SOCK_CONNECT_IND" ;
    case SOCK_CONN_CLOSED_IND:   return "SOCK_CONN_CLOSED_IND" ;
    case SOCK_ERROR_IND:         return "SOCK_ERROR_IND" ;
    case SOCK_FLOW_READY_IND:    return "SOCK_FLOW_READY_IND" ;
    case SOCK_OPEN_BEARER_CNF:   return "SOCK_OPEN_BEARER_CNF";
    case SOCK_CLOSE_BEARER_CNF:  return "SOCK_CLOSE_BEARER_CNF";
    case SOCK_BEARER_INFO_CNF:   return "SOCK_BEARER_INFO_CNF";
    case SOCK_BAERER_CLOSED_IND: return "SOCK_BAERER_CLOSED_IND";
    default:                     return "<INVALID EVENT>" ;
  }
}



/*
 * Process functions.
 */

static void proc_shutdown(PROC_CONTEXT_T *pcont) ;
static void proc_close_socket(PROC_CONTEXT_T *pcont) ;
static void proc_begin_comm(PROC_CONTEXT_T *pcont) ;
static void proc_close_conn(PROC_CONTEXT_T *pcont) ;


/** Switch process to a new state. Done mostly to have a single place to trace
 * process state transitions.
 *
 * @param pcont       Pointer to process context.
 * @param newstate    New state of process.
 */
static void proc_new_state(PROC_CONTEXT_T *pcont, PROC_STAT_T newstate)
{
  if (newstate < PS_INVALID)
  {
    TRACE_EVENT_P2("%s -> %s", proc_string(pcont), proc_state_name[newstate]) ;
    pcont->pstate = newstate ;
  }
  else
  {
    TRACE_EVENT_P2("%s invalid new state %d", proc_string(pcont), newstate) ;
    proc_shutdown(pcont) ;
  }
}


/** Fork a new TCP server process context to handle a TCP client. Return a
 * pointer to the process context or NULL, if no process context is free any
 * more.
 *
 */
static PROC_CONTEXT_T *proc_new_tcpfork(PROC_CONTEXT_T *oldp)
{
    int i ;
    PROC_CONTEXT_T *pcont ;

    TRACE_FUNCTION("proc_new_tcpfork()") ;
    for (i = 0; i < APP_N_CLIENTS; i++)
    {
        if (cl_context[i].ptype EQ AP_NONE)
        {
            break ;
        }
    }
    if (i == APP_N_CLIENTS)
    {
        return NULL ;
    }

    pcont = &cl_context[i] ;
    memset(pcont, 0, sizeof(*pcont)) ;
    pcont->f_id = i ;
    pcont->ptype = AP_TCPFORK ;
    pcont->pstate = PS_IDLE ;
    pcont->ipproto = oldp->ipproto ;
    pcont->server_name = oldp->server_name ;
    pcont->server_ipaddr = oldp->server_ipaddr ;
    pcont->server_port = oldp->server_port ;
    pcont->network_is_open = TRUE ;
    pcont->psocket_is_open = TRUE ;

    return pcont ;
}

/** Free a TCP server process context.
 *
 */
static void proc_free_tcpfork(PROC_CONTEXT_T *pcont)
{
    TRACE_FUNCTION("proc_free_tcpfork()") ;
    proc_new_state(pcont, PS_IDLE) ;
    memset(pcont, 0, sizeof(*pcont)) ;
}


static void proc_init(int prov, int size, int reps, APP_PROCTYPE_T ptype,
                      T_SOCK_IPPROTO ipproto, U16 port)
{
  T_SOCK_BEARER_INFO bearer_info;
  PROC_CONTEXT_T *pcont ;
  BOOL bear_only = proc_context.bearer_only;

  TRACE_FUNCTION("proc_init()") ;

  pcont = &proc_context ;
  if (pcont->pstate != PS_IDLE)
  {
    TRACE_ERROR("proc_init: process still active") ;
    return ;
  }
  memset(pcont, 0, sizeof(*pcont)) ;
  pcont->bearer_only = bear_only;
  pcont->ptype = ptype ;
  pcont->ipproto = ipproto ;
  pcont->server_name = (ptype EQ AP_TCPSRV) ? "<myself>" : server_name ;
  pcont->server_ipaddr = (ptype EQ AP_TCPSRV) ?
    SOCK_IPADDR_ANY : inet_aton(pcont->server_name) ;
  pcont->server_port = HTONS(port) ;
  pcont->spec_items = size ;
  pcont->spec_reps = reps ;
  pcont->in_shutdown = FALSE;

  pcont->psocket = 0;
  pcont->network_is_open = FALSE;
  pcont->psocket_is_open = FALSE;

  TRACE_EVENT_P7("%s for %d bytes %d reps, server %s:%d/%s on %s",
                 proc_string(pcont), pcont->spec_items, pcont->spec_reps,
                 inet_ntoa(pcont->server_ipaddr), NTOHS(pcont->server_port),
                 (ipproto EQ SOCK_IPPROTO_UDP) ? "udp" : "tcp",
                 sock_bearer_type_string(sock_bearer_type)) ;
  app_pstat();

  // fill connection params
  bearer_info.bearer_handle = sock_bearer_handle;
  bearer_info.app_handle    = APP_handle;
  bearer_info.bearer_type   = sock_bearer_type;

  if(sock_bearer_type == SOCK_BEARER_GPRS)
  {
    bearer_info.apn_valid = TRUE;
    bearer_info.phone_nr_valid = FALSE;
    bearer_info.cid = 1;

    switch(prov)
    {
      case APP_PROV_T_MOBILE:
        strcpy(bearer_info.apn, "internet.t-d1.de");
        strcpy(bearer_info.user_id, "t-d1");
        strcpy(bearer_info.password, "gprs");
        break;

      case APP_PROV_HUTCH:
        strcpy(bearer_info.apn, "www");
        strcpy(bearer_info.user_id, "");
        strcpy(bearer_info.password, "");
        break;

      case APP_PROV_AIRTEL:
        strcpy(bearer_info.apn, "airtelgprs.com");
        strcpy(bearer_info.user_id, "");
        strcpy(bearer_info.password, "");
        break;

      case APP_PROV_CUSTOM:
        /* Copy valid APN */
        if(custom_apn_valid)
        {
          strcpy(bearer_info.apn, custom_apn);
          strcpy(bearer_info.user_id, custom_user_id);
          strcpy(bearer_info.password, custom_password);
          break;
        }
        /* Copy default settings for invalid APN settings */
	
      default:
        strcpy(bearer_info.apn,"web.vodafone.de");
        strcpy(bearer_info.user_id, "");
        strcpy(bearer_info.password, "");
        break;
    }
  }
  else
  {
    bearer_info.phone_nr_valid = TRUE;
    bearer_info.apn_valid = FALSE;
    bearer_info.cid = 0;
    if(prov == APP_PROV_T_MOBILE)
    {
      strcpy(bearer_info.phone_nr, "+491712524120");
      strcpy(bearer_info.user_id, "t-d1");
      strcpy(bearer_info.password, "wap");
    }
    else
    {
      strcpy(bearer_info.phone_nr, "+491722290000");
      strcpy(bearer_info.user_id, "");
      strcpy(bearer_info.password, "");
    }
  }
  bearer_info.user_id_valid = TRUE;
  bearer_info.password_valid = TRUE;

  bearer_info.ip_address = SOCK_IPADDR_ANY;
  bearer_info.dns1 = SOCK_IPADDR_ANY;
  bearer_info.dns2 = SOCK_IPADDR_ANY;
  bearer_info.gateway = SOCK_IPADDR_ANY;
  bearer_info.authtype = SOCK_AUTH_NO;
  bearer_info.data_compr = FALSE;
  bearer_info.header_comp = FALSE;
  bearer_info.precedence = 0;
  bearer_info.delay = 0;
  bearer_info.reliability = 0;
  bearer_info.peak_throughput = 0;
  bearer_info.mean_througput = 0;
  bearer_info.shareable = FALSE;

  sock_open_bearer(sock_api_inst,bearer_select,0,&bearer_info,app_sock_callback,pcont);
  if(pcont->bearer_only)
  {
    proc_new_state(pcont, PS_W_DCM_OPEN_ONLY) ;
  }
  else
  {
    proc_new_state(pcont, PS_W_DCM_OPEN) ;
  }
}


static void proc_client_closed(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;
  TRACE_FUNCTION("proc_client_closed()") ;
  
  result = sock_close(pcont->psocket);
  if (result != SOCK_RESULT_OK)
  {
    TRACE_EVENT_P1("%s: error closing client socket", proc_string(pcont)) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_free_tcpfork(pcont) ;
}



/***********************************************************************
 * Communication functions.
 */


/** Fill and send data buffer.
 *
 * @param pcont        Pointer to process context.
 * @param size         Size of data buffer.
 * @return
 */
static BOOL comm_send_buffer(PROC_CONTEXT_T *pcont, int size)
{
  char *payload ;               /* Pointer to payload buffer. */
  char *cp ;                    /* Pointer into paylaod buffer. */
  char *pp ;                    /* Pointer into test pattern. */
  T_SOCK_RESULT result ;        /* Result of send call. */

  TRACE_FUNCTION("comm_send_buffer()") ;
  MALLOC(payload, size) ;
  TRACE_EVENT_P1("PALLOC payload %x", payload) ;
  /* Fill buffer with pattern. */
  for (cp = payload, pp = APP_SEND_PATTERN; cp < payload + size; cp++, pp++)
  {
    if (pp >= APP_SEND_PATTERN + sizeof(APP_SEND_PATTERN) - 1)
    {
      pp = APP_SEND_PATTERN ;
    }
    *cp = *pp ;
  }
  if(pcont->ipproto == SOCK_IPPROTO_UDP)
  {
    // use UDP socket and specify destination IP address and destination port
    result = sock_sendto(pcont->psocket, payload, (U16)size,
                         pcont->server_ipaddr,pcont->server_port) ;
  }
  else
  {
    result = sock_send(pcont->psocket, payload, (U16)size) ;
  }
  sock_trace_result(pcont, "sock_send()", result) ;
  MFREE(payload) ;
  switch (result)
  {
    case SOCK_RESULT_OK:
      TRACE_EVENT_P6("%s sent %d (%d/%d) bytes in rep %d/%d",
                     proc_string(pcont), size, pcont->data_sent,
                     pcont->spec_items,
                     //* ((pcont->ipproto EQ SOCK_IPPROTO_TCP) ? 1 : size),
                     pcont->n_reps, pcont->spec_reps) ;
      return TRUE ;
    case SOCK_RESULT_NO_BUFSPACE:
      return FALSE ;            /* Pause until SOCK_FLOW_READY_IND. */
    default:
      proc_shutdown(pcont) ;
      return FALSE ;
  }
}


/** Server: send some data to the client.
 *
 * @param pcont        Pointer to process context.
 * @return
 */
static BOOL comm_send_srvprompt(PROC_CONTEXT_T *pcont)
{
  char *payload ;               /* Pointer to payload buffer. */
  int size ;                    /* Actual size of payload. */
  T_SOCK_RESULT result ;        /* Result of send call. */
  TRACE_FUNCTION("comm_send_srv()") ;

  MALLOC(payload, 600) ;
  sprintf(payload, "%s: %sin_shutdown, last_evt %s, will %srepeat\n",
          proc_string(pcont), pcont->in_shutdown ? "" : "not ",
          pcont->last_evt
          ? sock_event_string(pcont->last_evt->event_type) : "NULL",
          pcont->spec_reps ? "" : "not ") ;
  sprintf(payload + strlen(payload),
          "    rx %d B %d pkts, tx %d B %d pkts, errs %d conn %d\n",
          pcont->data_rcvd, pcont->items_rcvd,
          pcont->data_sent, pcont->items_sent,
          pcont->errors, pcont->n_reps) ;

  size = strlen(payload) ;
  result = sock_send(pcont->psocket, payload, (U16)size) ;
  sock_trace_result(pcont, "sock_send()", result) ;
  MFREE(payload) ;
  switch (result)
  {
    case SOCK_RESULT_OK:
      pcont->data_sent += size ;
      pcont->items_sent++ ;
      TRACE_EVENT_P5("%s sent %d (%d/%d) bytes in conn %d",
                     proc_string(pcont), size, pcont->data_sent,
                     pcont->spec_items * size,
                     pcont->n_reps) ;
      return TRUE ;
    case SOCK_RESULT_NO_BUFSPACE:
      return FALSE ;            /* Pause until SOCK_FLOW_READY_IND. */
    default:
      proc_shutdown(pcont) ;
      return FALSE ;
  }
}


/** Issue a DNS query. Called for AP_DNSQRY in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_query(PROC_CONTEXT_T *pcont)
{
  static int next_query = 0 ;   /* Next query index. */
  char *name ;                  /* Domain name to query for. */
  T_SOCK_RESULT result ;        /* Result of query call. */

  TRACE_FUNCTION("comm_query()") ;
  if (query_name[0])
  {
    name = query_name ;
  }
  else
  {
    name = domain_name[next_query] ;
  }
  TRACE_EVENT_P4("%s: query (%d/%d) for %s", proc_string(pcont),
                 pcont->items_sent + 1, pcont->spec_items, name) ;
  result = sock_gethostbyname(sock_api_inst, name, app_sock_callback, pcont) ;
  sock_trace_result(pcont, "sock_gethostbyname()", result) ;
  if (result != SOCK_RESULT_OK)
  {
    pcont->errors++ ;
    TRACE_ERROR("sock_gethostbyname() failed, sleep...") ;
    vsi_t_sleep(VSI_CALLER 2000) ;
  }

  if (!domain_name[++next_query])
  {
    next_query = 0 ;
  }
  pcont->items_sent++ ;
}



/** Send data. Called for all but AP_DNSQRY in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_send(PROC_CONTEXT_T *pcont)
{
  TRACE_EVENT_P1("comm_send() %s", proc_string(pcont)) ;

  switch (pcont->ptype)
  {
    case AP_TCPDL:
      /* Do nothing -- the server will send again anyway. */
      return ;
    case AP_UDPDL:
      if (pcont->data_sent >= pcont->spec_items)
      {
        return;
      }
      break;
    case AP_TCPUL:
    case AP_UDPUL:
      if (pcont->data_sent >= pcont->spec_items)
      {
        TRACE_EVENT_P2("%s done after %d bytes",
                       proc_string(pcont), pcont->data_sent) ;
        proc_close_socket(pcont) ;
        pcont->n_reps++ ;
        return ;
      }
      break ;
    case AP_TCPECHO:
    case AP_UDPECHO:
      if (pcont->items_sent >= pcont->spec_items)
      {
        TRACE_EVENT_P2("%s done after %d writes",
                       proc_string(pcont), pcont->items_sent) ;
        proc_close_socket(pcont) ;
        pcont->n_reps++ ;
        return ;
      }
      break ;
    case AP_DNSQRY:
      comm_query(pcont) ;
      return ;
    case AP_TCPFORK:             /* Send some data, perhaps. */
      switch (pcont->server_port)
      {
        case PORT_CHARGEN:      /* Send something (below). */
          break ;
        case PORT_ECHO:         /* Send somewhere else. */
        case PORT_DISCARD:      /* Don't send anything. */
          return ;
        default:                /* Send a server prompt. */
          comm_send_srvprompt(pcont) ;
          break ;
      }
      return ;
    case AP_NONE:
    case AP_INVALID:
    default:
      TRACE_EVENT_P1("Invalid process type %s", proc_string(pcont)) ;
      return ;
  }

  if (comm_send_buffer(pcont, buffer_size))
  {
    pcont->items_sent++ ;
    TRACE_EVENT_P1("Sent Items: %u",pcont->items_sent);
    pcont->data_sent += buffer_size ;
    vsi_t_sleep(VSI_CALLER udp_interval);
  }
  else
  {
    if (pcont->ptype EQ AP_UDPUL)
    {
      TRACE_EVENT_P2("%s sleeps %d ms", proc_string(pcont), udp_interval) ;
      vsi_t_sleep(VSI_CALLER udp_interval);
    }
  }
}


/** Handle an incoming DNS result. Called for AP_DNSQRY in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_dns_result(PROC_CONTEXT_T *pcont)
{
  T_SOCK_HOSTINFO_CNF *hinfo ;

  TRACE_FUNCTION("comm_dns_result()") ;
  pcont->items_rcvd++ ;

  hinfo = (T_SOCK_HOSTINFO_CNF *) pcont->last_evt ;
  if (hinfo->result != SOCK_RESULT_OK)
  {
    TRACE_EVENT_P3("lookup error %d in %d/%d queries",
                   pcont->errors, pcont->items_rcvd, pcont->spec_items) ;
  }
  else
  {
    TRACE_EVENT_P1("Answer for host %s", hinfo->hostname) ;
    TRACE_EVENT_P3("has address %s (%d/%d)",
                   inet_ntoa(hinfo->ipaddr),
                   pcont->items_rcvd, pcont->spec_items) ;
  }
  if (pcont->items_rcvd < pcont->spec_items)
  {
    comm_send(pcont) ;
  }
  else
  {
    proc_close_conn(pcont) ;
  }
}


/** Receive incoming data. Called for all but AP_TCPUL in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_recv(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RECV_IND *recv_ind = (T_SOCK_RECV_IND *) pcont->last_evt ;

  TRACE_FUNCTION("comm_recv()") ;
  if (pcont->ptype EQ AP_DNSQRY OR pcont->ptype EQ AP_TCPUL)
  {
    TRACE_EVENT_P2("%s: %s unexpected for ptype", proc_string(pcont),
                   sock_event_string(pcont->last_evt->event_type)) ;
    proc_shutdown(pcont) ;
    return ;
  }
  pcont->data_rcvd += recv_ind->data_length ;
  pcont->items_rcvd++ ;
  TRACE_EVENT_P5("%s: recv #%d:%u bytes, total %u, total items sent:%u", proc_string(pcont),
                 pcont->items_rcvd, recv_ind->data_length, pcont->data_rcvd,pcont->items_sent);
  trace_dump_data((U8 *) recv_ind->data_buffer,
                  MIN(APP_DATA_DUMP_LENGTH, recv_ind->data_length)) ;
  MFREE(recv_ind->data_buffer) ;
  recv_ind->data_buffer = 0 ;
  switch (pcont->ptype)
  {
    case AP_UDPDL:
      /* After every sent UDP packet, a "answer" comes from the Chargen server. 
       * If all packets are sent we are waiting for the last packet to 
       * receive, else an unexpected event would be the result in the 
       * "app_sock_callback()"; TCPIP_DATA_IND is received instead of SOCK_CLOSE_CNF
       * TODO: why (pcont->items_sent-1), I assume that the server "confirms"
       *       every packet
       */
      if ((pcont->data_sent >= pcont->spec_items) &&
          (pcont->items_rcvd == pcont->items_sent))
      {
        TRACE_EVENT("last UDP-DL packet received");
        pcont->n_reps++ ;
        proc_close_socket(pcont) ;
      }
      else {
        comm_send(pcont);
      }
      break;
    case AP_TCPDL:
      if (pcont->data_rcvd >= pcont->spec_items)
      {
        TRACE_EVENT_P3("%s done after %d/%d bytes",
                       proc_string(pcont), pcont->data_rcvd,
                       pcont->spec_items) ;
        pcont->n_reps++ ;
        proc_close_socket(pcont) ;
      }
      break ;
    case AP_UDPECHO:
    case AP_TCPECHO:
    case AP_TCPFORK:
      comm_send(pcont) ;
      break ;
    default:
      TRACE_ERROR("Unexpected ptype in comm_recv()") ;
      break ;
  }
}


/** Handle a communication event according to the process type. Called for all
 * process types in state PS_COMM.
 *
 * @param pcont        Pointer to process context.
 */
static void comm_event(PROC_CONTEXT_T *pcont)
{
  TRACE_FUNCTION("comm_event()") ;

  switch (pcont->last_evt->event_type)
  {
    case SOCK_CONN_CLOSED_IND:
      if (pcont->ptype EQ AP_TCPFORK)
      {
        proc_client_closed(pcont) ;
        break ;
      }
      /*lint -fallthrough */
    case SOCK_ERROR_IND:
      TRACE_EVENT_P2("%s: %s, shutdown", proc_string(pcont),
                     sock_event_string(pcont->last_evt->event_type)) ;
      // server should not reset , even if connection is reset by client.
      // but client should shutdown , if connection is reset by server.
      if((pcont->ptype EQ AP_TCPFORK)  AND
         (pcont->last_evt->result == SOCK_RESULT_CONN_RESET OR
          pcont->last_evt->result == SOCK_RESULT_TIMEOUT))
      {
         proc_client_closed(pcont) ;
         return;
      }
      else
        proc_shutdown(pcont) ;
      return ;
    case SOCK_RECV_IND:
      comm_recv(pcont) ;
      break ;
    case SOCK_FLOW_READY_IND:
      if(pcont->ptype NEQ AP_UDPDL) {
        comm_send(pcont) ;
      }
      break ;
    case SOCK_HOSTINFO_CNF:
      if (pcont->ptype EQ AP_DNSQRY) {
        comm_dns_result(pcont) ;
        break ;
      }
      /*lint -fallthrough */
    case SOCK_CREATE_CNF:
    case SOCK_CLOSE_CNF:
    case SOCK_BIND_CNF:
    case SOCK_LISTEN_CNF:
    case SOCK_CONNECT_CNF:
    case SOCK_SOCKNAME_CNF:
    case SOCK_PEERNAME_CNF:
    case SOCK_MTU_SIZE_CNF:
    case SOCK_CONNECT_IND:
      TRACE_EVENT_P2("%s: %s unexpected at all", proc_string(pcont),
                     sock_event_string(pcont->last_evt->event_type)) ;
      proc_shutdown(pcont) ;
      return ;
    case SOCK_BAERER_CLOSED_IND:
      proc_shutdown(pcont);
      break;
    default:
      TRACE_EVENT_P2("comm_event(): %s unknown event %d",
                     proc_string(pcont), pcont->last_evt->event_type) ;
      proc_shutdown(pcont) ;
      break ;
  }
}



/***********************************************************************
 * State machine functions (i. e. state-changing functions)
 */

/** Finish the process after the network connection has been closed.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_finish(PROC_CONTEXT_T *pcont)
{
  TRACE_EVENT_P1("%s finished", proc_string(pcont)) ;
  pcont->network_is_open = FALSE ;
  pcont->in_shutdown = FALSE;
  proc_new_state(pcont, PS_IDLE) ;
}


/** Shutdown process hard, usually after an error or user request. This
 * includes closing the process's socket and network connection.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_shutdown(PROC_CONTEXT_T *pcont)
{
  TRACE_FUNCTION("proc_shutdown()") ;

  if(pcont->in_shutdown)
  {
    TRACE_EVENT("Allready in shutdown");
    return;
  }
  pcont->in_shutdown = TRUE ;
  app_pstat() ;
  if (pcont->psocket_is_open)
  {
    proc_close_socket(pcont);
    return;
  }
  if (pcont->network_is_open OR
      pcont->pstate == PS_W_DCM_OPEN OR pcont->pstate == PS_W_DCM_OPEN_ONLY)
  {
    proc_close_conn(pcont);
    return;
  }
  else
  {
    proc_finish(pcont);
  }
}


/** Create a socket after the network connection has been established.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_open_socket(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;

  TRACE_FUNCTION("proc_open_socket()") ;
  /* We don't need to do this for the DNS query process. */
  if (pcont->ptype EQ AP_DNSQRY)
  {
    proc_begin_comm(pcont) ;
  }
  else
  {
    result = sock_create(sock_api_inst, pcont->ipproto, app_sock_callback, pcont);
    if (result NEQ SOCK_RESULT_OK)
    {
      sock_trace_result(pcont, "sock_create()", result) ;
      proc_shutdown(pcont) ;
      return;
    }
    proc_new_state(pcont, PS_W_CREAT) ;
  }
}


/** Close the network connection after the task has been done.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_close_conn(PROC_CONTEXT_T *pcont)
{
  

  TRACE_FUNCTION("proc_close_conn()");
  if(pcont->network_is_open)
  {
    pcont->in_shutdown = TRUE;
    sock_close_bearer(sock_api_inst, sock_bearer_handle, app_sock_callback, pcont);
    proc_new_state(pcont, PS_W_DCLOS) ;
  }
  else
  {
    proc_finish(pcont);
  }
}


/** Connect the socket after it has been created.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_connect_socket(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;

  TRACE_FUNCTION("proc_connect_socket()") ;
  /* If we do not yet have an IP address to connect to, look it up first. */
  if (pcont->server_ipaddr EQ SOCK_IPADDR_ANY)
  {
    result = sock_gethostbyname(sock_api_inst, pcont->server_name,app_sock_callback, pcont);
    if (result NEQ SOCK_RESULT_OK)
    {
      sock_trace_result(pcont, "sock_gethostbyname()", result) ;
      proc_shutdown(pcont) ;
      return;
    }
    proc_new_state(pcont, PS_W_DNS) ;
    return ;
  }

  result = sock_connect(pcont->psocket, pcont->server_ipaddr, pcont->server_port);
  if (result NEQ SOCK_RESULT_OK)
  {
    sock_trace_result(pcont, "sock_connect()", result) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_new_state(pcont, PS_W_SCONN) ;
}


/** Begin communicating after the socket has been created.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_begin_comm(PROC_CONTEXT_T *pcont)
{
  TRACE_FUNCTION("proc_begin_comm()") ;

  proc_new_state(pcont, PS_COMM) ;
  switch (pcont->ptype)
  {
    case AP_TCPDL:
      /* We wait for data from the server to arrive. */
      break ;
    case AP_UDPDL:
      /* Trigger the chargen server to send fisrt UDP packet */
       comm_send(pcont);
      break ;
    case AP_TCPUL:
    case AP_UDPUL:
    case AP_TCPECHO:
    case AP_UDPECHO:
    case AP_DNSQRY:
    case AP_TCPFORK:
      comm_send(pcont) ;
      break ;
    default:
      TRACE_EVENT_P2("%s unknown state (%d)",
                     proc_string (pcont), pcont->ptype) ;
      break ;
  }
}


/** Close the socket after the requested communication has been done.
 *
 * @param pcont       Pointer to process context.
 */
static void proc_close_socket(PROC_CONTEXT_T *pcont)
{
  TRACE_FUNCTION("proc_close_socket()") ;

  sock_close(pcont->psocket) ;
  proc_new_state(pcont, PS_W_SCLOS) ;
}


static void proc_bind_socket(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;
  TRACE_FUNCTION("proc_bind_socket()") ;

  if ((result = sock_bind(pcont->psocket, pcont->server_port))
      != SOCK_RESULT_OK)
  {
    sock_trace_result(pcont, "sock_bind()", result) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_new_state(pcont, PS_W_BIND) ;
}


static void proc_listen(PROC_CONTEXT_T *pcont)
{
  T_SOCK_RESULT result ;
  TRACE_FUNCTION("proc_listen()") ;

  if ((result = sock_listen(pcont->psocket)) != SOCK_RESULT_OK)
  {
    sock_trace_result(pcont, "sock_listen()", result) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_new_state(pcont, PS_W_LISTN) ;
}


static void proc_incoming(PROC_CONTEXT_T *pcont)
{
  T_SOCK_CONNECT_IND *conn_ind ;
  PROC_CONTEXT_T *newp ;
  T_SOCK_RESULT result ;
  TRACE_FUNCTION("proc_incoming()") ;

  conn_ind = (T_SOCK_CONNECT_IND *) pcont->last_evt ;

  if ((newp = proc_new_tcpfork(pcont)) EQ NULL)
  {
      TRACE_EVENT_P1("%s: failed to fork server, close new socket",
                    proc_string(pcont)) ;
      sock_close(conn_ind->new_socket) ;
      return ;
  }

  /* We cannot make two calls to proc_string() without one overwriting the
   * other, so we print the process strings in two successive traces. */
  TRACE_EVENT_P1("%s: forking to handle client connection...",
                 proc_string(pcont)) ;
  TRACE_EVENT_P1("...forked process is %s", proc_string(newp)) ;
  newp->psocket = conn_ind->new_socket ;
  sock_set_callback(newp->psocket, app_sock_callback, newp) ;
  TRACE_EVENT_P3("%s connection from %s:%d, looking up...", proc_string(pcont),
                 inet_ntoa(conn_ind->peer_ipaddr), NTOHS(conn_ind->peer_port)) ;
  if ((result = sock_gethostbyaddr(sock_api_inst, conn_ind->peer_ipaddr,
                                   app_sock_callback, newp))
      != SOCK_RESULT_OK)
  {
    sock_trace_result(newp, "sock_gethostbyaddr()", result) ;
    proc_shutdown(newp) ;
    proc_shutdown(pcont) ;
    return;
  }
  proc_new_state(newp, PS_W_DNS) ;
}


static void proc_hostinfo_recvd(PROC_CONTEXT_T *pcont)
{
  T_SOCK_HOSTINFO_CNF *hinfo ;
  TRACE_FUNCTION("proc_hostinfo_recvd()") ;

  hinfo = (T_SOCK_HOSTINFO_CNF *) pcont->last_evt ;
  if (hinfo->result != SOCK_RESULT_OK)
  {
    sock_trace_result(pcont, "SOCK_HOSTINFO_CNF", hinfo->result) ;
  }
  else
  {
    TRACE_EVENT_P3("%s: connected peer is %s (%s)", proc_string(pcont),
                   hinfo->hostname, inet_ntoa(hinfo->ipaddr)) ;
  }
  proc_begin_comm(pcont) ;
}



/*==== Exported functions ====================================================*/


/** Initialize the application core.
 *
 * @param handle    own communication handle
 * @return PEI_OK/PEI_ERROR depending on the success of the initialization.
 */
BOOL app_initialize_tcpip(T_HANDLE app_handle)
{
  TRACE_FUNCTION("app_initialize_tcpip()") ;
  memset(&proc_context, 0, sizeof(proc_context)) ;
  return PEI_OK ;
}


/* Macro for checking the Socket API events in app_sock_callback(). */
#define CHECK_SOCK_EVT(evttype)                                       \
{                                                                     \
  if (event->event_type != evttype)                                   \
  {                                                                   \
    TRACE_ERROR("unexpected event type waiting for " #evttype) ;      \
    proc_shutdown(pcont) ;                                            \
    break ;                                                           \
  }                                                                   \
  if (event->result != SOCK_RESULT_OK)                                \
  {                                                                   \
    if(pcont->pstate == PS_W_DCM_OPEN OR                              \
       pcont->pstate == PS_W_DCM_OPEN_ONLY)                           \
       { proc_new_state(pcont, PS_IDLE); }                            \
    proc_shutdown(pcont) ;                                            \
    break ;                                                           \
  }                                                                   \
}


/** Socket callback function as specified in the Socket API.
 *
 * @param event      Pointer to event struct passed by API.
 * @param context    Pointer to application context (here: process context)
 * @return
 */
void app_sock_callback(T_SOCK_EVENTSTRUCT *event, void *context)
{
  PROC_CONTEXT_T *pcont ;
  T_SOCK_BEARER_INFO_CNF *info;

  TRACE_FUNCTION("app_sock_callback()") ;

  pcont = (PROC_CONTEXT_T *)context ;
  pcont->last_evt = event ;     /* Save event in process context. */

  sock_trace_result(pcont, sock_event_string(event->event_type),
                    event->result) ;
  if (event->result != SOCK_RESULT_OK)
  {
    pcont->errors++ ;
    if(event->result == SOCK_RESULT_NETWORK_LOST)
    {
      pcont->network_is_open = FALSE;
    }
  }
  switch (pcont->pstate)        /* Do a preliminary check of the event. */
  {
    case PS_W_DCM_OPEN:            /* Waiting for DCM to open connection. */
      CHECK_SOCK_EVT(SOCK_OPEN_BEARER_CNF);
      
      // FST: can't be evaluated-> see makro CHECK_SOCK_EVT
      if (event->result != SOCK_RESULT_OK AND 
          event->result != SOCK_RESULT_BEARER_ACTIVE)
      {
        proc_shutdown(pcont) ;
        return ;
      }
      if (pcont->network_is_open)
      {
        TRACE_ERROR("SOCK_OPEN_BEARER_CNF received but pcont->network_is_open") ;
        proc_shutdown(pcont);
        return ;
      }
      pcont->network_is_open = TRUE ;
      proc_open_socket(pcont) ;
      break;
      
    case PS_W_DCM_OPEN_ONLY:
      CHECK_SOCK_EVT(SOCK_OPEN_BEARER_CNF);
      pcont->network_is_open = TRUE ;
      proc_new_state(pcont, PS_DCM_OPEN);
      break;

    case PS_W_DCLOS:            /* Waiting for DCM to close connection. */
      CHECK_SOCK_EVT(SOCK_CLOSE_BEARER_CNF);

      if (!pcont->network_is_open AND pcont->pstate != PS_IDLE)
      {
        TRACE_ERROR("DCM_CONN_CLOSED received but !pcont->network_is_open") ;
        proc_shutdown(pcont) ;
        return ;
      }
      proc_finish(pcont) ;
      break;

    case PS_W_CONN_INFO:
      CHECK_SOCK_EVT(SOCK_BEARER_INFO_CNF);

      info = (T_SOCK_BEARER_INFO_CNF *)event;
      app_print_conn_info(info);

      TRACE_EVENT("SOCK_BEARER_INFO_CNF received");
      break;

    case PS_W_CREAT:            /* Waiting for socket create confirmation. */
      CHECK_SOCK_EVT(SOCK_CREATE_CNF) ;
      pcont->psocket = event->socket ;
      pcont->psocket_is_open = TRUE ;
      if (pcont->ptype EQ AP_TCPSRV)
      {
        proc_bind_socket(pcont) ;
      }
      else if(pcont->ipproto == SOCK_IPPROTO_TCP)
      {
        proc_connect_socket(pcont) ;
      }
      else
      {
        // This is not possible in the moment because the RNET_API does not 
        // provide a sendto() function. Therefore it is is only possible to sent
        // via "connected" UDP sockets.
        // TODO: if the next statement will be enabled the "proc_connect_socket()" has to be removed!!
        // proc_begin_comm(pcont);
        proc_connect_socket(pcont) ;
      }
      break ;

    case PS_W_BIND:
      CHECK_SOCK_EVT(SOCK_BIND_CNF) ;
      proc_listen(pcont) ;
      break ;

    case PS_W_LISTN:
      CHECK_SOCK_EVT(SOCK_LISTEN_CNF) ;
      app_pstat() ;
      proc_new_state(pcont, PS_LISTENS) ; /* Nothing more to do here. */
      break ;

    case PS_LISTENS:            /* SOCK_CONNECT_IND or SOCK_CLOSE_CNF */
      if (event->event_type EQ SOCK_CONNECT_IND)
      {
        proc_incoming(pcont) ;
      }
      break ;

    case PS_W_DNS:
      /* After sending connect confirm to client, client will send data to server
         and server is dnsquerying now , server will be shutdown ,here Unfortunately..
         but we want to exchange data happily, so this code is added....  */
      if((event->event_type == SOCK_RECV_IND) AND (pcont->ptype == AP_TCPFORK))
         break;

      CHECK_SOCK_EVT(SOCK_HOSTINFO_CNF) ;
      proc_hostinfo_recvd(pcont) ;
      break ;

    case PS_W_SCONN:            /* Waiting for socket connect confirmation. */
      CHECK_SOCK_EVT(SOCK_CONNECT_CNF) ;
      proc_begin_comm(pcont) ;
      break ;

    case PS_COMM:               /* Happily exchanging data. */
      comm_event(pcont) ;
      break ;

    case PS_W_SCLOS:            /* Waiting for socket close confirmation. */
      CHECK_SOCK_EVT(SOCK_CLOSE_CNF) ;
                                                                     
      pcont->psocket_is_open = FALSE ;
      pcont->psocket = 0;
      app_pstat() ;
      if (pcont->n_reps >= pcont->spec_reps OR
          pcont->in_shutdown)
      {        
        proc_close_conn(pcont) ;
      }
      else
      {
        pcont->data_sent = 0 ;
        pcont->data_rcvd = 0 ;
        pcont->items_sent = 0 ;
        pcont->items_rcvd = 0 ;
        proc_open_socket(pcont) ;
      }
      break ;

    case PS_IDLE:               /* Initial state, process not running. */
      TRACE_EVENT_P2("app_sock_callback(): %s receives %s (ignored)",
                     proc_string(pcont), sock_event_string(event->event_type)) ;
      break ;

    case PS_DCM_OPEN:
      if(event->event_type == SOCK_BAERER_CLOSED_IND)
      {
        TRACE_ERROR("SOCK_BAERER_CLOSED_IND -> Shutdown");
        if(event->result == SOCK_RESULT_NETWORK_LOST)
        {
          pcont->network_is_open = FALSE;
        }
        proc_shutdown(pcont) ;
      }
      break;
    
    case PS_INVALID:            /* Invalid state. */
      TRACE_EVENT_P1("app_sock_callback(): %s invalid state", proc_string(pcont)) ;
      break;

    default:
      TRACE_ERROR("app_sock_callback(): Default Statement");
      break;
      /*
      if(event->event_type == SOCK_DCM_ERR_IND)
      {
        TRACE_ERROR("SOCK_DCM_ERR_IND -> Shutdown");
      }
      proc_shutdown(pcont) ;
      return ;
      */
  }
  /* Free data buffer if it has not been freed yet. */
  if ( (event->event_type EQ SOCK_RECV_IND) AND
       ((T_SOCK_RECV_IND *) event)->data_buffer )
  {
    MFREE(((T_SOCK_RECV_IND *) event)->data_buffer) ;
  }
  pcont->last_evt = NULL ;
  TRACE_EVENT_P1("leave app_sock_callback() for %s", proc_string(pcont)) ;
}



/*
 * Application command functions.
 */


/** Start a data communication process of the appropriate type..
 *
 * @param size     Amount of data to download or number of items to transfer.
 */
void app_start_tcpdl(int prov, int size, int reps)
{ proc_init(prov, size, reps, AP_TCPDL, SOCK_IPPROTO_TCP,
            port_number ? port_number : PORT_CHARGEN) ; }

void app_start_tcpul(int prov, int size, int reps)
{ proc_init(prov, size, reps, AP_TCPUL, SOCK_IPPROTO_TCP,
            port_number ? port_number : PORT_DISCARD) ; }

void app_start_udpdl(int prov, int size, int reps)
{ proc_init(prov, size, reps, AP_UDPDL, SOCK_IPPROTO_UDP,
            port_number ? port_number : PORT_CHARGEN) ; }

void app_start_udpul(int prov, int size, int reps)
{ proc_init(prov, size, reps, AP_UDPUL, SOCK_IPPROTO_UDP,
            port_number ? port_number : PORT_DISCARD) ; }

void app_start_tcpecho(int prov, int items, int reps)
{ proc_init(prov, items, reps, AP_TCPECHO, SOCK_IPPROTO_TCP,
            port_number ? port_number : PORT_ECHO) ; }

void app_start_udpecho(int prov, int items, int reps)
{ proc_init(prov, items, reps, AP_UDPECHO, SOCK_IPPROTO_UDP,
            port_number ? port_number : PORT_ECHO) ; }

void app_start_dnsquery(int prov, int times, char *address)
{
  if (address)
  {
    strncpy(query_name, address, FQDN_LENGTH) ;
  }
  else
  {
    query_name[0] =0 ;
  }
  proc_init(prov, times,1, AP_DNSQRY,(T_SOCK_IPPROTO)0, 0) ;
}

void app_start_tcpsrv(int prov, int port, int repeat)
{ proc_init(prov, 0, repeat, AP_TCPSRV, SOCK_IPPROTO_TCP, port) ; }



/** Shutdown the specified process.
 *
 * @param pid    Process ID.
 */
void app_shutdown(void)
{
  TRACE_FUNCTION("app_shutdown()") ;
  proc_shutdown(&proc_context) ;
}


/** Set the current server name or IP address.
 *
 * @param server    Name or IP address (in dotted decimal notation) of server.
 */
void app_server(char *server)
{
  if (server)
  {
    strncpy(server_name, server, FQDN_LENGTH) ;
  }
  TRACE_EVENT_P1("server_name is %s", server_name) ;
}


/** Set or show the current buffer size.
 *
 * @param bufsize    size of buffer as a string or NULL
 */
void app_buffer(char *bufsize)
{
  if (bufsize)
  {
    buffer_size = atoi(bufsize) ;
  }
  TRACE_EVENT_P1("buffer_size is %d", buffer_size) ;
}


/** Set or show the current buffer size.
 *
 * @param port     port number override
 */
void app_port(char *port)
{
  if (port)
  {
    port_number = (U16) atoi(port) ;
  }
  if (port_number)
  {
    TRACE_EVENT_P1("port number override is %d", port_number) ;
  }
  else
  {
    TRACE_EVENT("standard port numbers used") ;
  }
}


/** Set or show the current bearer type.
 *
 * @param bearer     bearer type
 */
void app_bearer(char *bearer)
{
  if (bearer)
  {
    if (!strcmp(string_to_lower(bearer), "any"))
    {
      sock_bearer_type = SOCK_BEARER_ANY;
    }
    else if (!strcmp(string_to_lower(bearer), "gprs"))
    {
      sock_bearer_type = SOCK_BEARER_GPRS ;
    }
    else if (!strcmp(string_to_lower(bearer), "gsm"))
    {
      sock_bearer_type = SOCK_BEARER_GSM;
    }
    else if (!strcmp(string_to_lower(bearer), "prof"))
    {
      sock_bearer_type = SOCK_BEARER_USE_PROFILE;
    }    
    else if (!strcmp(string_to_lower(bearer), "spec"))
    {
      sock_bearer_type = SOCK_BEARER_AS_SPECIFIED;
    }

    else
    {
      TRACE_EVENT_P1("bearer type %s unknown", bearer) ;
    }
  }
  TRACE_EVENT_P1("bearer type is %s", sock_bearer_type_string(sock_bearer_type)) ;
}


/** Trace information about the process.
 */
void app_pstat(void)
{
  PROC_CONTEXT_T *pcont ;

  TRACE_FUNCTION("app_pstat()") ;

  pcont = &proc_context ;
  TRACE_EVENT_P3("%s in_shutdown %d last_evt %08x",
                 proc_string(pcont), pcont->in_shutdown, pcont->last_evt) ;
  TRACE_EVENT_P6("prot %d srv %s %s:%d sp_it %d sp_rep %d",
                 pcont->ipproto,
                 pcont->server_name ? pcont->server_name : "",
                 inet_ntoa(pcont->server_ipaddr),
                 NTOHS(pcont->server_port),
                 pcont->spec_items,
                 pcont->spec_reps) ;
  TRACE_EVENT_P6("dta tx %d dta rx %d it tx %d it rx %d rep %d errs %d",
                 pcont->data_sent,
                 pcont->data_rcvd,
                 pcont->items_sent,
                 pcont->items_rcvd,
                 pcont->n_reps,
                 pcont->errors) ;
  TRACE_EVENT_P5("Socket descr: %x, %sNetwork%s, %spSocket%s",
                 pcont->psocket,
                 pcont->network_is_open ? "" : "no ",
                 pcont->network_is_open ? " open" : "",
                 pcont->psocket_is_open ? "" : "no ",
                 pcont->psocket_is_open ? " open" : "") ;
  TRACE_EVENT_P4("global: server %s query %s buffer %d port %d",
                 server_name, query_name, buffer_size, port_number) ;
}


/** Make the application stop or continue receiving data from the network by
 * calling the xoff or xon function, respectively.
 *
 * @param flow_on    if non-zero, switch flow on; off otherwise.
 */
void app_switch_flow(int flow_on)
{
  PROC_CONTEXT_T *pcont = &proc_context ;
  TRACE_FUNCTION("app_switch_flow()") ;

  if (flow_on)
  {
    TRACE_EVENT("switching socket to xon") ;
    sock_flow_xon(pcont->psocket) ;
  }
  else
  {
    TRACE_EVENT("switching socket to xoff") ;
    sock_flow_xoff(pcont->psocket) ;
  }
}


LOCAL void app_print_conn_info(T_SOCK_BEARER_INFO_CNF *info)
{
  TRACE_EVENT_P1("BearerType: %s",
                 sock_bearer_type_string(info->bearer_params.bearer_type));
  TRACE_EVENT_P1("APN: %s", info->bearer_params.apn);
  TRACE_EVENT_P1("PhoneNumber: %s", info->bearer_params.phone_nr);
  TRACE_EVENT_P1("UserId: %s", info->bearer_params.user_id);
  TRACE_EVENT_P1("Password: %s", info->bearer_params.password);
  TRACE_EVENT_P1("IP-Address: %s", inet_ntoa(info->bearer_params.ip_address));
  TRACE_EVENT_P1("DNS1-Address: %s", inet_ntoa(info->bearer_params.dns1));  
  TRACE_EVENT_P1("DNS2-Address: %s", inet_ntoa(info->bearer_params.dns2));
  TRACE_EVENT_P1("Gateway-Address: %s", inet_ntoa(info->bearer_params.gateway));
  TRACE_EVENT_P1("CID: %d",info->bearer_params.cid);
}



void app_open_bearer(int prov, int size, int reps)
{
  proc_context.bearer_only = TRUE;
  proc_init(prov, size, reps, AP_NONE, SOCK_IPPROTO_TCP, port_number );
  proc_context.bearer_only = FALSE;
  
}

void app_close_bearer()
{
  app_shutdown();
}


#endif /* FF_GPF_TCPIP */

/* EOF */