home *** CD-ROM | disk | FTP | other *** search
- /****************************** KILLCONN.C ******************************
- **
- ** KILLCONN is a program which selectively clears workstation connections
- ** to fileservers running under Netware versions 2.x and up. It has
- ** one mandatory command line parameter (an indication of who it is you want
- ** to kill) and a number of optional parameters. The possible parameters
- ** are:
- **
- ** -help, -h, or /h: produces a help message.
- ** user=SOMEBODY : kill connection for user SOMEBODY.
- ** This parameter may contain Novell-type wildcards,
- ** but I recommend using the group parameter instead,
- ** since it keeps things cleaner administratively.
- ** group=SOMEGROUP : kill connections for all members of SOMEGROUP
- ** server=MYSERVE : by default, KILLCONN will go after the named victims
- ** on all the file servers it can find. Specifying a
- ** server will only break the victim's connection on
- ** the named server. Useful on internetworks when you
- ** only want to kill one.
- ** confirm=YES|NO : if confirm=YES is specified, KILLCONN will pause
- ** and prompt you before disconnecting each user.
- ** Default is NO; KILLCONN is intended for unattended
- ** operation.
- ** log=filename : writes a logfile of KILLCONN actions. A full DOS path
- ** can be specified. If a file with the name 'filename'
- ** exists, it will be overwritten.
- ** logadd=filename : same as log=, except that if the logfile exists,
- ** KILLCONN will append to it.
- **
- ** Two new features added 4-9-91: RETRIES and WARNING
- **
- ** warning=nn : gives user nn seconds warning before disconnecting
- ** retries=nn : retries the disconnect nn times before giving up.
- ** intended for slower networks.
- **
- ** ...and another thing: you may optionally create a group called
- ** IMMORTAL on any or all file servers; KILLCONN will ignore them.
- ** KILLCON also ignores any attempt to disconnect any station logged in
- ** with the same login_name as the station running KILLCONN.
- **
- */
- /**************************************************************************
- Some technoid stuff (I imagine if you're reading this you're at least
- a little bit interested):
-
- KILLCONN was built using BORLAND C/C++ 2.0, the TXCL interface library,
- and of course the Netware API for C. The TCXL library is cheap,
- excellent, and highly recommended. If for some reason you're rebuilding
- this code without it (tsk-tsk: better contact the author first....)
- it's only used in the do_titles, do_the_help_thing, and in
- process_list to let the user know what the hell is happening. You
- could easily substitute windowing routines of your own design.
-
- The overall program strategy works like this: we first set up a loop
- which will check each of the eight possible server connections at the
- workstation running KILLCONN. If it finds a server, good: we proceed to
- determine the maximum number of workstations which might be connected
- to that server, and then check each one to see if the user connected to
- it is a worthy victim as determined by the command-line parameters. We
- always reject ourselves and anyone belonging to the group IMMORTAL. This
- latter protects us from situations where a wildcard might be a little
- too wild. If we have a worthy victim, the appropriate information is
- added to a singly-linked list. After victim scanning, we walk through
- the list laying waste as we go; as Arnold Schwarzenegger so aptly put
- it in _Conan the Barbarian_, "We drive our enemies before us and hear
- the lamentations of their women." If confirmation and logging options
- are set, the program behaves accordingly.
-
- While this probably seems like a lot of iterations, we have to account
- for situations where we might have one user logged in with multiple
- connections (often the case where someone is using generic login_names
- for a student lab or other public facility, or where there might be
- "holes" in the controlling workstation's server table or the server's
- connection table caused by a connection being established and dropped
- sometime prior to the life of KILLCONN.
-
- Needless to say, the program IS copyrighted and the author, Thomas R.
- Bruce, cannot be held liable for any damage you do with it. Any changes
- to program code or function must be approved by the author:
-
- Thomas R. Bruce
- 147 Chestnut Street E-22
- Ithaca, NY 14850 607-255-1221 or 273-2661
-
- In cyberspace:
-
- lemuel!tom @ cs.cornell.edu
- or (infrequently checked) Compuserve 75360,542
- ***************************************************************************/
-
-
-
- /* assorted includes */
- #ifndef _STDIO_H
- #include <stdio.h>
- #endif
-
- #ifndef _STRING_H
- #include <string.h>
- #endif
-
- #ifndef _CONIO_H
- #include <conio.h>
- #endif
-
- #ifndef _DOS_H
- #include <dos.h>
- #endif
-
- #ifndef _STDLIB_H
- #include <stdlib.h>
- #endif
-
-
- /* TCXL headers */
-
- #include <TCXLdef.h>
- #include <TCXLwin.h>
- #include <TCXLinp.h>
-
- /* Novell API headers */
-
- #include <nit.h>
- #include <nxt.h>
- #include <niterror.h>
-
- /* assorted defines */
-
- /* Novell API data types from their headers. Included here in the
- ** interest of clarity.
- */
-
- #ifndef BYTE
- #define BYTE unsigned char
- #endif
-
- #ifndef WORD
- #define WORD unsigned short
- #endif
-
- #ifndef LONG
- #define LONG unsigned long
- #endif
-
- /* useful stuff local to this program */
-
- #ifndef YES
- #define YES 1
- #endif
-
- #ifndef NO
- #define NO 0
- #endif
-
- #define SINGLE_USER 1
- #define WILD_USER 2
- #define USER_GROUP 3
-
- #define NEW_LOG 4
- #define APPEND_LOG 5
- #define NO_LOG 6
-
- #define ALL_SERVERS 7
- #define ONE_SERVER 8
-
- #define TITLES 10
- #define NOTITLES 11
-
- #define CONFIRM 12
- #define NOCONFIRM 13
-
- #define WARNINGS 14
- #define NOWARNINGS 15
-
- #define ASTERISK 42 /* Yep, you got it: ASCII codes */
- #define QUESMARK 63
- #define SPACE 32
- #define HVYCHECK 178
-
- /* typedefs and whatnot */
-
- typedef struct userinfo{ /* Struct to hold info about a victim */
- char *username;
- long objectID;
- WORD connectionNumber;
- struct userinfo *next;
- }_userinfo;
-
- typedef _userinfo *USERPTR;
-
- /* function prototypes */
-
- void parse_cmd_line(int _argc, char *_argv[]);
- /* parses the command line (no kidding) */
- void do_titles(void); /* takes care of screen setup, etc. */
- void do_the_help_thing(int mode); /* guess what? */
-
- USERPTR getnode(void); /* memory allocation for list node */
- void freenode(USERPTR the_ptr); /* free a list node */
- void add_to_list(USERPTR targ_user); /* adds user info to the victim list */
- void listzap(USERPTR list_start); /* destroys list */
-
- void process_list(void); /* processes the victim list */
-
- int is_user_in_group(USERPTR targ_user, char *groupname);
- /* well, is she? */
- int is_wild_match(USERPTR targ_user, char *wildstring);
- /* does username match wildcard? */
-
- /* global variables */
-
- USERPTR list_head;
-
- /* program control. initialization here sets defaults */
-
- int scope= ALL_SERVERS;
- char targ_server[48]; /* target server if doing just one */
-
- int search_type= SINGLE_USER;
- char search_string[48]; /* who (all) we're looking for */
-
- int log_type= NO_LOG;
- int do_confirm= NOCONFIRM;
- char log_file[256]; /* 256 is the max under Netware */
-
- int warnings=NOWARNINGS;
- int warn_time=0;
-
- int retry_count=5;
-
- char curr_server[48]; /* current server we're looking at */
- WndT mainWin; /* handle for the main window */
- /* WndT is a TCXL data type. */
-
- void main(int argc, char *argv[]){
-
- int i,j,k; /* typical dumb index variable(s) */
-
- WORD serverConnID; /* connection ID of current server */
- FILE_SERV_INFO *serverInfo; /* all we could want to know and more */
- USERPTR this_user; /* struct for current user */
- WORD objType; /* junk variable used in NWare calls */
- BYTE loginTime[7];
- WORD testConn; /* more junk */
- char retname[48];
-
- /* a couple of initializations */
-
- this_user=getnode();
-
-
- /* get the preliminaries out of the way */
-
- do_titles();
- parse_cmd_line(argc,argv);
-
- /* test for IPX installation */
-
- if (IPXInitialize() == IPX_NOT_INSTALLED) {
- fprintf(stderr,"\n\tKILLCONN finds no IPX interface loaded. Aborting.\n");
- exit(2);
- }
-
- /* check to see if the workstation is actually logged in to something */
-
- serverConnID = GetPrimaryConnectionID();
- if (serverConnID == 0) {
- fprintf(stderr,"\n\tKILLCONN finds no primary file server. Aborting.\n");
- exit(3);
- }
-
-
- /* Per-server loop. */
-
- for(i=1;i<=8;i++){ /* Novell table numbering starts at 1 */
-
- /* figure out where the hell we are */
-
- GetFileServerName((WORD)i,curr_server);
-
- if ((curr_server==NULL) || (strcmp(curr_server,"")==0)) continue;
- if ((scope==ONE_SERVER)&& (strcmp(targ_server,curr_server)!=0)) continue;
- SetPreferredConnectionID((BYTE)i);
- SetPrimaryConnectionID((BYTE)i);
-
- if(CheckConsolePrivileges()!=0){
- fprintf(stderr,"\n\nNo console privileges on server %s. Aborting.", curr_server);
- exit(1);
- }
-
- GetServerInformation(128,serverInfo);
-
- /* Build the victim list. This strategy was selected over others because
- it would seem that the usual environment for the program will be one
- where not a lot of users are logged in. However, there is no
- guarantee that the logged-in connections will be sequential, or that
- one user is not logged in more than once on the file server, which
- makes all the iteration necessary. */
-
- list_head=NULL; /* zero out the list */
-
- for (j=1;j <= serverInfo->maxConnectionsSupported; j++){
- testConn=j; /* stupid way to force a type conversion,but mine own */
- GetConnectionInformation(testConn,retname,
- &objType,&(this_user->objectID),loginTime);
- this_user->connectionNumber=j;
-
- /* find out if the user is a worthy victim or not */
- /* if there's nobody using the connection, skip onward */
-
- if (this_user->objectID==0) continue;
-
- /* if the user is in the IMMORTAL group, skip onward */
-
- strcpy(this_user->username,retname);
- if (is_user_in_group(this_user,"IMMORTAL")==YES) continue;
-
- switch (search_type){
-
- case SINGLE_USER:
- if(strcmp(this_user->username,search_string)!=0) continue;
- add_to_list(this_user);
- break;
-
- case WILD_USER:
- if(is_wild_match(this_user, search_string)==YES) add_to_list(this_user);
- break;
-
- case USER_GROUP:
- if(is_user_in_group(this_user, search_string)==YES) add_to_list(this_user);
- break;
-
- } /* end switch on search type */
- } /* end of the per-connection loop */
-
- if (list_head != NULL) process_list(); /* g'wan and kill 'em */
- listzap(list_head); /* liberate the list */
-
- } /* end of per-server loop */
- clrscr();
- exit(0);
- } /* end of main () */
-
-
-
- void process_list(void){
-
- USERPTR q; /* all purpose victim pointer */
- int maxstr;
- FILE *out_file; /* handle for log file, if any */
- char *conf_string; /* junk string for messages */
- WORD myConnection; /* connection number of workstation
- running this mess */
- WORD msgConnList[100];
- BYTE *resultList;
- char *msg;
- int j, k;
- int confirmed;
-
- char myname[48];
- WORD myobjType;
- long myobjID;
- BYTE mylogTime[7]; /* all this just to find out who I am */
- BYTE dateAndTime[7];
- char *timeStr;
-
- timeStr=(char *)calloc(24,sizeof(char));
-
- myConnection=GetConnectionNumber();
- GetConnectionInformation(myConnection,myname,(WORD *) &myobjType,(long *)&myobjID,(BYTE *)&mylogTime);
-
- GetFileServerDateAndTime(dateAndTime);
- sprintf(timeStr,"%02d/%02d/%02d %02d:%02d:%02d:%02d",dateAndTime[1],dateAndTime[2],dateAndTime[0],dateAndTime[3],dateAndTime[4],dateAndTime[5],dateAndTime[6]);
-
- conf_string=(char *)calloc(128,sizeof(char));
- msg=(char *)calloc(56,sizeof(char));
-
- switch (log_type){
-
- case NEW_LOG:
- if((out_file=fopen(log_file,"wt"))==NULL)
- log_type=NO_LOG;
- break;
-
- case APPEND_LOG:
- if((out_file=fopen(log_file,"at"))==NULL)
- log_type=NO_LOG;
- break;
- }
-
- if (warnings==WARNINGS){ /* warn the little buggers */
- sprintf(conf_string," User warnings being sent. Please wait.", q->username);
- maxstr=max(strlen(conf_string),strlen(curr_server));
- WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
- Wshadow(_BLACK | DGREY);
- Wtitle(" Warning users....",TTL_CNT,LIGHTCYAN|_RED|INTENSE|BLINK);
- WprtCen(0,WHITE|_RED, curr_server);
- WprtCen(1,WHITE|_RED, conf_string);
- for (q=list_head; q!= NULL; q=q->next){
- msgConnList[0]=q->connectionNumber;
- sprintf(msg,"You will be disconnected in %d seconds. Log out.", warn_time);
- SendBroadcastMessage(msg,msgConnList,resultList,(WORD)1);
- }
-
- delay(1000*warn_time); /* hang on to your hat */
- Wclose();
-
-
- }
-
-
- for(q=list_head;q != NULL; q=q->next){
-
- if (strcmp(q->username,myname)==0)
- continue; /* wouldn't want to disconnect myself- or would I? */
- if (do_confirm==CONFIRM){ /* pop up a confirmation window */
- sprintf(conf_string," OK to disconnect %s (y/N)?", q->username);
- maxstr=max(strlen(conf_string),strlen(curr_server));
- WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
- Wshadow(_BLACK | DGREY);
- Wtitle(" Are you sure? ",TTL_CNT,LIGHTCYAN|_RED|INTENSE|BLINK);
- WprtCen(0,WHITE|_RED, curr_server);
- WprtCen(1,WHITE|_RED, conf_string);
-
- /* manual-abort handler */
-
- if(KwGetYn(NO)!='Y'){
- Wclose();
- sprintf(conf_string,"Disconnect of %s aborted.",q->username);
- maxstr=max(strlen(conf_string),strlen(curr_server));
- WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
- Wshadow(_BLACK | DGREY);
- Wtitle(" Cancelling... ",TTL_CNT,LIGHTCYAN|_RED|INTENSE|BLINK);
- WprtCen(0,WHITE|_RED, curr_server);
- WprtCen(1,WHITE|_RED, conf_string);
- if(log_type != NO_LOG) fprintf(out_file,"%s\tDisconnect of %s from %s was aborted by operator.\n",timeStr,q->username,curr_server);
- delay(2000);
- continue;
- }
-
- Wclose();
- }
-
- /* leave some evidence at the workstation */
-
- msgConnList[0]=q->connectionNumber;
- sprintf(msg,"Station cleared by KILLCONN at %s",timeStr);
- SendBroadcastMessage(msg,msgConnList,resultList,(WORD)1);
- confirmed=1;
- for(j=0;j < retry_count && confirmed!=0; ++j){
-
- sprintf(conf_string,"Disconnecting %s.", q->username);
- maxstr=max(strlen(conf_string),strlen(curr_server));
- WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
- Wshadow(_BLACK | DGREY);
- Wtitle(" Clearing connection... ",TTL_CNT,LIGHTCYAN | _RED|INTENSE|BLINK);
- WprtCen(0,WHITE|_RED, curr_server);
- WprtCen(1,WHITE|_RED, conf_string);
- delay(3000); /* to permit message to arrive */
-
- confirmed=ClearConnectionNumber(q->connectionNumber); /* He's dead, Jim */
- }
-
- if((log_type != NO_LOG) && (confirmed==0)) fprintf(out_file,"%s\t %s was disconnected from %s.\n",timeStr,q->username,curr_server);
- if((log_type != NO_LOG) && (confirmed!=0)) fprintf(out_file,"%s\t %s disconnect from %s failed.\n",timeStr,q->username,curr_server);
- if (confirmed!=0){
- Wclose();
- sprintf(conf_string,"Attempted disconnect of %s failed.", q->username);
- maxstr=max(strlen(conf_string),strlen(curr_server));
- WpopUp(CNT_CNT, 0, 0, 4, maxstr+5, BOX_EXP, WHITE |_RED, WHITE|_RED);
- Wshadow(_BLACK | DGREY);
- Wtitle(" Disconnect failed ",TTL_CNT,LIGHTCYAN | _RED|INTENSE|BLINK);
- WprtCen(0,WHITE|_RED, curr_server);
- WprtCen(1,WHITE|_RED, conf_string);
- }
- }
- /* clean up for this server-pass */
-
- free (conf_string);
- free (timeStr);
- fcloseall();
- if(Wisactiv(mainWin)==FALSE) Wclose();
- }
-
- int is_user_in_group(USERPTR targ_user, char *groupname){
-
- int retcode;
-
- retcode = IsBinderyObjectInSet(groupname, OT_USER_GROUP, "GROUP_MEMBERS",targ_user->username, OT_USER);
- if (retcode == 0) return (YES);
- else return (NO);
- }
-
-
- int is_wild_match(USERPTR targ_user, char *wildstring){
-
- int cCode=0;
-
- WORD srchObjType; /* type of object we're scanning for;
- the name can be wildcarded,
- which is why we're using this
- API call */
- long objID= -1L;
- char retName[48]; /* name returned by scanning function */
- WORD objType; /* better damn well be OT_USER and not
- a group or something else */
- char objHasProperties;
- char objFlag;
- char objSecurity;
-
- while (cCode==0){
- cCode=ScanBinderyObject(wildstring, OT_USER, &objID,
- retName, &objType, &objHasProperties, &objFlag, &objSecurity);
- if ((strcmp(retName,targ_user->username)==0)&& (objType==OT_USER))
- return(YES);
- }
- return(NO);
- }
-
- void do_titles(void){
-
- TcxlInit();
- WsetFil(HVYCHECK);
- mainWin=Wopen(0,0,24,79,BOX_EXP,WHITE|_BLUE,CYAN|_BLUE);
- WsetFil(SPACE);
- Wopen(0,0,7,28,BOX_EXP,WHITE|_BLUE,WHITE|_BLUE);
- Wshadow(_BLACK | DGREY);
- WprtCen(1,WHITE|_BLUE,"KILLCONN");
- WprtCen(2,WHITE|_BLUE,"A Connection-Killer");
- WprtCen(3,WHITE|_BLUE,"for Novell Netware");
- WprtCen(5,WHITE|_BLUE,"(C) 1991 Thomas R. Bruce");
- Wslide(17,49);
- Wslide(2,49);
- Wslide(17,2);
- Wcenter(CNT_CNT);
- delay(3000);
- Wclose();
- Wopen(1,1,1,78,BOX_SPA,WHITE|_BLUE,WHITE|_BLUE);
- WprtCen(0,WHITE|_BLUE,"KILLCONN copyright (C)1991 Thomas R. Bruce");
- Wactiv(mainWin);
- return;
- }
-
- void do_the_help_thing(int mode){
-
- if (mode==TITLES)do_titles();
- WpopUp(CNT_CNT,0,0,16,60,BOX_EXP,WHITE|_RED,WHITE|_RED);
- Wshadow(_BLACK | DGREY);
- Wtitle(" Help! ", TTL_CNT,WHITE|_RED);
- Wprts(0,4,LCYAN|_RED,"Command line syntax:");
- Wprts(2,4,WHITE|_RED,"killconn who-options [other-options]");
- Wprts(4,4,LCYAN|_RED,"where who-options is one of:");
- Wprts(6,8,WHITE|_RED,"user=SOMEBODY (can contain wildcard chars)");
- Wprts(7,8,WHITE|_RED,"group=USERGROUP");
- Wprts(9,4,LCYAN|_RED,"and [other-options] are:");
- Wprts(11,8,WHITE|_RED,"server=MYSERVE (for a single server)");
- Wprts(12,8,WHITE|_RED,"confirm=YES to check before disconnecting");
- Wprts(13,8,WHITE|_RED,"log=filename to log to a new text file");
- Wprts(14,8,WHITE|_RED,"logadd=filename to append to an existing log");
- KwGetCh();
- exit(0);
- }
-
- void parse_cmd_line(int _argc, char *_argv[])
- {
- int j;
- char *arg_lead; /* server= or whatever */
- char *arg_targ; /* actual 'content' of argument specification */
- char *arg_temp;
-
- /* No args. Indicates confused user. Give 'em help, quick */
- /* Leaving this structured this way provides a socket for a
- possible menu-driven version someday */
-
- if (_argc==1) do_the_help_thing(NOTITLES);
-
- for (j = 1; j < _argc; ++j) {
-
- arg_temp = (char *) strupr(_argv[j]);
-
- if ((strcmp(arg_temp, "-H") == 0)||(strcmp(arg_temp, "/H") == 0)||
- (strcmp(arg_temp, "HELP") == 0)) do_the_help_thing(NOTITLES);
-
- /* now the slightly more complex ones */
-
- arg_lead = (char *) strupr(strtok(_argv[j], "="));
- if (!arg_lead){
- fprintf(stderr,"\nBad command line argument. Terminating.");
- exit(4);
- }
-
- arg_targ = (char *) strupr(strtok(NULL, "="));
-
- if (strcmp(arg_lead, "USER") == 0) {
- if ((strchr(arg_targ,ASTERISK)!=NULL) ||
- (strchr(arg_targ,QUESMARK)!=NULL)) search_type=WILD_USER;
- else search_type= SINGLE_USER;
- strcpy(search_string, arg_targ);
- continue;
- }
- if (strcmp(arg_lead, "GROUP") == 0) {
- search_type=USER_GROUP;
- strcpy(search_string, arg_targ);
- continue;
- }
-
- if (strcmp(arg_lead, "SERVER") == 0) {
- scope=ONE_SERVER;
- strcpy(targ_server,arg_targ);
- continue;
- }
- if (strcmp(arg_lead, "CONFIRM") == 0) {
- if (strcmp(arg_targ,"YES")==0) do_confirm=CONFIRM;
- continue;
- }
- if (strcmp(arg_lead, "LOG") == 0) {
- log_type=NEW_LOG;
- strcpy(log_file, arg_targ);
- continue;
- }
- if (strcmp(arg_lead, "LOGADD") == 0) {
- log_type=APPEND_LOG;
- strcpy(log_file, arg_targ);
- continue;
- }
-
- /* Code added 4-9-91 to accomodate two new features: retries and
- warnings */
-
- if (strcmp(arg_lead, "WARNING") == 0) {
- warnings=WARNINGS;
- warn_time=atoi(arg_targ);
- continue;
- }
-
- if (strcmp(arg_lead, "RETRIES") == 0) {
- retry_count=atoi(arg_targ);
- continue;
- }
-
-
- printf("Incomprehensible command line argument type ");
- printf(arg_lead);
- printf(" entered. Shame on you. Any key terminates.");
- getch();
- exit(1);
- }
- }
-
-
- /* Function to add nodes to the victim list */
-
- void add_to_list(USERPTR targ_user){
-
- USERPTR p,q;
-
- p=getnode();
- strcpy(p->username,targ_user->username);
- p->objectID=targ_user->objectID;
- p->connectionNumber=targ_user->connectionNumber;
- q=list_head;
- list_head=p;
- p->next=q;
-
- }
-
-
- /* Function which allocates memory for the victim list nodes */
-
- USERPTR getnode(void){
-
- USERPTR p;
-
- p=(USERPTR)malloc(sizeof(struct userinfo));
- p->username=(char *)calloc(48,sizeof(char));
- return (p);
- }
-
- /* Function to free up nodes */
-
- void freenode(USERPTR the_ptr){
- free(the_ptr->username);
- free(the_ptr);
- }
-
- /* Function to zap a whole list */
- void listzap(USERPTR list_start){
-
- USERPTR p,q;
-
- for (p=list_start;p==NULL;p=q){
- q=p->next;
- freenode(p);
- }
- return;
- }
-
- /****************************************************************************
-
- That's all, folks.
- Completed 04/06/91 by TRB.
-
- *****************************************************************************/
-