[TCP] Example-C Creating and Deleting an Outbound TELNET Device

PRODUCT: TCP/IP Services for OpenVMS, Version 5.1 HP C for OpenVMS Alpha V6.5
OP/SYS: OpenVMS Alpha, Version 7.3
SOURCE: Philippe Vouters Fontainebleau/France
LOW-COST HIGH-TECH: http://techno-star.fr
OVERVIEW: This program shows how to eventually create and delete an outbound TELNET session, much like the TELNET/CREATE_SESSION and the TELNET/DELETE_SESSION perform. To test if the remote connection to a DECserver Internet port is successfull a small text is output to the DECserver port terminal by means of a terminal QIO.
*** CAUTION *** This sample program has been tested using DEC TCP/IP V5.1 ECO 4, Compaq C, V6.5 on OpenVMS Alpha V7.3. However, we cannot guarantee its effectiveness because of the possibility of error in transmitting or implementing it. It is meant to be used as a template for writing your own program, and may require modification for use on your system.
PROGRAM NOTES: Compile/link the program the following way: $ cc tn + alpha$library:sys$lib_c/library $ link tn Define a foreign command and activate the program the following way: $ tn :== $disk:[directory]tn.exe $ tn TNA<device_number> <Access String> [<"String to output">] The <Access String> is in the form : <IP hostname or IPv4 address>:<IP Port Number> Make sure you correctly configured a remote Telnet access DECserver port. For example: $ tn TNA1000 16.188.23.46:2008 with 16.188.23.46 the IP address of the DECserver and 2008 the IP port number of the remote Telnet access DECserver port. If you followed the IP port assignments convention, it should correspond to the DECserver port 8. If you do not have a handy DECserver to use, you may use the command procedure at the end of this article to test on. If you do, specify the IP port as 5020.
PROGRAM: /* COPYRIGHT (C) 2002 BY HEWLETT-PACKARD COMPANY, ALL RIGHTS RESERVED. THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY TRANSFERRED. THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY HEWLETT-PACKARD COMPANY. HP ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY HP. NO RESPONSIBILITY IS ASSUMED FOR THE USE OR RELIABILITY OF SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY HEWLETT-PACKARD COMPANY. SUPPORT FOR THIS SOFTWARE IS NOT COVERED UNDER ANY HP SOFTWARE PRODUCT SUPPORT CONTRACT, BUT MAY BE PROVIDED UNDER THE TERMS OF THE CONSULTING AGREEMENT UNDER WHICH THIS SOFTWARE WAS DEVELOPED. */ /* * Compilation, link and activation: * $ CC TN + ALPHA$LIBRARY:SYS$LIB_C/LIBRARY * $ LINK TN * $ TN :== $disk[directory]TN.EXE * $ TN <TNA device> <Access port string> "<String to output>" * The <Access port string> parameter is the comnination of a IP hostname * or address and an IP port. * Examples : * * HALLES > tn tna1000 dscom1:2003 "This is a text to TELNET port" * HALLES > tn tna1000 15.129.24.173:2003 "This is a text to TELNET port" * * This creates terminal TNA1000: and set the Remote Port address and port * to 15.129.24.173, IP port 2003 which corresponds to a DECserver IP, * terminal port 3. */ #ifndef _SOCKADDR_LEN #define _SOCKADDR_LEN /* this is required for the BSD 4.4 struct sockaddr */ #endif #include <types.h> #include <socket.h> #include <in.h> #include <inet.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <tniodef.h> #include <tcpip$inetdef.h> #include <descrip.h> #include <starlet.h> #include <iodef.h> #include <ssdef.h> #include <dcdef.h> #include <ttdef.h> #include <dvidef.h> #include <devdef.h> #include <ucbdef.h> #define OUTPUT_MESSAGE "\015\012This is a text to TELNET port" #ifdef __ALPHA #pragma member_alignment save #pragma nomember_alignment #endif typedef struct { /* I/O Status Block */ unsigned short status; unsigned short count; unsigned long funcdep; } iosb_t; typedef struct { /* Structure descriptor */ unsigned long length; void *address; } struct_desc_t; typedef struct { /* Item definition for TELNET QIO services */ unsigned short length; unsigned short code; void *buffer; } item_l2_t; typedef struct { /* Standard OpenVMS item definition */ unsigned short length; unsigned short code; void *buffer; void *retlen; } item_t; typedef struct { unsigned char class; unsigned char type; unsigned short width; unsigned int termchar:24; unsigned char length; } termchar_t; #ifdef __ALPHA #pragma member_alignment restore #endif typedef struct { int *flink; int (*exit_handler)(); unsigned int arg; int *status; unsigned short *channels; }desblk_t; /* * The delete_tna_device acts very much like the TELNET> DELETE_SESSION in * all its visible aspects on the target TNA and associated BG device. */ int delete_tna_device(char *tnname){ struct dsc$descriptor_s tn_dev = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; struct dsc$descriptor_s bg_dev = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; int status; item_l2_t *itmlst; item_t *getdvi_itmlst; iosb_t iosb; int devchar; int devchar2; int devsts; unsigned short tnchan,BGchan; unsigned short len; char BGname[64]; tn_dev.dsc$w_length = strlen(tnname); tn_dev.dsc$a_pointer=tnname; getdvi_itmlst = calloc(4,sizeof(item_t)); getdvi_itmlst[0].length = sizeof(devchar); getdvi_itmlst[0].code = DVI$_DEVCHAR; getdvi_itmlst[0].buffer = &devchar; getdvi_itmlst[0].retlen = &len; getdvi_itmlst[1].length = sizeof(devchar2); getdvi_itmlst[1].code = DVI$_DEVCHAR2; getdvi_itmlst[1].buffer = &devchar2; getdvi_itmlst[1].retlen = &len; getdvi_itmlst[2].length = sizeof(devsts); getdvi_itmlst[2].code = DVI$_STS; getdvi_itmlst[2].buffer = &devsts; getdvi_itmlst[2].retlen = &len; status = sys$getdviw( 0, 0, &tn_dev, getdvi_itmlst, &iosb, NULL, 0, NULL); free (getdvi_itmlst); if (status & 1) status = iosb.status; if (!(status & 1)) { if ((status != SS$_NOSUCHDEV) && (status != SS$_DEVOFFLINE)) printf("Failed to get %s device dependent characteristics.\n", tnname); return(status); } /* * Check inbound Telnet devices and templates or temporary UCBs. * Do not delete them. */ if ((!(devchar & DEV$M_AVL)) || (devchar2 & DEV$M_RED) || ((devsts & UCB$M_TEMPLATE) & (!(devsts & UCB$M_DELETEUCB)))) return (SS$_ABORT); status = sys$assign(&tn_dev, &tnchan, 0, 0); if (!(status & 1)) { printf("Failed to assign channel to %s channel.\n",tnname); return(status); } /* * Allocate and Build the item list. */ itmlst = calloc(2,sizeof(item_l2_t)); itmlst[0].length = sizeof(BGname); itmlst[0].code = TN$_NETWORK_DEVICE_NAME; itmlst[0].buffer = BGname; status = sys$qiow(0, tnchan, IO$_TTY_PORT_BUFIO | IO$M_TN_SENSEMODE, &iosb, 0, 0, 0, 0, 0, 0, itmlst , 0); free(itmlst); /* * At this point under the OpenVMS debugger, we retreive the * BG device name that has been visible after the WRITEVBLK. * DBG> exa/ascic BGname * TN\delete_tna_device\BGname: 'BG18206:' */ if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to SENSEMODE %s device.Status=%1x\n",tnname,status); return(status); } bg_dev.dsc$w_length = BGname[0]; bg_dev.dsc$a_pointer=&BGname[1]; status = sys$assign(&bg_dev, &BGchan, 0, 0); if (!(status & 1)) { printf("Failed to assign channel to %.*s.\n",BGname[0],&BGname[1]); return(status); } status = sys$qiow(0, tnchan, IO$_TTY_PORT | IO$M_TN_SHUTDOWN, &iosb, 0, 0, BGchan, 0, 0, 0, 0 , 0); if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to SHUTDOWN %s device.Status=%1x\n",tnname,status); return(status); } /* * Deassign BG device channel. * At this point, one can see the following: * HALLES > tcpip sho dev bg18206 * * Port Remote * Device_socket Type Local Remote Service Host * * bg18206 STREAM 49230 2003 15.129.24.173 */ status = sys$dassgn(BGchan); /* * At this point: * HALLES > tcpip sho dev bg18206 * %TCPIP-W-NODEVSOCK, device_socket not found * HALLES > telnet * TELNET> sho dev tna1000 * TNA1000: Outgoing Local: (none) * Remote: (none) * TELNET> sho dev tna1000/full * Device TNA1000: * Access port name: (none) * Characteristics: (none) * Connection attempts: 0 (tries) * Connection interval: 0 (seconds) * Connection timeout: 0 (seconds) * Data high limit: 512 (bytes at VCI port) * Data low limit: 256 (bytes at VCI port) * Idle interval: 0 (seconds) * Idle timeout: 0 (seconds) * Network device name: (not connected) * Protocol: undefined (raw) * Local address: (not available) * port: * Remote address: (not available) * port: * Service type: Outgoing * TELNET> */ if (!(status & 1)){ printf("Failed to deassign the %.*s channel.\n",BGname[0],&BGname[1]); return(status); } /* * Deassign temporary channel. */ status = sys$dassgn(tnchan); if (!(status & 1)){ printf("Failed to deassign the %s channel.\n",tnname); return(status); } /* * At this point: * HALLES > telnet * TELNET> sho dev tna1000 * TNA52: Temporary Local: (none) * Remote: (none) * TELNET> */ return (status); } /* * The create_tna_device acts very much like the TELNET> CREATE_SESSION in * all its visible aspects on the created TNA and associated BG device. */ int create_tna_device(int argc,char **argv){ $DESCRIPTOR(tna0_dev,"TNA0:"); $DESCRIPTOR(inet_dev,"UCX$DEVICE"); struct dsc$descriptor_s dev = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; unsigned short tna0chan, BGchan; struct { unsigned char len; char devname[63]; } ASCIC_devname; int protocol = TN$K_PROTOCOL_UNDEFINED; int service_type = TN$K_SERVICE_OUTGOING; int status; int devchar; item_l2_t *itmlst; item_t *getdvi_itmlst; iosb_t iosb; int characteristics = TN$M_PERMANENT_UCB | TN$M_RETAIN_ON_DASSGN | TN$M_AUTOCONNECT; int device_unit; char junk[50]; unsigned short len; struct sockaddr_in to; struct hostent *hp; char *tmp; struct IL2 { unsigned short il2_length; unsigned short il2_code; void *il2_address; } rhst_adrs; unsigned short sck_parm[2]; /* * Check this is the TNA device. */ if (strncasecmp(argv[1],"TNA",3) != 0) return (SS$_ABORT); /* Get device unit. */ status = sscanf(argv[1],"%c %c %c %d %c %s",&junk[0],&junk[1],&junk[2], &device_unit,&junk[3],&junk[4]); if ((status < 4) || (status == 6)) return (SS$_ABORT); dev.dsc$w_length = strlen(argv[1]); dev.dsc$a_pointer = argv[1]; getdvi_itmlst = calloc(2,sizeof(item_t)); getdvi_itmlst[0].length = sizeof(devchar); getdvi_itmlst[0].code = DVI$_DEVCHAR; getdvi_itmlst[0].buffer = &devchar; getdvi_itmlst[0].retlen = &len; status = sys$getdviw( 0, 0, &dev, getdvi_itmlst, &iosb, NULL, 0, NULL); free (getdvi_itmlst); if (status & 1) status = iosb.status; if ((status & 1)) { if (!(devchar & DEV$M_AVL)) return (SS$_DEVOFFLINE); printf("Device %s device already exists.Not created\n",argv[1]); return(SS$_DEVALRALLOC); } strcpy(junk,argv[2]); tmp = strrchr(junk,':'); if (!tmp) return (SS$_ABORT); *tmp++=0; memset(&to,0,sizeof(to)); to.sin_len=sizeof(to); to.sin_family=AF_INET; to.sin_port=htons(atoi(tmp)); to.sin_addr.s_addr=inet_addr(junk); if ((int)to.sin_addr.s_addr == -1){ hp = gethostbyname(junk); if (hp) memcpy ((char *)&to.sin_addr.s_addr,(char *)*hp->h_addr_list, hp->h_length); else{ printf("No such host %s\n",junk); return(SS$_ABORT); } } status = sys$assign(&tna0_dev, &tna0chan, 0, 0); if (!(status & 1)) { printf("Failed to assign channel to TNA0.\n"); return(status); } /* * Allocate and Build the item list. */ itmlst = calloc(6,sizeof(item_l2_t)); itmlst[0].length = sizeof (int); itmlst[0].code = TN$_SERVICE_TYPE; itmlst[0].buffer = &service_type; itmlst[1].length = sizeof(to); itmlst[1].code = TN$_REMOTE_ADDRESS; itmlst[1].buffer = &to; itmlst[2].length = sizeof (int); itmlst[2].code = TN$_PROTOCOL; itmlst[2].buffer = &protocol; itmlst[3].length = sizeof (int); itmlst[3].code = TN$_DEVICE_UNIT; itmlst[3].buffer = &device_unit; itmlst[4].length = sizeof (int); itmlst[4].code = TN$_CHARACTERISTICS; itmlst[4].buffer = &characteristics; /* * Create TNAxxx: device and set device characteristics. * When the below statement is executed, we have something * like: * HALLES > telnet * TELNET> sho dev tna1000 * TNA1000: Outgoing Local: (none) * Remote: ::782c:d77b:0:0:2003 * TELNET> sho dev tna1000/full * Device TNA1000: * Access port name: (none) * Characteristics: Autoconnect Permanent * Connection attempts: 0 (tries) * Connection interval: 0 (seconds) * Connection timeout: 0 (seconds) * Data high limit: 512 (bytes at VCI port) * Data low limit: 256 (bytes at VCI port) * Idle interval: 0 (seconds) * Idle timeout: 0 (seconds) * Network device name: (not connected) * Protocol: undefined (raw) * Local address: (not available) * port: * Remote address: ::782c:d77b:0:0 * port: 2003 * Service type: Outgoing * TELNET> */ status = sys$qiow(0, tna0chan, IO$_TTY_PORT_BUFIO | IO$M_TN_SETMODE, &iosb, 0, 0, 0, 0, 0, 0, itmlst , 0); free(itmlst); if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to create %s terminal.\n",argv[1]); return(status); } /* * Deassign TNA0 temporary channel. */ status = sys$dassgn(tna0chan); if (!(status & 1)){ printf("Failed to deassign the TNA0 channel.\n"); return(status); } /* * Connect to TNAxxxx specified in program's as the first parameter. */ status = sys$assign(&dev, &tna0chan, 0, 0); if (!(status & 1)) { printf("Failed to assign channel to %s.\n",argv[1]); return(status); } /* * Create and connect to the remote service a BG device. */ status = sys$assign(&inet_dev, &BGchan, 0, 0); if (!(status & 1)) { printf("Failed to assign channel to UCX$DEVICE.\n"); return (status); } sck_parm[0] = TCPIP$C_TCP; /* TCP/IP protocol */ sck_parm[1] = INET_PROTYP$C_STREAM; /* stream type of socket */ /* * Create the SOCK_STREAM type socket */ status = sys$qiow(0, /* Event flag */ BGchan, /* Channel number */ IO$_SETMODE, /* I/O function */ &iosb, /* I/O status block */ 0, 0, &sck_parm, 0, /* P1 Socket creation parameter */ 0, 0, 0, 0); if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to create the SOCK_STREAM socket.\n"); return(status); } /* * Setup the item_list_2 descriptor required by the IO$_ACCESS QIO. */ rhst_adrs.il2_length = sizeof(to); rhst_adrs.il2_code = TCPIP$C_SOCK_NAME; rhst_adrs.il2_address = &to; /* * Connect to specified host and port number. */ status = sys$qiow(0 , /* Event flag */ BGchan, /* Channel number */ IO$_ACCESS, /* I/O function */ &iosb, /* I/O status block */ 0, 0, 0, 0, /* AST addr, param, P1, P2 */ &rhst_adrs, /* P3 Remote IP address */ 0, 0, 0); /* P4, P5, P6 */ if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to connect to remote host.\n"); return(status); } /* * Binds the socket to a TELNET terminal. */ status = sys$qiow(0, tna0chan, IO$_TTY_PORT | IO$M_TN_STARTUP, &iosb, 0, 0, BGchan, protocol, characteristics, 0, 0, 0); if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to bind to a TELNET terminal.\n"); return(status); } /* * Deassign BG temporary channel. */ status = sys$dassgn(BGchan); if (!(status & 1)){ printf("Failed to deassign the BG device channel.\n"); return(status); } /* * Deassign TNAxxxx temporary channel. */ status = sys$dassgn(tna0chan); if (!(status & 1)){ printf("Failed to deassign the TNA0 channel.\n"); return(status); } /* * At this point we deassigned all channels. * Check the TNA and BG device. If they're gone, we're wrong. * HALLES > telnet * TELNET> sho device tna1000:/full * Device TNA1000: * Access port name: "15.129.24.173:2003" * Characteristics: Autoconnect Permanent * Connection attempts: 0 (tries) * Connection interval: 0 (seconds) * Connection timeout: 0 (seconds) * Data high limit: 512 (bytes at VCI port) * Data low limit: 256 (bytes at VCI port) * Idle interval: 0 (seconds) * Idle timeout: 0 (seconds) * Network device name: BG21509: * Protocol: undefined (raw) * Local address: ::782c:d77b:0:0 * port: 49291 * Remote address: ::782c:d77b:0:0 * port: 2003 * Service type: Outgoing * TELNET> * So, all looks good we have a TNA and an associated BG device. * Check the BG device. HALLES > tcpip sho dev BG21509:/full Device_socket: bg21509 Type: STREAM LOCAL REMOTE Port: 49291 2003 Host: 15.129.24.133 15.129.24.173 Service: RECEIVE SEND Queued I/O 0 0 Q0LEN 0 Socket buffer bytes 0 0 QLEN 0 Socket buffer quota 131072 131072 QLIMIT 0 Total buffer alloc 0 0 TIMEO 0 Total buffer limit 1048576 1048576 ERROR 0 Buffer or I/O waits 1 0 OOBMARK 0 Buffer or I/O drops 0 0 I/O completed 1 0 Bytes transferred 0 0 Options: None State: ISCONNECTED PRIV ASYNC RCV Buff: ASYNC SND Buff: ASYNC * Everything looks good. We can thus say the create_tna_device * is emulating some sort of the TELNET> CREATE_SESSION command. * However our TNA device has the characteristics * "Autoconnect Permanent" whereas a TNA device created with the * TELNET> CREATE_SESSION as the characteristics "Permanent" only. * What does this imply for the sake of our code ? */ return (status); } int cleanup (int *stat,unsigned short *tnchan) { int status; /* used in RTL calls */ iosb_t iosb; /* I/O status block */ item_l2_t *itmlst; item_t *getdvi_itmlst; char tnname[64]; char BGname[64]; struct sockaddr to; struct dsc$descriptor_s BGdsc={0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}; unsigned short len; printf ("Exiting the application ...\n"); /* * Get device name using $getdvi. */ getdvi_itmlst = calloc(2,sizeof(item_t)); getdvi_itmlst[0].length = sizeof(tnname); getdvi_itmlst[0].code = DVI$_DEVNAM; getdvi_itmlst[0].buffer = &tnname; getdvi_itmlst[0].retlen = &len; status = sys$getdviw( 0, *tnchan, 0, getdvi_itmlst, &iosb, NULL, 0, NULL); free (getdvi_itmlst); if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Unable to get device name. Channel closed.\n"); if (*stat & 1) return (status); else return (*stat); } status = sys$dassgn(*tnchan); if (!(status & 1)){ printf("Failed to deassign the %s channel.\n",tnname); if (*stat & 1) return (status); else return (*stat); } /* * Here comes a huge difference with the TELNET> CREATE_SESSION * command. If you wait too long here, the TNA device gets * automatically deleted. That's why we test here the SS$_NOSUCHDEV * and SS$_DEVOFFLINE statuses. Attempt to delete TNA device. */ status = delete_tna_device(tnname); if ((status == SS$_NOSUCHDEV) || (status == SS$_DEVOFFLINE)) return(*stat); if (!(status & 1)){ printf ("Failed to delete %s device.\n",tnname); if (*stat & 1) return (status); else return (*stat); } return (*stat); } int main(int argc,char **argv){ $DESCRIPTOR (inet_dev,"UCX$DEVICE"); unsigned short tnchan,socket_channel; int status; iosb_t iosb; desblk_t desblk; short sck_parm[2]; /* Socket creation parameter */ termchar_t termchar; char buffer[100]; struct dsc$descriptor_s tn_dev = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; if (argc <3){ printf ("Usage : TN <TNA Device> <Access port string>\n"); exit(1); } status = create_tna_device(argc,argv); if (!(status & 1)){ printf ("Failed to create %s device.\n",argv[1]); exit(status); } else{ /* * Set TNAxxx: string descriptor. */ tn_dev.dsc$w_length = strlen(argv[1]); tn_dev.dsc$a_pointer = argv[1]; /* * Assign and return channel to dynamically created device. */ if (!((status = sys$assign(&tn_dev, &tnchan, 0, 0)) & 1)) { printf ("Failed to assign channel from %s device.\n",argv[1]); exit(status); } } /* * Initialize the declare exit handler data block and call $DCLEXH */ desblk.exit_handler = &cleanup; desblk.arg = 2; desblk.status = &status; desblk.channels = &tnchan; status = sys$dclexh(&desblk); if (!(status & 1)) { printf("Failed to declare exit handler.\n"); exit(status); } /* * Get the current terminal characteristics for this TNAxxx device. */ status = sys$qiow (0, tnchan, IO$_SENSEMODE, &iosb, 0, 0, &termchar, sizeof (termchar), 0, 0, 0, 0); if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to get %s terminal characteristics.\n",argv[1]); exit(status); } /* * Set the terminal type for this TNAxxx device. */ if (termchar.class != DC$_TERM) exit (SS$_ABORT); termchar.type = TT$_VT300_SERIES; termchar.width = 80; termchar.length = 24; status = sys$qiow (0, tnchan, IO$_SETCHAR, &iosb, 0, 0, &termchar, sizeof (termchar), 0, 0, 0, 0); if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to set %s terminal characteristics.\n",argv[1]); exit(status); } if (argc == 4) strcpy(buffer,argv[3]); else strcpy(buffer,OUTPUT_MESSAGE); /* * Write I/O buffer on TNAxxx channel. */ status = sys$qiow(0, /* Event flag */ tnchan, /* Channel number */ IO$_WRITEVBLK, /* I/O function */ &iosb, /* I/O status block */ 0, 0, buffer, /* P1 buffer */ strlen(buffer), /* P2 buffer length */ 0, 0, 0, 0); if (status & 1) status = iosb.status; if (!(status & 1)) { printf("Failed to write to remote TELNET terminal.\n"); exit(status); } /* * Invoke exit handler. */ exit(SS$_NORMAL); }
REFERENCE(S): Compaq TCP/IP Services for OpenVMS Sockets API and System Services Programming Chapter 6.3 - TELNET Port Driver I/O Function Codes System Services Reference Manual A-GETMSG Routine $GETDVI
PROCEDURE FOR TESTING WITHOUT A DECSERVER PORT:
PROCEDURE NOTE: Extract the code below to a file named for example AUXSERVER.COM. Activate the TN program, and then enter the following command: $ TYPE SYS$LOGIN:AUXSRV.LOG
PROCEDURE: $ verify = f$verify(0) $ if f$search ("auxsrv.com") .nes. "" then - delete/nolog/noconfirm auxsrv.com;* $ if f$search ("auxserver.exe") .nes. "" then - delete/nolog/noconfirm auxserver.exe;* $ ucx sho service/permanent mark $ if $status .ne. 1 then goto start $ ucx disable service mark $ ucx set noservice mark Y $start: $ create auxserver.c #include <errno.h> #include <types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <descrip.h> #include <starlet.h> #include <pthread.h> #ifdef __DECC #pragma extern_prefix save #pragma extern_prefix "DECC$" #endif #include <unixio.h> #include <socket.h> #include <in.h> #include <netdb.h> /* change hostent to comply with BSD 4.3 */ #include <inet.h> #ifdef __DECC #pragma extern_prefix restore #endif #include <ucx$inetdef.h> /* INET symbol definitions */ #ifndef FD_SET #ifndef __DECC struct fd_set_struct {u_char fds_bits[64/8];}; typedef struct fd_set_struct fd_set; #endif #define NFDBITS sizeof(fd_set)/sizeof (u_char) #define FD_SETSIZE NFDBITS #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) memset((char *)(p), 0,sizeof(*(p))) #endif #define AUXSERVER_SERVICE "mark" typedef struct { pthread_attr_t *attr; pthread_mutex_t *mutex; int s; } arg_t; cleanup(int s) { int retval; /* * If given, shutdown and close sock2. */ retval = shutdown(s,2); if (retval == -1) perror ("shutdown"); retval = close (s); if (retval) perror ("close"); } /* end cleanup*/ /* * This is our per socket thread. */ void *serve_connection (void *ptr){ fd_set readmask; int flag; arg_t *arg =(arg_t *)ptr; int r,retval; char message[BUFSIZ]; for (;;) { FD_ZERO(&readmask); FD_SET(arg->s,&readmask); /* Is the socket s readable ? If yes, the bit at position s in readmask */ /* will be set to one enabling us to receive data from the remote partner */ switch(select (arg->s+1,&readmask,NULL,NULL,NULL)){ case -1: pthread_mutex_lock(arg->mutex); perror("Socket select error"); pthread_mutex_unlock(arg->mutex); goto out; case 0 : pthread_mutex_lock(arg->mutex); printf ("No data received ??\n"); pthread_mutex_unlock(arg->mutex); goto out; case 1 : if (FD_ISSET (arg->s,&readmask)) { /* * Receive message from socket s */ flag = 0; /* maybe 0 or MSG_OOB or MSG_PEEK */ if ((r = recv(arg->s, message ,sizeof (message), flag))<= 0){ if (r<0){ pthread_mutex_lock(arg->mutex); perror (" error on read"); pthread_mutex_unlock(arg->mutex); } goto out; } pthread_mutex_lock(arg->mutex); printf (" received length ::> %d\n", r); printf (" Received Message: %.*s\n",r,message); fflush(stdout); fsync(fileno(stdout)); pthread_mutex_unlock(arg->mutex); } }/* end switch */ } /* end for(;;) */ out:; cleanup (arg->s) ; free(arg->attr); free(arg); return(NULL); } main(argc,argv) int argc; /* we dont use these in this program */ char **argv; { int s; /* sockets */ int on = 1; struct sockaddr_in s_name; /* Address struct for socket2.*/ struct hostent hostentstruct; /* Storage for hostent data. */ struct hostent *hostentptr; /* Pointer to hostent data. */ char hostname[256]; /* Name of local host. */ unsigned int namelength; int drop_option = 1; /* 1 seconds for the drop timer */ arg_t *arg; pthread_t dynthread; pthread_mutex_t *mutex; struct servent *service; printf(" beginning of the program\n "); if ((s = socket (AF_INET, SOCK_STREAM, 0)) == -1){ perror ("socket error") ; exit (1) ; } if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0){ perror("error on setsocketopt (SO_REUSEADDR)"); exit (1) ; } /* * Setup the sockaddr_in structure. */ s_name.sin_family = AF_INET ; /* * Get the service port number. */ if ((service = getservbyname(AUXSERVER_SERVICE,"tcp"))== NULL){ perror("getservbyname error"); exit(1); } s_name.sin_port = service->s_port ; s_name.sin_addr.s_addr = htonl(INADDR_ANY); /* * Associate the the socket number with the structure (family/port/inet addr) */ if (bind (s,(struct sockaddr *)&s_name, sizeof (s_name))<0){ perror ("bind error") ; cleanup (s) ; exit(1); } /* * Setup the drop option. */ if (setsockopt (s , UCX$C_TCPOPT, UCX$C_TCP_DROP_IDLE, &drop_option, sizeof (drop_option))<0){ perror("error on setsocketopt (UCX$C_TCP_DROP_IDLE)"); cleanup(s); exit(1); } /* * Accept 5 outstanding (queued) connections. */ if (listen (s, 5) < 0){ perror ("listen error") ; cleanup (s) ; exit(2); } mutex = malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(mutex,NULL); for (;;){ namelength = sizeof (s_name); arg = malloc(sizeof (arg_t)); arg->mutex = mutex; /* * Hang here until a connection comes up. */ arg->s = accept (s,(struct sockaddr *)&s_name,&namelength) ; if (arg->s == -1){ pthread_mutex_lock(mutex); perror ("accept error") ; pthread_mutex_unlock(mutex); cleanup (s); free(arg); pthread_mutex_destroy(mutex); exit(2); } pthread_mutex_lock(mutex); printf ("Client address : %s\n",inet_ntoa(s_name.sin_addr)) ; printf ("Client port : %d\n",ntohs(s_name.sin_port) ) ; pthread_mutex_unlock(mutex); /* * Start the thread that will serve the connection */ arg->attr = malloc(sizeof(pthread_attr_t)); pthread_attr_init(arg->attr); pthread_attr_setstacksize(arg->attr,PTHREAD_STACK_MIN+BUFSIZ); pthread_attr_setdetachstate(arg->attr,PTHREAD_CREATE_DETACHED); pthread_create(&dynthread, arg->attr, serve_connection, arg); } } /* end main */ /* end of example source code */ $ cc/prefix=all/reentrancy=multithread auxserver $ delete auxserver.c;* $ link auxserver $ delete auxserver.obj;* $ this_proc = f$env("PROCEDURE") $ this_dev=f$parse(this_proc,,,"DEVICE") $ this_dir=f$parse(this_proc,,,"DIRECTORY") $ nodename = f$getsyi ("NODENAME") $ username = f$getjpi("","USERNAME") $ open/write fred auxsrv.com $! write fred "$DEFINE SYS$INPUT NL:" $! write fred "$ DEFINE SYS$OUTPUT NL:" $ write fred "$ RUN ''this_dev'''this_dir'AUXSERVER.EXE" $ close fred $ set verify $ ucx set service mark /port=5020 /user='username' /proc=markd - /file='this_dev''this_dir'AUXSRV.COM /flag=nolisten - /inactivity_timer=1 /prot=tcp /limit=40 $ ucx enable service mark $ if verify .eq. 1 $ then $ set verify $ else $ set noverify $ endif $ write sys$output "Enter $ tn TNA1000 ''nodename':5020" $ exit
Did you find this helpful?