#pragma module SPOP3_SRV "SPOP3_SRV-2-H" #define __MODULE__ "SPOP3_SRV" /* **++ ** FACILITY: StarLet POP3 server form OpenVMS ** ** MODULE DESCRIPTION: ** ** This is a POP3 server for OpenVMS based on RFC1460 & RFC1939 & RFC2449 & RFC2595. ** ** AUTHORS: ** ** Ruslan R. Laishev ** ** CREATION DATE: 23-MAR-2005 ** ** DESIGN ISSUES: ** ** MODIFICATION HISTORY: ** ** 24-AUG-2005 RRL Added SPOP3_CHARSET configuration logical to put charset ** into DecNet mail headers. ** 25-AUG-2005 RRL Fixed EXPIRE 0 problem. ** 28-AUG-2005 RRL Changed MCR []SPOP3_SRV SHUT to ** MCR []SPOP3_SRV SHUT |* ** 30-AUG-2005 RRL Some changes of format of intrusion source, ** removed "format=flowed" for Decnet messages. ** 31-AUG-2005 RRL Changed pop3_user() to prevent change username after ** open session; ** 1-SEP-2005 RRL Added SPOP3_PR_THRESHOLD configuration variable. ** 2-SEP-2005 RRL Added POP3 LAST command support. ** 8-SEP-2005 RRL Fixed processing TOP 0 command. ** 17-JAN-2006 RRL Fixed pop3_retr(), pop3_top(): change one-byte string with ** to . ** 18-JAN-2007 RRL Introducing SSL support, SPOP3 Version is 2-A ** 24-JAN-2007 RRL Temporarly removed lock access to mailbox. ** 5-MAR-2007 RRL Fixed shutdown problem had been introduced by SSL piece of code. ** 3-APR-2007 RRL Changed spop3__shut() to $enq with LCK$M_NOQUEUE. ** 5-APR-2007 RRL Removed "$enq with LCK$M_NOQUEUE". ** 20-APR-2007 RRL Added releasing a res/lock into the evt_rtn_blkast(). ** 2-JUL-2007 RRL Some cosmetic changes. ** 18-SEP-2007 RRL Changed evt_rtn_blkast() ** 1-OCT-2007 RRL Added: SPOP3_FOLDER a default folder name to retrive mail, ** SPOP3_NEW2MAIL - if the logical is present then all mails from folder NEWMAIL ** will be moved to folder MAIL. ** 4-OCT-2007 RRL Improving of SSL error diagnostic. ** 25-OCT-2007 RRL NOFREECTX - force exit, ** added a periodical wakeuping of main thread. ** 18-DEC-2007 RRL Fixed a potential overlaping of user field in a context area ** in spop3_user(); ** reduce a thread stack size from 128000 -> 64000. ** 20-OCT-2008 RRL Do not processing scan_intrusion if user is not in SYSUAF. ** 5-AUG-2009 RRL Changed dsc_DNETHDR's "Date: !%D" -> "Date: !20%D", because ** MacOS mail prog cannot handle milliseconds correctly. ** 13-OCT-2010 RRL Include Intrusion Pervention System using. ** 14-OCT-2010 RRL Added UID in the 'Hello string". ** 3-DEC-2010 RRL Display mail size from msz (if non-zero) instead of ** mp->mhdr$l_lines*SPOP3__$K_LINESZ. ** 12-SEP-2011 RRL Added using of SSL's compression stuff. ** 19-DEC-2011 RRL Added checking of SSL stuff initialization. ** 8-MAY-2013 RRL Fixed a bug with the FAO specs in the NOFREECTX/SPOP3_MSG.MSG : "!>" -> ".>" ** 13-MAY-2013 RRL Added ENAIPS//SPOP3_MSG.MSG to display a result of TCPWare IPS initialization. ** ** ** ** {@tbs@}... **-- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tcpware_include:filter_server_api.h" #define __NEW_STARLET 1 #include #include "spop3def.h" /* StarLet POP3 Server definitions */ #include "spop3_msg.h" /* StarLet POP3 Server message codes */ #define INIT_SDESC(dsc, len, ptr) {(dsc).dsc$b_dtype = DSC$K_DTYPE_T;\ (dsc).dsc$b_class = DSC$K_CLASS_S; (dsc).dsc$w_length = (short) (len);\ (dsc).dsc$a_pointer = (char *) (ptr);} #define INIT_DDESC(dsc) {(dsc).dsc$b_dtype = DSC$K_DTYPE_T;\ (dsc).dsc$b_class = DSC$K_CLASS_D;(dsc).dsc$w_length = 0;\ (dsc).dsc$a_pointer = NULL;} #define min(x,y) ((x > y)?y:x) #define max(x,y) ((x < y)?y:x) int debug_flag = 0,exit_flag = 0, pr_threshold = 0,lkid = 0, spop3_new2mail = 0; unsigned char spop3_folder[64]; #define DEBUG if(debug_flag)printf("%-24.24s:%-08.0u ",__MODULE__,__LINE__);if(debug_flag)printf /* ** Forward routines declaration */ int netio_start (...); int netio_close (...); int netio_read (...); int netio_write (...); int netio_accept (...); int vmail_init (...); int vmail_shut (...); int vmail_init_ctx (...); int vmail_shut_ctx (...); int vmail_get_msg (...); int vmail_msg_end (...); /* ** Right ID to designate user as "user under debug" */ $DESCRIPTOR(pop3_iddebug, "SPOP3_DEBUG"); /* Session is under debug */ $DESCRIPTOR(pop3_idexpire, "SPOP3_NOTEXPIRE"); /* Mail box never expired */ unsigned pop3_iddebug_ = 0, pop3_idexpire_ = 0; /* ** POP3 protocol messages */ $DESCRIPTOR(dsc_BUSY, "-ERR POP3 Server is too busy, try again just later."); $DESCRIPTOR(dsc_OK , "+OK"); $DESCRIPTOR(dsc_DOT , "."); $DESCRIPTOR(dsc_EOM , "!/."); $DESCRIPTOR(dsc_WELCOME , "+OK !%T StarLet POP3/SSL Srv. for OpenVMS, http://www.StarLet.SPB.RU !36AS"); $DESCRIPTOR(dsc_IQUIT , "+OK StarLet POP3 Srv. signing off."); $DESCRIPTOR(dsc_LQUIT , "+OK StarLet POP3 Srv. signing off (!UL mail(s) in the VMS MailBox), context = [!2ZB:!AC/!AC]."); $DESCRIPTOR(dsc_LISTMSG , "+OK !UL !UL."); $DESCRIPTOR(dsc_PASSREQ , "+OK Password required for '!AC'."); $DESCRIPTOR(dsc_LOGGED , "+OK POP3 session is opened, context = [!2ZB:!AC/!AC], !UL mails."); $DESCRIPTOR(dsc_ALIVE , "+OK Session is alive, context = [!2ZB:!AC/!AC]."); $DESCRIPTOR(dsc_UIDL1 , "+OK !UL !AC_!UL!UL"); $DESCRIPTOR(dsc_UIDLX , "!UL !AC_!UL!UL"); $DESCRIPTOR(dsc_DELETED , "+OK Message #!UL is deleted."); $DESCRIPTOR(dsc_STATM , "+OK !UL !UL"); $DESCRIPTOR(dsc_LIST1 , "+OK !UL !UL"); $DESCRIPTOR(dsc_LISTF , "+OK !UL messages (!UL bytes estimated)"); $DESCRIPTOR(dsc_LISTX , "!UL !UL"); $DESCRIPTOR(dsc_RSETM , "+OK VMS MailBox has !UL messages (!UL bytes estimated)"); $DESCRIPTOR(dsc_RETRM , "+OK !UL bytes estimated"); /* $DESCRIPTOR(dsc_DNETHDR , "From: !AC!/Date: !20%D!/To: !AC!/Subject: !AC!/\ Content-Type: text/plain; charset=!AC!/"); */ $DESCRIPTOR(dsc_DNETHDR , "From: !AC!/Date: !20%D!/To: !AC!/Subject: !AC!/"); $DESCRIPTOR(dsc_LAST , "+OK !UL"); $DESCRIPTOR(dsc_CAPA , "+OK Capability list follows:\ !/TOP!/USER!/LOGIN-DELAY !UL!/EXPIRE !UL!/UIDL!/."); $DESCRIPTOR(dsc_CAPA2 , "+OK Capability list follows:\ !/TOP!/USER!/LOGIN-DELAY !UL!/EXPIRE NEVER!/UIDL!/."); $DESCRIPTOR(dsc_NOSUCHMSG, "-ERR no such message #!UL."); $DESCRIPTOR(dsc_INVARGLEN, "-ERR Invalid username length (!UB)."); $DESCRIPTOR(dsc_ERRLOGIN, "-ERR Invalid username or password."); $DESCRIPTOR(dsc_BADCMD , "-ERR Unknown/invalid command: '!AS'."); $DESCRIPTOR(dsc_OUTRANGE, "-ERR Message #!UL is out of range [1-!UL]."); $DESCRIPTOR(dsc_HASDELTD, "-ERR Message #!UL has been deleted."); $DESCRIPTOR(dsc_NOARG , "-ERR Missing required argument."); unsigned char charset [ 32 ]; /* ** Forward POP3 functions declarations */ int pop3_user (POP3CTX *,unsigned char *arg,unsigned short); int pop3_pass (POP3CTX *,unsigned char *arg,unsigned short); int pop3_quit (POP3CTX *,unsigned char *arg,unsigned short); int pop3_noop (POP3CTX *,unsigned char *arg,unsigned short); int pop3_uidl (POP3CTX *,unsigned char *arg,unsigned short); int pop3_dele (POP3CTX *,unsigned char *arg,unsigned short); int pop3_stat (POP3CTX *,unsigned char *arg,unsigned short); int pop3_list (POP3CTX *,unsigned char *arg,unsigned short); int pop3_rset (POP3CTX *,unsigned char *arg,unsigned short); int pop3_retr (POP3CTX *,unsigned char *arg,unsigned short); int pop3_top (POP3CTX *,unsigned char *arg,unsigned short); int pop3_capa (POP3CTX *,unsigned char *arg,unsigned short); int pop3_last (POP3CTX *,unsigned char *arg,unsigned short); #define ASCIC(a) ((unsigned char)sizeof(a)-1),a struct pop3cmd { unsigned char clen, cname[16]; int (*fun) (POP3CTX *,unsigned char *arg,unsigned short); unsigned level; }; const struct pop3cmd pop3cmd_tbl [] = { {ASCIC("user"), pop3_user, POP3LVL$K_INITIAL}, {ASCIC("pass"), pop3_pass, POP3LVL$K_INITIAL}, {ASCIC("quit"), pop3_quit, POP3LVL$K_INITIAL}, {ASCIC("quit"), pop3_quit, POP3LVL$K_LOGGED}, {ASCIC("noop"), pop3_noop, POP3LVL$K_LOGGED}, {ASCIC("uidl"), pop3_uidl, POP3LVL$K_LOGGED}, {ASCIC("dele"), pop3_dele, POP3LVL$K_LOGGED}, {ASCIC("stat"), pop3_stat, POP3LVL$K_LOGGED}, {ASCIC("list"), pop3_list, POP3LVL$K_LOGGED}, {ASCIC("rset"), pop3_rset, POP3LVL$K_LOGGED}, {ASCIC("retr"), pop3_retr, POP3LVL$K_LOGGED}, {ASCIC("top"), pop3_top, POP3LVL$K_LOGGED}, {ASCIC("capa"), pop3_capa, POP3LVL$K_INITIAL}, {ASCIC("capa"), pop3_capa, POP3LVL$K_LOGGED}, {ASCIC("last"), pop3_last, POP3LVL$K_LOGGED}, {0,NULL, NULL, 0}}; POP3CTX pop3ctx_tbl [SPOP3__$K_MAXCTX]; /* POP contexts table */ /* Mutex to control an access to the context table */ pthread_mutex_t pop3ctx_tbl_mtx = PTHREAD_MUTEX_INITIALIZER; int iotmo [ 2 ], /* A global network I/O timeout */ mainchan = 0, /* Main network I/O channel number */ smainchan = 0, /* Main network I/O channel number for SSL */ expire = 0, /* A number of days to keep mails before auto-purging */ logintvl = 0, /* LOGIN-DISABLE in seconds value */ wakeuptmo [2], enaips = 0; /* Enable/Disable Intrusion Prevention System */ $DESCRIPTOR(resfao,"SPOP3_SRV@!AZ"); /* A template for resource name */ pthread_attr_t tattr; SSL_METHOD *ssl_meth; SSL_CTX *ssl_ctx; SSL *ssl; const char SPOP3_RSA_CERT [] = {"SPOP3_RSA_CERT"}; char host[32]; const char compname [] = "POP3", /* IPS Stuf */ rule [] = "POP3_AUTHFAILED"; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Put formated message to standard output device (SYS$OUTPUT). ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** msgid: VMS condition code ** variable agriments list ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ const char msg_pref[] = {"!%D "}, msg_pref_ctx[2][19] = {{"!%D [!2ZB !AC/!AC] "}, {"!%D {!2ZB !AC/!AC} "}}; int spop3__log ( POP3CTX * ctx, int msgid, ... ) { long status,retvalue = msgid,len = 0; va_list args; char buf[1024],outadr[4]; struct dsc$descriptor opr_dsc,buf_dsc,fao_dsc; int argc,argl[32],idx = 0,flag=15,lvl; char msg_buf[1024]; if ( !ctx ) memcpy(buf,msg_pref,sizeof(msg_pref)-1); else memcpy(buf,&msg_pref_ctx[ctx->pop3ctx$v_tls],sizeof(msg_pref_ctx[0])-1); len = (ctx)?sizeof(msg_pref_ctx[0])-1:sizeof(msg_pref)-1; /* ** Get a message text with given msgid */ INIT_SDESC(fao_dsc,sizeof(buf)-len,&buf[len]); if ( !(1 & (status = sys$getmsg (msgid,&fao_dsc.dsc$w_length,&fao_dsc,flag,&outadr))) ) lib$signal(status); memset(&argl,0,sizeof(argl)); if ( ctx ) { argl[1] = ctx->pop3ctx$b_idx; argl[2] = &ctx->pop3ctx$b_alen; argl[3] = &ctx->pop3ctx$b_ulen; } /* ** Reorganize parameters list for $FAOL */ va_start(args,msgid); for (idx = ctx?4:1, va_count(argc); idx < argc; idx++) argl[idx] = va_arg(args,unsigned); va_end((char *) args); /* ** Format a message, put it to SYS$OUTPUT */ fao_dsc.dsc$a_pointer -=len; fao_dsc.dsc$w_length +=len; INIT_SDESC(buf_dsc, sizeof(msg_buf),msg_buf); if ( !(1 & (status = sys$faol(&fao_dsc,&buf_dsc.dsc$w_length,&buf_dsc,argl))) ) lib$signal(status); lib$put_output(&buf_dsc); return retvalue; } int spop3__send ( POP3CTX * ctx, struct dsc$descriptor_s *msgdsc, ... ) { unsigned status,idx,argc = 0,argl[32]; va_list args; char buf[8192]; struct dsc$descriptor buf_dsc; INIT_SDESC(buf_dsc,sizeof(buf),buf); /* ** Reorganize parameters list for $FAOL */ va_start(args,msgdsc); for (idx = 0, va_count(argc); idx < argc; idx++) argl[idx] = va_arg(args,unsigned); va_end((char *) args); if ( !(1 & (status = sys$faol(msgdsc,&buf_dsc.dsc$w_length,&buf_dsc,&argl))) ) lib$signal(status); /* ** Write data to network I/O channel or send it with SSL */ if ( !ctx->pop3ctx$v_tls ) { if ( !(1 & (status = netio_write(ctx->pop3ctx$l_chan,&buf_dsc))) ) return status; } else { /* ** Add pair to string */ buf_dsc.dsc$a_pointer[buf_dsc.dsc$w_length++] = '\r'; buf_dsc.dsc$a_pointer[buf_dsc.dsc$w_length++] = '\n'; if ( 0 >= (status = SSL_write(ctx->pop3ctx$a_ssl,buf_dsc.dsc$a_pointer,buf_dsc.dsc$w_length)) ) spop3__log(ctx,POP3_SSLERR,__MODULE__,__LINE__,status); status = (0 >= status)?SS$_DISCONNECT:SS$_NORMAL; /* ** Truncate pair from the string */ buf_dsc.dsc$w_length -= 2; } /* ** Write debug output */ if ( ctx->pop3ctx$v_debug ) spop3__log(ctx,POP3_DEBUG,&buf_dsc); return status; } int spop3__recv ( POP3CTX * ctx, struct dsc$descriptor_s *buf, unsigned short *retlen ) { int status, bufl = buf->dsc$w_length; /* ** For ordinary session we just reading from network I/O channel */ if ( !ctx->pop3ctx$v_tls ) return netio_read(ctx->pop3ctx$l_chan,buf,retlen,&iotmo); /* ** Handling reading line over SSL connection... */ *retlen = 0; while ( *retlen < bufl ) { if ( 0 >= (status = SSL_read(ctx->pop3ctx$a_ssl,buf->dsc$a_pointer+(*retlen),bufl-(*retlen))) ) { spop3__log(ctx,POP3_SSLERR,__MODULE__,__LINE__,status); break; } *retlen += status; /* ** Check for line termination sequence */ if ( (*retlen >= 2) && (*(buf->dsc$a_pointer+(*retlen) - 2) == '\r') && (*(buf->dsc$a_pointer+(*retlen) - 1) == '\n') ) { *retlen -= 2; break; } } return ((status > 0)?SS$_NORMAL:SS$_DISCONNECT); } int spop3__chkid ( struct dsc$descriptor_s *dsc$right, int uic, unsigned *id ) { unsigned status,uai_id,context = 0; struct { union uicdef uai_uic; int mbz; } holder = {uic,0}; /* ** Convert VMS Right id from ASCII to Binary from */ if ( !(*id) && !(1 & (status = sys$asctoid(dsc$right,id,0))) ) return status; /* ** Find for presence of right_id for UIC */ while ( 1 & (status = sys$find_held(&holder,&uai_id,0,&context)) ) if ( *id == uai_id ) break; sys$finish_rdb (&context); return status; } int pop3_user ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { int status; /* ** Don't allow to change usernames if the session has been opened */ if ( ctx->pop3ctx$l_level == POP3LVL$K_LOGGED ) return spop3__send(ctx,&dsc_OK); /* ** Check gotten username length, SYSUAF's username length is up to 12 characters */ if ( arglen > 12 ) return spop3__send(ctx,&dsc_INVARGLEN,arglen); else if ( !arglen || !arg ) return spop3__send(ctx,&dsc_NOARG); /* ** Store username for future processing */ ctx->pop3ctx$b_ulen = (unsigned char) min(sizeof(ctx->pop3ctx$t_uname),arglen); for (int i = 0; i < ctx->pop3ctx$b_ulen; i++, arg++) ctx->pop3ctx$t_uname[i] = (unsigned char) tolower(*arg); /* ** Send a prompt for password input */ return spop3__send(ctx,&dsc_PASSREQ,&ctx->pop3ctx$b_ulen); } int pop3_pass ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { int status,uai_flags, uai_pwd[2], putative_pw[2], uai_expire_date[2],lgi_time[2],delta_time[2]; union uicdef uai_uic; int uai_salt = 0, uai_encrypt = 0; char buf[1024],usr_buf[32],node_buf [32]; short authlen,buflen; struct dsc$descriptor buf_dsc,usr_dsc,pwd_dsc,node_dsc; ile3 get_itmlst[] = { {4, UAI$_FLAGS, &uai_flags, 0}, {1, UAI$_ENCRYPT, &uai_encrypt, 0}, {2, UAI$_SALT, &uai_salt, 0}, {8, UAI$_PWD, &uai_pwd, 0}, {8, UAI$_EXPIRATION, &uai_expire_date,0}, {4, UAI$_UIC, &uai_uic, 0}, {0, 0, 0, 0}}; ile3 set_itmlst[] = { {8, UAI$_LASTLOGIN_N, &lgi_time, 0}, {0, 0, 0, 0}}; if ( !arglen || !arg ) return spop3__send(ctx,&dsc_NOARG); INIT_SDESC(buf_dsc,ctx->pop3ctx$b_ulen,ctx->pop3ctx$t_uname); INIT_SDESC(usr_dsc,ctx->pop3ctx$b_ulen,usr_buf); INIT_SDESC(pwd_dsc,arglen,arg); INIT_SDESC(node_dsc,0,node_buf); node_dsc.dsc$w_length = sprintf(node_buf,"POP3::%.*s", ctx->pop3ctx$b_alen,ctx->pop3ctx$t_addr); if ( !(1 & (status = str$upcase(&usr_dsc,&buf_dsc))) ) lib$signal(status); if ( !(1 & (status = str$upcase(&pwd_dsc,&pwd_dsc))) ) lib$signal(status); /* ** Get login time for future checking */ if ( !(1 & (status = sys$gettim(&lgi_time))) ) lib$signal(status); /* ** Find & get user's record from SYSUAF.DAT & check: ** ** - Disabled Account & Restricted flags, ** - Expiration date of account ** */ status = sys$getuai(EFN$C_ENF, 0, &usr_dsc, &get_itmlst, 0, 0, 0); if ( status == RMS$_RNF ) { spop3__send(ctx,&dsc_ERRLOGIN); return spop3__log(ctx,SS$_NOSUCHUSER); } else if ( !(1 & status) ) { spop3__send(ctx,&dsc_ERRLOGIN); return spop3__log(ctx,POP3_GENERR,__MODULE__,__LINE__,status); } if ( (!uai_expire_date[0]) && (!uai_expire_date[1]) ) uai_expire_date[0] = uai_expire_date[1] = -1; /* ** Check DisUser+NoMail flags and account expiration date */ if ( uai_flags & (UAI$M_DISACNT | UAI$M_NOMAIL) ) status = LGI$_DISUSER; else { status = lib$sub_times (uai_expire_date,lgi_time,delta_time); status = (status == LIB$_NEGTIM)?LGI$_ACNTEXPIR:SS$_NORMAL; } if ( !(1 & status )) { sys$scan_intrusion (status,&usr_dsc,JPI$K_NETWORK,0, &node_dsc,&usr_dsc,0,&pwd_dsc,0,0, CIA$M_IGNORE_RETURN|CIA$M_REAL_USERNAME); spop3__send(ctx,&dsc_ERRLOGIN); return spop3__log(ctx,POP3_GENERR,__MODULE__,__LINE__,status); } /* ** Now hash the putative password to see if it matches the one on file. */ if ( !(1 & (status = sys$hash_password(&pwd_dsc, uai_encrypt, uai_salt, &usr_dsc,putative_pw))) ) lib$signal(status); if ( (putative_pw[0] != uai_pwd[0]) || (putative_pw[1] != uai_pwd[1]) ) { sys$scan_intrusion (LGI$_INVPWD,&usr_dsc,JPI$K_NETWORK,0, &node_dsc,&usr_dsc,0,&pwd_dsc,0,0, CIA$M_IGNORE_RETURN|CIA$M_REAL_USERNAME); spop3__send(ctx,&dsc_ERRLOGIN); return spop3__log(ctx,LGI$_INVPWD); } /* ** Set last non-interactive login time in SYSUAF.DAT. */ /* if ( !(1 & (status = sys$setuai(EFN$C_ENF,0,&usr_dsc,&set_itmlst,0,0,0))) ) { spop3__send(ctx,&dsc_ERRLOGIN); return spop3__log(ctx,POP3_GENERR,__MODULE__,__LINE__,status); } */ /* ** Is this user is under debug ? */ if ( 1 & spop3__chkid(&pop3_iddebug,uai_uic.uic$l_uic,&pop3_iddebug_) ) ctx->pop3ctx$v_debug = 1; /* ** Mail retention ? */ if ( 1 & spop3__chkid(&pop3_idexpire,uai_uic.uic$l_uic,&pop3_idexpire_) ) ctx->pop3ctx$l_expire = 0; else ctx->pop3ctx$l_expire = expire; /* ** Open & initialize maildrop context */ ctx->pop3ctx$l_level = POP3LVL$K_LOGGED; spop3__log(ctx,POP3_SOPENED,vmail_init_ctx(ctx)); return spop3__send(ctx,&dsc_LOGGED,ctx->pop3ctx$b_idx,&ctx->pop3ctx$b_ulen, &ctx->pop3ctx$b_alen,ctx->pop3ctx$l_mlistsz); } int pop3_noop ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { return spop3__send(ctx,&dsc_ALIVE,ctx->pop3ctx$b_idx,&ctx->pop3ctx$b_ulen, &ctx->pop3ctx$b_alen); } int pop3_quit ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { /* ** If session has not been opened send a short "goodbye" */ if ( ctx->pop3ctx$l_level == POP3LVL$K_INITIAL ) { spop3__send(ctx,&dsc_IQUIT); return SS$_DISCONNECT; } /* ** Session has been opened - send a long "goodbye" */ spop3__send(ctx,&dsc_LQUIT,ctx->pop3ctx$l_mlistsz-ctx->pop3ctx$l_mdeltd, ctx->pop3ctx$b_idx,&ctx->pop3ctx$b_ulen, &ctx->pop3ctx$b_alen); return SS$_DISCONNECT; } int pop3_uidl ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { unsigned msg = 0; MHDR *mp = ctx->pop3ctx$a_mlist; /* ** Get message number if presented */ if ( arglen && arg ) { lib$cvt_dtb(arglen,arg,&msg); /* ** Check that message in legal range */ if ( !msg || (msg > ctx->pop3ctx$l_mlistsz) ) return spop3__send(ctx,&dsc_OUTRANGE,msg,ctx->pop3ctx$l_mlistsz); } /* ** UIDL for the particular message */ if ( msg ) { /* ** Try to jump to nearest message */ if ( ctx->pop3ctx$a_mlast && (msg >= ctx->pop3ctx$a_mlast->mhdr$l_messid) ) mp = ctx->pop3ctx$a_mlast; /* ** Run over the list to find message with the id */ for(;mp && (mp->mhdr$l_messid != msg);mp = mp->mhdr$a_next); if ( !mp ) return spop3__send(ctx,&dsc_NOSUCHMSG,msg); if ( mp->mhdr$v_deleted ) return spop3__send(ctx,&dsc_DELETED); /* ** Store pointer to last processed message */ ctx->pop3ctx$a_mlast = mp; return spop3__send(ctx,&dsc_UIDL1,mp->mhdr$l_messid,&ctx->pop3ctx$b_ulen, mp->mhdr$l_date[1],mp->mhdr$l_date[0]); } /* ** UIDL for whole list */ spop3__send(ctx,&dsc_OK); for(;mp;mp = mp->mhdr$a_next) { if ( mp->mhdr$v_deleted ) continue; spop3__send(ctx,&dsc_UIDLX,mp->mhdr$l_messid,&ctx->pop3ctx$b_ulen, mp->mhdr$l_date[1],mp->mhdr$l_date[0]); } return spop3__send(ctx,&dsc_DOT); } int pop3_dele ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { unsigned msg = 0; MHDR *mp = ctx->pop3ctx$a_mlist; if ( !arglen || !arg ) return spop3__send(ctx,&dsc_NOARG); /* ** Get message number if presented */ lib$cvt_dtb(arglen,arg,&msg); /* ** Check that message in legal range */ if ( !msg || (msg > ctx->pop3ctx$l_mlistsz) ) return spop3__send(ctx,&dsc_OUTRANGE,msg,ctx->pop3ctx$l_mlistsz); /* ** Try to jump to nearest message */ if ( ctx->pop3ctx$a_mlast && (msg >= ctx->pop3ctx$a_mlast->mhdr$l_messid) ) mp = ctx->pop3ctx$a_mlast; /* ** DELEting the particular message */ for(;mp && (mp->mhdr$l_messid != msg);mp = mp->mhdr$a_next); if ( !mp ) return spop3__send(ctx,&dsc_NOSUCHMSG,msg); if ( mp->mhdr$v_deleted ) return spop3__send(ctx,&dsc_HASDELTD,msg); /* ** Mark the message as DELTED for future deletion at end of session */ mp->mhdr$v_deleted = 1; /* ** Store pointer to last processed message */ ctx->pop3ctx$a_mlast = mp; /* ** Increment deleted mails count */ ctx->pop3ctx$l_mdeltd++; ctx->pop3ctx$l_mlines -= mp->mhdr$l_lines; return spop3__send(ctx,&dsc_DELETED,msg); } int pop3_stat ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { return spop3__send(ctx,&dsc_STATM,ctx->pop3ctx$l_mlistsz-ctx->pop3ctx$l_mdeltd, ctx->pop3ctx$l_mlines*SPOP3__$K_LINESZ); } int pop3_list ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { unsigned msg = 0; MHDR *mp = ctx->pop3ctx$a_mlist; /* ** Get message number if presented */ if ( arglen && arg ) { lib$cvt_dtb(arglen,arg,&msg); /* ** Check that message in legal range */ if ( !msg || (msg > ctx->pop3ctx$l_mlistsz) ) return spop3__send(ctx,&dsc_OUTRANGE,msg,ctx->pop3ctx$l_mlistsz); } /* ** LIST for the particular message */ if ( msg ) { /* ** Try to jump to nearest message */ if ( ctx->pop3ctx$a_mlast && (msg >= ctx->pop3ctx$a_mlast->mhdr$l_messid) ) mp = ctx->pop3ctx$a_mlast; /* ** Run over the list to find message with the id */ for(;mp && (mp->mhdr$l_messid != msg);mp = mp->mhdr$a_next); if ( !mp ) return spop3__send(ctx,&dsc_NOSUCHMSG,msg); if ( mp->mhdr$v_deleted ) return spop3__send(ctx,&dsc_DELETED); /* ** Store pointer to last processed message */ ctx->pop3ctx$a_mlast = mp; return spop3__send(ctx,&dsc_LIST1,mp->mhdr$l_messid, mp->mhdr$l_msz?mp->mhdr$l_msz:mp->mhdr$l_lines*SPOP3__$K_LINESZ); } /* ** LIST for whole list of messages */ spop3__send(ctx,&dsc_LISTF,ctx->pop3ctx$l_mlistsz-ctx->pop3ctx$l_mdeltd, ctx->pop3ctx$l_mlines*SPOP3__$K_LINESZ); for(;mp;mp = mp->mhdr$a_next) { if ( mp->mhdr$v_deleted ) continue; spop3__send(ctx,&dsc_LISTX,mp->mhdr$l_messid, mp->mhdr$l_msz?mp->mhdr$l_msz:mp->mhdr$l_lines*SPOP3__$K_LINESZ); } return spop3__send(ctx,&dsc_DOT); } int pop3_rset ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { unsigned msg = 0; MHDR *mp = ctx->pop3ctx$a_mlist; /* ** UnDELEting all marked for death messages */ for(;mp && (mp->mhdr$l_messid != msg);mp = mp->mhdr$a_next) { if ( mp->mhdr$v_deleted ) { mp->mhdr$v_deleted = 0; /* ** Increment deleted mails count */ ctx->pop3ctx$l_mdeltd--; ctx->pop3ctx$l_mlines += mp->mhdr$l_lines; } } return spop3__send(ctx,&dsc_RSETM,ctx->pop3ctx$l_mlistsz,ctx->pop3ctx$l_mlines*SPOP3__$K_LINESZ); } int pop3_retr ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { unsigned status = MAIL$_NOMOREREC, msg = 0; MHDR *mp = ctx->pop3ctx$a_mlist; unsigned char buf[4096]; struct dsc$descriptor buf_dsc; if ( !arglen || !arg ) return spop3__send(ctx,&dsc_NOARG); /* ** Get message number if presented */ lib$cvt_dtb(arglen,arg,&msg); /* ** Check that message is in legal range */ if ( !msg || (msg > ctx->pop3ctx$l_mlistsz) ) return spop3__send(ctx,&dsc_OUTRANGE,msg,ctx->pop3ctx$l_mlistsz); /* ** Try to jump to nearest message */ if ( ctx->pop3ctx$a_mlast && (msg >= ctx->pop3ctx$a_mlast->mhdr$l_messid) ) mp = ctx->pop3ctx$a_mlast; /* ** FINDing the particular message */ for(;mp && (mp->mhdr$l_messid != msg);mp = mp->mhdr$a_next); if ( !mp ) return spop3__send(ctx,&dsc_NOSUCHMSG,msg); if ( mp->mhdr$v_deleted ) return spop3__send(ctx,&dsc_HASDELTD,msg); /* ** Store pointer to last processed message */ ctx->pop3ctx$a_mlast = mp; spop3__send(ctx,&dsc_RETRM, mp->mhdr$l_msz?mp->mhdr$l_msz:mp->mhdr$l_lines*SPOP3__$K_LINESZ); /* ** If it's DECNET mail form a pseudo-internet header and send it before body */ if ( mp->mhdr$v_decnet ) { /* ** */ spop3__send(ctx,&dsc_DNETHDR,&mp->mhdr$b_fromlen,&mp->mhdr$l_date, &mp->mhdr$b_tolen,&mp->mhdr$b_subjlen,&charset); } INIT_SDESC(buf_dsc,sizeof(buf),buf); for (int i = 0;i < mp->mhdr$l_lines;i++) { if ( !(1 & (status = vmail_get_msg(ctx,mp,!i,buf,sizeof(buf),&buf_dsc.dsc$w_length))) ) break; /* ** If buffer contains a just dot (".", 0x2E), replace it with */ if ( (buf_dsc.dsc$w_length == 1) && (buf[0] == '.') ) buf[buf_dsc.dsc$w_length++] = ' '; /* ** Write data to network I/O channel or send it with SSL */ if ( !ctx->pop3ctx$v_tls ) { if ( !(1 & (status = netio_write(ctx->pop3ctx$l_chan,&buf_dsc))) ) break; } else { /* ** Add pair to string */ buf_dsc.dsc$a_pointer[buf_dsc.dsc$w_length++] = '\r'; buf_dsc.dsc$a_pointer[buf_dsc.dsc$w_length++] = '\n'; if ( 0 >= (status = SSL_write(ctx->pop3ctx$a_ssl,buf_dsc.dsc$a_pointer,buf_dsc.dsc$w_length)) ) { spop3__log(ctx,POP3_SSLERR,__MODULE__,__LINE__,status); status = SS$_DISCONNECT; break; } else status = SS$_NORMAL; } buf_dsc.dsc$w_length = sizeof(buf); } if ( !(1 & status) && (status != MAIL$_NOMOREREC) ) spop3__log(ctx,POP3_GENERR,__MODULE__,__LINE__,status); /* ** Close message retrieving context and mark message as "sent" */ vmail_msg_end(ctx,mp); mp->mhdr$v_sent |= 1; return spop3__send(ctx,&dsc_EOM); } int pop3_top ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { unsigned status = MAIL$_NOMOREREC, msg = 0, lines = 0; MHDR *mp = ctx->pop3ctx$a_mlist; unsigned char buf[4096]; struct dsc$descriptor buf_dsc; if ( !arglen || !arg ) return spop3__send(ctx,&dsc_NOARG); /* ** Get message number and number of lines */ *(arg + arglen) = '\0'; if ( 2 != sscanf(arg,"%u %u",&msg,&lines) ) return spop3__send(ctx,&dsc_NOARG); /* ** Check that message in legal range */ if ( !msg || (msg > ctx->pop3ctx$l_mlistsz) ) return spop3__send(ctx,&dsc_OUTRANGE,msg,ctx->pop3ctx$l_mlistsz); /* ** Try to jump to nearest message */ if ( ctx->pop3ctx$a_mlast && (msg >= ctx->pop3ctx$a_mlast->mhdr$l_messid) ) mp = ctx->pop3ctx$a_mlast; /* ** FINDing the particular message */ for(;mp && (mp->mhdr$l_messid != msg);mp = mp->mhdr$a_next); if ( !mp ) return spop3__send(ctx,&dsc_NOSUCHMSG,msg); if ( mp->mhdr$v_deleted ) return spop3__send(ctx,&dsc_HASDELTD,msg); /* ** Store pointer to last processed message */ ctx->pop3ctx$a_mlast = mp; spop3__send(ctx,&dsc_OK); /* ** If it's DECNET mail form a pseudo-internet header and send it before body */ if ( mp->mhdr$v_decnet ) { /* ** */ spop3__send(ctx,&dsc_DNETHDR,&mp->mhdr$b_fromlen,&mp->mhdr$l_date, &mp->mhdr$b_tolen,&mp->mhdr$b_subjlen,&charset); } INIT_SDESC(buf_dsc,sizeof(buf),buf); for (int i = 0;i < (lines?lines:mp->mhdr$l_lines);i++) { if ( !(1 & (status = vmail_get_msg(ctx,mp,!i,buf,sizeof(buf),&buf_dsc.dsc$w_length))) ) break; /* ** If lines is ZERO we sending only header */ if ( !lines && (!buf_dsc.dsc$w_length || mp->mhdr$v_decnet) ) break; /* ** If buffer contains a just dot (".", 0x2E), replace it with */ if ( (buf_dsc.dsc$w_length == 1) && (buf[0] == '.') ) buf[buf_dsc.dsc$w_length++] = ' '; /* ** Write data to network I/O channel or send it with SSL */ if ( !ctx->pop3ctx$v_tls ) { if ( !(1 & (status = netio_write(ctx->pop3ctx$l_chan,&buf_dsc))) ) break; } else { /* ** Add pair to string */ buf_dsc.dsc$a_pointer[buf_dsc.dsc$w_length++] = '\r'; buf_dsc.dsc$a_pointer[buf_dsc.dsc$w_length++] = '\n'; if ( 0 >= (status = SSL_write(ctx->pop3ctx$a_ssl,buf_dsc.dsc$a_pointer,buf_dsc.dsc$w_length)) ) { spop3__log(ctx,POP3_SSLERR,__MODULE__,__LINE__,status); status = SS$_DISCONNECT; break; } else status = SS$_NORMAL; } buf_dsc.dsc$w_length = sizeof(buf); } if ( !(1 & status) && (status != MAIL$_NOMOREREC) ) spop3__log(ctx,POP3_GENERR,__MODULE__,__LINE__,status); /* ** Close message retrievibng context and mark message as "sent" */ vmail_msg_end(ctx,mp); mp->mhdr$v_sent |= 1; return spop3__send(ctx,&dsc_EOM); } int pop3_capa ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { if ( ctx->pop3ctx$l_expire ) return spop3__send(ctx,&dsc_CAPA,logintvl,ctx->pop3ctx$l_expire); return spop3__send(ctx,&dsc_CAPA2,logintvl); } int pop3_last ( POP3CTX *ctx, unsigned char *arg, unsigned short arglen ) { unsigned status, msg = 0, lines = 0; MHDR *mp = ctx->pop3ctx$a_mlist; unsigned char buf[1024]; struct dsc$descriptor buf_dsc; /* ** Return to client a number last has beed accessed message */ return spop3__send(ctx,&dsc_LAST,ctx->pop3ctx$a_mlast?ctx->pop3ctx$a_mlast->mhdr$l_messid:0); } void * spop3__thread (POP3CTX *ctx) { int status,st,uid[4]; unsigned char buf[255]; struct dsc$descriptor_s buf_dsc; struct pop3cmd *cmdp; /* ** Print out WELCOME message */ if ( !(1 & (status = sys$create_uid(&uid))) ) lib$signal(status); INIT_SDESC(buf_dsc,sizeof(buf),buf); if ( !(1 & (status = lib$uid_to_ascii(&uid,&buf_dsc))) ) lib$signal(status); status = spop3__send(ctx,&dsc_WELCOME,0,&buf_dsc); /* ** Main session command processing loop */ while ( 1 & status ) { /* ** Start reading a command from network client */ INIT_SDESC(buf_dsc,sizeof(buf),buf); if ( !(1 & (status = spop3__recv(ctx,&buf_dsc,&buf_dsc.dsc$w_length))) ) break; /* ** Log an input if the session under DEBUG */ if ( ctx->pop3ctx$v_debug ) spop3__log(ctx,POP3_DEBUG,&buf_dsc); /* ** Run over command table to match gottent command */ for ( cmdp = &pop3cmd_tbl[0];cmdp->fun;cmdp++) { /* ** Check that current session level is enough for ** the command, if not - just skip the command. */ if ( ctx->pop3ctx$l_level != cmdp->level ) continue; if ( (buf_dsc.dsc$w_length >= cmdp->clen) && (!strncasecmp(cmdp->cname,buf,cmdp->clen)) ) { /* ** Dispatch processing of command */ status = cmdp->fun(ctx, (buf_dsc.dsc$w_length > cmdp->clen)?buf + cmdp->clen + 1:NULL, (buf_dsc.dsc$w_length > cmdp->clen)?buf_dsc.dsc$w_length-cmdp->clen-1:0); /* ** Is this POP3' PASS ? */ if ( enaips && (cmdp->fun == pop3_pass) && !(status & 1) ) { char srch[32] = {0}; if ( 1 == sscanf(ctx->pop3ctx$t_addr,"%[^:\n]",&srch) ) st = send_filter_event(&rule,&srch,110,&host); } break; } } /* ** A command has not been recognized - log this */ if ( !cmdp->fun ) { status = spop3__send(ctx,&dsc_BADCMD,&buf_dsc); // spop3__log(ctx,POP3_ILLCMD,&buf_dsc); } }; /* while (...) */ /* ** Close network stuff */ if ( !(1 & (st = netio_close (ctx->pop3ctx$l_chan))) ) spop3__log(ctx,POP3_GENERR,__MODULE__,__LINE__,st); /* ** Check a completion status */ if ( status != SS$_DISCONNECT ) spop3__log(ctx,POP3_CLOSED,status); /* ** Mark context as "terminated and to be cleanuped" */ ctx->pop3ctx$v_termd = 1; /* ** Stop sleeping, there is something to do!!! */ sys$wake(0,0); pthread_exit(&status); return NULL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Send SHUTDOWN signal. A target(s) node(s) designated by input parameter: ** * - shutdown SPOP3 server at all cluster member ** - shutdown SPOP3 server at target node ** ** ** FORMAL PARAMETERS: ** ** None. ** ** RETURN VALUE: ** ** VMS Condition code ** ** **-- */ int spop3__shut ( unsigned char *nodemask ) { int status,csid = -1,retlen = 0; lksb lksb_; iosb iosb_; char buf [ 32 ], res [32] ; struct dsc$descriptor res_dsc; ile3 items [] = {{sizeof(buf),SYI$_NODENAME,buf,&retlen},{0,0,0,0}}; INIT_SDESC(res_dsc,sizeof(res),res); /* ** Send signal to a specified node */ if ( *nodemask != '*' ) { if ( !(1 & (status = sys$fao(&resfao,&res_dsc.dsc$w_length,&res_dsc,nodemask))) ) lib$signal(status); if ( !(1 & (status = str$upcase(&res_dsc,&res_dsc))) ) lib$signal(status); /* ** We send a signal by $ENQ request */ if ( (1 & (status = sys$enqw (EFN$C_ENF, LCK$K_EXMODE, &lksb_, LCK$M_SYSTEM,&res_dsc,0,0,0,0,0,0))) ) status = sys$deq(lksb_.lksb$l_lkid,0,0,0); spop3__log(NULL,POP3_SENTSHUT,&res_dsc,status); return status; } /* ** If nodemask is "*" - send shutdown signal to all cluster ** members */ while ( (1 & (status = sys$getsyiw(EFN$C_ENF,&csid,0,items,&iosb_,0,0))) && (1 & iosb_.iosb$w_status) ) { buf [ retlen ] = '\0'; if ( !(1 & (status = sys$fao(&resfao,&res_dsc.dsc$w_length,&res_dsc,buf))) ) lib$signal(status); if ( !(1 & (status = str$upcase(&res_dsc,&res_dsc))) ) lib$signal(status); /* ** We send a signal by $ENQ request */ if ( (1 & (status = sys$enqw (EFN$C_ENF, LCK$K_EXMODE, &lksb_, LCK$M_SYSTEM,&res_dsc,0,0,0,0,0,0))) ) status = sys$deq(lksb_.lksb$l_lkid,0,0,0); spop3__log(NULL,POP3_SENTSHUT,&res_dsc,status); } return status; } int evt_rtn_blkast (void) { /* ** Set global exit flag */ exit_flag = spop3__log(NULL,POP3_RCVDSHUT); /* ** Close POP3/POP3S channel to stop accepting incomming connections */ if ( smainchan ) netio_close(smainchan); netio_close(mainchan); /* ** Release a global lock */ sys$deq(lkid,0,0,0); /* ** Wake-up a main thread */ return sys$wake(0,0); } void evt_rtn_wakeup (void) { int status; /* ** Prepare to timeout processing */ if ( !(1 & (status = sys$setimr(EFN$C_ENF, &wakeuptmo,evt_rtn_wakeup,0,0))) ) lib$signal(status); /* ** Wake-up a main thread */ sys$wake(0,0); } void * lstn_pop3 (void) { int status,i = 0; unsigned chan = 0, port = 0; POP3CTX *newctx; struct in_addr addr; /* ** A main loop */ while ( !exit_flag ) { /* ** Start waiting for new connection request */ if ( !(1 & (status = netio_accept (mainchan,&chan,&addr,&port))) ) { /* ** Error checking. */ if ( (status == SS$_CANCEL) || (SS$_ABORT) ) break; else lib$signal(status); } /* ** Get a free connection/session context from a pool */ pthread_mutex_lock(&pop3ctx_tbl_mtx); for (newctx = NULL, i = 0; i < SPOP3__$K_MAXCTX; i++) { if ( !pop3ctx_tbl[i].pop3ctx$v_used && !pop3ctx_tbl[i].pop3ctx$v_termd ) { /* ** Found a free context */ newctx = &pop3ctx_tbl[i]; memset(newctx,0,sizeof(POP3CTX)); newctx->pop3ctx$v_used = 1; break; } } pthread_mutex_unlock(&pop3ctx_tbl_mtx); if ( !newctx ) { // exit_flag = spop3__log(NULL,POP3_NOFREECTX); spop3__log(NULL,POP3_NOFREECTX); netio_write(chan,&dsc_BUSY); netio_close(chan); continue; } /* ** Clear & Filling new context area */ newctx->pop3ctx$b_idx = (unsigned char) i; newctx->pop3ctx$l_chan = chan; newctx->pop3ctx$b_alen = (unsigned char) sprintf(newctx->pop3ctx$t_addr,"%u.%u.%u.%u", addr.S_un.S_un_b.s_b1,addr.S_un.S_un_b.s_b2, addr.S_un.S_un_b.s_b3,addr.S_un.S_un_b.s_b4); /* ** Start a worker thread */ if ( status = pthread_create((pthread_t *)&newctx->pop3ctx$q_tid,&tattr,(void*) spop3__thread,(void *)newctx) ) { /* ** If error is occured close connection & return connection/session ** area to the free" pool */ spop3__log(NULL,POP3_ERRTHREAD, status,strerror(status)); netio_close(chan); pthread_mutex_lock(&pop3ctx_tbl_mtx); newctx->pop3ctx$v_used = 0; pthread_mutex_unlock(&pop3ctx_tbl_mtx); } else { char buf [64]; sprintf(buf,"%04.4x/%.*s:%u",newctx->pop3ctx$l_chan, newctx->pop3ctx$b_alen,newctx->pop3ctx$t_addr,port); pthread_setname_np(newctx->pop3ctx$q_tid,buf,0); } }; pthread_exit(&status); return NULL; } void * lstn_pop3s (void) { int status,i = 0; unsigned chan = 0, port = 0; POP3CTX *newctx; struct in_addr addr; /* ** A main loop */ while ( !exit_flag ) { /* ** Start waiting for new connection request */ if ( !(1 & (status = netio_accept (smainchan,&chan,&addr,&port))) ) { /* ** Error checking. */ if ( (status == SS$_CANCEL) || (SS$_ABORT) ) break; else lib$signal(status); } /* ** Get a free connection/session context from a pool */ pthread_mutex_lock(&pop3ctx_tbl_mtx); for (newctx = NULL, i = 0; i < SPOP3__$K_MAXCTX; i++) { if ( !pop3ctx_tbl[i].pop3ctx$v_used && !pop3ctx_tbl[i].pop3ctx$v_termd ) { /* ** Found a free context, zeroing and mark as "used" */ newctx = &pop3ctx_tbl[i]; memset(newctx,0,sizeof(POP3CTX)); newctx->pop3ctx$v_used = 1; break; } } pthread_mutex_unlock(&pop3ctx_tbl_mtx); if ( !newctx ) { exit_flag = spop3__log(NULL,POP3_NOFREECTX); netio_write(chan,&dsc_BUSY); netio_close(chan); continue; } /* ** Clear & Filling new context area */ newctx->pop3ctx$b_idx = (unsigned char) i; newctx->pop3ctx$l_chan = chan; newctx->pop3ctx$b_alen = (unsigned char) sprintf(newctx->pop3ctx$t_addr,"%u.%u.%u.%u", addr.S_un.S_un_b.s_b1,addr.S_un.S_un_b.s_b2, addr.S_un.S_un_b.s_b3,addr.S_un.S_un_b.s_b4); /* ** Initialize SSL/SSL stuff: ** - Create/Initialise SSL/SSL context ** - Get a Socket corresponded to the network I/O chanel number ** - Perform SSL Handshake with the remote client */ newctx->pop3ctx$v_tls = 1; if ( !(newctx->pop3ctx$a_ssl = SSL_new(ssl_ctx)) ) { spop3__log(newctx,POP3_SSLERR,__MODULE__,__LINE__,ERR_get_error()); SSL_free(newctx->pop3ctx$a_ssl); netio_close(chan); pthread_mutex_lock(&pop3ctx_tbl_mtx); newctx->pop3ctx$v_used = 0; pthread_mutex_unlock(&pop3ctx_tbl_mtx); continue; } if ( (!SSL_set_fd(newctx->pop3ctx$a_ssl, decc$socket_fd (chan))) || (1 != (status = SSL_accept(newctx->pop3ctx$a_ssl))) ) { spop3__log(newctx,POP3_SSLERR,__MODULE__,__LINE__,ERR_get_error()); SSL_free(newctx->pop3ctx$a_ssl); netio_close(chan); pthread_mutex_lock(&pop3ctx_tbl_mtx); newctx->pop3ctx$v_used = 0; pthread_mutex_unlock(&pop3ctx_tbl_mtx); continue; } /* ** Start a worker thread */ if ( (1 != status ) || (status = pthread_create((pthread_t *)&newctx->pop3ctx$q_tid,&tattr,(void*) spop3__thread, (void *)newctx)) ) { /* ** If error is occured close connection & return connection/session ** area to the free" pool */ spop3__log(newctx,POP3_ERRTHREAD, status,strerror(status)); SSL_free(newctx->pop3ctx$a_ssl); netio_close(chan); pthread_mutex_lock(&pop3ctx_tbl_mtx); newctx->pop3ctx$v_used = 0; pthread_mutex_unlock(&pop3ctx_tbl_mtx); } else { char buf [64]; sprintf(buf,"%04.4x/%.*s:%u",newctx->pop3ctx$l_chan, newctx->pop3ctx$b_alen,newctx->pop3ctx$t_addr,port); pthread_setname_np(newctx->pop3ctx$q_tid,buf,0); } } pthread_exit(&status); return NULL; } int main (int argc,char **argv) { int status,i = 0, ret_code = 0,option = LIB$K_DELTA_SECONDS, seconds = 0; unsigned port = 0, sport = POP3__$K_SPORT; POP3CTX *newctx; struct in_addr addr; lksb lksb_; iosb iosb_; pthread_t tid,stid; struct dsc$descriptor res_dsc; unsigned char buf[32],resbuf[32], *cp; ile3 items [] = {{sizeof(buf),SYI$_NODENAME,buf,&ret_code},{0,0,0,0}}; COMP_METHOD * cm; /* ** It's SHUT request ? */ if ( argc == 3 && !strncasecmp("SHUT",argv[1],4)) return spop3__shut(argv[2]); else if ( argc != 1 ) return SS$_INVARG; /* ** Enqueue a LOCK request to listening for SHUTDOWN command ** */ if ( !(1 & (status = sys$getsyiw(EFN$C_ENF,0,0,items,&iosb_,0,0))) || !(1 & iosb_.iosb$w_status) ) sys$exit( 1 & status?iosb_.iosb$w_status:status); buf [ ret_code ] = '\0'; INIT_SDESC(res_dsc,sizeof(resbuf),resbuf); if ( !(1 & (status = sys$fao(&resfao,&res_dsc.dsc$w_length,&res_dsc,buf))) ) lib$signal(status); if ( !(1 & (status = sys$enqw (EFN$C_ENF, LCK$K_CRMODE, &lksb_, LCK$M_SYSTEM,&res_dsc,0,0,0,evt_rtn_blkast,0,0,0))) || !(1 & lksb_.lksb$w_status) ) sys$exit( 1 & status?lksb_.lksb$w_status:status); lkid = lksb_.lksb$l_lkid; /* ** Get configuration variables */ if ( cp = getenv("SPOP3_PORT") ) port = atoi(cp); port = port?port:POP3__$K_PORT; if ( cp = getenv("SPOP3_SPORT") ) sport = atoi(cp); if ( cp = getenv("SPOP3_EXPIRE") ) expire = atoi(cp); else expire = POP3__$K_EXPIRE; if ( cp = getenv("SPOP3_LOGINTVL") ) logintvl = atoi(cp); logintvl= logintvl?logintvl:POP3__$K_LOGINTVL; if ( cp = getenv("SPOP3_PR_THRESHOLD") ) pr_threshold = atoi(cp); if ( cp = getenv("SPOP3_TIMEOUT") ) { seconds = atoi(cp); seconds = seconds?seconds:SPOP3__$K_TIMEOUT; } else seconds = SPOP3__$K_TIMEOUT; if ( !(1 & (status = lib$cvt_to_internal_time(&option,&seconds,&iotmo))) ) return status; seconds /= 2; if ( !(1 & (status = lib$cvt_to_internal_time(&option,&seconds,&wakeuptmo))) ) return status; if ( !(cp = getenv("SPOP3_CHARSET")) ) cp = "iso-8859-5"; strncpy(&charset[1],cp,sizeof(charset) - 1); charset[0] = (unsigned char) strnlen(cp,sizeof(charset) - 1); if ( cp = getenv("SPOP3_IPS") ) { strncpy(&host,cp,sizeof(host)-1); enaips = filter_server_register(&compname, 0, 0); spop3__log(NULL,POP3_ENAIPS, &compname, &rule, &host, enaips?"enabled":"disabled"); } spop3_new2mail = NULL != getenv("SPOP3_NEW2MAIL"); if ( !(cp = getenv("SPOP3_FOLDER")) ) cp = "NEWMAIL"; strncpy(&spop3_folder[1],cp,sizeof(spop3_folder) - 1); spop3_folder[0] = (unsigned char) strnlen(cp,sizeof(spop3_folder) - 1); /* ** SSL/SSL related initialization: ** - Load encryption & hashing algorithms for the SSL program ** - Load the error strings for SSL & CRYPTO APIs ** - Create a SSL_METHOD structure (choose a SSL/SSL protocol version) ** - Create a SSL_CTX structure */ SSL_library_init(); SSL_load_error_strings(); ssl_meth = SSLv23_method(); if ( !(ssl_ctx = SSL_CTX_new(ssl_meth)) ) spop3__log(NULL,POP3_SSLERR,__MODULE__,__LINE__,ERR_get_error()); /* ** - Load the server certificate into the SSL_CTX structure ** - Load the private-key corresponding to the server certificate ** - Check if the server certificate and private-key matches */ if ( !SSL_CTX_use_certificate_file(ssl_ctx,SPOP3_RSA_CERT,SSL_FILETYPE_PEM) || !SSL_CTX_use_PrivateKey_file(ssl_ctx,SPOP3_RSA_CERT,SSL_FILETYPE_PEM) || !SSL_CTX_check_private_key(ssl_ctx) ) { fprintf(stderr, "Error setting up SSL_CTX\n"); spop3__log(NULL,POP3_SSLERR,__MODULE__,__LINE__,ERR_get_error()); return 0; } /* ** Initialize compression stuff */ if ( (cm = COMP_zlib()) && (cm->type != NID_undef) ) i = 0XE0; else if ( (cm = COMP_rle()) && (cm->type != NID_undef) ) i = 0XE1; else i = 0; if ( i ) spop3__log(NULL,POP3_SSLCOMPR,i); if ( i && (status = SSL_COMP_add_compression_method(i, cm)) ) { fprintf(stderr, "SSL_COMP_add_compression_method(%d,..)\n",i); spop3__log(NULL,POP3_SSLERR,__MODULE__,__LINE__,ERR_get_error()); } else if (i && (!status) ) spop3__log(NULL,POP3_SSLCOMPR,i); /* ** Initialize VMS MAIL API */ if ( !(1 & (status = vmail_init())) ) sys$exit(status); /* ** Initialize network socket to listening incomming connection request ** optionaly check and start listening on the POP3/RLS port */ if ( !(1 & (status = netio_start(&mainchan,port))) ) lib$signal(status); if ( sport && !(1 & (status = netio_start(&smainchan,sport))) ) lib$signal(status); /* ** Initalize attributes for threads and multithreaded environment of the C RTL */ decc$set_reentrancy(C$C_MULTITHREAD); status = pthread_attr_init(&tattr); status = pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_JOINABLE); status = pthread_attr_setstacksize(&tattr,64000); /* ** Start a main listetning thread ** check sport and start listening thread on the SSL port */ if ( status = pthread_create(&tid,&tattr,(void*) lstn_pop3,NULL) ) { netio_close(mainchan); return spop3__log(NULL,POP3_ERRTHREAD, status,strerror(status)); } else pthread_setname_np(tid,"lstn_pop3",0); if ( sport && (status = pthread_create(&stid,&tattr,(void*) lstn_pop3s,NULL)) ) { netio_close(smainchan); spop3__log(NULL,POP3_ERRTHREAD, status, strerror(status)); } else pthread_setname_np(stid,"lstn_pop3s",0); spop3__log(NULL,POP3_FDINFO,&spop3_folder,spop3_new2mail?"":"NOT "); spop3__log(NULL,POP3_STARTED,port,sport,expire,logintvl,&iotmo); /* ** Start "watchdog" */ evt_rtn_wakeup(); /* ** A main loop */ do { /* ** Performs a cleanuping terminated connections/threads */ for (i = 0; i < SPOP3__$K_MAXCTX; i++) { if ( !pop3ctx_tbl[i].pop3ctx$v_termd ) continue; /* ** Joining with the thread to detach it */ if ( status = pthread_join((pthread_t) pop3ctx_tbl[i].pop3ctx$q_tid,(void *)&ret_code) ) spop3__log(&pop3ctx_tbl[i],POP3_ERRTHREAD, status, strerror(status)); /* ** Performs VMS Mail Box cleanuping */ if ( !(1 & (status = vmail_shut_ctx(&pop3ctx_tbl[i]))) ) spop3__log(&pop3ctx_tbl[i],POP3_VMSHUT,status); /* ** Mark context a "free to use" */ pthread_mutex_lock(&pop3ctx_tbl_mtx); pop3ctx_tbl[i].pop3ctx$v_used = pop3ctx_tbl[i].pop3ctx$v_termd = 0; pthread_mutex_unlock(&pop3ctx_tbl_mtx); } } while ( !exit_flag && (1 & sys$hiber()) ); /* ** Close main connection socket */ if ( !(1 & (status = netio_close (mainchan))) && !exit_flag ) lib$signal(status); if ( smainchan && !(1 & (status = netio_close (smainchan))) && !exit_flag ) lib$signal(status); /* ** Joining with the main connection threads */ if ( status = pthread_join(tid,(void *)&ret_code) ) spop3__log(NULL,POP3_ERRTHREAD, status, strerror(status)); if ( status = pthread_join(stid,(void *)&ret_code) ) spop3__log(NULL,POP3_ERRTHREAD, status, strerror(status)); /* ** Performs a cleanuping running & terminated threads */ for (i = 0; i < SPOP3__$K_MAXCTX; i++) { /* ** Skip running and not yet terminated */ if ( !pop3ctx_tbl[i].pop3ctx$v_termd || !pop3ctx_tbl[i].pop3ctx$v_used ) continue; /* ** Joining with the thread to detach it */ if ( status = pthread_join((pthread_t) pop3ctx_tbl[i].pop3ctx$q_tid,(void *)&ret_code) ) spop3__log(&pop3ctx_tbl[i],POP3_ERRTHREAD, status, strerror(status)); /* ** Is this session was over SSL (?) - free/cleanup SSL/SSL stuff */ if ( pop3ctx_tbl[i].pop3ctx$v_tls && pop3ctx_tbl[i].pop3ctx$a_ssl ) SSL_free(pop3ctx_tbl[i].pop3ctx$a_ssl); /* ** Performs VMS Mail Box cleanuping */ if ( !(1 & (status = vmail_shut_ctx(&pop3ctx_tbl[i]))) ) spop3__log(&pop3ctx_tbl[i],POP3_VMSHUT,status); } /* ** Free the SSL_CTX structure */ SSL_CTX_free(ssl_ctx); {int msgvec[] = {1,exit_flag}; sys$putmsg(&msgvec,0,0,0);} return exit_flag; }