tetrinet/sockets.c

206 lines
4.5 KiB
C

/* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
* This program is public domain.
*
* Socket routines.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include "sockets.h"
#include "tetrinet.h"
static FILE *logfile;
/*************************************************************************/
static int lastchar = EOF;
int sgetc(int s)
{
int c;
char ch;
if (lastchar != EOF) {
c = lastchar;
lastchar = EOF;
return c;
}
if (read(s, &ch, 1) != 1)
return EOF;
c = ch & 0xFF;
return c;
}
int sungetc(int c, int s)
{
return lastchar = c;
}
/*************************************************************************/
/* Read a string, stopping with (and discarding) 0xFF as line terminator.
* If connection was broken, return NULL.
*/
char *sgets(char *buf, int len, int s)
{
int c;
unsigned char *ptr = (unsigned char *) buf;
if (len == 0)
return NULL;
c = sgetc(s);
while (--len && (*ptr++ = c) != 0xFF && (c = sgetc(s)) >= 0)
;
if (c < 0)
return NULL;
if (c == 0xFF)
ptr--;
*ptr = 0;
if (log) {
if (!logfile)
logfile = fopen(logname, "a");
if (logfile) {
struct timeval tv;
gettimeofday(&tv, NULL);
fprintf(logfile, "[%d.%03d] <<< %s\n",
(int) tv.tv_sec, (int) tv.tv_usec/1000, buf);
fflush(logfile);
}
}
return buf;
}
/*************************************************************************/
/* Adds a 0xFF line terminator. */
int sputs(const char *str, int s)
{
unsigned char c = 0xFF;
int n = 0;
if (log) {
if (!logfile)
logfile = fopen(logname, "a");
if (logfile) {
struct timeval tv;
gettimeofday(&tv, NULL);
fprintf(logfile, "[%d.%03d] >>> %s\n",
(int) tv.tv_sec, (int) tv.tv_usec/1000, str);
}
}
if (*str != 0) {
n = write(s, str, strlen(str));
if (n <= 0)
return n;
}
if (write(s, &c, 1) <= 0)
return n;
return n+1;
}
/*************************************************************************/
/* Adds a 0xFF line terminator. */
int sockprintf(int s, const char *fmt, ...)
{
va_list args;
char buf[16384]; /* Really huge, to try and avoid truncation */
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
return sputs(buf, s);
}
/*************************************************************************/
/*************************************************************************/
int conn(const char *host, int port, char ipbuf[4])
{
#ifdef HAVE_IPV6
char hbuf[NI_MAXHOST];
struct addrinfo hints, *res, *res0;
char service[11];
#else
struct hostent *hp;
struct sockaddr_in sa;
#endif
int sock = -1;
#ifdef HAVE_IPV6
snprintf(service, sizeof(service), "%d", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(host, service, &hints, &res0))
return -1;
for (res = res0; res; res = res->ai_next) {
int errno_save;
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock < 0)
continue;
getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
NULL, 0, 0);
if (connect(sock, res->ai_addr, res->ai_addrlen) == 0) {
if (ipbuf) {
if (res->ai_family == AF_INET6) {
struct sockaddr_in6 *sin6 =
(struct sockaddr_in6 *)(res->ai_addr);
memcpy(ipbuf, (char *)(&sin6->sin6_addr) + 12, 4);
} else {
struct sockaddr_in *sin =
(struct sockaddr_in *)(res->ai_addr);
memcpy(ipbuf, &sin->sin_addr, 4);
}
}
break;
}
errno_save = errno;
close(sock);
sock = -1;
errno = errno_save;
}
freeaddrinfo(res0);
#else /* !HAVE_IPV6 */
memset(&sa, 0, sizeof(sa));
if (!(hp = gethostbyname(host)))
return -1;
memcpy((char *)&sa.sin_addr, hp->h_addr, hp->h_length);
sa.sin_family = hp->h_addrtype;
sa.sin_port = htons((unsigned short)port);
if ((sock = socket(sa.sin_family, SOCK_STREAM, 0)) < 0)
return -1;
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
int errno_save = errno;
close(sock);
errno = errno_save;
return -1;
}
if (ipbuf)
memcpy(retbuf, &sa.sin_addr, 4);
#endif
return sock;
}
/*************************************************************************/
void disconn(int s)
{
shutdown(s, 2);
close(s);
}
/*************************************************************************/