// //sock.c // //Functions for handling sockets. // // //-UserX 2001/11/07 #include "misc/compat.h" #include //#include "isproxy.h" #include "net/sock.h" #include "base/mem.h" #include "net/noderef.h" #include "base/logger.h" #include "crypt/random.h" #include "pipe/dummy.h" #ifndef _WINDOZE_ #include #endif //#ifdef _WINDOZE_ #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif //#endif /** Functions for handling \Ref{SockHandle}s.

Normally SockHandle's are maniuplated else where (\Ref{sockserv}) but to manual/direct use of \Ref{SockHandle} can be done in this manner.

Opening out bound connections
SockHandle *sh;
NodeRef *nr;
int er;
char *s;

nr = noderefMake();
nr->host = "myhost";
nr->port = 1234;

sh = \Ref{sockMake}();
er = sockSetNodeRef(sh, nr);
noderefFree(nr);
if(er != SOCK_ERR_NONE) {
return; //insert error action
}

er = sockOpen(sh);
if(er != SOCK_ERR_NONE) {
return; //insert error action
}

sh->IOPipe->OutBuffer = dblockAppenString(sh->IOPipe->OutBuffer, "Hello World!");
sockWrite(sh);

sockRead(sh);
s = dblockToString(sh->IOPipe->InBuffer);

sockClose(sh);
sockFree(sh);

Inbound connections:
er = sockSetLocalNodeRef(sh, nr);
er = sockListen(sh);
nsh = sockAccept(sh);
//todo: Better support for detecting inbound connections.
//todo: Allow options for non-blocking sockets.
//todo: Look into what needs to be done for supporting other transports.
@author UserX @name sock */ //@{ struct sockaddr_in blanksockaddr_in = BLANKSOCKADDR_IN; struct sockaddr_in localsockaddr_in = LOCALSOCKADDR_IN; SockHandle blanksockhandle = { BLANKPIPE_SOCK, //NULL, // in datablock //NULL, // out datablock BLANKSOCKADDR_IN, //remote sockaddress LOCALSOCKADDR_IN, //local sockaddress //BLANKSOCKADDR_IN, //remote sockaddress //LOCALSOCKADDR_IN, //local sockaddress (SOCKET) SOCKET_ERROR, //socket NULL, //noderef NULL, //localnoderef SOCK_STATUS_NEW, //status SOCK_DIR_UNKNOWN, //connection direction 0, //socket error 0 //reg count }; /** Initializes sock functions. Doesn't actually do anything now. Will probably be deleted later. */ void SocketAtStart() { LOGDEBUG ("SocketAtStart called."); return; } /** Initializes an existing SockHandle. @param sh The SockHandle to initialize. */ void sockInit(SockHandle *sh) { LOGDEBUG (stringJoin("sockInit:", ptrToString(sh))); if(sh == NULL) { return; } sh->RegCount = 1; sh->status = SOCK_STATUS_NEW; pipeInit(&sh->IOPipe); pipeInitFunctions(&sh->IOPipe, NULL, NULL, (PipeFuncAttach) sockPipeAttach, (PipeFuncDetach) sockPipeDetach, (PipeFuncClose) sockPipeClose, NULL); //sh->IOPipe.attachFunction = sockPipeAttach; //sh->IOPipe.detachFunction = sockPipeDetach; //sh->IOPipe.closeFunction = sockPipeClose; sh->IOPipe.status = PSTATUS_NORMAL; } /** Creates and initializes a new SockHandle. @return Pointer to the new SockHandle. */ SockHandle *sockMake() { SockHandle *sh; LOGMEMDUMP(); sh = memCopy(&blanksockhandle, sizeof(SockHandle), "Sock", NULL); LOGDEBUG (stringJoin("sockMake:", ptrToString(sh))); sockInit(sh); return sh; } /** Creates a new SockHandle and saves the pointer to the specified address. @param sh A pointer to where the pointer to a SockHandle will be stored. */ void sockMakeAt(SockHandle **sh) { LOGDEBUG (stringJoin("sockMakeAt:", ptrToString(sh))); if(sh == NULL) { return; } *sh = sockMake(); } /** Increase the reference count to this sock. @param sh Pointer to the SockHandle. */ void sockRegister(SockHandle *sh) { LOGDEBUG (stringJoin("sockRegister:", ptrToString(sh))); ++sh->RegCount; return; } /** Decrements the reference count to this sock and releases it when it reaches zero. @param sh Pointer to the SockHandle to release. */ void sockFree(SockHandle *sh) { LOGDEBUG (stringJoin("sockFree:", ptrToString(sh))); LOGDEBUG (stringJoin("- reference count:", intToString(sh->RegCount))); if(--sh->RegCount != 0) { return; } noderefFree(sh->noderef); noderefFree(sh->localnoderef); sockClose(sh); pipeFree(&sh->IOPipe); memFree(sh); LOGMEMDUMP(); } /** Internal - Gets the error code of the last socket operation. @param so The socket handle. */ int socketGetError(SOCKET so) { int err = SOCKET_ERROR; #ifdef _WINDOZE_ err = WSAGetLastError(); #else err = errno; #endif LOGDEBUG (stringJoinMany( "sockerGetError: socket(", intToString(so), ") error(", intToString(err), ")", NULL)); return err; } /** Internal - Gets and saves the last socket error, also returns the error code. @param sh Pointer to the SockHandle. @return The error code. */ int sockRecordError(SockHandle *sh) { LOGDEBUG (stringJoin("sockRecordError:", ptrToString(sh))); if(sh == NULL) { return SOCKET_ERROR; } return sh->socketerror = socketGetError(sh->Socket); } /** Logs an error related to a SockHandle. @param sh Pointer to the SockHandle. @param msg Releasable string, descriptive context of the error. */ void sockPrintError(SockHandle *sh, char *msg) { LOGERROR(stringJoinMany( "Socket(", intToString(sh->Socket), "), Error (", intToString(sh->socketerror), "):", msg, NULL )); } /** Internal - Converts a NodeRef to a network socket address. @param nr Pointer to the NodeRef to lookup. @param sai Pointer to the address structure. @return Zero on success, non-zero error code on failure. */ int nodeRefToSockAddr(NodeRef *nr, struct sockaddr_in *sai) { if (sai == NULL) { return SOCK_ERR_NOSOCKHANDLE; } if (nr == NULL) { return SOCK_ERR_BADNODEREF; } sai->sin_port = htons((unsigned short)nr->PortNum); if(sai->sin_port == 0 && nr->PortNum != 0) { //noderefFree(nr); return SOCK_ERR_BADPORT; } sai->sin_addr.s_addr = inet_addr(nr->HostName); //try #.#.#.# first if(sai->sin_addr.s_addr == INADDR_NONE) { struct hostent *nameresult = gethostbyname(nr->HostName); // try a DNS lookup if (nameresult == NULL) { LOGERROR(stringJoinMany("nodeRefToSockAddr: bad host name: host(", stringCopy(nr->HostName), "), port(", intToString(nr->PortNum) ,")", _("Failed to lookup host name."), NULL)); return SOCK_ERR_BADHOST; } memcpy(&sai->sin_addr, nameresult->h_addr, nameresult->h_length); } return SOCK_ERR_NONE; } /** Set the remote NodeRef, does a look up. A copy of the NodeRef is made. @param sh The SockHandle. @param nr The NodeRef to copy. @return Zero if successful, or non-zero error code. */ int sockSetNodeRef(SockHandle *sh, NodeRef *nr) { struct sockaddr_in tmpin = BLANKSOCKADDR_IN; int er; if (sh == NULL) { return SOCK_ERR_NOSOCKHANDLE; } if (sh->noderef != NULL) { noderefFree(sh->noderef); sh->noderef = NULL; } if(nr == NULL) { return SOCK_ERR_BADNODEREF; } nr = noderefCopy(nr); //nodeRefFree(nr); er = nodeRefToSockAddr(nr, &tmpin); if(er != SOCK_ERR_NONE) { noderefFree(nr); return er; } sh->SockAddress.sin_port = tmpin.sin_port; sh->SockAddress.sin_addr.s_addr = tmpin.sin_addr.s_addr; sh->noderef = nr; return SOCK_ERR_NONE; } /** Set the local NodeRef, does a look up. A copy of the NodeRef is made. @param sh Pointer to the SockHandle. @param nr Pointer to the NodeRef to copy. @return Zero if successful, or non-zero error code. */ int sockSetLocalNodeRef(SockHandle *sh, NodeRef *nr) { struct sockaddr_in tmpin = BLANKSOCKADDR_IN; int er; if (sh == NULL) { return SOCK_ERR_NOSOCKHANDLE; } if (sh->localnoderef != NULL) { noderefFree(sh->localnoderef); sh->localnoderef = NULL; } if(nr == NULL) { return SOCK_ERR_BADNODEREF; } nr = noderefCopy(nr); //nodeRefFree(nr); er = nodeRefToSockAddr(nr, &tmpin); if(er != SOCK_ERR_NONE) { noderefFree(nr); return er; } sh->LocalSockAddress.sin_port = tmpin.sin_port; sh->LocalSockAddress.sin_addr.s_addr = tmpin.sin_addr.s_addr; sh->localnoderef = nr; return SOCK_ERR_NONE; } /** Internal - Sets the remote network socket address. Does not update any NodeRef @param sh Pointer to the SockHandle. @param sain Pointer to the network address to set. @return Zero if successful, or non-zero error code. */ int sockSetSockAddress(SockHandle *sh, struct sockaddr_in *sain) { if(sh == NULL) { return SOCK_ERR_NOSOCKHANDLE; } if(sain == NULL) { return SOCK_ERR_NOSOCKHANDLE; } memcpy(&sh->SockAddress, sain, sizeof(struct sockaddr_in)); return SOCK_ERR_NONE; } /** Internal - Sets the local network socket address. Does not update any NodeRef @param sh Pointer to the SockHandle. @param sain Pointer to the network address to set. @return Zero if successful, or non-zero error code. */ int sockSetLocalSockAddress(SockHandle *sh, struct sockaddr_in *sain) { if(sh == NULL) { return SOCK_ERR_NOSOCKHANDLE; } if(sain == NULL) { return SOCK_ERR_NOSOCKHANDLE; } memcpy(&sh->SockAddress, sain, sizeof(struct sockaddr_in)); return SOCK_ERR_NONE; } /** Opens an outbound connection. @param sh Pointer to the SockHandle to use. @return Zero if successful, or non-zero error code. */ int sockOpen(SockHandle *sh) { int er; LOGDEBUG (stringJoin("sockOpen:", ptrToString(sh))); if(sh == NULL) { return SOCK_ERR_NOSOCKHANDLE; } sh->direction = SOCK_DIR_OUT; //todo: check status //todo: check return error codes #ifdef __FreeBSD__ sh->Socket = socket(PF_INET, SOCK_STREAM, 0); #else sh->Socket = socket(AF_INET, SOCK_STREAM, 0); #endif LOGDEBUG(stringJoin("- socket:", intToString(sh->Socket))); if(sh->Socket == SOCKET_ERROR) { return SOCK_ERR_SOCKETERROR; } er = bind(sh->Socket, (const struct sockaddr *) &sh->LocalSockAddress, sizeof(struct sockaddr_in)); if(er == SOCKET_ERROR) { sockRecordError(sh); sockPrintError(sh, "Failed to bind new connection."); sockClose(sh); return SOCK_ERR_BINDFAILED; } er = connect(sh->Socket, (const struct sockaddr *) &sh->SockAddress, sizeof(struct sockaddr_in)); //sh if(er == SOCKET_ERROR) { sockRecordError(sh); sockPrintError(sh, "Failed to open new connection."); LOGERROR(stringJoinMany("sockOpen: open failed: host(", stringCopy(sh->noderef->HostName), "), port(", intToString(sh->noderef->PortNum) ,")", _("Failed to open an outbound connection."), NULL)); sockClose(sh); return SOCK_ERR_NOCONNECT; } sh->status = SOCK_STATUS_OPEN; sh->IOPipe.status |= PSTATUS_OPENED; randomAddEntropyClock(2); return SOCK_ERR_NONE; } /** Closes a connection. @param sh Pointer to the SockHandle to use. @return Zero if successful, or non-zero error code. */ int sockClose(SockHandle *sh) { LOGDEBUG (stringJoin("sockClose:", ptrToString(sh))); if(sh == NULL) { return SOCK_ERR_NOSOCKHANDLE; } //todo: check status //todo: check return error codes if(sh->Socket != SOCKET_ERROR) { closesocket(sh->Socket); } sh->direction = SOCK_DIR_UNKNOWN; sh->status = SOCK_STATUS_CLOSED; sh->IOPipe.status |= PSTATUS_CLOSED; sh->Socket = (SOCKET) SOCKET_ERROR; randomAddEntropyClock(1); return SOCK_ERR_NONE; } /** Starts a SockHandle listening for connections. @param sh Pointer to the SockHandle to use. @return Zero if successful, or non-zero error code. */ int sockListen(SockHandle *sh) { int er; LOGDEBUG (stringJoin("sockListen:", ptrToString(sh))); if(sh == NULL) { return SOCK_ERR_NOSOCKHANDLE; } sh->direction = SOCK_DIR_IN; //todo: check status //todo: check return error codes #ifdef __FreeBSD__ sh->Socket = socket(PF_INET, SOCK_STREAM, 0); #else sh->Socket = socket(AF_INET, SOCK_STREAM, 0); #endif if(sh->Socket == SOCKET_ERROR) { return SOCK_ERR_SOCKETERROR; } er = bind(sh->Socket, (struct sockaddr *) &sh->LocalSockAddress, sizeof(struct sockaddr_in)); if(er == SOCKET_ERROR) { sockRecordError(sh); sockPrintError(sh, "Failed to bind listening socket."); LOGERROR(stringJoinMany("sockListen: bind failed: host(", stringCopy(sh->localnoderef->HostName), "), port(", intToString(sh->localnoderef->PortNum) ,")", _("Failed to bind listening socket."), NULL)); sockClose(sh); return SOCK_ERR_BINDFAILED; } er = listen(sh->Socket, SOMAXCONN); if(er == SOCKET_ERROR) { sockRecordError(sh); sockPrintError(sh, "Failed to establish listening socket."); LOGERROR(stringJoinMany("sockListen: listen failed: host(", stringCopy(sh->localnoderef->HostName), "), port(", intToString(sh->localnoderef->PortNum) ,")", _("Failed to establish a listening socket."), NULL)); sockClose(sh); return SOCK_ERR_LISTENFAILED; } randomAddEntropyClock(1); sh->status = SOCK_STATUS_LISTENING; LOGDEBUG(stringJoin("- socket:", intToString(sh->Socket))); return SOCK_ERR_NONE; } /** Accepts an incoming connection from a listening SockHandle. @param sh Pointer to the SockHandle to use. @return A pointer to the SockHandle of the new connection. */ SockHandle *sockAccept(SockHandle *sh) { SockHandle *nsh; int i = sizeof(struct sockaddr_in); LOGDEBUG(stringJoin("sockAccept:", ptrToString(sh))); if(sh == NULL) { return NULL; } //todo: check status nsh = sockMake(); LOGDEBUG(stringJoin("- SockHandle:", ptrToString(nsh))); nsh->direction = SOCK_DIR_IN; nsh->Socket = accept(sh->Socket, (struct sockaddr *) &sh->SockAddress, &i); LOGDEBUG(stringJoin("- socket:", intToString(sh->Socket))); if(nsh->Socket == SOCKET_ERROR) { sockFree(nsh); return NULL; } memcpy(&nsh->LocalSockAddress, &sh->LocalSockAddress, sizeof(struct sockaddr_in)); nsh->status = SOCK_STATUS_OPEN; nsh->IOPipe.status |= PSTATUS_OPENED; nsh->localnoderef = noderefCopy(sh->localnoderef); randomAddEntropyClock(2); return nsh; } /** Receives notice that a pipe has been attached to this SockHandle. @param sh Pointer to the SockHandle in question. @param newpipe Pointer to the Pipe being attached. */ void sockPipeAttach(SockHandle *sh, Pipe *newpipe) { LOGDEBUG(stringJoin("sockPipeAttach:", ptrToString(sh))); sockRegister(sh); } /** Receives notice that a pipe has been detached from this SockHandle. @param sh Pointer to the SockHandle in question. */ void sockPipeDetach(SockHandle *sh) { LOGDEBUG(stringJoin("sockPipeDetach:", ptrToString(sh))); sockFree(sh); } /** Receives notice that a pipe that this SockHandle is part of is closing. @param sh Pointer to the SockHandle in question. */ void sockPipeClose(SockHandle *sh) { LOGDEBUG(stringJoin("sockPipeClose:", ptrToString(sh))); sockClose(sh); } /** Internal - Read data from a socket into a DataBlock. @param so The socket handle to read from. @param db The DataBlock to store read data. @param status Pointer to a SockHandle status. @return The new pointer for db. */ DataBlock *socketRead(SOCKET so, DataBlock *db, int *status) { int i; if(so == SOCKET_ERROR) { return db; } db = dblockExpand(db, db->size + 1024); randomAddEntropyClock(2); i = recv(so, SOCKETBUFFERCAST db->data + db->size, db->maxSize - db->size, MSG_NOSIGNAL); if(i == SOCKET_ERROR || i == 0) { if(status != NULL) { *status = SOCK_STATUS_CLOSING; } //sockRecordError(sh); } else { db->size += i; LOGDEBUGTRAFFIC(stringJoinMany("socketRead: socket ", intToString(so), " got bytes ", intToHexString(i), NULL)); } return db; } /** Internal - Writes data to a socket from a DataBlock. @param so The socket handle to read from. @param db The DataBlock with the data to be sent. @param status Pointer to a SockHandle status. @return The new pointer for db cotaining the unsent portion. */ DataBlock *socketWrite(SOCKET so, DataBlock *db, int *status) { int i = SOCK_MAX_WRITE; int er; if(so == SOCKET_ERROR || db == NULL || db->size == 0) { return db; } if(db->size < i) { if(db->size < 0) { db->size = 0; } i = db->size; } if(i == 0) { return db; } randomAddEntropyClock(2); er = send(so, SOCKETBUFFERCAST db->data, i, MSG_NOSIGNAL); if(er == SOCKET_ERROR || er == 0) { if(status != NULL) { *status = SOCK_STATUS_CLOSING; } //sockRecordError(sh); } else { db = dblockDelete(db, 0, er); LOGDEBUGTRAFFIC(stringJoinMany("socketWrite: socket ", intToString(so), " sent bytes ", intToHexString(er), NULL)); } return db; } /** Reads data from the SockHandle's socket and into its Pipe. @param sh Pointer to the SockHandle to use. */ void sockRead(SockHandle *sh) { sh->IOPipe.inBuffer = socketRead(sh->Socket, sh->IOPipe.inBuffer, &sh->status); } /** Writes the data queued in the SockHandle's Pipe out to it's corresponding socket. @param sh Pointer to the SockHandle to use. */ void sockWrite(SockHandle *sh) { sh->IOPipe.outBuffer = socketWrite(sh->Socket, sh->IOPipe.outBuffer, &sh->status); } //@}