diff --git a/ctrl/sbbs.ini b/ctrl/sbbs.ini index f123bf27d70b185f9bdf8063246a638106050e28..25e3034eafeb588dfdbcc2f71661a93521d5c393 100644 --- a/ctrl/sbbs.ini +++ b/ctrl/sbbs.ini @@ -230,6 +230,8 @@ Options=INDEX_FILE | HTML_INDEX_FILE | ALLOW_QWK AutoStart=true Interface= Port=80 + TLSInterface= + TLSPort=443 MaxClients=150 RootDirectory=../web/root ErrorDirectory=error diff --git a/ctrl/sockopts.ini b/ctrl/sockopts.ini index a2f65b882cb10ed6166e47b3555360844df4e8b5..96108f9d4f11b59a19fa0555a80eac075e5c0a84 100644 --- a/ctrl/sockopts.ini +++ b/ctrl/sockopts.ini @@ -28,6 +28,7 @@ ; Global socket options set here, in root section SNDBUF = 8192 RCVBUF = 8192 +IPV6_V6ONLY = 1 ; TCP-specific options set here [tcp] diff --git a/exec/load/sbbsdefs.js b/exec/load/sbbsdefs.js index c5a3048e3ff34e2190548f56b6194a96f71e0e0d..13ffa8a4cda80ff09848ff6c7b90dac461002ef3 100644 --- a/exec/load/sbbsdefs.js +++ b/exec/load/sbbsdefs.js @@ -771,12 +771,13 @@ var LEN_FCDT =9; /* 9 digits for file credit values */ var LEN_TITLE =70; /* Message title */ var LEN_MAIN_CMD =34; /* Storage in user.dat for custom commands */ var LEN_XFER_CMD =40; -var LEN_SCAN_CMD =40; -var LEN_MAIL_CMD =40; -var LEN_CID =25; /* Caller ID (phone number) */ +var LEN_SCAN_CMD =35; +var LEN_IPADDR =45; +var LEN_CID =45; /* Caller ID (phone number or IP address) */ var LEN_ARSTR =40; /* Max length of Access Requirement string */ var LEN_CHATACTCMD =9; /* Chat action command */ -var LEN_CHATACTOUT =65; /* Chat action output string */ /************************************************/ +var LEN_CHATACTOUT =65; /* Chat action output string */ + /************************************************/ /********************************************/ @@ -833,8 +834,8 @@ var U_CURXTRN =U_CURSUB+16; /* Current xtrn (internal code) */ var U_MAIN_CMD =U_CURXTRN+8+2; /* unused */ var U_XFER_CMD =U_MAIN_CMD+LEN_MAIN_CMD; /* unused */ var U_SCAN_CMD =U_XFER_CMD+LEN_XFER_CMD+2; /* unused */ -var U_MAIL_CMD =U_SCAN_CMD+LEN_SCAN_CMD; /* unused */ -var U_FREECDT =U_MAIL_CMD+LEN_MAIL_CMD+2; +var U_IPADDR =U_SCAN_CMD+LEN_SCAN_CMD; /* unused */ +var U_FREECDT =U_IPADDR+LEN_IPADDR+2; var U_FLAGS3 =U_FREECDT+10; /* Flag set #3 */ var U_FLAGS4 =U_FLAGS3+8; /* Flag set #4 */ var U_XEDIT =U_FLAGS4+8; /* External editor (code */ diff --git a/src/sbbs3/answer.cpp b/src/sbbs3/answer.cpp index 706a186d857cf901f922e388a993856602b17c52..3912e8b96ab3a15b39e405711ec10993b3b95a21 100644 --- a/src/sbbs3/answer.cpp +++ b/src/sbbs3/answer.cpp @@ -52,7 +52,7 @@ bool sbbs_t::answer() useron.number=0; answertime=logontime=starttime=now=time(NULL); /* Caller ID is IP address */ - SAFECOPY(cid,inet_ntoa(client_addr.sin_addr)); + SAFECOPY(cid,client_ipaddr); memset(&tm,0,sizeof(tm)); localtime_r(&now,&tm); @@ -349,7 +349,7 @@ bool sbbs_t::answer() /* AutoLogon via IP or Caller ID here */ if(!useron.number && !(sys_status&SS_RLOGIN) && (startup->options&BBS_OPT_AUTO_LOGON) && cid[0]) { - useron.number=userdatdupe(0, U_NOTE, LEN_NOTE, cid); + useron.number=userdatdupe(0, U_IPADDR, LEN_IPADDR, cid); if(useron.number) { getuserdat(&cfg, &useron); if(!(useron.misc&AUTOLOGON) || !(useron.exempt&FLAG('V'))) @@ -436,8 +436,8 @@ bool sbbs_t::answer() /* Save the IP to the user's note */ if(cid[0]) { - SAFECOPY(useron.note,cid); - putuserrec(&cfg,useron.number,U_NOTE,LEN_NOTE,useron.note); + SAFECOPY(useron.ipaddr,cid); + putuserrec(&cfg,useron.number,U_IPADDR,LEN_IPADDR,useron.ipaddr); } /* Save host name to the user's computer description */ diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp index 3f3bd27fbd008643bb0bb3fe851fd2dd49faf3fe..cecbfe452e4e6b9dd6f4044486fa5e3e6cbc6b23 100644 --- a/src/sbbs3/atcodes.cpp +++ b/src/sbbs3/atcodes.cpp @@ -636,11 +636,8 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen) if(!strcmp(sp,"CID") || !strcmp(sp,"IP")) return(cid); - if(!strcmp(sp,"LOCAL-IP")) { - struct in_addr in_addr; - in_addr.s_addr=local_addr; - return(inet_ntoa(in_addr)); - } + if(!strcmp(sp,"LOCAL-IP")) + return(local_addr); if(!strcmp(sp,"CRLF")) return("\r\n"); diff --git a/src/sbbs3/chk_ar.cpp b/src/sbbs3/chk_ar.cpp index 8ecd32c92d25e0fb5ef30afd7ee43fbbe6733207..807c09faec49af99e0664dad66825c0df8a228b7 100644 --- a/src/sbbs3/chk_ar.cpp +++ b/src/sbbs3/chk_ar.cpp @@ -631,7 +631,7 @@ bool sbbs_t::ar_exp(const uchar **ptrptr, user_t* user, client_t* client) if(client!=NULL) p=client->addr; else - p=user->note; + p=user->ipaddr; if(!findstr_in_string(p,(char*)*ptrptr)) result=_not; else diff --git a/src/sbbs3/client.h b/src/sbbs3/client.h index 167b559065a1b835715f1d135688685324ac966c..fab1f6e89b6d25f7db9553c123e8cefa2e6ab093 100644 --- a/src/sbbs3/client.h +++ b/src/sbbs3/client.h @@ -39,18 +39,18 @@ #define _CLIENT_H #include "gen_defs.h" /* WORD, DWORD */ +#include "sockwrap.h" /* INET6_ADDRSTRLEN */ #include <time.h> /* time_t */ /* Used for sbbsctrl->client window */ typedef struct { size_t size; /* size of this struct */ - char addr[16]; /* IP address */ - char host[64]; /* host name */ + char addr[INET6_ADDRSTRLEN]; /* IP address */ + char host[256]; /* host name */ WORD port; /* TCP port number */ time32_t time; /* connect time */ const char* protocol; /* protocol description */ const char* user; /* user name */ - char pad[32]; /* padding for future expansion */ } client_t; /* Used for ctrl/client.dab */ diff --git a/src/sbbs3/cmdshell.h b/src/sbbs3/cmdshell.h index 78cfffd142ceb447f61e3ecec5f22a613b931362..6267b81dfb122cc73690a59afc55d63c1b72ab4d 100644 --- a/src/sbbs3/cmdshell.h +++ b/src/sbbs3/cmdshell.h @@ -596,6 +596,7 @@ enum { ,USER_STRING_MODEM ,USER_STRING_COMMENT ,USER_STRING_NETMAIL + ,USER_STRING_IPADDR }; diff --git a/src/sbbs3/ctrl/FtpCfgDlgUnit.cpp b/src/sbbs3/ctrl/FtpCfgDlgUnit.cpp index e5e5d5e84fceac6161ecfd97cd0b4670bd81fbd6..ff75f6a8e5ca8c6837fff1ddbc361257ec13aa01 100644 --- a/src/sbbs3/ctrl/FtpCfgDlgUnit.cpp +++ b/src/sbbs3/ctrl/FtpCfgDlgUnit.cpp @@ -55,25 +55,25 @@ void __fastcall TFtpCfgDlg::FormShow(TObject *Sender) { char str[128]; - if(MainForm->ftp_startup.interface_addr==0) + if(MainForm->ftp_startup.outgoing4.s_addr==0) NetworkInterfaceEdit->Text="<ANY>"; else { sprintf(str,"%d.%d.%d.%d" - ,(MainForm->ftp_startup.interface_addr>>24)&0xff - ,(MainForm->ftp_startup.interface_addr>>16)&0xff - ,(MainForm->ftp_startup.interface_addr>>8)&0xff - ,MainForm->ftp_startup.interface_addr&0xff + ,(MainForm->ftp_startup.outgoing4.s_addr>>24)&0xff + ,(MainForm->ftp_startup.outgoing4.s_addr>>16)&0xff + ,(MainForm->ftp_startup.outgoing4.s_addr>>8)&0xff + ,MainForm->ftp_startup.outgoing4.s_addr&0xff ); NetworkInterfaceEdit->Text=AnsiString(str); } - if(MainForm->ftp_startup.pasv_ip_addr==0) + if(MainForm->ftp_startup.pasv_ip_addr.s_addr==0) PasvIpAddrEdit->Text="<unspecified>"; else { sprintf(str,"%d.%d.%d.%d" - ,(MainForm->ftp_startup.pasv_ip_addr>>24)&0xff - ,(MainForm->ftp_startup.pasv_ip_addr>>16)&0xff - ,(MainForm->ftp_startup.pasv_ip_addr>>8)&0xff - ,MainForm->ftp_startup.pasv_ip_addr&0xff + ,(MainForm->ftp_startup.pasv_ip_addr.s_addr>>24)&0xff + ,(MainForm->ftp_startup.pasv_ip_addr.s_addr>>16)&0xff + ,(MainForm->ftp_startup.pasv_ip_addr.s_addr>>8)&0xff + ,MainForm->ftp_startup.pasv_ip_addr.s_addr&0xff ); PasvIpAddrEdit->Text=AnsiString(str); } @@ -135,9 +135,9 @@ void __fastcall TFtpCfgDlg::OKBtnClick(TObject *Sender) while(*p && *p!='.') p++; if(*p=='.') p++; addr|=atoi(p); - MainForm->ftp_startup.interface_addr=addr; + MainForm->ftp_startup.outgoing4.s_addr=addr; } else - MainForm->ftp_startup.interface_addr=0; + MainForm->ftp_startup.outgoing4.s_addr=0; SAFECOPY(str,PasvIpAddrEdit->Text.c_str()); p=str; while(*p && *p<=' ') p++; @@ -152,9 +152,9 @@ void __fastcall TFtpCfgDlg::OKBtnClick(TObject *Sender) while(*p && *p!='.') p++; if(*p=='.') p++; addr|=atoi(p); - MainForm->ftp_startup.pasv_ip_addr=addr; + MainForm->ftp_startup.pasv_ip_addr.s_addr=addr; } else - MainForm->ftp_startup.pasv_ip_addr=0; + MainForm->ftp_startup.pasv_ip_addr.s_addr=0; MainForm->ftp_startup.max_clients=MaxClientsEdit->Text.ToIntDef(FTP_DEFAULT_MAX_CLIENTS); MainForm->ftp_startup.max_inactivity=MaxInactivityEdit->Text.ToIntDef(FTP_DEFAULT_MAX_INACTIVITY); diff --git a/src/sbbs3/ctrl/LoginAttemptsFormUnit.cpp b/src/sbbs3/ctrl/LoginAttemptsFormUnit.cpp index 298be978a5e271d03624b7dbd3880e391c9f2af6..1aeddfbcfbd37c634c9bd069a97a5a763f54af90 100644 --- a/src/sbbs3/ctrl/LoginAttemptsFormUnit.cpp +++ b/src/sbbs3/ctrl/LoginAttemptsFormUnit.cpp @@ -78,7 +78,7 @@ void __fastcall TLoginAttemptsForm::FillListView(TObject *Sender) Item->Caption=AnsiString(attempt->count-attempt->dupes); Item->Data=(void*)attempt->time; Item->SubItems->Add(attempt->dupes); - Item->SubItems->Add(inet_ntoa(attempt->addr)); + Item->SubItems->Add("TODO: Add addresses"); Item->SubItems->Add(attempt->prot); Item->SubItems->Add(attempt->user); Item->SubItems->Add(attempt->pass); diff --git a/src/sbbs3/ctrl/MailCfgDlgUnit.cpp b/src/sbbs3/ctrl/MailCfgDlgUnit.cpp index 123ebffa64b48c86a1b7fc3f18442e7df166dc0e..88811049ebefc19ccef5d926526fc67224173d6f 100644 --- a/src/sbbs3/ctrl/MailCfgDlgUnit.cpp +++ b/src/sbbs3/ctrl/MailCfgDlgUnit.cpp @@ -75,14 +75,14 @@ void __fastcall TMailCfgDlg::FormShow(TObject *Sender) { char str[128]; - if(MainForm->mail_startup.interface_addr==0) + if(MainForm->mail_startup.outgoing4.s_addr==0) NetworkInterfaceEdit->Text="<ANY>"; else { sprintf(str,"%d.%d.%d.%d" - ,(MainForm->mail_startup.interface_addr>>24)&0xff - ,(MainForm->mail_startup.interface_addr>>16)&0xff - ,(MainForm->mail_startup.interface_addr>>8)&0xff - ,MainForm->mail_startup.interface_addr&0xff + ,(MainForm->mail_startup.outgoing4.s_addr>>24)&0xff + ,(MainForm->mail_startup.outgoing4.s_addr>>16)&0xff + ,(MainForm->mail_startup.outgoing4.s_addr>>8)&0xff + ,MainForm->mail_startup.outgoing4.s_addr&0xff ); NetworkInterfaceEdit->Text=AnsiString(str); } @@ -233,9 +233,9 @@ void __fastcall TMailCfgDlg::OKBtnClick(TObject *Sender) while(*p && *p!='.') p++; if(*p=='.') p++; addr|=atoi(p); - MainForm->mail_startup.interface_addr=addr; + MainForm->mail_startup.outgoing4.s_addr=addr; } else - MainForm->mail_startup.interface_addr=0; + MainForm->mail_startup.outgoing4.s_addr=0; MainForm->mail_startup.smtp_port=SMTPPortEdit->Text.ToIntDef(IPPORT_SMTP); MainForm->mail_startup.submission_port=SubPortEdit->Text.ToIntDef(IPPORT_SUBMISSION); diff --git a/src/sbbs3/ctrl/MainFormUnit.cpp b/src/sbbs3/ctrl/MainFormUnit.cpp index 7dda8be4357f8034de3b5d7714d0089205b1afe9..f4514a1c4776996784fce7b02b508187cd7a5acc 100644 --- a/src/sbbs3/ctrl/MainFormUnit.cpp +++ b/src/sbbs3/ctrl/MainFormUnit.cpp @@ -845,9 +845,7 @@ __fastcall TMainForm::TMainForm(TComponent* Owner) bbs_startup.last_node=4; bbs_startup.options=BBS_OPT_XTRN_MINIMIZED|BBS_OPT_SYSOP_AVAILABLE; bbs_startup.telnet_port=IPPORT_TELNET; - bbs_startup.telnet_interface=INADDR_ANY; bbs_startup.rlogin_port=513; - bbs_startup.rlogin_interface=INADDR_ANY; bbs_startup.lputs=lputs; bbs_startup.event_lputs=lputs; bbs_startup.errormsg=errormsg; @@ -867,7 +865,6 @@ __fastcall TMainForm::TMainForm(TComponent* Owner) mail_startup.smtp_port=IPPORT_SMTP; mail_startup.relay_port=IPPORT_SMTP; mail_startup.pop3_port=110; - mail_startup.interface_addr=INADDR_ANY; mail_startup.lputs=lputs; mail_startup.errormsg=errormsg; mail_startup.status=mail_status; @@ -890,7 +887,6 @@ __fastcall TMainForm::TMainForm(TComponent* Owner) ftp_startup.size=sizeof(ftp_startup); ftp_startup.cbdata=&ftp_log_list; ftp_startup.port=IPPORT_FTP; - ftp_startup.interface_addr=INADDR_ANY; ftp_startup.lputs=lputs; ftp_startup.errormsg=errormsg; ftp_startup.status=ftp_status; @@ -927,7 +923,6 @@ __fastcall TMainForm::TMainForm(TComponent* Owner) memset(&services_startup,0,sizeof(services_startup)); services_startup.size=sizeof(services_startup); services_startup.cbdata=&services_log_list; - services_startup.interface_addr=INADDR_ANY; services_startup.lputs=lputs; services_startup.errormsg=errormsg; services_startup.status=services_status; @@ -2018,10 +2013,12 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender) if(Registry->ValueExists("JS_YieldInterval")) global.js.yield_interval=Registry->ReadInteger("JS_YieldInterval"); +/* if(Registry->ValueExists("TelnetInterface")) bbs_startup.telnet_interface=Registry->ReadInteger("TelnetInterface"); if(Registry->ValueExists("RLoginInterface")) bbs_startup.rlogin_interface=Registry->ReadInteger("RLoginInterface"); +*/ if(Registry->ValueExists("TelnetPort")) bbs_startup.telnet_port=Registry->ReadInteger("TelnetPort"); @@ -2060,8 +2057,10 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender) if(Registry->ValueExists("MailMaxInactivity")) mail_startup.max_inactivity=Registry->ReadInteger("MailMaxInactivity"); +/* if(Registry->ValueExists("MailInterface")) mail_startup.interface_addr=Registry->ReadInteger("MailInterface"); +*/ if(Registry->ValueExists("MailMaxDeliveryAttempts")) mail_startup.max_delivery_attempts @@ -2140,8 +2139,10 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender) if(Registry->ValueExists("FtpQwkTimeout")) ftp_startup.qwk_timeout=Registry->ReadInteger("FtpQwkTimeout"); +/* if(Registry->ValueExists("FtpInterface")) ftp_startup.interface_addr=Registry->ReadInteger("FtpInterface"); +*/ if(Registry->ValueExists("FtpPort")) ftp_startup.port=Registry->ReadInteger("FtpPort"); @@ -2173,9 +2174,11 @@ void __fastcall TMainForm::StartupTimerTick(TObject *Sender) if(Registry->ValueExists("FtpOptions")) ftp_startup.options=Registry->ReadInteger("FtpOptions"); +/* if(Registry->ValueExists("ServicesInterface")) services_startup.interface_addr =Registry->ReadInteger("ServicesInterface"); +*/ if(Registry->ValueExists("ServicesAnswerSound")) SAFECOPY(services_startup.answer_sound diff --git a/src/sbbs3/ctrl/ServicesCfgDlgUnit.cpp b/src/sbbs3/ctrl/ServicesCfgDlgUnit.cpp index c389dfe1a1825cd9bff859b0436b09088a3056bc..b8e6e96f5c9e5e10256edfb65d18aa3ea89a1154 100644 --- a/src/sbbs3/ctrl/ServicesCfgDlgUnit.cpp +++ b/src/sbbs3/ctrl/ServicesCfgDlgUnit.cpp @@ -23,15 +23,17 @@ __fastcall TServicesCfgDlg::TServicesCfgDlg(TComponent* Owner) void __fastcall TServicesCfgDlg::FormShow(TObject *Sender) { char str[128]; - - if(MainForm->services_startup.interface_addr==0) +/* +TODO: This is broken and stuff. +*/ + if(MainForm->services_startup.outgoing4.s_addr==0) NetworkInterfaceEdit->Text="<ANY>"; else { sprintf(str,"%d.%d.%d.%d" - ,(MainForm->services_startup.interface_addr>>24)&0xff - ,(MainForm->services_startup.interface_addr>>16)&0xff - ,(MainForm->services_startup.interface_addr>>8)&0xff - ,MainForm->services_startup.interface_addr&0xff + ,(MainForm->services_startup.outgoing4.s_addr>>24)&0xff + ,(MainForm->services_startup.outgoing4.s_addr>>16)&0xff + ,(MainForm->services_startup.outgoing4.s_addr>>8)&0xff + ,MainForm->services_startup.outgoing4.s_addr&0xff ); NetworkInterfaceEdit->Text=AnsiString(str); } @@ -99,9 +101,9 @@ void __fastcall TServicesCfgDlg::OKButtonClick(TObject *Sender) while(*p && *p!='.') p++; if(*p=='.') p++; addr|=atoi(p); - MainForm->services_startup.interface_addr=addr; + MainForm->services_startup.outgoing4.s_addr=addr; } else - MainForm->services_startup.interface_addr=0; + MainForm->services_startup.outgoing4.s_addr=0; MainForm->ServicesAutoStart=AutoStartCheckBox->Checked; diff --git a/src/sbbs3/ctrl/TelnetCfgDlgUnit.cpp b/src/sbbs3/ctrl/TelnetCfgDlgUnit.cpp index 73e7f713106c6f47a4929c06cdc0fae09054b796..7ab481fdb4397fab4d9af3ed20cd1299da155a7d 100644 --- a/src/sbbs3/ctrl/TelnetCfgDlgUnit.cpp +++ b/src/sbbs3/ctrl/TelnetCfgDlgUnit.cpp @@ -55,37 +55,37 @@ void __fastcall TTelnetCfgDlg::FormShow(TObject *Sender) { char str[128]; - if(MainForm->bbs_startup.telnet_interface==0) + if(MainForm->bbs_startup.outgoing4.s_addr==0) TelnetInterfaceEdit->Text="<ANY>"; else { sprintf(str,"%d.%d.%d.%d" - ,(MainForm->bbs_startup.telnet_interface>>24)&0xff - ,(MainForm->bbs_startup.telnet_interface>>16)&0xff - ,(MainForm->bbs_startup.telnet_interface>>8)&0xff - ,MainForm->bbs_startup.telnet_interface&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>24)&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>16)&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>8)&0xff + ,MainForm->bbs_startup.outgoing4.s_addr&0xff ); TelnetInterfaceEdit->Text=AnsiString(str); } - if(MainForm->bbs_startup.rlogin_interface==0) + if(MainForm->bbs_startup.outgoing4.s_addr==0) RLoginInterfaceEdit->Text="<ANY>"; else { sprintf(str,"%d.%d.%d.%d" - ,(MainForm->bbs_startup.rlogin_interface>>24)&0xff - ,(MainForm->bbs_startup.rlogin_interface>>16)&0xff - ,(MainForm->bbs_startup.rlogin_interface>>8)&0xff - ,MainForm->bbs_startup.rlogin_interface&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>24)&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>16)&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>8)&0xff + ,MainForm->bbs_startup.outgoing4.s_addr&0xff ); RLoginInterfaceEdit->Text=AnsiString(str); } - if(MainForm->bbs_startup.ssh_interface==0) + if(MainForm->bbs_startup.outgoing4.s_addr==0) SshInterfaceEdit->Text="<ANY>"; else { sprintf(str,"%d.%d.%d.%d" - ,(MainForm->bbs_startup.ssh_interface>>24)&0xff - ,(MainForm->bbs_startup.ssh_interface>>16)&0xff - ,(MainForm->bbs_startup.ssh_interface>>8)&0xff - ,MainForm->bbs_startup.ssh_interface&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>24)&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>16)&0xff + ,(MainForm->bbs_startup.outgoing4.s_addr>>8)&0xff + ,MainForm->bbs_startup.outgoing4.s_addr&0xff ); SshInterfaceEdit->Text=AnsiString(str); } @@ -148,9 +148,9 @@ void __fastcall TTelnetCfgDlg::OKBtnClick(TObject *Sender) while(*p && *p!='.') p++; if(*p=='.') p++; addr|=atoi(p); - MainForm->bbs_startup.telnet_interface=addr; + MainForm->bbs_startup.outgoing4.s_addr=addr; } else - MainForm->bbs_startup.telnet_interface=0; + MainForm->bbs_startup.outgoing4.s_addr=0; SAFECOPY(str,RLoginInterfaceEdit->Text.c_str()); p=str; @@ -166,9 +166,9 @@ void __fastcall TTelnetCfgDlg::OKBtnClick(TObject *Sender) while(*p && *p!='.') p++; if(*p=='.') p++; addr|=atoi(p); - MainForm->bbs_startup.rlogin_interface=addr; + MainForm->bbs_startup.outgoing4.s_addr=addr; } else - MainForm->bbs_startup.rlogin_interface=0; + MainForm->bbs_startup.outgoing4.s_addr=0; SAFECOPY(str,SshInterfaceEdit->Text.c_str()); p=str; @@ -184,9 +184,9 @@ void __fastcall TTelnetCfgDlg::OKBtnClick(TObject *Sender) while(*p && *p!='.') p++; if(*p=='.') p++; addr|=atoi(p); - MainForm->bbs_startup.ssh_interface=addr; + MainForm->bbs_startup.outgoing4.s_addr=addr; } else - MainForm->bbs_startup.ssh_interface=0; + MainForm->bbs_startup.outgoing4.s_addr=0; MainForm->bbs_startup.telnet_port=TelnetPortEdit->Text.ToIntDef(23); MainForm->bbs_startup.rlogin_port=RLoginPortEdit->Text.ToIntDef(513); diff --git a/src/sbbs3/ctrl/WebCfgDlgUnit.cpp b/src/sbbs3/ctrl/WebCfgDlgUnit.cpp index 6524d29b451a9d1d3381b121ecd4e814d9c1f44d..3578358d30e4e647a9f9b5a7458547b511c1fd5b 100644 --- a/src/sbbs3/ctrl/WebCfgDlgUnit.cpp +++ b/src/sbbs3/ctrl/WebCfgDlgUnit.cpp @@ -58,14 +58,14 @@ void __fastcall TWebCfgDlg::FormShow(TObject *Sender) char str[128]; char** p; - if(MainForm->web_startup.interface_addr==0) + if(MainForm->web_startup.outgoing4.s_addr==0) NetworkInterfaceEdit->Text="<ANY>"; else { sprintf(str,"%d.%d.%d.%d" - ,(MainForm->web_startup.interface_addr>>24)&0xff - ,(MainForm->web_startup.interface_addr>>16)&0xff - ,(MainForm->web_startup.interface_addr>>8)&0xff - ,MainForm->web_startup.interface_addr&0xff + ,(MainForm->web_startup.outgoing4.s_addr>>24)&0xff + ,(MainForm->web_startup.outgoing4.s_addr>>16)&0xff + ,(MainForm->web_startup.outgoing4.s_addr>>8)&0xff + ,MainForm->web_startup.outgoing4.s_addr&0xff ); NetworkInterfaceEdit->Text=AnsiString(str); } @@ -140,10 +140,10 @@ void __fastcall TWebCfgDlg::OKBtnClick(TObject *Sender) while(*p && *p!='.') p++; if(*p=='.') p++; addr|=atoi(p); - MainForm->web_startup.interface_addr=addr; + MainForm->web_startup.outgoing4.s_addr=addr; } else - MainForm->web_startup.interface_addr=0; - MainForm->web_startup.max_clients=MaxClientsEdit->Text.ToIntDef(0); + MainForm->web_startup.outgoing4.s_addr=0; + MainForm->web_startup.max_clients=MaxClientsEdit->Text.ToIntDef(10); MainForm->web_startup.max_inactivity=MaxInactivityEdit->Text.ToIntDef(WEB_DEFAULT_MAX_INACTIVITY); MainForm->web_startup.port=PortEdit->Text.ToIntDef(IPPORT_HTTP); MainForm->WebAutoStart=AutoStartCheckBox->Checked; diff --git a/src/sbbs3/exec.cpp b/src/sbbs3/exec.cpp index 8509760379cfb417377a2e1ae4589ddca5f75f99..c425f2d4a9b0c9001628ccd733fec3a568faee6c 100644 --- a/src/sbbs3/exec.cpp +++ b/src/sbbs3/exec.cpp @@ -1658,6 +1658,12 @@ int sbbs_t::exec(csi_t *csi) ,useron.phone); csi->logic=LOGIC_TRUE; break; + case USER_STRING_IPADDR: + sprintf(useron.ipaddr,"%.*s",LEN_IPADDR,csi->str); + putuserrec(&cfg,useron.number,U_IPADDR,LEN_IPADDR + ,useron.phone); + csi->logic=LOGIC_TRUE; + break; case USER_STRING_COMMENT: sprintf(useron.comment,"%.*s",LEN_COMMENT,csi->str); putuserrec(&cfg,useron.number,U_COMMENT,LEN_COMMENT diff --git a/src/sbbs3/execnet.cpp b/src/sbbs3/execnet.cpp index 0c19b3b2b7d9d88d670e46cd5fe8d6564782391c..4e868e58549a73dce2021a63924dad0d6d767be3 100644 --- a/src/sbbs3/execnet.cpp +++ b/src/sbbs3/execnet.cpp @@ -42,6 +42,7 @@ #define TIMEOUT_SOCK_LISTEN 30 /* seconds */ #define TIMEOUT_FTP_RESPONSE 300 /* seconds */ +/* TODO: IPv6 */ int sbbs_t::exec_net(csi_t* csi) { char str[512],rsp[512],buf[1025],ch,*p,**pp,**pp1,**pp2; @@ -67,7 +68,7 @@ int sbbs_t::exec_net(csi_t* csi) SOCKADDR_IN addr; memset(&addr,0,sizeof(addr)); - addr.sin_addr.s_addr = htonl(startup->telnet_interface); + addr.sin_addr.s_addr = htonl(startup->outgoing4.s_addr); addr.sin_family = AF_INET; if((i=bind(sock, (struct sockaddr *) &addr, sizeof (addr)))!=0) { @@ -550,10 +551,10 @@ SOCKET sbbs_t::ftp_data_sock(csi_t* csi, SOCKET ctrl_sock, SOCKADDR_IN* addr) } memset(addr,0,sizeof(SOCKADDR_IN)); - addr->sin_addr.s_addr = htonl(startup->telnet_interface); + addr->sin_addr.s_addr = htonl(startup->outgoing4.s_addr); addr->sin_family = AF_INET; - if(bind(data_sock, (struct sockaddr *)addr,sizeof(SOCKADDR_IN))!= 0) { + if(bind(data_sock, (struct sockaddr *)addr,xp_sockaddr_len(addr))!= 0) { csi->socket_error=ERROR_VALUE; close_socket(data_sock); return(INVALID_SOCKET); @@ -645,24 +646,25 @@ bool sbbs_t::ftp_get(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest, bool d BOOL data_avail; ulong total=0; SOCKET data_sock; - SOCKADDR_IN addr; + union xp_sockaddr addr; socklen_t addr_len; FILE* fp=NULL; struct timeval tv; fd_set socket_set; - if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr))==INVALID_SOCKET) + if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr.in))==INVALID_SOCKET) return(false); if(csi->ftp_mode&CS_FTP_PASV) { #if 0 // Debug bprintf("Connecting to %s:%hd\r\n" - ,inet_ntoa(addr.sin_addr) - ,ntohs(addr.sin_port)); + ,inet_ntoa(addr.in.sin_addr) + ,ntohs(addr.in.sin_port)); #endif - if(connect(data_sock,(struct sockaddr *)&addr,sizeof(addr))!=0) { + /* TODO: IPv6 */ + if(connect(data_sock,&addr.addr,sizeof(SOCKADDR_IN))!=0) { csi->socket_error=ERROR_VALUE; close_socket(data_sock); return(false); @@ -699,7 +701,7 @@ bool sbbs_t::ftp_get(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest, bool d SOCKET accept_sock; addr_len=sizeof(addr); - if((accept_sock=accept_socket(data_sock,(struct sockaddr*)&addr,&addr_len)) + if((accept_sock=accept_socket(data_sock,&addr,&addr_len)) ==INVALID_SOCKET) { csi->socket_error=ERROR_VALUE; closesocket(data_sock); @@ -770,7 +772,7 @@ bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest) int result; ulong total=0; SOCKET data_sock; - SOCKADDR_IN addr; + union xp_sockaddr addr; socklen_t addr_len; FILE* fp=NULL; bool error=false; @@ -782,7 +784,7 @@ bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest) if(!fexistcase(path)) return(false); - if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr))==INVALID_SOCKET) { + if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr.in))==INVALID_SOCKET) { bprintf("ftp: failure, line %d",__LINE__); return(false); } @@ -791,11 +793,11 @@ bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest) #if 0 // Debug bprintf("Connecting to %s:%hd\r\n" - ,inet_ntoa(addr.sin_addr) - ,ntohs(addr.sin_port)); + ,inet_ntoa(addr.in.sin_addr) + ,ntohs(addr.in.sin_port)); #endif - if(connect(data_sock,(struct sockaddr *)&addr,sizeof(addr))!=0) { + if(connect(data_sock,&addr.addr,sizeof(addr.in))!=0) { bprintf("ftp: failure, line %d",__LINE__); csi->socket_error=ERROR_VALUE; close_socket(data_sock); @@ -837,7 +839,7 @@ bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest) SOCKET accept_sock; addr_len=sizeof(addr); - if((accept_sock=accept_socket(data_sock,(struct sockaddr*)&addr,&addr_len)) + if((accept_sock=accept_socket(data_sock,&addr,&addr_len)) ==INVALID_SOCKET) { csi->socket_error=ERROR_VALUE; closesocket(data_sock); diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c index 1e553969838a4fe0d738e6318ceb140a940b9a12..bee3410b0b683b117702dd98fe60dd8e53cc020e 100644 --- a/src/sbbs3/ftpsrvr.c +++ b/src/sbbs3/ftpsrvr.c @@ -53,6 +53,7 @@ #include "telnet.h" #include "js_rtpool.h" #include "js_request.h" +#include "multisock.h" /* Constants */ @@ -78,7 +79,7 @@ static ftp_startup_t* startup=NULL; static scfg_t scfg; -static SOCKET server_socket=INVALID_SOCKET; +static struct xpms_set *ftp_set = NULL; static protected_uint32_t active_clients; static protected_uint32_t thread_count; static volatile time_t uptime=0; @@ -106,7 +107,8 @@ char* genvpath(int lib, int dir, char* str); typedef struct { SOCKET socket; - SOCKADDR_IN client_addr; + union xp_sockaddr client_addr; + socklen_t client_addr_len; } ftp_t; @@ -220,18 +222,29 @@ static int32_t thread_down(void) return count; } -static SOCKET ftp_open_socket(int type) +static void ftp_open_socket_cb(SOCKET sock, void *cbdata) { - SOCKET sock; char error[256]; - sock=socket(AF_INET, type, IPPROTO_IP); - if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) + if(startup!=NULL && startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - if(sock!=INVALID_SOCKET) { - if(set_socket_options(&scfg, sock, "FTP", error, sizeof(error))) - lprintf(LOG_ERR,"%04d !ERROR %s",sock, error); - } + if(set_socket_options(&scfg, sock, "FTP", error, sizeof(error))) + lprintf(LOG_ERR,"%04d !ERROR %s",sock, error); +} + +static void ftp_close_socket_cb(SOCKET sock, void *cbdata) +{ + if(startup!=NULL && startup->socket_open!=NULL) + startup->socket_open(startup->cbdata,FALSE); +} + +static SOCKET ftp_open_socket(int domain, int type) +{ + SOCKET sock; + + sock=socket(domain, type, IPPROTO_IP); + if(sock != INVALID_SOCKET) + ftp_open_socket_cb(sock, NULL); return(sock); } @@ -1127,7 +1140,7 @@ int sockreadline(SOCKET socket, char* buf, int len, time_t* lastactive) i=select(socket+1,&socket_set,NULL,NULL,&tv); - if(server_socket==INVALID_SOCKET || terminate_server) { + if(ftp_set==NULL || terminate_server) { sockprintf(socket,"421 Server downed, aborting."); lprintf(LOG_WARNING,"%04d Server downed, aborting",socket); return(0); @@ -1171,7 +1184,7 @@ int sockreadline(SOCKET socket, char* buf, int len, time_t* lastactive) void DLLCALL ftp_terminate(void) { - lprintf(LOG_INFO,"%04d FTP Server terminate",server_socket); + lprintf(LOG_INFO,"FTP Server terminate"); terminate_server=TRUE; } @@ -1209,6 +1222,7 @@ static void send_thread(void* arg) char str[128]; char tmp[128]; char username[128]; + char host_ip[INET6_ADDRSTRLEN]; int i; int rd; int wr; @@ -1227,7 +1241,7 @@ static void send_thread(void* arg) time_t start; time_t last_report; user_t uploader; - SOCKADDR_IN addr; + union xp_sockaddr addr; socklen_t addr_len; fd_set socket_set; struct timeval tv; @@ -1287,7 +1301,7 @@ static void send_thread(void* arg) error=TRUE; break; } - if(server_socket==INVALID_SOCKET || terminate_server) { + if(ftp_set==NULL || terminate_server) { lprintf(LOG_WARNING,"%04d !DATA Transfer locally aborted",xfer.ctrl_sock); sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted."); error=TRUE; @@ -1422,8 +1436,9 @@ static void send_thread(void* arg) if(!(scfg.dir[f.dir]->misc&DIR_QUIET)) { addr_len = sizeof(addr); if(uploader.level>=SYSOP_LEVEL - && getpeername(xfer.ctrl_sock,(struct sockaddr *)&addr,&addr_len)==0) - SAFEPRINTF2(username,"%s [%s]",xfer.user->alias,inet_ntoa(addr.sin_addr)); + && getpeername(xfer.ctrl_sock,&addr.addr,&addr_len)==0 + && inet_addrtop(&addr, host_ip, sizeof(host_ip))!=NULL) + SAFEPRINTF2(username,"%s [%s]",xfer.user->alias,host_ip); else SAFECOPY(username,xfer.user->alias); /* Inform uploader of downloaded file */ @@ -1447,7 +1462,7 @@ static void send_thread(void* arg) } fclose(fp); - if(server_socket!=INVALID_SOCKET && !terminate_server) + if(ftp_set!=NULL && !terminate_server) *xfer.inprogress=FALSE; if(xfer.tmpfile) { if(!(startup->options&FTP_OPT_KEEP_TEMP_FILES)) @@ -1547,7 +1562,7 @@ static void receive_thread(void* arg) error=TRUE; break; } - if(server_socket==INVALID_SOCKET || terminate_server) { + if(ftp_set==NULL || terminate_server) { lprintf(LOG_WARNING,"%04d !DATA Transfer locally aborted",xfer.ctrl_sock); /* Send NAK */ sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted."); @@ -1751,7 +1766,7 @@ static void receive_thread(void* arg) sockprintf(xfer.ctrl_sock,"226 Upload complete (%lu cps).",cps); } - if(server_socket!=INVALID_SOCKET && !terminate_server) + if(ftp_set!=NULL && !terminate_server) *xfer.inprogress=FALSE; thread_down(); @@ -1759,7 +1774,7 @@ static void receive_thread(void* arg) -static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCKET* data_sock +static void filexfer(union xp_sockaddr* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCKET* data_sock ,char* filename, long filepos, BOOL* inprogress, BOOL* aborted ,BOOL delfile, BOOL tmpfile ,time_t* lastactive @@ -1774,11 +1789,12 @@ static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCK int result; ulong l; socklen_t addr_len; - SOCKADDR_IN server_addr; + union xp_sockaddr server_addr; BOOL reuseaddr; xfer_t* xfer; struct timeval tv; fd_set socket_set; + char host_ip[INET6_ADDRSTRLEN]; if((*inprogress)==TRUE) { lprintf(LOG_WARNING,"%04d !TRANSFER already in progress",ctrl_sock); @@ -1792,9 +1808,10 @@ static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCK if(*data_sock!=INVALID_SOCKET) ftp_close_socket(data_sock,__LINE__); + inet_addrtop(addr, host_ip, sizeof(host_ip)); if(pasv_sock==INVALID_SOCKET) { /* !PASV */ - if((*data_sock=socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == INVALID_SOCKET) { + if((*data_sock=socket(addr->addr.sa_family, SOCK_STREAM, IPPROTO_IP)) == INVALID_SOCKET) { lprintf(LOG_ERR,"%04d !DATA ERROR %d opening socket", ctrl_sock, ERROR_VALUE); sockprintf(ctrl_sock,"425 Error %d opening socket",ERROR_VALUE); if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) @@ -1811,16 +1828,19 @@ static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCK reuseaddr=TRUE; setsockopt(*data_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&reuseaddr,sizeof(reuseaddr)); - memset(&server_addr, 0, sizeof(server_addr)); + addr_len = sizeof(server_addr); + if((result=getsockname(ctrl_sock, &server_addr.addr,&addr_len))!=0) { + lprintf(LOG_ERR,"%04d !ERROR %d (%d) getting address/port of command socket (%u)" + ,ctrl_sock,result,ERROR_VALUE,pasv_sock); + return; + } - server_addr.sin_addr.s_addr = htonl(startup->interface_addr); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons((WORD)(startup->port-1)); /* 20? */ + inet_setaddrport(&server_addr, inet_addrport(&server_addr)-1); /* 20? */ - result=bind(*data_sock, (struct sockaddr *) &server_addr,sizeof(server_addr)); + result=bind(*data_sock, &server_addr.addr,addr_len); if(result!=0) { - server_addr.sin_port = 0; /* any user port */ - result=bind(*data_sock, (struct sockaddr *) &server_addr,sizeof(server_addr)); + inet_setaddrport(&server_addr, 0); /* any user port */ + result=bind(*data_sock, &server_addr.addr,addr_len); } if(result!=0) { lprintf(LOG_ERR,"%04d !DATA ERROR %d (%d) binding socket %d" @@ -1833,11 +1853,11 @@ static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCK return; } - result=connect(*data_sock, (struct sockaddr *)addr,sizeof(struct sockaddr)); + result=connect(*data_sock, &addr->addr,xp_sockaddr_len(addr)); if(result!=0) { lprintf(LOG_WARNING,"%04d !DATA ERROR %d (%d) connecting to client %s port %u on socket %d" ,ctrl_sock,result,ERROR_VALUE - ,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port),*data_sock); + ,host_ip,inet_addrport(addr),*data_sock); sockprintf(ctrl_sock,"425 Error %d connecting to socket",ERROR_VALUE); if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) ftp_remove(ctrl_sock, __LINE__, filename); @@ -1847,18 +1867,18 @@ static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCK } if(startup->options&FTP_OPT_DEBUG_DATA) lprintf(LOG_DEBUG,"%04d DATA socket %d connected to %s port %u" - ,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port)); + ,ctrl_sock,*data_sock,host_ip,inet_addrport(addr)); } else { /* PASV */ if(startup->options&FTP_OPT_DEBUG_DATA) { - addr_len=sizeof(SOCKADDR_IN); - if((result=getsockname(pasv_sock, (struct sockaddr *)addr,&addr_len))!=0) + addr_len=sizeof(addr); + if((result=getsockname(pasv_sock, &addr->addr,&addr_len))!=0) lprintf(LOG_ERR,"%04d !ERROR %d (%d) getting address/port of passive socket (%u)" ,ctrl_sock,result,ERROR_VALUE,pasv_sock); else lprintf(LOG_DEBUG,"%04d PASV DATA socket %d listening on %s port %u" - ,ctrl_sock,pasv_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port)); + ,ctrl_sock,pasv_sock,host_ip,inet_addrport(addr)); } /* Setup for select() */ @@ -1884,11 +1904,11 @@ static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCK return; } - addr_len=sizeof(SOCKADDR_IN); + addr_len=sizeof(addr); #ifdef SOCKET_DEBUG_ACCEPT socket_debug[ctrl_sock]|=SOCKET_DEBUG_ACCEPT; #endif - *data_sock=accept(pasv_sock,(struct sockaddr*)addr,&addr_len); + *data_sock=accept(pasv_sock,&addr->addr,&addr_len); #ifdef SOCKET_DEBUG_ACCEPT socket_debug[ctrl_sock]&=~SOCKET_DEBUG_ACCEPT; #endif @@ -1905,7 +1925,7 @@ static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCK startup->socket_open(startup->cbdata,TRUE); if(startup->options&FTP_OPT_DEBUG_DATA) lprintf(LOG_DEBUG,"%04d PASV DATA socket %d connected to %s port %u" - ,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port)); + ,ctrl_sock,*data_sock,host_ip,inet_addrport(addr)); } do { @@ -2200,7 +2220,7 @@ void ftp_printfile(SOCKET sock, const char* name, unsigned code) } } -static BOOL ftp_hacklog(char* prot, char* user, char* text, char* host, SOCKADDR_IN* addr) +static BOOL ftp_hacklog(char* prot, char* user, char* text, char* host, union xp_sockaddr* addr) { #ifdef _WIN32 if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE)) @@ -2214,17 +2234,20 @@ static BOOL ftp_hacklog(char* prot, char* user, char* text, char* host, SOCKADDR /* Consecutive failed login (possible password hack) attempt tracking */ /****************************************************************************/ -static BOOL badlogin(SOCKET sock, ulong* login_attempts, char* user, char* passwd, char* host, SOCKADDR_IN* addr) +static BOOL badlogin(SOCKET sock, ulong* login_attempts, char* user, char* passwd, char* host, union xp_sockaddr* addr) { ulong count; + char host_ip[INET6_ADDRSTRLEN]; if(addr!=NULL) { count=loginFailure(startup->login_attempt_list, addr, "FTP", user, passwd); if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold) ftp_hacklog("FTP LOGIN", user, passwd, host, addr); - if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) + if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) { + inet_addrtop(addr, host_ip, sizeof(host_ip)); filter_ip(&scfg, "FTP", "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS" - ,host, inet_ntoa(addr->sin_addr), user, /* fname: */NULL); + ,host, host_ip, user, /* fname: */NULL); + } if(count > *login_attempts) *login_attempts=count; } else @@ -2266,8 +2289,10 @@ static void ctrl_thread(void* arg) char aliasline[512]; char desc[501]=""; char sys_pass[128]; - char* host_name; - char host_ip[64]; + char host_name[256]; + char host_ip[INET6_ADDRSTRLEN]; + char data_ip[INET6_ADDRSTRLEN]; + uint16_t data_port; char path[MAX_PATH+1]; char local_dir[MAX_PATH+1]; char ren_from[MAX_PATH+1]=""; @@ -2315,9 +2340,9 @@ static void ctrl_thread(void* arg) SOCKET pasv_sock=INVALID_SOCKET; SOCKET data_sock=INVALID_SOCKET; HOSTENT* host; - SOCKADDR_IN addr; - SOCKADDR_IN data_addr; - SOCKADDR_IN pasv_addr; + union xp_sockaddr addr; + union xp_sockaddr data_addr; + union xp_sockaddr pasv_addr; ftp_t ftp=*(ftp_t*)arg; user_t user; time_t t; @@ -2347,10 +2372,9 @@ static void ctrl_thread(void* arg) lastactive=time(NULL); sock=ftp.socket; - data_addr=ftp.client_addr; + memcpy(&data_addr, &ftp.client_addr, ftp.client_addr_len); /* Default data port is ctrl port-1 */ - data_addr.sin_port=ntohs(data_addr.sin_port)-1; - data_addr.sin_port=htons(data_addr.sin_port); + data_port = inet_addrport(&data_addr)-1; lprintf(LOG_DEBUG,"%04d CTRL thread started", sock); @@ -2378,21 +2402,17 @@ static void ctrl_thread(void* arg) memset(&user,0,sizeof(user)); - SAFECOPY(host_ip,inet_ntoa(ftp.client_addr.sin_addr)); + inet_addrtop(&ftp.client_addr, host_ip, sizeof(host_ip)); lprintf(LOG_INFO,"%04d CTRL connection accepted from: %s port %u" - ,sock, host_ip, ntohs(ftp.client_addr.sin_port)); + ,sock, host_ip, inet_addrport(&ftp.client_addr)); if(startup->options&FTP_OPT_NO_HOST_LOOKUP) - host=NULL; - else - host=gethostbyaddr ((char *)&ftp.client_addr.sin_addr - ,sizeof(ftp.client_addr.sin_addr),AF_INET); - - if(host!=NULL && host->h_name!=NULL) - host_name=host->h_name; - else - host_name="<no name>"; + strcpy(host_name,"<no name>"); + else { + if(getnameinfo(&ftp.client_addr.addr, sizeof(ftp.client_addr), host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD)!=0) + strcpy(host_name,"<no name>"); + } if(!(startup->options&FTP_OPT_NO_HOST_LOOKUP)) lprintf(LOG_INFO,"%04d Hostname: %s", sock, host_name); @@ -2415,7 +2435,7 @@ static void ctrl_thread(void* arg) /* For PASV mode */ addr_len=sizeof(pasv_addr); - if((result=getsockname(sock, (struct sockaddr *)&pasv_addr,&addr_len))!=0) { + if((result=getsockname(sock, &pasv_addr.addr,&addr_len))!=0) { lprintf(LOG_ERR,"%04d !ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE); sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE); ftp_close_socket(&sock,__LINE__); @@ -2431,7 +2451,7 @@ static void ctrl_thread(void* arg) client.time=time32(NULL); SAFECOPY(client.addr,host_ip); SAFECOPY(client.host,host_name); - client.port=ntohs(ftp.client_addr.sin_port); + client.port=inet_addrport(&ftp.client_addr); client.protocol="FTP"; client.user="<unknown>"; client_on(sock,&client,FALSE /* update */); @@ -2439,7 +2459,7 @@ static void ctrl_thread(void* arg) if(startup->login_attempt_throttle && (login_attempts=loginAttempts(startup->login_attempt_list, &ftp.client_addr)) > 1) { lprintf(LOG_DEBUG,"%04d Throttling suspicious connection from: %s (%u login attempts)" - ,sock, inet_ntoa(ftp.client_addr.sin_addr), login_attempts); + ,sock, host_ip, login_attempts); mswait(login_attempts*startup->login_attempt_throttle); } @@ -2690,7 +2710,7 @@ static void ctrl_thread(void* arg) user.ltoday++; SAFECOPY(user.modem,"FTP"); SAFECOPY(user.comp,host_name); - SAFECOPY(user.note,host_ip); + SAFECOPY(user.ipaddr,host_ip); user.logontime=logintime; putuserdat(&scfg, &user); @@ -2788,7 +2808,7 @@ static void ctrl_thread(void* arg) } #endif - if(strnicmp(cmd, "PORT ",5)==0 || strnicmp(cmd, "EPRT ",5)==0) { + if(strnicmp(cmd, "PORT ",5)==0 || strnicmp(cmd, "EPRT ",5)==0 || strnicmp(cmd, "LPRT ",5)==0) { if(pasv_sock!=INVALID_SOCKET) ftp_close_socket(&pasv_sock,__LINE__); @@ -2797,47 +2817,154 @@ static void ctrl_thread(void* arg) SKIP_WHITESPACE(p); if(strnicmp(cmd, "PORT ",5)==0) { sscanf(p,"%u,%u,%u,%u,%hd,%hd",&h1,&h2,&h3,&h4,&p1,&p2); - data_addr.sin_addr.s_addr=htonl((h1<<24)|(h2<<16)|(h3<<8)|h4); - data_addr.sin_port=(u_short)((p1<<8)|p2); - } else { /* EPRT */ + data_addr.in.sin_family=AF_INET; + data_addr.in.sin_addr.s_addr=htonl((h1<<24)|(h2<<16)|(h3<<8)|h4); + data_port = (p2<<8)|p1; + } else if(strnicmp(cmd, "EPRT ", 5)==0) { /* EPRT */ char delim = *p; int prot; + char addr_str[INET6_ADDRSTRLEN]; - if(*p) p++; + memset(&data_addr, 0, sizeof(data_addr)); + if(*p) + p++; prot=strtol(p,NULL,/* base: */10); - if(prot!=1) { - lprintf(LOG_WARNING,"%04d UNSUPPORTED protocol: %d", sock, prot); - sockprintf(sock,"522 Network protocol not supported, use (1)"); + switch(prot) { + case 1: + FIND_CHAR(p,delim); + if(*p) + p++; + data_addr.in.sin_addr.s_addr=inet_addr(p); + FIND_CHAR(p,delim); + if(*p) + p++; + data_port=atoi(p); + data_addr.in.sin_family=AF_INET; + break; + case 2: + FIND_CHAR(p,delim); + if(*p) + p++; + strncpy(addr_str, p, sizeof(addr_str)); + addr_str[sizeof(addr_str)-1]=0; + tp=addr_str; + FIND_CHAR(tp, delim); + *tp=0; + if(inet_ptoaddr(addr_str, &data_addr, sizeof(data_addr))==NULL) { + lprintf(LOG_WARNING,"%04d Unable to parse IPv6 address %s",sock,addr_str); + sockprintf(sock,"522 Unable to parse IPv6 address (1)"); + continue; + } + FIND_CHAR(p,delim); + if(*p) + p++; + data_port=atoi(p); + data_addr.in6.sin6_family=AF_INET6; + break; + default: + lprintf(LOG_WARNING,"%04d UNSUPPORTED protocol: %d", sock, prot); + sockprintf(sock,"522 Network protocol not supported, use (1)"); + continue; + } + } + else { /* LPRT */ + if(sscanf(p,"%u,%u",&h1, &h2)!=2) { + lprintf(LOG_ERR, "Unable to parse LPRT %s", p); + sockprintf(sock, "521 Address family not supported"); continue; } - FIND_CHAR(p,delim); - if(*p) p++; - data_addr.sin_addr.s_addr=inet_addr(p); - FIND_CHAR(p,delim); - if(*p) p++; - data_addr.sin_port=atoi(p); + FIND_CHAR(p,','); + if(*p) + p++; + FIND_CHAR(p,','); + if(*p) + p++; + switch(h1) { + case 4: /* IPv4 */ + if(h2 != 4) { + lprintf(LOG_ERR, "Unable to parse LPRT %s", p); + sockprintf(sock, "501 IPv4 Address is the wrong length"); + continue; + } + for(h1 = 0; h1 < h2; h1++) { + ((unsigned char *)(&data_addr.in.sin_addr))[h1]=atoi(p); + FIND_CHAR(p,','); + if(*p) + p++; + } + if(atoi(p)!=2) { + lprintf(LOG_ERR, "Unable to parse LPRT %s", p); + sockprintf(sock, "501 IPv4 Port is the wrong length"); + continue; + } + FIND_CHAR(p,','); + if(*p) + p++; + for(h1 = 0; h1 < 2; h1++) { + ((unsigned char *)(&data_port))[1-h1]=atoi(p); + FIND_CHAR(p,','); + if(*p) + p++; + } + data_addr.in.sin_family=AF_INET; + break; + case 6: /* IPv6 */ + if(h2 != 16) { + lprintf(LOG_ERR, "Unable to parse LPRT %s", p); + sockprintf(sock, "501 IPv6 Address is the wrong length"); + continue; + } + for(h1 = 0; h1 < h2; h1++) { + ((unsigned char *)(&data_addr.in6.sin6_addr))[h1]=atoi(p); + FIND_CHAR(p,','); + if(*p) + p++; + } + if(atoi(p)!=2) { + lprintf(LOG_ERR, "Unable to parse LPRT %s", p); + sockprintf(sock, "501 IPv6 Port is the wrong length"); + continue; + } + FIND_CHAR(p,','); + if(*p) + p++; + for(h1 = 0; h1 < 2; h1++) { + ((unsigned char *)(&data_port))[1-h1]=atoi(p); + FIND_CHAR(p,','); + if(*p) + p++; + } + data_addr.in6.sin6_family=AF_INET6; + break; + default: + lprintf(LOG_ERR, "Unable to parse LPRT %s", p); + sockprintf(sock, "521 Address family not supported"); + continue; + } } - if(data_addr.sin_port< IPPORT_RESERVED) { + + inet_addrtop(&data_addr, data_ip, sizeof(data_ip)); + if(data_port< IPPORT_RESERVED) { lprintf(LOG_WARNING,"%04d !SUSPECTED BOUNCE ATTACK ATTEMPT by %s to %s port %u" ,sock,user.alias - ,inet_ntoa(data_addr.sin_addr),data_addr.sin_port); + ,data_ip,data_port); ftp_hacklog("FTP BOUNCE", user.alias, cmd, host_name, &ftp.client_addr); sockprintf(sock,"504 Bad port number."); continue; /* As recommended by RFC2577 */ } - data_addr.sin_port=htons(data_addr.sin_port); + inet_setaddrport(&data_addr, data_port); sockprintf(sock,"200 PORT Command successful."); mode="active"; continue; } if(stricmp(cmd, "PASV")==0 || stricmp(cmd, "P@SW")==0 /* Kludge required for SMC Barricade V1.2 */ - || stricmp(cmd, "EPSV")==0) { + || stricmp(cmd, "EPSV")==0 || stricmp(cmd, "LPSV")==0) { if(pasv_sock!=INVALID_SOCKET) ftp_close_socket(&pasv_sock,__LINE__); - if((pasv_sock=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) { + if((pasv_sock=ftp_open_socket(pasv_addr.addr.sa_family, SOCK_STREAM))==INVALID_SOCKET) { lprintf(LOG_WARNING,"%04d !PASV ERROR %d opening socket", sock,ERROR_VALUE); sockprintf(sock,"425 Error %d opening PASV data socket", ERROR_VALUE); continue; @@ -2860,9 +2987,9 @@ static void ctrl_thread(void* arg) lprintf(LOG_DEBUG,"%04d PASV DATA trying to bind socket to port %u" ,sock,port); - pasv_addr.sin_port = htons(port); + inet_setaddrport(&pasv_addr, port); - if((result=bind(pasv_sock, (struct sockaddr *) &pasv_addr,sizeof(pasv_addr)))==0) + if((result=bind(pasv_sock, &pasv_addr.addr,xp_sockaddr_len(&pasv_addr)))==0) break; if(port==startup->pasv_port_high) break; @@ -2878,7 +3005,7 @@ static void ctrl_thread(void* arg) lprintf(LOG_DEBUG,"%04d PASV DATA socket %d bound to port %u",sock,pasv_sock,port); addr_len=sizeof(addr); - if((result=getsockname(pasv_sock, (struct sockaddr *)&addr,&addr_len))!=0) { + if((result=getsockname(pasv_sock, &addr.addr,&addr_len))!=0) { lprintf(LOG_ERR,"%04d !PASV ERROR %d (%d) getting address/port" ,sock, result, ERROR_VALUE); sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE); @@ -2894,26 +3021,61 @@ static void ctrl_thread(void* arg) continue; } - /* Choose IP address to use in passive response */ - ip_addr=0; - if(startup->options&FTP_OPT_LOOKUP_PASV_IP - && (host=gethostbyname(startup->host_name))!=NULL) - ip_addr=ntohl(*((ulong*)host->h_addr_list[0])); - if(ip_addr==0 && (ip_addr=startup->pasv_ip_addr)==0) - ip_addr=ntohl(pasv_addr.sin_addr.s_addr); - - if(startup->options&FTP_OPT_DEBUG_DATA) - lprintf(LOG_INFO,"%04d PASV DATA IP address in response: %u.%u.%u.%u (subject to NAT)" - ,sock - ,(ip_addr>>24)&0xff - ,(ip_addr>>16)&0xff - ,(ip_addr>>8)&0xff - ,ip_addr&0xff - ); - port=ntohs(addr.sin_port); + port=inet_addrport(&addr); if(stricmp(cmd, "EPSV")==0) sockprintf(sock,"229 Entering Extended Passive Mode (|||%hu|)", port); - else + else if (stricmp(cmd,"LPSV")==0) { + switch(addr.addr.sa_family) { + case AF_INET: + sockprintf(sock, "228 Entering Long Passive Mode (4, 4, %d, %d, %d, %d, 2, %d, %d)" + ,((unsigned char *)&(addr.in.sin_addr))[0] + ,((unsigned char *)&(addr.in.sin_addr))[1] + ,((unsigned char *)&(addr.in.sin_addr))[2] + ,((unsigned char *)&(addr.in.sin_addr))[3] + ,((unsigned char *)&(addr.in.sin_port))[0] + ,((unsigned char *)&(addr.in.sin_port))[1]); + break; + case AF_INET6: + sockprintf(sock, "228 Entering Long Passive Mode (6, 16, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, 2, %d, %d)" + ,((unsigned char *)&(addr.in6.sin6_addr))[0] + ,((unsigned char *)&(addr.in6.sin6_addr))[1] + ,((unsigned char *)&(addr.in6.sin6_addr))[2] + ,((unsigned char *)&(addr.in6.sin6_addr))[3] + ,((unsigned char *)&(addr.in6.sin6_addr))[4] + ,((unsigned char *)&(addr.in6.sin6_addr))[5] + ,((unsigned char *)&(addr.in6.sin6_addr))[6] + ,((unsigned char *)&(addr.in6.sin6_addr))[7] + ,((unsigned char *)&(addr.in6.sin6_addr))[8] + ,((unsigned char *)&(addr.in6.sin6_addr))[9] + ,((unsigned char *)&(addr.in6.sin6_addr))[10] + ,((unsigned char *)&(addr.in6.sin6_addr))[11] + ,((unsigned char *)&(addr.in6.sin6_addr))[12] + ,((unsigned char *)&(addr.in6.sin6_addr))[13] + ,((unsigned char *)&(addr.in6.sin6_addr))[14] + ,((unsigned char *)&(addr.in6.sin6_addr))[15] + ,((unsigned char *)&(addr.in6.sin6_port))[0] + ,((unsigned char *)&(addr.in6.sin6_port))[1]); + break; + } + } + else { + /* Choose IP address to use in passive response */ + ip_addr=0; + /* TODO: IPv6 this here lookup */ + if(startup->options&FTP_OPT_LOOKUP_PASV_IP + && (host=gethostbyname(startup->host_name))!=NULL) + ip_addr=ntohl(*((ulong*)host->h_addr_list[0])); + if(ip_addr==0 && (ip_addr=startup->pasv_ip_addr.s_addr)==0) + ip_addr=ntohl(pasv_addr.in.sin_addr.s_addr); + + if(startup->options&FTP_OPT_DEBUG_DATA) + lprintf(LOG_INFO,"%04d PASV DATA IP address in response: %u.%u.%u.%u (subject to NAT)" + ,sock + ,(ip_addr>>24)&0xff + ,(ip_addr>>16)&0xff + ,(ip_addr>>8)&0xff + ,ip_addr&0xff + ); sockprintf(sock,"227 Entering Passive Mode (%u,%u,%u,%u,%hu,%hu)" ,(ip_addr>>24)&0xff ,(ip_addr>>16)&0xff @@ -2922,6 +3084,7 @@ static void ctrl_thread(void* arg) ,(port>>8)&0xff ,port&0xff ); + } mode="passive"; continue; } @@ -4422,7 +4585,7 @@ static void ctrl_thread(void* arg) lprintf(LOG_DEBUG,"%04d Waiting for transfer to complete...",sock); count=0; while(transfer_inprogress==TRUE) { - if(server_socket==INVALID_SOCKET || terminate_server) { + if(ftp_set==NULL || terminate_server) { mswait(2000); /* allow xfer threads to terminate */ break; } @@ -4531,8 +4694,10 @@ static void cleanup(int code, int line) semfile_list_free(&recycle_semfiles); semfile_list_free(&shutdown_semfiles); - if(server_socket!=INVALID_SOCKET) - ftp_close_socket(&server_socket,__LINE__); + if(ftp_set != NULL) { + xpms_destroy(ftp_set, ftp_close_socket_cb, NULL); + ftp_set = NULL; + } update_clients(); /* active_clients is destroyed below */ @@ -4584,18 +4749,16 @@ void DLLCALL ftp_server(void* arg) char error[256]; char compiler[32]; char str[256]; - SOCKADDR_IN server_addr; - SOCKADDR_IN client_addr; + union xp_sockaddr client_addr; socklen_t client_addr_len; SOCKET client_socket; int i; - int result; time_t t; time_t start; time_t initialized=0; - fd_set socket_set; ftp_t* ftp; - struct timeval tv; + struct in_addr iaddr; + char client_ip[INET6_ADDRSTRLEN]; ftp_ver(); @@ -4644,7 +4807,8 @@ void DLLCALL ftp_server(void* arg) js_server_props.version_detail=ftp_ver(); js_server_props.clients=&active_clients.value; js_server_props.options=&startup->options; - js_server_props.interface_addr=&startup->interface_addr; + /* TODO: IPv6 */ + js_server_props.interface_addr=&startup->outgoing4; #endif uptime=0; @@ -4759,48 +4923,21 @@ void DLLCALL ftp_server(void* arg) dotname(scfg.lib[i]->sname,scfg.lib[i]->sname); } /* open a socket and wait for a client */ - - if((server_socket=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE); - cleanup(1,__LINE__); - break; - } - - lprintf(LOG_DEBUG,"%04d FTP Server socket opened",server_socket); - - /*****************************/ - /* Listen for incoming calls */ - /*****************************/ - memset(&server_addr, 0, sizeof(server_addr)); - - server_addr.sin_addr.s_addr = htonl(startup->interface_addr); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(startup->port); - - if(startup->port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result=retry_bind(server_socket, (struct sockaddr *) &server_addr,sizeof(server_addr) - ,startup->bind_retry_count,startup->bind_retry_delay,"FTP Server",lprintf); - if(startup->port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); - } - if(result!=0) { - lprintf(LOG_CRIT,"%04d %s", server_socket, BIND_FAILURE_HELP); - cleanup(1,__LINE__); - break; + ftp_set = xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf); + + if(ftp_set == NULL) { + lprintf(LOG_CRIT,"!ERROR %d creating FTP socket set", ERROR_VALUE); + cleanup(1, __LINE__); + return; } + lprintf(LOG_DEBUG,"FTP Server socket set created"); - if((result=listen(server_socket, 1))!= 0) { - lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on socket" - ,server_socket, result, ERROR_VALUE); - cleanup(1,__LINE__); - break; - } + /* + * Add interfaces + */ + xpms_add_list(ftp_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->port, "FTP Server", ftp_open_socket_cb, startup->seteuid, NULL); - lprintf(LOG_INFO,"%04d FTP Server listening on port %u",server_socket,startup->port); + lprintf(LOG_INFO,"FTP Server listening"); status(STATUS_WFC); /* Setup recycle/shutdown semaphore file lists */ @@ -4817,9 +4954,9 @@ void DLLCALL ftp_server(void* arg) if(startup->started!=NULL) startup->started(startup->cbdata); - lprintf(LOG_INFO,"%04d FTP Server thread started",server_socket); + lprintf(LOG_INFO,"FTP Server thread started"); - while(server_socket!=INVALID_SOCKET && !terminate_server) { + while(ftp_set!=NULL && !terminate_server) { if(protected_uint32_value(thread_count) <= 1) { if(!(startup->options&FTP_OPT_NO_RECYCLE)) { @@ -4842,53 +4979,22 @@ void DLLCALL ftp_server(void* arg) break; } } - /* now wait for connection */ - - tv.tv_sec=startup->sem_chk_freq; - tv.tv_usec=0; - FD_ZERO(&socket_set); - FD_SET(server_socket,&socket_set); - - if((i=select(server_socket+1,&socket_set,NULL,NULL,&tv))<1) { - if(i==0) - continue; - if(ERROR_VALUE==EINTR) - lprintf(LOG_DEBUG,"%04d FTP Server listening interrupted", server_socket); - else if(ERROR_VALUE == ENOTSOCK) - lprintf(LOG_NOTICE,"%04d FTP Server socket closed", server_socket); - else - lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket",server_socket, ERROR_VALUE); - continue; - } - - if(server_socket==INVALID_SOCKET || terminate_server) /* terminated */ + if(ftp_set==NULL || terminate_server) /* terminated */ break; + /* now wait for connection */ client_addr_len = sizeof(client_addr); - client_socket = accept(server_socket, (struct sockaddr *)&client_addr - ,&client_addr_len); + client_socket = xpms_accept(ftp_set, &client_addr, &client_addr_len, startup->sem_chk_freq*1000, NULL); if(client_socket == INVALID_SOCKET) - { -#if 0 /* is this necessary still? */ - if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR || ERROR_VALUE == EINVAL) { - lprintf(LOG_NOTICE,"0000 FTP socket closed while listening"); - break; - } -#endif - lprintf(LOG_WARNING,"%04d !ERROR %d accepting connection" - ,server_socket, ERROR_VALUE); -#ifdef _WIN32 - if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */ - break; -#endif continue; - } + if(startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) { + inet_addrtop(&client_addr, client_ip, sizeof(client_ip)); + if(trashcan(&scfg,client_ip,"ip-silent")) { ftp_close_socket(&client_socket,__LINE__); continue; } @@ -4912,7 +5018,8 @@ void DLLCALL ftp_server(void* arg) } ftp->socket=client_socket; - ftp->client_addr=client_addr; + memcpy(&ftp->client_addr, &client_addr, client_addr_len); + ftp->client_addr_len = client_addr_len; protected_uint32_adjust(&thread_count,1); _beginthread(ctrl_thread, 0, ftp); @@ -4920,17 +5027,16 @@ void DLLCALL ftp_server(void* arg) } #if 0 /* def _DEBUG */ - lprintf(LOG_DEBUG,"0000 server_socket: %d",server_socket); lprintf(LOG_DEBUG,"0000 terminate_server: %d",terminate_server); #endif if(protected_uint32_value(active_clients)) { - lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..." - ,server_socket, protected_uint32_value(active_clients)); + lprintf(LOG_DEBUG,"Waiting for %d active clients to disconnect..." + , protected_uint32_value(active_clients)); start=time(NULL); while(protected_uint32_value(active_clients)) { if(time(NULL)-start>startup->max_inactivity) { - lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients" - ,server_socket, protected_uint32_value(active_clients)); + lprintf(LOG_WARNING,"!TIMEOUT waiting for %d active clients" + , protected_uint32_value(active_clients)); break; } mswait(100); diff --git a/src/sbbs3/ftpsrvr.h b/src/sbbs3/ftpsrvr.h index 1bd27cbb0dbad0aa5e2b4a97c62f1de93f756852..aea75ef72267f79016e51d59b122fb2cf8fa20cd 100644 --- a/src/sbbs3/ftpsrvr.h +++ b/src/sbbs3/ftpsrvr.h @@ -51,8 +51,11 @@ typedef struct { WORD qwk_timeout; #define FTP_DEFAULT_QWK_TIMEOUT 600 WORD sem_chk_freq; /* semaphore file checking frequency (in seconds) */ - DWORD interface_addr; - DWORD pasv_ip_addr; + struct in_addr outgoing4; + struct in6_addr outgoing6; + str_list_t interfaces; + struct in_addr pasv_ip_addr; + struct in6_addr pasv_ip6_addr; WORD pasv_port_low; WORD pasv_port_high; DWORD options; /* See FTP_OPT definitions */ @@ -109,7 +112,7 @@ typedef struct { #if defined(STARTUP_INIT_FIELD_TABLES) static struct init_field ftp_init_fields[] = { OFFSET_AND_SIZE(ftp_startup_t,port) - ,OFFSET_AND_SIZE(ftp_startup_t,interface_addr) + ,OFFSET_AND_SIZE(ftp_startup_t,interfaces) ,OFFSET_AND_SIZE(ftp_startup_t,ctrl_dir) ,OFFSET_AND_SIZE(ftp_startup_t,temp_dir) ,{ 0,0 } /* terminator */ diff --git a/src/sbbs3/getctrl.c b/src/sbbs3/getctrl.c index 7a17e3db626ab72b0a9a97023a0acc5a47b122af..3f6e1f809134122de24ddb19fda34c58c9f4df2b 100644 --- a/src/sbbs3/getctrl.c +++ b/src/sbbs3/getctrl.c @@ -20,7 +20,7 @@ char *get_ctrl_dir(char *path, size_t pathsz) strncpy(path, PREFIX"/etc", pathsz); if(pathsz > 0) path[pathsz-1]=0; - iniFileName(ini_file, sizeof(ini_file)-1, PREFIX"/etc", sbbs.ini); + iniFileName(ini_file, sizeof(ini_file)-1, PREFIX"/etc", "sbbs.ini"); if(fexistcase(ini_file)) { FILE* fini; char* str; diff --git a/src/sbbs3/ident.c b/src/sbbs3/ident.c index 91c45cfc7a16b22cea31dc5281bea9b4adf8d6a3..d00703120a7c72b1015be90e4d92adf6878213f9 100644 --- a/src/sbbs3/ident.c +++ b/src/sbbs3/ident.c @@ -38,7 +38,7 @@ #include "sbbs.h" #include "ident.h" -BOOL identify(SOCKADDR_IN* client_addr, u_short local_port, char* buf +BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf ,size_t maxlen, int timeout) { char req[128]; @@ -47,11 +47,13 @@ BOOL identify(SOCKADDR_IN* client_addr, u_short local_port, char* buf int rd; ulong val; SOCKET sock=INVALID_SOCKET; - SOCKADDR_IN addr; + union xp_sockaddr addr; struct timeval tv; fd_set socket_set; BOOL success=FALSE; + if(client_addr->addr.sa_family != AF_INET && client_addr->addr.sa_family != AF_INET6) + return FALSE; if(timeout<=0) timeout=IDENT_DEFAULT_TIMEOUT; @@ -64,10 +66,10 @@ BOOL identify(SOCKADDR_IN* client_addr, u_short local_port, char* buf val=1; ioctlsocket(sock,FIONBIO,&val); - addr=*client_addr; - addr.sin_port=htons(IPPORT_IDENT); + memcpy(&addr, client_addr, xp_sockaddr_len(client_addr)); + inet_setaddrport(&addr, IPPORT_IDENT); - result=connect(sock, (struct sockaddr*)&addr, sizeof(addr)); + result=connect(sock, &addr.addr, xp_sockaddr_len(&addr)); if(result==SOCKET_ERROR && (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) { @@ -99,7 +101,7 @@ BOOL identify(SOCKADDR_IN* client_addr, u_short local_port, char* buf break; } - sprintf(req,"%u,%u\r\n", ntohs(client_addr->sin_port), local_port); + sprintf(req,"%u,%u\r\n", inet_addrport(client_addr), local_port); if(sendsocket(sock,req,strlen(req))!=(int)strlen(req)) { sprintf(buf,"ERROR %d sending request",ERROR_VALUE); break; diff --git a/src/sbbs3/ident.h b/src/sbbs3/ident.h index 64100aa41e25dd5fe35ca7c11c42dd81d9477e5b..b1767e9c628a5f358e6a4af8b458176d0546cc57 100644 --- a/src/sbbs3/ident.h +++ b/src/sbbs3/ident.h @@ -45,7 +45,7 @@ extern "C" { #endif -BOOL identify(SOCKADDR_IN* client_addr, u_short local_port, char* buf +BOOL identify(union xp_sockaddr* client_addr, u_short local_port, char* buf ,size_t maxlen, int timeout /* in seconds */); #ifdef __cplusplus diff --git a/src/sbbs3/js_global.c b/src/sbbs3/js_global.c index 72ceb49caceeb82e07fe7fe30c4b1964af0bdabc..19bb062746e6019ae698d9ca54bb75f81141dfb5 100644 --- a/src/sbbs3/js_global.c +++ b/src/sbbs3/js_global.c @@ -3379,7 +3379,6 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist) struct timeval tv = {0, 0}; jsuint i; jsuint limit; - SOCKET* index; jsval val; int len=0; jsrefcount rc; @@ -3405,9 +3404,6 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist) if((rarray = JS_NewArrayObject(cx, 0, NULL))==NULL) return(JS_FALSE); - if((index=(SOCKET *)malloc(sizeof(SOCKET)*limit))==NULL) - return(JS_FALSE); - FD_ZERO(&socket_set); if(poll_for_write) wr_set=&socket_set; @@ -3417,10 +3413,8 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist) for(i=0;i<limit;i++) { if(!JS_GetElement(cx, inarray, i, &val)) break; - sock=js_socket(cx,val); - index[i]=sock; + sock=js_socket_add(cx,val,&socket_set); if(sock!=INVALID_SOCKET) { - FD_SET(sock,&socket_set); if(sock>maxsock) maxsock=sock; } @@ -3428,9 +3422,10 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist) rc=JS_SUSPENDREQUEST(cx); if(select(maxsock+1,rd_set,wr_set,NULL,&tv) >= 0) { - for(i=0;i<limit;i++) { - if(index[i]!=INVALID_SOCKET && FD_ISSET(index[i],&socket_set)) { + if(!JS_GetElement(cx, inarray, i, &val)) + break; + if(js_socket_isset(cx,val,&socket_set)) { val=INT_TO_JSVAL(i); JS_RESUMEREQUEST(cx, rc); if(!JS_SetElement(cx, rarray, len++, &val)) { @@ -3443,7 +3438,6 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(rarray)); } - free(index); JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); @@ -3572,36 +3566,80 @@ js_strftime(JSContext *cx, uintN argc, jsval *arglist) return(JS_TRUE); } +/* TODO: IPv6 */ static JSBool js_resolve_ip(JSContext *cx, uintN argc, jsval *arglist) { jsval *argv=JS_ARGV(cx, arglist); - struct in_addr addr; JSString* str; - char* p; + char* p=NULL; jsrefcount rc; + struct addrinfo hints,*res,*cur; + char ip_str[INET6_ADDRSTRLEN]; + BOOL want_array=FALSE; + JSObject *rarray; + unsigned alen=0; + uintN argn; + jsval val; + int result; JS_SET_RVAL(cx, arglist, JSVAL_NULL); if(argc==0 || JSVAL_IS_VOID(argv[0])) return(JS_TRUE); - JSVALUE_TO_MSTRING(cx, argv[0], p, NULL) - HANDLE_PENDING(cx); - if(p==NULL) + for(argn=0; argn < argc; argn++) { + if(JSVAL_IS_BOOLEAN(argv[argn])) + want_array = JSVAL_TO_BOOLEAN(argv[argn]); + else if(JSVAL_IS_STRING(argv[argn])) { + if(p) + free(p); + JSVALUE_TO_MSTRING(cx, argv[argn], p, NULL) + HANDLE_PENDING(cx); + } + } + if(p==NULL) return(JS_TRUE); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; rc=JS_SUSPENDREQUEST(cx); - addr.s_addr=resolve_ip(p); + if((result=getaddrinfo(p, NULL, &hints, &res))!=0) { + lprintf(LOG_ERR, "!ERROR resolve_ip %s failed with error %d",p, result); + JS_RESUMEREQUEST(cx, rc); + free(p); + return JS_TRUE; + } free(p); - JS_RESUMEREQUEST(cx, rc); - if(addr.s_addr==INADDR_NONE) - return(JS_TRUE); - - if((str=JS_NewStringCopyZ(cx, inet_ntoa(addr)))==NULL) - return(JS_FALSE); - JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str)); + if(want_array) { + JS_RESUMEREQUEST(cx, rc); + if((rarray = JS_NewArrayObject(cx, 0, NULL))==NULL) + return(JS_FALSE); + JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(rarray)); + for(cur=res; cur; cur=cur->ai_next) { + inet_addrtop((void *)cur->ai_addr, ip_str, sizeof(ip_str)); + if((str=JS_NewStringCopyZ(cx, ip_str))==NULL) { + freeaddrinfo(res); + return(JS_FALSE); + } + val = STRING_TO_JSVAL(str); + if(!JS_SetElement(cx, rarray, alen++, &val)) + break; + } + freeaddrinfo(res); + } + else { + inet_addrtop((void *)res->ai_addr, ip_str, sizeof(ip_str)); + freeaddrinfo(res); + JS_RESUMEREQUEST(cx, rc); + + if((str=JS_NewStringCopyZ(cx, ip_str))==NULL) + return(JS_FALSE); + + JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str)); + } return(JS_TRUE); } @@ -3610,10 +3648,10 @@ static JSBool js_resolve_host(JSContext *cx, uintN argc, jsval *arglist) { jsval *argv=JS_ARGV(cx, arglist); - struct in_addr addr; - HOSTENT* h; char* p; jsrefcount rc; + struct addrinfo hints,*res; + char host_name[256]; JS_SET_RVAL(cx, arglist, JSVAL_NULL); @@ -3622,17 +3660,29 @@ js_resolve_host(JSContext *cx, uintN argc, jsval *arglist) JSVALUE_TO_MSTRING(cx, argv[0], p, NULL) HANDLE_PENDING(cx); - if(p==NULL) + if(p==NULL) return(JS_TRUE); rc=JS_SUSPENDREQUEST(cx); - addr.s_addr=inet_addr(p); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + if(getaddrinfo(p, NULL, NULL, &res)!=0) { + free(p); + JS_RESUMEREQUEST(cx, rc); + return(JS_TRUE); + } free(p); - h=gethostbyaddr((char *)&addr,sizeof(addr),AF_INET); + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = NI_NAMEREQD; + if(getnameinfo(res->ai_addr, res->ai_addrlen, host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD)!=0) { + JS_RESUMEREQUEST(cx, rc); + return(JS_TRUE); + } JS_RESUMEREQUEST(cx, rc); - if(h!=NULL && h->h_name!=NULL) - JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,h->h_name))); + JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,host_name))); + freeaddrinfo(res); return(JS_TRUE); @@ -4000,8 +4050,9 @@ static jsSyncMethodSpec js_global_functions[] = { ,311 }, {"gethostbyname", js_resolve_ip, 1, JSTYPE_ALIAS }, - {"resolve_ip", js_resolve_ip, 1, JSTYPE_STRING, JSDOCSTR("hostname") - ,JSDOCSTR("resolve IP address of specified hostname (AKA gethostbyname)") + {"resolve_ip", js_resolve_ip, 1, JSTYPE_STRING, JSDOCSTR("hostname [,array=<tt>false</tt>]") + ,JSDOCSTR("resolve IP address of specified hostname (AKA gethostbyname). If array is true (added in 3.17), will return " + "an array of all addresses rather than just the first one") ,311 }, {"gethostbyaddr", js_resolve_host, 1, JSTYPE_ALIAS }, diff --git a/src/sbbs3/js_socket.c b/src/sbbs3/js_socket.c index 4047b65a31813a2196eef6f86eaa479353a540f6..730e0ba23e24e6d1757e12b7515d3292b8cc244d 100644 --- a/src/sbbs3/js_socket.c +++ b/src/sbbs3/js_socket.c @@ -38,27 +38,14 @@ #include <cryptlib.h> #include "sbbs.h" +#include "js_socket.h" #include "js_request.h" +#include "multisock.h" #ifdef JAVASCRIPT int cryptInitialized=0; -typedef struct -{ - SOCKET sock; - BOOL external; /* externally created, don't close */ - BOOL debug; - BOOL nonblocking; - BOOL is_connected; - BOOL network_byte_order; - int last_error; - int type; - SOCKADDR_IN remote_addr; - CRYPT_SESSION session; - char *hostname; -} private_t; - static const char* getprivate_failure = "line %d %s JS_GetPrivate failed"; static void do_cryptEnd(void) @@ -68,13 +55,27 @@ static void do_cryptEnd(void) static int do_cryptAttribute(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, int val) { - int ret=cryptSetAttribute(session, attr, val); + int ret; + + /* Force "sane" values (requirements) */ + switch(attr) { + case CRYPT_OPTION_NET_READTIMEOUT: + if (val < 0) + val = 0; + if (val > 300) + val = 300; + break; + default: + break; + } + + ret=cryptSetAttribute(session, attr, val); if(ret != CRYPT_OK) - lprintf(LOG_ERR, "cryptSetAttribute(%d) returned %d", attr, ret); + lprintf(LOG_ERR, "cryptSetAttribute(%d=%d) returned %d", attr, val, ret); return ret; } -int do_cryptInit(void) +int DLLCALL do_cryptInit(void) { int ret; @@ -84,8 +85,12 @@ int do_cryptInit(void) cryptInitialized=1; atexit(do_cryptEnd); } - else - lprintf(LOG_ERR,"cryptInit() returned %d", ret); + else { + if (ret == -12) // This is a bit of a hack... + cryptInitialized=1; + else + lprintf(LOG_ERR,"cryptInit() returned %d", ret); + } } return cryptInitialized; } @@ -94,31 +99,53 @@ static int do_cryptAttributeString(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_ { int ret=cryptSetAttributeString(session, attr, val, len); if(ret != CRYPT_OK) - lprintf(LOG_ERR, "cryptSetAttributeString(%d) returned %d", attr, ret); + lprintf(LOG_ERR, "cryptSetAttributeString(%d=%.*s) returned %d", attr, len, val, ret); return ret; } static void do_CryptFlush(const CRYPT_CONTEXT session) { int ret=cryptFlushData(session); + int len = 0; + char estr[CRYPT_MAX_TEXTSIZE+1]; + + ret = cryptFlushData(session); - if(ret!=CRYPT_OK) - lprintf(LOG_ERR, "cryptFlushData() returned %d", ret); + if(ret!=CRYPT_OK) { + cryptGetAttributeString(session, CRYPT_ATTRIBUTE_ERRORMESSAGE, estr, &len); + estr[len]=0; + if (len) + lprintf(LOG_ERR, "cryptFlushData() returned %d (%s)", ret, estr); + else + lprintf(LOG_ERR, "cryptFlushData() returned %d", ret); + } +} + +static void do_js_close(js_socket_private_t *p) +{ + if(p->session != -1) { + cryptDestroySession(p->session); + p->session=-1; + } + if(p->sock==INVALID_SOCKET) + return; + close_socket(p->sock); + p->last_error = ERROR_VALUE; + p->sock = INVALID_SOCKET; + p->is_connected = FALSE; } -static ptrdiff_t js_socket_recv(private_t *p, void *buf, size_t len, int flags, int timeout) +static ptrdiff_t js_socket_recv(js_socket_private_t *p, void *buf, size_t len, int flags, int timeout) { ptrdiff_t total=0; int copied,ret; if(p->session==-1) return(recv(p->sock, buf, len, flags)); /* Blocked here, indefinitely, in MSP-UDP service */ - if(p->nonblocking) { - do_cryptAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, 0); - } - else { - do_cryptAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, timeout); - } +#if 0 + if (do_cryptAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, p->nonblocking?0:timeout) != CRYPT_OK) + return -1; +#endif do { if((ret=cryptPopData(p->session, buf, len, &copied))==CRYPT_OK) { if(p->nonblocking) @@ -131,13 +158,16 @@ static ptrdiff_t js_socket_recv(private_t *p, void *buf, size_t len, int flags, } else { lprintf(LOG_ERR,"cryptPopData() returned %d", ret); - return total; + if (total > 0) + return total; + do_js_close(p); + return -1; } } while(len); return total; // Shouldn't happen... } -static ptrdiff_t js_socket_sendsocket(private_t *p, const void *msg, size_t len, int flush) +static ptrdiff_t js_socket_sendsocket(js_socket_private_t *p, const void *msg, size_t len, int flush) { ptrdiff_t total=0; int copied=0,ret; @@ -168,7 +198,7 @@ static ptrdiff_t js_socket_sendsocket(private_t *p, const void *msg, size_t len, return total; // shouldn't happen... } -static int js_socket_sendfilesocket(private_t *p, int file, off_t *offset, off_t count) +static int js_socket_sendfilesocket(js_socket_private_t *p, int file, off_t *offset, off_t count) { char buf[1024*16]; off_t len; @@ -230,7 +260,7 @@ static int js_socket_sendfilesocket(private_t *p, int file, off_t *offset, off_t return(total); } -static void dbprintf(BOOL error, private_t* p, char* fmt, ...) +static void dbprintf(BOOL error, js_socket_private_t* p, char* fmt, ...) { va_list argptr; char sbuf[1024]; @@ -250,9 +280,9 @@ static void dbprintf(BOOL error, private_t* p, char* fmt, ...) static void js_finalize_socket(JSContext *cx, JSObject *obj) { - private_t* p; + js_socket_private_t* p; - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) return; if(p->session != -1) { @@ -278,32 +308,19 @@ static JSBool js_close(JSContext *cx, uintN argc, jsval *arglist) { JSObject *obj=JS_THIS_OBJECT(cx, arglist); - private_t* p; + js_socket_private_t* p; jsrefcount rc; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } - if(p->session != -1) { - cryptDestroySession(p->session); - p->session=-1; - } - if(p->sock==INVALID_SOCKET) - return(JS_TRUE); - rc=JS_SUSPENDREQUEST(cx); - close_socket(p->sock); - - p->last_error = ERROR_VALUE; - + do_js_close(p); dbprintf(FALSE, p, "closed"); - - p->sock = INVALID_SOCKET; - p->is_connected = FALSE; JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); @@ -354,6 +371,73 @@ SOCKET DLLCALL js_socket(JSContext *cx, jsval val) return(sock); } +SOCKET DLLCALL js_socket_add(JSContext *cx, jsval val, fd_set *fds) +{ + js_socket_private_t *p; + JSClass* cl; + SOCKET sock=INVALID_SOCKET; + int32 i; + + if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) { + if(cl->flags&JSCLASS_HAS_PRIVATE) { + if((p=(js_socket_private_t *)JS_GetPrivate(cx,JSVAL_TO_OBJECT(val)))!=NULL) { + if(p->set) { + for(i=0; i<p->set->sock_count; i++) { + if(p->set->socks[i].sock == INVALID_SOCKET) + continue; + FD_SET(p->set->socks[i].sock, fds); + if(p->set->socks[i].sock > sock) + sock = p->set->socks[i].sock; + } + } + else { + sock = p->sock; + if(sock != INVALID_SOCKET) + FD_SET(p->sock, fds); + } + } + } + } else if(val!=JSVAL_VOID) { + if(JS_ValueToInt32(cx,val,&i)) { + sock = i; + FD_SET(sock, fds); + } + } + return sock; +} + +BOOL DLLCALL js_socket_isset(JSContext *cx, jsval val, fd_set *fds) +{ + js_socket_private_t *p; + JSClass* cl; + int i; + + if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) { + if(cl->flags&JSCLASS_HAS_PRIVATE) { + if((p=(js_socket_private_t *)JS_GetPrivate(cx,JSVAL_TO_OBJECT(val)))!=NULL) { + if(p->set) { + for(i=0; i<p->set->sock_count; i++) { + if(p->set->socks[i].sock == INVALID_SOCKET) + continue; + if(FD_ISSET(p->set->socks[i].sock, fds)) + return TRUE; + } + } + else { + if(FD_ISSET(p->sock, fds)) + return TRUE; + } + } + } + } else if(val!=JSVAL_VOID) { + if(JS_ValueToInt32(cx,val,&i)) { + if(FD_ISSET(i, fds)) + return TRUE; + } + } + return FALSE; +} + void DLLCALL js_timeval(JSContext* cx, jsval val, struct timeval* tv) { jsdouble jsd; @@ -372,40 +456,55 @@ js_bind(JSContext *cx, uintN argc, jsval *arglist) { JSObject *obj=JS_THIS_OBJECT(cx, arglist); jsval *argv=JS_ARGV(cx, arglist); - ulong ip=0; - private_t* p; + js_socket_private_t* p; ushort port=0; - SOCKADDR_IN addr; + union xp_sockaddr addr; jsrefcount rc; - char *cstr; + char *cstr=NULL; + char portstr[6]; + struct addrinfo hints, *res, *tres; + int ret; - JS_SET_RVAL(cx, arglist, JSVAL_VOID); + JS_SET_RVAL(cx, arglist, JSVAL_FALSE); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } memset(&addr,0,sizeof(addr)); - addr.sin_family = AF_INET; + memset(&hints, 0, sizeof(hints)); if(argc) port = js_port(cx,argv[0],p->type); - addr.sin_port = htons(port); - if(argc > 1) - JSVALUE_TO_ASTRING(cx, argv[1], cstr, 16, NULL); - if(argc>1 && cstr != NULL - && (ip=inet_addr(cstr))!=INADDR_NONE) - addr.sin_addr.s_addr = ip; + if(argc > 1) { + JSVALUE_TO_ASTRING(cx, argv[1], cstr, INET6_ADDRSTRLEN, NULL); + } + + hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICHOST|AI_NUMERICSERV|AI_PASSIVE; + hints.ai_socktype = p->type; + + /* We need servname to be non-NULL so we can use a NULL hostname */ + sprintf(portstr, "%hu", port); rc=JS_SUSPENDREQUEST(cx); - if(bind(p->sock, (struct sockaddr *) &addr, sizeof(addr))!=0) { - p->last_error=ERROR_VALUE; - dbprintf(TRUE, p, "bind failed with error %d",ERROR_VALUE); - JS_SET_RVAL(cx, arglist, JSVAL_FALSE); - JS_RESUMEREQUEST(cx, rc); + if((ret=getaddrinfo(cstr, portstr, &hints, &res)) != 0) { + JS_RESUMEREQUEST(cx,rc); + dbprintf(TRUE, p, "getaddrinfo failed with error %d",ret); return(JS_TRUE); } + for(tres=res; tres->ai_next; tres=tres->ai_next) { + if(bind(p->sock, res->ai_addr, res->ai_addrlen)!=0) { + if (tres->ai_next == NULL) { + p->last_error=ERROR_VALUE; + dbprintf(TRUE, p, "bind failed with error %d",ERROR_VALUE); + freeaddrinfo(res); + JS_RESUMEREQUEST(cx, rc); + return(JS_TRUE); + } + } + } + freeaddrinfo(res); dbprintf(FALSE, p, "bound to port %u",port); JS_SET_RVAL(cx, arglist, JSVAL_TRUE); @@ -418,13 +517,13 @@ js_listen(JSContext *cx, uintN argc, jsval *arglist) { JSObject *obj=JS_THIS_OBJECT(cx, arglist); jsval *argv=JS_ARGV(cx, arglist); - private_t* p; + js_socket_private_t* p; int32 backlog=1; jsrefcount rc; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -451,8 +550,8 @@ static JSBool js_accept(JSContext *cx, uintN argc, jsval *arglist) { JSObject *obj=JS_THIS_OBJECT(cx, arglist); - private_t* p; - private_t* new_p; + js_socket_private_t* p; + js_socket_private_t* new_p; JSObject* sockobj; SOCKET new_socket; socklen_t addrlen; @@ -460,7 +559,7 @@ js_accept(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -468,11 +567,21 @@ js_accept(JSContext *cx, uintN argc, jsval *arglist) addrlen=sizeof(p->remote_addr); rc=JS_SUSPENDREQUEST(cx); - if((new_socket=accept_socket(p->sock,(struct sockaddr *)&(p->remote_addr),&addrlen))==INVALID_SOCKET) { - p->last_error=ERROR_VALUE; - dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE); - JS_RESUMEREQUEST(cx, rc); - return(JS_TRUE); + if(p->set) { + if((new_socket=xpms_accept(p->set,&(p->remote_addr),&addrlen,XPMS_FOREVER,NULL))==INVALID_SOCKET) { + p->last_error=ERROR_VALUE; + dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE); + JS_RESUMEREQUEST(cx, rc); + return(JS_TRUE); + } + } + else { + if((new_socket=accept_socket(p->sock,&(p->remote_addr),&addrlen))==INVALID_SOCKET) { + p->last_error=ERROR_VALUE; + dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE); + JS_RESUMEREQUEST(cx, rc); + return(JS_TRUE); + } } if((sockobj=js_CreateSocketObject(cx, obj, "new_socket", new_socket))==NULL) { @@ -481,7 +590,7 @@ js_accept(JSContext *cx, uintN argc, jsval *arglist) JS_ReportError(cx,"Error creating new socket object"); return(JS_TRUE); } - if((new_p=(private_t*)JS_GetPrivate(cx,sockobj))==NULL) { + if((new_p=(js_socket_private_t*)JS_GetPrivate(cx,sockobj))==NULL) { JS_RESUMEREQUEST(cx, rc); JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); @@ -506,15 +615,16 @@ js_connect(JSContext *cx, uintN argc, jsval *arglist) jsval *argv=JS_ARGV(cx, arglist); int result; ulong val; - ulong ip_addr; ushort port; JSString* str; - private_t* p; + js_socket_private_t* p; fd_set socket_set; struct timeval tv = {0, 0}; jsrefcount rc; + char ip_str[256]; + struct addrinfo hints,*res,*cur; - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -523,60 +633,62 @@ js_connect(JSContext *cx, uintN argc, jsval *arglist) if(p->hostname) free(p->hostname); JSSTRING_TO_MSTRING(cx, str, p->hostname, NULL); + port = js_port(cx,argv[1],p->type); rc=JS_SUSPENDREQUEST(cx); dbprintf(FALSE, p, "resolving hostname: %s", p->hostname); - if((ip_addr=resolve_ip(p->hostname))==INADDR_NONE) { - p->last_error=ERROR_VALUE; - dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = p->type; + hints.ai_flags = AI_ADDRCONFIG; + result = getaddrinfo(p->hostname, NULL, &hints, &res); + if(result != 0) { JS_SET_RVAL(cx, arglist, JSVAL_FALSE); + dbprintf(TRUE, p, "looking up addresses for %s", p->hostname); JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } - - JS_RESUMEREQUEST(cx, rc); - port = js_port(cx,argv[1],p->type); - - tv.tv_sec = 10; /* default time-out */ - - if(argc>2) /* time-out value specified */ - js_timeval(cx,argv[2],&tv); - - rc=JS_SUSPENDREQUEST(cx); - dbprintf(FALSE, p, "connecting to port %u at %s", port, p->hostname); - - memset(&(p->remote_addr),0,sizeof(p->remote_addr)); - p->remote_addr.sin_addr.s_addr = ip_addr; - p->remote_addr.sin_family = AF_INET; - p->remote_addr.sin_port = htons(port); - /* always set to nonblocking here */ val=1; ioctlsocket(p->sock,FIONBIO,&val); + for(cur=res,result=1; result && cur; cur=cur->ai_next) { + tv.tv_sec = 10; /* default time-out */ - result=connect(p->sock, (struct sockaddr *)&(p->remote_addr), sizeof(p->remote_addr)); + if(argc>2) /* time-out value specified */ + js_timeval(cx,argv[2],&tv); - if(result==SOCKET_ERROR - && (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) { - FD_ZERO(&socket_set); - FD_SET(p->sock,&socket_set); - if(select(p->sock+1,NULL,&socket_set,NULL,&tv)==1) - result=0; /* success */ - } + inet_addrtop((void *)cur->ai_addr, ip_str, sizeof(ip_str)); + dbprintf(FALSE, p, "connecting to %s on port %u at %s", ip_str, port, p->hostname); + inet_setaddrport((void *)cur->ai_addr, port); + result=connect(p->sock, cur->ai_addr, cur->ai_addrlen); + + if(result==SOCKET_ERROR + && (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) { + FD_ZERO(&socket_set); + FD_SET(p->sock,&socket_set); + if(select(p->sock+1,NULL,&socket_set,NULL,&tv)==1) + result=0; /* success */ + } + if(result==0) + break; + } /* Restore original setting here */ ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking)); if(result!=0) { + freeaddrinfo(res); p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "connect failed with error %d",ERROR_VALUE); JS_SET_RVAL(cx, arglist, JSVAL_FALSE); JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } + memcpy(&p->remote_addr, cur->ai_addr, cur->ai_addrlen); + freeaddrinfo(res); p->is_connected = TRUE; JS_SET_RVAL(cx, arglist, JSVAL_TRUE); - dbprintf(FALSE, p, "connected to port %u at %s", port, p->hostname); + dbprintf(FALSE, p, "connected to %s on port %u at %s", ip_str, port, p->hostname); JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); @@ -590,12 +702,12 @@ js_send(JSContext *cx, uintN argc, jsval *arglist) char* cp; size_t len; JSString* str; - private_t* p; + js_socket_private_t* p; jsrefcount rc; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -622,6 +734,46 @@ js_send(JSContext *cx, uintN argc, jsval *arglist) return(JS_TRUE); } +static JSBool +js_sendline(JSContext *cx, uintN argc, jsval *arglist) +{ + JSObject *obj=JS_THIS_OBJECT(cx, arglist); + jsval *argv=JS_ARGV(cx, arglist); + char* cp; + size_t len; + JSString* str; + js_socket_private_t* p; + jsrefcount rc; + + JS_SET_RVAL(cx, arglist, JSVAL_VOID); + + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { + JS_ReportError(cx,getprivate_failure,WHERE); + return(JS_FALSE); + } + + JS_SET_RVAL(cx, arglist, JSVAL_FALSE); + + str = JS_ValueToString(cx, argv[0]); + JSSTRING_TO_MSTRING(cx, str, cp, &len); + HANDLE_PENDING(cx); + if(cp==NULL) + return JS_TRUE; + + rc=JS_SUSPENDREQUEST(cx); + if(js_socket_sendsocket(p,cp,len,FALSE)==len && js_socket_sendsocket(p,"\r\n",2,TRUE)==2) { + dbprintf(FALSE, p, "sent %u bytes",len+2); + JS_SET_RVAL(cx, arglist, JSVAL_TRUE); + } else { + p->last_error=ERROR_VALUE; + dbprintf(TRUE, p, "send of %u bytes failed",len+2); + } + free(cp); + JS_RESUMEREQUEST(cx, rc); + + return(JS_TRUE); +} + static JSBool js_sendto(JSContext *cx, uintN argc, jsval *arglist) { @@ -629,17 +781,18 @@ js_sendto(JSContext *cx, uintN argc, jsval *arglist) jsval *argv=JS_ARGV(cx, arglist); char* cp; size_t len; - ulong ip_addr; ushort port; JSString* data_str; JSString* ip_str; - private_t* p; - SOCKADDR_IN addr; + js_socket_private_t* p; jsrefcount rc; + struct addrinfo hints,*res,*cur; + int result; + char ip_addr[INET6_ADDRSTRLEN]; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -666,38 +819,37 @@ js_sendto(JSContext *cx, uintN argc, jsval *arglist) free(cp); return JS_TRUE; } + port = js_port(cx,argv[2],p->type); rc=JS_SUSPENDREQUEST(cx); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = p->type; + hints.ai_flags = AI_ADDRCONFIG; dbprintf(FALSE, p, "resolving hostname: %s", p->hostname); - if((ip_addr=resolve_ip(p->hostname))==INADDR_NONE) { - p->last_error=ERROR_VALUE; - dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE); + + if((result=getaddrinfo(p->hostname, NULL, &hints, &res) != 0)) { + dbprintf(TRUE, p, "getaddrinfo failed with error %d",result); JS_SET_RVAL(cx, arglist, JSVAL_FALSE); free(cp); JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } - /* port */ - JS_RESUMEREQUEST(cx, rc); - port = js_port(cx,argv[2],p->type); - rc=JS_SUSPENDREQUEST(cx); - - dbprintf(FALSE, p, "sending %d bytes to port %u at %s" - ,len, port, p->hostname); - - memset(&addr,0,sizeof(addr)); - addr.sin_addr.s_addr = ip_addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - - if(sendto(p->sock,cp,len,0 /* flags */,(SOCKADDR*)&addr,sizeof(addr))==len) { - dbprintf(FALSE, p, "sent %u bytes",len); - JS_SET_RVAL(cx, arglist, JSVAL_TRUE); - } else { - p->last_error=ERROR_VALUE; - dbprintf(TRUE, p, "send of %u bytes failed",len); + for(cur=res; cur; cur=cur->ai_next) { + inet_addrtop((void *)cur->ai_addr, ip_addr, sizeof(ip_addr)); + dbprintf(FALSE, p, "sending %d bytes to %s port %u at %s" + ,len, ip_addr, port, p->hostname); + inet_setaddrport((void *)cur->ai_addr, port); + if(sendto(p->sock,cp,len,0 /* flags */,cur->ai_addr,cur->ai_addrlen)==len) { + dbprintf(FALSE, p, "sent %u bytes",len); + JS_SET_RVAL(cx, arglist, JSVAL_TRUE); + } else { + p->last_error=ERROR_VALUE; + dbprintf(TRUE, p, "send of %u bytes failed to %s",len, ip_addr); + } } free(cp); + freeaddrinfo(res); JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); @@ -712,12 +864,12 @@ js_sendfile(JSContext *cx, uintN argc, jsval *arglist) char* fname; long len; int file; - private_t* p; + js_socket_private_t* p; jsrefcount rc; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -764,12 +916,12 @@ js_sendbin(JSContext *cx, uintN argc, jsval *arglist) int32 val=0; size_t wr=0; int32 size=sizeof(DWORD); - private_t* p; + js_socket_private_t* p; jsrefcount rc; JS_SET_RVAL(cx, arglist, JSVAL_FALSE); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -824,11 +976,11 @@ js_recv(JSContext *cx, uintN argc, jsval *arglist) int32 len=512; JSString* str; jsrefcount rc; - private_t* p; + js_socket_private_t* p; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -873,7 +1025,7 @@ js_recvfrom(JSContext *cx, uintN argc, jsval *arglist) JSObject *obj=JS_THIS_OBJECT(cx, arglist); jsval *argv=JS_ARGV(cx, arglist); char* buf; - char ip_addr[64]; + char ip_addr[INET6_ADDRSTRLEN]; char port[32]; int rd=0; int32 len=512; @@ -885,14 +1037,14 @@ js_recvfrom(JSContext *cx, uintN argc, jsval *arglist) jsval data_val=JSVAL_NULL; JSString* str; JSObject* retobj; - SOCKADDR_IN addr; + union xp_sockaddr addr; socklen_t addrlen; jsrefcount rc; - private_t* p; + js_socket_private_t* p; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -915,11 +1067,11 @@ js_recvfrom(JSContext *cx, uintN argc, jsval *arglist) rc=JS_SUSPENDREQUEST(cx); switch(len) { case sizeof(BYTE): - if((rd=recvfrom(p->sock,&b,len,0,(SOCKADDR*)&addr,&addrlen))==len) + if((rd=recvfrom(p->sock,&b,len,0,&addr.addr,&addrlen))==len) data_val = INT_TO_JSVAL(b); break; case sizeof(WORD): - if((rd=recvfrom(p->sock,(BYTE*)&w,len,0,(SOCKADDR*)&addr,&addrlen))==len) { + if((rd=recvfrom(p->sock,(BYTE*)&w,len,0,&addr.addr,&addrlen))==len) { if(p->network_byte_order) w=ntohs(w); data_val = INT_TO_JSVAL(w); @@ -927,7 +1079,7 @@ js_recvfrom(JSContext *cx, uintN argc, jsval *arglist) break; default: case sizeof(DWORD): - if((rd=recvfrom(p->sock,(BYTE*)&l,len,0,(SOCKADDR*)&addr,&addrlen))==len) { + if((rd=recvfrom(p->sock,(BYTE*)&l,len,0,&addr.addr,&addrlen))==len) { if(p->network_byte_order) l=ntohl(l); data_val=UINT_TO_JSVAL(l); @@ -950,7 +1102,7 @@ js_recvfrom(JSContext *cx, uintN argc, jsval *arglist) } rc=JS_SUSPENDREQUEST(cx); - len = recvfrom(p->sock,buf,len,0,(SOCKADDR*)&addr,&addrlen); + len = recvfrom(p->sock,buf,len,0,&addr.addr,&addrlen); JS_RESUMEREQUEST(cx, rc); if(len<0) { p->last_error=ERROR_VALUE; @@ -978,14 +1130,14 @@ js_recvfrom(JSContext *cx, uintN argc, jsval *arglist) ,data_val ,NULL,NULL,JSPROP_ENUMERATE); - sprintf(port,"%u",ntohs(addr.sin_port)); + sprintf(port,"%u",inet_addrport(&addr)); if((str=JS_NewStringCopyZ(cx,port))==NULL) return(JS_FALSE); JS_DefineProperty(cx, retobj, "port" ,STRING_TO_JSVAL(str) ,NULL,NULL,JSPROP_ENUMERATE); - SAFECOPY(ip_addr,inet_ntoa(addr.sin_addr)); + inet_addrtop(&addr, ip_addr, sizeof(ip_addr)); if((str=JS_NewStringCopyZ(cx,ip_addr))==NULL) return(JS_FALSE); JS_DefineProperty(cx, retobj, "ip_address" @@ -1011,11 +1163,11 @@ js_peek(JSContext *cx, uintN argc, jsval *arglist) int32 len=512; JSString* str; jsrefcount rc; - private_t* p; + js_socket_private_t* p; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -1056,7 +1208,7 @@ js_peek(JSContext *cx, uintN argc, jsval *arglist) } static int -js_sock_read_check(private_t *p, time_t start, int32 timeout, int i) +js_sock_read_check(js_socket_private_t *p, time_t start, int32 timeout, int i) { BOOL rd; @@ -1090,17 +1242,17 @@ js_recvline(JSContext *cx, uintN argc, jsval *arglist) jsval *argv=JS_ARGV(cx, arglist); char ch; char* buf; - int i; + int i,got; int32 len=512; time_t start; int32 timeout=30; /* seconds */ JSString* str; - private_t* p; + js_socket_private_t* p; jsrefcount rc; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -1135,12 +1287,16 @@ js_recvline(JSContext *cx, uintN argc, jsval *arglist) } } - if(js_socket_recv(p, &ch, 1, 0, timeout)!=1) { + if((got=js_socket_recv(p, &ch, 1, 0, i?1000:timeout))!=1) { if(p->session==-1) { p->last_error=ERROR_VALUE; break; } else { + if (got == -1) { + len = 0; + continue; + } switch(js_sock_read_check(p,start,timeout,i)) { case 1: JS_SET_RVAL(cx, arglist, JSVAL_NULL); @@ -1191,12 +1347,12 @@ js_recvbin(JSContext *cx, uintN argc, jsval *arglist) DWORD l; int32 size=sizeof(DWORD); int rd=0; - private_t* p; + js_socket_private_t* p; jsrefcount rc; JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1)); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -1242,7 +1398,7 @@ js_getsockopt(JSContext *cx, uintN argc, jsval *arglist) int opt; int level; int val; - private_t* p; + js_socket_private_t* p; LINGER linger; void* vp=&val; socklen_t len=sizeof(val); @@ -1251,7 +1407,7 @@ js_getsockopt(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1)); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -1294,7 +1450,7 @@ js_setsockopt(JSContext *cx, uintN argc, jsval *arglist) int opt; int level; int32 val=1; - private_t* p; + js_socket_private_t* p; LINGER linger; void* vp=&val; socklen_t len=sizeof(val); @@ -1303,7 +1459,7 @@ js_setsockopt(JSContext *cx, uintN argc, jsval *arglist) JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -1343,12 +1499,12 @@ js_ioctlsocket(JSContext *cx, uintN argc, jsval *arglist) jsval *argv=JS_ARGV(cx, arglist); int32 cmd=0; int32 arg=0; - private_t* p; + js_socket_private_t* p; jsrefcount rc; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } @@ -1378,7 +1534,7 @@ js_poll(JSContext *cx, uintN argc, jsval *arglist) { JSObject *obj=JS_THIS_OBJECT(cx, arglist); jsval *argv=JS_ARGV(cx, arglist); - private_t* p; + js_socket_private_t* p; BOOL poll_for_write=FALSE; fd_set socket_set; fd_set* rd_set=NULL; @@ -1387,15 +1543,17 @@ js_poll(JSContext *cx, uintN argc, jsval *arglist) int result; struct timeval tv = {0, 0}; jsrefcount rc; + int i; + SOCKET high=0; JS_SET_RVAL(cx, arglist, JSVAL_VOID); - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx,getprivate_failure,WHERE); return(JS_FALSE); } - if(p->sock==INVALID_SOCKET) { + if(p->sock==INVALID_SOCKET && p->set == NULL) { dbprintf(TRUE, p, "INVALID SOCKET in call to poll"); JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1)); return(JS_TRUE); @@ -1410,13 +1568,23 @@ js_poll(JSContext *cx, uintN argc, jsval *arglist) rc=JS_SUSPENDREQUEST(cx); FD_ZERO(&socket_set); - FD_SET(p->sock,&socket_set); + if(p->set) { + for(i=0; i<p->set->sock_count; i++) { + FD_SET(p->set->socks[i].sock,&socket_set); + if(p->set->socks[i].sock > high) + high = p->set->socks[i].sock; + } + } + else { + high=p->sock; + FD_SET(p->sock,&socket_set); + } if(poll_for_write) wr_set=&socket_set; else rd_set=&socket_set; - result = select(p->sock+1,rd_set,wr_set,NULL,&tv); + result = select(high+1,rd_set,wr_set,NULL,&tv); p->last_error=ERROR_VALUE; @@ -1479,12 +1647,12 @@ static JSBool js_socket_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict { jsval idval; jsint tiny; - private_t* p; + js_socket_private_t* p; jsrefcount rc; BOOL b; int32 i; - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { // Prototype access return(JS_TRUE); } @@ -1582,13 +1750,14 @@ static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) ulong cnt; BOOL rd; BOOL wr; - private_t* p; + js_socket_private_t* p; JSString* js_str; - SOCKADDR_IN addr; + union xp_sockaddr addr; socklen_t len=sizeof(addr); jsrefcount rc; + char str[256]; - if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL) { // Protoype access return(JS_TRUE); } @@ -1612,14 +1781,24 @@ static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) *vp = BOOLEAN_TO_JSVAL(socket_check(p->sock,NULL,NULL,0)); break; case SOCK_PROP_IS_WRITEABLE: - socket_check(p->sock,NULL,&wr,0); + if(p->sock==INVALID_SOCKET && p->set) + wr = FALSE; + else + socket_check(p->sock,NULL,&wr,0); *vp = BOOLEAN_TO_JSVAL(wr); break; case SOCK_PROP_DATA_WAITING: - socket_check(p->sock,&rd,NULL,0); + if(p->sock==INVALID_SOCKET && p->set) + rd = FALSE; + else + socket_check(p->sock,&rd,NULL,0); *vp = BOOLEAN_TO_JSVAL(rd); break; case SOCK_PROP_NREAD: + if(p->sock==INVALID_SOCKET && p->set) { + *vp = JSVAL_ZERO; + break; + } cnt=0; if(ioctlsocket(p->sock, FIONREAD, &cnt)==0) { *vp=DOUBLE_TO_JSVAL((double)cnt); @@ -1641,7 +1820,8 @@ static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if(getsockname(p->sock, (struct sockaddr *)&addr,&len)!=0) return(JS_FALSE); JS_RESUMEREQUEST(cx, rc); - if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(addr.sin_addr)))==NULL) + inet_addrtop(&addr, str, sizeof(str)); + if((js_str=JS_NewStringCopyZ(cx,str))==NULL) return(JS_FALSE); *vp = STRING_TO_JSVAL(js_str); rc=JS_SUSPENDREQUEST(cx); @@ -1651,13 +1831,10 @@ static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) break; case SOCK_PROP_LOCAL_PORT: if(p->sock != INVALID_SOCKET) { - if(getsockname(p->sock, (struct sockaddr *)&addr,&len)!=0) + if(getsockname(p->sock, &addr.addr,&len)!=0) return(JS_FALSE); JS_RESUMEREQUEST(cx, rc); - if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(addr.sin_addr)))==NULL) - return(JS_FALSE); - - *vp = INT_TO_JSVAL(ntohs(addr.sin_port)); + *vp = INT_TO_JSVAL(inet_addrport(&addr)); rc=JS_SUSPENDREQUEST(cx); } else @@ -1666,7 +1843,8 @@ static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) case SOCK_PROP_REMOTE_IP: if(p->is_connected) { JS_RESUMEREQUEST(cx, rc); - if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(p->remote_addr.sin_addr)))==NULL) + inet_addrtop(&p->remote_addr, str, sizeof(str)); + if((js_str=JS_NewStringCopyZ(cx,str))==NULL) return(JS_FALSE); *vp = STRING_TO_JSVAL(js_str); rc=JS_SUSPENDREQUEST(cx); @@ -1676,7 +1854,7 @@ static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) break; case SOCK_PROP_REMOTE_PORT: if(p->is_connected) - *vp = INT_TO_JSVAL(ntohs(p->remote_addr.sin_port)); + *vp = INT_TO_JSVAL(inet_addrport(&p->remote_addr)); else *vp=JSVAL_VOID; break; @@ -1748,6 +1926,11 @@ static jsSyncMethodSpec js_socket_functions[] = { ,JSDOCSTR("send a string (AKA write)") ,310 }, + {"writeln", js_sendline, 1, JSTYPE_ALIAS }, + {"sendline", js_sendline, 1, JSTYPE_BOOLEAN, JSDOCSTR("data") + ,JSDOCSTR("send a string (AKA write) with a carriage return line feed appended") + ,317 + }, {"sendto", js_sendto, 3, JSTYPE_BOOLEAN, JSDOCSTR("data, address, port") ,JSDOCSTR("send data to a specific host (IP address or host name) and port (number or service name), for UDP sockets") ,310 @@ -1884,7 +2067,7 @@ js_socket_constructor(JSContext *cx, uintN argc, jsval *arglist) jsval *argv=JS_ARGV(cx, arglist); int32 type=SOCK_STREAM; /* default = TCP */ uintN i; - private_t* p; + js_socket_private_t* p; char* protocol=NULL; obj=JS_NewObject(cx, &js_socket_class, NULL, NULL); @@ -1899,13 +2082,13 @@ js_socket_constructor(JSContext *cx, uintN argc, jsval *arglist) } } - if((p=(private_t*)malloc(sizeof(private_t)))==NULL) { + if((p=(js_socket_private_t*)malloc(sizeof(js_socket_private_t)))==NULL) { JS_ReportError(cx,"malloc failed"); if(protocol) free(protocol); return(JS_FALSE); } - memset(p,0,sizeof(private_t)); + memset(p,0,sizeof(js_socket_private_t)); if((p->sock=open_socket(type,protocol))==INVALID_SOCKET) { JS_ReportError(cx,"open_socket failed with error %d",ERROR_VALUE); @@ -1959,7 +2142,7 @@ JSObject* DLLCALL js_CreateSocketClass(JSContext* cx, JSObject* parent) JSObject* DLLCALL js_CreateSocketObject(JSContext* cx, JSObject* parent, char *name, SOCKET sock) { JSObject* obj; - private_t* p; + js_socket_private_t* p; int type=SOCK_STREAM; socklen_t len; @@ -1975,9 +2158,9 @@ JSObject* DLLCALL js_CreateSocketObject(JSContext* cx, JSObject* parent, char *n if(!js_DefineSocketOptionsArray(cx, obj, type)) return(NULL); - if((p=(private_t*)malloc(sizeof(private_t)))==NULL) + if((p=(js_socket_private_t*)malloc(sizeof(js_socket_private_t)))==NULL) return(NULL); - memset(p,0,sizeof(private_t)); + memset(p,0,sizeof(js_socket_private_t)); p->sock = sock; p->external = TRUE; @@ -1985,8 +2168,52 @@ JSObject* DLLCALL js_CreateSocketObject(JSContext* cx, JSObject* parent, char *n p->session=-1; len=sizeof(p->remote_addr); - if(getpeername(p->sock, (struct sockaddr *)&p->remote_addr,&len)==0) + if(getpeername(p->sock, &p->remote_addr.addr,&len)==0) p->is_connected=TRUE; + else + lprintf(LOG_ERR, "Error %d calling getpeername()",errno); + + if(!JS_SetPrivate(cx, obj, p)) { + dbprintf(TRUE, p, "JS_SetPrivate failed"); + return(NULL); + } + + dbprintf(FALSE, p, "object created"); + + return(obj); +} + +JSObject* DLLCALL js_CreateSocketObjectFromSet(JSContext* cx, JSObject* parent, char *name, struct xpms_set *set) +{ + JSObject* obj; + js_socket_private_t* p; + int type=SOCK_STREAM; + socklen_t len; + + obj = JS_DefineObject(cx, parent, name, &js_socket_class, NULL + ,JSPROP_ENUMERATE|JSPROP_READONLY); + + if(obj==NULL) + return(NULL); + + if(set->sock_count < 1) + return NULL; + + len = sizeof(type); + getsockopt(set->socks[0].sock,SOL_SOCKET,SO_TYPE,(void*)&type,&len); + + if(!js_DefineSocketOptionsArray(cx, obj, type)) + return(NULL); + + if((p=(js_socket_private_t*)malloc(sizeof(js_socket_private_t)))==NULL) + return(NULL); + memset(p,0,sizeof(js_socket_private_t)); + + p->set = set; + p->sock = INVALID_SOCKET; + p->external = TRUE; + p->network_byte_order = TRUE; + p->session=-1; if(!JS_SetPrivate(cx, obj, p)) { dbprintf(TRUE, p, "JS_SetPrivate failed"); diff --git a/src/sbbs3/js_socket.h b/src/sbbs3/js_socket.h index 67f01c3143785cea706d2e62f4951e41a35bff38..75f79519492bdd4fb0271e7ee67831cf6454fead 100644 --- a/src/sbbs3/js_socket.h +++ b/src/sbbs3/js_socket.h @@ -1,13 +1,34 @@ #ifndef JS_SOCKET_H #define JS_SOCKET_H +#include <cryptlib.h> +#include "gen_defs.h" +#include "sockwrap.h" +#include "multisock.h" + +typedef struct +{ + SOCKET sock; + BOOL external; /* externally created, don't close */ + BOOL debug; + BOOL nonblocking; + BOOL is_connected; + BOOL network_byte_order; + int last_error; + int type; + union xp_sockaddr remote_addr; + CRYPT_SESSION session; + char *hostname; + struct xpms_set *set; +} js_socket_private_t; + #ifdef __cplusplus extern "C" { #endif extern int cryptInitialized; -int do_cryptInit(void); +DLLEXPORT int DLLCALL do_cryptInit(void); #ifdef __cplusplus } diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c index 109ea5945d6099e074b227f9a49ed03392d58980..1c1484b40dd8b40318bfa09a6bf204e8d0c3c4f1 100644 --- a/src/sbbs3/js_system.c +++ b/src/sbbs3/js_system.c @@ -1206,7 +1206,7 @@ js_hacklog(JSContext *cx, uintN argc, jsval *arglist) char* user=NULL; char* text=NULL; char* host=NULL; - SOCKADDR_IN addr; + union xp_sockaddr addr; scfg_t* cfg; jsrefcount rc; BOOL ret; @@ -1220,10 +1220,10 @@ js_hacklog(JSContext *cx, uintN argc, jsval *arglist) for(i=0;i<argc;i++) { if(JSVAL_IS_NUMBER(argv[i])) { JS_ValueToInt32(cx,argv[i],&i32); - if(addr.sin_addr.s_addr==0) - addr.sin_addr.s_addr=i32; + if(addr.in.sin_addr.s_addr==0) + addr.in.sin_addr.s_addr=i32; else - addr.sin_port=(ushort)i32; + addr.in.sin_port=(ushort)i32; continue; } if(!JSVAL_IS_STRING(argv[i])) @@ -1550,7 +1550,7 @@ js_new_user(JSContext *cx, uintN argc, jsval *arglist) if(client!=NULL) { SAFECOPY(user.modem,client->protocol); SAFECOPY(user.comp,client->host); - SAFECOPY(user.note,client->addr); + SAFECOPY(user.ipaddr,client->addr); } user.sex=' '; diff --git a/src/sbbs3/js_user.c b/src/sbbs3/js_user.c index 3636f4a18ed7800c35997705bd71e1d19da3ab50..33ed9a38371d5f4217b6c2f85c04d0727ce9141c 100644 --- a/src/sbbs3/js_user.c +++ b/src/sbbs3/js_user.c @@ -56,6 +56,7 @@ enum { ,USER_PROP_NAME ,USER_PROP_HANDLE ,USER_PROP_NOTE + ,USER_PROP_IPADDR ,USER_PROP_COMP ,USER_PROP_COMMENT ,USER_PROP_NETMAIL @@ -173,6 +174,9 @@ static JSBool js_user_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) case USER_PROP_NOTE: s=p->user->note; break; + case USER_PROP_IPADDR: + s=p->user->ipaddr; + break; case USER_PROP_COMP: s=p->user->comp; break; @@ -465,6 +469,10 @@ static JSBool js_user_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, SAFECOPY(p->user->note,str); putuserrec(scfg,p->user->number,U_NOTE,LEN_NOTE,str); break; + case USER_PROP_IPADDR: + SAFECOPY(p->user->ipaddr,str); + putuserrec(scfg,p->user->number,U_IPADDR,LEN_IPADDR,str); + break; case USER_PROP_COMP: SAFECOPY(p->user->comp,str); putuserrec(scfg,p->user->number,U_COMP,LEN_COMP,str); @@ -733,7 +741,7 @@ static jsSyncPropertySpec js_user_properties[] = { { "alias" ,USER_PROP_ALIAS ,USER_PROP_FLAGS, 310}, { "name" ,USER_PROP_NAME ,USER_PROP_FLAGS, 310}, { "handle" ,USER_PROP_HANDLE ,USER_PROP_FLAGS, 310}, - { "ip_address" ,USER_PROP_NOTE ,USER_PROP_FLAGS, 310}, + { "ip_address" ,USER_PROP_IPADDR ,USER_PROP_FLAGS, 310}, { "note" ,USER_PROP_NOTE ,USER_PROP_FLAGS, 310}, { "host_name" ,USER_PROP_COMP ,USER_PROP_FLAGS, 310}, { "computer" ,USER_PROP_COMP ,USER_PROP_FLAGS, 310}, @@ -776,7 +784,7 @@ static char* user_prop_desc[] = { ,"real name" ,"chat handle" ,"IP address last logged on from" - ,"AKA ip_address" + ,"Sysop note (AKA ip_address on 3.16 and before)" ,"host name last logged on from" ,"AKA host_name" ,"sysop's comment" diff --git a/src/sbbs3/logfile.cpp b/src/sbbs3/logfile.cpp index 8845dab0be5e4686517b6d4744ba0d91dfb96e39..eee3810cecbbdfde14276666281097eb3f3e928b 100644 --- a/src/sbbs3/logfile.cpp +++ b/src/sbbs3/logfile.cpp @@ -37,12 +37,13 @@ #include "sbbs.h" -extern "C" BOOL DLLCALL hacklog(scfg_t* cfg, char* prot, char* user, char* text, char* host, SOCKADDR_IN* addr) +extern "C" BOOL DLLCALL hacklog(scfg_t* cfg, char* prot, char* user, char* text, char* host, union xp_sockaddr* addr) { char hdr[1024]; char tstr[64]; char fname[MAX_PATH+1]; int file; + char ip[INET6_ADDRSTRLEN]; time32_t now=time32(NULL); sprintf(fname,"%shack.log",cfg->logs_dir); @@ -50,13 +51,14 @@ extern "C" BOOL DLLCALL hacklog(scfg_t* cfg, char* prot, char* user, char* text, if((file=sopen(fname,O_CREAT|O_RDWR|O_BINARY|O_APPEND,SH_DENYWR,DEFFILEMODE))==-1) return(FALSE); + inet_addrtop(addr, ip, sizeof(ip)); sprintf(hdr,"SUSPECTED %s HACK ATTEMPT for user '%s' on %.24s\r\nUsing port %u at %s [%s]\r\nDetails: " ,prot ,user ,timestr(cfg,now,tstr) - ,addr->sin_port + ,inet_addrport(addr) ,host - ,inet_ntoa(addr->sin_addr) + ,ip ); write(file,hdr,strlen(hdr)); write(file,text,strlen(text)); diff --git a/src/sbbs3/login.cpp b/src/sbbs3/login.cpp index 5f96eca1ee17c70a4c401c620cfadfa4363903da..62526b6d94fdeedba7e7dfd9164252519e703a18 100644 --- a/src/sbbs3/login.cpp +++ b/src/sbbs3/login.cpp @@ -152,7 +152,7 @@ void sbbs_t::badlogin(char* user, char* passwd) ::hacklog(&cfg, reason, user, passwd, client_name, &client_addr); if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) filter_ip(&cfg, connection, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS" - ,client_name, inet_ntoa(client_addr.sin_addr), user, /* fname: */NULL); + ,client_name, client_ipaddr, user, /* fname: */NULL); mswait(startup->login_attempt_delay); } diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp index cabab50eb9611d20c649f0e20a5246fe0e15c287..d8f10f21c6c235bd72c61b5381ab082c948bcb11 100644 --- a/src/sbbs3/logon.cpp +++ b/src/sbbs3/logon.cpp @@ -418,11 +418,11 @@ bool sbbs_t::logon() errormsg(WHERE,ERR_OPEN,str,O_RDWR|O_CREAT|O_APPEND); return(false); } - getuserrec(&cfg,useron.number,U_NOTE,LEN_NOTE,useron.note); + getuserrec(&cfg,useron.number,U_IPADDR,LEN_IPADDR,useron.ipaddr); getuserrec(&cfg,useron.number,U_LOCATION,LEN_LOCATION,useron.location); sprintf(str,text[LastFewCallersFmt],cfg.node_num ,totallogons,useron.alias - ,cfg.sys_misc&SM_LISTLOC ? useron.location : useron.note + ,cfg.sys_misc&SM_LISTLOC ? useron.location : useron.ipaddr ,tm.tm_hour,tm.tm_min ,connection,useron.ltoday > 999 ? 999 : useron.ltoday); write(file,str,strlen(str)); diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c index 1ecb66bfb20ff8145e457abd152ad0052f79e08c..d3639c77b6fd52245432309b5590321c1eefd3bf 100644 --- a/src/sbbs3/mailsrvr.c +++ b/src/sbbs3/mailsrvr.c @@ -58,6 +58,7 @@ #include "xpendian.h" #include "js_rtpool.h" #include "js_request.h" +#include "multisock.h" /* Constants */ static const char* server_name="Synchronet Mail Server"; @@ -86,9 +87,8 @@ static char* badrsp_err = "%s replied with:\r\n\"%s\"\r\n" static mail_startup_t* startup=NULL; static scfg_t scfg; -static SOCKET server_socket=INVALID_SOCKET; -static SOCKET submission_socket=INVALID_SOCKET; -static SOCKET pop3_socket=INVALID_SOCKET; +static struct xpms_set *mail_set=NULL; +static BOOL terminated=FALSE; static protected_uint32_t active_clients; static protected_uint32_t thread_count; static volatile int active_sendmail=0; @@ -137,7 +137,8 @@ struct mailproc { typedef struct { SOCKET socket; - SOCKADDR_IN client_addr; + union xp_sockaddr client_addr; + socklen_t client_addr_len; } smtp_t,pop3_t; static int lprintf(int level, const char *fmt, ...) @@ -229,26 +230,26 @@ static int32_t thread_down(void) return count; } -SOCKET mail_open_socket(int type, const char* protocol) +void mail_open_socket(SOCKET sock, void* cb_protocol) { + char *protocol=(char *)cb_protocol; char error[256]; char section[128]; - SOCKET sock; - sock=socket(AF_INET, type, IPPROTO_IP); - if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) + if(startup!=NULL && startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - if(sock!=INVALID_SOCKET) { - SAFEPRINTF(section,"mail|%s",protocol); - if(set_socket_options(&scfg, sock, section, error, sizeof(error))) - lprintf(LOG_ERR,"%04d !ERROR %s",sock,error); + SAFEPRINTF(section,"mail|%s",protocol); + if(set_socket_options(&scfg, sock, section, error, sizeof(error))) + lprintf(LOG_ERR,"%04d !ERROR %s",sock,error); - stats.sockets++; -#if 0 /*def _DEBUG */ - lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",sock,stats.sockets); -#endif - } - return(sock); + stats.sockets++; +} + +void mail_close_socket_cb(SOCKET sock, void* cb_protocol) +{ + if(startup!=NULL && startup->socket_open!=NULL) + startup->socket_open(startup->cbdata,FALSE); + stats.sockets--; } int mail_close_socket(SOCKET sock) @@ -383,7 +384,7 @@ static int sockreadline(SOCKET socket, char* buf, int len) while(rd<len-1) { - if(server_socket==INVALID_SOCKET || terminate_server) { + if(terminated || terminate_server) { lprintf(LOG_WARNING,"%04d !ABORTING sockreadline",socket); return(-1); } @@ -724,9 +725,10 @@ static u_long resolve_ip(char *inaddr) /* A successful login from the same host resets the counter. */ /****************************************************************************/ -static void badlogin(SOCKET sock, const char* prot, const char* resp, char* user, char* passwd, char* host, SOCKADDR_IN* addr) +static void badlogin(SOCKET sock, const char* prot, const char* resp, char* user, char* passwd, char* host, union xp_sockaddr* addr) { char reason[128]; + char ip[INET6_ADDRSTRLEN]; ulong count; if(addr!=NULL) { @@ -734,9 +736,10 @@ static void badlogin(SOCKET sock, const char* prot, const char* resp, char* user count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd); if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold) hacklog(&scfg, reason, user, passwd, host, addr); + inet_addrtop(addr, ip, sizeof(ip)); if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) filter_ip(&scfg, (char*)prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS" - ,host, inet_ntoa(addr->sin_addr), user, /* fname: */NULL); + ,host, ip, user, /* fname: */NULL); } mswait(startup->login_attempt_delay); @@ -749,7 +752,7 @@ static void pop3_thread(void* arg) char str[128]; char buf[512]; char host_name[128]; - char host_ip[64]; + char host_ip[INET6_ADDRSTRLEN]; char username[128]; char password[128]; char challenge[256]; @@ -768,7 +771,6 @@ static void pop3_thread(void* arg) long msgnum; ulong bytes; SOCKET socket; - HOSTENT* host; smb_t smb; smbmsg_t msg; user_t user; @@ -791,22 +793,14 @@ static void pop3_thread(void* arg) PlaySound(startup->pop3_sound, NULL, SND_ASYNC|SND_FILENAME); #endif - SAFECOPY(host_ip,inet_ntoa(pop3.client_addr.sin_addr)); + inet_addrtop(&pop3.client_addr, host_ip, sizeof(host_ip)); if(startup->options&MAIL_OPT_DEBUG_POP3) lprintf(LOG_INFO,"%04d POP3 connection accepted from: %s port %u" - ,socket, host_ip, ntohs(pop3.client_addr.sin_port)); + ,socket, host_ip, inet_addrport(&pop3.client_addr)); - if(startup->options&MAIL_OPT_NO_HOST_LOOKUP) - host=NULL; - else - host=gethostbyaddr((char *)&pop3.client_addr.sin_addr - ,sizeof(pop3.client_addr.sin_addr),AF_INET); - - if(host!=NULL && host->h_name!=NULL) - SAFECOPY(host_name,host->h_name); - else - strcpy(host_name,"<no name>"); + if(getnameinfo(&pop3.client_addr.addr, pop3.client_addr_len, host_name, sizeof(host_name), NULL, 0, (startup->options&MAIL_OPT_NO_HOST_LOOKUP)?NI_NUMERICHOST:0)!=0) + SAFECOPY(host_name, "<no name>"); if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP) && (startup->options&MAIL_OPT_DEBUG_POP3)) lprintf(LOG_INFO,"%04d POP3 Hostname: %s", socket, host_name); @@ -837,7 +831,7 @@ static void pop3_thread(void* arg) client.time=time32(NULL); SAFECOPY(client.addr,host_ip); SAFECOPY(client.host,host_name); - client.port=ntohs(pop3.client_addr.sin_port); + client.port=inet_addrport(&pop3.client_addr); client.protocol="POP3"; client.user="<unknown>"; client_on(socket,&client,FALSE /* update */); @@ -848,7 +842,7 @@ static void pop3_thread(void* arg) if(startup->login_attempt_throttle && (login_attempts=loginAttempts(startup->login_attempt_list, &pop3.client_addr)) > 1) { lprintf(LOG_DEBUG,"%04d POP3 Throttling suspicious connection from: %s (%u login attempts)" - ,socket, inet_ntoa(pop3.client_addr.sin_addr), login_attempts); + ,socket, host_ip, login_attempts); mswait(login_attempts*startup->login_attempt_throttle); } @@ -956,7 +950,7 @@ static void pop3_thread(void* arg) loginSuccess(startup->login_attempt_list, &pop3.client_addr); putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name); - putuserrec(&scfg,user.number,U_NOTE,LEN_NOTE,host_ip); + putuserrec(&scfg,user.number,U_IPADDR,LEN_IPADDR,host_ip); /* Update client display */ client.user=user.alias; @@ -1336,10 +1330,10 @@ static void pop3_thread(void* arg) if(activity) { if(user.number) lprintf(LOG_INFO,"%04d POP3 %s logged out from port %u on %s [%s]" - ,socket, user.alias, ntohs(pop3.client_addr.sin_port), host_name, host_ip); + ,socket, user.alias, inet_addrport(&pop3.client_addr), host_name, host_ip); else lprintf(LOG_INFO,"%04d POP3 client disconnected from port %u on %s [%s]" - ,socket, ntohs(pop3.client_addr.sin_port), host_name, host_ip); + ,socket, inet_addrport(&pop3.client_addr), host_name, host_ip); } status(STATUS_WFC); @@ -1366,22 +1360,69 @@ static void pop3_thread(void* arg) mail_close_socket(socket); } -static ulong rblchk(SOCKET sock, DWORD mail_addr_n, const char* rbl_addr) +static ulong rblchk(SOCKET sock, union xp_sockaddr *addr, const char* rbl_addr) { char name[256]; DWORD mail_addr; HOSTENT* host; struct in_addr dnsbl_result; - - mail_addr=ntohl(mail_addr_n); - safe_snprintf(name,sizeof(name),"%ld.%ld.%ld.%ld.%.128s" - ,mail_addr&0xff - ,(mail_addr>>8)&0xff - ,(mail_addr>>16)&0xff - ,(mail_addr>>24)&0xff - ,rbl_addr - ); - + unsigned char *addr6; + + switch(addr->addr.sa_family) { + case AF_INET: + mail_addr=ntohl(addr->in.sin_addr.s_addr); + safe_snprintf(name,sizeof(name),"%ld.%ld.%ld.%ld.%.128s" + ,mail_addr&0xff + ,(mail_addr>>8)&0xff + ,(mail_addr>>16)&0xff + ,(mail_addr>>24)&0xff + ,rbl_addr + ); + break; + case AF_INET6: + addr6 = (unsigned char *)&addr->in6.sin6_addr; + safe_snprintf(name,sizeof(name),"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x." + "%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x." + "%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x." + "%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%.128s" + ,addr6[15]&0x0f + ,addr6[15]>>4 + ,addr6[14]&0x0f + ,addr6[14]>>4 + ,addr6[13]&0x0f + ,addr6[13]>>4 + ,addr6[12]&0x0f + ,addr6[12]>>4 + ,addr6[11]&0x0f + ,addr6[11]>>4 + ,addr6[10]&0x0f + ,addr6[10]>>4 + ,addr6[9]&0x0f + ,addr6[9]>>4 + ,addr6[8]&0x0f + ,addr6[8]>>4 + ,addr6[7]&0x0f + ,addr6[7]>>4 + ,addr6[6]&0x0f + ,addr6[6]>>4 + ,addr6[5]&0x0f + ,addr6[5]>>4 + ,addr6[4]&0x0f + ,addr6[4]>>4 + ,addr6[3]&0x0f + ,addr6[3]>>4 + ,addr6[2]&0x0f + ,addr6[2]>>4 + ,addr6[1]&0x0f + ,addr6[1]>>4 + ,addr6[0]&0x0f + ,addr6[0]>>4 + ,rbl_addr + ); + break; + default: + return 0; + } lprintf(LOG_DEBUG,"%04d SMTP DNSBL Query: %s",sock,name); if((host=gethostbyname(name))==NULL) @@ -1394,7 +1435,7 @@ static ulong rblchk(SOCKET sock, DWORD mail_addr_n, const char* rbl_addr) return(dnsbl_result.s_addr); } -static ulong dns_blacklisted(SOCKET sock, IN_ADDR addr, char* host_name, char* list, char* dnsbl_ip) +static ulong dns_blacklisted(SOCKET sock, union xp_sockaddr *addr, char* host_name, char* list, char* dnsbl_ip) { char fname[MAX_PATH+1]; char str[256]; @@ -1402,9 +1443,11 @@ static ulong dns_blacklisted(SOCKET sock, IN_ADDR addr, char* host_name, char* l char* tp; FILE* fp; ulong found=0; + char ip[INET6_ADDRSTRLEN]; SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir); - if(findstr(inet_ntoa(addr),fname)) + inet_addrtop(addr, ip, sizeof(ip)); + if(findstr(ip,fname)) return(FALSE); if(findstr(host_name,fname)) return(FALSE); @@ -1430,11 +1473,11 @@ static ulong dns_blacklisted(SOCKET sock, IN_ADDR addr, char* host_name, char* l FIND_WHITESPACE(tp); *tp=0; - found = rblchk(sock, addr.s_addr, p); + found = rblchk(sock, addr, p); } fclose(fp); if(found) - strcpy(dnsbl_ip, inet_ntoa(addr)); + strcpy(dnsbl_ip, ip); return(found); } @@ -2036,17 +2079,17 @@ static int parse_header_field(char* buf, smbmsg_t* msg, ushort* type) static int chk_received_hdr(SOCKET socket,const char *buf,IN_ADDR *dnsbl_result, char *dnsbl, char *dnsbl_ip) { char host_name[128]; - IN_ADDR check_addr; char *fromstr; char ip[16]; char *p; char *p2; char *last; + union xp_sockaddr addr; + struct addrinfo ai,*res; - fromstr=(char *)malloc(strlen(buf)+1); + fromstr=strdup(buf); if(fromstr==NULL) return(0); - strcpy(fromstr,buf); strlwr(fromstr); do { p=strstr(fromstr,"from "); @@ -2067,11 +2110,28 @@ static int chk_received_hdr(SOCKET socket,const char *buf,IN_ADDR *dnsbl_result, p=strtok_r(NULL,"]",&last); if(p==NULL) break; - strncpy(ip,p,16); - ip[15]=0; - check_addr.s_addr = inet_addr(ip); - lprintf(LOG_DEBUG,"%04d SMTP DNSBL checking received header address %s [%s]",socket,host_name,ip); - if((dnsbl_result->s_addr=dns_blacklisted(socket,check_addr,host_name,dnsbl,dnsbl_ip))!=0) + if(strnicmp("IPv6:", p, 5)) { + p+=5; + SKIP_WHITESPACE(p); + memset(&ai, 0, sizeof(ai)); + ai.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV|AI_PASSIVE; + if(getaddrinfo(p, NULL, &ai, &res)!=0) + break; + if(res->ai_family == AF_INET6) + memcpy(&addr, res->ai_addr, res->ai_addrlen); + else + break; + freeaddrinfo(res); + } + else { + strncpy(ip,p,16); + ip[15]=0; + addr.in.sin_family=AF_INET; + addr.in.sin_addr.s_addr=inet_addr(ip); + lprintf(LOG_DEBUG,"%04d SMTP DNSBL checking received header address %s [%s]",socket,host_name,ip); + } + + if((dnsbl_result->s_addr=dns_blacklisted(socket,&addr,host_name,dnsbl,dnsbl_ip))!=0) lprintf(LOG_NOTICE,"%04d SMTP BLACKLISTED SERVER on %s: %s [%s] = %s" ,socket, dnsbl, host_name, ip, inet_ntoa(*dnsbl_result)); } while(0); @@ -2241,9 +2301,10 @@ static void smtp_thread(void* arg) char spam_block[MAX_PATH+1]; char spam_block_exempt[MAX_PATH+1]; char host_name[128]; - char host_ip[64]; + char host_ip[INET6_ADDRSTRLEN]; + char server_ip[INET6_ADDRSTRLEN]; char dnsbl[256]; - char dnsbl_ip[64]; + char dnsbl_ip[INET6_ADDRSTRLEN]; char* telegram_buf; char* msgbuf; char challenge[256]; @@ -2287,7 +2348,6 @@ static void smtp_thread(void* arg) char session_id[MAX_PATH+1]; FILE* spy=NULL; SOCKET socket; - HOSTENT* host; int smb_error; smb_t smb; smb_t spam; @@ -2298,7 +2358,7 @@ static void smtp_thread(void* arg) node_t node; client_t client; smtp_t smtp=*(smtp_t*)arg; - SOCKADDR_IN server_addr; + union xp_sockaddr server_addr; IN_ADDR dnsbl_result; BOOL* mailproc_to_match; int mailproc_match; @@ -2348,14 +2408,14 @@ static void smtp_thread(void* arg) #endif addr_len=sizeof(server_addr); - if((i=getsockname(socket, (struct sockaddr *)&server_addr,&addr_len))!=0) { + if((i=getsockname(socket, &server_addr.addr, &addr_len))!=0) { lprintf(LOG_CRIT,"%04d !SMTP ERROR %d (%d) getting address/port" ,socket, i, ERROR_VALUE); sockprintf(socket,sys_error); mail_close_socket(socket); thread_down(); return; - } + } if((mailproc_to_match=malloc(sizeof(BOOL)*mailproc_count))==NULL) { lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating memory for mailproc_to_match", socket); @@ -2372,21 +2432,13 @@ static void smtp_thread(void* arg) memset(&user,0,sizeof(user)); memset(&relay_user,0,sizeof(relay_user)); - SAFECOPY(host_ip,inet_ntoa(smtp.client_addr.sin_addr)); + inet_addrtop(&smtp.client_addr,host_ip,sizeof(host_ip)); lprintf(LOG_INFO,"%04d SMTP Connection accepted on port %u from: %s port %u" - ,socket, BE_INT16(server_addr.sin_port), host_ip, ntohs(smtp.client_addr.sin_port)); + ,socket, inet_addrport(&server_addr), host_ip, inet_addrport(&smtp.client_addr)); - if(startup->options&MAIL_OPT_NO_HOST_LOOKUP) - host=NULL; - else - host=gethostbyaddr ((char *)&smtp.client_addr.sin_addr - ,sizeof(smtp.client_addr.sin_addr),AF_INET); - - if(host!=NULL && host->h_name!=NULL) - SAFECOPY(host_name,host->h_name); - else - strcpy(host_name,"<no name>"); + if(getnameinfo(&smtp.client_addr.addr, smtp.client_addr_len, host_name, sizeof(host_name), NULL, 0, (startup->options&MAIL_OPT_NO_HOST_LOOKUP)?NI_NUMERICHOST:0)!=0) + SAFECOPY(host_name, "<no name>"); if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) lprintf(LOG_INFO,"%04d SMTP Hostname: %s", socket, host_name); @@ -2400,8 +2452,9 @@ static void smtp_thread(void* arg) SAFEPRINTF(spam_block,"%sspamblock.cfg",scfg.ctrl_dir); SAFEPRINTF(spam_block_exempt,"%sspamblock_exempt.cfg",scfg.ctrl_dir); - if(smtp.client_addr.sin_addr.s_addr==server_addr.sin_addr.s_addr - || smtp.client_addr.sin_addr.s_addr==htonl(IPv4_LOCALHOST)) { + inet_addrtop(&server_addr,server_ip,sizeof(server_ip)); + + if(strcmp(server_ip, host_ip)==0) { /* local connection */ dnsbl_result.s_addr=0; } else { @@ -2432,7 +2485,7 @@ static void smtp_thread(void* arg) } /* SPAM Filters (mail-abuse.org) */ - dnsbl_result.s_addr = dns_blacklisted(socket,smtp.client_addr.sin_addr,host_name,dnsbl,dnsbl_ip); + dnsbl_result.s_addr = dns_blacklisted(socket,&smtp.client_addr,host_name,dnsbl,dnsbl_ip); if(dnsbl_result.s_addr) { lprintf(LOG_NOTICE,"%04d SMTP BLACKLISTED SERVER on %s: %s [%s] = %s" ,socket, dnsbl, host_name, dnsbl_ip, inet_ntoa(dnsbl_result)); @@ -2502,7 +2555,7 @@ static void smtp_thread(void* arg) client.time=time32(NULL); SAFECOPY(client.addr,host_ip); SAFECOPY(client.host,host_name); - client.port=ntohs(smtp.client_addr.sin_port); + client.port=inet_addrport(&smtp.client_addr); client.protocol="SMTP"; client.user="<unknown>"; client_on(socket,&client,FALSE /* update */); @@ -2513,7 +2566,7 @@ static void smtp_thread(void* arg) if(startup->login_attempt_throttle && (login_attempts=loginAttempts(startup->login_attempt_list, &smtp.client_addr)) > 1) { lprintf(LOG_DEBUG,"%04d SMTP Throttling suspicious connection from: %s (%u login attempts)" - ,socket, inet_ntoa(smtp.client_addr.sin_addr), login_attempts); + ,socket, host_ip, login_attempts); mswait(login_attempts*startup->login_attempt_throttle); } @@ -2573,11 +2626,33 @@ static void smtp_thread(void* arg) if(telegram==TRUE) { /* Telegram */ const char* head="\1n\1h\1cInstant Message\1n from \1h\1y"; const char* tail="\1n:\r\n\1h"; + struct addrinfo ai; + struct addrinfo *res,*cur; + BOOL matched=FALSE; + rewind(msgtxt); length=filelength(fileno(msgtxt)); p=strchr(sender_addr,'@'); - if(p==NULL || resolve_ip(p+1)!=smtp.client_addr.sin_addr.s_addr) + memset(&ai, 0, sizeof(ai)); + ai.ai_flags = AI_PASSIVE; + ai.ai_family = smtp.client_addr.addr.sa_family; + if(getaddrinfo(p+1, NULL, &ai, &res) != 0) + p=NULL; + else { + for(cur=res; cur; cur=cur->ai_next) { + char cur_ip[INET6_ADDRSTRLEN]; + + if(inet_addrtop((void *)cur->ai_addr, cur_ip, sizeof(cur_ip))) { + if(strcmp(host_ip, cur_ip)==0) + matched=TRUE; + } + } + freeaddrinfo(res); + if(!matched) + p=NULL; + } + if(p==NULL) /* Append real IP and hostname if different */ safe_snprintf(str,sizeof(str),"%s%s\r\n\1w[\1n%s\1h] (\1n%s\1h)%s" ,head,sender_addr,host_ip,host_name,tail); @@ -3097,12 +3172,16 @@ static void smtp_thread(void* arg) } snprintf(hdrfield,sizeof(hdrfield), - "from %s (%s [%s])\r\n" - " by %s [%s] (%s %s-%s) with %s\r\n" + "from %s (%s [%s%s])\r\n" + " by %s [%s%s] (%s %s-%s) with %s\r\n" " for %s; %s\r\n" " (envelope-from %s)" - ,host_name,hello_name,host_ip - ,startup->host_name,inet_ntoa(server_addr.sin_addr) + ,host_name,hello_name + ,smtp.client_addr.addr.sa_family==AF_INET6?"IPv6: ":"" + ,host_ip + ,startup->host_name + ,server_addr.addr.sa_family==AF_INET6?"IPv6: ":"" + ,server_ip ,server_name ,revision,PLATFORM_DESC ,esmtp ? "ESMTP" : "SMTP" @@ -3722,9 +3801,8 @@ static void smtp_thread(void* arg) tp=strrchr(p,'@'); if(cmd==SMTP_CMD_MAIL && tp!=NULL) { - /* RELAY */ - dest_port=server_addr.sin_port; + dest_port=inet_addrport(&server_addr); SAFECOPY(dest_host,tp+1); cp=strrchr(dest_host,':'); if(cp!=NULL) { @@ -3735,12 +3813,12 @@ static void smtp_thread(void* arg) if((stricmp(dest_host,scfg.sys_inetaddr)!=0 && stricmp(dest_host,startup->host_name)!=0 && findstr(dest_host,domain_list)==FALSE) - || dest_port!=server_addr.sin_port) { + || dest_port!=inet_addrport(&server_addr)) { SAFEPRINTF(relay_list,"%srelay.cfg",scfg.ctrl_dir); if(relay_user.number==0 /* not authenticated, search for IP */ && startup->options&MAIL_OPT_SMTP_AUTH_VIA_IP) { - relay_user.number=userdatdupe(&scfg, 0, U_NOTE, LEN_NOTE, host_ip, /* del */FALSE, /* next */FALSE); + relay_user.number=userdatdupe(&scfg, 0, U_IPADDR, LEN_IPADDR, host_ip, /* del */FALSE, /* next */FALSE); if(relay_user.number) { getuserdat(&scfg,&relay_user); if(relay_user.laston < time(NULL)-(60*60)) /* logon in past hour? */ @@ -4235,6 +4313,7 @@ void get_dns_server(char* dns_server, size_t len) } } +/* TODO: IPv6 etc. */ #ifdef __BORLANDC__ #pragma argsused #endif @@ -4273,6 +4352,7 @@ static void sendmail_thread(void* arg) SOCKET sock=INVALID_SOCKET; SOCKADDR_IN addr; SOCKADDR_IN server_addr; + char server_ip[INET6_ADDRSTRLEN]; time_t last_scan=0; smb_t smb; smbmsg_t msg; @@ -4295,7 +4375,7 @@ static void sendmail_thread(void* arg) listInit(&failed_server_list, /* flags: */0); - while(server_socket!=INVALID_SOCKET && !terminate_sendmail) { + while((!terminated) && !terminate_sendmail) { if(startup->options&MAIL_OPT_NO_SENDMAIL) { sem_trywait_block(&sendmail_wakeup_sem,1000); @@ -4349,7 +4429,7 @@ static void sendmail_thread(void* arg) if(active_sendmail!=0) active_sendmail=0, update_clients(); - if(server_socket==INVALID_SOCKET || terminate_sendmail) /* server stopped */ + if(terminated || terminate_sendmail) /* server stopped */ break; if(sock!=INVALID_SOCKET) { @@ -4449,14 +4529,15 @@ static void sendmail_thread(void* arg) || findstr(p,domain_list)) { /* This is a local message... no need to send to remote */ port = startup->smtp_port; - if(startup->interface_addr==0) + /* TODO: IPv6 */ + if(startup->outgoing4.s_addr==0) server="127.0.0.1"; else { SAFEPRINTF4(numeric_ip, "%u.%u.%u.%u" - , startup->interface_addr >> 24 - , (startup->interface_addr >> 16) & 0xff - , (startup->interface_addr >> 8) & 0xff - , startup->interface_addr & 0xff); + , startup->outgoing4.s_addr >> 24 + , (startup->outgoing4.s_addr >> 16) & 0xff + , (startup->outgoing4.s_addr >> 8) & 0xff + , startup->outgoing4.s_addr & 0xff); server = numeric_ip; } sending_locally=TRUE; @@ -4498,11 +4579,12 @@ static void sendmail_thread(void* arg) if(!port) port=IPPORT_SMTP; - if((sock=mail_open_socket(SOCK_STREAM,"smtp|sendmail"))==INVALID_SOCKET) { + if((sock=socket(AF_INET, SOCK_STREAM, IPPROTO_IP))==INVALID_SOCKET) { remove_msg_intransit(&smb,&msg); lprintf(LOG_ERR,"0000 !SEND ERROR %d opening socket", ERROR_VALUE); continue; } + mail_open_socket(sock,"smtp|sendmail"); if(startup->connect_timeout) { /* Use non-blocking socket */ long nbio=1; @@ -4515,16 +4597,10 @@ static void sendmail_thread(void* arg) } memset(&addr,0,sizeof(addr)); - addr.sin_addr.s_addr = htonl(startup->interface_addr); + addr.sin_addr.s_addr = htonl(startup->outgoing4.s_addr); addr.sin_family = AF_INET; - /* Not needed. Port is zero - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); */ i=bind(sock,(struct sockaddr *)&addr, sizeof(addr)); - /* Not needed. Port is zero - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); */ if(i!=0) { remove_msg_intransit(&smb,&msg); lprintf(LOG_ERR,"%04d !SEND ERROR %d (%d) binding socket", sock, i, ERROR_VALUE); @@ -4555,13 +4631,14 @@ static void sendmail_thread(void* arg) server_addr.sin_addr.s_addr = ip_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); + inet_addrtop(&server_addr,server_ip,sizeof(server_ip)); if((node=listFindNode(&failed_server_list,&server_addr,sizeof(server_addr))) != NULL) { lprintf(LOG_INFO,"%04d SEND skipping failed SMTP server: Error %d connecting to port %u on %s [%s]" ,sock ,node->tag - ,ntohs(server_addr.sin_port) - ,server,inet_ntoa(server_addr.sin_addr)); + ,inet_addrport(&server_addr) + ,server,server_ip); SAFEPRINTF2(err,"Error %d connecting to SMTP server: %s" ,node->tag, server); continue; @@ -4570,14 +4647,14 @@ static void sendmail_thread(void* arg) if((server==mx || server==mx2) && ((ip_addr&0xff)==127 || ip_addr==0)) { SAFEPRINTF2(err,"Bad IP address (%s) for MX server: %s" - ,inet_ntoa(server_addr.sin_addr),server); + ,server_ip,server); continue; } lprintf(LOG_INFO,"%04d SEND connecting to port %u on %s [%s]" ,sock - ,ntohs(server_addr.sin_port) - ,server,inet_ntoa(server_addr.sin_addr)); + ,inet_addrport(&server_addr) + ,server,server_ip); if((i=nonblocking_connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr), startup->connect_timeout))!=0) { lprintf(LOG_WARNING,"%04d !SEND ERROR %d connecting to SMTP server: %s" ,sock @@ -4804,7 +4881,7 @@ static void sendmail_thread(void* arg) void DLLCALL mail_terminate(void) { - lprintf(LOG_INFO,"%04d Mail Server terminate",server_socket); + lprintf(LOG_INFO,"Mail Server terminate"); terminate_server=TRUE; } @@ -4834,20 +4911,9 @@ static void cleanup(int code) FREE_AND_NULL(mailproc_list); } - if(server_socket!=INVALID_SOCKET) { - mail_close_socket(server_socket); - server_socket=INVALID_SOCKET; - } - - if(submission_socket!=INVALID_SOCKET) { - mail_close_socket(submission_socket); - submission_socket=INVALID_SOCKET; - } - - if(pop3_socket!=INVALID_SOCKET) { - mail_close_socket(pop3_socket); - pop3_socket=INVALID_SOCKET; - } + xpms_destroy(mail_set, mail_close_socket_cb, NULL); + mail_set=NULL; + terminated=TRUE; update_clients(); /* active_clients is destroyed below */ @@ -4921,23 +4987,21 @@ void DLLCALL mail_server(void* arg) char str[256]; char error[256]; char compiler[32]; - SOCKADDR_IN server_addr; - SOCKADDR_IN client_addr; + union xp_sockaddr client_addr; socklen_t client_addr_len; + char host_ip[INET6_ADDRSTRLEN]; SOCKET client_socket; int i; - int result; ulong l; time_t t; time_t start; time_t initialized=0; - fd_set socket_set; - SOCKET high_socket_set; pop3_t* pop3; smtp_t* smtp; - struct timeval tv; FILE* fp; str_list_t sec_list; + struct in_addr iaddr; + void *cbdata; mail_ver(); @@ -4982,7 +5046,8 @@ void DLLCALL mail_server(void* arg) js_server_props.version_detail=mail_ver(); js_server_props.clients=&active_clients.value; js_server_props.options=&startup->options; - js_server_props.interface_addr=&startup->interface_addr; + /* TODO: IPv6 */ + js_server_props.interface_addr=&startup->outgoing4; uptime=0; memset(&stats,0,sizeof(stats)); @@ -5122,152 +5187,28 @@ void DLLCALL mail_server(void* arg) update_clients(); /* open a socket and wait for a client */ - - server_socket = mail_open_socket(SOCK_STREAM,"smtp"); - - if(server_socket == INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE); + mail_set = xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf); + if(mail_set == NULL) { + lprintf(LOG_CRIT,"!ERROR creating mail server socket set", ERROR_VALUE); cleanup(1); return; } - - lprintf(LOG_DEBUG,"%04d SMTP socket opened",server_socket); - - /*****************************/ - /* Listen for incoming calls */ - /*****************************/ - memset(&server_addr, 0, sizeof(server_addr)); - - server_addr.sin_addr.s_addr = htonl(startup->interface_addr); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(startup->smtp_port); - - if(startup->smtp_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result = retry_bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) - ,startup->bind_retry_count,startup->bind_retry_delay,"SMTP Server",lprintf); - if(startup->smtp_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); - } - if(result != 0) { - lprintf(LOG_CRIT,"%04d %s",server_socket, BIND_FAILURE_HELP); - cleanup(1); - return; - } - result = listen(server_socket, 1); - - if(result != 0) { - lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on SMTP socket" - ,server_socket, result, ERROR_VALUE); - cleanup(1); - return; - } - lprintf(LOG_INFO,"%04d SMTP Server listening on port %u" - ,server_socket, startup->smtp_port); + if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->smtp_port, "SMTP Server", mail_open_socket, startup->seteuid, "smtp")) + lprintf(LOG_INFO,"SMTP No extra interfaces listening"); + lprintf(LOG_INFO,"SMTP Server listening"); if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT) { - - submission_socket = mail_open_socket(SOCK_STREAM,"submission"); - - if(submission_socket == INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE); - cleanup(1); - return; - } - - lprintf(LOG_DEBUG,"%04d SUBMISSION socket opened",submission_socket); - - /*****************************/ - /* Listen for incoming calls */ - /*****************************/ - memset(&server_addr, 0, sizeof(server_addr)); - - server_addr.sin_addr.s_addr = htonl(startup->interface_addr); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(startup->submission_port); - - if(startup->submission_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result = retry_bind(submission_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) - ,startup->bind_retry_count,startup->bind_retry_delay,"SMTP Submission Agent",lprintf); - if(startup->submission_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); - } - if(result != 0) { - lprintf(LOG_CRIT,"%04d %s",submission_socket, BIND_FAILURE_HELP); - cleanup(1); - return; - } - - result = listen(submission_socket, 1); - - if(result != 0) { - lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on SUBMISSION socket" - ,submission_socket, result, ERROR_VALUE); - cleanup(1); - return; - } - - lprintf(LOG_INFO,"%04d SUBMISSION Server listening on port %u" - ,submission_socket, startup->submission_port); + if(xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->submission_port, "SMTP Submission Agent", mail_open_socket, startup->seteuid, "submission")) + lprintf(LOG_INFO,"SUBMISSION Server listening on port %u" + ,startup->submission_port); } if(startup->options&MAIL_OPT_ALLOW_POP3) { /* open a socket and wait for a client */ - - pop3_socket = mail_open_socket(SOCK_STREAM,"pop3"); - - if(pop3_socket == INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d opening POP3 socket", ERROR_VALUE); - cleanup(1); - return; - } - - lprintf(LOG_DEBUG,"%04d POP3 socket opened",pop3_socket); - - /*****************************/ - /* Listen for incoming calls */ - /*****************************/ - memset(&server_addr, 0, sizeof(server_addr)); - - server_addr.sin_addr.s_addr = htonl(startup->interface_addr); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(startup->pop3_port); - - if(startup->pop3_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result = retry_bind(pop3_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) - ,startup->bind_retry_count,startup->bind_retry_delay,"POP3 Server",lprintf); - if(startup->pop3_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - if(result != 0) { - lprintf(LOG_CRIT,"%04d %s",pop3_socket,BIND_FAILURE_HELP); - cleanup(1); - return; - } - - result = listen(pop3_socket, 1); - - if(result != 0) { - lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on POP3 socket" - ,pop3_socket, result, ERROR_VALUE); - cleanup(1); - return; - } - - lprintf(LOG_INFO,"%04d POP3 Server listening on port %u" - ,pop3_socket, startup->pop3_port); + if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->pop3_interfaces, startup->pop3_port, "POP3 Server", mail_open_socket, startup->seteuid, "pop3")) + lprintf(LOG_INFO,"SMTP No extra interfaces listening"); + lprintf(LOG_INFO,"POP3 Server listening"); } sem_init(&sendmail_wakeup_sem,0,0); @@ -5295,28 +5236,26 @@ void DLLCALL mail_server(void* arg) if(startup->started!=NULL) startup->started(startup->cbdata); - lprintf(LOG_INFO,"%04d Mail Server thread started",server_socket); + lprintf(LOG_INFO,"Mail Server thread started"); - while(server_socket!=INVALID_SOCKET && !terminate_server) { + while(!terminated && !terminate_server) { if(protected_uint32_value(thread_count) <= 1+sendmail_running) { if(!(startup->options&MAIL_OPT_NO_RECYCLE)) { if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) { - lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected" - ,server_socket,p); + lprintf(LOG_INFO,"Recycle semaphore file (%s) detected",p); break; } if(startup->recycle_now==TRUE) { - lprintf(LOG_NOTICE,"%04d Recycle semaphore signaled", server_socket); + lprintf(LOG_NOTICE,"Recycle semaphore signaled"); startup->recycle_now=FALSE; break; } } if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL - && lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected" - ,server_socket,p)) + && lprintf(LOG_INFO,"Shutdown semaphore file (%s) detected",p)) || (startup->shutdown_now==TRUE - && lprintf(LOG_INFO,"%04d Shutdown semaphore signaled",server_socket))) { + && lprintf(LOG_INFO,"Shutdown semaphore signaled"))) { startup->shutdown_now=FALSE; terminate_server=TRUE; break; @@ -5324,145 +5263,23 @@ void DLLCALL mail_server(void* arg) } /* now wait for connection */ - - FD_ZERO(&socket_set); - FD_SET(server_socket,&socket_set); - high_socket_set=server_socket+1; - if(startup->options&MAIL_OPT_ALLOW_POP3 - && pop3_socket!=INVALID_SOCKET) { - FD_SET(pop3_socket,&socket_set); - if(pop3_socket+1>high_socket_set) - high_socket_set=pop3_socket+1; - } - if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT - && submission_socket!=INVALID_SOCKET) { - FD_SET(submission_socket,&socket_set); - if(submission_socket+1>high_socket_set) - high_socket_set=submission_socket+1; - } - - tv.tv_sec=startup->sem_chk_freq; - tv.tv_usec=0; - - if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) { - if(i==0) - continue; - if(ERROR_VALUE==EINTR) - lprintf(LOG_DEBUG,"%04d Mail Server listening interrupted",server_socket); - else if(ERROR_VALUE == ENOTSOCK) - lprintf(LOG_NOTICE,"%04d Mail Server sockets closed",server_socket); - else - lprintf(LOG_WARNING,"%04d !ERROR %d selecting sockets",server_socket,ERROR_VALUE); - continue; - } - - if(server_socket!=INVALID_SOCKET && !terminate_server - && (FD_ISSET(server_socket,&socket_set) - || (startup->options&MAIL_OPT_USE_SUBMISSION_PORT - && FD_ISSET(submission_socket,&socket_set)))) { - - client_addr_len = sizeof(client_addr); - client_socket = accept( - FD_ISSET(server_socket,&socket_set) ? server_socket:submission_socket - ,(struct sockaddr *)&client_addr - ,&client_addr_len); - - if(client_socket == INVALID_SOCKET) - { -#if 0 /* is this necessary still? */ - if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL) { - lprintf(LOG_NOTICE,"%04d SMTP socket closed while listening" - ,server_socket); - break; - } -#endif - lprintf(LOG_WARNING,"%04d SMTP !ERROR %d accepting connection" - ,FD_ISSET(server_socket,&socket_set) ? server_socket:submission_socket - ,ERROR_VALUE); -#ifdef _WIN32 - if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */ - break; -#endif - continue; - } + client_addr_len = sizeof(client_addr); + client_socket = xpms_accept(mail_set,&client_addr, &client_addr_len, startup->sem_chk_freq*1000, &cbdata); + if(client_socket != INVALID_SOCKET) { if(startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); stats.sockets++; - if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) { + inet_addrtop(&client_addr, host_ip, sizeof(host_ip)); + if(trashcan(&scfg,host_ip,"ip-silent")) { mail_close_socket(client_socket); stats.connections_ignored++; continue; } if(protected_uint32_value(active_clients)>=startup->max_clients) { - lprintf(LOG_WARNING,"%04d SMTP !MAXIMUM CLIENTS (%u) reached, access denied (%u total)" - ,client_socket, startup->max_clients, ++stats.connections_refused); - sockprintf(client_socket,"421 Maximum active clients reached, please try again later."); - mswait(3000); - mail_close_socket(client_socket); - continue; - } - - l=1; - - if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) { - lprintf(LOG_CRIT,"%04d SMTP !ERROR %d (%d) disabling blocking on socket" - ,client_socket, i, ERROR_VALUE); - mail_close_socket(client_socket); - continue; - } - - if((smtp=malloc(sizeof(smtp_t)))==NULL) { - lprintf(LOG_CRIT,"%04d SMTP !ERROR allocating %u bytes of memory for smtp_t" - ,client_socket, sizeof(smtp_t)); - mail_close_socket(client_socket); - continue; - } - - smtp->socket=client_socket; - smtp->client_addr=client_addr; - protected_uint32_adjust(&thread_count,1); - _beginthread(smtp_thread, 0, smtp); - stats.connections_served++; - } - - if(pop3_socket!=INVALID_SOCKET - && FD_ISSET(pop3_socket,&socket_set)) { - - client_addr_len = sizeof(client_addr); - client_socket = accept(pop3_socket, (struct sockaddr *)&client_addr - ,&client_addr_len); - - if(client_socket == INVALID_SOCKET) - { -#if 0 /* is this necessary still? */ - if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL) { - lprintf(LOG_NOTICE,"%04d POP3 socket closed while listening",pop3_socket); - break; - } -#endif - lprintf(LOG_WARNING,"%04d POP3 !ERROR %d accepting connection" - ,pop3_socket, ERROR_VALUE); -#ifdef _WIN32 - if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */ - break; -#endif - continue; - } - if(startup->socket_open!=NULL) - startup->socket_open(startup->cbdata,TRUE); - stats.sockets++; - - if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) { - mail_close_socket(client_socket); - stats.connections_ignored++; - continue; - } - - if(protected_uint32_value(active_clients)>=startup->max_clients) { - lprintf(LOG_WARNING,"%04d POP3 !MAXIMUM CLIENTS (%u) reached, access denied (%u total)" - ,client_socket, startup->max_clients, ++stats.connections_refused); + lprintf(LOG_WARNING,"%04d %s !MAXIMUM CLIENTS (%u) reached, access denied (%u total)" + ,client_socket, (char *)cbdata, startup->max_clients, ++stats.connections_refused); sockprintf(client_socket,"-ERR Maximum active clients reached, please try again later."); mswait(3000); mail_close_socket(client_socket); @@ -5472,39 +5289,57 @@ void DLLCALL mail_server(void* arg) l=1; if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) { - lprintf(LOG_CRIT,"%04d POP3 !ERROR %d (%d) disabling blocking on socket" - ,client_socket, i, ERROR_VALUE); + lprintf(LOG_CRIT,"%04d %s !ERROR %d (%d) disabling blocking on socket" + ,client_socket, (char *)cbdata, i, ERROR_VALUE); sockprintf(client_socket,"-ERR System error, please try again later."); mswait(3000); mail_close_socket(client_socket); continue; } - if((pop3=malloc(sizeof(pop3_t)))==NULL) { - lprintf(LOG_CRIT,"%04d POP3 !ERROR allocating %u bytes of memory for pop3_t" - ,client_socket,sizeof(pop3_t)); - sockprintf(client_socket,"-ERR System error, please try again later."); - mswait(3000); - mail_close_socket(client_socket); - continue; + if(strcmp((char *)cbdata, "pop3")) { /* Not POP3 */ + if((smtp=malloc(sizeof(smtp_t)))==NULL) { + lprintf(LOG_CRIT,"%04d SMTP !ERROR allocating %u bytes of memory for smtp_t" + ,client_socket, sizeof(smtp_t)); + mail_close_socket(client_socket); + continue; + } + + smtp->socket=client_socket; + memcpy(&smtp->client_addr, &client_addr, client_addr_len); + smtp->client_addr_len=client_addr_len; + protected_uint32_adjust(&thread_count,1); + _beginthread(smtp_thread, 0, smtp); + stats.connections_served++; } + else { + if((pop3=malloc(sizeof(pop3_t)))==NULL) { + lprintf(LOG_CRIT,"%04d POP3 !ERROR allocating %u bytes of memory for pop3_t" + ,client_socket,sizeof(pop3_t)); + sockprintf(client_socket,"-ERR System error, please try again later."); + mswait(3000); + mail_close_socket(client_socket); + continue; + } - pop3->socket=client_socket; - pop3->client_addr=client_addr; - protected_uint32_adjust(&thread_count,1); - _beginthread(pop3_thread, 0, pop3); - stats.connections_served++; + pop3->socket=client_socket; + memcpy(&pop3->client_addr, &client_addr, client_addr_len); + pop3->client_addr_len=client_addr_len; + protected_uint32_adjust(&thread_count,1); + _beginthread(pop3_thread, 0, pop3); + stats.connections_served++; + } } } if(protected_uint32_value(active_clients)) { - lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..." - ,server_socket, protected_uint32_value(active_clients)); + lprintf(LOG_DEBUG,"Waiting for %d active clients to disconnect..." + , protected_uint32_value(active_clients)); start=time(NULL); while(protected_uint32_value(active_clients)) { if(startup->max_inactivity && time(NULL)-start>startup->max_inactivity) { - lprintf(LOG_WARNING,"%04d !TIMEOUT (%u seconds) waiting for %d active clients" - ,server_socket, startup->max_inactivity, protected_uint32_value(active_clients)); + lprintf(LOG_WARNING,"!TIMEOUT (%u seconds) waiting for %d active clients" + , startup->max_inactivity, protected_uint32_value(active_clients)); break; } mswait(100); @@ -5517,13 +5352,11 @@ void DLLCALL mail_server(void* arg) mswait(100); } if(sendmail_running) { - lprintf(LOG_DEBUG,"%04d Waiting for SendMail thread to terminate..." - ,server_socket); + lprintf(LOG_DEBUG,"Waiting for SendMail thread to terminate..."); start=time(NULL); while(sendmail_running) { if(time(NULL)-start>TIMEOUT_THREAD_WAIT) { - lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for sendmail thread to terminate" - ,server_socket); + lprintf(LOG_WARNING,"!TIMEOUT waiting for sendmail thread to terminate"); break; } mswait(500); diff --git a/src/sbbs3/mailsrvr.h b/src/sbbs3/mailsrvr.h index 7853bee7396c514b74e70aa85f40c9234dd39f61..5e53521d4141cfa5a8454ce1c1e564dfad07ba5f 100644 --- a/src/sbbs3/mailsrvr.h +++ b/src/sbbs3/mailsrvr.h @@ -61,7 +61,10 @@ typedef struct { WORD max_recipients; #define MAIL_DEFAULT_MAX_RECIPIENTS 100 WORD sem_chk_freq; /* semaphore file checking frequency (in seconds) */ - DWORD interface_addr; + struct in_addr outgoing4; + struct in6_addr outgoing6; + str_list_t interfaces; + str_list_t pop3_interfaces; DWORD options; /* See MAIL_OPT definitions */ DWORD max_msg_size; /* Max msg size in bytes (0=unlimited) */ #define MAIL_DEFAULT_MAX_MSG_SIZE (20*1024*1024) /* 20MB */ @@ -130,7 +133,7 @@ typedef struct { static struct init_field mail_init_fields[] = { OFFSET_AND_SIZE(mail_startup_t,smtp_port) ,OFFSET_AND_SIZE(mail_startup_t,pop3_port) - ,OFFSET_AND_SIZE(mail_startup_t,interface_addr) + ,OFFSET_AND_SIZE(mail_startup_t,interfaces) ,OFFSET_AND_SIZE(mail_startup_t,ctrl_dir) ,{ 0,0 } /* terminator */ }; diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp index 69744fbe28d12ca1b25f314cfaeac2d82a307f50..1e51ca479291171bd02b658f74a0f149f969fa8f 100644 --- a/src/sbbs3/main.cpp +++ b/src/sbbs3/main.cpp @@ -42,13 +42,11 @@ #include "js_rtpool.h" #include "js_request.h" #include "js_socket.h" +#include <multisock.h> +#include <limits.h> // HOST_NAME_MAX #ifdef __unix__ #include <sys/un.h> - #ifndef SUN_LEN - #define SUN_LEN(su) \ - (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) - #endif #endif //#define SBBS_TELNET_ENVIRON_SUPPORT 1 @@ -90,11 +88,7 @@ SOCKET spy_socket[MAX_NODES]; SOCKET uspy_socket[MAX_NODES]; /* UNIX domain spy sockets */ #endif SOCKET node_socket[MAX_NODES]; -static SOCKET telnet_socket=INVALID_SOCKET; -static SOCKET rlogin_socket=INVALID_SOCKET; -#ifdef USE_CRYPTLIB -static SOCKET ssh_socket=INVALID_SOCKET; -#endif +struct xpms_set *ts_set; static sbbs_t* sbbs=NULL; static scfg_t scfg; static char * text[TOTAL_TEXT]; @@ -202,6 +196,30 @@ int eprintf(int level, const char *fmt, ...) return(startup->event_lputs(startup->event_cbdata,level,sbuf)); } +struct main_sock_cb_data { + bbs_startup_t *startup; + const char *protocol; +}; + +void sock_cb(SOCKET sock, void *cb_data) +{ + char error_str[256]; + struct main_sock_cb_data *cb=(struct main_sock_cb_data *)cb_data; + + if(cb->startup && cb->startup->socket_open) + cb->startup->socket_open(cb->startup->cbdata, TRUE); + if(set_socket_options(&scfg, sock, cb->protocol, error_str, sizeof(error_str))) + lprintf(LOG_ERR,"%04d !ERROR %s",sock,error_str); +} + +void sock_close_cb(SOCKET sock, void *cb_data) +{ + bbs_startup_t *su=(bbs_startup_t *)cb_data; + + if(su && su->socket_open) + su->socket_open(su->cbdata, FALSE); +} + SOCKET open_socket(int type, const char* protocol) { SOCKET sock; @@ -216,12 +234,12 @@ SOCKET open_socket(int type, const char* protocol) return(sock); } -SOCKET accept_socket(SOCKET s, SOCKADDR* addr, socklen_t* addrlen) +SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen) { SOCKET sock; - sock=accept(s,addr,addrlen); - if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) + sock=accept(s,&addr->addr,addrlen); + if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); return(sock); @@ -243,7 +261,7 @@ int close_socket(SOCKET sock) return(result); } - +/* TODO: IPv6 */ u_long resolve_ip(char *addr) { HOSTENT* host; @@ -303,7 +321,7 @@ DLLEXPORT void DLLCALL sbbs_srand() rd=read(rf, &seed, sizeof(seed)); close(rf); } - if(rd != sizeof(seed)) + if (rd != sizeof(seed)) #endif seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId(); @@ -441,7 +459,7 @@ DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec } JSBool -DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append) +DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs) { int i; jsuint len=0; @@ -450,21 +468,33 @@ DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *fun JSObject* method; JSObject* method_array; JSString* js_str; + size_t str_len=0; + char *str=NULL; /* Return existing method_list array if it's already been created */ - if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) + if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) { method_array=JSVAL_TO_OBJECT(val); - else + // If the first item is already in the list, don't do anything. + if(!JS_GetArrayLength(cx, method_array, &len)) + return(JS_FALSE); + for(i=0; i<len; i++) { + if(JS_GetElement(cx, method_array, i, &val)!=JS_TRUE || val == JSVAL_VOID) + continue; + JS_GetProperty(cx, JSVAL_TO_OBJECT(val), "name", &val); + JSVALUE_TO_RASTRING(cx, val, str, &str_len, NULL); + if(str==NULL) + continue; + if(strcmp(str, funcs[0].name)==0) + return(JS_TRUE); + } + } + else { if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) return(JS_FALSE); - - if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array) - , NULL, NULL, 0)) - return(JS_FALSE); - - if(append) - if(!JS_GetArrayLength(cx, method_array, &len)) + if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array) + , NULL, NULL, 0)) return(JS_FALSE); + } for(i=0;funcs[i].name;i++) { @@ -540,7 +570,7 @@ DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertyS } if(funcs) { - if(!js_DefineSyncMethods(cx, obj, funcs, 0)) + if(!js_DefineSyncMethods(cx, obj, funcs)) ret=JS_FALSE; } @@ -569,7 +599,7 @@ DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec JSBool -DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append) +DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs) { uint i; @@ -1154,6 +1184,10 @@ bool sbbs_t::js_init(ulong* stack_frame) break; rooted=true; +#ifdef BUILD_JSDOCS + js_CreateUifcObject(js_cx, js_glob); +#endif + /* BBS Object */ if(js_CreateBbsObject(js_cx, js_glob)==NULL) break; @@ -2003,7 +2037,7 @@ void output_thread(void* arg) char node[128]; char stats[128]; BYTE buf[IO_THREAD_BUF_SIZE]; - int i; + int i=0; // Assignment to silence Valgrind ulong avail; ulong total_sent=0; ulong total_pkts=0; @@ -2562,7 +2596,7 @@ void event_thread(void* arg) sbbs->console&=~CON_L_ECHO; sbbs->online=FALSE; remove(str); - } + } } globfree(&g); } @@ -2898,7 +2932,7 @@ void event_thread(void* arg) //**************************************************************************** -sbbs_t::sbbs_t(ushort node_num, SOCKADDR_IN addr, const char* name, SOCKET sd, +sbbs_t::sbbs_t(ushort node_num, union xp_sockaddr *addr, size_t addr_len, const char* name, SOCKET sd, scfg_t* global_cfg, char* global_text[], client_t* client_info) { char nodestr[32]; @@ -2949,7 +2983,7 @@ sbbs_t::sbbs_t(ushort node_num, SOCKADDR_IN addr, const char* name, SOCKET sd, memset(&client,0,sizeof(client)); else memcpy(&client,client_info,sizeof(client)); - client_addr = addr; + client_addr.store = addr->store; client_socket = sd; SAFECOPY(client_name, name); client_socket_dup=INVALID_SOCKET; @@ -3086,7 +3120,7 @@ bool sbbs_t::init() uint i,j,k,l; node_t node; socklen_t addr_len; - SOCKADDR_IN addr; + union xp_sockaddr addr; RingBufInit(&inbuf, IO_THREAD_BUF_SIZE); if(cfg.node_num>0) @@ -3114,15 +3148,16 @@ bool sbbs_t::init() #endif addr_len=sizeof(addr); - if((result=getsockname(client_socket, (struct sockaddr *)&addr,&addr_len))!=0) { + if((result=getsockname(client_socket, &addr.addr, &addr_len))!=0) { lprintf(LOG_ERR,"Node %d !ERROR %d (%d) getting address/port" ,cfg.node_num, result, ERROR_VALUE); return(false); } + inet_addrtop(&addr, local_addr, sizeof(local_addr)); + inet_addrtop(&client_addr, client_ipaddr, sizeof(client_ipaddr)); lprintf(LOG_INFO,"Node %d attached to local interface %s port %u" - ,cfg.node_num, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + ,cfg.node_num, local_addr, inet_addrport(&addr)); - local_addr=addr.sin_addr.s_addr; } if((comspec=os_cmdshell())==NULL) { @@ -3553,7 +3588,7 @@ void sbbs_t::spymsg(const char* msg) return; SAFEPRINTF4(str,"\r\n\r\n*** Spy Message ***\r\nNode %d: %s [%s]\r\n*** %s ***\r\n\r\n" - ,cfg.node_num,client_name,inet_ntoa(client_addr.sin_addr),msg); + ,cfg.node_num,client_name,client_ipaddr,msg); if(startup->node_spybuf!=NULL && startup->node_spybuf[cfg.node_num-1]!=NULL) { RingBufWrite(startup->node_spybuf[cfg.node_num-1],(uchar*)str,strlen(str)); @@ -3691,6 +3726,11 @@ void sbbs_t::hangup(void) if(client_socket!=INVALID_SOCKET) { mswait(1000); /* Give socket output buffer time to flush */ client_off(client_socket); + if(ssh_mode) { + pthread_mutex_lock(&sbbs->ssh_mutex); + cryptDestroySession(sbbs->ssh_session); + pthread_mutex_unlock(&sbbs->ssh_mutex); + } close_socket(client_socket); client_socket=INVALID_SOCKET; } @@ -4006,7 +4046,7 @@ void node_thread(void* arg) if(startup->login_attempt_throttle && (login_attempts=loginAttempts(startup->login_attempt_list, &sbbs->client_addr)) > 1) { lprintf(LOG_DEBUG,"Node %d Throttling suspicious connection from: %s (%u login attempts)" - ,sbbs->cfg.node_num, inet_ntoa(sbbs->client_addr.sin_addr), login_attempts); + ,sbbs->cfg.node_num, sbbs->client_ipaddr, login_attempts); mswait(login_attempts*startup->login_attempt_throttle); } @@ -4369,25 +4409,12 @@ void DLLCALL bbs_terminate(void) terminate_server=true; } +extern bbs_startup_t bbs_startup; static void cleanup(int code) { lputs(LOG_INFO,"Terminal Server thread terminating"); - if(telnet_socket!=INVALID_SOCKET) { - close_socket(telnet_socket); - telnet_socket=INVALID_SOCKET; - } - if(rlogin_socket!=INVALID_SOCKET) { - close_socket(rlogin_socket); - rlogin_socket=INVALID_SOCKET; - } -#ifdef USE_CRYPTLIB - if(ssh_socket!=INVALID_SOCKET) { - close_socket(ssh_socket); - ssh_socket=INVALID_SOCKET; - } -#endif - + xpms_destroy(ts_set, sock_close_cb, startup); #ifdef _WINSOCKAPI_ if(WSAInitialized && WSACleanup()!=0) @@ -4433,17 +4460,15 @@ static void cleanup(int code) void DLLCALL bbs_thread(void* arg) { - const char* host_name; + char host_name[256]; char* identity; char* p; char str[MAX_PATH+1]; char logstr[256]; - SOCKADDR_IN server_addr={0}; - SOCKADDR_IN client_addr; + union xp_sockaddr server_addr={0}; + union xp_sockaddr client_addr; socklen_t client_addr_len; SOCKET client_socket=INVALID_SOCKET; - fd_set socket_set; - SOCKET high_socket_set; int i; int file; int result; @@ -4457,12 +4482,17 @@ void DLLCALL bbs_thread(void* arg) BOOL is_client=FALSE; #ifdef __unix__ SOCKET uspy_listen_socket[MAX_NODES]; - struct sockaddr_un uspy_addr; - socklen_t uspy_addr_len; + struct main_sock_cb_data uspy_cb[MAX_NODES]={}; + union xp_sockaddr uspy_addr; #endif #ifdef USE_CRYPTLIB CRYPT_CONTEXT ssh_context; #endif + struct main_sock_cb_data telnet_cb; + struct main_sock_cb_data ssh_cb; + struct main_sock_cb_data rlogin_cb; + void *ts_cb; + struct in_addr iaddr; if(startup==NULL) { sbbs_beep(100,500); @@ -4498,7 +4528,8 @@ void DLLCALL bbs_thread(void* arg) js_server_props.version_detail=bbs_ver(); js_server_props.clients=&node_threads_running.value; js_server_props.options=&startup->options; - js_server_props.interface_addr=&startup->telnet_interface; + /* TODO: IPv6 */ + js_server_props.interface_addr=(uint32_t *)&startup->outgoing4.s_addr; uptime=0; served=0; @@ -4653,97 +4684,27 @@ void DLLCALL bbs_thread(void* arg) startup->node_inbuf=node_inbuf; /* open a socket and wait for a client */ - - telnet_socket = open_socket(SOCK_STREAM, "telnet"); - - if(telnet_socket == INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d creating Telnet socket", ERROR_VALUE); + ts_set = xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf); + if(ts_set==NULL) { + lprintf(LOG_CRIT,"!ERROR %d creating Terminal Server socket set", ERROR_VALUE); cleanup(1); return; } + telnet_cb.protocol="telnet"; + telnet_cb.startup=startup; - lprintf(LOG_DEBUG,"Telnet socket %d opened",telnet_socket); - - /*****************************/ - /* Listen for incoming calls */ - /*****************************/ - memset(&server_addr, 0, sizeof(server_addr)); - - server_addr.sin_addr.s_addr = htonl(startup->telnet_interface); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(startup->telnet_port); - - if(startup->telnet_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result = retry_bind(telnet_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) - ,startup->bind_retry_count,startup->bind_retry_delay,"Telnet Server",lprintf); - if(startup->telnet_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); - } - if(result != 0) { - lprintf(LOG_CRIT,"%s",BIND_FAILURE_HELP); - cleanup(1); - return; - } - - result = listen(telnet_socket, 1); + /* + * Add interfaces + */ + xpms_add_list(ts_set, PF_UNSPEC, SOCK_STREAM, 0, startup->telnet_interfaces, startup->telnet_port, "Telnet Server", sock_cb, startup->seteuid, &telnet_cb); - if(result != 0) { - lprintf(LOG_CRIT,"!ERROR %d (%d) listening on Telnet socket", result, ERROR_VALUE); - cleanup(1); - return; - } lprintf(LOG_INFO,"Telnet Server listening on port %u",startup->telnet_port); if(startup->options&BBS_OPT_ALLOW_RLOGIN) { - /* open a socket and wait for a client */ - - rlogin_socket = open_socket(SOCK_STREAM, "rlogin"); - - if(rlogin_socket == INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d creating RLogin socket", ERROR_VALUE); - cleanup(1); - return; - } - - lprintf(LOG_DEBUG,"RLogin socket %d opened",rlogin_socket); - - /*****************************/ - /* Listen for incoming calls */ - /*****************************/ - memset(&server_addr, 0, sizeof(server_addr)); - - server_addr.sin_addr.s_addr = htonl(startup->rlogin_interface); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(startup->rlogin_port); - - if(startup->rlogin_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result = retry_bind(rlogin_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) - ,startup->bind_retry_count,startup->bind_retry_delay,"RLogin Server",lprintf); - if(startup->rlogin_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); - } - if(result != 0) { - lprintf(LOG_CRIT,"%s",BIND_FAILURE_HELP); - cleanup(1); - return; - } - - result = listen(rlogin_socket, 1); - - if(result != 0) { - lprintf(LOG_CRIT,"!ERROR %d (%d) listening on RLogin socket", result, ERROR_VALUE); - cleanup(1); - return; - } + rlogin_cb.protocol="rlogin"; + rlogin_cb.startup=startup; + xpms_add_list(ts_set, PF_UNSPEC, SOCK_STREAM, 0, startup->rlogin_interfaces, startup->rlogin_port, "RLogin Server", sock_cb, startup->seteuid, &rlogin_cb); lprintf(LOG_INFO,"RLogin Server listening on port %u",startup->rlogin_port); } @@ -4761,15 +4722,10 @@ void DLLCALL bbs_thread(void* arg) /* Get the private key... first try loading it from a file... */ SAFEPRINTF2(str,"%s%s",scfg.ctrl_dir,"cryptlib.key"); if(cryptStatusOK(cryptKeysetOpen(&ssh_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_NONE))) { - if(cryptStatusOK(cryptGetPrivateKey(ssh_keyset, &ssh_context, CRYPT_KEYID_NAME, "ssh_server", scfg.sys_pass))) - loaded_key=true; - cryptKeysetClose(ssh_keyset); - /* Failed to load the key... delete the keyfile and create a new one */ - if(!loaded_key) - remove(str); + if(!cryptStatusOK(cryptGetPrivateKey(ssh_keyset, &ssh_context, CRYPT_KEYID_NAME, "ssh_server", scfg.sys_pass))) + goto NO_SSH; } - - if(!loaded_key) { + else { /* Couldn't do that... create a new context and use the key from there... */ if(!cryptStatusOK(i=cryptCreateContext(&ssh_context, CRYPT_UNUSED, CRYPT_ALGO_RSA))) { @@ -4793,56 +4749,16 @@ void DLLCALL bbs_thread(void* arg) } /* open a socket and wait for a client */ - - ssh_socket = open_socket(SOCK_STREAM, "ssh"); - - if(ssh_socket == INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d creating SSH socket", ERROR_VALUE); - cleanup(1); - return; - } - - lprintf(LOG_DEBUG,"SSH socket %d opened",ssh_socket); - - /*****************************/ - /* Listen for incoming calls */ - /*****************************/ - memset(&server_addr, 0, sizeof(server_addr)); - - server_addr.sin_addr.s_addr = htonl(startup->ssh_interface); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(startup->ssh_port); - - if(startup->ssh_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result = retry_bind(ssh_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) - ,startup->bind_retry_count,startup->bind_retry_delay,"SSH Server",lprintf); - if(startup->ssh_port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); - } - if(result != 0) { - lprintf(LOG_CRIT,"%s",BIND_FAILURE_HELP); - cleanup(1); - return; - } - - result = listen(ssh_socket, 1); - - if(result != 0) { - lprintf(LOG_CRIT,"!ERROR %d (%d) listening on SSH socket", result, ERROR_VALUE); - cleanup(1); - return; - } + ssh_cb.protocol="ssh"; + ssh_cb.startup=startup; + xpms_add_list(ts_set, PF_UNSPEC, SOCK_STREAM, 0, startup->ssh_interfaces, startup->ssh_port, "SSH Server", sock_cb, startup->seteuid, &ssh_cb); lprintf(LOG_INFO,"SSH Server listening on port %u",startup->ssh_port); } NO_SSH: #endif - sbbs = new sbbs_t(0, server_addr - ,"Terminal Server", telnet_socket, &scfg, text, NULL); + sbbs = new sbbs_t(0, &server_addr, sizeof(server_addr) + ,"Terminal Server", ts_set->socks[0].sock, &scfg, text, NULL); if(sbbs->init()==false) { lputs(LOG_CRIT,"!BBS initialization failed"); cleanup(1); @@ -4852,7 +4768,7 @@ NO_SSH: _beginthread(output_thread, 0, sbbs); if(!(startup->options&BBS_OPT_NO_EVENTS)) { - events = new sbbs_t(0, server_addr + events = new sbbs_t(0, &server_addr, sizeof(server_addr) ,"BBS Events", INVALID_SOCKET, &scfg, text, NULL); if(events->init()==false) { lputs(LOG_CRIT,"!Events initialization failed"); @@ -4922,44 +4838,19 @@ NO_SSH: #ifdef __unix__ // unix-domain spy sockets for(i=first_node;i<=last_node && !(startup->options&BBS_OPT_NO_SPY_SOCKETS);i++) { - if((uspy_listen_socket[i-1]=socket(PF_UNIX,SOCK_STREAM,0))==INVALID_SOCKET) - lprintf(LOG_ERR,"Node %d !ERROR %d creating local spy socket" - , i, errno); - else { - lprintf(LOG_INFO,"Node %d local spy using socket %d", i, uspy_listen_socket[i-1]); - if(startup!=NULL && startup->socket_open!=NULL) - startup->socket_open(startup->cbdata,TRUE); - } - - uspy_addr.sun_family=AF_UNIX; - if((unsigned int)snprintf(str,sizeof(uspy_addr.sun_path), + if((unsigned int)snprintf(str,sizeof(uspy_addr.un.sun_path), "%slocalspy%d.sock", startup->temp_dir, i) - >=sizeof(uspy_addr.sun_path)) - uspy_listen_socket[i-1]=INVALID_SOCKET; + >=sizeof(uspy_addr.un.sun_path)) { + lprintf(LOG_ERR,"Node %d !ERROR local spy socket path \"%slocalspy%d.sock\" too long." + , i, startup->temp_dir, i); + continue; + } else { - strcpy(uspy_addr.sun_path,str); - if(fexist(str)) - unlink(str); - } - if(uspy_listen_socket[i-1]!=INVALID_SOCKET) { - uspy_addr_len=SUN_LEN(&uspy_addr); - if(bind(uspy_listen_socket[i-1], (struct sockaddr *) &uspy_addr, uspy_addr_len)) { - lprintf(LOG_ERR,"Node %d !ERROR %d binding local spy socket %d to %s" - , i, errno, uspy_listen_socket[i-1], uspy_addr.sun_path); - close_socket(uspy_listen_socket[i-1]); - uspy_listen_socket[i-1]=INVALID_SOCKET; - continue; - } - lprintf(LOG_INFO,"Node %d local spy socket %d bound to %s" - , i, uspy_listen_socket[i-1], uspy_addr.sun_path); - if(listen(uspy_listen_socket[i-1],1)) { - lprintf(LOG_ERR,"Node %d !ERROR %d listening local spy socket %d" - ,i, errno, uspy_listen_socket[i-1]); - close_socket(uspy_listen_socket[i-1]); - uspy_listen_socket[i-1]=INVALID_SOCKET; - continue; - } - uspy_addr_len=sizeof(uspy_addr); + if(xpms_add(ts_set, PF_UNIX, SOCK_STREAM, 0, str, 0, "Spy Socket", sock_cb, NULL, &uspy_cb[i-1])) + lprintf(LOG_INFO,"Node %d local spy using socket %s", i, str); + else + lprintf(LOG_ERR,"Node %d !ERROR %d creating local spy socket %s" + , i, errno, str); } } #endif // __unix__ (unix-domain spy sockets) @@ -4992,22 +4883,21 @@ NO_SSH: break; if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) { - lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected" - ,telnet_socket,p); + lprintf(LOG_INFO,"Recycle semaphore file (%s) detected" + ,p); break; } if(startup->recycle_now==TRUE) { - lprintf(LOG_INFO,"%04d Recycle semaphore signaled",telnet_socket); + lprintf(LOG_INFO,"Recycle semaphore signaled"); startup->recycle_now=FALSE; break; } } if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL - && lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected" - ,telnet_socket,p)) + && lprintf(LOG_INFO,"Shutdown semaphore file (%s) detected" + ,p)) || (startup->shutdown_now==TRUE - && lprintf(LOG_INFO,"%04d Shutdown semaphore signaled" - ,telnet_socket))) { + && lprintf(LOG_INFO,"Shutdown semaphore signaled"))) { startup->shutdown_now=FALSE; terminate_server=TRUE; break; @@ -5021,62 +4911,15 @@ NO_SSH: #endif /* now wait for connection */ - - FD_ZERO(&socket_set); - high_socket_set=0; - if(telnet_socket!=INVALID_SOCKET) { - FD_SET(telnet_socket,&socket_set); - high_socket_set=telnet_socket+1; - } - if(startup->options&BBS_OPT_ALLOW_RLOGIN - && rlogin_socket!=INVALID_SOCKET) { - FD_SET(rlogin_socket,&socket_set); - if(rlogin_socket+1>high_socket_set) - high_socket_set=rlogin_socket+1; - } -#ifdef USE_CRYPTLIB - if(startup->options&BBS_OPT_ALLOW_SSH - && ssh_socket!=INVALID_SOCKET) { - FD_SET(ssh_socket,&socket_set); - if(ssh_socket+1>high_socket_set) - high_socket_set=ssh_socket+1; - } -#endif -#ifdef __unix__ - for(i=first_node;i<=last_node;i++) { - if(uspy_listen_socket[i-1]!=INVALID_SOCKET) { - FD_SET(uspy_listen_socket[i-1],&socket_set); - if(uspy_listen_socket[i-1]+1>high_socket_set) - high_socket_set=uspy_listen_socket[i-1]+1; - } - if(uspy_socket[i-1]!=INVALID_SOCKET) { - FD_SET(uspy_socket[i-1],&socket_set); - if(uspy_socket[i-1]+1>high_socket_set) - high_socket_set=uspy_listen_socket[i-1]+1; - } - } -#endif - - struct timeval tv; - tv.tv_sec=startup->sem_chk_freq; - tv.tv_usec=0; - - if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) { - if(i==0) - continue; - if(ERROR_VALUE==EINTR) - lprintf(LOG_DEBUG,"Terminal Server listening interrupted"); - else if(ERROR_VALUE == ENOTSOCK) - lprintf(LOG_NOTICE,"Terminal Server sockets closed"); - else - lprintf(LOG_WARNING,"!ERROR %d selecting sockets",ERROR_VALUE); - continue; - } + client_addr_len = sizeof(client_addr); + client_socket=xpms_accept(ts_set, &client_addr + ,&client_addr_len, startup->sem_chk_freq*1000, &ts_cb); if(terminate_server) /* terminated */ break; - client_addr_len = sizeof(client_addr); + if(client_socket == INVALID_SOCKET) + continue; bool rlogin = false; #ifdef USE_CRYPTLIB @@ -5084,23 +4927,13 @@ NO_SSH: #endif is_client=FALSE; - if(telnet_socket!=INVALID_SOCKET - && FD_ISSET(telnet_socket,&socket_set)) { - client_socket = accept_socket(telnet_socket, (struct sockaddr *)&client_addr - ,&client_addr_len); + if(ts_cb == &telnet_cb) { is_client=TRUE; - } else if(rlogin_socket!=INVALID_SOCKET - && FD_ISSET(rlogin_socket,&socket_set)) { - client_socket = accept_socket(rlogin_socket, (struct sockaddr *)&client_addr - ,&client_addr_len); + } else if(ts_cb == &rlogin_cb) { rlogin = true; is_client=TRUE; #ifdef USE_CRYPTLIB - } else if(ssh_socket!=INVALID_SOCKET - && FD_ISSET(ssh_socket,&socket_set)) { - - client_socket = accept_socket(ssh_socket, (struct sockaddr *)&client_addr - ,&client_addr_len); + } else if(ts_cb == &ssh_cb) { ssh = true; is_client=TRUE; sbbs->ssh_mode=true; @@ -5108,8 +4941,7 @@ NO_SSH: } else { #ifdef __unix__ for(i=first_node;i<=last_node;i++) { - if(uspy_socket[i-1]!=INVALID_SOCKET - && FD_ISSET(uspy_socket[i-1],&socket_set)) { + if(&uspy_cb[i-1] == ts_cb) { if(node_socket[i-1]==INVALID_SOCKET) read(uspy_socket[i-1],str,sizeof(str)); if(!socket_check(uspy_socket[i-1],NULL,NULL,0)) { @@ -5118,25 +4950,17 @@ NO_SSH: uspy_socket[i-1]=INVALID_SOCKET; } } - if(uspy_listen_socket[i-1]!=INVALID_SOCKET - && FD_ISSET(uspy_listen_socket[i-1],&socket_set)) { + if(&uspy_cb[i-1] == ts_cb) { BOOL already_connected=(uspy_socket[i-1]!=INVALID_SOCKET); - SOCKET new_socket=INVALID_SOCKET; - new_socket = accept(uspy_listen_socket[i-1], (struct sockaddr *)&uspy_addr - ,&uspy_addr_len); - if(new_socket < 0) { - lprintf(LOG_ERR,"!ERROR Spy socket for node %d unable to accept()",i); - close_socket(uspy_listen_socket[i-1]); - uspy_listen_socket[i-1]=INVALID_SOCKET; - } + SOCKET new_socket=client_socket; fcntl(new_socket,F_SETFL,fcntl(new_socket,F_GETFL)|O_NONBLOCK); if(already_connected) { - lprintf(LOG_ERR,"!ERROR Spy socket %s already in use",uspy_addr.sun_path); + lprintf(LOG_ERR,"!ERROR Spy socket %s already in use",uspy_addr.un.sun_path); send(new_socket,"Spy socket already in use.\r\n",27,0); close_socket(new_socket); } else { - lprintf(LOG_ERR,"!Spy socket %s (%d) connected",uspy_addr.sun_path,new_socket); + lprintf(LOG_ERR,"!Spy socket %s (%d) connected",uspy_addr.un.sun_path,new_socket); uspy_socket[i-1]=new_socket; SAFEPRINTF(str,"Spy connection established to node %d\r\n",i); send(uspy_socket[i-1],str,strlen(str),0); @@ -5162,15 +4986,15 @@ NO_SSH: #endif lprintf(LOG_ERR,"!ERROR %d accepting connection", ERROR_VALUE); #ifdef _WIN32 - if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */ + if(WSAGetLastError()==WSAENOBUFS) { /* recycle (re-init WinSock) on this error */ break; + } #endif - SSH_END(); continue; } - char host_ip[32]; + char host_ip[INET6_ADDRSTRLEN]; - strcpy(host_ip,inet_ntoa(client_addr.sin_addr)); + inet_addrtop(&client_addr, host_ip, sizeof(host_ip)); if(trashcan(&scfg,host_ip,"ip-silent")) { SSH_END(); @@ -5185,7 +5009,7 @@ NO_SSH: #else ,rlogin ? "RLogin" : "Telnet" #endif - , host_ip, ntohs(client_addr.sin_port)); + , host_ip, inet_addrport(&client_addr)); #ifdef _WIN32 if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE)) @@ -5284,19 +5108,14 @@ NO_SSH: sbbs->bprintf("Connection from: %s\r\n", host_ip); - struct hostent* h; - if(startup->options&BBS_OPT_NO_HOST_LOOKUP) - h=NULL; - else { + if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP)) { sbbs->bprintf("Resolving hostname..."); - h=gethostbyaddr((char *)&client_addr.sin_addr - ,sizeof(client_addr.sin_addr),AF_INET); + if(getnameinfo(&client_addr.addr, client_addr_len, host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD)) + strcpy(host_name, "<no name>"); sbbs->putcom(crlf); } - if(h!=NULL && h->h_name!=NULL) - host_name=h->h_name; else - host_name="<no name>"; + strcpy(host_name, "<no name>"); if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP)) lprintf(LOG_INFO,"%04d Hostname: %s", client_socket, host_name); @@ -5315,7 +5134,7 @@ NO_SSH: if(startup->options&BBS_OPT_GET_IDENT) { sbbs->bprintf("Resolving identity..."); /* ToDo: Make ident timeout configurable */ - if(identify(&client_addr, startup->telnet_port, str, sizeof(str)-1, /* timeout: */1)) { + if(identify(&client_addr, inet_addrport(&client_addr), str, sizeof(str)-1, /* timeout: */1)) { lprintf(LOG_DEBUG,"%04d Ident Response: %s",client_socket, str); identity=strrchr(str,':'); if(identity!=NULL) { @@ -5332,7 +5151,7 @@ NO_SSH: client.time=time32(NULL); SAFECOPY(client.addr,host_ip); SAFECOPY(client.host,host_name); - client.port=ntohs(client_addr.sin_port); + client.port=inet_addrport(&client_addr); #ifdef USE_CRYPTLIB client.protocol=rlogin ? "RLogin":(ssh ? "SSH" : "Telnet"); #else @@ -5382,7 +5201,7 @@ NO_SSH: node_socket[i-1]=client_socket; - sbbs_t* new_node = new sbbs_t(i, client_addr, host_name + sbbs_t* new_node = new sbbs_t(i, &client_addr, client_addr_len, host_name ,client_socket ,&scfg, text, &client); @@ -5426,6 +5245,7 @@ NO_SSH: } #ifdef USE_CRYPTLIB if(ssh) { + /* TODO: IPv6? */ SOCKET tmp_sock; SOCKADDR_IN tmp_addr={0}; socklen_t tmp_addr_len; @@ -5545,7 +5365,7 @@ NO_PASSTHRU: if(uspy_listen_socket[i]!=INVALID_SOCKET) { close_socket(uspy_listen_socket[i]); uspy_listen_socket[i]=INVALID_SOCKET; - snprintf(str,sizeof(uspy_addr.sun_path),"%slocalspy%d.sock", startup->temp_dir, i+1); + snprintf(str,sizeof(uspy_addr.un.sun_path),"%slocalspy%d.sock", startup->temp_dir, i+1); if(fexist(str)) unlink(str); } diff --git a/src/sbbs3/newuser.cpp b/src/sbbs3/newuser.cpp index c08b5c156004bdfbd919012378fa1217e6098d37..3eed0cd80fc53f5d384590a1919d5d84016b964b 100644 --- a/src/sbbs3/newuser.cpp +++ b/src/sbbs3/newuser.cpp @@ -101,8 +101,8 @@ BOOL sbbs_t::newuser() useron.sex=' '; useron.prot=cfg.new_prot; SAFECOPY(useron.comp,client_name); /* hostname or CID name */ - SAFECOPY(useron.note,cid); /* IP address or CID number */ - if((i=userdatdupe(0,U_NOTE,LEN_NOTE,cid, /* del */true))!=0) { /* Duplicate IP address */ + SAFECOPY(useron.ipaddr,cid); /* IP address or CID number */ + if((i=userdatdupe(0,U_IPADDR,LEN_IPADDR,cid, /* del */true))!=0) { /* Duplicate IP address */ SAFEPRINTF2(useron.comment,"Warning: same IP address as user #%d %s" ,i,username(&cfg,i,str)); logline(LOG_NOTICE,"N!",useron.comment); diff --git a/src/sbbs3/objects.mk b/src/sbbs3/objects.mk index d40bdb2eea56dad46aff99308503748c932f2765..880a0ef222d5bc2ae0708d13d19703d30b586c96 100644 --- a/src/sbbs3/objects.mk +++ b/src/sbbs3/objects.mk @@ -114,7 +114,7 @@ OBJS = $(MTOBJODIR)$(DIRSEP)ansiterm$(OFILE) \ $(MTOBJODIR)$(DIRSEP)xtrn$(OFILE)\ $(MTOBJODIR)$(DIRSEP)xtrn_sec$(OFILE)\ $(MTOBJODIR)$(DIRSEP)yenc$(OFILE)\ - $(MTOBJODIR)$(DIRSEP)ver$(OFILE) + $(MTOBJODIR)$(DIRSEP)ver$(OFILE)\ # Must add new additions to MONO_OBJS too! CON_OBJS = $(MTOBJODIR)$(DIRSEP)sbbscon$(OFILE) \ @@ -133,6 +133,7 @@ MAIL_OBJS = $(MTOBJODIR)$(DIRSEP)mailsrvr$(OFILE) \ # Must add new additions to MONO_OBJS too! WEB_OBJS = $(MTOBJODIR)$(DIRSEP)websrvr$(OFILE) \ + $(MTOBJODIR)$(DIRSEP)ssl$(OFILE) \ $(MTOBJODIR)$(DIRSEP)base64$(OFILE) \ $(MTOBJODIR)$(DIRSEP)ars$(OFILE) \ $(MTOBJODIR)$(DIRSEP)ringbuf$(OFILE) @@ -150,7 +151,8 @@ MONO_OBJS = \ $(MTOBJODIR)$(DIRSEP)sbbs_ini$(OFILE) \ $(MTOBJODIR)$(DIRSEP)sbbscon$(OFILE) \ $(MTOBJODIR)$(DIRSEP)services$(OFILE) \ - $(MTOBJODIR)$(DIRSEP)websrvr$(OFILE) + $(MTOBJODIR)$(DIRSEP)websrvr$(OFILE) \ + $(MTOBJODIR)$(DIRSEP)ssl$(OFILE) BAJA_OBJS = \ $(OBJODIR)$(DIRSEP)baja$(OFILE) \ diff --git a/src/sbbs3/putnode.cpp b/src/sbbs3/putnode.cpp index 2343ed0b4032fdd453797ef9873e423ab5ab7c0b..13c2af4ea5b44d87aa9c5e3ac5a523c37328fff7 100644 --- a/src/sbbs3/putnode.cpp +++ b/src/sbbs3/putnode.cpp @@ -69,7 +69,7 @@ int sbbs_t::putnodedat(uint number, node_t* node) ,getage(&cfg,useron.birth) ,useron.sex ,useron.comp - ,useron.note + ,useron.ipaddr ,unixtodstr(&cfg,useron.firston,firston) ,node->aux&0xff ,node->connection diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h index 65e7b320b0b71f3c60ba0c841c935403c6658f7a..c21c690ec0af02d21bcb060f34f6f25a8a2d49b7 100644 --- a/src/sbbs3/sbbs.h +++ b/src/sbbs3/sbbs.h @@ -264,6 +264,7 @@ extern int thread_suid_broken; /* NPTL is no longer broken */ #include "filewrap.h" #include "datewrap.h" #include "sockwrap.h" +#include "multisock.h" #include "eventwrap.h" #include "link_list.h" #include "msg_queue.h" @@ -299,7 +300,7 @@ class sbbs_t public: - sbbs_t(ushort node_num, SOCKADDR_IN addr, const char* host_name, SOCKET + sbbs_t(ushort node_num, union xp_sockaddr *addr, size_t addr_len, const char* host_name, SOCKET ,scfg_t*, char* text[], client_t* client_info); ~sbbs_t(); @@ -311,10 +312,11 @@ public: client_t client; SOCKET client_socket; SOCKET client_socket_dup; - SOCKADDR_IN client_addr; + union xp_sockaddr client_addr; char client_name[128]; char client_ident[128]; - DWORD local_addr; + char client_ipaddr[INET6_ADDRSTRLEN]; + char local_addr[INET6_ADDRSTRLEN]; #ifdef USE_CRYPTLIB CRYPT_SESSION ssh_session; bool ssh_mode; @@ -1087,7 +1089,7 @@ extern "C" { DLLEXPORT int DLLCALL errorlog(scfg_t* cfg, const char* host, const char* text); DLLEXPORT BOOL DLLCALL hacklog(scfg_t* cfg, char* prot, char* user, char* text - ,char* host, SOCKADDR_IN* addr); + ,char* host, union xp_sockaddr* addr); DLLEXPORT BOOL DLLCALL spamlog(scfg_t* cfg, char* prot, char* action, char* reason ,char* host, char* ip_addr, char* to, char* from); @@ -1111,7 +1113,7 @@ extern "C" { typedef struct { const char* name; JSNative call; - uint8 nargs; + uint8_t nargs; int type; /* return type */ const char* args; /* arguments */ const char* desc; /* description */ @@ -1120,8 +1122,8 @@ extern "C" { typedef struct { const char *name; - int8 tinyid; - uint8 flags; + int8_t tinyid; + uint8_t flags; int ver; /* version added/modified */ } jsSyncPropertySpec; @@ -1156,12 +1158,12 @@ extern "C" { /* main.cpp */ DLLEXPORT JSBool DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char*, int ver); DLLEXPORT JSBool DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char*); - DLLEXPORT JSBool DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec*, BOOL append); + DLLEXPORT JSBool DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec*); DLLEXPORT JSBool DLLCALL js_DefineSyncProperties(JSContext* cx, JSObject* obj, jsSyncPropertySpec*); DLLEXPORT JSBool DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags); DLLEXPORT JSBool DLLCALL js_DefineConstIntegers(JSContext* cx, JSObject* obj, jsConstIntSpec*, int flags); DLLEXPORT JSBool DLLCALL js_CreateArrayOfStrings(JSContext* cx, JSObject* parent - ,const char* name, char* str[], uintN flags); + ,const char* name, char* str[], unsigned flags); /* js_server.c */ DLLEXPORT JSObject* DLLCALL js_CreateServerObject(JSContext* cx, JSObject* parent @@ -1176,7 +1178,7 @@ extern "C" { sem_t bg_sem; str_list_t exit_func; } global_private_t; - DLLEXPORT BOOL DLLCALL js_argc(JSContext *cx, uintN argc, uintN min); + DLLEXPORT BOOL DLLCALL js_argc(JSContext *cx, unsigned argc, unsigned min); DLLEXPORT BOOL DLLCALL js_CreateGlobalObject(JSContext* cx, scfg_t* cfg, jsSyncMethodSpec* methods, js_startup_t*, JSObject**); DLLEXPORT BOOL DLLCALL js_CreateCommonObjects(JSContext* cx ,scfg_t* cfg /* common */ @@ -1238,8 +1240,13 @@ extern "C" { DLLEXPORT JSObject* DLLCALL js_CreateSocketClass(JSContext* cx, JSObject* parent); DLLEXPORT JSObject* DLLCALL js_CreateSocketObject(JSContext* cx, JSObject* parent ,char *name, SOCKET sock); + DLLEXPORT JSObject* DLLCALL js_CreateSocketObjectFromSet(JSContext* cx, JSObject* parent + ,char *name, struct xpms_set *set); + DLLEXPORT void DLLCALL js_timeval(JSContext* cx, jsval val, struct timeval* tv); DLLEXPORT SOCKET DLLCALL js_socket(JSContext *cx, jsval val); + DLLEXPORT SOCKET DLLCALL js_socket_add(JSContext *cx, jsval val, fd_set *fds); + DLLEXPORT BOOL DLLCALL js_socket_isset(JSContext *cx, jsval val, fd_set *fds); /* js_queue.c */ DLLEXPORT JSObject* DLLCALL js_CreateQueueClass(JSContext* cx, JSObject* parent); @@ -1251,7 +1258,7 @@ extern "C" { DLLEXPORT JSObject* DLLCALL js_CreateFileClass(JSContext* cx, JSObject* parent); /* js_sprintf.c */ - DLLEXPORT char* DLLCALL js_sprintf(JSContext* cx, uint argn, uintN argc, jsval *argv); + DLLEXPORT char* DLLCALL js_sprintf(JSContext* cx, uint argn, unsigned argc, jsval *argv); DLLEXPORT void DLLCALL js_sprintf_free(char *); /* js_console.cpp */ @@ -1294,7 +1301,7 @@ BOOL md(char *path); int lprintf(int level, const char *fmt, ...); /* log output */ int eprintf(int level, const char *fmt, ...); /* event log */ SOCKET open_socket(int type, const char* protocol); - SOCKET accept_socket(SOCKET s, SOCKADDR* addr, socklen_t* addrlen); + SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen); int close_socket(SOCKET); u_long resolve_ip(char *addr); diff --git a/src/sbbs3/sbbs_ini.c b/src/sbbs3/sbbs_ini.c index d8849c36989d458bdde8563bf794b68dcfbcb1c9..3b58cf9f87abed59508e4adc39af9a5097d292b9 100644 --- a/src/sbbs3/sbbs_ini.c +++ b/src/sbbs3/sbbs_ini.c @@ -48,7 +48,9 @@ static const char* strAutoStart="AutoStart"; static const char* strCtrlDirectory="CtrlDirectory"; static const char* strTempDirectory="TempDirectory"; static const char* strOptions="Options"; -static const char* strInterface="Interface"; +static const char* strOutgoing4="OutgoingV4"; +static const char* strOutgoing6="OutgoingV6"; +static const char* strInterfaces="Interface"; static const char* strPort="Port"; static const char* strMaxClients="MaxClients"; static const char* strMaxInactivity="MaxInactivity"; @@ -193,6 +195,7 @@ static void get_ini_globals(str_list_t list, global_startup_t* global) const char* section = "Global"; char value[INI_MAX_VALUE_LEN]; char* p; + struct in6_addr wildcard6 = {0}; p=iniGetString(list,section,strCtrlDirectory,nulstr,value); if(*p) { @@ -211,7 +214,9 @@ static void get_ini_globals(str_list_t list, global_startup_t* global) SAFECOPY(global->host_name,value); global->sem_chk_freq=iniGetShortInt(list,section,strSemFileCheckFrequency,0); - global->interface_addr=iniGetIpAddress(list,section,strInterface,INADDR_ANY); + global->interfaces=iniGetStringList(list,section,strInterfaces, ",", "0.0.0.0,::"); + global->outgoing4.s_addr=iniGetIpAddress(list,section,strOutgoing4,0); + global->outgoing6=iniGetIp6Address(list,section,strOutgoing6,wildcard6); global->log_level=iniGetLogLevel(list,section,strLogLevel,DEFAULT_LOG_LEVEL); global->bind_retry_count=iniGetInteger(list,section,strBindRetryCount,DEFAULT_BIND_RETRY_COUNT); global->bind_retry_delay=iniGetInteger(list,section,strBindRetryDelay,DEFAULT_BIND_RETRY_DELAY); @@ -254,6 +259,8 @@ void sbbs_read_ini( char value[INI_MAX_VALUE_LEN]; str_list_t list; global_startup_t global_buf; + struct in6_addr wildcard6 = {0}; + char *global_interfaces; if(global==NULL) { memset(&global_buf,0,sizeof(global_buf)); @@ -271,7 +278,9 @@ void sbbs_read_ini( if(mail!=NULL) SAFECOPY(mail->ctrl_dir,global->ctrl_dir); if(services!=NULL) SAFECOPY(services->ctrl_dir,global->ctrl_dir); } - + + global_interfaces = strListCombine(global->interfaces, NULL, 16384, ","); + /***********************************************************************/ section = "BBS"; @@ -280,20 +289,25 @@ void sbbs_read_ini( if(bbs!=NULL) { - bbs->telnet_interface - =iniGetIpAddress(list,section,"TelnetInterface",global->interface_addr); + bbs->outgoing4.s_addr + =iniGetIpAddress(list,section,strOutgoing4,global->outgoing4.s_addr); + bbs->outgoing6 + =iniGetIp6Address(list,section,strOutgoing6,global->outgoing6); + bbs->telnet_port =iniGetShortInt(list,section,"TelnetPort",IPPORT_TELNET); + bbs->telnet_interfaces + =iniGetStringList(list,section,"TelnetInterface",",",global_interfaces); - bbs->rlogin_interface - =iniGetIpAddress(list,section,"RLoginInterface",global->interface_addr); bbs->rlogin_port =iniGetShortInt(list,section,"RLoginPort",513); + bbs->rlogin_interfaces + =iniGetStringList(list,section,"RLoginInterface",",",global_interfaces); - bbs->ssh_interface - =iniGetIpAddress(list,section,"SSHInterface",global->interface_addr); bbs->ssh_port =iniGetShortInt(list,section,"SSHPort",22); + bbs->ssh_interfaces + =iniGetStringList(list,section,"SSHInterface",",",global_interfaces); bbs->first_node =iniGetShortInt(list,section,"FirstNode",1); @@ -371,10 +385,14 @@ void sbbs_read_ini( if(ftp!=NULL) { - ftp->interface_addr - =iniGetIpAddress(list,section,strInterface,global->interface_addr); + ftp->outgoing4.s_addr + =iniGetIpAddress(list,section,strOutgoing4,global->outgoing4.s_addr); + ftp->outgoing6 + =iniGetIp6Address(list,section,strOutgoing6,global->outgoing6); ftp->port =iniGetShortInt(list,section,strPort,IPPORT_FTP); + ftp->interfaces + =iniGetStringList(list,section,strInterfaces,",",global_interfaces); ftp->max_clients =iniGetShortInt(list,section,strMaxClients,FTP_DEFAULT_MAX_CLIENTS); ftp->max_inactivity @@ -389,8 +407,10 @@ void sbbs_read_ini( =iniGetBytes(list,section,"MaxFileSize",1,0); /* Passive transfer settings (for stupid firewalls/NATs) */ - ftp->pasv_ip_addr + ftp->pasv_ip_addr.s_addr =iniGetIpAddress(list,section,"PasvIpAddress",0); + ftp->pasv_ip6_addr + =iniGetIp6Address(list,section,"PasvIp6Address",wildcard6); ftp->pasv_port_low =iniGetShortInt(list,section,"PasvPortLow",IPPORT_RESERVED); ftp->pasv_port_high @@ -442,12 +462,18 @@ void sbbs_read_ini( if(mail!=NULL) { - mail->interface_addr - =iniGetIpAddress(list,section,strInterface,global->interface_addr); + mail->interfaces + =iniGetStringList(list,section,"SMTPInterface",",",global_interfaces); + mail->outgoing4.s_addr + =iniGetIpAddress(list,section,strOutgoing4,global->outgoing4.s_addr); + mail->outgoing6 + =iniGetIp6Address(list,section,strOutgoing6,global->outgoing6); mail->smtp_port =iniGetShortInt(list,section,"SMTPPort",IPPORT_SMTP); mail->submission_port =iniGetShortInt(list,section,"SubmissionPort",IPPORT_SUBMISSION); + mail->pop3_interfaces + =iniGetStringList(list,section,"POP3Interface",",",global_interfaces); mail->pop3_port =iniGetShortInt(list,section,"POP3Port",IPPORT_POP3); mail->relay_port @@ -532,8 +558,12 @@ void sbbs_read_ini( if(services!=NULL) { - services->interface_addr - =iniGetIpAddress(list,section,strInterface,global->interface_addr); + services->interfaces + =iniGetStringList(list,section,strInterfaces,",",global_interfaces); + services->outgoing4.s_addr + =iniGetIpAddress(list,section,strOutgoing4,global->outgoing4.s_addr); + services->outgoing6 + =iniGetIp6Address(list,section,strOutgoing6,global->outgoing6); services->sem_chk_freq =iniGetShortInt(list,section,strSemFileCheckFrequency,global->sem_chk_freq); @@ -574,10 +604,18 @@ void sbbs_read_ini( if(web!=NULL) { - web->interface_addr - =iniGetIpAddress(list,section,strInterface,global->interface_addr); + web->interfaces + =iniGetStringList(list,section,strInterfaces,",",global_interfaces); + web->tls_interfaces + =iniGetStringList(list,section,"TLSInterface",",",global_interfaces); + web->outgoing4.s_addr + =iniGetIpAddress(list,section,strOutgoing4,global->outgoing4.s_addr); + web->outgoing6 + =iniGetIp6Address(list,section,strOutgoing6,global->outgoing6); web->port =iniGetShortInt(list,section,strPort,IPPORT_HTTP); + web->tls_port + =iniGetShortInt(list,section,"TLSPort",IPPORT_HTTPS); web->max_clients =iniGetShortInt(list,section,strMaxClients,WEB_DEFAULT_MAX_CLIENTS); web->max_inactivity @@ -653,6 +691,7 @@ void sbbs_read_ini( web->login_attempt_filter_threshold=iniGetInteger(list,section,strLoginAttemptFilterThreshold,global->login_attempt_filter_threshold); } + free(global_interfaces); iniFreeStringList(list); } @@ -703,7 +742,9 @@ BOOL sbbs_write_ini( iniSetString(lp,section,strTempDirectory,global->temp_dir,&style); iniSetString(lp,section,strHostName,global->host_name,&style); iniSetShortInt(lp,section,strSemFileCheckFrequency,global->sem_chk_freq,&style); - iniSetIpAddress(lp,section,strInterface,global->interface_addr,&style); + iniSetIpAddress(lp,section,strOutgoing4,global->outgoing4.s_addr,&style); + iniSetIp6Address(lp,section,strOutgoing6,global->outgoing6,&style); + iniSetStringList(lp, section, strInterfaces, ",", global->interfaces, &style); iniSetLogLevel(lp,section,strLogLevel,global->log_level,&style); iniSetInteger(lp,section,strBindRetryCount,global->bind_retry_count,&style); iniSetInteger(lp,section,strBindRetryDelay,global->bind_retry_delay,&style); @@ -725,24 +766,24 @@ BOOL sbbs_write_ini( if(!iniSetBool(lp,section,strAutoStart,run_bbs,&style)) break; - if(bbs->telnet_interface==global->interface_addr) + if(strListCmp(bbs->telnet_interfaces, global->interfaces)==0) iniRemoveValue(lp,section,"TelnetInterface"); - else if(!iniSetIpAddress(lp,section,"TelnetInterface",bbs->telnet_interface,&style)) + else if(!iniSetStringList(lp,section,"TelnetInterface", ",", bbs->telnet_interfaces, &style)) break; if(!iniSetShortInt(lp,section,"TelnetPort",bbs->telnet_port,&style)) break; - if(bbs->rlogin_interface==global->interface_addr) + if(strListCmp(bbs->rlogin_interfaces, global->interfaces)==0) iniRemoveValue(lp,section,"RLoginInterface"); - else if(!iniSetIpAddress(lp,section,"RLoginInterface",bbs->rlogin_interface,&style)) + else if(!iniSetStringList(lp,section,"RLoginInterface", ",", bbs->rlogin_interfaces,&style)) break; if(!iniSetShortInt(lp,section,"RLoginPort",bbs->rlogin_port,&style)) break; - if(bbs->ssh_interface==global->interface_addr) + if(strListCmp(bbs->ssh_interfaces, global->interfaces)==0) iniRemoveValue(lp,section,"SSHInterface"); - else if(!iniSetIpAddress(lp,section,"SSHInterface",bbs->ssh_interface,&style)) + else if(!iniSetStringList(lp,section,"SSHInterface", ",", bbs->ssh_interfaces,&style)) break; if(!iniSetShortInt(lp,section,"SSHPort",bbs->ssh_port,&style)) break; @@ -813,9 +854,19 @@ BOOL sbbs_write_ini( if(!iniSetBool(lp,section,strAutoStart,run_ftp,&style)) break; - if(ftp->interface_addr==global->interface_addr) - iniRemoveValue(lp,section,strInterface); - else if(!iniSetIpAddress(lp,section,strInterface,ftp->interface_addr,&style)) + if(strListCmp(ftp->interfaces, global->interfaces)==0) + iniRemoveValue(lp,section,strInterfaces); + else if(!iniSetStringList(lp,section,strInterfaces,",",ftp->interfaces,&style)) + break; + + if(ftp->outgoing4.s_addr == global->outgoing4.s_addr) + iniRemoveValue(lp,section,strOutgoing4); + else if(!iniSetIpAddress(lp, section, strOutgoing4, ftp->outgoing4.s_addr, &style)) + break; + + if(memcmp(&ftp->outgoing6, &global->outgoing6, sizeof(ftp->outgoing6))) + iniRemoveValue(lp,section,strOutgoing6); + else if(!iniSetIp6Address(lp, section, strOutgoing6, ftp->outgoing6, &style)) break; if(!iniSetShortInt(lp,section,strPort,ftp->port,&style)) @@ -832,7 +883,9 @@ BOOL sbbs_write_ini( break; /* Passive transfer settings */ - if(!iniSetIpAddress(lp,section,"PasvIpAddress",ftp->pasv_ip_addr,&style)) + if(!iniSetIpAddress(lp,section,"PasvIpAddress",ftp->pasv_ip_addr.s_addr,&style)) + break; + if(!iniSetIp6Address(lp,section,"PasvIp6Address",ftp->pasv_ip6_addr,&style)) break; if(!iniSetShortInt(lp,section,"PasvPortLow",ftp->pasv_port_low,&style)) break; @@ -899,9 +952,19 @@ BOOL sbbs_write_ini( if(!iniSetBool(lp,section,strAutoStart,run_mail,&style)) break; - if(mail->interface_addr==global->interface_addr) - iniRemoveValue(lp,section,strInterface); - else if(!iniSetIpAddress(lp,section,strInterface,mail->interface_addr,&style)) + if(strListCmp(mail->interfaces, global->interfaces)==0) + iniRemoveValue(lp,section,strInterfaces); + else if(!iniSetStringList(lp,section,strInterfaces,",",mail->interfaces,&style)) + break; + + if(mail->outgoing4.s_addr == global->outgoing4.s_addr) + iniRemoveValue(lp,section,strOutgoing4); + else if(!iniSetIpAddress(lp, section, strOutgoing4, mail->outgoing4.s_addr, &style)) + break; + + if(memcmp(&mail->outgoing6, &global->outgoing6, sizeof(ftp->outgoing6))) + iniRemoveValue(lp,section,strOutgoing6); + else if(!iniSetIp6Address(lp, section, strOutgoing6, mail->outgoing6, &style)) break; if(mail->sem_chk_freq==global->sem_chk_freq) @@ -1003,9 +1066,19 @@ BOOL sbbs_write_ini( if(!iniSetBool(lp,section,strAutoStart,run_services,&style)) break; - if(services->interface_addr==global->interface_addr) - iniRemoveValue(lp,section,strInterface); - else if(!iniSetIpAddress(lp,section,strInterface,services->interface_addr,&style)) + if(strListCmp(services->interfaces, global->interfaces)==0) + iniRemoveValue(lp,section,strInterfaces); + else if(!iniSetStringList(lp,section,strInterfaces,",",services->interfaces,&style)) + break; + + if(services->outgoing4.s_addr == global->outgoing4.s_addr) + iniRemoveValue(lp,section,strOutgoing4); + else if(!iniSetIpAddress(lp, section, strOutgoing4, services->outgoing4.s_addr, &style)) + break; + + if(memcmp(&services->outgoing6, &global->outgoing6, sizeof(ftp->outgoing6))) + iniRemoveValue(lp,section,strOutgoing6); + else if(!iniSetIp6Address(lp, section, strOutgoing6, services->outgoing6, &style)) break; if(services->sem_chk_freq==global->sem_chk_freq) @@ -1059,13 +1132,30 @@ BOOL sbbs_write_ini( if(!iniSetBool(lp,section,strAutoStart,run_web,&style)) break; - if(web->interface_addr==global->interface_addr) - iniRemoveValue(lp,section,strInterface); - else if(!iniSetIpAddress(lp,section,strInterface,web->interface_addr,&style)) + if(strListCmp(web->interfaces, global->interfaces)==0) + iniRemoveValue(lp,section,strInterfaces); + else if(!iniSetStringList(lp,section,strInterfaces,",",web->interfaces,&style)) + break; + + if(strListCmp(web->tls_interfaces, global->interfaces)==0) + iniRemoveValue(lp,section,"TLSInterface"); + else if(!iniSetStringList(lp,section,"TLSInterface",",",web->tls_interfaces,&style)) + break; + + if(web->outgoing4.s_addr == global->outgoing4.s_addr) + iniRemoveValue(lp,section,strOutgoing4); + else if(!iniSetIpAddress(lp, section, strOutgoing4, web->outgoing4.s_addr, &style)) + break; + + if(memcmp(&web->outgoing6, &global->outgoing6, sizeof(ftp->outgoing6))) + iniRemoveValue(lp,section,strOutgoing6); + else if(!iniSetIp6Address(lp, section, strOutgoing6, web->outgoing6, &style)) break; if(!iniSetShortInt(lp,section,strPort,web->port,&style)) break; + if(!iniSetShortInt(lp,section,"TLSPort",web->tls_port,&style)) + break; if(!iniSetShortInt(lp,section,strMaxClients,web->max_clients,&style)) break; if(!iniSetShortInt(lp,section,strMaxInactivity,web->max_inactivity,&style)) diff --git a/src/sbbs3/sbbscon.c b/src/sbbs3/sbbscon.c index 430f186691c1991119cb11321856149b43cdaf52..ad16fbb6460ccbfbc9f5f5b518794de3beb1b531 100644 --- a/src/sbbs3/sbbscon.c +++ b/src/sbbs3/sbbscon.c @@ -1165,7 +1165,11 @@ static void show_usage(char *cmd) /****************************************************************************/ /* Main Entry Point */ /****************************************************************************/ +#ifdef BUILD_JSDOCS +int CIOLIB_main(int argc, char** argv) +#else int main(int argc, char** argv) +#endif { int i; int n; @@ -2063,16 +2067,19 @@ int main(int argc, char** argv) struct tm tm; list_node_t* node; login_attempt_t* login_attempt; + char ip_addr[INET6_ADDRSTRLEN]; listLock(&login_attempt_list); count=0; for(node=login_attempt_list.first; node!=NULL; node=node->next) { login_attempt=node->data; localtime32(&login_attempt->time,&tm); + if(inet_addrtop(&login_attempt->addr, ip_addr, sizeof(ip_addr))==NULL) + strcpy(ip_addr, "<invalid address>"); printf("%lu attempts (%lu duplicate) from %s, last via %s on %u/%u %02u:%02u:%02u (user: %s, password: %s)\n" ,login_attempt->count ,login_attempt->dupes - ,inet_ntoa(login_attempt->addr) + ,ip_addr ,login_attempt->prot ,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec ,login_attempt->user diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h index 02bad9dc813cf588311fe09a37c62a4a415f9155..dd8ae213e6e697e796254cfebfad0b5de2ea9160 100644 --- a/src/sbbs3/sbbsdefs.h +++ b/src/sbbs3/sbbsdefs.h @@ -50,10 +50,10 @@ /* Constants */ /*************/ -#define VERSION "3.16" /* Version: Major.minor */ -#define REVISION 'd' /* Revision: lowercase letter */ -#define VERSION_NUM (31600 + (tolower(REVISION)-'a')) -#define VERSION_HEX (0x31600 + (tolower(REVISION)-'a')) +#define VERSION "3.17" /* Version: Major.minor */ +#define REVISION 'a' /* Revision: lowercase letter */ +#define VERSION_NUM (31700 + (tolower(REVISION)-'a')) +#define VERSION_HEX (0x31700 + (tolower(REVISION)-'a')) #define VERSION_NOTICE "Synchronet BBS for "PLATFORM_DESC\ " Version " VERSION @@ -498,9 +498,9 @@ typedef enum { /* Values for xtrn_t.event */ #define LEN_TITLE 70 /* Message title */ #define LEN_MAIN_CMD 34 /* Storage in user.dat for custom commands */ #define LEN_XFER_CMD 40 -#define LEN_SCAN_CMD 40 -#define LEN_MAIL_CMD 40 -#define LEN_CID 25 /* Caller ID (phone number) */ +#define LEN_SCAN_CMD 35 +#define LEN_IPADDR 45 +#define LEN_CID 45 /* Caller ID (phone number) */ #define LEN_ARSTR 40 /* Max length of Access Requirement string */ #define LEN_CHATACTCMD 9 /* Chat action command */ #define LEN_CHATACTOUT 65 /* Chat action output string */ @@ -566,8 +566,8 @@ typedef enum { /* Values for xtrn_t.event */ #define U_MAIN_CMD U_CURXTRN+8+2 /* unused */ #define U_XFER_CMD U_MAIN_CMD+LEN_MAIN_CMD /* unused */ #define U_SCAN_CMD U_XFER_CMD+LEN_XFER_CMD+2 /* unused */ -#define U_MAIL_CMD U_SCAN_CMD+LEN_SCAN_CMD /* unused */ -#define U_FREECDT U_MAIL_CMD+LEN_MAIL_CMD+2 +#define U_IPADDR U_SCAN_CMD+LEN_SCAN_CMD /* unused */ +#define U_FREECDT U_IPADDR+LEN_IPADDR+2 #define U_FLAGS3 U_FREECDT+10 /* Flag set #3 */ #define U_FLAGS4 U_FLAGS3+8 /* Flag set #4 */ #define U_XEDIT U_FLAGS4+8 /* External editor (code */ @@ -937,7 +937,8 @@ typedef struct { /* Users information */ comment[LEN_COMMENT+1], /* Private comment about user */ cursub[LEN_EXTCODE+1], /* Current sub-board internal code */ curdir[LEN_EXTCODE+1], /* Current directory internal code */ - curxtrn[9]; /* Current external program internal code */ + curxtrn[9], /* Current external program internal code */ + ipaddr[LEN_IPADDR+1]; /* Last known IP address */ uchar level, /* Security level */ sex, /* Sex - M or F */ diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c index fdfbd0eeb3ef18549aac51b2062c30ef53dfebae..4f0280524c04c1f043948807c138b1d66aaa95bb 100644 --- a/src/sbbs3/services.c +++ b/src/sbbs3/services.c @@ -61,6 +61,9 @@ #include "sbbs_ini.h" #include "js_rtpool.h" #include "js_request.h" +#include "js_socket.h" +#include "multisock.h" +#include "ssl.h" /* Constants */ @@ -79,37 +82,42 @@ static protected_uint32_t threads_pending_start; typedef struct { /* These are sysop-configurable */ - uint32_t interface_addr; - uint16_t port; - char protocol[34]; - char cmd[128]; - uint max_clients; - uint32_t options; - int listen_backlog; - int log_level; - uint32_t stack_size; + uint32_t interface_addr; + uint16_t port; + str_list_t interfaces; + struct in_addr outgoing4; + struct in6_addr outgoing6; + char protocol[34]; + char cmd[128]; + uint max_clients; + uint32_t options; + int listen_backlog; + int log_level; + uint32_t stack_size; js_startup_t js; js_server_props_t js_server_props; /* These are run-time state and stat vars */ - uint32_t clients; - ulong served; - SOCKET socket; - BOOL running; - BOOL terminated; + uint32_t clients; + ulong served; + struct xpms_set *set; + int running; + BOOL terminated; } service_t; typedef struct { - SOCKET socket; - SOCKADDR_IN addr; - time_t logintime; - user_t user; - client_t* client; - service_t* service; - js_callback_t callback; + SOCKET socket; + struct xpms_set *set; + union xp_sockaddr addr; + time_t logintime; + user_t user; + client_t* client; + service_t* service; + js_callback_t callback; /* Initial UDP datagram */ - BYTE* udp_buf; - int udp_len; - subscan_t *subscan; + BYTE* udp_buf; + int udp_len; + subscan_t *subscan; + CRYPT_SESSION tls_sess; } service_client_t; static service_t *service=NULL; @@ -169,6 +177,8 @@ static BOOL winsock_startup(void) #endif +static CRYPT_CONTEXT tls_context = -1; + static ulong active_clients(void) { ulong i; @@ -210,20 +220,32 @@ static void thread_down(void) startup->thread_up(startup->cbdata,FALSE,FALSE); } -static SOCKET open_socket(int type, const char* protocol) +void open_socket_cb(SOCKET sock, void *serv_ptr) { char error[256]; char section[128]; - SOCKET sock; + service_t *serv=(service_t *)serv_ptr; - sock=socket(AF_INET, type, IPPROTO_IP); - if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) + if(startup!=NULL && startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - if(sock!=INVALID_SOCKET) { - SAFEPRINTF(section,"services|%s", protocol); - if(set_socket_options(&scfg, sock, section, error, sizeof(error))) - lprintf(LOG_ERR,"%04d !ERROR %s",sock, error); - } + SAFEPRINTF(section,"services|%s", serv->protocol); + if(set_socket_options(&scfg, sock, section, error, sizeof(error))) + lprintf(LOG_ERR,"%04d !ERROR %s",sock, error); +} + +void close_socket_cb(SOCKET sock, void *serv_ptr) +{ + if(startup!=NULL && startup->socket_open!=NULL) + startup->socket_open(startup->cbdata,FALSE); +} + +static SOCKET open_socket(int family, int type, service_t* serv) +{ + SOCKET sock; + + sock=socket(family, type, IPPROTO_IP); + if(sock!=INVALID_SOCKET) + open_socket_cb(sock, serv); return(sock); } @@ -252,171 +274,6 @@ static void status(char* str) /* Global JavaScript Methods */ -static JSBool -js_read(JSContext *cx, uintN argc, jsval *arglist) -{ - jsval *argv=JS_ARGV(cx, arglist); - char* buf; - int32 len=512; - service_client_t* client; - jsrefcount rc; - - JS_SET_RVAL(cx, arglist, JSVAL_VOID); - - if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) - return(JS_FALSE); - - if(argc) { - if(!JS_ValueToInt32(cx,argv[0],&len)) - return JS_FALSE; - } - - if((buf=malloc(len))==NULL) - return(JS_FALSE); - - rc=JS_SUSPENDREQUEST(cx); - len=recv(client->socket,buf,len,0); - JS_RESUMEREQUEST(cx, rc); - - if(len>0) - JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,len))); - free(buf); - - return(JS_TRUE); -} - -static JSBool -js_readln(JSContext *cx, uintN argc, jsval *arglist) -{ - jsval *argv=JS_ARGV(cx, arglist); - char ch; - char* buf; - int i; - int32 len=512; - BOOL rd; - time_t start; - int32 timeout=30; /* seconds */ - JSString* str; - service_client_t* client; - jsrefcount rc; - - JS_SET_RVAL(cx, arglist, JSVAL_VOID); - - if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) - return(JS_FALSE); - - if(argc) { - if(!JS_ValueToInt32(cx,argv[0],&len)) - return JS_FALSE; - } - - if((buf=(char*)malloc(len+1))==NULL) { - JS_ReportError(cx,"Error allocating %u bytes",len+1); - return(JS_FALSE); - } - - if(argc>1) { - if(!JS_ValueToInt32(cx,argv[1],(int32*)&timeout)) { - free(buf); - return JS_FALSE; - } - } - - rc=JS_SUSPENDREQUEST(cx); - start=time(NULL); - for(i=0;i<len;) { - - if(!socket_check(client->socket,&rd,NULL,1000)) - break; /* disconnected */ - - if(!rd) { - if(time(NULL)-start>timeout) { - JS_SET_RVAL(cx, arglist, JSVAL_NULL); - JS_RESUMEREQUEST(cx, rc); - free(buf); - return(JS_TRUE); /* time-out */ - } - continue; /* no data */ - } - - if(recv(client->socket, &ch, 1, 0)!=1) - break; - - if(ch=='\n' /* && i>=1 */) /* Mar-9-2003: terminate on sole LF */ - break; - - buf[i++]=ch; - } - if(i>0 && buf[i-1]=='\r') - buf[i-1]=0; - else - buf[i]=0; - JS_RESUMEREQUEST(cx, rc); - - str = JS_NewStringCopyZ(cx, buf); - free(buf); - if(str==NULL) - return(JS_FALSE); - - JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str)); - - return(JS_TRUE); -} - -static JSBool -js_write(JSContext *cx, uintN argc, jsval *arglist) -{ - jsval *argv=JS_ARGV(cx, arglist); - uintN i; - char* cp=NULL; - size_t cp_sz=0; - size_t len; - service_client_t* client; - jsrefcount rc; - - JS_SET_RVAL(cx, arglist, JSVAL_VOID); - - if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) - return(JS_FALSE); - - JS_SET_RVAL(cx, arglist, argv[0]); - - for(i=0; i<argc; i++) { - JSVALUE_TO_RASTRING(cx, argv[i], cp, &cp_sz, &len); - if(cp==NULL) - continue; - rc=JS_SUSPENDREQUEST(cx); - sendsocket(client->socket,cp,len); - JS_RESUMEREQUEST(cx, rc); - } - if(cp) - free(cp); - - return(JS_TRUE); -} - -static JSBool -js_writeln(JSContext *cx, uintN argc, jsval *arglist) -{ - char* cp; - service_client_t* client; - jsrefcount rc; - - JS_SET_RVAL(cx, arglist, JSVAL_VOID); - - if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) - return(JS_FALSE); - - js_write(cx,argc,arglist); - - rc=JS_SUSPENDREQUEST(cx); - cp="\r\n"; - sendsocket(client->socket,cp,2); - JS_RESUMEREQUEST(cx, rc); - - return(JS_TRUE); -} - static JSBool js_log(JSContext *cx, uintN argc, jsval *arglist) { @@ -464,18 +321,21 @@ js_log(JSContext *cx, uintN argc, jsval *arglist) return(JS_TRUE); } -static void badlogin(SOCKET sock, char* prot, char* user, char* passwd, char* host, SOCKADDR_IN* addr) +static void badlogin(SOCKET sock, char* prot, char* user, char* passwd, char* host, union xp_sockaddr* addr) { char reason[128]; + char addr_ip[INET6_ADDRSTRLEN]; ulong count; SAFEPRINTF(reason,"%s LOGIN", prot); count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd); if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold) hacklog(&scfg, reason, user, passwd, host, addr); - if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) + if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) { + inet_addrtop(addr, addr_ip, sizeof(addr_ip)); filter_ip(&scfg, prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS" - ,host, inet_ntoa(addr->sin_addr), user, /* fname: */NULL); + ,host, addr_ip, user, /* fname: */NULL); + } mswait(startup->login_attempt_delay); } @@ -551,7 +411,7 @@ js_login(JSContext *cx, uintN argc, jsval *arglist) rc=JS_SUSPENDREQUEST(cx); if(client->client!=NULL) { - SAFECOPY(client->user.note,client->client->addr); + SAFECOPY(client->user.ipaddr,client->client->addr); SAFECOPY(client->user.comp,client->client->host); SAFECOPY(client->user.modem,client->service->protocol); } @@ -633,11 +493,6 @@ js_logout(JSContext *cx, uintN argc, jsval *arglist) } static JSFunctionSpec js_global_functions[] = { - {"read", js_read, 0}, /* read from client socket */ - {"readln", js_readln, 0}, /* read line from client socket */ - {"write", js_write, 0}, /* write to client socket */ - {"writeln", js_writeln, 0}, /* write line to client socket */ - {"print", js_writeln, 0}, /* write line to client socket */ {"log", js_log, 0}, /* Log a string */ {"login", js_login, 2}, /* Login specified username and password */ {"logout", js_logout, 0}, /* Logout user */ @@ -698,13 +553,13 @@ static JSBool js_client_add(JSContext *cx, uintN argc, jsval *arglist) { jsval *argv=JS_ARGV(cx, arglist); - client_t client; - SOCKET sock=INVALID_SOCKET; - socklen_t addr_len; - SOCKADDR_IN addr; + client_t client; + SOCKET sock=INVALID_SOCKET; + socklen_t addr_len; + union xp_sockaddr addr; service_client_t* service_client; - jsrefcount rc; - char *cstr=NULL; + jsrefcount rc; + char *cstr=NULL; JS_SET_RVAL(cx, arglist, JSVAL_VOID); @@ -721,13 +576,13 @@ js_client_add(JSContext *cx, uintN argc, jsval *arglist) client.time=time32(NULL); client.user="<unknown>"; SAFECOPY(client.host,client.user); - + sock=js_socket(cx,argv[0]); - + addr_len = sizeof(addr); - if(getpeername(sock, (struct sockaddr *)&addr, &addr_len)==0) { - SAFECOPY(client.addr,inet_ntoa(addr.sin_addr)); - client.port=ntohs(addr.sin_port); + if(getpeername(sock, &addr.addr, &addr_len)==0) { + inet_addrtop(&addr, client.addr, sizeof(client.addr)); + client.port=inet_addrport(&addr); } if(argc>1) { @@ -742,8 +597,8 @@ js_client_add(JSContext *cx, uintN argc, jsval *arglist) rc=JS_SUSPENDREQUEST(cx); client_on(sock, &client, /* update? */ FALSE); #ifdef _DEBUG - lprintf(LOG_DEBUG,"%04d %s client_add(%04u,%s,%s)" - ,service_client->service->socket,service_client->service->protocol + lprintf(LOG_DEBUG,"%s client_add(%04u,%s,%s)" + ,service_client->service->protocol ,sock,client.user,client.host); #endif if(cstr) @@ -759,8 +614,8 @@ js_client_update(JSContext *cx, uintN argc, jsval *arglist) client_t client; SOCKET sock=INVALID_SOCKET; socklen_t addr_len; - SOCKADDR_IN addr; - service_client_t* service_client; + union xp_sockaddr addr; + service_client_t* service_client; jsrefcount rc; char *cstr=NULL; @@ -778,9 +633,9 @@ js_client_update(JSContext *cx, uintN argc, jsval *arglist) sock=js_socket(cx,argv[0]); addr_len = sizeof(addr); - if(getpeername(sock, (struct sockaddr *)&addr, &addr_len)==0) { - SAFECOPY(client.addr,inet_ntoa(addr.sin_addr)); - client.port=ntohs(addr.sin_port); + if(getpeername(sock, &addr.addr, &addr_len)==0) { + inet_addrtop(&addr, client.addr, sizeof(client.addr)); + client.port=inet_addrport(&addr); } if(argc>1) { @@ -794,8 +649,8 @@ js_client_update(JSContext *cx, uintN argc, jsval *arglist) rc=JS_SUSPENDREQUEST(cx); client_on(sock, &client, /* update? */ TRUE); #ifdef _DEBUG - lprintf(LOG_DEBUG,"%04d %s client_update(%04u,%s,%s)" - ,service_client->service->socket,service_client->service->protocol + lprintf(LOG_DEBUG,"%s client_update(%04u,%s,%s)" + ,service_client->service->protocol ,sock,client.user,client.host); #endif if(cstr) @@ -826,8 +681,8 @@ js_client_remove(JSContext *cx, uintN argc, jsval *arglist) client_off(sock); if(service_client->service->clients==0) - lprintf(LOG_WARNING,"%04d %s !client_remove() called with 0 service clients" - ,service_client->service->socket, service_client->service->protocol); + lprintf(LOG_WARNING,"%s !client_remove() called with 0 service clients" + ,service_client->service->protocol); else { service_client->service->clients--; update_clients(); @@ -836,8 +691,8 @@ js_client_remove(JSContext *cx, uintN argc, jsval *arglist) } #ifdef _DEBUG - lprintf(LOG_DEBUG,"%04d %s client_remove(%04u)" - ,service_client->service->socket, service_client->service->protocol, sock); + lprintf(LOG_DEBUG,"%s client_remove(%04u)" + ,service_client->service->protocol, sock); #endif return(JS_TRUE); } @@ -849,6 +704,10 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, JSObject* server; BOOL success=FALSE; BOOL rooted=FALSE; + jsval val; + JSObject* obj; + JSObject* socket_obj; + js_socket_private_t* p; if((js_cx = JS_NewContext(js_runtime, service_client->service->js.cx_stack))==NULL) return(NULL); @@ -874,9 +733,39 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, break; /* Client Object */ - if(service_client->client!=NULL) + if(service_client->client!=NULL) { if(js_CreateClientObject(js_cx, *glob, "client", service_client->client, sock)==NULL) break; + /* Copy client socket stuff into the global context */ + if (!JS_GetProperty(js_cx, *glob, "client", &val) || val == JSVAL_VOID) + break; + obj=JSVAL_TO_OBJECT(val); + if (!JS_GetProperty(js_cx, obj, "socket", &val) || val == JSVAL_VOID) + break; + socket_obj=JSVAL_TO_OBJECT(val); + if (service_client->service->options & SERVICE_OPT_TLS) { + p=(js_socket_private_t*)JS_GetPrivate(js_cx,socket_obj); + p->session=service_client->tls_sess; + } + if (!JS_GetProperty(js_cx, socket_obj, "read", &val) || val == JSVAL_VOID) + break; + if (!JS_DefineProperty(js_cx, *glob, "read", val, NULL, NULL, JSPROP_ENUMERATE)) + break; + if (!JS_GetProperty(js_cx, socket_obj, "readln", &val) || val == JSVAL_VOID) + break; + if (!JS_DefineProperty(js_cx, *glob, "readln", val, NULL, NULL, JSPROP_ENUMERATE)) + break; + if (!JS_GetProperty(js_cx, socket_obj, "write", &val) || val == JSVAL_VOID) + break; + if (!JS_DefineProperty(js_cx, *glob, "write", val, NULL, NULL, JSPROP_ENUMERATE)) + break; + if (!JS_GetProperty(js_cx, socket_obj, "writeln", &val) || val == JSVAL_VOID) + break; + if (!JS_DefineProperty(js_cx, *glob, "writeln", val, NULL, NULL, JSPROP_ENUMERATE)) + break; + if (!JS_DefineProperty(js_cx, *glob, "print", val, NULL, NULL, JSPROP_ENUMERATE)) + break; + } /* User Class */ if(js_CreateUserClass(js_cx, *glob, &scfg)==NULL) @@ -912,33 +801,6 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, if(js_CreateSystemObject(js_cx, *glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL) break; -#if 0 - char ver[256]; - JSString* js_str; - jsval val; - - /* server object */ - if((server=JS_DefineObject(js_cx, *glob, "server", &js_server_class - ,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL) - break; - - if(!JS_DefineProperties(js_cx, server, js_server_properties)) - break; - - sprintf(ver,"Synchronet Services %s",revision); - if((js_str=JS_NewStringCopyZ(js_cx, ver))==NULL) - break; - val = STRING_TO_JSVAL(js_str); - if(!JS_SetProperty(js_cx, server, "version", &val)) - break; - - if((js_str=JS_NewStringCopyZ(js_cx, services_ver()))==NULL) - break; - val = STRING_TO_JSVAL(js_str); - if(!JS_SetProperty(js_cx, server, "version_detail", &val)) - break; - -#else if(service_client->service->js_server_props.version[0]==0) { SAFEPRINTF(service_client->service->js_server_props.version @@ -956,11 +818,11 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, if((server=js_CreateServerObject(js_cx,*glob ,&service_client->service->js_server_props))==NULL) break; -#endif - if(service_client->client==NULL) /* static service */ - if(js_CreateSocketObject(js_cx, server, "socket", service_client->socket)==NULL) + if(service_client->client==NULL) { /* static service */ + if(js_CreateSocketObjectFromSet(js_cx, server, "socket", service_client->set)==NULL) break; + } JS_DefineFunction(js_cx, server, "client_add" , js_client_add, 1, 0); JS_DefineFunction(js_cx, server, "client_update", js_client_update, 1, 0); @@ -1048,10 +910,41 @@ static void js_init_args(JSContext* js_cx, JSObject* js_obj, const char* cmdline ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE); } +#define HANDLE_CRYPT_CALL(status, service_client) handle_crypt_call(status, service_client, __FILE__, __LINE__) + +static BOOL handle_crypt_call(int status, service_client_t *service_client, const char *file, int line) +{ + int len = 0; + char estr[CRYPT_MAX_TEXTSIZE+1]; + int sock = 0; + + if (status == CRYPT_OK) + return TRUE; + if (service_client != NULL) { + if (service_client->service->options & SERVICE_OPT_TLS) + cryptGetAttributeString(service_client->tls_sess, CRYPT_ATTRIBUTE_ERRORMESSAGE, estr, &len); + sock = service_client->socket; + } + estr[len]=0; + if (len) + lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d (%s)", sock, status, file, line, estr); + else + lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d", sock, status, file, line); + return FALSE; +} + +static void js_service_failure_cleanup(service_t *service, SOCKET socket) +{ + close_socket(socket); + if(service->clients) + service->clients--; + thread_down(); + return; +} + static void js_service_thread(void* arg) { - char* host_name; - HOSTENT* host; + char host_name[256]; SOCKET socket; client_t client; service_t* service; @@ -1084,28 +977,17 @@ static void js_service_thread(void* arg) /* Host name lookup and filtering */ if(service->options&BBS_OPT_NO_HOST_LOOKUP - || startup->options&BBS_OPT_NO_HOST_LOOKUP) - host=NULL; - else - host=gethostbyaddr((char *)&service_client.addr.sin_addr - ,sizeof(service_client.addr.sin_addr),AF_INET); - - if(host!=NULL && host->h_name!=NULL) - host_name=host->h_name; - else - host_name="<no name>"; + || startup->options&BBS_OPT_NO_HOST_LOOKUP) + strcpy(host_name, "<no name>"); + else { + if(getnameinfo(&service_client.addr.addr, xp_sockaddr_len(&service_client), host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD) != 0) + strcpy(host_name, "<no name>"); + } if(!(service->options&BBS_OPT_NO_HOST_LOOKUP) && !(startup->options&BBS_OPT_NO_HOST_LOOKUP)) { lprintf(LOG_INFO,"%04d %s Hostname: %s" ,socket, service->protocol, host_name); -#if 0 /* gethostbyaddr() is apparently not (always) thread-safe - and getnameinfo() doesn't return alias information */ - for(i=0;host!=NULL && host->h_aliases!=NULL - && host->h_aliases[i]!=NULL;i++) - lprintf(LOG_INFO,"%04d %s HostAlias: %s" - ,socket, service->protocol, host->h_aliases[i]); -#endif } if(trashcan(&scfg,host_name,"host")) { @@ -1118,12 +1000,44 @@ static void js_service_thread(void* arg) return; } + if (service_client.service->options & SERVICE_OPT_TLS) { + /* Create and initialize the TLS session */ + if (!HANDLE_CRYPT_CALL(cryptCreateSession(&service_client.tls_sess, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER), &service_client)) { + js_service_failure_cleanup(service, socket); + return; + } + /* Add all the user/password combinations */ +#if 0 // TLS-PSK is currently broken in cryptlib + last = lastuser(&scfg); + for (i=1; i <= last; i++) { + user.number = i; + getuserdat(&scfg,&user); + if(user.misc&(DELETED|INACTIVE)) + continue; + if (user.alias[0] && user.pass[0]) { + if(HANDLE_CRYPT_CALL(cryptSetAttributeString(service_client.tls_sess, CRYPT_SESSINFO_USERNAME, user.alias, strlen(user.alias)), &session)) + HANDLE_CRYPT_CALL(cryptSetAttributeString(service_client.tls_sess, CRYPT_SESSINFO_PASSWORD, user.pass, strlen(user.pass)), &session); + } + } +#endif + if (tls_context != -1) { + HANDLE_CRYPT_CALL(cryptSetAttribute(service_client.tls_sess, CRYPT_SESSINFO_PRIVATEKEY, tls_context), &service_client); + } + BOOL nodelay=TRUE; + setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay)); + + HANDLE_CRYPT_CALL(cryptSetAttribute(service_client.tls_sess, CRYPT_SESSINFO_NETWORKSOCKET, socket), &service_client); + if (!HANDLE_CRYPT_CALL(cryptSetAttribute(service_client.tls_sess, CRYPT_SESSINFO_ACTIVE, 1), &service_client)) { + js_service_failure_cleanup(service, socket); + return; + } + } #if 0 /* Need to export from SBBS.DLL */ identity=NULL; if(service->options&BBS_OPT_GET_IDENT && startup->options&BBS_OPT_GET_IDENT) { - identify(&service_client.addr, service->port, str, sizeof(str)-1); + identify(&service_client, service->port, str, sizeof(str)-1); identity=strrchr(str,':'); if(identity!=NULL) { identity++; /* skip colon */ @@ -1136,9 +1050,9 @@ static void js_service_thread(void* arg) client.size=sizeof(client); client.time=time32(NULL); - SAFECOPY(client.addr,inet_ntoa(service_client.addr.sin_addr)); + inet_addrtop(&service_client.addr, client.addr, sizeof(client.addr)); SAFECOPY(client.host,host_name); - client.port=ntohs(service_client.addr.sin_port); + client.port=inet_addrport(&service_client.addr); client.protocol=service->protocol; client.user="<unknown>"; service_client.client=&client; @@ -1163,7 +1077,7 @@ static void js_service_thread(void* arg) if(startup->login_attempt_throttle && (login_attempts=loginAttempts(startup->login_attempt_list, &service_client.addr)) > 1) { lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%u login attempts)" - ,socket, service->protocol, inet_ntoa(service_client.addr.sin_addr), login_attempts); + ,socket, service->protocol, client.addr, login_attempts); mswait(login_attempts*startup->login_attempt_throttle); } @@ -1243,7 +1157,7 @@ static void js_static_service_thread(void* arg) char fname[MAX_PATH+1]; service_t* service; service_client_t service_client; - SOCKET socket; + struct xpms_set *set; /* JavaScript-specific */ JSObject* js_glob; JSObject* js_script; @@ -1256,16 +1170,16 @@ static void js_static_service_thread(void* arg) service=(service_t*)arg; service->running=TRUE; - socket = service->socket; + set = service->set; - lprintf(LOG_DEBUG,"%04d %s static JavaScript service thread started", service->socket, service->protocol); + lprintf(LOG_DEBUG,"%s static JavaScript service thread started", service->protocol); SetThreadName("JS Static Service"); thread_up(TRUE /* setuid */); protected_uint32_adjust(&threads_pending_start, -1); memset(&service_client,0,sizeof(service_client)); - service_client.socket = service->socket; + service_client.set = service->set; service_client.service = service; service_client.callback.limit = service->js.time_limit; service_client.callback.gc_interval = service->js.gc_interval; @@ -1274,10 +1188,10 @@ static void js_static_service_thread(void* arg) service_client.callback.auto_terminate = TRUE; if((js_runtime=jsrt_GetNew(service->js.max_bytes, 5000, __FILE__, __LINE__))==NULL) { - lprintf(LOG_ERR,"%04d !%s ERROR initializing JavaScript runtime" - ,service->socket,service->protocol); - close_socket(service->socket); - service->socket=INVALID_SOCKET; + lprintf(LOG_ERR,"!%s ERROR initializing JavaScript runtime" + ,service->protocol); + xpms_destroy(service->set, close_socket_cb, service); + service->set = NULL; thread_down(); return; } @@ -1289,9 +1203,9 @@ static void js_static_service_thread(void* arg) sprintf(spath,"%s%s",scfg.exec_dir,fname); do { - if((js_cx=js_initcx(js_runtime,service->socket,&service_client,&js_glob))==NULL) { - lprintf(LOG_ERR,"%04d !%s ERROR initializing JavaScript context" - ,service->socket,service->protocol); + if((js_cx=js_initcx(js_runtime,0,&service_client,&js_glob))==NULL) { + lprintf(LOG_ERR,"!%s ERROR initializing JavaScript context" + ,service->protocol); break; } @@ -1303,7 +1217,7 @@ static void js_static_service_thread(void* arg) JS_SetOperationCallback(js_cx, js_OperationCallback); if((js_script=JS_CompileFile(js_cx, js_glob, spath))==NULL) { - lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",service->socket,spath); + lprintf(LOG_ERR,"!JavaScript FAILED to compile script (%s)",spath); break; } @@ -1325,35 +1239,39 @@ static void js_static_service_thread(void* arg) jsrt_Release(js_runtime); if(service->clients) { - lprintf(LOG_WARNING,"%04d %s !service terminating with %u active clients" - ,socket, service->protocol, service->clients); + lprintf(LOG_WARNING,"%s !service terminating with %u active clients" + , service->protocol, service->clients); service->clients=0; } thread_down(); - lprintf(LOG_INFO,"%04d %s service thread terminated (%lu clients served)" - ,socket, service->protocol, service->served); + lprintf(LOG_INFO,"%s service thread terminated (%lu clients served)" + , service->protocol, service->served); - close_socket(service->socket); - service->socket=INVALID_SOCKET; + xpms_destroy(service->set, close_socket_cb, service); + service->set = NULL; service->running=FALSE; } +struct native_service_instance { + service_t *service; + SOCKET socket; +}; + static void native_static_service_thread(void* arg) { char cmd[MAX_PATH]; char fullcmd[MAX_PATH*2]; - SOCKET socket; SOCKET socket_dup; - service_t* service; + struct native_service_instance inst; - service = (service_t*)arg; + inst = *(struct native_service_instance *)arg; + free(arg); - service->running=TRUE; - socket = service->socket; + inst.service->running++; - lprintf(LOG_DEBUG,"%04d %s static service thread started", socket, service->protocol); + lprintf(LOG_DEBUG,"%04d %s static service thread started", inst.socket, inst.service->protocol); SetThreadName("Static Service"); thread_up(TRUE /* setuid */); @@ -1361,51 +1279,49 @@ static void native_static_service_thread(void* arg) #ifdef _WIN32 if(!DuplicateHandle(GetCurrentProcess(), - (HANDLE)socket, - GetCurrentProcess(), - (HANDLE*)&socket_dup, - 0, - TRUE, /* Inheritable */ - DUPLICATE_SAME_ACCESS)) { + (HANDLE)inst.socket, + GetCurrentProcess(), + (HANDLE*)&socket_dup, + 0, + TRUE, /* Inheritable */ + DUPLICATE_SAME_ACCESS)) { lprintf(LOG_ERR,"%04d !%s ERROR %d duplicating socket descriptor" - ,socket,service->protocol,GetLastError()); - close_socket(service->socket); - service->socket=INVALID_SOCKET; + ,inst.socket,inst.service->protocol,GetLastError()); + close_socket(inst.socket); thread_down(); + inst.service->running--; return; } #else - socket_dup = dup(service->socket); + socket_dup = dup(inst.socket); #endif /* RUN SCRIPT */ - if(strpbrk(service->cmd,"/\\")==NULL) - sprintf(cmd,"%s%s",scfg.exec_dir,service->cmd); + if(strpbrk(inst.service->cmd,"/\\")==NULL) + sprintf(cmd,"%s%s",scfg.exec_dir,inst.service->cmd); else - strcpy(cmd,service->cmd); + strcpy(cmd,inst.service->cmd); sprintf(fullcmd,cmd,socket_dup); do { system(fullcmd); - } while(!service->terminated && service->options&SERVICE_OPT_STATIC_LOOP); + } while(!inst.service->terminated && inst.service->options&SERVICE_OPT_STATIC_LOOP); thread_down(); lprintf(LOG_INFO,"%04d %s service thread terminated (%lu clients served)" ,socket, service->protocol, service->served); - close_socket(service->socket); - service->socket=INVALID_SOCKET; + close_socket(inst.socket); closesocket(socket_dup); /* close duplicate handle */ - service->running=FALSE; + service->running--; } static void native_service_thread(void* arg) { char cmd[MAX_PATH]; char fullcmd[MAX_PATH*2]; - char* host_name; - HOSTENT* host; + char host_name[256]; SOCKET socket; SOCKET socket_dup; client_t client; @@ -1426,16 +1342,11 @@ static void native_service_thread(void* arg) /* Host name lookup and filtering */ if(service->options&BBS_OPT_NO_HOST_LOOKUP - || startup->options&BBS_OPT_NO_HOST_LOOKUP) - host=NULL; - else - host=gethostbyaddr((char *)&service_client.addr.sin_addr - ,sizeof(service_client.addr.sin_addr),AF_INET); - - if(host!=NULL && host->h_name!=NULL) - host_name=host->h_name; - else - host_name="<no name>"; + || startup->options&BBS_OPT_NO_HOST_LOOKUP) + strcpy(host_name, "<no name>"); + else + if(getnameinfo(&service_client.addr.addr, xp_sockaddr_len(&service_client), host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD)!=0) + strcpy(host_name, "<no name>"); if(!(service->options&BBS_OPT_NO_HOST_LOOKUP) && !(startup->options&BBS_OPT_NO_HOST_LOOKUP)) { @@ -1465,7 +1376,7 @@ static void native_service_thread(void* arg) identity=NULL; if(service->options&BBS_OPT_GET_IDENT && startup->options&BBS_OPT_GET_IDENT) { - identify(&service_client.addr, service->port, str, sizeof(str)-1); + identify(&service_client, service->port, str, sizeof(str)-1); identity=strrchr(str,':'); if(identity!=NULL) { identity++; /* skip colon */ @@ -1478,9 +1389,9 @@ static void native_service_thread(void* arg) client.size=sizeof(client); client.time=time32(NULL); - SAFECOPY(client.addr,inet_ntoa(service_client.addr.sin_addr)); + inet_addrtop(&service_client.addr, client.addr, sizeof(client.addr)); SAFECOPY(client.host,host_name); - client.port=ntohs(service_client.addr.sin_port); + client.port=inet_addrport(&service_client.addr); client.protocol=service->protocol; client.user="<unknown>"; @@ -1510,7 +1421,7 @@ static void native_service_thread(void* arg) if(startup->login_attempt_throttle && (login_attempts=loginAttempts(startup->login_attempt_list, &service_client.addr)) > 1) { lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%u login attempts)" - ,socket, service->protocol, inet_ntoa(service_client.addr.sin_addr), login_attempts); + ,socket, service->protocol, client.addr, login_attempts); mswait(login_attempts*startup->login_attempt_throttle); } @@ -1573,6 +1484,7 @@ static service_t* read_services_ini(const char* services_ini, service_t* service uint max_clients; uint32_t options; uint32_t stack_size; + char *default_interfaces; if((fp=fopen(services_ini,"r"))==NULL) { lprintf(LOG_CRIT,"!ERROR %d opening %s", errno, services_ini); @@ -1592,6 +1504,7 @@ static service_t* read_services_ini(const char* services_ini, service_t* service /* Enumerate and parse each service configuration */ sec_list = iniGetSectionList(list,""); + default_interfaces = strListCombine(startup->interfaces, NULL, 16384, ","); for(i=0; sec_list!=NULL && sec_list[i]!=NULL; i++) { if(!iniGetBool(list,sec_list[i],"Enabled",TRUE)) { lprintf(LOG_WARNING,"Ignoring disabled service: %s",sec_list[i]); @@ -1599,8 +1512,10 @@ static service_t* read_services_ini(const char* services_ini, service_t* service } memset(&serv,0,sizeof(service_t)); SAFECOPY(serv.protocol,iniGetString(list,sec_list[i],"Protocol",sec_list[i],prot)); - serv.socket=INVALID_SOCKET; - serv.interface_addr=iniGetIpAddress(list,sec_list[i],"Interface",startup->interface_addr); + serv.set = NULL; + serv.interfaces=iniGetStringList(list,sec_list[i],"Interface",",",default_interfaces); + serv.outgoing4.s_addr=iniGetIpAddress(list,sec_list[i],"OutgoingV4",startup->outgoing4.s_addr); + serv.outgoing6=iniGetIp6Address(list,sec_list[i],"OutgoingV6",startup->outgoing6); serv.max_clients=iniGetInteger(list,sec_list[i],"MaxClients",max_clients); serv.listen_backlog=iniGetInteger(list,sec_list[i],"ListenBacklog",listen_backlog); serv.stack_size=(uint32_t)iniGetBytes(list,sec_list[i],"StackSize",1,stack_size); @@ -1654,12 +1569,14 @@ static service_t* read_services_ini(const char* services_ini, service_t* service if((np=(service_t*)realloc(service,sizeof(service_t)*((*services)+1)))==NULL) { fclose(fp); lprintf(LOG_CRIT,"!MALLOC FAILURE"); + free(default_interfaces); return(service); } service=np; service[*services]=serv; (*services)++; } + free(default_interfaces); iniFreeStringList(sec_list); strListFree(&list); @@ -1682,6 +1599,11 @@ static void cleanup(int code) semfile_list_free(&recycle_semfiles); semfile_list_free(&shutdown_semfiles); + if (tls_context != -1) { + cryptDestroyContext(tls_context); + tls_context = -1; + } + update_clients(); #ifdef _WINSOCKAPI_ @@ -1723,23 +1645,47 @@ const char* DLLCALL services_ver(void) return(ver); } +void service_udp_sock_cb(SOCKET sock, void *cbdata) +{ + service_t *serv = (service_t *)cbdata; + int optval; + + open_socket_cb(sock, cbdata); + + /* We need to set the REUSE ADDRESS socket option */ + optval=TRUE; + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&optval,sizeof(optval))!=0) { + lprintf(LOG_ERR,"%04d !ERROR %d setting %s socket option" + ,sock, ERROR_VALUE, serv->protocol); + close_socket(sock); + return; + } + #ifdef BSD + if(setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&optval,sizeof(optval))!=0) { + lprintf(LOG_ERR,"%04d !ERROR %d setting %s socket option",sock, ERROR_VALUE, serv->protocol); + close_socket(sock); + return; + } + #endif +} + void DLLCALL services_thread(void* arg) { char* p; char path[MAX_PATH+1]; char error[256]; - char host_ip[32]; + char host_ip[64]; char compiler[32]; char str[128]; char services_ini[MAX_PATH+1]; - SOCKADDR_IN addr; - SOCKADDR_IN client_addr; + union xp_sockaddr addr; + socklen_t addr_len; + union xp_sockaddr client_addr; socklen_t client_addr_len; - SOCKET socket; SOCKET client_socket; BYTE* udp_buf = NULL; int udp_len; - int i; + int i,j; int result; int optval; ulong total_running; @@ -1750,6 +1696,7 @@ void DLLCALL services_thread(void* arg) ulong total_sockets; struct timeval tv; service_client_t* client; + char ssl_estr[SSL_ESTR_LEN]; services_ver(); @@ -1865,82 +1812,44 @@ void DLLCALL services_thread(void* arg) return; } + tls_context = get_ssl_cert(&scfg, ssl_estr); + if (tls_context == -1) + lprintf(LOG_ERR, "Error creating TLS certificate: %s", ssl_estr); + update_clients(); /* Open and Bind Listening Sockets */ total_sockets=0; - for(i=0;i<(int)services;i++) - service[i].socket=INVALID_SOCKET; for(i=0;i<(int)services && !startup->shutdown_now;i++) { + struct in_addr iaddr; - if((socket = open_socket( - (service[i].options&SERVICE_OPT_UDP) ? SOCK_DGRAM : SOCK_STREAM - ,service[i].protocol)) - ==INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d opening %s socket" - ,ERROR_VALUE, service[i].protocol); - cleanup(1); - return; - } - - if(service[i].options&SERVICE_OPT_UDP) { - /* We need to set the REUSE ADDRESS socket option */ - optval=TRUE; - if(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR - ,(char*)&optval,sizeof(optval))!=0) { - lprintf(LOG_ERR,"%04d !ERROR %d setting %s socket option" - ,socket, ERROR_VALUE, service[i].protocol); - close_socket(socket); + if (service[i].options & SERVICE_OPT_TLS) { + if (tls_context == -1) + continue; + if (service[i].options & SERVICE_OPT_UDP) { + lprintf(LOG_ERR, "Option error, TLS and UDP specified for %s", service[i].protocol); continue; } - #ifdef BSD - if(setsockopt(socket,SOL_SOCKET,SO_REUSEPORT - ,(char*)&optval,sizeof(optval))!=0) { - lprintf(LOG_ERR,"%04d !ERROR %d setting %s socket option" - ,socket, ERROR_VALUE, service[i].protocol); - close_socket(socket); + if (service[i].options & SERVICE_OPT_NATIVE) { + lprintf(LOG_ERR, "Option error, TLS not yet supported for native services (%s)", service[i].protocol); continue; } - #endif - } - memset(&addr, 0, sizeof(addr)); - - addr.sin_addr.s_addr = htonl(service[i].interface_addr); - addr.sin_family = AF_INET; - addr.sin_port = htons(service[i].port); - - if(service[i].port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result=retry_bind(socket, (struct sockaddr *) &addr, sizeof(addr) - ,startup->bind_retry_count, startup->bind_retry_delay, service[i].protocol, lprintf); - if(service[i].port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); - } - if(result!=0) { - lprintf(LOG_ERR,"%04d %s",socket,BIND_FAILURE_HELP); - close_socket(socket); - continue; - } - - lprintf(LOG_INFO,"%04d %s socket bound to %s port %u" - ,socket, service[i].protocol - ,service[i].options&SERVICE_OPT_UDP ? "UDP" : "TCP" - ,service[i].port); - - if(!(service[i].options&SERVICE_OPT_UDP)) { - if(listen(socket,service[i].listen_backlog)!=0) { - lprintf(LOG_ERR,"%04d !ERROR %d listening on %s socket" - ,socket, ERROR_VALUE, service[i].protocol); - close_socket(socket); + if (service[i].options & SERVICE_OPT_STATIC) { + lprintf(LOG_ERR, "Option error, TLS not yet supported for static services (%s)", service[i].protocol); continue; } } - service[i].socket=socket; - total_sockets++; + service[i].set=xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf); + if(service[i].set == NULL) { + lprintf(LOG_CRIT,"!ERROR creating %s socket set", service[i].protocol); + cleanup(1); + return; + } + xpms_add_list(service[i].set, PF_UNSPEC, (service[i].options&SERVICE_OPT_UDP) ? SOCK_DGRAM : SOCK_STREAM + , IPPROTO_IP, service[i].interfaces, service[i].port, service[i].protocol + , (service[i].options&SERVICE_OPT_UDP) ? service_udp_sock_cb : open_socket_cb, startup->seteuid, &service[i]); + total_sockets += service[i].set->sock_count; } if(!total_sockets) { @@ -1953,15 +1862,25 @@ void DLLCALL services_thread(void* arg) for(i=0;i<(int)services;i++) { if(!(service[i].options&SERVICE_OPT_STATIC)) continue; - if(service[i].socket==INVALID_SOCKET) /* bind failure? */ + if(service[i].set==NULL) /* bind failure? */ continue; /* start thread here */ - protected_uint32_adjust(&threads_pending_start, 1); - if(service[i].options&SERVICE_OPT_NATIVE) /* Native */ - _beginthread(native_static_service_thread, service[i].stack_size, &service[i]); - else /* JavaScript */ + if(service[i].options&SERVICE_OPT_NATIVE) { /* Native */ + for(j=0; j<service[i].set->sock_count; j++) { + struct native_service_instance *inst=(struct native_service_instance *)malloc(sizeof(struct native_service_instance)); + if(inst) { + inst->socket=service[i].set->socks[j].sock; + inst->service=&service[i]; + protected_uint32_adjust(&threads_pending_start, 1); + _beginthread(native_static_service_thread, service[i].stack_size, inst); + } + } + } + else { /* JavaScript */ + protected_uint32_adjust(&threads_pending_start, 1); _beginthread(js_static_service_thread, service[i].stack_size, &service[i]); + } } status("Listening"); @@ -2016,14 +1935,16 @@ void DLLCALL services_thread(void* arg) for(i=0;i<(int)services;i++) { if(service[i].options&SERVICE_OPT_STATIC) continue; - if(service[i].socket==INVALID_SOCKET) + if(service[i].set==NULL) continue; if(!(service[i].options&SERVICE_OPT_FULL_ACCEPT) && service[i].max_clients && service[i].clients >= service[i].max_clients) continue; - FD_SET(service[i].socket,&socket_set); - if(service[i].socket>high_socket) - high_socket=service[i].socket; + for(j=0; j<service[i].set->sock_count; j++) { + FD_SET(service[i].set->socks[j].sock,&socket_set); + if(service[i].set->socks[j].sock>high_socket) + high_socket=service[i].set->socks[j].sock; + } } if(high_socket==0) { /* No dynamic services? */ YIELD(); @@ -2047,183 +1968,182 @@ void DLLCALL services_thread(void* arg) /* Determine who services this socket */ for(i=0;i<(int)services;i++) { - if(service[i].socket==INVALID_SOCKET) - continue; - - if(!FD_ISSET(service[i].socket,&socket_set)) + if(service[i].set==NULL) continue; - client_addr_len = sizeof(client_addr); - - udp_len=0; - - if(service[i].options&SERVICE_OPT_UDP) { - /* UDP */ - if((udp_buf = (BYTE*)calloc(1, MAX_UDP_BUF_LEN)) == NULL) { - lprintf(LOG_CRIT,"%04d %s !ERROR %d allocating UDP buffer" - ,service[i].socket, service[i].protocol, errno); + for(j=0; j<service[i].set->sock_count; j++) { + if(!FD_ISSET(service[i].set->socks[j].sock,&socket_set)) continue; - } - udp_len = recvfrom(service[i].socket - ,udp_buf, MAX_UDP_BUF_LEN, 0 /* flags */ - ,(struct sockaddr *)&client_addr, &client_addr_len); - if(udp_len<1) { - FREE_AND_NULL(udp_buf); - lprintf(LOG_ERR,"%04d %s !ERROR %d recvfrom failed" - ,service[i].socket, service[i].protocol, ERROR_VALUE); - continue; + client_addr_len = sizeof(client_addr); + + udp_len=0; + + if(service[i].options&SERVICE_OPT_UDP) { + /* UDP */ + if((udp_buf = (BYTE*)calloc(1, MAX_UDP_BUF_LEN)) == NULL) { + lprintf(LOG_CRIT,"%04d %s !ERROR %d allocating UDP buffer" + ,service[i].set->socks[j].sock, service[i].protocol, errno); + continue; + } + + udp_len = recvfrom(service[i].set->socks[j].sock + ,udp_buf, MAX_UDP_BUF_LEN, 0 /* flags */ + ,&client_addr.addr, &client_addr_len); + if(udp_len<1) { + FREE_AND_NULL(udp_buf); + lprintf(LOG_ERR,"%04d %s !ERROR %d recvfrom failed" + ,service[i].set->socks[j].sock, service[i].protocol, ERROR_VALUE); + continue; + } + + if((client_socket = open_socket(service[i].set->socks[j].domain, SOCK_DGRAM, &service[i])) + ==INVALID_SOCKET) { + FREE_AND_NULL(udp_buf); + lprintf(LOG_ERR,"%04d %s !ERROR %d opening socket" + ,service[i].set->socks[j].sock, service[i].protocol, ERROR_VALUE); + continue; + } + + lprintf(LOG_DEBUG,"%04d %s created client socket: %d" + ,service[i].set->socks[j].sock, service[i].protocol, client_socket); + + /* We need to set the REUSE ADDRESS socket option */ + optval=TRUE; + if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEADDR + ,(char*)&optval,sizeof(optval))!=0) { + FREE_AND_NULL(udp_buf); + lprintf(LOG_ERR,"%04d %s !ERROR %d setting socket option" + ,client_socket, service[i].protocol, ERROR_VALUE); + close_socket(client_socket); + continue; + } + #ifdef BSD + if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEPORT + ,(char*)&optval,sizeof(optval))!=0) { + FREE_AND_NULL(udp_buf); + lprintf(LOG_ERR,"%04d %s !ERROR %d setting socket option" + ,client_socket, service[i].protocol, ERROR_VALUE); + close_socket(client_socket); + continue; + } + #endif + + addr_len = sizeof(addr); + getsockname(service[i].set->socks[j].sock, &addr.addr, &addr_len); + result=bind(client_socket, &addr.addr, addr_len); + if(result==SOCKET_ERROR) { + /* Failed to re-bind to same port number, use user port */ + lprintf(LOG_NOTICE,"%04d %s ERROR %d re-binding socket to port %u failed, " + "using user port" + ,client_socket, service[i].protocol, ERROR_VALUE, service[i].port); + inet_setaddrport(&addr, 0); + result=bind(client_socket, (struct sockaddr *) &addr, addr_len); + } + if(result!=0) { + FREE_AND_NULL(udp_buf); + lprintf(LOG_ERR,"%04d %s !ERROR %d re-binding socket to port %u" + ,client_socket, service[i].protocol, ERROR_VALUE, service[i].port); + close_socket(client_socket); + continue; + } + + /* Set client address as default addres for send/recv */ + if(connect(client_socket + ,(struct sockaddr *)&client_addr, client_addr_len)!=0) { + FREE_AND_NULL(udp_buf); + lprintf(LOG_ERR,"%04d %s !ERROR %d connect failed" + ,client_socket, service[i].protocol, ERROR_VALUE); + close_socket(client_socket); + continue; + } + + } else { + /* TCP */ + if((client_socket=accept(service[i].set->socks[j].sock + ,(struct sockaddr *)&client_addr, &client_addr_len))==INVALID_SOCKET) { + if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL) + lprintf(LOG_NOTICE,"%04d %s socket closed while listening" + ,service[i].set->socks[j].sock, service[i].protocol); + else + lprintf(LOG_WARNING,"%04d %s !ERROR %d accepting connection" + ,service[i].set->socks[j].sock, service[i].protocol, ERROR_VALUE); + #ifdef _WIN32 + if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */ + break; + #endif + continue; + } + if(startup->socket_open!=NULL) /* Callback, increments socket counter */ + startup->socket_open(startup->cbdata,TRUE); } + inet_addrtop(&client_addr, host_ip, sizeof(host_ip)); - if((client_socket = open_socket(SOCK_DGRAM, service[i].protocol)) - ==INVALID_SOCKET) { + if(trashcan(&scfg,host_ip,"ip-silent")) { FREE_AND_NULL(udp_buf); - lprintf(LOG_ERR,"%04d %s !ERROR %d opening socket" - ,service[i].socket, service[i].protocol, ERROR_VALUE); + close_socket(client_socket); continue; } - lprintf(LOG_DEBUG,"%04d %s created client socket: %d" - ,service[i].socket, service[i].protocol, client_socket); + lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u" + ,client_socket + ,service[i].protocol, host_ip, inet_addrport(&client_addr)); - /* We need to set the REUSE ADDRESS socket option */ - optval=TRUE; - if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEADDR - ,(char*)&optval,sizeof(optval))!=0) { - FREE_AND_NULL(udp_buf); - lprintf(LOG_ERR,"%04d %s !ERROR %d setting socket option" - ,client_socket, service[i].protocol, ERROR_VALUE); - close_socket(client_socket); - continue; - } - #ifdef BSD - if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEPORT - ,(char*)&optval,sizeof(optval))!=0) { - FREE_AND_NULL(udp_buf); - lprintf(LOG_ERR,"%04d %s !ERROR %d setting socket option" - ,client_socket, service[i].protocol, ERROR_VALUE); + if(service[i].max_clients && service[i].clients+1>service[i].max_clients) { + lprintf(LOG_WARNING,"%04d !%s MAXIMUM CLIENTS (%u) reached, access denied" + ,client_socket, service[i].protocol, service[i].max_clients); + mswait(3000); close_socket(client_socket); continue; } - #endif - - memset(&addr, 0, sizeof(addr)); - addr.sin_addr.s_addr = htonl(service[i].interface_addr); - addr.sin_family = AF_INET; - addr.sin_port = htons(service[i].port); - - result=bind(client_socket, (struct sockaddr *) &addr, sizeof(addr)); - if(result==SOCKET_ERROR) { - /* Failed to re-bind to same port number, use user port */ - lprintf(LOG_NOTICE,"%04d %s ERROR %d re-binding socket to port %u failed, " - "using user port" - ,client_socket, service[i].protocol, ERROR_VALUE, service[i].port); - addr.sin_port=0; - result=bind(client_socket, (struct sockaddr *) &addr, sizeof(addr)); - } - if(result!=0) { + + #ifdef _WIN32 + if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE) + && !(service[i].options&BBS_OPT_MUTE)) + PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME); + #endif + + if(trashcan(&scfg,host_ip,"ip")) { FREE_AND_NULL(udp_buf); - lprintf(LOG_ERR,"%04d %s !ERROR %d re-binding socket to port %u" - ,client_socket, service[i].protocol, ERROR_VALUE, service[i].port); + lprintf(LOG_NOTICE,"%04d !%s CLIENT BLOCKED in ip.can: %s" + ,client_socket, service[i].protocol, host_ip); + mswait(3000); close_socket(client_socket); continue; } - /* Set client address as default addres for send/recv */ - if(connect(client_socket - ,(struct sockaddr *)&client_addr, client_addr_len)!=0) { + if((client=malloc(sizeof(service_client_t)))==NULL) { FREE_AND_NULL(udp_buf); - lprintf(LOG_ERR,"%04d %s !ERROR %d connect failed" - ,client_socket, service[i].protocol, ERROR_VALUE); + lprintf(LOG_CRIT,"%04d !%s ERROR allocating %u bytes of memory for service_client" + ,client_socket, service[i].protocol, sizeof(service_client_t)); + mswait(3000); close_socket(client_socket); continue; } - } else { - /* TCP */ - if((client_socket=accept(service[i].socket - ,(struct sockaddr *)&client_addr, &client_addr_len))==INVALID_SOCKET) { - if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL) - lprintf(LOG_NOTICE,"%04d %s socket closed while listening" - ,service[i].socket, service[i].protocol); - else - lprintf(LOG_WARNING,"%04d %s !ERROR %d accepting connection" - ,service[i].socket, service[i].protocol, ERROR_VALUE); -#ifdef _WIN32 - if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */ - break; -#endif - continue; - } - if(startup->socket_open!=NULL) /* Callback, increments socket counter */ - startup->socket_open(startup->cbdata,TRUE); - } - SAFECOPY(host_ip,inet_ntoa(client_addr.sin_addr)); - - if(trashcan(&scfg,host_ip,"ip-silent")) { - FREE_AND_NULL(udp_buf); - close_socket(client_socket); - continue; - } - - lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u" - ,client_socket - ,service[i].protocol, host_ip, ntohs(client_addr.sin_port)); - - if(service[i].max_clients && service[i].clients+1>service[i].max_clients) { - lprintf(LOG_WARNING,"%04d !%s MAXIMUM CLIENTS (%u) reached, access denied" - ,client_socket, service[i].protocol, service[i].max_clients); - mswait(3000); - close_socket(client_socket); - continue; - } - -#ifdef _WIN32 - if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE) - && !(service[i].options&BBS_OPT_MUTE)) - PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME); -#endif - - if(trashcan(&scfg,host_ip,"ip")) { - FREE_AND_NULL(udp_buf); - lprintf(LOG_NOTICE,"%04d !%s CLIENT BLOCKED in ip.can: %s" - ,client_socket, service[i].protocol, host_ip); - mswait(3000); - close_socket(client_socket); - continue; + memset(client,0,sizeof(service_client_t)); + client->socket=client_socket; + client->addr=client_addr; + client->service=&service[i]; + client->service->clients++; /* this should be mutually exclusive */ + client->udp_buf=udp_buf; + client->udp_len=udp_len; + client->callback.limit = service[i].js.time_limit; + client->callback.gc_interval = service[i].js.gc_interval; + client->callback.yield_interval = service[i].js.yield_interval; + client->callback.terminated = &client->service->terminated; + client->callback.auto_terminate = TRUE; + + udp_buf = NULL; + + protected_uint32_adjust(&threads_pending_start, 1); + if(service[i].options&SERVICE_OPT_NATIVE) /* Native */ + _beginthread(native_service_thread, service[i].stack_size, client); + else /* JavaScript */ + _beginthread(js_service_thread, service[i].stack_size, client); + service[i].served++; + served++; } - - if((client=malloc(sizeof(service_client_t)))==NULL) { - FREE_AND_NULL(udp_buf); - lprintf(LOG_CRIT,"%04d !%s ERROR allocating %u bytes of memory for service_client" - ,client_socket, service[i].protocol, sizeof(service_client_t)); - mswait(3000); - close_socket(client_socket); - continue; - } - - memset(client,0,sizeof(service_client_t)); - client->socket=client_socket; - client->addr=client_addr; - client->service=&service[i]; - client->service->clients++; /* this should be mutually exclusive */ - client->udp_buf=udp_buf; - client->udp_len=udp_len; - client->callback.limit = service[i].js.time_limit; - client->callback.gc_interval = service[i].js.gc_interval; - client->callback.yield_interval = service[i].js.yield_interval; - client->callback.terminated = &client->service->terminated; - client->callback.auto_terminate = TRUE; - - udp_buf = NULL; - - protected_uint32_adjust(&threads_pending_start, 1); - if(service[i].options&SERVICE_OPT_NATIVE) /* Native */ - _beginthread(native_service_thread, service[i].stack_size, client); - else /* JavaScript */ - _beginthread(js_service_thread, service[i].stack_size, client); - service[i].served++; - served++; } } @@ -2231,12 +2151,12 @@ void DLLCALL services_thread(void* arg) lprintf(LOG_DEBUG,"0000 Closing service sockets"); for(i=0;i<(int)services;i++) { service[i].terminated=TRUE; - if(service[i].socket==INVALID_SOCKET) + if(service[i].set==NULL) continue; if(service[i].options&SERVICE_OPT_STATIC) continue; - close_socket(service[i].socket); - service[i].socket=INVALID_SOCKET; + xpms_destroy(service[i].set, close_socket_cb, &service[i]); + service[i].set=NULL; } /* Wait for Dynamic Service Threads to terminate */ diff --git a/src/sbbs3/services.h b/src/sbbs3/services.h index 317234f2c0b19d05851250507aca783841e5c499..642073f62c311fd8c1fea72d69150bc3d30ee73d 100644 --- a/src/sbbs3/services.h +++ b/src/sbbs3/services.h @@ -43,7 +43,9 @@ typedef struct { DWORD size; /* sizeof(bbs_struct_t) */ - DWORD interface_addr; + struct in_addr outgoing4; + struct in6_addr outgoing6; + str_list_t interfaces; DWORD options; /* See BBS_OPT definitions */ WORD sem_chk_freq; /* semaphore file checking frequency (in seconds) */ @@ -92,7 +94,8 @@ typedef struct { #if 0 /* startup options that requires re-initialization/recycle when changed */ static struct init_field services_init_fields[] = { - OFFSET_AND_SIZE(services_startup_t,interface_addr) + OFFSET_AND_SIZE(services_startup_t,outgoing4) + OFFSET_AND_SIZE(services_startup_t,outgoing6) ,OFFSET_AND_SIZE(services_startup_t,ctrl_dir) ,{ 0,0 } /* terminator */ }; @@ -104,6 +107,7 @@ static struct init_field services_init_fields[] = { #define SERVICE_OPT_STATIC_LOOP (1<<2) /* Loop static service until terminated */ #define SERVICE_OPT_NATIVE (1<<3) /* non-JavaScript service */ #define SERVICE_OPT_FULL_ACCEPT (1<<4) /* Accept/close connections when server is full */ +#define SERVICE_OPT_TLS (1<<5) /* Use TLS */ /* services_startup_t.options bits that require re-init/recycle when changed */ #define SERVICE_INIT_OPTS (0) @@ -120,6 +124,7 @@ static ini_bitdesc_t service_options[] = { { SERVICE_OPT_STATIC_LOOP ,"LOOP" }, { SERVICE_OPT_NATIVE ,"NATIVE" }, { SERVICE_OPT_FULL_ACCEPT ,"FULL_ACCEPT" }, + { SERVICE_OPT_TLS ,"TLS" }, /* terminator */ { 0 ,NULL } }; diff --git a/src/sbbs3/sockopts.c b/src/sbbs3/sockopts.c index 91e1aad35164fb339629ebf712e4511d4eb02e4e..5a484d10903878d1f6f8b6411118a17e3332abe6 100644 --- a/src/sbbs3/sockopts.c +++ b/src/sbbs3/sockopts.c @@ -42,7 +42,7 @@ int DLLCALL set_socket_options(scfg_t* cfg, SOCKET sock, const char* protocol, c { char cfgfile[MAX_PATH+1]; FILE* fp; - int type; + int type=0; // Assignment is to silence Valgrind int result=0; str_list_t list; socklen_t len; diff --git a/src/sbbs3/ssl.c b/src/sbbs3/ssl.c index 40987e5575897ff31a5fb39c360ab121ec6ff712..ff78893597f6a2356abc5d592b07c0721c39cc88 100644 --- a/src/sbbs3/ssl.c +++ b/src/sbbs3/ssl.c @@ -25,7 +25,7 @@ static bool get_error_string(int status, CRYPT_SESSION sess, char *estr, char *f #define DO(x) get_error_string(x, ssl_context, estr, __FILE__, __LINE__) -CRYPT_CONTEXT get_ssl_cert(scfg_t *cfg, char *estr) +CRYPT_CONTEXT DLLCALL get_ssl_cert(scfg_t *cfg, char *estr) { CRYPT_KEYSET ssl_keyset; CRYPT_CONTEXT ssl_context; diff --git a/src/sbbs3/ssl.h b/src/sbbs3/ssl.h index 1b4a9bfd81be1f5ee4f9b2f8a677b3d2d5bc6c84..820c3b5b1b1cd307e3213a6b69e827350c4809ea 100644 --- a/src/sbbs3/ssl.h +++ b/src/sbbs3/ssl.h @@ -1,11 +1,12 @@ #ifndef SBBS_SSL_H #define SBBS_SSL_H +#include "sbbs.h" // For DLLEXPORT #include <cryptlib.h> #include "scfgdefs.h" #define SSL_ESTR_LEN CRYPT_MAX_TEXTSIZE + 1024 /* File name, line number, status code, and some static text */ -CRYPT_CONTEXT get_ssl_cert(scfg_t *cfg, char *estr); +DLLEXPORT CRYPT_CONTEXT DLLCALL get_ssl_cert(scfg_t *cfg, char *estr); #endif diff --git a/src/sbbs3/startup.h b/src/sbbs3/startup.h index 8e821e9ed6962fcb2ed40ca6017f55f4c016e920..5be96476f241f07f17a397baa3d4fdabab59205c 100644 --- a/src/sbbs3/startup.h +++ b/src/sbbs3/startup.h @@ -64,7 +64,9 @@ typedef struct { char temp_dir[INI_MAX_VALUE_LEN]; char host_name[INI_MAX_VALUE_LEN]; ushort sem_chk_freq; - ulong interface_addr; + struct in_addr outgoing4; + struct in6_addr outgoing6; + str_list_t interfaces; int log_level; js_startup_t js; uint bind_retry_count; /* Number of times to retry bind() calls */ @@ -87,10 +89,12 @@ typedef struct { WORD outbuf_highwater_mark; /* output block size control */ WORD outbuf_drain_timeout; WORD sem_chk_freq; /* semaphore file checking frequency (in seconds) */ - uint32_t telnet_interface; + struct in_addr outgoing4; + struct in6_addr outgoing6; + str_list_t telnet_interfaces; uint32_t options; /* See BBS_OPT definitions */ - DWORD rlogin_interface; - DWORD ssh_interface; + str_list_t rlogin_interfaces; + str_list_t ssh_interfaces; RingBuf** node_spybuf; /* Spy output buffer (each node) */ RingBuf** node_inbuf; /* User input buffer (each node) */ sem_t** node_spysem; /* Spy output semaphore (each node) */ @@ -154,8 +158,9 @@ static struct init_field { ,OFFSET_AND_SIZE(bbs_startup_t,last_node) ,OFFSET_AND_SIZE(bbs_startup_t,telnet_port) ,OFFSET_AND_SIZE(bbs_startup_t,rlogin_port) - ,OFFSET_AND_SIZE(bbs_startup_t,telnet_interface) - ,OFFSET_AND_SIZE(bbs_startup_t,rlogin_interface) + ,OFFSET_AND_SIZE(bbs_startup_t,telnet_interfaces) + ,OFFSET_AND_SIZE(bbs_startup_t,rlogin_interfaces) + ,OFFSET_AND_SIZE(bbs_startup_t,ssh_interfaces) ,OFFSET_AND_SIZE(bbs_startup_t,ctrl_dir) ,OFFSET_AND_SIZE(bbs_startup_t,temp_dir) ,{ 0,0 } /* terminator */ diff --git a/src/sbbs3/str.cpp b/src/sbbs3/str.cpp index 59d7953bc339fa7c39de7b37a372f3630c4f856e..666c3be7406d3c57041ba1a39a25a0aa2bb0239e 100644 --- a/src/sbbs3/str.cpp +++ b/src/sbbs3/str.cpp @@ -95,14 +95,14 @@ void sbbs_t::userlist(long mode) } sprintf(name,"%s #%d",user.alias,i); sprintf(line[j],text[UserListFmt],name - ,cfg.sys_misc&SM_LISTLOC ? user.location : user.note + ,cfg.sys_misc&SM_LISTLOC ? user.location : user.ipaddr ,unixtodstr(&cfg,user.laston,tmp) ,user.modem); } else { sprintf(name,"%s #%u",user.alias,i); bprintf(text[UserListFmt],name - ,cfg.sys_misc&SM_LISTLOC ? user.location : user.note + ,cfg.sys_misc&SM_LISTLOC ? user.location : user.ipaddr ,unixtodstr(&cfg,user.laston,tmp) ,user.modem); } diff --git a/src/sbbs3/telgate.cpp b/src/sbbs3/telgate.cpp index 5cfa402f3b90e491884ea0346675c438302fe1d6..ed606b46de4836b7febd60e8f7f8fb1a79e64f91 100644 --- a/src/sbbs3/telgate.cpp +++ b/src/sbbs3/telgate.cpp @@ -77,7 +77,7 @@ void sbbs_t::telnet_gate(char* destaddr, ulong mode, char* client_user_name, cha } memset(&addr,0,sizeof(addr)); - addr.sin_addr.s_addr = htonl(startup->telnet_interface); + addr.sin_addr.s_addr = htonl(startup->outgoing4.s_addr); addr.sin_family = AF_INET; if((i=bind(remote_socket, (struct sockaddr *) &addr, sizeof (addr)))!=0) { diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c index 659ab360bea2fb944840c82a79a6fa7a680a086f..00317c00f1b1c938f5fdf383013432aa87f4d89d 100644 --- a/src/sbbs3/userdat.c +++ b/src/sbbs3/userdat.c @@ -263,6 +263,7 @@ int DLLCALL getuserdat(scfg_t* cfg, user_t *user) getrec(userdat,U_PHONE,LEN_PHONE,user->phone); getrec(userdat,U_BIRTH,LEN_BIRTH,user->birth); getrec(userdat,U_MODEM,LEN_MODEM,user->modem); + getrec(userdat,U_IPADDR,LEN_IPADDR,user->ipaddr); getrec(userdat,U_LASTON,8,str); user->laston=ahtoul(str); getrec(userdat,U_FIRSTON,8,str); user->firston=ahtoul(str); getrec(userdat,U_EXPIRE,8,str); user->expire=ahtoul(str); @@ -433,6 +434,7 @@ int DLLCALL putuserdat(scfg_t* cfg, user_t* user) putrec(userdat,U_PHONE,LEN_PHONE,user->phone); putrec(userdat,U_BIRTH,LEN_BIRTH,user->birth); putrec(userdat,U_MODEM,LEN_MODEM,user->modem); + putrec(userdat,U_IPADDR,LEN_IPADDR,user->modem); putrec(userdat,U_LASTON,8,ultoa((ulong)user->laston,str,16)); putrec(userdat,U_FIRSTON,8,ultoa((ulong)user->firston,str,16)); putrec(userdat,U_EXPIRE,8,ultoa((ulong)user->expire,str,16)); @@ -481,7 +483,7 @@ int DLLCALL putuserdat(scfg_t* cfg, user_t* user) putrec(userdat,U_XFER_CMD+LEN_XFER_CMD,2,crlf); - putrec(userdat,U_MAIL_CMD+LEN_MAIL_CMD,2,crlf); + putrec(userdat,U_IPADDR+LEN_IPADDR,2,crlf); putrec(userdat,U_FREECDT,10,ultoa(user->freecdt,str,10)); @@ -1835,7 +1837,7 @@ static BOOL ar_exp(scfg_t* cfg, uchar **ptrptr, user_t* user, client_t* client) if(client!=NULL) p=client->addr; else if(user!=NULL) - p=user->note; + p=user->ipaddr; else p=NULL; if(!findstr_in_string(p,(char*)*ptrptr)) @@ -2407,6 +2409,7 @@ int DLLCALL user_rec_len(int offset) case U_PHONE: return(LEN_PHONE); case U_BIRTH: return(LEN_BIRTH); case U_MODEM: return(LEN_MODEM); + case U_IPADDR: return(LEN_IPADDR); /* Internal codes (16 chars) */ case U_CURSUB: @@ -2734,27 +2737,39 @@ long DLLCALL loginAttemptListClear(link_list_t* list) } /****************************************************************************/ -static list_node_t* login_attempted(link_list_t* list, const SOCKADDR_IN* addr) +static list_node_t* login_attempted(link_list_t* list, const union xp_sockaddr* addr) { list_node_t* node; login_attempt_t* attempt; + struct in6_addr ia; if(list==NULL) return NULL; for(node=list->first; node!=NULL; node=node->next) { attempt=node->data; - if(memcmp(&attempt->addr,&addr->sin_addr,sizeof(attempt->addr))==0) + switch(addr->addr.sa_family) { + case AF_INET: + memset(&ia, 0, sizeof(ia)); + memcpy(&ia, &addr->in.sin_addr, sizeof(addr->in.sin_addr)); + break; + case AF_INET6: + ia = addr->in6.sin6_addr; + break; + } + if(memcmp(&attempt->addr,&ia,sizeof(attempt->addr))==0) break; } return node; } /****************************************************************************/ -long DLLCALL loginAttempts(link_list_t* list, const SOCKADDR_IN* addr) +long DLLCALL loginAttempts(link_list_t* list, const union xp_sockaddr* addr) { long count=0; list_node_t* node; + if(addr->addr.sa_family != AF_INET && addr->addr.sa_family != AF_INET6) + return 0; listLock(list); if((node=login_attempted(list, addr))!=NULL) count = ((login_attempt_t*)node->data)->count - ((login_attempt_t*)node->data)->dupes; @@ -2764,10 +2779,12 @@ long DLLCALL loginAttempts(link_list_t* list, const SOCKADDR_IN* addr) } /****************************************************************************/ -void DLLCALL loginSuccess(link_list_t* list, const SOCKADDR_IN* addr) +void DLLCALL loginSuccess(link_list_t* list, const union xp_sockaddr* addr) { list_node_t* node; + if(addr->addr.sa_family != AF_INET && addr->addr.sa_family != AF_INET6) + return; listLock(list); if((node=login_attempted(list, addr)) != NULL) listRemoveNode(list, node, /* freeData: */TRUE); @@ -2777,13 +2794,15 @@ void DLLCALL loginSuccess(link_list_t* list, const SOCKADDR_IN* addr) /****************************************************************************/ /* Returns number of *unique* login attempts (excludes consecutive dupes) */ /****************************************************************************/ -ulong DLLCALL loginFailure(link_list_t* list, const SOCKADDR_IN* addr, const char* prot, const char* user, const char* pass) +ulong DLLCALL loginFailure(link_list_t* list, const union xp_sockaddr* addr, const char* prot, const char* user, const char* pass) { list_node_t* node; login_attempt_t first; login_attempt_t* attempt=&first; ulong count=0; + if(addr->addr.sa_family != AF_INET && addr->addr.sa_family != AF_INET6) + return 0; if(list==NULL) return 0; memset(&first, 0, sizeof(first)); @@ -2796,7 +2815,16 @@ ulong DLLCALL loginFailure(link_list_t* list, const SOCKADDR_IN* addr, const cha } SAFECOPY(attempt->prot,prot); attempt->time=time32(NULL); - attempt->addr=addr->sin_addr; + memset(&attempt->addr, 0, sizeof(attempt->addr)); + attempt->family = addr->addr.sa_family; + switch(addr->addr.sa_family) { + case AF_INET: + memcpy(&attempt->addr, &addr->in.sin_addr, sizeof(addr->in.sin_addr)); + break; + case AF_INET6: + memcpy(&attempt->addr, &addr->in6.sin6_addr, sizeof(addr->in6.sin6_addr)); + break; + } SAFECOPY(attempt->user, user); SAFECOPY(attempt->pass, pass); attempt->count++; diff --git a/src/sbbs3/userdat.h b/src/sbbs3/userdat.h index d90fd1ddb5c7b41bb7391cdf9c65185e7519245a..846afda9ed9e66b374ac7090fef25d9a7ea6c7d6 100644 --- a/src/sbbs3/userdat.h +++ b/src/sbbs3/userdat.h @@ -135,7 +135,8 @@ DLLEXPORT BOOL DLLCALL check_name(scfg_t* cfg, const char* name); /* Login attempt/hack tracking */ typedef struct { - IN_ADDR addr; /* host with consecutive failed login attmepts */ + struct in6_addr addr; /* host with consecutive failed login attmepts */ + sa_family_t family; ulong count; /* number of consecutive failed login attempts */ ulong dupes; /* number of consecutive dupliate login attempts (same name and password) */ time32_t time; /* time of last attempt */ @@ -147,9 +148,9 @@ typedef struct { DLLEXPORT link_list_t* DLLCALL loginAttemptListInit(link_list_t*); DLLEXPORT BOOL DLLCALL loginAttemptListFree(link_list_t*); DLLEXPORT long DLLCALL loginAttemptListClear(link_list_t*); -DLLEXPORT long DLLCALL loginAttempts(link_list_t*, const SOCKADDR_IN*); -DLLEXPORT void DLLCALL loginSuccess(link_list_t*, const SOCKADDR_IN*); -DLLEXPORT ulong DLLCALL loginFailure(link_list_t*, const SOCKADDR_IN*, const char* prot, const char* user, const char* pass); +DLLEXPORT long DLLCALL loginAttempts(link_list_t*, const union xp_sockaddr*); +DLLEXPORT void DLLCALL loginSuccess(link_list_t*, const union xp_sockaddr*); +DLLEXPORT ulong DLLCALL loginFailure(link_list_t*, const union xp_sockaddr*, const char* prot, const char* user, const char* pass); #ifdef __cplusplus } diff --git a/src/sbbs3/useredit/MainFormUnit.pas b/src/sbbs3/useredit/MainFormUnit.pas index 3b834e655e6289956ed1ad13d480a406156752c6..a8a92e925ec21a8c341d2f6f234e086beb079ccd 100644 --- a/src/sbbs3/useredit/MainFormUnit.pas +++ b/src/sbbs3/useredit/MainFormUnit.pas @@ -266,8 +266,8 @@ const { String lengths } LEN_TITLE =70; { Message title } LEN_MAIN_CMD =40; { Storage in user.dat for custom commands } LEN_XFER_CMD =40; - LEN_SCAN_CMD =40; - LEN_MAIL_CMD =40; + LEN_SCAN_CMD =35; + LEN_IPADDR =45; LEN_CID =25; { Caller ID (phone number) } LEN_ARSTR =40; { Max length of Access Requirement string } LEN_CHATACTCMD =9; { Chat action command } @@ -335,8 +335,8 @@ const U_MAIN_CMD =U_CMDSET+2+2; { unused } U_XFER_CMD =U_MAIN_CMD+LEN_MAIN_CMD; { unused } U_SCAN_CMD =U_XFER_CMD+LEN_XFER_CMD+2; { unused } - U_MAIL_CMD =U_SCAN_CMD+LEN_SCAN_CMD; { unused } - U_FREECDT =U_MAIL_CMD+LEN_MAIL_CMD+2; + U_IPADDR =U_SCAN_CMD+LEN_SCAN_CMD; { unused } + U_FREECDT =U_IPADDR+LEN_IPADDR+2; U_FLAGS3 =U_FREECDT+10; { Flag set #3 } U_FLAGS4 =U_FLAGS3+8; { Flag set #4 } U_XEDIT =U_FLAGS4+8; { External editor (code) } diff --git a/src/sbbs3/v4upgrade.c b/src/sbbs3/v4upgrade.c index b39f3cd650a4e9c0d8e221c7bc9b5ea6466f7dde..827a7449524ee02185847e435f18db743a91dd84 100644 --- a/src/sbbs3/v4upgrade.c +++ b/src/sbbs3/v4upgrade.c @@ -136,11 +136,12 @@ BOOL upgrade_users(void) } /******************************************/ /* personal info */ - len=sprintf(rec,"%s\t%s\t%s\t%s\t%s\t%s\t" + len=sprintf(rec,"%s\t%s\t%s\t%s\t%s\t%s\t%s\t" ,user.alias ,user.name ,user.handle ,user.note + ,user.ipaddr ,user.comp ,user.comment ); diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c index 7de6ff0c738854884647313b19e3d7440a64ff09..a8f4a146ba7aa642c7624dfb34c4428921f77612 100644 --- a/src/sbbs3/websrvr.c +++ b/src/sbbs3/websrvr.c @@ -48,8 +48,6 @@ * */ -//#define ONE_JS_RUNTIME - /* Headers for CGI stuff */ #if defined(__unix__) #include <sys/wait.h> /* waitpid() */ @@ -65,6 +63,7 @@ #include "sbbs.h" #include "sbbsdefs.h" #include "sockwrap.h" /* sendfilesocket() */ +#include "multisock.h" #include "threadwrap.h" #include "semwrap.h" #include "websrvr.h" @@ -72,8 +71,10 @@ #include "md5.h" #include "js_rtpool.h" #include "js_request.h" +#include "js_socket.h" #include "xpmap.h" #include "xpprintf.h" +#include "ssl.h" static const char* server_name="Synchronet Web Server"; static const char* newline="\r\n"; @@ -84,7 +85,9 @@ static const char* error_302="302 Moved Temporarily"; static const char* error_404="404 Not Found"; static const char* error_416="416 Requested Range Not Satisfiable"; static const char* error_500="500 Internal Server Error"; +static const char* error_503="503 Service Unavailable\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"; static const char* unknown="<unknown>"; +static int len_503 = 0; #define TIMEOUT_THREAD_WAIT 60 /* Seconds */ #define MAX_REQUEST_LINE 1024 /* NOT including terminator */ @@ -106,8 +109,9 @@ static protected_uint32_t active_clients; static protected_uint32_t thread_count; static volatile ulong sockets=0; static volatile BOOL terminate_server=FALSE; +static volatile BOOL terminated=FALSE; static volatile BOOL terminate_http_logging_thread=FALSE; -static SOCKET server_socket=INVALID_SOCKET; +static struct xpms_set *ws_set=NULL; static char revision[16]; static char root_dir[MAX_PATH+1]; static char error_dir[MAX_PATH+1]; @@ -146,12 +150,14 @@ enum auth_type { AUTHENTICATION_UNKNOWN ,AUTHENTICATION_BASIC ,AUTHENTICATION_DIGEST + ,AUTHENTICATION_TLS_PSK }; -char *auth_type_names[4] = { +char *auth_type_names[] = { "Unknown" ,"Basic" ,"Digest" + ,"TLS-PSK" ,NULL }; @@ -236,11 +242,10 @@ typedef struct { typedef struct { SOCKET socket; - SOCKADDR_IN addr; - SOCKET socket6; - SOCKADDR_IN addr6; + union xp_sockaddr addr; + socklen_t addr_len; http_request_t req; - char host_ip[64]; + char host_ip[INET6_ADDRSTRLEN]; char host_name[128]; /* Resolved remote host */ int http_ver; /* HTTP version. 0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */ BOOL finished; /* Do not accept any more imput from client */ @@ -272,9 +277,18 @@ typedef struct { /* Synchronization stuff */ pthread_mutex_t struct_filled; + + /* TLS Stuff */ + BOOL is_tls; + CRYPT_SESSION tls_sess; + BOOL tls_pending; + BOOL peeked_valid; + char peeked; } http_session_t; -enum { +static CRYPT_CONTEXT tls_context = -1; + +enum { HTTP_0_9 ,HTTP_1_0 ,HTTP_1_1 @@ -517,9 +531,10 @@ static int writebuf(http_session_t *session, const char *buf, size_t len) size_t avail; while(sent < len) { + ResetEvent(session->outbuf.empty_event); avail=RingBufFree(&session->outbuf); if(!avail) { - SLEEP(1); + WaitForEvent(session->outbuf.empty_event, 1); continue; } if(avail > len-sent) @@ -529,47 +544,114 @@ static int writebuf(http_session_t *session, const char *buf, size_t len) return(sent); } -static int sock_sendbuf(SOCKET *sock, const char *buf, size_t len, BOOL *failed) +#define HANDLE_CRYPT_CALL(status, session) handle_crypt_call(status, session, __FILE__, __LINE__) + +static BOOL handle_crypt_call(int status, http_session_t *session, const char *file, int line) +{ + int len = 0; + char estr[CRYPT_MAX_TEXTSIZE+1]; + int sock = 0; + + if (status == CRYPT_OK) + return TRUE; + if (session != NULL) { + if (session->is_tls) + cryptGetAttributeString(session->tls_sess, CRYPT_ATTRIBUTE_ERRORMESSAGE, estr, &len); + sock = session->socket; + } + estr[len]=0; + if (len) + lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d (%s)", sock, status, file, line, estr); + else + lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d", sock, status, file, line); + return FALSE; +} + +static BOOL session_check(http_session_t *session, BOOL *rd, BOOL *wr, unsigned timeout) +{ + BOOL ret = FALSE; + BOOL lcl_rd; + BOOL *rd_ptr = rd?rd:&lcl_rd; + + if (session->is_tls) { + if(wr) + *wr=1; + if(rd) { + if(session->tls_pending) { + *rd = TRUE; + return TRUE; + } + } + ret = socket_check(session->socket, rd_ptr, wr, timeout); + if (ret && *rd_ptr) { + session->tls_pending = TRUE; + return TRUE; + } + return ret; + } + return socket_check(session->socket, rd, wr, timeout); +} + +static int sess_sendbuf(http_session_t *session, const char *buf, size_t len, BOOL *failed) { size_t sent=0; + int tls_sent; int result; int sel; fd_set wr_set; struct timeval tv; + int status; - while(sent<len && *sock!=INVALID_SOCKET) { + while(sent<len && session->socket!=INVALID_SOCKET) { FD_ZERO(&wr_set); - FD_SET(*sock,&wr_set); + FD_SET(session->socket,&wr_set); /* Convert timeout from ms to sec/usec */ tv.tv_sec=startup->max_inactivity; tv.tv_usec=0; - sel=select(*sock+1,NULL,&wr_set,NULL,&tv); + sel=select(session->socket+1,NULL,&wr_set,NULL,&tv); switch(sel) { case 1: - result=sendsocket(*sock,buf+sent,len-sent); - if(result==SOCKET_ERROR) { - if(ERROR_VALUE==ECONNRESET) - lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",*sock); - else if(ERROR_VALUE==ECONNABORTED) - lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",*sock); + if (session->is_tls) { + status = cryptPushData(session->tls_sess, buf+sent, len-sent, &tls_sent); + if (status == CRYPT_ERROR_TIMEOUT) { + tls_sent = 0; + cryptPopData(session->tls_sess, "", 0, &status); + status = CRYPT_OK; + } + if(!HANDLE_CRYPT_CALL(status, session)) { + HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session); + if (failed) + *failed=TRUE; + return tls_sent; + } + result = tls_sent; + } + else { + result=sendsocket(session->socket,buf+sent,len-sent); + if(result==SOCKET_ERROR) { + if(ERROR_VALUE==ECONNRESET) + lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",session->socket); + else if(ERROR_VALUE==ECONNABORTED) + lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",session->socket); #ifdef EPIPE - else if(ERROR_VALUE==EPIPE) - lprintf(LOG_NOTICE,"%04d Unable to send to peer",*sock); + else if(ERROR_VALUE==EPIPE) + lprintf(LOG_NOTICE,"%04d Unable to send to peer",session->socket); #endif - else - lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",*sock,ERROR_VALUE); - if(failed) - *failed=TRUE; - return(sent); + else + lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",session->socket,ERROR_VALUE); + if(failed) + *failed=TRUE; + return(sent); + } } break; case 0: - lprintf(LOG_WARNING,"%04d Timeout selecting socket for write",*sock); + lprintf(LOG_WARNING,"%04d Timeout selecting socket for write",session->socket); if(failed) *failed=TRUE; return(sent); case -1: - lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for write",*sock,ERROR_VALUE); + lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for write",session->socket,ERROR_VALUE); if(failed) *failed=TRUE; return(sent); @@ -578,6 +660,8 @@ static int sock_sendbuf(SOCKET *sock, const char *buf, size_t len, BOOL *failed) } if(failed && sent<len) *failed=TRUE; + if(session->is_tls) + HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session); return(sent); } @@ -801,21 +885,35 @@ static time_t decode_date(char *date) return(t); } -static SOCKET open_socket(int type) +static void open_socket(SOCKET sock, void *cbdata) { char error[256]; - SOCKET sock; +#ifdef SO_ACCEPTFILTER + struct accept_filter_arg afa; +#endif - sock=socket(AF_INET, type, IPPROTO_IP); - if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) - startup->socket_open(startup->cbdata,TRUE); - if(sock!=INVALID_SOCKET) { + startup->socket_open(startup->cbdata,TRUE); + if (cbdata != NULL && !strcmp(cbdata, "TLS")) { + if(set_socket_options(&scfg, sock, "web|http|tls", error, sizeof(error))) + lprintf(LOG_ERR,"%04d !ERROR %s",sock,error); + } + else { if(set_socket_options(&scfg, sock, "web|http", error, sizeof(error))) lprintf(LOG_ERR,"%04d !ERROR %s",sock,error); - - sockets++; } - return(sock); +#ifdef SO_ACCEPTFILTER + memset(&afa, 0, sizeof(afa)); + strcpy(afa.af_name, "httpready"); + setsockopt(sock, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)); +#endif + + sockets++; +} + +static void close_socket_cb(SOCKET sock, void *cbdata) +{ + startup->socket_open(startup->cbdata,FALSE); + sockets--; } static int close_socket(SOCKET *sock) @@ -841,6 +939,31 @@ static int close_socket(SOCKET *sock) return(result); } +static int close_session_socket(http_session_t *session) +{ + char buf[1]; + int len; + + if(session==NULL || session->socket==INVALID_SOCKET) + return(-1); + + if (session->is_tls) { + // First, wait for the ringbuffer to drain... + while(RingBufFull(&session->outbuf) && session->socket!=INVALID_SOCKET) { + HANDLE_CRYPT_CALL(cryptPopData(session->tls_sess, buf, 1, &len), session); + SLEEP(1); + } + // Now wait for tranmission to complete + while(pthread_mutex_trylock(&session->outbuf_write) == EBUSY) { + HANDLE_CRYPT_CALL(cryptPopData(session->tls_sess, buf, 1, &len), session); + SLEEP(1); + } + pthread_mutex_unlock(&session->outbuf_write); + HANDLE_CRYPT_CALL(cryptDestroySession(session->tls_sess), session); + } + return close_socket(&session->socket); +} + /* Waits for the outbuf to drain */ static void drain_outbuf(http_session_t * session) { @@ -921,7 +1044,7 @@ static void close_request(http_session_t * session) */ if((!session->req.keep_alive) || terminate_server) { drain_outbuf(session); - close_socket(&session->socket); + close_session_socket(session); } if(session->socket==INVALID_SOCKET) session->finished=TRUE; @@ -1381,7 +1504,7 @@ void http_logon(http_session_t * session, user_t *usr) /* Adjust Connect and host */ SAFECOPY(session->user.modem, session->client.protocol); SAFECOPY(session->user.comp, session->host_name); - SAFECOPY(session->user.note, session->host_ip); + SAFECOPY(session->user.ipaddr, session->host_ip); session->user.logontime = (time32_t)session->logon_time; putuserdat(&scfg, &session->user); } @@ -1696,6 +1819,18 @@ static BOOL check_ars(http_session_t * session) thisuser.number=i; getuserdat(&scfg, &thisuser); switch(session->req.auth.type) { + case AUTHENTICATION_TLS_PSK: + if((auth_allowed & (1<<AUTHENTICATION_TLS_PSK))==0) + return(FALSE); + if(session->last_user_num!=0) { + if(session->last_user_num>0) + http_logoff(session,session->socket,__LINE__); + session->user.number=0; + http_logon(session,NULL); + } + if(!http_checkuser(session)) + return(FALSE); + break; case AUTHENTICATION_BASIC: if((auth_allowed & (1<<AUTHENTICATION_BASIC))==0) return(FALSE); @@ -1752,6 +1887,9 @@ static BOOL check_ars(http_session_t * session) if(authorized) { switch(session->req.auth.type) { + case AUTHENTICATION_TLS_PSK: + add_env(session,"AUTH_TYPE","TLS-PSK"); + break; case AUTHENTICATION_BASIC: add_env(session,"AUTH_TYPE","Basic"); break; @@ -1800,6 +1938,46 @@ static named_string_t** read_ini_list(char* path, char* section, char* desc return(list); } +static int sess_recv(http_session_t *session, char *buf, size_t length, int flags) +{ + int len; + + if (session->is_tls) { + if (flags == MSG_PEEK) { + if (session->peeked_valid) { + buf[0] = session->peeked; + return 1; + } + if (HANDLE_CRYPT_CALL(cryptPopData(session->tls_sess, &session->peeked, 1, &len), session)) { + if (len == 1) { + session->peeked_valid = TRUE; + buf[0] = session->peeked; + return 1; + } + return 0; + } + return -1; + } + else { + if (session->peeked_valid) { + buf[0] = session->peeked; + session->peeked_valid = FALSE; + return 1; + } + if (HANDLE_CRYPT_CALL(cryptPopData(session->tls_sess, buf, length, &len), session)) { + if (len == 0) { + session->tls_pending = FALSE; + } + return len; + } + return -1; + } + } + else { + return recv(session->socket, buf, length, flags); + } +} + static int sockreadline(http_session_t * session, char *buf, size_t length) { char ch; @@ -1812,37 +1990,41 @@ static int sockreadline(http_session_t * session, char *buf, size_t length) for(i=0;TRUE;) { if(session->socket==INVALID_SOCKET) return(-1); - FD_ZERO(&rd_set); - FD_SET(session->socket,&rd_set); - /* Convert timeout from ms to sec/usec */ - tv.tv_sec=startup->max_inactivity; - tv.tv_usec=0; - sel=select(session->socket+1,&rd_set,NULL,NULL,&tv); - switch(sel) { - case 1: - break; - case -1: - close_socket(&session->socket); - lprintf(LOG_DEBUG,"%04d !ERROR %d selecting socket for read",session->socket,ERROR_VALUE); - return(-1); - default: - /* Timeout */ - lprintf(LOG_NOTICE,"%04d Session timeout due to inactivity (%d seconds)",session->socket,startup->max_inactivity); - return(-1); + if ((!session->is_tls) || (!session->tls_pending)) { + FD_ZERO(&rd_set); + FD_SET(session->socket,&rd_set); + /* Convert timeout from ms to sec/usec */ + tv.tv_sec=startup->max_inactivity; + tv.tv_usec=0; + sel=select(session->socket+1,&rd_set,NULL,NULL,&tv); + switch(sel) { + case 1: + if (session->is_tls) + session->tls_pending=TRUE; + break; + case -1: + close_session_socket(session); + lprintf(LOG_DEBUG,"%04d !ERROR %d selecting socket for read",session->socket,ERROR_VALUE); + return(-1); + default: + /* Timeout */ + lprintf(LOG_NOTICE,"%04d Session timeout due to inactivity (%d seconds)",session->socket,startup->max_inactivity); + return(-1); + } } - switch(recv(session->socket, &ch, 1, 0)) { + switch(sess_recv(session, &ch, 1, 0)) { case -1: - if(ERROR_VALUE!=EAGAIN) { + if(session->is_tls || ERROR_VALUE!=EAGAIN) { if(startup->options&WEB_OPT_DEBUG_RX) lprintf(LOG_DEBUG,"%04d !ERROR %d receiving on socket",session->socket,ERROR_VALUE); - close_socket(&session->socket); + close_session_socket(session); return(-1); } break; case 0: /* Socket has been closed */ - close_socket(&session->socket); + close_session_socket(session); return(-1); } @@ -1936,7 +2118,7 @@ static int pipereadline(int pipe, char *buf, size_t length, char *fullbuf, size_ return(i); } -int recvbufsocket(SOCKET *sock, char *buf, long count) +static int recvbufsocket(http_session_t *session, char *buf, long count) { int rd=0; int i; @@ -1947,14 +2129,14 @@ int recvbufsocket(SOCKET *sock, char *buf, long count) return(0); } - while(rd<count && socket_check(*sock,NULL,NULL,startup->max_inactivity*1000)) { - i=recv(*sock,buf+rd,count-rd,0); + while(rd<count && session_check(session,NULL,NULL,startup->max_inactivity*1000)) { + i=sess_recv(session,buf+rd,count-rd,0); switch(i) { case -1: - if(ERROR_VALUE!=EAGAIN) - close_socket(sock); + if(session->is_tls || ERROR_VALUE!=EAGAIN) + close_session_socket(session); case 0: - close_socket(sock); + close_session_socket(session); *buf=0; return(0); } @@ -2250,124 +2432,127 @@ static BOOL parse_headers(http_session_t * session) while(*value && *value<=' ') value++; switch(i) { case HEAD_AUTH: - if((p=strtok_r(value," ",&last))!=NULL) { - if(stricmp(p, "Basic")==0) { - p=strtok_r(NULL," ",&last); - if(p==NULL) - break; - while(*p && *p<' ') p++; - b64_decode(p,strlen(p),p,strlen(p)); - p=strtok_r(p,":",&last); - if(p) { - if(strlen(p) >= sizeof(session->req.auth.username)) + /* If you're authenticated via TLS-PSK, you can't use basic or digest */ + if (session->req.auth.type != AUTHENTICATION_TLS_PSK) { + if((p=strtok_r(value," ",&last))!=NULL) { + if(stricmp(p, "Basic")==0) { + p=strtok_r(NULL," ",&last); + if(p==NULL) break; - SAFECOPY(session->req.auth.username, p); - p=strtok_r(NULL,":",&last); + while(*p && *p<' ') p++; + b64_decode(p,strlen(p),p,strlen(p)); + p=strtok_r(p,":",&last); if(p) { - if(strlen(p) >= sizeof(session->req.auth.password)) + if(strlen(p) >= sizeof(session->req.auth.username)) break; - SAFECOPY(session->req.auth.password, p); - session->req.auth.type=AUTHENTICATION_BASIC; + SAFECOPY(session->req.auth.username, p); + p=strtok_r(NULL,":",&last); + if(p) { + if(strlen(p) >= sizeof(session->req.auth.password)) + break; + SAFECOPY(session->req.auth.password, p); + session->req.auth.type=AUTHENTICATION_BASIC; + } } } - } - else if(stricmp(p, "Digest")==0) { - p=strtok_r(NULL, "", &last); - /* Defaults */ - session->req.auth.algorithm=ALGORITHM_MD5; - session->req.auth.type=AUTHENTICATION_DIGEST; - /* Parse out values one at a time and store */ - while(*p) { - while(isspace(*p)) - p++; - if(strnicmp(p,"username=",9)==0) { - p+=9; - tvalue=get_token_value(&p); - if(strlen(tvalue) >= sizeof(session->req.auth.username)) - break; - SAFECOPY(session->req.auth.username, tvalue); - } - else if(strnicmp(p,"realm=",6)==0) { - p+=6; - session->req.auth.realm=strdup(get_token_value(&p)); - } - else if(strnicmp(p,"nonce=",6)==0) { - p+=6; - session->req.auth.nonce=strdup(get_token_value(&p)); - } - else if(strnicmp(p,"uri=",4)==0) { - p+=4; - session->req.auth.digest_uri=strdup(get_token_value(&p)); - } - else if(strnicmp(p,"response=",9)==0) { - p+=9; - tvalue=get_token_value(&p); - if(strlen(tvalue)==32) { - for(i=0; i<16; i++) { - session->req.auth.digest[i]=hexval(tvalue[i*2])<<4 | hexval(tvalue[i*2+1]); + else if(stricmp(p, "Digest")==0) { + p=strtok_r(NULL, "", &last); + /* Defaults */ + session->req.auth.algorithm=ALGORITHM_MD5; + session->req.auth.type=AUTHENTICATION_DIGEST; + /* Parse out values one at a time and store */ + while(*p) { + while(isspace(*p)) + p++; + if(strnicmp(p,"username=",9)==0) { + p+=9; + tvalue=get_token_value(&p); + if(strlen(tvalue) >= sizeof(session->req.auth.username)) + break; + SAFECOPY(session->req.auth.username, tvalue); + } + else if(strnicmp(p,"realm=",6)==0) { + p+=6; + session->req.auth.realm=strdup(get_token_value(&p)); + } + else if(strnicmp(p,"nonce=",6)==0) { + p+=6; + session->req.auth.nonce=strdup(get_token_value(&p)); + } + else if(strnicmp(p,"uri=",4)==0) { + p+=4; + session->req.auth.digest_uri=strdup(get_token_value(&p)); + } + else if(strnicmp(p,"response=",9)==0) { + p+=9; + tvalue=get_token_value(&p); + if(strlen(tvalue)==32) { + for(i=0; i<16; i++) { + session->req.auth.digest[i]=hexval(tvalue[i*2])<<4 | hexval(tvalue[i*2+1]); + } } } - } - else if(strnicmp(p,"algorithm=",10)==0) { - p+=10; - tvalue=get_token_value(&p); - if(stricmp(tvalue,"MD5")==0) { - session->req.auth.algorithm=ALGORITHM_MD5; + else if(strnicmp(p,"algorithm=",10)==0) { + p+=10; + tvalue=get_token_value(&p); + if(stricmp(tvalue,"MD5")==0) { + session->req.auth.algorithm=ALGORITHM_MD5; + } + else { + session->req.auth.algorithm=ALGORITHM_UNKNOWN; + } } - else { - session->req.auth.algorithm=ALGORITHM_UNKNOWN; + else if(strnicmp(p,"cnonce=",7)==0) { + p+=7; + session->req.auth.cnonce=strdup(get_token_value(&p)); } - } - else if(strnicmp(p,"cnonce=",7)==0) { - p+=7; - session->req.auth.cnonce=strdup(get_token_value(&p)); - } - else if(strnicmp(p,"qop=",4)==0) { - p+=4; - tvalue=get_token_value(&p); - if(stricmp(tvalue,"auth")==0) { - session->req.auth.qop_value=QOP_AUTH; + else if(strnicmp(p,"qop=",4)==0) { + p+=4; + tvalue=get_token_value(&p); + if(stricmp(tvalue,"auth")==0) { + session->req.auth.qop_value=QOP_AUTH; + } + else if (stricmp(tvalue,"auth-int")==0) { + session->req.auth.qop_value=QOP_AUTH_INT; + } + else { + session->req.auth.qop_value=QOP_UNKNOWN; + } } - else if (stricmp(tvalue,"auth-int")==0) { - session->req.auth.qop_value=QOP_AUTH_INT; + else if(strnicmp(p,"nc=",3)==0) { + p+=3; + session->req.auth.nonce_count=strdup(get_token_value(&p)); } else { - session->req.auth.qop_value=QOP_UNKNOWN; + while(*p && *p != '=') + p++; + if(*p == '=') + get_token_value(&p); } } - else if(strnicmp(p,"nc=",3)==0) { - p+=3; - session->req.auth.nonce_count=strdup(get_token_value(&p)); - } - else { - while(*p && *p != '=') - p++; - if(*p == '=') - get_token_value(&p); - } - } - if(session->req.auth.digest_uri==NULL) - session->req.auth.digest_uri=strdup(session->req.request_line); - /* Validate that we have the required values... */ - switch(session->req.auth.qop_value) { - case QOP_NONE: - if(session->req.auth.realm==NULL - || session->req.auth.nonce==NULL - || session->req.auth.digest_uri==NULL) - send_error(session,"400 Bad Request"); - break; - case QOP_AUTH: - case QOP_AUTH_INT: - if(session->req.auth.realm==NULL - || session->req.auth.nonce==NULL - || session->req.auth.nonce_count==NULL - || session->req.auth.cnonce==NULL - || session->req.auth.digest_uri==NULL) + if(session->req.auth.digest_uri==NULL) + session->req.auth.digest_uri=strdup(session->req.request_line); + /* Validate that we have the required values... */ + switch(session->req.auth.qop_value) { + case QOP_NONE: + if(session->req.auth.realm==NULL + || session->req.auth.nonce==NULL + || session->req.auth.digest_uri==NULL) + send_error(session,"400 Bad Request"); + break; + case QOP_AUTH: + case QOP_AUTH_INT: + if(session->req.auth.realm==NULL + || session->req.auth.nonce==NULL + || session->req.auth.nonce_count==NULL + || session->req.auth.cnonce==NULL + || session->req.auth.digest_uri==NULL) + send_error(session,"400 Bad Request"); + break; + default: send_error(session,"400 Bad Request"); - break; - default: - send_error(session,"400 Bad Request"); - break; + break; + } } } } @@ -2683,10 +2868,10 @@ static BOOL get_request_headers(http_session_t * session) while(sockreadline(session,head_line,sizeof(head_line)-1)>0) { /* Multi-line headers */ - while((i=recv(session->socket,&next_char,1,MSG_PEEK))>0 + while((i=sess_recv(session,&next_char,1,MSG_PEEK))>0 && (next_char=='\t' || next_char==' ')) { - if(i==-1 && ERROR_VALUE != EAGAIN) - close_socket(&session->socket); + if(i==-1 && (session->is_tls || ERROR_VALUE != EAGAIN)) + close_session_socket(session); i=strlen(head_line); if(i>sizeof(head_line)-1) { lprintf(LOG_ERR,"%04d !ERROR long multi-line header. The web server is broken!", session->socket); @@ -2919,6 +3104,43 @@ static BOOL check_extra_path(http_session_t * session) return(FALSE); } +static void read_webctrl_section(FILE *file, char *section, http_session_t *session, BOOL *recheck_dynamic) +{ + char str[MAX_PATH+1]; + + if(iniReadString(file, section, "AccessRequirements", session->req.ars,str)==str) + SAFECOPY(session->req.ars,str); + if(iniReadString(file, section, "Realm", scfg.sys_name,str)==str) { + FREE_AND_NULL(session->req.realm); + /* FREE()d in close_request() */ + session->req.realm=strdup(str); + } + if(iniReadString(file, section, "DigestRealm", scfg.sys_name,str)==str) { + FREE_AND_NULL(session->req.digest_realm); + /* FREE()d in close_request() */ + session->req.digest_realm=strdup(str); + } + if(iniReadString(file, section, "ErrorDirectory", error_dir,str)==str) { + prep_dir(root_dir, str, sizeof(str)); + FREE_AND_NULL(session->req.error_dir); + /* FREE()d in close_request() */ + session->req.error_dir=strdup(str); + } + if(iniReadString(file, section, "CGIDirectory", cgi_dir,str)==str) { + prep_dir(root_dir, str, sizeof(str)); + FREE_AND_NULL(session->req.cgi_dir); + /* FREE()d in close_request() */ + session->req.cgi_dir=strdup(str); + *recheck_dynamic=TRUE; + } + if(iniReadString(file, section, "Authentication", default_auth_list,str)==str) { + FREE_AND_NULL(session->req.auth_list); + /* FREE()d in close_request() */ + session->req.auth_list=strdup(str); + } + session->req.path_info_index=iniReadBool(file, section, "PathInfoIndex", FALSE); +} + static BOOL check_request(http_session_t * session) { char path[MAX_PATH+1]; @@ -2935,6 +3157,8 @@ static BOOL check_request(http_session_t * session) char *spec; str_list_t specs; BOOL recheck_dynamic=FALSE; + char *spath, *sp, *nsp, *pspec; + size_t len; if(session->req.finished) return(FALSE); @@ -3043,71 +3267,28 @@ static BOOL check_request(http_session_t * session) /* FREE()d in this block */ specs=iniReadSectionList(file,NULL); /* Read in globals */ - if(iniReadString(file, NULL, "AccessRequirements", session->req.ars,str)==str) - SAFECOPY(session->req.ars,str); - if(iniReadString(file, NULL, "Realm", scfg.sys_name,str)==str) { - FREE_AND_NULL(session->req.realm); - /* FREE()d in close_request() */ - session->req.realm=strdup(str); - } - if(iniReadString(file, NULL, "DigestRealm", scfg.sys_name,str)==str) { - FREE_AND_NULL(session->req.digest_realm); - /* FREE()d in close_request() */ - session->req.digest_realm=strdup(str); - } - if(iniReadString(file, NULL, "ErrorDirectory", error_dir,str)==str) { - prep_dir(root_dir, str, sizeof(str)); - FREE_AND_NULL(session->req.error_dir); - /* FREE()d in close_request() */ - session->req.error_dir=strdup(str); - } - if(iniReadString(file, NULL, "CGIDirectory", cgi_dir,str)==str) { - prep_dir(root_dir, str, sizeof(str)); - FREE_AND_NULL(session->req.cgi_dir); - /* FREE()d in close_request() */ - session->req.cgi_dir=strdup(str); - recheck_dynamic=TRUE; - } - if(iniReadString(file, NULL, "Authentication", default_auth_list,str)==str) { - FREE_AND_NULL(session->req.auth_list); - /* FREE()d in close_request() */ - session->req.auth_list=strdup(str); - } - session->req.path_info_index=iniReadBool(file, NULL, "PathInfoIndex", FALSE); + read_webctrl_section(file, NULL, session, &recheck_dynamic); /* Read in per-filespec */ while((spec=strListPop(&specs))!=NULL) { - if(wildmatch(filename,spec,TRUE)) { - if(iniReadString(file, spec, "AccessRequirements", session->req.ars,str)==str) - SAFECOPY(session->req.ars,str); - if(iniReadString(file, spec, "Realm", scfg.sys_name,str)==str) { - FREE_AND_NULL(session->req.realm); - /* FREE()d in close_request() */ - session->req.realm=strdup(str); - } - if(iniReadString(file, spec, "DigestRealm", scfg.sys_name,str)==str) { - FREE_AND_NULL(session->req.digest_realm); - /* FREE()d in close_request() */ - session->req.digest_realm=strdup(str); - } - if(iniReadString(file, spec, "ErrorDirectory", error_dir,str)==str) { - FREE_AND_NULL(session->req.error_dir); - prep_dir(root_dir, str, sizeof(str)); - /* FREE()d in close_request() */ - session->req.error_dir=strdup(str); - } - if(iniReadString(file, spec, "CGIDirectory", cgi_dir,str)==str) { - FREE_AND_NULL(session->req.cgi_dir); - prep_dir(root_dir, str, sizeof(str)); - /* FREE()d in close_request() */ - session->req.cgi_dir=strdup(str); - recheck_dynamic=TRUE; - } - if(iniReadString(file, spec, "Authentication", default_auth_list,str)==str) { - FREE_AND_NULL(session->req.auth_list); - /* FREE()d in close_request() */ - session->req.auth_list=strdup(str); + len=strlen(spec); + if(spec[0] && IS_PATH_DELIM(spec[len-1])) { + /* Search for matching path elements... */ + spath=strdup(path+(p-curdir+1)); + pspec=strdup(spec); + pspec[len-1]=0; + for(sp=spath, nsp=find_first_slash(sp+1); nsp; nsp=find_first_slash(sp+1)) { + *nsp=0; + nsp++; + if(wildmatch(sp, pspec, TRUE)) { + read_webctrl_section(file, spec, session, &recheck_dynamic); + } + sp=nsp; } - session->req.path_info_index=iniReadBool(file, spec, "PathInfoIndex", FALSE); + free(spath); + free(pspec); + } + else if(wildmatch(filename,spec,TRUE)) { + read_webctrl_section(file, spec, session, &recheck_dynamic); } free(spec); } @@ -3714,7 +3895,7 @@ static BOOL exec_cgi(http_session_t *session) SAFECOPY(cgi_status,session->req.status); SAFEPRINTF2(content_type,"%s: %s",get_header(HEAD_TYPE),startup->default_cgi_content); - while(server_socket!=INVALID_SOCKET) { + while(!terminated) { if(WaitForSingleObject(process_info.hProcess,0)==WAIT_OBJECT_0) process_terminated=TRUE; /* handle remaining data in pipe before breaking */ @@ -3726,13 +3907,13 @@ static BOOL exec_cgi(http_session_t *session) } /* Check socket for received POST Data */ - if(!socket_check(session->socket, &rd, NULL, /* timeout: */0)) { + if(!session_check(session, &rd, NULL, /* timeout: */0)) { lprintf(LOG_WARNING,"%04d CGI Socket disconnected", session->socket); break; } if(rd) { /* Send received POST Data to stdin of CGI process */ - if((i=recv(session->socket, buf, sizeof(buf), 0)) > 0) { + if((i=sess_recv(session, buf, sizeof(buf), 0)) > 0) { lprintf(LOG_DEBUG,"%04d CGI Received %d bytes of POST data" ,session->socket, i); WriteFile(wrpipe, buf, i, &wr, /* Overlapped: */NULL); @@ -4526,6 +4707,9 @@ static JSContext* js_initcx(http_session_t *session) { JSContext* js_cx; + jsval val; + JSObject* obj; + js_socket_private_t* p; lprintf(LOG_DEBUG,"%04d JavaScript: Initializing context (stack: %lu bytes)" ,session->socket,startup->js.cx_stack); @@ -4559,6 +4743,14 @@ js_initcx(http_session_t *session) JS_DestroyContext(js_cx); return(NULL); } + if (session->is_tls) { + JS_GetProperty(js_cx, session->js_glob, "client", &val); + obj=JSVAL_TO_OBJECT(val); + JS_GetProperty(js_cx, obj, "socket", &val); + obj=JSVAL_TO_OBJECT(val); + p=(js_socket_private_t*)JS_GetPrivate(js_cx,obj); + p->session=session->tls_sess; + } return(js_cx); } @@ -4567,7 +4759,6 @@ static BOOL js_setup(http_session_t* session) { JSObject* argv; -#ifndef ONE_JS_RUNTIME if(session->js_runtime == NULL) { lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes" ,session->socket,startup->js.max_bytes); @@ -4577,7 +4768,6 @@ static BOOL js_setup(http_session_t* session) return(FALSE); } } -#endif if(session->js_cx==NULL) { /* Context not yet created, create it now */ /* js_initcx() begins a context */ @@ -4823,7 +5013,7 @@ BOOL post_to_file(http_session_t *session, FILE*fp, size_t ch_len) int bytes_read; for(k=0; k<ch_len;) { - bytes_read=recvbufsocket(&session->socket,buf,(ch_len-k)>sizeof(buf)?sizeof(buf):(ch_len-k)); + bytes_read=recvbufsocket(session,buf,(ch_len-k)>sizeof(buf)?sizeof(buf):(ch_len-k)); if(!bytes_read) { send_error(session,error_500); fclose(fp); @@ -4868,7 +5058,7 @@ FILE *open_post_file(http_session_t *session) int read_post_data(http_session_t * session) { - unsigned i=0; + uint64_t i=0; FILE *fp=NULL; if(session->req.dynamic!=IS_CGI && (session->req.post_len || session->req.read_chunked)) { @@ -4919,7 +5109,7 @@ int read_post_data(http_session_t * session) } session->req.post_data=p; /* read new data */ - bytes_read=recvbufsocket(&session->socket,session->req.post_data+session->req.post_len,ch_len); + bytes_read=recvbufsocket(session,session->req.post_data+session->req.post_len,ch_len); if(!bytes_read) { send_error(session,error_500); if(fp) fclose(fp); @@ -4963,7 +5153,7 @@ int read_post_data(http_session_t * session) else { /* FREE()d in close_request() */ if(i < (MAX_POST_LEN+1) && (session->req.post_data=malloc(i+1)) != NULL) - session->req.post_len=recvbufsocket(&session->socket,session->req.post_data,i); + session->req.post_len=recvbufsocket(session,session->req.post_data,i); else { lprintf(LOG_CRIT,"%04d !ERROR Allocating %d bytes of memory",session->socket,i); send_error(session,"413 Request entity too large"); @@ -5001,7 +5191,7 @@ void http_output_thread(void *arg) /* Destroyed at end of function */ if((i=pthread_mutex_init(&session->outbuf_write,NULL))!=0) { lprintf(LOG_DEBUG,"Error %d initializing outbuf mutex",i); - close_socket(&session->socket); + close_session_socket(session); thread_down(); return; } @@ -5084,6 +5274,7 @@ void http_output_thread(void *arg) pthread_mutex_lock(&session->outbuf_write); RingBufRead(obuf, (uchar*)bufdata, avail); + pthread_mutex_unlock(&session->outbuf_write); if(chunked) { bufdata+=avail; *(bufdata++)='\r'; @@ -5092,8 +5283,7 @@ void http_output_thread(void *arg) } if(!failed) - sock_sendbuf(&session->socket, buf, len, &failed); - pthread_mutex_unlock(&session->outbuf_write); + sess_sendbuf(session, buf, len, &failed); } thread_down(); /* Ensure outbuf isn't currently being drained */ @@ -5106,8 +5296,6 @@ void http_output_thread(void *arg) void http_session_thread(void* arg) { - char* host_name; - HOSTENT* host; SOCKET socket; char redir_req[MAX_REQUEST_LINE+1]; char *redirp; @@ -5116,6 +5304,9 @@ void http_session_thread(void* arg) ulong login_attempts=0; BOOL init_error; int32_t clients_remain; + int i; + int last; + user_t user; SetThreadName("HTTP Session"); pthread_mutex_lock(&((http_session_t*)arg)->struct_filled); @@ -5144,11 +5335,46 @@ void http_session_thread(void* arg) session.finished=FALSE; + if (session.is_tls) { + /* Create and initialize the TLS session */ + if (!HANDLE_CRYPT_CALL(cryptCreateSession(&session.tls_sess, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER), &session)) { + close_session_socket(&session); + thread_down(); + return; + } + /* Add all the user/password combinations */ +#if 0 // TLS-PSK is currently broken in cryptlib + last = lastuser(&scfg); + for (i=1; i <= last; i++) { + user.number = i; + getuserdat(&scfg,&user); + if(user.misc&(DELETED|INACTIVE)) + continue; + if (user.alias[0] && user.pass[0]) { + if(HANDLE_CRYPT_CALL(cryptSetAttributeString(session.tls_sess, CRYPT_SESSINFO_USERNAME, user.alias, strlen(user.alias)), &session)) + HANDLE_CRYPT_CALL(cryptSetAttributeString(session.tls_sess, CRYPT_SESSINFO_PASSWORD, user.pass, strlen(user.pass)), &session); + } + } +#endif + if (tls_context != -1) { + HANDLE_CRYPT_CALL(cryptSetAttribute(session.tls_sess, CRYPT_SESSINFO_PRIVATEKEY, tls_context), &session); + } + BOOL nodelay=TRUE; + setsockopt(session.socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay)); + + HANDLE_CRYPT_CALL(cryptSetAttribute(session.tls_sess, CRYPT_SESSINFO_NETWORKSOCKET, session.socket), &session); + if (!HANDLE_CRYPT_CALL(cryptSetAttribute(session.tls_sess, CRYPT_SESSINFO_ACTIVE, 1), &session)) { + close_session_socket(&session); + thread_down(); + return; + } + } + /* Start up the output buffer */ /* FREE()d in this block (RingBufDispose before all returns) */ if(RingBufInit(&(session.outbuf), OUTBUF_LEN)) { lprintf(LOG_ERR,"%04d Canot create output ringbuffer!", session.socket); - close_socket(&session.socket); + close_session_socket(&session); thread_down(); return; } @@ -5160,18 +5386,8 @@ void http_session_thread(void* arg) sbbs_srand(); /* Seed random number generator */ - if(startup->options&BBS_OPT_NO_HOST_LOOKUP) - host=NULL; - else - host=gethostbyaddr ((char *)&session.addr.sin_addr - ,sizeof(session.addr.sin_addr),AF_INET); - - if(host!=NULL && host->h_name!=NULL) - host_name=host->h_name; - else - host_name=session.host_ip; - - SAFECOPY(session.host_name,host_name); + if(getnameinfo(&session.addr.addr, session.addr_len, session.host_name, sizeof(session.host_name), NULL, 0, (startup->options&BBS_OPT_NO_HOST_LOOKUP)?NI_NUMERICHOST:0)!=0) + SAFECOPY(session.host_name, session.host_ip); if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP)) { lprintf(LOG_INFO,"%04d Hostname: %s", session.socket, session.host_name); @@ -5183,7 +5399,7 @@ void http_session_thread(void* arg) #endif if(trashcan(&scfg,session.host_name,"host")) { lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s", session.socket, session.host_name); - close_socket(&session.socket); + close_session_socket(&session); sem_wait(&session.output_thread_terminated); sem_destroy(&session.output_thread_terminated); RingBufDispose(&session.outbuf); @@ -5195,7 +5411,7 @@ void http_session_thread(void* arg) /* host_ip wasn't defined in http_session_thread */ if(trashcan(&scfg,session.host_ip,"ip")) { lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip); - close_socket(&session.socket); + close_session_socket(&session); sem_wait(&session.output_thread_terminated); sem_destroy(&session.output_thread_terminated); RingBufDispose(&session.outbuf); @@ -5209,9 +5425,9 @@ void http_session_thread(void* arg) SAFECOPY(session.client.addr,session.host_ip); SAFECOPY(session.client.host,session.host_name); - session.client.port=ntohs(session.addr.sin_port); + session.client.port=inet_addrport(&session.addr); session.client.time=time32(NULL); - session.client.protocol="HTTP"; + session.client.protocol=session.is_tls ? "HTTP":"HTTPS"; session.client.user=session.username; session.client.size=sizeof(session.client); client_on(session.socket, &session.client, /* update existing client record? */FALSE); @@ -5232,6 +5448,14 @@ void http_session_thread(void* arg) while(!session.finished) { init_error=FALSE; memset(&(session.req), 0, sizeof(session.req)); + if (session.is_tls) { +#if 0 // TLS-PSK is currently broken in cryptlib + if (cryptGetAttributeString(session.tls_sess, CRYPT_SESSINFO_USERNAME, session.req.auth.username, &i)==CRYPT_OK) { + session.req.auth.username[i]=0; + session.req.auth.type = AUTHENTICATION_TLS_PSK; + } +#endif + } redirp=NULL; loop_count=0; if(startup->options&WEB_OPT_HTTP_LOGGING) { @@ -5310,20 +5534,18 @@ void http_session_thread(void* arg) session.js_cx=NULL; } -#ifndef ONE_JS_RUNTIME if(session.js_runtime!=NULL) { lprintf(LOG_DEBUG,"%04d JavaScript: Destroying runtime",socket); jsrt_Release(session.js_runtime); session.js_runtime=NULL; } -#endif #ifdef _WIN32 if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE)) PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME); #endif - close_socket(&session.socket); + close_session_socket(&session); sem_wait(&session.output_thread_terminated); sem_destroy(&session.output_thread_terminated); RingBufDispose(&session.outbuf); @@ -5345,7 +5567,7 @@ void http_session_thread(void* arg) void DLLCALL web_terminate(void) { - lprintf(LOG_INFO,"%04d Web Server terminate",server_socket); + lprintf(LOG_INFO,"Web Server terminate"); terminate_server=TRUE; } @@ -5370,9 +5592,16 @@ static void cleanup(int code) semfile_list_free(&recycle_semfiles); semfile_list_free(&shutdown_semfiles); + + if (tls_context != -1) { + cryptDestroyContext(tls_context); + tls_context = -1; + } - if(server_socket!=INVALID_SOCKET) { - close_socket(&server_socket); + if(!terminated) { + xpms_destroy(ws_set, close_socket_cb, NULL); + ws_set=NULL; + terminated=TRUE; } update_clients(); /* active_clients is destroyed below */ @@ -5437,7 +5666,7 @@ void http_logging_thread(void* arg) thread_up(TRUE /* setuid */); - lprintf(LOG_INFO,"%04d HTTP logging thread started", server_socket); + lprintf(LOG_INFO,"HTTP logging thread started"); for(;;) { struct log_data *ld; @@ -5458,8 +5687,7 @@ void http_logging_thread(void* arg) if(ld==NULL) { if(terminate_http_logging_thread) break; - lprintf(LOG_ERR,"%04d HTTP logging thread received NULL linked list log entry" - ,server_socket); + lprintf(LOG_ERR,"HTTP logging thread received NULL linked list log entry"); continue; } SAFECOPY(newfilename,base); @@ -5475,7 +5703,7 @@ void http_logging_thread(void* arg) SAFECOPY(filename,newfilename); logfile=fopen(filename,"ab"); if(logfile) - lprintf(LOG_INFO,"%04d HTTP logfile is now: %s",server_socket,filename); + lprintf(LOG_INFO,"HTTP logfile is now: %s",filename); } if(logfile!=NULL) { if(ld->status) { @@ -5502,7 +5730,7 @@ void http_logging_thread(void* arg) } } else { - lprintf(LOG_ERR,"%04d HTTP server failed to open logfile %s (%d)!",server_socket,filename,errno); + lprintf(LOG_ERR,"HTTP server failed to open logfile %s (%d)!",filename,errno); } FREE_AND_NULL(ld->hostname); FREE_AND_NULL(ld->ident); @@ -5518,41 +5746,32 @@ void http_logging_thread(void* arg) logfile=NULL; } thread_down(); - lprintf(LOG_INFO,"%04d HTTP logging thread terminated",server_socket); + lprintf(LOG_INFO,"HTTP logging thread terminated"); http_logging_thread_running=FALSE; } void DLLCALL web_server(void* arg) { - int i; - int result; time_t start; WORD host_port; - char host_ip[32]; + char host_ip[INET6_ADDRSTRLEN]; char path[MAX_PATH+1]; char logstr[256]; char mime_types_ini[MAX_PATH+1]; char web_handler_ini[MAX_PATH+1]; - SOCKADDR_IN server_addr={0}; - SOCKADDR_IN client_addr; + union xp_sockaddr client_addr; socklen_t client_addr_len; SOCKET client_socket; - SOCKET high_socket_set; - fd_set socket_set; time_t t; time_t initialized=0; FILE* fp; char* p; char compiler[32]; http_session_t * session=NULL; - struct timeval tv; -#ifdef ONE_JS_RUNTIME - JSRuntime* js_runtime; -#endif -#ifdef SO_ACCEPTFILTER - struct accept_filter_arg afa; -#endif + struct in_addr iaddr; + void *acc_type; + char ssl_estr[SSL_ESTR_LEN]; startup=(web_startup_t*)arg; @@ -5598,7 +5817,8 @@ void DLLCALL web_server(void* arg) js_server_props.version_detail=web_ver(); js_server_props.clients=&active_clients.value; js_server_props.options=&startup->options; - js_server_props.interface_addr=&startup->interface_addr; + /* TODO IPv6 */ + js_server_props.interface_addr=&startup->outgoing4; uptime=0; served=0; @@ -5684,6 +5904,11 @@ void DLLCALL web_server(void* arg) lprintf(LOG_DEBUG,"Error directory: %s", error_dir); lprintf(LOG_DEBUG,"CGI directory: %s", cgi_dir); + lprintf(LOG_DEBUG,"Loading/Creating TLS certificate"); + tls_context = get_ssl_cert(&scfg, ssl_estr); + if (tls_context == -1) + lprintf(LOG_ERR, "Error creating TLS certificate: %s", ssl_estr); + iniFileName(mime_types_ini,sizeof(mime_types_ini),scfg.ctrl_dir,"mime_types.ini"); mime_types=read_ini_list(mime_types_ini,NULL /* root section */,"MIME types" ,mime_types); @@ -5712,65 +5937,21 @@ void DLLCALL web_server(void* arg) update_clients(); /* open a socket and wait for a client */ - - server_socket = open_socket(SOCK_STREAM); - - if(server_socket == INVALID_SOCKET) { - lprintf(LOG_CRIT,"!ERROR %d creating HTTP socket", ERROR_VALUE); - cleanup(1); - return; - } + ws_set = xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf); -/* - * i=1; - * if(setsockopt(server_socket, IPPROTO_TCP, TCP_NOPUSH, &i, sizeof(i))) - * lprintf("Cannot set TCP_NOPUSH socket option"); - */ - -#ifdef SO_ACCEPTFILTER - memset(&afa, 0, sizeof(afa)); - strcpy(afa.af_name, "httpready"); - setsockopt(server_socket, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)); -#endif - - lprintf(LOG_DEBUG,"%04d Web Server socket opened",server_socket); - - /*****************************/ - /* Listen for incoming calls */ - /*****************************/ - memset(&server_addr, 0, sizeof(server_addr)); - - server_addr.sin_addr.s_addr = htonl(startup->interface_addr); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(startup->port); - - if(startup->port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); - } - result = retry_bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) - ,startup->bind_retry_count,startup->bind_retry_delay,"Web Server",lprintf); - if(startup->port < IPPORT_RESERVED) { - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); - } - if(result != 0) { - lprintf(LOG_CRIT,"%s",BIND_FAILURE_HELP); + if(ws_set == NULL) { + lprintf(LOG_CRIT,"!ERROR %d creating HTTP socket set", ERROR_VALUE); cleanup(1); return; } + lprintf(LOG_DEBUG,"Web Server socket set created"); - result = listen(server_socket, 64); - - if(result != 0) { - lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on socket" - ,server_socket, result, ERROR_VALUE); - cleanup(1); - return; - } - lprintf(LOG_INFO,"%04d Web Server listening on port %u" - ,server_socket, startup->port); - status("Listening"); + /* + * Add interfaces + */ + xpms_add_list(ws_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->port, "Web Server", open_socket, startup->seteuid, NULL); + if(do_cryptInit()) + xpms_add_list(ws_set, PF_UNSPEC, SOCK_STREAM, 0, startup->tls_interfaces, startup->tls_port, "Secure Web Server", open_socket, startup->seteuid, "TLS"); listInit(&log_list,/* flags */ LINK_LIST_MUTEX|LINK_LIST_SEMAPHORE); if(startup->options&WEB_OPT_HTTP_LOGGING) { @@ -5782,21 +5963,6 @@ void DLLCALL web_server(void* arg) _beginthread(http_logging_thread, 0, startup->logfile_base); } -#ifdef ONE_JS_RUNTIME - if(js_runtime == NULL) { - lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes" - ,server_socket,startup->js.max_bytes); - - if((js_runtime=jsrt_GetNew(startup->js.max_bytes, 0, __FILE__, __LINE__))==NULL) { - lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",server_socket); - /* Sleep 15 seconds then try again */ - /* ToDo: Something better should be used here. */ - SLEEP(15000); - continue; - } - } -#endif - /* Setup recycle/shutdown semaphore file lists */ shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","web"); recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","web"); @@ -5815,16 +5981,15 @@ void DLLCALL web_server(void* arg) if(startup->started!=NULL) startup->started(startup->cbdata); - lprintf(LOG_INFO,"%04d Web Server thread started", server_socket); + lprintf(LOG_INFO,"Web Server thread started"); - while(server_socket!=INVALID_SOCKET && !terminate_server) { + while(!terminated && !terminate_server) { /* check for re-cycle/shutdown semaphores */ if(protected_uint32_value(thread_count) <= (2 /* web_server() and http_output_thread() */ + http_logging_thread_running)) { if(!(startup->options&BBS_OPT_NO_RECYCLE)) { if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) { - lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected" - ,server_socket,p); + lprintf(LOG_INFO,"Recycle semaphore file (%s) detected",p); if(session!=NULL) { pthread_mutex_unlock(&session->struct_filled); session=NULL; @@ -5832,7 +5997,7 @@ void DLLCALL web_server(void* arg) break; } if(startup->recycle_now==TRUE) { - lprintf(LOG_INFO,"%04d Recycle semaphore signaled",server_socket); + lprintf(LOG_INFO,"Recycle semaphore signaled"); startup->recycle_now=FALSE; if(session!=NULL) { pthread_mutex_unlock(&session->struct_filled); @@ -5842,11 +6007,9 @@ void DLLCALL web_server(void* arg) } } if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL - && lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected" - ,server_socket,p)) + && lprintf(LOG_INFO,"Shutdown semaphore file (%s) detected",p)) || (startup->shutdown_now==TRUE - && lprintf(LOG_INFO,"%04d Shutdown semaphore signaled" - ,server_socket))) { + && lprintf(LOG_INFO,"Shutdown semaphore signaled"))) { startup->shutdown_now=FALSE; terminate_server=TRUE; if(session!=NULL) { @@ -5861,9 +6024,7 @@ void DLLCALL web_server(void* arg) if(session==NULL) { /* FREE()d at the start of the session thread */ if((session=malloc(sizeof(http_session_t)))==NULL) { - lprintf(LOG_CRIT,"%04d !ERROR allocating %u bytes of memory for http_session_t" - ,server_socket, sizeof(http_session_t)); - mswait(3000); + lprintf(LOG_CRIT,"!ERROR allocating %u bytes of memory for http_session_t", sizeof(http_session_t)); continue; } memset(session, 0, sizeof(http_session_t)); @@ -5876,60 +6037,22 @@ void DLLCALL web_server(void* arg) } /* now wait for connection */ + client_addr_len = sizeof(client_addr); + client_socket = xpms_accept(ws_set, &client_addr, &client_addr_len, startup->sem_chk_freq*1000, &acc_type); - FD_ZERO(&socket_set); - FD_SET(server_socket,&socket_set); - high_socket_set=server_socket+1; - - tv.tv_sec=startup->sem_chk_freq; - tv.tv_usec=0; - - if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) { - if(i==0) - continue; - if(ERROR_VALUE==EINTR) - lprintf(LOG_DEBUG,"Web Server listening interrupted"); - else if(ERROR_VALUE == ENOTSOCK) - lprintf(LOG_INFO,"Web Server socket closed"); - else - lprintf(LOG_WARNING,"!ERROR %d selecting socket",ERROR_VALUE); + if(client_socket == INVALID_SOCKET) continue; - } - if(server_socket==INVALID_SOCKET) { /* terminated */ + if(terminated) { /* terminated */ pthread_mutex_unlock(&session->struct_filled); session=NULL; break; } - client_addr_len = sizeof(client_addr); - - if(server_socket!=INVALID_SOCKET - && FD_ISSET(server_socket,&socket_set)) { - client_socket = accept(server_socket, (struct sockaddr *)&client_addr - ,&client_addr_len); - } - else { - lprintf(LOG_NOTICE,"!NO SOCKETS set by select"); - continue; - } - - if(client_socket == INVALID_SOCKET) { - lprintf(LOG_WARNING,"!ERROR %d accepting connection", ERROR_VALUE); -#ifdef _WIN32 - if(WSAGetLastError()==WSAENOBUFS) { /* recycle (re-init WinSock) on this error */ - pthread_mutex_unlock(&session->struct_filled); - session=NULL; - break; - } -#endif - continue; - } - if(startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - SAFECOPY(host_ip,inet_ntoa(client_addr.sin_addr)); + inet_addrtop(&client_addr, host_ip, sizeof(host_ip)); if(trashcan(&scfg,host_ip,"ip-silent")) { close_socket(&client_socket); @@ -5939,29 +6062,31 @@ void DLLCALL web_server(void* arg) if(startup->max_clients && protected_uint32_value(active_clients)>=startup->max_clients) { lprintf(LOG_WARNING,"%04d !MAXIMUM CLIENTS (%d) reached, access denied" ,client_socket, startup->max_clients); - mswait(3000); + if (!len_503) + len_503 = strlen(error_503); + sendsocket(client_socket, error_503, len_503); close_socket(&client_socket); continue; - } + } - host_port=ntohs(client_addr.sin_port); + host_port=inet_addrport(&client_addr); - lprintf(LOG_INFO,"%04d HTTP connection accepted from: %s port %u" + if (acc_type != NULL && !strcmp(acc_type, "TLS")) + session->is_tls=TRUE; + lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u" ,client_socket + ,session->is_tls ? "HTTP":"HTTPS" ,host_ip, host_port); SAFECOPY(session->host_ip,host_ip); - session->addr=client_addr; + memcpy(&session->addr, &client_addr, sizeof(session->addr)); + session->addr_len=client_addr_len; session->socket=client_socket; session->js_callback.auto_terminate=TRUE; session->js_callback.terminated=&terminate_server; session->js_callback.limit=startup->js.time_limit; session->js_callback.gc_interval=startup->js.gc_interval; session->js_callback.yield_interval=startup->js.yield_interval; -#ifdef ONE_JS_RUNTIME - session->js_runtime=js_runtime; -#endif - pthread_mutex_unlock(&session->struct_filled); session=NULL; served++; @@ -5974,13 +6099,13 @@ void DLLCALL web_server(void* arg) /* Wait for active clients to terminate */ if(protected_uint32_value(active_clients)) { - lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..." - ,server_socket, protected_uint32_value(active_clients)); + lprintf(LOG_DEBUG,"Waiting for %d active clients to disconnect..." + , protected_uint32_value(active_clients)); start=time(NULL); while(protected_uint32_value(active_clients)) { if(time(NULL)-start>startup->max_inactivity) { - lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients" - ,server_socket, protected_uint32_value(active_clients)); + lprintf(LOG_WARNING,"!TIMEOUT waiting for %d active clients" + , protected_uint32_value(active_clients)); break; } mswait(100); @@ -5993,27 +6118,18 @@ void DLLCALL web_server(void* arg) mswait(100); } if(http_logging_thread_running) { - lprintf(LOG_DEBUG,"%04d Waiting for HTTP logging thread to terminate..." - ,server_socket); + lprintf(LOG_DEBUG,"Waiting for HTTP logging thread to terminate..."); start=time(NULL); while(http_logging_thread_running) { if(time(NULL)-start>TIMEOUT_THREAD_WAIT) { - lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for HTTP logging thread to " - "terminate", server_socket); + lprintf(LOG_WARNING,"!TIMEOUT waiting for HTTP logging thread to " + "terminate"); break; } mswait(100); } } -#ifdef ONE_JS_RUNTIME - if(js_runtime!=NULL) { - lprintf(LOG_DEBUG,"%04d JavaScript: Destroying runtime",server_socket); - jsrt_Release(js_runtime); - js_runtime=NULL; - } -#endif - cleanup(0); if(!terminate_server) { diff --git a/src/sbbs3/websrvr.h b/src/sbbs3/websrvr.h index e089e219325897bf7ac8be064a4db0f7823c51f8..7d38fffddb48d1fab17393993438bd219cf5ce2d 100644 --- a/src/sbbs3/websrvr.h +++ b/src/sbbs3/websrvr.h @@ -43,17 +43,21 @@ #include "semwrap.h" /* sem_t */ typedef struct { - DWORD size; /* sizeof(web_startup_t) */ - WORD port; - WORD max_clients; + DWORD size; /* sizeof(web_startup_t) */ + WORD max_clients; #define WEB_DEFAULT_MAX_CLIENTS 0 /* 0=unlimited */ - WORD max_inactivity; + WORD max_inactivity; #define WEB_DEFAULT_MAX_INACTIVITY 120 /* seconds */ - WORD max_cgi_inactivity; + WORD max_cgi_inactivity; #define WEB_DEFAULT_MAX_CGI_INACTIVITY 120 /* seconds */ - WORD sem_chk_freq; /* semaphore file checking frequency (in seconds) */ - DWORD interface_addr; - DWORD options; + WORD sem_chk_freq; /* semaphore file checking frequency (in seconds) */ + DWORD options; + WORD port; + WORD tls_port; + struct in_addr outgoing4; + struct in6_addr outgoing6; + str_list_t interfaces; + str_list_t tls_interfaces; void* cbdata; /* Private data passed to callbacks */ @@ -114,7 +118,9 @@ typedef struct { /* startup options that requires re-initialization/recycle when changed */ static struct init_field web_init_fields[] = { OFFSET_AND_SIZE(web_startup_t,port) - ,OFFSET_AND_SIZE(web_startup_t,interface_addr) + ,OFFSET_AND_SIZE(web_startup_t,interfaces) + ,OFFSET_AND_SIZE(web_startup_t,outgoing4) + ,OFFSET_AND_SIZE(web_startup_t,outgoing6) ,OFFSET_AND_SIZE(web_startup_t,ctrl_dir) ,OFFSET_AND_SIZE(web_startup_t,root_dir) ,OFFSET_AND_SIZE(web_startup_t,error_dir) @@ -159,7 +165,7 @@ static ini_bitdesc_t web_options[] = { #define WEB_DEFAULT_ROOT_DIR "../web/root" #define WEB_DEFAULT_ERROR_DIR "error" #define WEB_DEFAULT_CGI_DIR "cgi-bin" -#define WEB_DEFAULT_AUTH_LIST "Basic,Digest" +#define WEB_DEFAULT_AUTH_LIST "Basic,Digest,TLS-PSK" #define WEB_DEFAULT_CGI_CONTENT "text/plain" #ifdef DLLEXPORT