?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Network Access: TCP Access Agent' ??
MODULE nlm$tcp_access_agent;

{ PURPOSE:
{   This module contains procedures neccesary to support the TCP Access Agent.
{ DESIGN:
{   These procedures are called by the socket layer external interface code and in turn
{   access the channel connections. These procedures process the events received over
{   the TCP channel connections from the TCP Access Provider in the device.
{   The XDCL'd procedures have been grouped in alphabetical order.
{   This module contains code that executes in ring 3. It resides on OSF$JOB_TEMPLATE_23D.
{
{ NOTES:
{   The following abbreviations have been used in this module:
{          TCP - Transmission Control Protocol

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nac$null_connection_id
*copyc nae$tcp_condition_codes
*copyc nat$connection_id
*copyc nat$sk_listen_queue_limit
*copyc nat$sk_socket_address
*copyc nlt$cc_device_and_data_record
*copyc nlt$cl_connection
*copyc nlt$cl_layer_name
*copyc nlt$tcpaa_connection
*copyc nlt$tcpaa_event
*copyc nlt$tcpaa_protocol_data_unit
*copyc nlt$tcpaa_release_req_reason
*copyc ost$signature_lock_status
*copyc ost$status
?? POP ??
*copyc nap$namve_system_error
*copyc nlp$bm_add_message_prefix
*copyc nlp$bm_create_message
*copyc nlp$bm_extract_message_prefix
*copyc nlp$bm_flush_message
*copyc nlp$bm_get_message_length
*copyc nlp$bm_release_message
*copyc nlp$cancel_timer
*copyc nlp$cc_accept_connection
*copyc nlp$cc_disconnect
*copyc nlp$cc_initialize_template
*copyc nlp$cc_report_undelivered_data
*copyc nlp$cc_request_connection
*copyc nlp$cc_send_data
*copyc nlp$cc_send_data_fragments
*copyc nlp$cl_activate_layer
*copyc nlp$cl_deactivate_layer
*copyc nlp$cl_get_connection_processor
*copyc nlp$cl_get_layer_connection
*copyc nlp$cl_get_sap_processor
*copyc nlp$cl_initialize_template
*copyc nlp$cl_release_exclusive_access
*copyc nlp$get_exclusive_access
*copyc nlp$get_nonexclusive_access
*copyc nlp$release_exclusive_access
*copyc nlp$release_nonexclusive_access
*copyc nlp$select_timer
*copyc nlp$timer_expired
*copyc osp$clear_job_signature_lock
*copyc osp$set_job_signature_lock
*copyc osp$set_status_condition
*copyc nav$network_paged_heap
*copyc nav$network_procedures
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    nlc$tcp_ack_delay_time = 300, { Five minutes in seconds.
    nlc$tcp_max_flush_release_wait = 600000000; { Ten minutes.

?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_accept_socket', EJECT ??
*copy nlh$tcp_accept_socket

  PROCEDURE [XDCL] nlp$tcp_accept_socket
    (    cl_connection { input, output } : ^nlt$cl_connection;
         graceful_close: boolean;
         traffic_pattern: nat$sk_traffic_pattern;
         class: nlt$cc_connection_class;
     VAR status: ost$status);

    VAR
      accept_socket_pdu: nlt$tcpaa_accept_request_pdu,
      connection: ^nlt$tcpaa_connection,
      data_fragments: array [1 .. 1] of nat$data_fragment,
      ignore_status: ost$status,
      layer_active: boolean,
      message_id: nlt$bm_message_id;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      IF connection^.state = nlc$tcpaa_conn_await_accept THEN
        accept_socket_pdu.header.kind := nlc$tcpaa_accept_connect_req;
        accept_socket_pdu.header.length := #SIZE (nlt$tcpaa_accept_request_pdu);
        accept_socket_pdu.graceful_close := graceful_close;
        accept_socket_pdu.traffic_pattern := traffic_pattern;
        data_fragments [1].address := ^accept_socket_pdu;
        data_fragments [1].length := accept_socket_pdu.header.length;
        nlp$bm_create_message (data_fragments, message_id, ignore_status);
        nlp$cc_accept_connection (cl_connection, class, message_id, status);
        IF status.normal THEN

{ FSM Transition #14.

          connection^.state := nlc$tcpaa_conn_open;
        IFEND;
      ELSE { Unexpected state
        IF connection^.state = nlc$tcpaa_conn_open THEN
          osp$set_status_condition (nae$tcp_socket_already_accepted, status);
        ELSE
          osp$set_status_condition (nae$tcp_socket_terminated, status);
        IFEND;
      IFEND;
    ELSE { Layer not active
      osp$set_status_condition (nae$tcp_socket_terminated, status);
    IFEND;

  PROCEND nlp$tcp_accept_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_connect_event_processor', EJECT ??
*copy nlh$tcp_connect_event_processor

  PROCEDURE [XDCL] nlp$tcp_connect_event_processor
    (    cl_connection { input, output } : ^nlt$cl_connection;
         event: nlt$cc_event;
     VAR inventory_report: integer);

?? NEWTITLE := 'disconnect', EJECT ??

    PROCEDURE disconnect
      (    reason: nlt$tcpaa_release_req_reason);

      VAR
        data_fragment: array [1 .. 1] of nat$data_fragment,
        message_id: nlt$bm_message_id,
        release_pdu: nlt$tcpaa_release_request_pdu;

      nap$namve_system_error ({Recoverable_error=} TRUE, 'TCP Connect Event Processor DISCONNECT', NIL);
      release_pdu.header.kind := nlc$tcpaa_release_req;
      release_pdu.header.length := #SIZE (nlt$tcpaa_release_request_pdu);
      release_pdu.reason := reason;
      data_fragment [1].address := ^release_pdu;
      data_fragment [1].length := release_pdu.header.length;
      nlp$bm_create_message (data_fragment, message_id, local_status);
      nlp$cc_disconnect (cl_connection, message_id, local_status);
    PROCEND disconnect;
?? OLDTITLE, EJECT ??

    VAR
      bytes_moved: nat$data_length,
      connect_indication_pdu: nlt$tcpaa_connect_ind_pdu,
      connection: ^nlt$tcpaa_connection,
      data_length: integer,
      error_string: ^string (80),
      event_processor: nlt$cl_event_processor,
      ignore_layer_active: boolean,
      length: integer,
      local_status: ost$status,
      message_id: nlt$bm_message_id,
      user_event: nlt$tcpaa_event;

{ This procedure processes only connect indications. Other indications
{ will be an error.

    IF event.kind = nlc$cc_connect_event THEN
      message_id := event.connect.data;
      nlp$bm_get_message_length (message_id, data_length);
      IF data_length = #SIZE (nlt$tcpaa_connect_ind_pdu) THEN
        nlp$bm_extract_message_prefix (^connect_indication_pdu, data_length, message_id,
              {ignore} bytes_moved);
        IF connect_indication_pdu.header.length = data_length THEN
          IF connect_indication_pdu.header.kind = nlc$tcpaa_connect_ind THEN

{ FSM Transition #12.

            user_event.kind := nlc$tcpaa_connect_event;
            user_event.connect.destination_socket.port := connect_indication_pdu.destination_port;
            user_event.connect.destination_socket.ip_address := connect_indication_pdu.destination_ip_address;
            user_event.connect.source_socket.port := connect_indication_pdu.source_port;
            user_event.connect.source_socket.ip_address := connect_indication_pdu.source_ip_address;
            user_event.connect.device_id := event.connect.device_id;
            nlp$cl_activate_layer (nlc$tcp_access_agent, cl_connection);
            nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, ignore_layer_active,
                  connection);
            connection^.device_id := event.connect.device_id;
            connection^.listen_active := FALSE;
            connection^.state := nlc$tcpaa_conn_await_accept;
            nlp$cancel_timer (connection^.flush_release_timer);
            nlp$cl_get_sap_processor (cl_connection^.application_layer, nlc$tcp_access_agent,
                  event_processor);
            nav$network_procedures [event_processor.tcpaa].tcpaa_event_processor^
                  (cl_connection, user_event, inventory_report);
            nlp$cl_get_connection_processor (cl_connection^.application_layer, nlc$tcp_access_agent,
                  event_processor);
            connection^.event_processor := event_processor.tcpaa;
          ELSE { IF connect_indication_pdu.header.kind <> nlc$tcpaa_connect_ind THEN

{ FSM Transition #13.

            disconnect (nlc$tcpaa_rr_invalid_encoding);
          IFEND;
        ELSE { IF connect_indication_pdu.header.length <> data_length THEN

{ FSM Transition #13.

          disconnect (nlc$tcpaa_rr_header_length_in);
        IFEND;
      ELSE { IF data_length <> #SIZE (nlt$tcpaa_connect_ind_pdu) THEN

{ FSM Transition #13.

        nlp$bm_release_message (message_id);
        disconnect (nlc$tcpaa_rr_header_indiscern);
      IFEND;
    ELSE { Invalid event kind
      PUSH error_string;
      STRINGREP (error_string^, length, 'Invalid channel connection event received by the TCP access agent. ',
            '  Expecting a CC connect event but a ', event.kind, ' event was received.');
      nap$namve_system_error ({Recoverable_error=} TRUE, error_string^ (1, length), NIL);
    IFEND;
  PROCEND nlp$tcp_connect_event_processor;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_connect_socket', EJECT ??
*copy nlh$tcp_connect_socket

  PROCEDURE [XDCL] nlp$tcp_connect_socket
    (    cl_connection { input, output } : ^nlt$cl_connection;
         source_socket: nat$sk_socket_address;
         destination_socket: nat$sk_socket_address;
         graceful_close: boolean;
         traffic_pattern: nat$sk_traffic_pattern;
         class: nlt$cc_connection_class;
         device_id: nlt$device_identifier;
     VAR status: ost$status);

    VAR
      connection: ^nlt$tcpaa_connection,
      connect_request_pdu: nlt$tcpaa_connect_request_pdu,
      data_fragments: array [1 .. 1] of nat$data_fragment,
      device_and_data_list: array [1 .. 1] of nlt$cc_device_and_data_record,
      event_processor: nlt$cl_event_processor,
      ignore_layer_active: boolean,
      message_id: nlt$bm_message_id;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, ignore_layer_active, connection);

    connect_request_pdu.header.kind := nlc$tcpaa_connect_req;
    connect_request_pdu.header.length := #SIZE (nlt$tcpaa_connect_request_pdu);
    connect_request_pdu.source_port := source_socket.port;
    connect_request_pdu.destination_port := destination_socket.port;
    connect_request_pdu.source_ip_address := source_socket.ip_address;
    connect_request_pdu.destination_ip_address := destination_socket.ip_address;
    connect_request_pdu.ack_delay_time := nlc$tcp_ack_delay_time;
    connect_request_pdu.graceful_close := graceful_close;
    connect_request_pdu.traffic_pattern := traffic_pattern;
    data_fragments [1].address := ^connect_request_pdu;
    data_fragments [1].length := connect_request_pdu.header.length;
    nlp$bm_create_message (data_fragments, message_id, {ignore} status);
    device_and_data_list [1].device_id := device_id;
    device_and_data_list [1].data := message_id;
    nlp$cc_request_connection (cl_connection, device_and_data_list, nlc$tcp_access_address, class, status);
    IF status.normal THEN

{ FSM Transition #7.

      nlp$cl_activate_layer (nlc$tcp_access_agent, cl_connection);
      connection^.device_id := device_id;
      connection^.listen_active := FALSE;
      connection^.state := nlc$tcpaa_conn_await_confirm;
      nlp$cancel_timer (connection^.flush_release_timer);
      nlp$cl_get_connection_processor (cl_connection^.application_layer, nlc$tcp_access_agent,
            event_processor);
      connection^.event_processor := event_processor.tcpaa;
    IFEND;
  PROCEND nlp$tcp_connect_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_event_processor', EJECT ??
*copy nlh$tcp_event_processor

  PROCEDURE [XDCL] nlp$tcp_event_processor
    (    cl_connection { input, output } : ^nlt$cl_connection;
         event: nlt$cc_event;
     VAR inventory_report: integer);

?? NEWTITLE := 'disconnect' ??
?? NEWTITLE := 'send_disconnect_indication', EJECT ??

    PROCEDURE disconnect
      (    reason: nlt$tcpaa_release_req_reason);

      VAR
        data_fragment: array [1 .. 1] of nat$data_fragment,
        message_id: nlt$bm_message_id,
        release_pdu: nlt$tcpaa_release_request_pdu;

      nap$namve_system_error ({Recoverable_error=} TRUE, 'TCP Event Processor DISCONNECT', NIL);
      release_pdu.header.kind := nlc$tcpaa_release_req;
      release_pdu.header.length := #SIZE (nlt$tcpaa_release_request_pdu);
      release_pdu.reason := reason;
      data_fragment [1].address := ^release_pdu;
      data_fragment [1].length := release_pdu.header.length;
      nlp$bm_create_message (data_fragment, message_id, local_status);
      nlp$cc_disconnect (cl_connection, message_id, local_status);
      send_disconnect_indication;
      connection^.state := nlc$tcpaa_conn_closed;
      nlp$cl_deactivate_layer (nlc$tcp_access_agent, cl_connection);
    PROCEND disconnect;

    PROCEDURE send_disconnect_indication;

{ Build TCP event.

      user_event.kind := nlc$tcpaa_release_event;
      user_event.release.reason := nlc$tcpaa_ri_protocol_error;
      nav$network_procedures [connection^.event_processor].
            tcpaa_event_processor^ (cl_connection, user_event, inventory_report);
    PROCEND send_disconnect_indication;
?? OLDTITLE ??
?? OLDTITLE, EJECT ??

    VAR
      bytes_moved: nat$data_length,
      connect_confirm_pdu: nlt$tcpaa_conn_confirm_ind_pdu,
      connection: ^nlt$tcpaa_connection,
      data_fragments: array [1 .. 1] of nat$data_fragment,
      data_indication_pdu: nlt$tcpaa_data_ind_pdu,
      data_length: integer,
      error_string: ^string (80),
      flush_release_ind_pdu: nlt$tcpaa_flush_release_ind_pdu,
      ignore_data_length: integer,
      layer_active: boolean,
      length: integer,
      local_status: ost$status,
      message_id: nlt$bm_message_id,
      release_indication_pdu: nlt$tcpaa_release_ind_pdu,
      release_request_pdu: nlt$tcpaa_release_request_pdu,
      user_event: nlt$tcpaa_event;

    inventory_report := 0;
    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      CASE event.kind OF
?? NEWTITLE := 'nlc$cc_data_event', EJECT ??
      = nlc$cc_data_event =
        message_id := event.data.data;
        nlp$bm_get_message_length (message_id, data_length);
        IF data_length >= #SIZE (nlt$tcpaa_data_ind_pdu) THEN
          nlp$bm_extract_message_prefix (^data_indication_pdu, #SIZE (nlt$tcpaa_data_ind_pdu), message_id,
                {ignore} bytes_moved);
          IF data_indication_pdu.header.kind = nlc$tcpaa_data_ind THEN
            IF connection^.state = nlc$tcpaa_conn_open THEN
              IF data_indication_pdu.header.length = data_length THEN

{ FSM Transition #18.

                user_event.kind := nlc$tcpaa_data_event;
                user_event.data.push_data := data_indication_pdu.push_data;
                user_event.data.urgent_data := data_indication_pdu.urgent_data;
                user_event.data.data := message_id;
                nav$network_procedures [connection^.event_processor].
                      tcpaa_event_processor^ (cl_connection, user_event, inventory_report);
              ELSE { IF data_indication_pdu.header.length <> data_length THEN

{ FSM Transition #20.

                nlp$bm_release_message (message_id);
                disconnect (nlc$tcpaa_rr_header_length_in);
              IFEND;
            ELSEIF connection^.state = nlc$tcpaa_conn_closing THEN

{ FSM Transition #27.

              nlp$bm_release_message (message_id);
            ELSE { Invalid state
              nap$namve_system_error ({Recoverable_error=} TRUE,
                    'TCPAA receive a CC event while NOT in the open state.', NIL);
              nlp$bm_release_message (message_id);
            IFEND;
          ELSE { Invalid PDU kind

{ FSM Transition #20, #29.

            nlp$bm_release_message (message_id);
            disconnect (nlc$tcpaa_rr_invalid_encoding);
          IFEND;
        ELSEIF data_length = #SIZE (nlt$tcpaa_flush_release_ind_pdu) THEN
          nlp$bm_extract_message_prefix (^flush_release_ind_pdu, #SIZE (nlt$tcpaa_flush_release_ind_pdu),
                message_id, {ignore} bytes_moved);
          IF flush_release_ind_pdu.header.kind = nlc$tcpaa_flush_release_ind THEN
            IF connection^.state = nlc$tcpaa_conn_open THEN
              IF flush_release_ind_pdu.header.length = data_length THEN

{ FSM Transition #26.

                user_event.kind := nlc$tcpaa_release_event;
                user_event.release.reason := nlc$tcpaa_ri_user_termination;
                nav$network_procedures [connection^.event_processor].
                      tcpaa_event_processor^ (cl_connection, user_event, inventory_report);
                release_request_pdu.header.kind := nlc$tcpaa_release_req;
                release_request_pdu.header.length := #SIZE (nlt$tcpaa_release_request_pdu);
                release_request_pdu.reason := nlc$tcpaa_rr_flush_confirm;
                data_fragments [1].address := ^release_request_pdu;
                data_fragments [1].length := release_request_pdu.header.length;
                nlp$bm_create_message (data_fragments, message_id, {ignore} local_status);
                nlp$cc_disconnect (cl_connection, message_id, {ignore} local_status);
                connection^.state := nlc$tcpaa_conn_closed;
                nlp$cl_deactivate_layer (nlc$tcp_access_agent, cl_connection);
              ELSE { IF flush_release_ind_pdu.header.length <> data_length THEN
                disconnect (nlc$tcpaa_rr_header_length_in);
              IFEND;
            ELSEIF connection^.state = nlc$tcpaa_conn_closing THEN
              IF flush_release_ind_pdu.header.length = data_length THEN

{ FSM Transition #28.

                release_request_pdu.header.kind := nlc$tcpaa_release_req;
                release_request_pdu.header.length := #SIZE (nlt$tcpaa_release_request_pdu);
                release_request_pdu.reason := nlc$tcpaa_rr_flush_confirm;
                data_fragments [1].address := ^release_request_pdu;
                data_fragments [1].length := release_request_pdu.header.length;
                nlp$bm_create_message (data_fragments, message_id, {ignore} local_status);
                nlp$cc_disconnect (cl_connection, message_id, {ignore} local_status);
                connection^.state := nlc$tcpaa_conn_closed;
                nlp$cl_deactivate_layer (nlc$tcp_access_agent, cl_connection);
              ELSE { IF flush_release_ind_pdu.header.length <> data_length THEN

{ FSM Transition #29.

                disconnect (nlc$tcpaa_rr_header_length_in);
              IFEND;
            ELSE { Invalid state
              nap$namve_system_error ({Recoverable_error=} TRUE,
                    'TCPAA receive a CC event while NOT in the open state.', NIL);
            IFEND;
          ELSE { Invalid PDU kind

{ FSM Transition #20, #29.

            disconnect (nlc$tcpaa_rr_invalid_encoding);
          IFEND;
        ELSE { Invalid data length.

{ FSM Transition #20, #29.

          nlp$bm_release_message (message_id);
          disconnect (nlc$tcpaa_rr_header_length_in);
        IFEND;
?? OLDTITLE ??
?? NEWTITLE := 'nlc$cc_accept_event', EJECT ??
      = nlc$cc_accept_event =
        message_id := event.accept.data;
        IF connection^.state = nlc$tcpaa_conn_await_confirm THEN
          nlp$bm_get_message_length (message_id, data_length);
          IF data_length = #SIZE (nlt$tcpaa_conn_confirm_ind_pdu) THEN
            nlp$bm_extract_message_prefix (^connect_confirm_pdu, data_length, message_id,
                  {ignore} bytes_moved);
            IF connect_confirm_pdu.header.length = data_length THEN
              IF connect_confirm_pdu.header.kind = nlc$tcpaa_connect_confirm_ind THEN
                IF NOT connection^.listen_active THEN

{ FSM Transition #9.

                  user_event.kind := nlc$tcpaa_connect_confirm_event;
                  user_event.connect_confirm.local_port := connect_confirm_pdu.local_port;
                  nav$network_procedures [connection^.event_processor].
                        tcpaa_event_processor^ (cl_connection, user_event, inventory_report);
                  connection^.state := nlc$tcpaa_conn_open;
                ELSE { listen active

{ FSM Transition #3.

                  disconnect (nlc$tcpaa_rr_invalid_encoding);
                IFEND;
              ELSEIF connect_confirm_pdu.header.kind = nlc$tcpaa_listen_confirm_ind THEN
                IF connection^.listen_active THEN

{ FSM Transition #2.

                  user_event.kind := nlc$tcpaa_listen_confirm_event;
                  user_event.listen_confirm.local_port := connect_confirm_pdu.local_port;
                  nav$network_procedures [connection^.event_processor].
                        tcpaa_event_processor^ (cl_connection, user_event, inventory_report);
                  connection^.state := nlc$tcpaa_conn_open;
                ELSE { Listen not active

{ FSM Transition #9.

                  disconnect (nlc$tcpaa_rr_invalid_encoding);
                IFEND;
              ELSE { Invalid pdu kind

{ FSM Transition #3, #9.

                disconnect (nlc$tcpaa_rr_invalid_encoding);
              IFEND;
            ELSE { IF connect_confirm_pdu.header.length <> data_length THEN

{ FSM Transition #3, #9.

              disconnect (nlc$tcpaa_rr_header_length_in);
            IFEND;
          ELSE { IF data_length <> #SIZE (nlt$tcpaa_conn_confirm_ind_pdu) THEN

{ FSM Transition #3, #9.

            nlp$bm_release_message (message_id);
            disconnect (nlc$tcpaa_rr_header_indiscern);
          IFEND;
        ELSE { IF connection^.state <> nlc$tcpaa_conn_await_confirm THEN

{ FSM Transition #3, #9.

          nlp$bm_release_message (message_id);
          disconnect (nlc$tcpaa_rr_invalid_encoding);
        IFEND;
?? OLDTITLE ??
?? NEWTITLE := 'nlc$cc_clear_to_send_event', EJECT ??
      = nlc$cc_clear_to_send_event =
        user_event.kind := nlc$tcpaa_clear_to_send_event;
        nav$network_procedures [connection^.event_processor].
              tcpaa_event_processor^ (cl_connection, user_event, inventory_report);

?? OLDTITLE ??
?? NEWTITLE := 'nlc$cc_disconnect_event', EJECT ??
      = nlc$cc_disconnect_event =
        message_id := event.disconnect.data;
        IF event.disconnect.reason = nlc$cc_dr_normal_disconnect THEN
          IF (connection^.state <> nlc$tcpaa_conn_closing) AND
                (connection^.state <> nlc$tcpaa_conn_closed) THEN
            nlp$bm_get_message_length (message_id, data_length);
            IF data_length = #SIZE (nlt$tcpaa_release_ind_pdu) THEN
              nlp$bm_extract_message_prefix (^release_indication_pdu, data_length, message_id,
                    {ignore} bytes_moved);
              IF release_indication_pdu.header.length = data_length THEN
                IF release_indication_pdu.header.kind = nlc$tcpaa_release_ind THEN
                  IF NOT connection^.listen_active THEN

{ FSM Transition #11, #16.

                    user_event.kind := nlc$tcpaa_release_event;
                    user_event.release.reason := release_indication_pdu.reason;
                    nav$network_procedures [connection^.event_processor].
                          tcpaa_event_processor^ (cl_connection, user_event, inventory_report);
                    connection^.state := nlc$tcpaa_conn_closed;
                  ELSE { listen active

{ FSM Transition #5.

                    send_disconnect_indication;
                  IFEND;
                ELSEIF release_indication_pdu.header.kind = nlc$tcpaa_listen_reject_ind THEN
                  IF connection^.listen_active THEN

{ FSM Transition #4.

                    user_event.kind := nlc$tcpaa_listen_reject_event;
                    user_event.listen_reject.reason := release_indication_pdu.reason;
                    nav$network_procedures [connection^.event_processor].
                          tcpaa_event_processor^ (cl_connection, user_event, inventory_report);
                    connection^.state := nlc$tcpaa_conn_closed;
                  ELSE { listen not active

{ FSM Transition #5.

                    send_disconnect_indication;
                  IFEND;
                ELSE { Invalid pdu kind.

{ FSM Transition #5.

                  send_disconnect_indication;
                IFEND;
              ELSE { IF release_indication_pdu.header.length <> data_length THEN

{ FSM Transition #5.

                send_disconnect_indication;
              IFEND;
            ELSE { IF data_length <> #SIZE (nlt$tcpaa_release_ind_pdu) THEN

{ FSM Transition #5.

              send_disconnect_indication;
              nlp$bm_release_message (message_id);
            IFEND;
          ELSE { IF connection^.state = nlc$tcpaa_conn_closing or nlc$tcpaa_conn_closed THEN

{ FSM Transition #30.

            nlp$bm_release_message (message_id);
          IFEND;
        ELSE { Not a normal disconnect
          IF (connection^.state <> nlc$tcpaa_conn_closing) AND
                (connection^.state <> nlc$tcpaa_conn_closed) THEN

{ FSM Transition #4, #10, #15, #22, #24.

            user_event.kind := nlc$tcpaa_release_event;
            user_event.release.reason := nlc$tcpaa_ri_network_disconnect;
            user_event.release.cc_disconnect_reason := event.disconnect.reason;
            nav$network_procedures [connection^.event_processor].
                  tcpaa_event_processor^ (cl_connection, user_event, inventory_report);
            connection^.state := nlc$tcpaa_conn_closed;
          IFEND;

{ FSM Transition #30.

          nlp$bm_release_message (message_id);
        IFEND;
        nlp$cl_deactivate_layer (nlc$tcp_access_agent, cl_connection);
      ELSE { Invalid event
        PUSH error_string;
        STRINGREP (error_string^, length,
              'Invalid channel connection event received by the TCP access agent.  A ', event.kind,
              ' event was received.');
        nap$namve_system_error ({Recoverable_error=} TRUE, error_string^ (1, length), NIL);
        IF event.kind = nlc$cc_connect_event THEN
          message_id := event.connect.data;
          nlp$bm_release_message (message_id);
        IFEND;
      CASEND;
    ELSE { Layer inactive
      nap$namve_system_error ({Recoverable_error=} TRUE, 'TCP Access Agent layer is inactive.', NIL);
      CASE event.kind OF
      = nlc$cc_connect_event =
        message_id := event.connect.data;
      = nlc$cc_accept_event =
        message_id := event.accept.data;
      = nlc$cc_data_event =
        message_id := event.data.data;
      = nlc$cc_expedited_data_event =
        message_id := event.expedited_data.data;
      = nlc$cc_disconnect_event =
        message_id := event.disconnect.data;
      ELSE
        message_id := nlv$bm_null_message_id;
      CASEND;
      nlp$bm_release_message (message_id);
    IFEND;
  PROCEND nlp$tcp_event_processor;
?? OLDTITLE ??
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_flush_release_socket', EJECT ??
*copy nlh$tcp_flush_release_socket

  PROCEDURE [XDCL] nlp$tcp_flush_release_socket
    (    cl_connection { input, output } : ^nlt$cl_connection;
     VAR status: ost$status);

    VAR
      connection: ^nlt$tcpaa_connection,
      data_fragments: array [1 .. 1] of nat$data_fragment,
      flush_release_request_pdu: nlt$tcpaa_flush_release_req_pdu,
      ignore_status: ost$status,
      layer_active: boolean,
      message_id: nlt$bm_message_id;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      IF connection^.state = nlc$tcpaa_conn_open THEN

{ FSM Transition #25.

        flush_release_request_pdu.header.kind := nlc$tcpaa_flush_release_req;
        flush_release_request_pdu.header.length := #SIZE (nlt$tcpaa_flush_release_req_pdu);
        data_fragments [1].address := ^flush_release_request_pdu;
        data_fragments [1].length := flush_release_request_pdu.header.length;
        nlp$bm_create_message (data_fragments, message_id, ignore_status);
        nlp$cc_send_data (cl_connection, message_id, status);
        IF status.normal THEN
          connection^.state := nlc$tcpaa_conn_closing;

{ Start the timer task.

          nlp$select_timer (nlc$tcp_max_flush_release_wait, 0, connection^.flush_release_timer);
        IFEND;
      ELSE { Unexpected state
        IF (connection^.state = nlc$tcpaa_conn_closed) OR (connection^.state = nlc$tcpaa_conn_closing) THEN
          osp$set_status_condition (nae$tcp_socket_terminated, status);
        ELSE
          osp$set_status_condition (nae$tcp_socket_not_open, status);
        IFEND;
      IFEND;
    ELSE { Layer inactive
      osp$set_status_condition (nae$tcp_socket_terminated, status);
    IFEND;

  PROCEND nlp$tcp_flush_release_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_flush_release_timer', EJECT ??

  PROCEDURE [XDCL] nlp$tcp_flush_release_timer
    (    current_time: integer;
         cl_connection: ^nlt$cl_connection);

    VAR
      connection: ^nlt$tcpaa_connection,
      data_fragments: array [1 .. 1] of nat$data_fragment,
      ignore_status: ost$status,
      layer_active: boolean,
      message_id: nlt$bm_message_id,
      release_request_pdu: nlt$tcpaa_release_request_pdu;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      IF connection^.state = nlc$tcpaa_conn_closing THEN
        IF nlp$timer_expired (current_time, connection^.flush_release_timer) THEN
          release_request_pdu.header.kind := nlc$tcpaa_release_req;
          release_request_pdu.header.length := #SIZE (nlt$tcpaa_release_request_pdu);
          release_request_pdu.reason := nlc$tcpaa_rr_user_request;
          data_fragments [1].address := ^release_request_pdu;
          data_fragments [1].length := release_request_pdu.header.length;
          nlp$bm_create_message (data_fragments, message_id, ignore_status);
          nlp$cc_disconnect (cl_connection, message_id, ignore_status);
          connection^.state := nlc$tcpaa_conn_closed;
          nlp$cl_deactivate_layer (nlc$tcp_access_agent, cl_connection);
        IFEND;
      IFEND;
    IFEND;
  PROCEND nlp$tcp_flush_release_timer;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_initialize', EJECT ??
*copy nlh$tcp_initialize

  PROCEDURE [XDCL] nlp$tcp_initialize
    (    application_layer: nlt$cl_application_layer;
         connect_event_processor: nat$network_procedure;
         event_processor: nat$network_procedure);

    VAR
      connection_processor: nlt$cl_event_processor,
      sap_processor: nlt$cl_event_processor;

    sap_processor.layer := nlc$tcp_access_agent;
    sap_processor.tcpaa := connect_event_processor;
    connection_processor.layer := nlc$tcp_access_agent;
    connection_processor.tcpaa := event_processor;
    nlp$cl_initialize_template (application_layer, nlc$tcp_access_agent, #SIZE (nlt$tcpaa_connection),
          {maximum_protocol_header_size =} 0, sap_processor, nac$nil, connection_processor,
          nlc$tcp_flush_release_timer);
    nlp$cc_initialize_template (application_layer);
  PROCEND nlp$tcp_initialize;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_listen_socket', EJECT ??
*copy nlh$tcp_listen_socket

  PROCEDURE [XDCL] nlp$tcp_listen_socket
    (    cl_connection { input, output } : ^nlt$cl_connection;
         local_port: nat$sk_port_number;
         queue_limit: nat$sk_listen_queue_limit;
         selection_criteria: nat$sk_socket_address;
         device_id: nlt$device_identifier;
         class: nlt$cc_connection_class;
     VAR status: ost$status);

    VAR
      connection: ^nlt$tcpaa_connection,
      data_fragments: array [1 .. 1] of nat$data_fragment,
      device_and_data_list: array [1 .. 1] of nlt$cc_device_and_data_record,
      event_processor: nlt$cl_event_processor,
      ignore_layer_active: boolean,
      ignore_status: ost$status,
      listen_request_pdu: nlt$tcpaa_listen_request_pdu,
      message_id: nlt$bm_message_id;


{ FSM Transition #1.

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, ignore_layer_active, connection);
    listen_request_pdu.header.kind := nlc$tcpaa_listen_req;
    listen_request_pdu.header.length := #SIZE (nlt$tcpaa_listen_request_pdu);
    listen_request_pdu.local_port := local_port;
    listen_request_pdu.queue_limit := queue_limit;
    listen_request_pdu.source_port := selection_criteria.port;
    listen_request_pdu.ack_delay_time := nlc$tcp_ack_delay_time;
    listen_request_pdu.source_ip_address := selection_criteria.ip_address;
    data_fragments [1].address := ^listen_request_pdu;
    data_fragments [1].length := listen_request_pdu.header.length;
    nlp$bm_create_message (data_fragments, message_id, ignore_status);
    device_and_data_list [1].device_id := device_id;
    device_and_data_list [1].data := message_id;
    nlp$cc_request_connection (cl_connection, device_and_data_list, nlc$tcp_access_address, class, status);
    IF status.normal THEN
      nlp$cl_activate_layer (nlc$tcp_access_agent, cl_connection);
      connection^.device_id := device_id;
      connection^.listen_active := TRUE;
      connection^.state := nlc$tcpaa_conn_await_confirm;
      nlp$cl_get_connection_processor (cl_connection^.application_layer, nlc$tcp_access_agent,
            event_processor);
      connection^.event_processor := event_processor.tcpaa;
    IFEND;

  PROCEND nlp$tcp_listen_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_release_socket', EJECT ??
*copy nlh$tcp_release_socket

  PROCEDURE [XDCL] nlp$tcp_release_socket
    (    cl_connection { input, output } : ^nlt$cl_connection;
     VAR status: ost$status);

    VAR
      connection: ^nlt$tcpaa_connection,
      data_fragments: array [1 .. 1] of nat$data_fragment,
      ignore_status: ost$status,
      layer_active: boolean,
      message_id: nlt$bm_message_id,
      release_request_pdu: nlt$tcpaa_release_request_pdu;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      IF connection^.state <> nlc$tcpaa_conn_closed THEN

{ FSM Transition #6, #21, #23, #31.

        release_request_pdu.header.kind := nlc$tcpaa_release_req;
        release_request_pdu.header.length := #SIZE (nlt$tcpaa_release_request_pdu);
        release_request_pdu.reason := nlc$tcpaa_rr_user_request;
        data_fragments [1].address := ^release_request_pdu;
        data_fragments [1].length := release_request_pdu.header.length;
        nlp$bm_create_message (data_fragments, message_id, ignore_status);
        nlp$cc_disconnect (cl_connection, message_id, ignore_status);
        connection^.state := nlc$tcpaa_conn_closed;
        nlp$cl_deactivate_layer (nlc$tcp_access_agent, cl_connection);
      ELSE
        osp$set_status_condition (nae$tcp_socket_terminated, status);
      IFEND;
    ELSE { layer inactive
      osp$set_status_condition (nae$tcp_socket_terminated, status);
    IFEND;

  PROCEND nlp$tcp_release_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_report_undelivered_data', EJECT ??
*copy nlh$tcp_report_undelivered_data

  PROCEDURE [XDCL] nlp$tcp_report_undelivered_data
    (    cl_connection { input, output } : ^nlt$cl_connection;
         accumulated_message_buffers: integer);

    VAR
      connection: ^nlt$tcpaa_connection,
      layer_active: boolean;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      nlp$cc_report_undelivered_data (cl_connection, accumulated_message_buffers);
    IFEND;
  PROCEND nlp$tcp_report_undelivered_data;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_send_data', EJECT ??
*copy nlh$tcp_send_data

  PROCEDURE [XDCL] nlp$tcp_send_data
    (    cl_connection { input, output } : ^nlt$cl_connection;
         user_data: nlt$bm_message_id;
         push_data: boolean;
         urgent_data: boolean;
     VAR status: ost$status);

    VAR
      connection: ^nlt$tcpaa_connection,
      data_length: integer,
      data_request_pdu: nlt$tcpaa_data_request_pdu,
      layer_active: boolean,
      message_id: nlt$bm_message_id;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      IF connection^.state = nlc$tcpaa_conn_open THEN

{ FSM Transition #17.

        data_request_pdu.header.kind := nlc$tcpaa_data_req;
        nlp$bm_get_message_length (user_data, data_length);
        data_request_pdu.header.length := #SIZE (nlt$tcpaa_data_request_pdu) + data_length;
        data_request_pdu.urgent_data := urgent_data;
        data_request_pdu.push_data := push_data;
        message_id := user_data;
        nlp$bm_add_message_prefix (^data_request_pdu, #SIZE (nlt$tcpaa_data_request_pdu), message_id);
        nlp$cc_send_data (cl_connection, message_id, status);
      ELSE
        IF (connection^.state = nlc$tcpaa_conn_closed) OR (connection^.state = nlc$tcpaa_conn_closing) THEN
          osp$set_status_condition (nae$tcp_socket_terminated, status);
        ELSE
          osp$set_status_condition (nae$tcp_socket_not_open, status);
        IFEND;
      IFEND;
    ELSE { Layer not active
      osp$set_status_condition (nae$tcp_socket_terminated, status);
    IFEND;

  PROCEND nlp$tcp_send_data;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_send_data_fragments', EJECT ??
*copy nlh$tcp_send_data_fragments

  PROCEDURE [XDCL] nlp$tcp_send_data_fragments
    (    cl_connection { input, output } : ^nlt$cl_connection;
         user_data: nat$data_fragments;
         push_data: boolean;
         urgent_data: boolean;
     VAR status: ost$status);

    VAR
      connection: ^nlt$tcpaa_connection,
      data_length: integer,
      data_request_pdu: nlt$tcpaa_data_request_pdu,
      i: integer,
      layer_active: boolean,
      tcpaa_pdu: ^nat$data_fragments;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      IF connection^.state = nlc$tcpaa_conn_open THEN

{ FSM Transition #17.

        data_request_pdu.header.kind := nlc$tcpaa_data_req;

        data_length := 0;
        FOR i := LOWERBOUND (user_data) TO UPPERBOUND (user_data) DO
          IF user_data [i].address <> NIL THEN
            data_length := data_length + user_data [i].length;
          IFEND;
        FOREND;

        data_request_pdu.header.length := #SIZE (nlt$tcpaa_data_request_pdu) + data_length;
        data_request_pdu.urgent_data := urgent_data;
        data_request_pdu.push_data := push_data;

        PUSH tcpaa_pdu: [1 .. (UPPERBOUND (user_data) + 1)];
        tcpaa_pdu^ [1].address := ^data_request_pdu;
        tcpaa_pdu^ [1].length := #SIZE (data_request_pdu);
        FOR i := 2 TO UPPERBOUND (tcpaa_pdu^) DO
          tcpaa_pdu^ [i] := user_data [i - 1];
        FOREND;
        nlp$cc_send_data_fragments (cl_connection, tcpaa_pdu^, status);
      ELSE
        IF (connection^.state = nlc$tcpaa_conn_closed) OR (connection^.state = nlc$tcpaa_conn_closing) THEN
          osp$set_status_condition (nae$tcp_socket_terminated, status);
        ELSE
          osp$set_status_condition (nae$tcp_socket_not_open, status);
        IFEND;
      IFEND;
    ELSE { Layer not active
      osp$set_status_condition (nae$tcp_socket_terminated, status);
    IFEND;

  PROCEND nlp$tcp_send_data_fragments;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$tcp_set_socket_options', EJECT ??
*copy nlh$tcp_set_socket_options

  PROCEDURE [XDCL] nlp$tcp_set_socket_options
    (    cl_connection { input, output } : ^nlt$cl_connection;
         graceful_close: boolean;
         traffic_pattern: nat$sk_traffic_pattern;
     VAR status: ost$status);

    VAR
      connection: ^nlt$tcpaa_connection,
      data_fragments: array [1 .. 1] of nat$data_fragment,
      ignore_status: ost$status,
      layer_active: boolean,
      message_id: nlt$bm_message_id,
      set_options_request_pdu: nlt$tcpaa_set_options_req_pdu;

    nlp$cl_get_layer_connection (nlc$tcp_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      IF connection^.state = nlc$tcpaa_conn_open THEN

{ FSM Transition #19.

        set_options_request_pdu.header.kind := nlc$tcpaa_set_options_req;
        set_options_request_pdu.header.length := #SIZE (nlt$tcpaa_set_options_req_pdu);
        set_options_request_pdu.graceful_close := graceful_close;
        set_options_request_pdu.traffic_pattern := traffic_pattern;
        set_options_request_pdu.ack_delay_time := nlc$tcp_ack_delay_time;
        data_fragments [1].address := ^set_options_request_pdu;
        data_fragments [1].length := set_options_request_pdu.header.length;
        nlp$bm_create_message (data_fragments, message_id, ignore_status);
        nlp$cc_send_data (cl_connection, message_id, status);
      ELSE
        IF (connection^.state = nlc$tcpaa_conn_closed) OR (connection^.state = nlc$tcpaa_conn_closing) THEN
          osp$set_status_condition (nae$tcp_socket_terminated, status);
        ELSE
          osp$set_status_condition (nae$tcp_socket_not_open, status);
        IFEND;
      IFEND;
    ELSE { Layer not active
      osp$set_status_condition (nae$tcp_socket_terminated, status);
    IFEND;

  PROCEND nlp$tcp_set_socket_options;
?? OLDTITLE ??
MODEND nlm$tcp_access_agent;
