/* NET.C - Network window procedure for WSMTPSRV - WinSock V1.1 required Author: Ian Blenke Ian Blenke cannot be held responsible for damages, expressed or implied, for the use of this software. No commercial use can be made of this product without the consent of the author. No profit of any kind can be made on the sale or distribution of this program. If you wish to distribute this program with other samples of WinSock programming, you must first contact the author so that he can keep accurate records of its usage. If you write any programs based on this source code, you may not sell them for any profit without the written consent of the author. If you incorporate this source code into a public domain program, all the author requires is a notification that "part of the code was written by Ian Blenke" and some form of notification that his name was used in the public domain software distribution. This does not represent a contract on the part of the author. If any issues cannot clearly be resolved by reading this text, immediately contact the author. If you have any bug reports and/or source patches... By all means, tell me! I would be glad to help keep this code up to date. Do not, however, modify this source in any way and re-distribute it without the author's knowledge. This would constitute something not-good. I don't like such agreements, but in today's world of lawyers and lawbreakers I have little other choice. Enjoy! */ #ifndef NET_C #define NET_C #include "Net.H" #include "Dialogs.h" #include // DOS Time functions for netGetTimeAndDate() /* BOOL netInit(void); Purpose: To initialize the network window Given: Nothing. Returns: TRUE if an error occured. */ BOOL netInit(void) { WNDCLASS wndclass; SOCKADDR_IN saMain; LPSERVENT lpseServEnt; LPHOSTENT lpheHostEnt; BOOL bDebug; if(gethostname((LPSTR)szLocalHostName, sizeof(szLocalHostName)) == SOCKET_ERROR) { // We need SOME valid name - make it "Unknown" lstrcpy((LPSTR)szLocalHostName, (LPSTR)STRING_UNKNOWN); } else { lpheHostEnt=gethostbyname((LPSTR)szLocalHostName); if(lpheHostEnt!=NULL) { lstrcpy((LPSTR)szLocalHostName, lpheHostEnt->h_name); } } lstrcpy((LPSTR)szNetworkClass, (LPSTR)CLASS_NETWORK); wndclass.style = 0; wndclass.lpfnWndProc = (WNDPROC)NetProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInst; wndclass.hIcon = NULL; wndclass.hCursor = NULL; wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = (LPSTR)szNetworkClass; if(!RegisterClass(&wndclass)) { return(TRUE); } // Create the main "invisible" network window hWndMain=CreateWindow((LPSTR)szNetworkClass, (LPSTR)"", WS_CHILD, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hWndDlg, NULL, hInst, NULL); if(!hWndMain) { UnregisterClass((LPSTR)szNetworkClass, hInst); return(TRUE); } sSocketMain=socket(AF_INET, SOCK_STREAM, 0); if(sSocketMain==SOCKET_ERROR) { netError(); DestroyWindow(hWndMain); UnregisterClass((LPSTR)szNetworkClass, hInst); return(TRUE); } lpseServEnt=getservbyname((LPSTR)SMTP_NAME, NULL); if(lpseServEnt==NULL) { saMain.sin_port=htons(SMTP_PORT); } else { saMain.sin_port=lpseServEnt->s_port; } saMain.sin_family=AF_INET; // Address Family type Internet saMain.sin_addr.s_addr=0; // Bind to local host if(bind(sSocketMain, (LPSOCKADDR)&saMain, sizeof(saMain))==SOCKET_ERROR) { netError(); DestroyWindow(hWndMain); closesocket(sSocketMain); UnregisterClass((LPSTR)szNetworkClass, hInst); return(TRUE); } if(listen(sSocketMain, MAXCLIENTS)==SOCKET_ERROR) { netError(); DestroyWindow(hWndMain); closesocket(sSocketMain); UnregisterClass((LPSTR)szNetworkClass, hInst); return(TRUE); } // Make the main socket non-blocking, and set up // the connection message. if(WSAAsyncSelect(sSocketMain, hWndMain, NET_ACTIVITY, FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR) { netError(); DestroyWindow(hWndMain); closesocket(sSocketMain); UnregisterClass((LPSTR)szNetworkClass, hInst); return(TRUE); } bDebug=TRUE; setsockopt(sSocketMain, SOL_SOCKET, SO_DEBUG, (char FAR *)&bDebug, sizeof(bDebug)); setsockopt(sSocketMain, IPPROTO_TCP, SO_DEBUG, (char FAR *)&bDebug, sizeof(bDebug)); // ALL sockets are accounted for with structures // Even the main one - to get the NetProc to work right. smtpAddClient(sSocketMain); return(FALSE); } /* netInit() */ /* void netError(void); Purpose: To tell the user something broke. Given: Nothing. Returns: Nothing. Uses: netErrorTable, WSAGetLastError(), smtpError() */ void netError(void) { int iError; int iIndex; iIndex=0; iError=WSAGetLastError(); if((iError==WSAEWOULDBLOCK)|| (iError==WSAENOTCONN) || (iError==WSANO_DATA)) // Why does v1.09beta tell me this? return; while(netErrorTable[iIndex].iError!=0) { if(netErrorTable[iIndex].iError==iError) { smtpError(netErrorTable[iIndex].iResourceID); return; } iIndex++; } return; } /* netError */ /* void netClose(void); Purpose: To close the network portion of WSMTPSRV Given: Nothing. Returns: Nothing. */ void netClose(void) { if(hWndMain) { smtpDestroyClient(smtpClientsHead); smtpClientsHead=NULL; DestroyWindow(hWndMain); UnregisterClass((LPCSTR)szNetworkClass, hInst); hWndMain=0; } WSACleanup(); return; } /* netClose */ /* BOOL CALLBACK NetProc(HWND, UINT, WPARAM, LPARAM); Purpose: To handle all Windows Sockets messages. Given: Standard CALLBACK args. Returns: FALSE if we handled it. */ LRESULT CALLBACK NetProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { // First thing we do is get the connection's window LPSMTPCLIENT lpClient; SOCKET sSocket; #ifdef USE_TITLE char szTitle[MAXLINE]; #endif /* USE_TITLE */ int iAddrSize; SOCKADDR_IN saPeer; LPSOCKADDR_IN lpsaHostAddr; LPHOSTENT lpheHostEnt; char szTime[40]; if(wParam!=0) lpClient=smtpSocketToClient((SOCKET)wParam); else lpClient=NULL; if(lpClient) // If it isn't a Client message - ignore it { switch(Msg) { case NET_ACTIVITY: { // This is the handler for the main network window switch(WSAGETSELECTEVENT(lParam)) { case FD_ACCEPT: { // Get a pending accept iAddrSize=sizeof(SOCKADDR_IN); sSocket=accept(sSocketMain, (LPSOCKADDR)&saPeer, (int FAR *)&iAddrSize); if(sSocket==INVALID_SOCKET) return(FALSE); // Allocate socket specific data lpClient=smtpAddClient(sSocket); if(!lpClient) return(FALSE); // Remember the connected Peer's address lpClient->saPeer=saPeer; // Get the Peer's Name asyncronously lpsaHostAddr=(LPSOCKADDR_IN)&(lpClient->saPeer); lpheHostEnt= gethostbyaddr((LPSTR)&(lpsaHostAddr->sin_addr.s_addr), 4, PF_INET); if(lpheHostEnt==NULL) { // Ah HA! No sneaking in! lstrcpy((LPSTR)lpClient->szPeer, inet_ntoa(lpsaHostAddr->sin_addr)); } else { lstrcpy((LPSTR)lpClient->szPeer, (LPSTR)(lpheHostEnt->h_name)); } smtpLog(LOG_HIGH | LOG_TIME, LOG_CONNECT_S, (LPSTR)lpClient->szPeer); netGetTimeAndDate((LPSTR)szTime, sizeof(szTime)); /* Set the window title (for debugging) */ #ifdef USE_TITLE if(iLogLevel==LOG_HIGH) { wsprintf((LPSTR)szTitle, "WSMTPD - Connect: from %s:%d", (LPSTR)lpClient->szPeer, lpClient->saPeer.sin_port); SetWindowText(hWndDlg, (LPSTR)szTitle); } #endif /* USE_TITLE */ smtpSendMessage(lpClient, 220, MSG_I_AM_S_S, (LPSTR)szLocalHostName, (LPSTR)szTime); // Not all stacks support this yet. /* if(WSAAsyncGetHostByAddr(hWnd, NET_NAME, (LPSTR)&(lpsaHostAddr->sin_addr.s_addr), 4, PF_INET, (LPSTR)lpClient->hePeer, MAXGETHOSTSTRUCT) != 0) { return(FALSE); } /**/ // Make sure that the name can be found netError(); PostMessage(hWnd, NET_NAME, 0, WSAMAKEASYNCREPLY(0 ,1)); return(FALSE); } // WinSock received something for us case FD_READ: { int iError; // FIFO log it iError=netReceiveData(lpClient); if(iError==0) { // It closed!?! smtpDestroyClient(lpClient); return(FALSE); } else if(iError==SOCKET_ERROR) { // Miscellaneous Error netError(); return(FALSE); } // Call the server routines (void)smtpServer(lpClient); // We handled the event, return FALSE return(FALSE); } break; /* FD_READ */ // WinSock is ready to send case FD_WRITE: { // FIFO log it if(netSendData(lpClient)==SOCKET_ERROR) { netError(); } return(FALSE); } break; // The MAIN accepting network window was closed!!! case FD_CLOSE: { // This DOESNT work. And a good thing too.. //smtpSendMessage(lpClient, 221, MSG_GOODBYE_S, // (LPSTR)szLocalHostName); /* Set the window title (for debugging) */ #ifdef USE_TITLE if(iLogLevel==LOG_HIGH) { wsprintf((LPSTR)szTitle, "WSMTPD - %s disconnected from port %d", (LPSTR)lpClient->szPeer, lpClient->saPeer.sin_port); SetWindowText(hWndDlg, (LPSTR)szTitle); } #endif /* USE_TITLE */ // Hasta la Vista, Baby smtpDestroyClient(lpClient); return(FALSE); } break; // Shouldn't get to here!!! default: { netError(); return(FALSE); } } } /* NET_ACCEPT */ break; // Get the peer's name - No spoofing allowed in this server! // gethostbyname() doesn't seem to block much anyway // so it's a decent compromise. /* case NET_NAME: { DEBUGIT("NET_NAME Event!"); if(WSAGETASYNCERROR(lParam)) { lstrcpy((LPSTR)lpClient->szPeer, (LPSTR)STRING_UNKNOWN); } else // We found a name!!!! { lstrcpy((LPSTR)lpClient->szPeer, (LPSTR)((HOSTENT*)(lpClient->hePeer))->h_name); } smtpLog(LOG_CONNECT_S, (LPSTR)lpClient->szPeer); } break; /**/ } /* Msg*/ } /* if(index) */ switch(Msg) { case WM_CLOSE: { // A WSACleanup() here, and a WSACleanup() there.... smtpDestroyClient(smtpClientsHead); smtpClientsHead=NULL; WSACleanup(); } break; default: { // Something has to handle the other messages! return(DefWindowProc(hWnd, Msg, wParam, lParam)); } } } /* NetProc() */ /* int netGetTimeAndDate(LPSTR, int); Purpose: To format the current system time/data into a passed buffer. Doesn't formats the output to what other SMTP servers report - uses the ctime() call. Given: String buffer pointer, and size of the buffer Returns: TRUE if an error occurs, false otherwise Notes: Version 1.0 uses DOS time functions. The Windows ones are undocumented. */ BOOL netGetTimeAndDate(LPSTR lpLine, int iLine) { struct tm tmClock; time_t tClock; LPSTR lpSysTime; char szTemp[256]; time(&tClock); tmClock=*localtime(&tClock); if(!strftime(szTemp, sizeof(szTemp), "%a, %d %b %y %H:%M:%S %z", &tmClock)) { lstrcpy((LPSTR)szTemp, (LPSTR)asctime(&tmClock)); szTemp[lstrlen((LPSTR)szTemp)-1]='\0'; }; if(lstrlen((LPSTR)szTemp)>iLine) { szTemp[iLine]='\0'; } lstrcpy(lpLine, (LPSTR)szTemp); return(FALSE); } /* netGetTimeAndDate() */ /* int netSendData(int); Purpose: To send data in the appropriate FIFO buffer Sends data, and updates the pointers to what was actually sent. Given: Client index; Returns: Same as send() Notes: If smtpSendString finds stop=start, then he should flag the fact that the buffer WAS empty, and then call this routine to set up sending messages!!! */ int netSendData(LPSMTPCLIENT lpClient) { LPSTR lpBuffer; LPSTR lpLine; int fifoStart; int fifoStop; int fifoWalker; int iDest; int iCount; int sSocket; #ifdef USE_TITLE char szTitle[MAXLINE]; #endif /* USE_TITLE */ sSocket = lpClient->sSocket; if(!lpClient) return(SOCKET_ERROR); lpLine = (LPSTR)szMainBuffer; /* Make things easier to deal with */ lpBuffer=lpClient->lpOutputBuffer; fifoStart=lpClient->fifoOutputStart; fifoWalker=fifoStart; fifoStop=lpClient->fifoOutputStop; if(fifoStart==fifoStop) { return(0); } /* Keep trying to send until buffer is empty, or WSAEWOULDBLOCK */ // do // { iDest=0; while(fifoWalker!=fifoStop) { lpLine[iDest]=lpBuffer[fifoWalker]; fifoWalker=(fifoWalker+1)%MAXSNDBUFF; iDest++; } iCount=send(sSocket, lpLine, iDest, 0); if(iCount==SOCKET_ERROR) { return(iCount); } fifoWalker=(fifoStart+iCount)%MAXSNDBUFF; lpClient->fifoOutputStart=fifoWalker; /* Set the window title (for debugging) */ #ifdef USE_TITLE if(iLogLevel==LOG_HIGH) { wsprintf((LPSTR)szTitle, "WSMTPD - Sent: %d bytes to %s:%u", iCount, (LPSTR)lpClient->szPeer, lpClient->saPeer.sin_port); SetWindowText(hWndDlg, (LPSTR)szTitle); } #endif /* USE_TITLE */ // iDest -= iCount; // } while(iDest!=0); return(iCount); } /* netSendData */ /* int netRecieveData(int); Purpose: To receive data from WinSock into FIFOs Updates the fifoStop pointer to the end of the new data. If no data is waiting, or there is no more room in the buffer, this routine returns like recv(). Given: Client index. Returns: same as recv(); */ int netReceiveData(LPSMTPCLIENT lpClient) { LPSTR lpBuffer; LPSTR lpLine; int fifoStart; int fifoStop; int fifoWalker; int iMax; int iDest; int iCount; int sSocket; #ifdef USE_TITLE char szTitle[MAXLINE]; #endif /* USE_TITLE */ sSocket = lpClient->sSocket; lpLine = (LPSTR)szMainBuffer; iMax=lpClient->iInputSize; fifoStart=lpClient->fifoInputStart; fifoStop=lpClient->fifoInputStop; // Calculate the room we have ready // szMainBuffer can be a bottleneck if the Receive buffer is big if(fifoStoplpInputBuffer; /* There is room at the end! Copy it in! */ /* Tell winsock to release it first, tho */ iCount=recv(sSocket, lpLine, iDest, 0); /* Set the window title (for debugging) */ #ifdef USE_TITLE if(iLogLevel==LOG_HIGH) { if(iCount!=SOCKET_ERROR) { wsprintf((LPSTR)szTitle, "WSMTPD - Received: %d bytes from %s:%u", iCount, (LPSTR)lpClient->szPeer, lpClient->saPeer.sin_port); SetWindowText(hWndDlg, (LPSTR)szTitle); } else { if((WSAGetLastError()!=WSAENOTCONN)&& (WSAGetLastError()!=WSAEINPROGRESS)) { wsprintf((LPSTR)szTitle, "WSMTPD - SOCKET_ERROR #%d from %s:%u", WSAGetLastError(), (LPSTR)lpClient->szPeer, lpClient->saPeer.sin_port); SetWindowText(hWndDlg, (LPSTR)szTitle); } } } #endif /* USE_TITLE */ if(iCount==SOCKET_ERROR) { // If something was blocking, always try again return(iCount); } if(iCount==0) return(iCount); iDest=0; fifoWalker=fifoStop; while(iDestfifoInputStop=fifoWalker; return(iCount); } /* netReceiveData */ #endif /* NET_C */