/**\file socketsWin32.c * \brief Socket layer/driver for Win32 * * This is the socket layer for Win32. * * \author Rainer Gerhards * \date 2003-08-04 * \date 2003-08-05 * Modified to be a plain lower layer. Moved * some of the code to \ref sockets.c. * * Copyright 2002-2003 * Rainer Gerhards and Adiscon GmbH. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of Adiscon GmbH or Rainer Gerhards * nor the names of its contributors may be used to * endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* ################################################################# * * private members * * ################################################################# */ /** Set last error status. Call as soon as a socket error occured. */ static int sbSockSetLastSockError(struct sbSockObject *pThis) { srRetVal iRet; pThis->dwLastError = WSAGetLastError(); switch(pThis->dwLastError) { case WSAEINVAL: iRet = SR_RET_INVALID_SOCKET; break; default: iRet = SR_RET_SOCKET_ERR; break; } return iRet; } /* ################################################################# * * public members * * ################################################################# */ srRetVal sbSockLayerInit(int bInitOSStack) { WORD wVersionRequested; WSADATA wsaData; int ret; /* for winsock */ if(bInitOSStack) { /* because of select(), we need version 2 of sockets */ wVersionRequested = MAKEWORD(2, 0); ret = WSAStartup(wVersionRequested, &wsaData); if (ret != 0) { /* nope, too old :( */ return SR_RET_INVALID_OS_SOCKETS_VERSION; } else { /* Confirm that the Windows Sockets DLL supports 2.0.*/ /* Note that if the DLL supports versions greater */ /* than 2.0 in addition to 2.0, it will still return */ /* 2.0 in wVersion since that is the version we */ /* requested. */ if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 ) { /* again, too old :( */ /* ... but we need to do some cleanup in this case! */ WSACleanup(); return SR_RET_INVALID_OS_SOCKETS_VERSION; } } } return SR_RET_OK; } srRetVal sbSockLayerExit(int bExitOSStack) { int iRetVal = SR_RET_OK; if(bExitOSStack) { if(WSACleanup() == SOCKET_ERROR) { iRetVal = SR_RET_ERR; } } return(iRetVal); } sbSockObj* sbSockInit(void) { return(sbSockInitEx(AF_INET, SOCK_STREAM)); } sbSockObj* sbSockInitEx(int iAF, int iSockType) { struct sbSockObject *pThis; assert((iSockType == SOCK_STREAM) || (iSockType == SOCK_DGRAM)); pThis = (struct sbSockObject*) calloc(1, sizeof(struct sbSockObject)); if(pThis != NULL) { /* initialize class members */ if((pThis->sock = socket(iAF, iSockType, 0)) == INVALID_SOCKET) { free(pThis); return(NULL); } /* rest of initialization */ pThis->bIsInError = FALSE; pThis->OID = OIDsbSock; pThis->iCurInBufPos = 0; pThis->iInBufLen = 0; } return(pThis); } /* ############################################################ * * # Now come more or less winsock wrappers # * * ############################################################ */ /** * Set the socket to nonblocking state. */ srRetVal sbSockSetNonblocking(sbSockObj*pThis) { u_long uArg; sbSockCHECKVALIDOBJECT(pThis); uArg = 1; if(ioctlsocket(pThis->sock, FIONBIO, &uArg) != 0) return sbSockSetSockErrState(pThis); return SR_RET_OK; } /** * Wrapper for the socket accept() call. */ srRetVal sbSockAccept(sbSockObj*pThis, sbSockObj* pNew, struct sockaddr *sa, int *iSizeSA) { sbSockCHECKVALIDOBJECT(pThis); sbSockCHECKVALIDOBJECT(pNew); if((pNew->sock = accept(pThis->sock, sa, iSizeSA)) == INVALID_SOCKET) return sbSockSetSockErrState(pThis); return SR_RET_OK; } /** * Wrapper for the socket listen() call. */ srRetVal sbSockListen(sbSockObj*pThis) { int iRetCode; sbSockCHECKVALIDOBJECT(pThis); if((iRetCode = listen(pThis->sock, SOMAXCONN)) != 0) { sbSockSetSockErrState(pThis); return SR_RET_ERR; } return SR_RET_OK; } srRetVal sbSockClosesocket(sbSockObj *pThis) { sbSockCHECKVALIDOBJECT(pThis); if(closesocket(pThis->sock) == SOCKET_ERROR) { sbSockSetSockErrState(pThis); return SR_RET_ERR; } return SR_RET_OK; } /** * Wrapper for the socket select call on a fd_set structure. * \param fdsetRD fdset structure for read-awaiting sockets. * May be NULL. * \param fdsetWR fdset structure for write-awaiting sockets. * May be NULL. * \param iTimOutSecs Seconds until timeout. -1 means indefinite * blocking. * \param iTimOutMSecs Milliseconds until timeout. * \retval Value returned by select. */ int sbSockSelectMulti(srSock_fd_set *fdsetRD, srSock_fd_set *fdsetWR, int iTimOutSecs, int iTimOutMSecs) { int iRet; struct timeval tv; struct timeval *ptv; if(iTimOutSecs == -1) ptv = NULL; else { tv.tv_sec = iTimOutSecs; tv.tv_usec = iTimOutMSecs; ptv = &tv; } iRet = select(0, fdsetRD, fdsetWR, NULL, ptv); return(iRet); } /** * Wrapper for the socket select call on a single socket. * \param iTimOutSecs Seconds until timeout. -1 means indefinite * blocking. * \param iTimOutMSecs Milliseconds until timeout. * \retval Value returned by select. */ int sbSockSelect(struct sbSockObject* pThis, int iTimOutSecs, int iTimOutMSecs) { int iRet; fd_set fdset; struct timeval tv = {iTimOutSecs, iTimOutMSecs}; struct timeval *ptv; sbSockCHECKVALIDOBJECT(pThis); FD_ZERO(&fdset); FD_SET(pThis->sock, &fdset); ptv = iTimOutSecs == -1 ? NULL : &tv; iRet = select(0, &fdset, NULL, NULL, ptv); return(iRet); } int sbSockReceive(struct sbSockObject* pThis, char * pszBuf, int iLen) { int iBytesRcvd; sbSockCHECKVALIDOBJECT(pThis); /* iLen - 1 to leave room for the terminating \0 character */ iBytesRcvd = recv(pThis->sock, pszBuf, iLen - 1, 0); if(iBytesRcvd == SOCKET_ERROR) { sbSockSetSockErrState(pThis); *pszBuf = '\0'; /* Just in case... */ } else { /* terminate string! */ *(pszBuf + iBytesRcvd) = '\0'; } return(iBytesRcvd); } int sbSockSend(struct sbSockObject* pThis, const char* pszBuf, int iLen) { int iRetCode = -1; sbSockCHECKVALIDOBJECT(pThis); if(pszBuf != NULL) { iRetCode = send(pThis->sock, pszBuf, iLen, 0); if(iRetCode == SOCKET_ERROR) { sbSockSetSockErrState(pThis); iRetCode = -1; } } return(iRetCode); } srRetVal sbSockConnectoToHost(sbSockObj* pThis, char* pszHost, int iPort) { SOCKADDR_IN remoteaddr; int remoteaddrlen = sizeof(remoteaddr); struct hostent *hstent; struct sockaddr_in srv_addr; char* pIpAddr; int iRetCode; sbSockCHECKVALIDOBJECT(pThis); /* Bind socket to any port on local machine */ srv_addr.sin_family = AF_INET; srv_addr.sin_addr.s_addr = INADDR_ANY; srv_addr.sin_port = 0; if(bind(pThis->sock, (LPSOCKADDR) &srv_addr, sizeof(srv_addr)) == SOCKET_ERROR) { /* debug: fprintf(stderr, "Error binding socket!\r\n"); */ return SR_RET_ERR; } /* Create target address */ remoteaddr.sin_family = AF_INET; remoteaddr.sin_port = htons(iPort); hstent = gethostbyname(pszHost); if(hstent == NULL) { sbSockSetSockErrState(pThis); return SR_RET_ERR; } /* extract host address - we always use the first one */ pIpAddr = *hstent->h_addr_list; memcpy(&(remoteaddr.sin_addr.s_addr), pIpAddr, sizeof(remoteaddr.sin_addr.s_addr)); iRetCode = connect(pThis->sock, (LPSOCKADDR) &remoteaddr, sizeof(remoteaddr)); if(iRetCode == SOCKET_ERROR) { sbSockSetSockErrState(pThis); return SR_RET_ERR; } return SR_RET_OK; } /** * Bind a socket to the provided address. * \param pszAddr Address to bind to. This is a string. If NULL, * no specific address is used. * Please keep in mind that a specific bind to an * address may steal the port and thus NULL could be * causing a security vulnerability. * \param iPort Port to bind to. */ srRetVal sbSockBind(sbSockObj* pThis, char* pszHost, int iPort) { struct sockaddr_in srv_addr; sbSockCHECKVALIDOBJECT(pThis); assert(pszHost == NULL); /** sorry, other modes currently not supported \todo implement! */ /* Bind socket to any port on local machine */ srv_addr.sin_family = AF_INET; srv_addr.sin_addr.s_addr = INADDR_ANY; srv_addr.sin_port = htons(iPort); if(bind(pThis->sock, (LPSOCKADDR) &srv_addr, sizeof(srv_addr)) == SOCKET_ERROR) { return SR_RET_CANT_BIND_SOCKET; } return SR_RET_OK; } /** * Wrapper for inet_ntoa(). * * \param psa Poiner to a struct sockaddr for which the representation * is to obtain. Must not be NULL. * * \param psz Pointer to buffer that should receive the IP address * string pointer. Must not be NULL. NOTE WELL: This pointer is * only valid until the next socket call. So the caller should * immediately copy it to its own buffer once control returns. * The returned buffer MUST NOT be free()ed. */ static srRetVal sbSock_inet_ntoa(struct sockaddr_in *psa, char **psz) { assert(psz != NULL); if((*psz = (char*) inet_ntoa(psa->sin_addr)) == NULL) return SR_RET_ERR; return SR_RET_OK; } /** * Wrapper for gethostname(). * * \note The Windows sockets API seems to be broken. We often * have seen that only the computer name part (not the domain * name) is returned by gethostname, even if the full * identity is configured. A way to work around this may be * to call gethostbyname() on the returned name and then * call gethostbyaddr() on what is returned there. * WE HAVE NOW IMPLEMENTED THIS WORKAROUND, but leave * the text above in just in case... * * \param psz Pointer to Pointer to hostname. Must not be NULL. * On return, this pointer will refer to a newly allocated * buffer containing the hostname. This buffer must be free()ed * by the caller! * */ srRetVal sbSock_gethostname(char **psz) { struct hostent *phe; assert(psz != NULL); if((*psz = (char*) malloc(256 * sizeof(char))) == NULL) return SR_RET_OUT_OF_MEMORY; if(gethostname(*psz, 256) != 0) return SR_RET_ERR; /* Now comes the ugly part, but at least it works this * way in Win32... gethostname() does not (always?) return * the FQDN. (@)#!! */ phe = gethostbyname(*psz); strcpy(*psz, phe->h_name); return SR_RET_OK; } /** * Wrapper for getsockname(). * * Please note: the socket MUST be connected before you can use * this method! It should NOT be used on UDP sockets. * * \param pName Pointer to a struct sockaddr_in that will * receive the remote address. Must not be NULL. * * \param iNameLen Length of the pName buffer. Updated on return. * Must not be NULL. */ static srRetVal sbSock_getsockname(sbSockObj* pThis, struct sockaddr_in *pName, int* piNameLen) { sbSockCHECKVALIDOBJECT(pThis); assert(pName != NULL); assert(piNameLen != NULL); assert(pThis->sock != INVALID_SOCKET); if(getsockname(pThis->sock, (struct sockaddr*) pName, piNameLen) != 0) return sbSockSetLastSockError(pThis); return SR_RET_OK; } /** * Wrapper for recvfrom(). */ static int sbSock_recvfrom(sbSockObj *pThis, char* buf, int len, int flags, struct sockaddr* from, int* fromlen) { sbSockCHECKVALIDOBJECT(pThis); assert(pThis->sock != INVALID_SOCKET); assert(buf != NULL); assert(len > 0); assert(from != NULL); assert(fromlen > 0); return recvfrom(pThis->sock, buf, len, flags, from, fromlen); }