git » nmdb » master » tree

[master] / libnmdb / tcp.c

#if ENABLE_TCP

#include <sys/types.h>		/* socket defines */
#include <sys/socket.h>		/* socket functions */
#include <stdlib.h>		/* malloc() */
#include <stdint.h>		/* uint32_t and friends */
#include <arpa/inet.h>		/* htonls() and friends */
#include <string.h>		/* memcpy() */
#include <unistd.h>		/* close() */

#include <netinet/tcp.h>	/* TCP stuff */
#include <netdb.h>		/* gethostbyname() */

#include "nmdb.h"
#include "net-const.h"
#include "internal.h"
#include "tcp.h"


/* Used internally to really add the server once we have an IP address. */
static int add_tcp_server_addr(nmdb_t *db, in_addr_t *inetaddr, int port)
{
	int rv, fd;
	struct nmdb_srv *newsrv, *newarray;

	fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0)
		return 0;

	newarray = realloc(db->servers,
			sizeof(struct nmdb_srv) * (db->nservers + 1));
	if (newarray == NULL) {
		close(fd);
		return 0;
	}

	db->servers = newarray;
	db->nservers++;

	if (port < 0)
		port = TCP_SERVER_PORT;

	newsrv = &(db->servers[db->nservers - 1]);

	newsrv->fd = fd;
	newsrv->info.in.srvsa.sin_family = AF_INET;
	newsrv->info.in.srvsa.sin_port = htons(port);
	newsrv->info.in.srvsa.sin_addr.s_addr = *inetaddr;

	rv = connect(fd, (struct sockaddr *) &(newsrv->info.in.srvsa),
			sizeof(newsrv->info.in.srvsa));
	if (rv < 0)
		goto error_exit;

	/* Disable Nagle algorithm because we often send small packets. Huge
	 * gain in performance. */
	rv = 1;
	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &rv, sizeof(rv)) < 0 )
		goto error_exit;

	newsrv->type = TCP_CONN;

	/* keep the list sorted by port, so we can do a reliable selection */
	qsort(db->servers, db->nservers, sizeof(struct nmdb_srv),
			compare_servers);

	return 1;

error_exit:
	close(fd);
	newarray = realloc(db->servers,
			sizeof(struct nmdb_srv) * (db->nservers - 1));
	if (newarray == NULL) {
		db->servers = NULL;
		db->nservers = 0;
		return 0;
	}

	db->servers = newarray;
	db->nservers -= 1;

	return 0;
}

/* Same as nmdb_add_tipc_server() but for TCP connections. */
int nmdb_add_tcp_server(nmdb_t *db, const char *addr, int port)
{
	int rv;
	struct hostent *he;
	struct in_addr ia;

	/* We try to resolve and then pass it to add_tcp_server_addr(). */
	rv = inet_pton(AF_INET, addr, &ia);
	if (rv <= 0) {
		he = gethostbyname(addr);
		if (he == NULL)
			return 0;

		ia.s_addr = *( (in_addr_t *) (he->h_addr_list[0]) );
	}

	return add_tcp_server_addr(db, &(ia.s_addr), port);
}

int tcp_srv_send(struct nmdb_srv *srv, unsigned char *buf, size_t bsize)
{
	ssize_t rv;
	uint32_t len;

	len = htonl(bsize);
	memcpy(buf, (const void *) &len, 4);

	rv = ssend(srv->fd, buf, bsize, 0);
	if (rv != bsize)
		return 0;
	return 1;
}

static ssize_t recv_msg(int fd, unsigned char *buf, size_t bsize)
{
	ssize_t rv, t;
	uint32_t msgsize;

	rv = recv(fd, buf, bsize, 0);
	if (rv <= 0)
		return rv;

	if (rv < 4) {
		t = srecv(fd, buf + rv, 4 - rv, 0);
		if (t <= 0) {
			return t;
		}

		rv = rv + t;
	}

	msgsize = * ((uint32_t *) buf);
	msgsize = ntohl(msgsize);

	if (msgsize > bsize)
		return -1;

	if (rv < msgsize) {
		t = srecv(fd, buf + rv, msgsize - rv, 0);
		if (t <= 0) {
			return t;
		}

		rv = rv + t;
	}

	return rv;
}


/* Used internally to get and parse replies from the server. */
uint32_t tcp_get_rep(struct nmdb_srv *srv,
		unsigned char *buf, size_t bsize,
		unsigned char **payload, size_t *psize)
{
	ssize_t rv;
	uint32_t id, reply;

	rv = recv_msg(srv->fd, buf, bsize);
	if (rv <= 0)
		return -1;

	id = * ((uint32_t *) buf + 1);
	id = ntohl(id);
	reply = * ((uint32_t *) buf + 2);
	reply = ntohl(reply);

	if (id != ID_CODE) {
		return -1;
	}

	if (payload != NULL) {
		*payload = buf + 4 + 4 + 4;
		*psize = rv - 4 - 4 - 4;
	}
	return reply;
}

#else
/* Stubs to use when TCP is not enabled. */

#include <stdint.h>
#include "nmdb.h"
#include "tcp.h"

int nmdb_add_tcp_server(nmdb_t *db, const char *addr, int port)
{
	return 0;
}

int tcp_srv_send(struct nmdb_srv *srv, unsigned char *buf, size_t bsize)
{
	return 0;
}

uint32_t tcp_get_rep(struct nmdb_srv *srv,
		unsigned char *buf, size_t bsize,
		unsigned char **payload, size_t *psize)
{
	return -1;
}

#endif /* ENABLE_TCP */