/**************************************************************************** /* /* Copyright Digital Equipment Corporation 1985,1995. All rights reserved. /* /* Restricted Rights: Use, duplication, or disclosure by the U.S. Government /* is subject to restrictions as set forth in subparagraph (c) (1) (ii) of /* DFARS 252.227-7013, or in FAR 52.227-19, or in FAR 52.227-14 Alt. III, as /* applicable. /* /* This software is proprietary to and embodies the confidential technology /* of Digital Equipment Corporation. Possession, use, or copying of this /* software and media is authorized only pursuant to a valid written license /* from Digital or an authorised sublicensor. /* /*****************************************************************************/ #include #include #include #include #include #include #include #define TRUE 1 #define FALSE 0 #define OSAK_EXAMPLE_WS_SIZE 1024 #define OSAK_EXAMPLE_BUFFER_SIZE 2048 #define OSAK_EXAMPLE_TIMEOUT 60 void open_responder (osak_port *port, struct osak_parameter_block *pb) ; unsigned long int accept_rsp (osak_port port, struct osak_parameter_block *pb) ; unsigned long int release_rsp (osak_port port, struct osak_parameter_block *pb) ; void wait_for_outbound_completion (osak_port port, struct osak_parameter_block *pb) ; void wait_for_inbound (osak_port port, struct osak_parameter_block *pb) ; void wait_for_TDISind (osak_port port, struct osak_parameter_block *pb) ; void give_buffer (osak_port port) ; void reuse_buffers (struct osak_buffer **buf_ptr) ; void display_data (struct osak_buffer *buf) ; void *alloc_memory (unsigned long int size, unsigned long int param) ; unsigned long int free_memory (unsigned long int size, void *ptr, unsigned long int param) ; extern unsigned long int osak_open_responder () ; extern unsigned long int osak_accept_rsp () ; extern unsigned long int osak_release_rsp () ; extern unsigned long int osak_close_port () ; extern unsigned long int osak_select () ; extern unsigned long int osak_get_event () ; extern unsigned long int osak_collect_pb () ; extern unsigned long int osak_give_buffers () ; /*--------------------------------------------------------------------------*/ /* Global variables */ /*--------------------------------------------------------------------------*/ /* object identifier for ASN.1 Basic Encoding Rules */ unsigned char ber[] = {0x06, 0x02, 0x51, 0x01}; /* {2 1 1} */ /* Structures to hold presentation context result list */ struct osak_pcontext_proposal_result context_res2 ; struct osak_pcontext_proposal_result context_res1 ; /* Structure to hold functional units */ struct osak_fus fus ; /* Structure to hold local address */ struct osak_aei local_address ; /* Structure to hold transport template */ struct osak_transport_templates transport_template ; /* Queue of buffer for osak_give_buffers. These are used in the */ /* routines give_buffer and reuse_buffers. */ struct osak_buffer *free_buffers = NULL ; struct osak_buffer *free_buffers_end = NULL ; /****************************************************************************/ /* FUNCTION: main */ /****************************************************************************/ main (int argc, char *argv[]) { struct osak_parameter_block *in_pb ; /* Parameter block to use in */ /* calls to osak_get_event */ struct osak_parameter_block *out_pb ; /* Parameter block to use in */ /* outbound calls */ osak_port port ; /* To hold the port handle */ /* returned from */ /* osak_open_responder */ unsigned long int status ; /* To hold return values from */ /* routines */ struct osak_buffer *buffer_ptr ; /* To hold list of buffers */ /* returned from */ /* osak_close_port */ struct osak_parameter_block *pb_ptr ; /* To hold list of parameter */ /* blocks returned from */ /* osak_close_port */ /*----------------------------------------------------------------------*/ /* Allocate a parameter block. The same parameter block is used on all */ /* outbound calls to OSAK. */ /*----------------------------------------------------------------------*/ out_pb = (struct osak_parameter_block *) malloc (sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE) ; if (out_pb == NULL) { printf ("Failed to allocate outbound parameter block\n") ; exit (0) ; } /*----------------------------------------------------------------------*/ /* Allocate a parameter block. The same parameter block is used on all */ /* inbound calls to OSAK. */ /*----------------------------------------------------------------------*/ in_pb = (struct osak_parameter_block *) malloc (sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE) ; if (in_pb == NULL) { printf ("Failed to allocate inbound parameter block\n") ; exit (0) ; } /*----------------------------------------------------------------------*/ /* Open Responder */ /*----------------------------------------------------------------------*/ printf ("Opening a responder port\n"); open_responder (&port, in_pb) ; /*----------------------------------------------------------------------*/ /* Wait for assoc-ind */ /*----------------------------------------------------------------------*/ printf ("Waiting for A-ASSOCIATE-indication.\n"); wait_for_inbound (port, in_pb) ; if (in_pb->event_type != OSAK_C_ASSOC_IND) { printf ("Expected A-ASSOCIATE-indication. Received some other event\n"); exit (0) ; } printf ("Received for A-ASSOCIATE-indication.\n"); /* Display the user-information sent by the initiator. */ if (in_pb->peer_data != NULL) { printf ("Data Received:\n") ; display_data (in_pb->peer_data) ; } reuse_buffers (&in_pb->tsdu_ptr) ; /* Here a real application would need to check then application context */ /* name, proposed presentation contexts and proposed functional units */ /* If these were not acceptable to the application then it would need */ /* to abort or reject the assocation. For simplicity this example will */ /* accept the assocation without doing these checks. */ /*----------------------------------------------------------------------*/ /* Send Accept Response */ /*----------------------------------------------------------------------*/ printf ("Sending A-ACCEPT-response.\n"); status = accept_rsp (port, out_pb) ; if ((status != OSAK_S_NORMAL) && (status != OSAK_S_QUEUED) && (status != OSAK_S_FREE)) { printf ("Failed in call to osak_accept_rsp\n"); exit (0) ; } if (status == OSAK_S_QUEUED || status == OSAK_S_FREE) { /* If OSAK returned OSAK_S_QUEUED or OSAK_S_FREE then it is still */ /* using the parameter block and the structures that the parameter */ /* block parameters point to. The following routine waits for OSAK */ /* to be finished with the parameter block (using osak_select) and */ /* gets the parameter block back from OSAK (using osak_collect_pb). */ printf ("OSAK returned QUEUED or FREE status.\n"); printf ("Waiting for request to complete.\n"); wait_for_outbound_completion (port, out_pb) ; printf ("Outbound request completed.\n"); } /*----------------------------------------------------------------------*/ /* Wait for release-ind */ /*----------------------------------------------------------------------*/ printf ("Waiting for A-RELEASE-indication.\n"); wait_for_inbound (port, in_pb) ; if (in_pb->event_type != OSAK_C_RELEASE_IND) { printf ("Expected A-RELEASE-indication. Received some other event\n"); exit (0) ; } printf ("Received A-RELEASE-indication.\n"); reuse_buffers (&in_pb->tsdu_ptr) ; /*----------------------------------------------------------------------*/ /* Send Release Response */ /*----------------------------------------------------------------------*/ printf ("Sending A-RELEASE-response.\n"); status = release_rsp (port, out_pb) ; if ((status != OSAK_S_NORMAL) && (status != OSAK_S_QUEUED) && (status != OSAK_S_FREE)) { printf ("Failed in call to osak_release_rsp\n"); exit (0) ; } if (status == OSAK_S_QUEUED || status == OSAK_S_FREE) { /* If OSAK returned OSAK_S_QUEUED or OSAK_S_FREE then it is still */ /* using the parameter block and the structures that the parameter */ /* block parameters point to. The following routine waits for OSAK */ /* to be finished with the parameter block (using osak_select) and */ /* gets the parameter block back from OSAK (using osak_collect_pb). */ printf ("OSAK returned QUEUED or FREE status.\n"); printf ("Waiting for request to complete.\n"); wait_for_outbound_completion (port, out_pb) ; printf ("Outbound request completed.\n"); } /*----------------------------------------------------------------------*/ /* Wait for transport disconnect */ /*----------------------------------------------------------------------*/ printf ("Waiting for TDISind\n"); wait_for_TDISind (port, in_pb) ; /*----------------------------------------------------------------------*/ /* Close the port */ /*----------------------------------------------------------------------*/ /* The osak_close_port is OSAK_C_DESTRUCTIVE rather than */ /* OSAK_C_NON_DESTRUCTIVE. This ensures that the transport connection */ /* is disconnected. The call to wait_for_TDISind above may haved timed */ /* out if the peer did not send a transport disconnect. This may not */ /* be necessary on OpenVMS since OSAK implements the session disconnect */ /* timer on that operating system. However the session disconnect */ /* timer is not implemented in OSAK on OSF/1. That is why the the */ /* applications has to wait for the disconnect, and send one if the */ /* peer does not send one before a certain timeout. The code in this */ /* example will work on all operating systems. */ printf ("Closing responder port\n"); status = osak_close_port (port, &buffer_ptr, &pb_ptr, OSAK_C_DESTRUCTIVE) ; if (status != OSAK_S_NORMAL) { printf ("Failed in call to osak_close_port\n"); exit (0) ; } reuse_buffers (&buffer_ptr) ; } /****************************************************************************/ /* FUNCTION: open_responder */ /* */ /* This routine sets up the parameter for a call to osak_open_responder and */ /* makes the call. */ /* */ /****************************************************************************/ void open_responder (osak_port *port, struct osak_parameter_block *pb) { unsigned long int status ; /* Set up the local address */ /* Initialize AE title */ local_address.aetitle.aptitle.size = 0 ; local_address.aetitle.aptitle.pointer = NULL ; local_address.aetitle.ae_qualifier.size = 0 ; local_address.aetitle.ae_qualifier.pointer = NULL ; /* Initialize AE invocation ID */ local_address.aeiid.apiid.size = 0 ; local_address.aeiid.apiid.pointer = NULL ; local_address.aeiid.aeiid.size = 0 ; local_address.aeiid.aeiid.pointer = NULL ; /* Initialize presentation address */ local_address.paddress.psel.size = 9 ; local_address.paddress.psel.pointer = (unsigned char *)"RESP-PSEL" ; local_address.paddress.ssel.size = 9 ; local_address.paddress.ssel.pointer = (unsigned char *)"RESP-SSEL" ; local_address.paddress.tsel.size = 9 ; local_address.paddress.tsel.pointer = (unsigned char *)"RESP-TSEL" ; local_address.paddress.nsap.id.size = 0 ; local_address.paddress.nsap.id.pointer = NULL ; local_address.paddress.nsap.type = OSAK_C_CLNS ; local_address.paddress.nsap.next = NULL ; /* initialize parameter block */ memset ((void *)pb, '\0', sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE ) ; pb->pb_length = sizeof (struct osak_parameter_block) ; pb->ws_length = OSAK_EXAMPLE_WS_SIZE ; pb->api_version = OSAK_C_API_VERSION_3 ; pb->protocol_versions = NULL ; /* Use default value */ pb->local_aei = &local_address ; pb->transport_template = NULL ; /* Use default value */ pb->alloc_rtn = (osak_rtn) alloc_memory ; pb->dealloc_rtn = (osak_rtn) free_memory ; pb->alloc_param = 0 ; pb->completion_rtn = NULL ; pb->completion_param = 0 ; status = osak_open_responder (port, pb) ; if (status != OSAK_S_NORMAL) { printf ("Failed to open responder.\n") ; exit (0) ; } } /****************************************************************************/ /* FUNCTION: accept_rsp */ /* */ /* This routine sets up the parameters for a call to osak_accept_rsp and */ /* makes the call. */ /* */ /* It does not do any of the parameter negotiation that a real application */ /* would need to do. For example, it does not check the functional units */ /* or the presentation contexts proposed in the A-ASSOCIATE-indication. */ /* */ /****************************************************************************/ unsigned long int accept_rsp (osak_port port, struct osak_parameter_block *pb) { unsigned long int status ; /* Set up the presentation context result list */ /* A real application would need to go through the contexts received in */ /* the A-ASSOCIATE-indication and decide whether to accept or reject */ /* each context. Because this is a simple example it assumes that only */ /* two contexts were proposed, and it will accept both of the contexts. */ context_res1.result = OSAK_C_ACCEPT ; context_res1.ts_name.size = 4 ; context_res1.ts_name.pointer = ber ; context_res1.next = &context_res2 ; context_res2.result = OSAK_C_ACCEPT ; context_res2.ts_name.size = 4 ; context_res2.ts_name.pointer = ber ; context_res2.next = NULL ; /* Set up functional units. */ /* A real application would need to check which functional units were */ /* proposed by the initiator and negotiate a common set of functional */ /* units. This simple example assumes that the duplex functional unit */ /* was proposed. It is only going to accept the duplex functional unit.*/ fus.duplex = 1 ; fus.half_duplex = 0 ; fus.expedited = 0 ; fus.syncminor = 0 ; fus.syncmajor = 0 ; fus.resynchronize = 0 ; fus.activities = 0 ; fus.negotiated_release = 0 ; fus.capability_data = 0 ; fus.exceptions = 0 ; fus.typed_data = 0 ; fus.data_separation = 0 ; fus.context_management = 0 ; /* initialize parameter block */ memset ((void *)pb, '\0', sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE ) ; pb->pb_length = sizeof (struct osak_parameter_block) ; pb->ws_length = OSAK_EXAMPLE_WS_SIZE ; pb->acontext = NULL ; /* Use default. This will use the same */ /* application context name that was in the */ /* A-ASSOCIATE-indication */ pb->pcontext_res_list = &context_res1 ; pb->responding_aei = NULL ; pb->sconnect_id = NULL ; pb->segmentation = NULL ; pb->initial_serial_number = NULL ; pb->initial_tokens = NULL ; pb->request_tokens = NULL ; pb->functional_units = &fus ; pb->pdefault_context_res = NULL ; pb->user_data = NULL ; pb->more_flag = 0 ; pb->data_length = 0 ; pb->completion_rtn = NULL ; pb->completion_param = 0 ; /* All other fields using default values */ status = osak_accept_rsp (port, pb) ; return status ; } /****************************************************************************/ /* FUNCTION: release_rsp */ /* */ /* This routine sets up the parameters for a call to osak_release_rsp and */ /* makes the call. */ /* */ /****************************************************************************/ unsigned long int release_rsp (osak_port port, struct osak_parameter_block *pb) { unsigned long int status ; /* initialize parameter block */ memset ((void *)pb, '\0', sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE ) ; pb->pb_length = sizeof (struct osak_parameter_block) ; pb->ws_length = OSAK_EXAMPLE_WS_SIZE ; pb->release_reason = OSAK_C_RLRE_NORMAL ; pb->user_data = NULL ; pb->more_flag = 0 ; pb->data_length = 0 ; pb->completion_rtn = NULL ; pb->completion_param = 0 ; status = osak_release_rsp (port, pb) ; return status ; } /****************************************************************************/ /* FUNCTION: wait_for_outbound_completion */ /* */ /* This routine is called when OSAK_S_QUEUED or OSAK_S_FREE is returned by */ /* OSAK. It uses osak_select to wait for OSAK to finish with the parameter */ /* block, and calls osak_collect_pb to get back ownership of the parameter */ /* block. */ /* */ /* This routine uses the mask OSAK_C_WRITEEVENT in the call to osak_select. */ /* If we wanted to wait for an inbound event (such as receiving the release */ /* indication) at the same time we should have used the mask */ /* OSAK_C_WRITEEVENT | OSAK_C_READEVENT. We would then have to check the */ /* returned mask from osak_select to see what action to take: whether to */ /* call osak_collect_pb or osak_get_event or both. The example */ /* osak_example_init.c does that in its routine wait_for_event. */ /****************************************************************************/ void wait_for_outbound_completion (osak_port port, struct osak_parameter_block *pb) { struct osak_parameter_block *ret_pb ; osak_handle_count handlecount ; osak_handle handle ; unsigned long int status ; osak_time select_time ; /* Set up parameter to call osak_select() */ handlecount = 1 ; handle.id = (unsigned long int) port ; handle.request_mask = OSAK_C_WRITEEVENT ; handle.returned_mask = 0 ; select_time = OSAK_EXAMPLE_TIMEOUT ; status = osak_select (handlecount, &handle, &select_time) ; if (status != OSAK_S_NORMAL) { printf ("call to osak_select failed\n") ; exit (0) ; } /* See if there is an inbound event. If so call osak_get_event() */ if (handle.returned_mask & OSAK_C_WRITEEVENT) { ret_pb = NULL ; status = osak_collect_pb (port, &ret_pb) ; if (status != OSAK_S_NORMAL) { printf ("osak_collect_pb failed\n"); exit (0) ; } /* The parameter block returned must be the same as that given in */ /* the OSAK call. We check here for sanity. */ if (ret_pb != pb) { printf ("Parameter block returned from osak_collect_pb is\n"); printf ("different from that expected.\n") ; exit (0) ; } /* The parameter block returned will have the status block filled */ /* in by OSAK. This needs to be checked to find out if an error */ /* occured. */ if (ret_pb->status_block.osak_status_1 != OSAK_S_NORMAL) { printf ("An error has been reported in the status block of the\n"); printf ("parameter block returned by OSAK.\n") ; exit (0) ; } } } /****************************************************************************/ /* FUNCTION: wait_for_inbound */ /* */ /* This routine waits for an inbound event, i.e. it waits to receive an */ /* event from the initiator process. */ /* */ /* It uses osak_select to wait for the event (using the mask */ /* OSAK_C_READEVENT) and picks up the event using osak_get_event. Buffers */ /* need to given to OSAK before an event can be read. The routine */ /* give_buffers is used to pass buffers to OSAK when they are needed (it */ /* calls osak_give_buffers to do this.) */ /* */ /* If we wanted to wait for an outbound completion (if OSAK returned */ /* OSAK_S_QUEUED or OSAK_S_FREE from a previous call) as well as an inbound */ /* event then we should have used the mask OSAK_C_WRITEEVENT | */ /* OSAK_C_READEVENT. We would then have to check the returned mask from */ /* osak_select to see what action to take: whether to call osak_collect_pb */ /* or osak_get_event or both. The example INITIATOR.C does that in its */ /* routine wait_for_event. */ /****************************************************************************/ void wait_for_inbound (osak_port port, struct osak_parameter_block *pb) { osak_handle_count handlecount ; osak_handle handle ; unsigned long int status ; osak_time select_time ; /* Give a buffer to OSAK to get inbound event */ give_buffer (port) ; /* Loop until an event is received */ do { /* Set up parameter to call osak_select() */ handlecount = 1 ; handle.id = (unsigned long int) port ; handle.request_mask = OSAK_C_READEVENT ; handle.returned_mask = 0 ; select_time = OSAK_EXAMPLE_TIMEOUT ; status = osak_select (handlecount, &handle, &select_time) ; if (status != OSAK_S_NORMAL) { printf ("call to osak_select failed\n") ; exit (0) ; } /* See if there is an inbound event. If so call osak_get_event() */ if (handle.returned_mask & OSAK_C_READEVENT) { do { /* Initialize parameter block */ memset ((void *)pb, '\0', sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE ) ; pb->pb_length = sizeof (struct osak_parameter_block) ; pb->ws_length = OSAK_EXAMPLE_WS_SIZE ; pb->completion_rtn = NULL ; pb->completion_param = 0 ; status = osak_get_event (port, pb) ; /* If OSAK needs more buffer to decode the event then give */ /* more buffers. */ if (status == OSAK_S_NOBUFFERS) { give_buffer (port) ; } } while (status == OSAK_S_NOBUFFERS) ; if ((status != OSAK_S_NORMAL) && (status != OSAK_S_NOEVENT)) { printf ("call to osak_get_event failed\n"); exit (0) ; } } } while (status == OSAK_S_NOEVENT) ; /* pb will now contain then decoded event. */ } /****************************************************************************/ /* FUNCTION: wait_for_TDISind */ /* */ /* This routine uses osak_select to wait for a transport disconnect */ /* indication after the release-response has been sent. */ /* */ /* It does not check the event since OSAK_S_NOEVENT may be returned. This */ /* would be the case when the peer did not send a disconnect. Osak_select */ /* would return either when it has timed out, or (on OpenVMS only) when the */ /* session disconnect timer fired. */ /****************************************************************************/ void wait_for_TDISind (osak_port port, struct osak_parameter_block *pb) { osak_handle_count handlecount ; osak_handle handle ; unsigned long int status ; osak_time select_time ; /* Give a buffer to OSAK to get inbound event */ give_buffer (port) ; /* Set up parameter to call osak_select() */ handlecount = 1 ; handle.id = (unsigned long int) port ; handle.request_mask = OSAK_C_READEVENT ; handle.returned_mask = 0 ; select_time = OSAK_EXAMPLE_TIMEOUT ; status = osak_select (handlecount, &handle, &select_time) ; if (status != OSAK_S_NORMAL) { printf ("call to osak_select failed\n") ; exit (0) ; } /* See if there is an inbound event. If so call osak_get_event() */ if (handle.returned_mask & OSAK_C_READEVENT) { /* Initialize parameter block */ memset ((void *)pb, '\0', sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE ) ; pb->pb_length = sizeof (struct osak_parameter_block) ; pb->ws_length = OSAK_EXAMPLE_WS_SIZE ; pb->completion_rtn = NULL ; pb->completion_param = 0 ; status = osak_get_event (port, pb) ; if ((status != OSAK_S_NORMAL) && (status != OSAK_S_NOEVENT)) { printf ("call to osak_get_event failed\n"); exit (0) ; } } } /****************************************************************************/ /* FUNCTION: give_buffer */ /* */ /* This routine is called to pass a buffer to OSAK for OSAK to used to */ /* receive inbound events. */ /* */ /* A list of unused buffers is maintained. One buffer from this list is */ /* passed to OSAK using osak_give_buffers. If the list is empty a new */ /* buffer is allocated. */ /****************************************************************************/ void give_buffer (osak_port port) { unsigned long int status ; struct osak_buffer *give_buf ; /* Give a buffer to OSAK */ if (free_buffers == NULL) { give_buf = (struct osak_buffer *)malloc (sizeof(struct osak_buffer)) ; if (give_buf == NULL) { printf ("Failed to allocate an osak_buffer.\n"); exit (0) ; } give_buf -> next = NULL ; give_buf -> buffer_length = OSAK_EXAMPLE_BUFFER_SIZE ; give_buf -> buffer_ptr = (unsigned char *) malloc (OSAK_EXAMPLE_BUFFER_SIZE) ; if (give_buf -> buffer_ptr == NULL) { printf ("Failed to allocate buffer.\n") ; exit (0) ; } } else { give_buf = free_buffers ; free_buffers = free_buffers -> next ; give_buf -> next = NULL ; } status = osak_give_buffers (port, give_buf) ; if (status != OSAK_S_NORMAL) { printf ("osak_give_buffers failed\n"); exit (0) ; } } /****************************************************************************/ /* FUNCTION: reuse_buffers */ /* */ /* This routine is called to place buffers returned by OSAK onto the list */ /* of unused buffers. */ /****************************************************************************/ void reuse_buffers (struct osak_buffer **buf_ptr) { struct osak_buffer *buf, *last_buf ; buf = *buf_ptr ; if (buf == NULL) return ; last_buf = buf ; while (last_buf->next != NULL) last_buf = last_buf -> next ; if (free_buffers == NULL) { free_buffers = buf ; } else { free_buffers_end->next = buf ; } free_buffers_end = last_buf ; *buf_ptr = NULL ; } /****************************************************************************/ /* FUNCTION: display_data */ /* */ /* Print out the data in the buffer 'buf'. */ /****************************************************************************/ void display_data (struct osak_buffer *buf) { unsigned char *ptr = buf->data_ptr ; unsigned long int length = buf->data_length ; int count = 0 ; while (length--) { if (count == 0) { count = 12 ; printf ("\n\t") ; } count-- ; printf ("0x%02X ", *ptr++) ; } printf ("\n"); } /****************************************************************************/ /* FUNCTION: alloc_memory */ /* */ /* Memory allocation routine expected by OSAK */ /****************************************************************************/ void * alloc_memory (unsigned long int size, unsigned long int param) { return (void *) malloc (size) ; } /****************************************************************************/ /* FUNCTION: free_memory */ /* */ /* Memory deallocation routine expected by OSAK */ /****************************************************************************/ unsigned long int free_memory (unsigned long int size, void *ptr, unsigned long int param) { free (ptr) ; return 0; }