/*
Library for:
* general networking
* sockets, pipes, etc.
* unbuffered I/O
* other items relating to the above
by John Goerzen, Linux Programming Bible
*/
#include <ctype.h>
#include <stdlib.h>
#include "networkinglib.h"
static int checkstring(const char *string);
/* checkstring() is a private function used only by this library. It checks
the passed string. It returns false if there are no nonnumeric
characters in the string, or true if there are such characters. */
static int checkstring(const char *string) {
int counter;
for (counter = 0; counter < strlen(string); counter++)
if (!(isdigit(string[counter])))
return 1;
return 0;
}
/* Send a string, including terminating null. readdelimstring() could be
perfect for reading it on the other end. And in fact, readstring()
uses just that. */
int writestring(int sockid, char *str) {
return write_buffer(sockid, str, strlen(str) + 1);
}
/* Reads a string from the network, terminated by a null. */
int readstring(int sockid, char *buf, int maxlen) {
return readdelimstring(sockid, buf, maxlen, 0);
}
/* Reads a string terminated by a newline */
int readnlstring(int sockid, char *buf, int maxlen) {
return readdelimstring(sockid, buf, maxlen, '\n');
}
/* Reads a string with an arbitrary ending delimiter. */
int readdelimstring(int sockid, char *buf, int maxlen, char delim) {
int count = 0, status;
while (count <= maxlen) {
status = saferead(sockid, buf+count, 1);
if (status < 0) return status;
if (status < 1) {
HandleError(0, "readdelimstring", "unexpected EOF from socket");
return status;
}
if (buf[count] == delim) { /* Found the delimiter */
buf[count] = 0;
return 0;
}
count++;
}
return 0;
}
/* Copies data from the in to the out file descriptor. If numsize
is nonzero, specifies the maximum number of bytes to copy. If
it is 0, data will continue being copied until in returns EOF. */
int copy(int in, int out, unsigned long maxbytes) {
char buffer[COPY_BUFSIZE];
int indata, remaining;
remaining = maxbytes;
while (remaining || !maxbytes) {
indata = saferead(in, buffer,
(!remaining || COPY_BUFSIZE < remaining) ? COPY_BUFSIZE
: remaining);
if (indata < 1) return indata;
write_buffer(out, buffer, indata);
if (maxbytes) remaining -= indata;
}
return (0);
}
/*
This function will write a certain number of bytes from the buffer
to the descriptor fd. The number of bytes written are returned.
This function will not return until all data is written or an error
occurs.
*/
int write_buffer(int fd, char *buf, int count) {
int status = 0, result;
if (count < 0) return (-1);
while (status != count) {
result = safewrite(fd, buf + status, count - status);
if (result < 0) return result;
status += result;
}
return (status);
}
/*
This function will read a number of bytes from the descriptor fd. The
number of bytes read are returned. In the event of an error, the
error handler is returned. In the event of an EOF at the first read
attempt, 0 is returned. In the event of an EOF after some data has
been received, the count of the already-received data is returned.
*/
int read_buffer(int fd, char *buf, int count) {
char *pts = buf;
int status = 0, n;
if (count < 0) return (-1);
while (status != count) {
n = saferead(fd, pts+status, count-status);
if (n < 0) return n;
if (n == 0) return status;
status += n;
}
return (status);
}
/* Reads a uint32 from the network in network byte order.
A note on the implementation: because some architectures cannot
write to the memory of the integer except all at once, a character
buffer is used that is then copied into place all at once. */
int read_netulong(int fd, uint32_t *value) {
char buffer[sizeof(uint32_t)];
int status;
status = read_buffer(fd, buffer, sizeof(uint32_t));
if (status != sizeof(uint32_t)) {
HandleError(0, "read_netulong", "unexpected EOF");
return -1;
}
bcopy(buffer, (char *)value, sizeof(uint32_t));
*value = ntohl(*value);
return (0);
}
/* Write an unsigned long in network byte order */
int write_netulong(int fd, const unsigned long int value) {
char buffer[sizeof(uint32_t)];
uint32_t temp;
int status;
temp = htonl(value);
bcopy((char *)&temp, buffer, sizeof(temp));
status = write_buffer(fd, buffer, sizeof(temp));
if (status != sizeof(temp)) return -1;
return (0);
}
/* Returns the fully qualified domain name of the current host. */
char *getmyfqdn(void) {
char hostname[200];
gethostname(hostname, sizeof(hostname));
return getfqdn(hostname);
}
/* Returns the fully qualified domain name of an arbitrary host. */
char *getfqdn(const char *host) {
struct hostent *hp;
static char fqdn[200];
hp = gethostbyname(host);
if (!hp)
return (char *)NULL;
safestrncpy(fqdn, (hp->h_aliases[0]) ? hp->h_aliases[0] : hp->h_name,
sizeof(fqdn));
return fqdn;
}
void socketaddr_init(struct sockaddr_in *socketaddr) {
bzero((char *) socketaddr, sizeof(*socketaddr));
socketaddr->sin_family = AF_INET;
}
int socketaddr_service(struct sockaddr_in *socketaddr,
const char *service, const char *proto) {
struct servent *serviceaddr;
/* Need to allow numeric as well as textual data. */
/* 0: pass right through. */
if (strcmp(service, "0") == 0)
socketaddr->sin_port = 0;
else { /* nonzero port */
serviceaddr = getservbyname(service, proto);
if (serviceaddr) {
socketaddr->sin_port = serviceaddr->s_port;
} else { /* name did not resolve, try number */
if (checkstring(service)) { /* and it's a text name, fail. */
HandleError(0, "socketaddr_service", "no lookup for %s/%s",
service, proto);
return -1;
}
if ((socketaddr->sin_port = htons((u_short)atoi(service))) == 0) {
HandleError(0, "socketaddr_service", "numeric conversion failed");
return -1;
}
}
}
return 0;
}
int socketaddr_host(struct sockaddr_in *socketaddr,
const char *host) {
struct hostent *hostaddr;
hostaddr = gethostbyname(host);
if (!hostaddr) {
HandleError(0, "socketaddr_host", "gethostbyname failed for %s", host);
return -1;
}
memcpy(&socketaddr->sin_addr, hostaddr->h_addr, hostaddr->h_length);
return 0;
}
int resolveproto(const char *proto) {
struct protoent *protocol;
protocol = getprotobyname(proto);
if (!protocol) {
HandleError(0, "resolveproto", "getprotobyname failed for %s", proto);
return -1;
}
return protocol->p_proto;
}
int prototype(const char *proto) {
if (strcmp(proto, "tcp") == 0) return SOCK_STREAM;
if (strcmp(proto, "udp") == 0) return SOCK_DGRAM;
return -1;
}
int clientconnect(const char *host, const char *port, const char *proto) {
struct sockaddr_in socketaddr;
int sockid;
socketaddr_init(&socketaddr);
socketaddr_service(&socketaddr, port, proto);
socketaddr_host(&socketaddr, host);
sockid = socket(PF_INET, prototype(proto), resolveproto(proto));
if (sockid < 0) {
HandleError(errno, "clientconnect", "socket failed");
return -1;
}
if (connect(sockid, &socketaddr, sizeof(socketaddr)) < 0) {
HandleError(errno, "clientconnect", "connect failed");
return -1;
}
return sockid;
}
int serverinit(const char *port, const char *proto) {
struct sockaddr_in socketaddr;
int mastersock;
int trueval = 1;
socketaddr_init(&socketaddr);
socketaddr.sin_addr.s_addr = INADDR_ANY;
socketaddr_service(&socketaddr, port, proto);
mastersock = socket(PF_INET, prototype(proto), resolveproto(proto));
if (mastersock < 0) {
HandleError(errno, "serverinit", "couldn't create socket");
return -1;
}
if (bind(mastersock, &socketaddr, sizeof(socketaddr)) < 0) {
HandleError(errno, "serverinit", "bind to port %d failed",
socketaddr.sin_port);
return -1;
}
setsockopt(mastersock, SOL_SOCKET, SO_REUSEADDR, &trueval, sizeof(trueval));
if (prototype(proto) == SOCK_STREAM) {
if (listen(mastersock, 5) < 0) {
HandleError(errno, "serverinit", "listen on port %d failed",
socketaddr.sin_port);
return -1;
}
}
return mastersock;
}
/* Removes CR and LF from the end of a string. */
void stripcrlf(char *temp)
{
while (strlen(temp) &&
((temp[strlen(temp)-1] == 13) || (temp[strlen(temp)-1] == 10))) {
temp[strlen(temp)-1] = 0;
}
}
return to top