/* sock.c * - General Socket Functions * * Copyright (c) 1999 Jack Moffitt, Barath Raghavan, and Alexander Haväng * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #ifdef HAVE_CONFIG_H #ifdef _WIN32 #include #else #include #endif #endif #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #ifndef _WIN32 #include #include #include #include #include #else #include #endif #ifndef HAVE_SOCKLEN_T #define socklen_t int #endif #ifndef _WIN32 extern int h_errno, errno; #endif #include "sock.h" int sock_recoverable(int error) { if (error == EAGAIN || error == EINTR || error == EINPROGRESS || error == EWOULDBLOCK) { return 1; } return 0; } int sock_valid_socket(SOCKET sockfd) { return (sockfd >= 0); } #ifdef _WIN32 int inet_aton(const char *s, struct in_addr *a) { int lsb, b2, b3, msb; if (sscanf(s, "%d.%d.%d.%d", &lsb, &b2, &b3, &msb) < 4) { return 0; } #ifdef HAVE_INET_ADDR a->s_addr = inet_addr(s); if(a->s_addr == -1) return 0; #else a->s_addr = lsb + (b2 << 8) + (b3 << 16) + (msb << 24); #endif return 1; } #endif /* _WIN32 */ int sock_set_blocking(SOCKET sockfd, const int block) { #ifdef _WIN32 int varblock = block; #endif if ((!sock_valid_socket(sockfd)) || (block < 0) || (block > 1)) return SOCKET_ERROR; #ifdef _WIN32 return ioctlsocket(sockfd, FIONBIO, &varblock); #else return fcntl(sockfd, F_SETFL, (block == SOCK_BLOCK) ? 0 : O_NONBLOCK); #endif } int sock_close(SOCKET sockfd) { #ifdef _WIN32 return closesocket(sockfd); #else return close(sockfd); #endif } /* * Write len bytes from buf to the socket. */ int sock_write_bytes(SOCKET sockfd, const char *buff, const int len) { int wrote, res, polled; struct pollfd socks; /* sanity check */ if (!buff) { return SOCKET_ERROR; } else if (len <= 0) { return SOCKET_ERROR; } else if (!sock_valid_socket(sockfd)) { return SOCKET_ERROR; } socks.fd = sockfd; socks.events = POLLOUT; wrote = 0; while (wrote < len) { polled = poll(&socks, 1, 30000); if ((polled == -1) && sock_recoverable(errno)) continue; if (polled != 1) return SOCKET_ERROR; res = send(sockfd, &buff[wrote], len - wrote, 0); if ((res < 0) && (!sock_recoverable(errno))) return SOCKET_ERROR; if (res > 0) wrote += res; } return wrote; } /* * Write a string to a socket. */ int sock_write_string(SOCKET sockfd, const char *buff) { return (sock_write_bytes(sockfd, buff, strlen(buff)) > 0); } /* * Write a printf() style formatted message to the socket * Return 1 if all bytes where successfully written, and 0 if not. * Potential problems: Will truncate the string if longer than 1024 bytes. * Might fuck everything up if no vsnprintf is available */ int sock_write(SOCKET sockfd, const char *fmt, ...) { char buff[1024]; va_list ap; va_start(ap, fmt); #ifdef HAVE_VSNPRINTF vsnprintf(buff, 1024, fmt, ap); #else vsprintf(buff, fmt, ap); #endif va_end(ap); return (sock_write_bytes(sockfd, buff, strlen(buff)) > 0); } /* * Read one line of at max len bytes from sockfd into buff. * If ok, return 1 and nullterminate buff. Otherwize return 0. * Terminating \n is not put into the buffer. * Assert Class: 2 */ int sock_read_line(SOCKET sockfd, char *buff, const int len) { char c = '\0'; int read_bytes, pos; if (!sock_valid_socket(sockfd)) { return 0; } else if (!buff) { return 0; } else if (len <= 0) { return 0; } pos = 0; read_bytes = recv(sockfd, &c, 1, 0); if (read_bytes < 0) { return 0; } while ((c != '\n') && (pos < len) && (read_bytes == 1)) { if (c != '\r') buff[pos++] = c; read_bytes = recv(sockfd, &c, 1, 0); } if (read_bytes == 1) { buff[pos] = '\0'; return 1; } else { return 0; } } /* * Connect to hostname on specified port and return the created socket. */ SOCKET sock_connect_wto(const char *hostname, const int port, const int timeout) { SOCKET sockfd; struct sockaddr_in sin, server; if (!hostname || !hostname[0]) { return INVALID_SOCKET; } else if (port <= 0) { return INVALID_SOCKET; } sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == INVALID_SOCKET) { sock_close(sockfd); return INVALID_SOCKET; } memset(&sin, 0, sizeof(sin)); memset(&server, 0, sizeof(struct sockaddr_in)); if (inet_aton(hostname, (struct in_addr *)&sin.sin_addr) == 0) { sock_close(sockfd); return INVALID_SOCKET; } memcpy(&server.sin_addr, &sin.sin_addr, sizeof(sin)); server.sin_family = AF_INET; server.sin_port = htons(port); /* if we have a timeout, use select, if not, use connect straight. */ /* dunno if this is portable, and it sure is complicated for such a simple thing to want to do. damn BSD sockets! */ if (timeout > 0) { fd_set wfds; struct timeval tv; int retval; int val; socklen_t valsize = sizeof(int); FD_ZERO(&wfds); FD_SET(sockfd, &wfds); tv.tv_sec = timeout; tv.tv_usec = 0; sock_set_blocking(sockfd, SOCK_NONBLOCK); retval = connect(sockfd, (struct sockaddr *)&server, sizeof(server)); if (retval == 0) { sock_set_blocking(sockfd, SOCK_BLOCK); return sockfd; } else { #ifdef _WIN32 if (WSAGetLastError() == WSAEINPROGRESS) { #else if (!sock_recoverable(errno)) { #endif sock_close(sockfd); return SOCKET_ERROR; } } if (select(sockfd + 1, NULL, &wfds, NULL, &tv)) { retval = getsockopt (sockfd, SOL_SOCKET, SO_ERROR, (void *)&val, (socklen_t *)&valsize); if ((retval == 0) && (val == 0)) { sock_set_blocking(sockfd, SOCK_BLOCK); return sockfd; } else { sock_close(sockfd); return SOCKET_ERROR; } } else { sock_close(sockfd); return SOCKET_ERROR; } } else { if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == 0) { return sockfd; } else { sock_close(sockfd); return SOCKET_ERROR; } } }