// // UDPSocket.cpp // interface // // Created by Stephen Birarda on 1/28/13. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // #include "UDPSocket.h" #include #include #include #include #ifdef _WIN32 #include "Syssocket.h" #else #include #include #include #include #endif #include "Log.h" sockaddr_in destSockaddr, senderAddress; bool socketMatch(const sockaddr* first, const sockaddr* second) { if (first != NULL && second != NULL) { // utility function that indicates if two sockets are equivalent // currently only compares two IPv4 addresses // expandable to IPv6 by adding else if for AF_INET6 if (first->sa_family != second->sa_family) { // not the same family, can't be equal return false; } else if (first->sa_family == AF_INET) { const sockaddr_in *firstIn = (const sockaddr_in *) first; const sockaddr_in *secondIn = (const sockaddr_in *) second; return firstIn->sin_addr.s_addr == secondIn->sin_addr.s_addr && firstIn->sin_port == secondIn->sin_port; } else { return false; } } else { return false; } } int packSocket(unsigned char* packStore, in_addr_t inAddress, in_port_t networkOrderPort) { packStore[0] = inAddress >> 24; packStore[1] = inAddress >> 16; packStore[2] = inAddress >> 8; packStore[3] = inAddress; packStore[4] = networkOrderPort >> 8; packStore[5] = networkOrderPort; return 6; // could be dynamically more if we need IPv6 } int packSocket(unsigned char* packStore, sockaddr* socketToPack) { return packSocket(packStore, ((sockaddr_in*) socketToPack)->sin_addr.s_addr, ((sockaddr_in*) socketToPack)->sin_port); } int unpackSocket(unsigned char* packedData, sockaddr* unpackDestSocket) { sockaddr_in* destinationSocket = (sockaddr_in*) unpackDestSocket; destinationSocket->sin_addr.s_addr = (packedData[0] << 24) + (packedData[1] << 16) + (packedData[2] << 8) + packedData[3]; destinationSocket->sin_port = (packedData[4] << 8) + packedData[5]; return 6; // this could be more if we ever need IPv6 } int getLocalAddress() { // get this agent's local address so we can pass that to DS struct ifaddrs* ifAddrStruct = NULL; struct ifaddrs* ifa = NULL; int family; int localAddress = 0; #ifndef _WIN32 getifaddrs(&ifAddrStruct); for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { family = ifa->ifa_addr->sa_family; if (family == AF_INET) { localAddress = ((sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; } } freeifaddrs(ifAddrStruct); #else // Get the local hostname char szHostName[255]; gethostname(szHostName, 255); struct hostent *host_entry; host_entry = gethostbyname(szHostName); char * szLocalIP; szLocalIP = inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); localAddress = inet_addr(szLocalIP); #endif return localAddress; } unsigned short loadBufferWithSocketInfo(char* addressBuffer, sockaddr* socket) { if (socket != NULL) { char* copyBuffer = inet_ntoa(((sockaddr_in*) socket)->sin_addr); memcpy(addressBuffer, copyBuffer, strlen(copyBuffer)); return htons(((sockaddr_in*) socket)->sin_port); } else { const char* unknownAddress = "Unknown"; memcpy(addressBuffer, unknownAddress, strlen(unknownAddress)); return 0; } } sockaddr_in socketForHostname(const char* hostname) { struct hostent* pHostInfo; sockaddr_in newSocket; if ((pHostInfo = gethostbyname(hostname))) { memcpy(&newSocket.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length); } return newSocket; } UDPSocket::UDPSocket(int listeningPort) : listeningPort(listeningPort), blocking(true) { init(); // create the socket handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (handle <= 0) { printLog("Failed to create socket.\n"); return; } destSockaddr.sin_family = AF_INET; // bind the socket to the passed listeningPort sockaddr_in bind_address; bind_address.sin_family = AF_INET; bind_address.sin_addr.s_addr = INADDR_ANY; bind_address.sin_port = htons((uint16_t) listeningPort); if (bind(handle, (const sockaddr*) &bind_address, sizeof(sockaddr_in)) < 0) { printLog("Failed to bind socket to port %d.\n", listeningPort); return; } // if we requested an ephemeral port, get the actual port if (listeningPort == 0) { socklen_t addressLength = sizeof(sockaddr_in); getsockname(handle, (sockaddr*) &bind_address, &addressLength); listeningPort = ntohs(bind_address.sin_port); } // set timeout on socket recieve to 0.5 seconds struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 500000; setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv); printLog("Created UDP socket listening on port %d.\n", listeningPort); } UDPSocket::~UDPSocket() { #ifdef _WIN32 closesocket(handle); #else close(handle); #endif } bool UDPSocket::init() { #ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 2, 2 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ return false; } /* Confirm that the WinSock DLL supports 2.2.*/ /* Note that if the DLL supports versions later */ /* than 2.2 in addition to 2.2, it will still return */ /* 2.2 in wVersion since that is the version we */ /* requested. */ if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ WSACleanup(); return false; } #endif return true; } void UDPSocket::setBlocking(bool blocking) { this->blocking = blocking; #ifdef _WIN32 u_long mode = blocking ? 0 : 1; ioctlsocket(handle, FIONBIO, &mode); #else int flags = fcntl(handle, F_GETFL, 0); fcntl(handle, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); #endif } // Receive data on this socket with retrieving address of sender bool UDPSocket::receive(void* receivedData, ssize_t* receivedBytes) const { return receive((sockaddr*) &senderAddress, receivedData, receivedBytes); } // Receive data on this socket with the address of the sender bool UDPSocket::receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const { #ifdef _WIN32 int addressSize = sizeof(*recvAddress); #else socklen_t addressSize = sizeof(&recvAddress); #endif *receivedBytes = recvfrom(handle, static_cast(receivedData), MAX_BUFFER_LENGTH_BYTES, 0, recvAddress, &addressSize); return (*receivedBytes > 0); } int UDPSocket::send(sockaddr* destAddress, const void* data, size_t byteLength) const { // send data via UDP int sent_bytes = sendto(handle, (const char*)data, byteLength, 0, (sockaddr *) destAddress, sizeof(sockaddr_in)); if (sent_bytes != byteLength) { printLog("Failed to send packet: %s\n", strerror(errno)); return false; } return sent_bytes; } int UDPSocket::send(char* destAddress, int destPort, const void* data, size_t byteLength) const { // change address and port on reusable global to passed variables destSockaddr.sin_addr.s_addr = inet_addr(destAddress); destSockaddr.sin_port = htons((uint16_t)destPort); return send((sockaddr *)&destSockaddr, data, byteLength); }