/******************************************************************************************** Clean OS Linux library module version 1.2.1. This module is part of the Clean Object I/O library, version 1.2.1, for the Linux platform. ********************************************************************************************/ /******************************************************************************************** About this module: Routines related to TCP. ********************************************************************************************/ //#define FD_SETSIZE "maximal number of sockets to select on" (default is 64) #include #include #include #include #include #include "util_121.h" #include "Clean.h" #include "cTCP_121.h" #define trace(x) /* ------------------------ THE FUNCTION PROTOTYPES ------------------------------------------ Naming convention: functions, which are called from Clean end with a "C". Function's that */ //************************************************ // functions, which are called from Clean (semantic is explained in tcp.icl or ostcp.icl) int os_eom(SOCKET endpointRef); int os_disconnected(SOCKET endpointRef); int os_connectrequestavailable(SOCKET endpointRef); void abortedHost_syncC(CleanString inetAddr, int *errCode, int *ipAddr); void abortedHost_asyncC(CleanString inetAddr, int *errCode, HANDLE *endpointRef); void openTCP_ListenerC(int portNum, int *pErrCode, SOCKET *pEndpointRef); void acceptC(SOCKET endpointRef, int *pErrCode, int *pInetHost, SOCKET *pEndpointRef); void os_connectTCPC(int isIOProg, int block, int doTimeout, unsigned int stopTime, int ipAddr, int portnum, int *errCode, int *timeoutExpiredP, int *endpointRefP ); void sendC(SOCKET endpointRef, CleanString data, int begin, int nBytes, int *pErrCode, int *pSentBytes); void receiveC(SOCKET endpointRef, int maxSize, CleanString *data); int getRcvBuffSizeC(); void resizeStringC(CleanString string, int newSize); int data_availableC(SOCKET endpointRef); void disconnectGracefulC(SOCKET endpointRef); void disconnectBrutalC(SOCKET endpointRef); void garbageCollectEndpointC(SOCKET endpointRef); void os_select_inetevents(SOCKET endpointRef, int receiverCategory, int referenceCount, int getReceiveEvents, int getSendEvents, int aborted ); void selectChC(int justForMac, int nonBlocking, int doTimeout, unsigned int stopTime, SOCKET *pRChannels, int *justForMac2, SOCKET *pSChannels, int *pErrCode ); int tcpPossibleC(void); //************************************************ // other functions void StartUp(int abort); // initialize winsock (if not started yet. uses global "tcpStartedUp") // if succesful: tcpStartedUp==TRUE afterwards // if not succesful && abort: aborts // if not succesful && !abort: tcpStartedUp==FALSE afterwards void CleanUp(void); //************************************************ // functions to deal with the endpoint dictionary: int insertNewDictionaryItem(SOCKET endpointRef); // allocates memory for new dictionary item, initializes it as far as possible and // adds it to the dictionary. returns error code: 0==ok, 1==not ok dictitem* lookup(SOCKET endpointRef); // lookup entry (CFN) void setEndpointDataC(int endpointRef, int referenceCount, int hasReceiveNotifier, int hasSendableNotifier, int aborted); // set the corresponding fields of the entry void getEndpointDataC(int endpointRef, int *referenceCount, int *hasReceiveNotifier, int *hasSendableNotifier, int *aborted); // returns the corresponding fields of the entry void removeDictionaryItem(SOCKET endpointRef); // remove one item via pointer manipulations (must not be called from notifier) //--------------------- GLOBAL VARIABLES ------------------------------------------ // The inetEventQueue: To append easily a new item to the end of the list, "toLastNext" points // to the "next" field of the last list element (or to &inetEventQueue) dictitem *endpointDict = NULL; int tcpStartedUp = FALSE; int rcvBuffSize; // contains the size of the sockets internal buffer for receiving data. CleanString pRcvBuff; extern DNSInfo *DNSInfoList; extern HWND ghMainWindow; extern void (*exit_tcpip_function)(); // the function will be called, when the Clean // program terminates //--------------------- FUNCTION IMPLEMENTATION ----------------------------------- int tcpPossibleC(void) { printf("tcpPossibleC\n"); StartUp(FALSE); return tcpStartedUp; } int os_eom(SOCKET endpointRef) { int err, err2; char dummyBuffer[1]; dictitem *pDictitem; printf("os_eom\n"); err = recv( endpointRef, dummyBuffer, 1, MSG_PEEK); err2 = WSAGetLastError(); if (err>0) return FALSE; if (err<0 && err2==WSAEWOULDBLOCK) return FALSE; pDictitem = lookup(endpointRef); trace( if (!pDictitem) rMessageBox(NULL, MB_APPLMODAL, "in os_eom", "ERROR");) if (pDictitem->availByteValid) return FALSE; if (err==0) return TRUE; return TRUE; } int os_disconnected(SOCKET endpointRef) { int err; char string[1]; dictitem *pDictitem; printf("os_disconnected\n"); pDictitem = lookup(endpointRef); trace( if (!pDictitem) rMessageBox(NULL, MB_APPLMODAL, "in os_disconnected", "ERROR");) if (pDictitem->disconnected) return TRUE; err = send( endpointRef, string, 0, 0); if (err!=SOCKET_ERROR) return FALSE; else return WSAGetLastError()!=WSAEWOULDBLOCK; // only this error can happen with sockets that can still send } int os_connectrequestavailable(SOCKET endpointRef) { FD_SET readSet; struct timeval timeout; int nr; printf("os_connectrequestavailable\n"); FD_ZERO(&readSet); FD_SET(endpointRef,&readSet); timeout.tv_sec = 0; // Timeout in sec's timeout.tv_usec = 0; // Timeout in microsec's nr = select(0,&readSet,NULL,NULL,&timeout); return nr>0 && FD_ISSET(endpointRef,&readSet); } #define majorVrs 1 #define minorVrs 1 void StartUp(int abort) { printf("StartUp\n"); if (!tcpStartedUp) { WORD wVersionRequested; WSADATA wsaData; int err, four=4; SOCKET s; wVersionRequested = MAKEWORD(majorVrs, minorVrs); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { if (abort) { rMessageBox(NULL, MB_APPLMODAL, "ERROR", "can't start up winsock" "\nprogram aborts"); ExitProcess (255); } else return; } /* Confirm that the Windows Sockets DLL supports version mj.mi.*/ /* Note that if the DLL supports versions greater */ /* than mj.mi in addition to mj.mi, it will still return */ /* mj.mi in wVersion since that is the version we */ /* requested. */ if ( LOBYTE( wsaData.wVersion ) != majorVrs || HIBYTE( wsaData.wVersion ) != minorVrs ) { WSACleanup(); if (abort) { rMessageBox(NULL, MB_APPLMODAL, "ERROR", "winsock 1.1 or higher not available" "\nprogram aborts"); ExitProcess (255); } else return; }; // initialize rcvBuffSize s = socket(PF_INET, SOCK_STREAM, 0); if (s==INVALID_SOCKET) { rMessageBox(NULL, MB_APPLMODAL, "ERROR", "can't create a socket" "\nprogram aborts"); ExitProcess (255); }; if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*) &rcvBuffSize, &four)) { rMessageBox(NULL, MB_APPLMODAL, "ERROR", "can't call getsockopt" "\nprogram aborts"); ExitProcess (255); }; pRcvBuff = (CleanString) LocalAlloc(LMEM_FIXED, rcvBuffSize+4); if (pRcvBuff==NULL) { rMessageBox(NULL, MB_APPLMODAL, "ERROR", "out of memory" "\nprogram aborts"); ExitProcess (255); }; exit_tcpip_function = CleanUp; tcpStartedUp = TRUE; }; } void lookupHost_syncC(CleanString inetAddr, int *errCode, int *ipAddrP) // error code: 0 ok, 1 error (also: addr doesn't exist) { HOSTENT *hostentP; unsigned long ipAddr; printf("lookupHost_syncC\n"); StartUp(TRUE); ipAddr = inet_addr(CleanStringCharacters(inetAddr)); if (ipAddr!=INADDR_NONE) { *errCode = 0; *ipAddrP = ntohl(ipAddr); return; }; *errCode = 1; hostentP = gethostbyname(CleanStringCharacters(inetAddr)); // string is alphanumerical if (hostentP!=NULL) { *ipAddrP = ntohl(((DWORD *)(*(hostentP->h_addr_list)))[0]); if (*ipAddrP!=0) *errCode = 0; }; } void lookupHost_asyncC(CleanString inetAddr, int *errCode, HANDLE *endpointRef) // errCode: 0 ok, 1 not ok { DNSInfo *newPtr; HANDLE dnsHdl; printf("lookupHost_asyncC\n"); StartUp(TRUE); *errCode = 1; newPtr = (DNSInfo*) LocalAlloc(LMEM_FIXED,sizeof(DNSInfo)); if (newPtr==NULL) { *errCode = 1; return; }; newPtr->next = DNSInfoList; DNSInfoList = newPtr; // and fill the fields and initiate DNS lookup. dnsHdl = WSAAsyncGetHostByName(ghMainWindow,PM_DNS_EVENT,CleanStringCharacters(inetAddr), DNSInfoList->junion.freeSpace, MAXGETHOSTSTRUCT); // this will cause the sending of a PM_DNS_EVENT message to the main window. // The wParam value of that message will be equal to dnsHdl so that // the ipAdress of the lookedup host can be retrieved then. // The element of the List should be deallocated then if (dnsHdl==0) return; DNSInfoList->dnsHdl = dnsHdl; *errCode = 0; *endpointRef = dnsHdl; } void openTCP_ListenerC(int portNum, int *pErrCode, SOCKET *pEndpointRef) // errCode: 0:ok; otherwise:not ok { SOCKET s; SOCKADDR_IN srvAdr; printf("openTCP_ListenerC\n"); StartUp(TRUE); *pErrCode = 1; s = socket(PF_INET, SOCK_STREAM, 0); if (s==INVALID_SOCKET) return; srvAdr.sin_family = AF_INET; // of course internet adress family srvAdr.sin_addr.s_addr = INADDR_ANY; // internet address will be given after "accept" srvAdr.sin_port = htons((short int)portNum); *pErrCode = bind(s, (LPSOCKADDR) &srvAdr, sizeof(srvAdr)); if (*pErrCode) { closesocket(s); return; }; *pErrCode = listen(s,5); if (*pErrCode) { closesocket(s); return; }; *pEndpointRef = s; *pErrCode = insertNewDictionaryItem(s); if (*pErrCode) return; setEndpointDataC(s, 1,0,0,0); } void acceptC(SOCKET listener, int *pErrCode, int *pInetHost, SOCKET *pEndpointRef) // errCode: 0:ok; otherwise:not ok { SOCKET endpointRef; SOCKADDR_IN clientAdr; int clientAdrSize, tru; printf("acceptC\n"); clientAdrSize = sizeof(clientAdr); endpointRef = accept(listener,(LPSOCKADDR) &clientAdr, &clientAdrSize); tru = TRUE; ioctlsocket(endpointRef, FIONBIO, &tru); // set mode to non blocking *pErrCode = endpointRef==INVALID_SOCKET; if (*pErrCode) return; *pInetHost = ntohl(clientAdr.sin_addr.s_addr); *pEndpointRef = endpointRef; *pErrCode = insertNewDictionaryItem(endpointRef); if (*pErrCode) return; setEndpointDataC(endpointRef,2,0,0,0); } void os_connectTCPC(int onlyForMac, int block, int doTimeout, unsigned int stopTime, int ipAddr, int portnum, int *errCodeP, int *timeoutExpiredP, int *endpointRefP) // errCode: 0 ok; 1 not ok { SOCKET client; SOCKADDR_IN srvAdr,clientAdr; int err, tru; printf("os_connectTCPC\n"); *errCodeP = 1; *timeoutExpiredP = FALSE; client = socket(PF_INET, SOCK_STREAM, 0); if (client==INVALID_SOCKET) return; clientAdr.sin_family = AF_INET; // of course internet adress family clientAdr.sin_addr.s_addr = INADDR_ANY; // internet adress will be given after "connect" clientAdr.sin_port = 0; // the winsock library will choose a free number between 1024 and 5000 err = bind(client, (LPSOCKADDR) &clientAdr, sizeof(clientAdr)); if (err) { closesocket(client); return; }; srvAdr.sin_family = AF_INET; // of course internet adress family srvAdr.sin_addr.s_addr = htonl(ipAddr); srvAdr.sin_port = htons((short int)portnum); tru = TRUE; ////////////////////////////////////////////////////////////////////////// if (block && doTimeout) { ioctlsocket(client, FIONBIO, &tru); // set mode to non blocking err = connect(client, (LPSOCKADDR) &srvAdr, sizeof(srvAdr)); if (!err) { *errCodeP = 0; *timeoutExpiredP = FALSE; *endpointRefP = client; } else if (WSAGetLastError()!=WSAEWOULDBLOCK) { closesocket(client); return; } else { FD_SET writeSet, exptnSet; struct timeval timeout; unsigned int now; int noOfWritableSockets, timeoutTicks; FD_ZERO(&writeSet); FD_SET(client,&writeSet); FD_ZERO(&exptnSet); FD_SET(client,&exptnSet); now = GetTickCount(); timeoutTicks = ((int)stopTime) - ((int)now); if (timeoutTicks<=0) { // timeout expired closesocket(client); *timeoutExpiredP = TRUE; return; }; timeout.tv_sec = timeoutTicks / 1000; // Timeout in sec's timeout.tv_usec = (timeoutTicks % 1000)*1000; // Timeout in microsec's noOfWritableSockets = select(0,NULL,&writeSet,&exptnSet,&timeout); *errCodeP = noOfWritableSockets<0 || (noOfWritableSockets>0 && FD_ISSET(client,&exptnSet)); *timeoutExpiredP = noOfWritableSockets==0; *endpointRefP = client; if (*errCodeP || *timeoutExpiredP) { closesocket(client); return; }; }; }; /////////////////////////////////////////////////////////////////////////// if (block && !doTimeout) { err = connect(client, (LPSOCKADDR) &srvAdr, sizeof(srvAdr)); if (err) { closesocket(client); return; }; ioctlsocket(client, FIONBIO, &tru); // set mode to non blocking *errCodeP = 0; *timeoutExpiredP = FALSE; *endpointRefP = client; }; //////////////////////////////////////////////////////////////////////////// if (!block) { err = WSAAsyncSelect(client,ghMainWindow,PM_SOCKET_EVENT,FD_CONNECT); if (err) { closesocket(client); return; }; err = connect(client, (LPSOCKADDR) &srvAdr, sizeof(srvAdr)); if (err==SOCKET_ERROR) { err = WSAGetLastError(); // a WSAEWOULDBLOCK error is a pretty harmless thing if (err!=WSAEWOULDBLOCK) { closesocket(client); return; }; }; *errCodeP = 0; *timeoutExpiredP = FALSE; *endpointRefP = client; }; ////////////////////////////////////////////////////////////////////////////// *errCodeP = insertNewDictionaryItem(client); if (*errCodeP) { closesocket(client); return; }; if (block) setEndpointDataC(client,2,0,0,0); else { dictitem *ptr; ptr = lookup(client); ptr->referenceCount = 1; ptr->hasReceiveNotifier = 0; ptr->hasSendableNotifier = 1; ptr->aborted = 0; }; } void sendC(SOCKET endpointRef, CleanString data, int begin, int nBytes, int *pErrCode, int *pSentBytes) { int sentBytes; printf("sendC\n"); *pErrCode = 0; sentBytes = send(endpointRef, CleanStringCharacters(data)+begin,nBytes, 0); if (sentBytes==SOCKET_ERROR) { int err; sentBytes = 0; err = WSAGetLastError(); if (err!=WSAEWOULDBLOCK) { dictitem *pDictitem; pDictitem = lookup(endpointRef); trace( if (!pDictitem) rMessageBox(NULL, MB_APPLMODAL, "in sendC", "ERROR");) pDictitem->disconnected =1; *pErrCode = 1; } }; *pSentBytes = sentBytes; } void receiveC(SOCKET endpointRef, int maxSize, CleanString *pReceived) { int size, received; dictitem *pDictitem; printf("receiveC\n"); *pReceived = (CleanString) pRcvBuff; size = maxSize<=0 ? rcvBuffSize : maxSize; received = recv( endpointRef, CleanStringCharacters(pRcvBuff), size, 0); pDictitem = lookup(endpointRef); trace( if (!pDictitem) rMessageBox(NULL, MB_APPLMODAL, "in receiveC", "ERROR");) if (received>0) { pDictitem->availByteValid = 0; CleanStringLength(pRcvBuff) = received; } else if (pDictitem->availByteValid) { CleanStringCharacters(pRcvBuff)[0] = pDictitem->availByte; pDictitem->availByteValid = 0; CleanStringLength(pRcvBuff) = 1; } else CleanStringLength(pRcvBuff) = 0; } int data_availableC(SOCKET endpointRef) { dictitem *pDictitem; int err; printf("data_availableC\n"); pDictitem = lookup(endpointRef); trace( if (!pDictitem) rMessageBox(NULL, MB_APPLMODAL, "in data_availableC", "ERROR\nendpoint %i not found", endpointRef);) if (pDictitem->availByteValid) return TRUE; err = recv( endpointRef, &pDictitem->availByte, 1, MSG_PEEK); if (err>0) { pDictitem->availByteValid = 1; return TRUE; }; return FALSE; } void disconnectGracefulC(SOCKET endpointRef) { printf("disconnecteGracefulC\n"); shutdown(endpointRef,1); // 1: graceful } void disconnectBrutalC(SOCKET endpointRef) { LINGER linger; printf("disconnectBrutalC\n"); linger.l_onoff = 1; linger.l_linger = 0; setsockopt(endpointRef, SOL_SOCKET, SO_LINGER, (char*) &linger, sizeof(linger)); } void garbageCollectEndpointC(SOCKET endpointRef) { dictitem *pDictitem; printf("garbageCollectEndpointC\n"); pDictitem = lookup(endpointRef); if (pDictitem!=NULL && pDictitem->referenceCount==0) { closesocket(endpointRef); removeDictionaryItem(endpointRef); }; } void os_select_inetevents( SOCKET endpointRef, int receiverCategory, int referenceCount, int getReceiveEvents, int getSendEvents, int aborted) { printf("os_select_inetevents\n"); setEndpointDataC(endpointRef, referenceCount, getReceiveEvents, getSendEvents, aborted); } void initFD_SET(FD_SET **ppSet, SOCKET sockets[], int n) { int i; FD_SET *pSet; printf("initFD_SET\n"); pSet = (FD_SET*) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, n*sizeof(SOCKET)+sizeof(u_int)); for(i=0; ireferenceCount; if (referenceCount!=0) { if (referenceCount==1 && pDictitem->aborted) { disconnectBrutalC(pDictitem->endpointRef); } else disconnectGracefulC(pDictitem->endpointRef); closesocket(pDictitem->endpointRef); }; pTemp = pDictitem; pDictitem = pDictitem->next; GlobalFree((char*) pTemp); }; WSACleanup(); } //------------------------ FUNCTION IMPLEMENTATIONS FOR THE ENDPOINT DICTIONARY ------- int insertNewDictionaryItem(SOCKET endpointRef) { dictitem *newItem; printf("insertNewDictionaryItem\n"); newItem = (dictitem*) GlobalAlloc(LMEM_FIXED, sizeof(dictitem)); if (newItem==NULL) return 1; newItem->endpointRef = endpointRef; newItem->next = endpointDict; newItem->availByteValid = 0; newItem->aborted = 0; newItem->disconnected = 0; endpointDict = newItem; return 0; } dictitem* lookup(SOCKET endpointRef) { dictitem *ptr=endpointDict; printf("lookup\n"); while (ptr!=NULL && (ptr->endpointRef!=endpointRef)) ptr = ptr->next; return ptr; } void setEndpointDataC( int endpointRef, int referenceCount, int hasReceiveNotifier, int hasSendableNotifier, int aborted) { dictitem *ptr; printf("setEndpointDataC\n"); ptr = lookup((SOCKET) endpointRef); if (ptr!=NULL) { ptr->referenceCount = referenceCount; ptr->hasReceiveNotifier = hasReceiveNotifier ? 1 : 0; ptr->hasSendableNotifier = hasSendableNotifier ? 1 : 0; ptr->aborted = aborted ? 1 : 0; }; WSAAsyncSelect(endpointRef, ghMainWindow, PM_SOCKET_EVENT, (hasReceiveNotifier ? FD_READ | FD_OOB | FD_ACCEPT | FD_CLOSE : 0) | (hasSendableNotifier ? FD_WRITE | FD_CLOSE : 0)); } void getEndpointDataC( int endpointRef, int *referenceCount, int *hasReceiveNotifier, int *hasSendableNotifier, int *aborted) { dictitem *ptr; printf("getEndpointDataC\n"); ptr = lookup((SOCKET) endpointRef); if (ptr!=NULL) { *referenceCount = ptr->referenceCount; *hasReceiveNotifier = ptr->hasReceiveNotifier!=0; *hasSendableNotifier = ptr->hasSendableNotifier!=0; *aborted = ptr->aborted!=0; }; } void removeDictionaryItem(SOCKET endpointRef) // the dictionary MUST contain a valid item with the endpointRef { dictitem **ptr, *temp; int notRemoved; printf("removeDictionaryItem\n"); ptr = &endpointDict; notRemoved = TRUE; while(notRemoved) if ((*ptr)->endpointRef==endpointRef) { temp = *ptr; *ptr = (*ptr)->next; GlobalFree((char*) temp); notRemoved = FALSE; } else ptr = &((*ptr)->next); }