/* =========================================================================== = = = (C) Copyright 1991 The Trustees of Indiana University = = = = Permission to use, copy, modify, and distribute this program for = = non-commercial use and without fee is hereby granted, provided that = = this copyright and permission notice appear on all copies and = = supporting documentation, the name of Indiana University not be used = = in advertising or publicity pertaining to distribution of the program = = without specific prior permission, and notice be given in supporting = = documentation that copying and distribution is by permission of = = Indiana University. = = = = Indiana University makes no representations about the suitability of = = this software for any purpose. It is provided "as is" without express = = or implied warranty. = = = =========================================================================== = = = File: = = IUPOP3.C - Version 1.6a = = = = Synopsis: = = This file contains miscellaneous functions that support the = = VMS IUPOP3 server. = = = = Authors: = = Jacob Levanon & Larry Hughes = = Indiana University = = University Computing Services, Network Applications = = = = Credits: = = This software is based on the Post Office Protocol version 3, = = as implemented by the University of California at Berkeley. = = = =========================================================================== */ /* ======================================================================== */ /* Includes */ /* ======================================================================== */ #if !defined(WINS) && !defined(MULTINET) && !defined(UCX) Don't know how to make IUPOP3 for your TCP/IP implementation! #endif #include #ifdef MULTINET #include #include #else /* WINS || UCX */ #include #include #endif #include #include #include #include #include #include #include #include #ifndef UCX #include #endif #include "iupop3_general.h" #define Main #include "iupop3_global.h" #undef Main #include "iupop3_vms.h" #include "version.h" /* ======================================================================== */ /* Defines */ /* ======================================================================== */ #define MAX_THREADS 31 /* VMS callable mail limit */ #define READ_BUFFERSIZE 256 #define ATTN_SLEEP 0 #define ATTN_CONNECT 1 #define ATTN_DISCONNECT 2 #define ATTN_TIMEOUT 3 #define ATTN_DATA 4 #ifdef UCX #define netclose(a) close(a) #endif #ifdef MULTINET #define netclose(a) socket_close(a) #endif /* ======================================================================== */ /* Global Variables */ /* ======================================================================== */ int initial_socket; int uerrno,verrno; POP pop[MAX_THREADS]; volatile int attn_state, attn_threadnum; volatile char read_buffer[MAX_THREADS][READ_BUFFERSIZE]; typedef unsigned short unsigned_word; volatile struct IOSB { unsigned_word status; unsigned_word terminator_offset; unsigned_word terminator; unsigned_word terminator_size; } read_iosb[MAX_THREADS]; volatile struct IOSB connect_iosb; /* ======================================================================== */ /* Prototypes */ /* ======================================================================== */ int cancel_client_timeout_qio(); void client_read_ast(); void client_timeout_ast(); int close_pop_thread(); int create_initial_socket(); int init_pop_thread(); int issue_client_read_qio(); int issue_client_timeout_qio(); int issue_new_connect_qio(); void new_connect_ast(); void pop_log(); void process_new_connect(); void process_thread(); void system_log(); /* ======================================================================== */ /* Main */ /* ======================================================================== */ main(argc, argv) int argc; char *argv[]; { state_table *s; char *message; int status; int count; int pop_port; int serving = TRUE; switch (argc) { case 1: pop_port = RFC_PORT; break; case 2: if (is_numeric(argv[1])) pop_port = atoi(argv[1]); else { puts("iupop3: portnumber must be numeric"); exit(-1); } break; default: puts("Usage: iupop3 [portnumber]\n"); exit(-1); } get_date(start_time); get_time(start_time+11); start_time[10] = ' '; system_log("starting pop3 server on port %d", pop_port,0); if (!create_initial_socket(pop_port)) { system_log("could not create or bind initial socket",0); exit(-1); } for (count=0; countin_use) { current_threads--; pop_log(p,"closing thread",0); #ifdef UCX status = sys$cancel(p->channel); #else status = sys$cancel((unsigned)p->sockfd); #endif if (vms_error(status)) pop_log(p,"sys$cancel: %s",vms_message(status),0); netclose(p->sockfd); if ((int) p->message_context != NO_CONTEXT) mail_close_message_context(p); if ((int) p->file_context != NO_CONTEXT) mail_close_file_context(p); if ((int) p->user_context != NO_CONTEXT) mail_close_user_context(p); p->in_use = FALSE; free(p->client); free(p->ipaddr); if (p->mptr) free((char *)p->mptr); memset(p,0,sizeof(pop[0])); status = TRUE; } else { pop_log(p,"thread not in use!",0); status = FALSE; } return(status); } /* ======================================================================== */ /* Client Read AST */ /* ======================================================================== */ void client_read_ast(int threadnum) { int status; status = ASTOFF; if (read_iosb[threadnum].status == SS$_CANCEL) { attn_state = ATTN_TIMEOUT; } else { if (vms_error(read_iosb[threadnum].status)) pop_log(&pop[threadnum],"read iosb: %s", vms_message(read_iosb[threadnum].status),0); if (vms_error(read_iosb[threadnum].status) || (read_iosb[threadnum].terminator_offset <= 0)) { /* Be paranoid and flag it as disconnected right now */ attn_state = ATTN_DISCONNECT; pop[threadnum].connected = FALSE; } else { attn_state = ATTN_DATA; read_buffer[threadnum][read_iosb[threadnum].terminator_offset-2] = '\0'; } } attn_threadnum = threadnum; status = sys$wake(0,0); if (vms_error(status)) pop_log(&pop[threadnum],"sys$wake: %s",vms_message(status),0); } /* ======================================================================== */ /* Client Timeout AST */ /* ======================================================================== */ void client_timeout_ast(int threadnum) { int status; /* * Just cancel, and let the read ast handle it. Remember * that the threadnum parameter needs to be decremented * since it was incremented when calling sys$setimr. */ status = ASTOFF; status = sys$cancel(pop[--threadnum].sockfd); status = ASTON; } /* ======================================================================== */ /* Create Initial Socket */ /* ======================================================================== */ int create_initial_socket(int pop_port) { int return_value = FALSE; int status; struct sockaddr_in server; static int one = 1; initial_socket = socket(AF_INET,SOCK_STREAM,0); if (initial_socket < 0) system_log("error creating initial socket",0); else { server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(pop_port); status = bind(initial_socket, &server, sizeof(server)); if (status != 0) system_log("error binding initial socket",0); else { setsockopt(initial_socket,SOL_SOCKET,SO_KEEPALIVE, (char *)&one,sizeof(one)); return_value = TRUE; } } return(return_value); } /* ====================================================================== */ /* Init POP Thread */ /* ====================================================================== */ int init_pop_thread(threadnum, p, sockfd) int threadnum; POP *p; int sockfd; { int status, len; char *pointer; struct hostent *ch; struct sockaddr_in sin; system_log("thread %d: initializing thread",threadnum,0); /* Initialize this POP structure -- all fields below are vital! */ memset(p, 0, sizeof(pop[0])); p->sockfd = sockfd; p->channel = vaxc$get_sdc(p->sockfd); p->threadnum = threadnum; p->bytes_deleted = 0; p->msg_count = 0; p->newmail_size = 0; p->msgs_deleted = 0; p->newmail_selected = FALSE; p->user_context = NO_CONTEXT; p->file_context = NO_CONTEXT; p->message_context = NO_CONTEXT; p->CurrentState = auth1; p->connected = TRUE; (void) gethostname(p->server, MAXHOSTNAMELEN); /* ** Get the address and socket of the client to whom we're speaking */ len = sizeof(sin); if ((status = getpeername(sockfd, (struct sockaddr *)&sin, &len)) < 0) { pop_log(p,"getpeername failed: error %d",status,0); return(FALSE); } p->ipaddr = malloc(16); pointer = inet_ntoa(sin.sin_addr); strcpy(p->ipaddr,pointer); p->ipport = ntohs(sin.sin_port); /* ** Get the canonical name of the client */ p->client = p->ipaddr; /***** Will hang with AST's disabled... ch = gethostbyaddr(&sin.sin_addr,sizeof(struct in_addr),sin.sin_family); if (ch == NULL) { pop_log(p,"gethostbyaddr failed",0); p->client = p->ipaddr; } else { p->client = malloc(sizeof(struct hostent)); memcpy(p->client,ch->h_name,sizeof(struct hostent)); } *****/ /* ** Everything succeeded, so mark this thread as "in use" */ p->in_use = TRUE; return(TRUE); } /* ======================================================================== */ /* Issue Client Read QIO */ /* ======================================================================== */ int issue_client_read_qio(int threadnum) { int status; #ifdef UCX status = sys$qio(0, pop[threadnum].channel, IO$_READVBLK, &read_iosb[threadnum], client_read_ast, threadnum, read_buffer[threadnum], READ_BUFFERSIZE, 0, 0, 0, 0); #else status = sys$qio(0, (unsigned)pop[threadnum].sockfd, IO$_READVBLK, &read_iosb[threadnum], &client_read_ast, threadnum, read_buffer[threadnum], READ_BUFFERSIZE, 0, 0, 0, 0); #endif if (vms_error(status)) pop_log(&pop[threadnum],"client read sys$qio: %s",vms_message(status),0); return(status); } /* ======================================================================== */ /* Issue Client Timeout QIO */ /* ======================================================================== */ int issue_client_timeout_qio(int threadnum) { int status; int value = FALSE; int interval[2]; $DESCRIPTOR(time_desc,"0 00:02:00.00"); status = sys$bintim(&time_desc,interval); if (vms_error(status)) pop_log(&pop[threadnum],"sys$bintim: %s",vms_message(status),0); else { /* Use "threadnum+1" since sys$cantim uses 0 to cancel all requests */ status = sys$setimr(0,interval,client_timeout_ast,threadnum+1,0); if (vms_error(status)) pop_log(&pop[threadnum],"sys$setimr: %s",vms_message(status),0); else value = TRUE; } return(value); } /* ======================================================================== */ /* Issue New Connect QIO */ /* ======================================================================== */ int issue_new_connect_qio() { int status; #ifdef UCX status = sys$qio(0, (unsigned)vaxc$get_sdc(initial_socket), IO$_SETMODE | IO$M_READATTN, &connect_iosb, 0, 0, &new_connect_ast, 0, 0, 0, 0, 0); #else status = sys$qio(0, initial_socket, IO$_ACCEPT_WAIT, &connect_iosb, &new_connect_ast, 0, 0, 0, 0, 0, 0, 0); #endif if (!vms_error(status)) return(TRUE); else { system_log("new connect sys$qio: %s",vms_message(status),0); return(FALSE); } } /* ======================================================================== */ /* New Connect AST */ /* ======================================================================== */ void new_connect_ast() { int status; status = ASTOFF; if (vms_error(connect_iosb.status)) system_log("connect iosb: %s",vms_message(connect_iosb.status),0); attn_state = ATTN_CONNECT; status = sys$wake(0,0); if (vms_error(status)) system_log("new connect ast sys$wake: %s",vms_message(status),0); } /* ======================================================================== */ /* Process New Connect */ /* ======================================================================== */ void process_new_connect() { int ok = FALSE; int threadnum; int status; int new_socket; int length; POP *p; struct sockaddr addr; static int one = 1; length = sizeof(addr); new_socket = accept(initial_socket,&addr,&length); if (new_socket < 0) system_log("accept failed",0); else { setsockopt(new_socket,SOL_SOCKET,SO_KEEPALIVE, (char *)&one,sizeof(one)); p = pop; for (threadnum=0; threadnumin_use) { ok = TRUE; break; } } if (ok) { current_threads++; if (current_threads > maximum_threads) maximum_threads = current_threads; system_log("new connection accepted: assigned thread %d",threadnum,0); if (init_pop_thread(threadnum,p,new_socket)) { system_log("thread %d: client is %s",threadnum,p->client,0); pop_msg(p, POP_SUCCESS, "IU POP3 server V%s at %s, up since %s", VERSION, p->server, start_time, 0); issue_client_read_qio(threadnum); issue_client_timeout_qio(threadnum); } else { system_log("thread %d could not be initialized; closing",threadnum,0); netclose(new_socket); } } else { too_many_threads++; system_log("too many threads!",0); netclose(new_socket); } } } /* ======================================================================== */ /* Process Thread */ /* ======================================================================== */ void process_thread(p,message) POP *p; char *message; { int status; state_table *s; char original[512]; strcpy(original,message); if ((s = pop_get_command(p, message)) == NULL) return; if (strcmp(s->command,"pass") == 0) pop_log(p,"will execute \"pass\"",0); else pop_log(p,"will execute \"%s\"",original,0); if (s->function) { p->CurrentState = s->PostState[(*s->function)(p)]; } else { p->CurrentState = s->PostState[0]; pop_msg(p, POP_SUCCESS, NULL); } }