/* Copyright (c) 2005 Mackenzie Straight <eiz@codealchemy.org> */
/* Use of this software for any purpose is allowed, provided this notice */
/* is preserved. It is provided as-is. If it breaks, you get to keep */
/* both pieces. */
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/select.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "opas.h"
static opas_client_t *_opas_new_client(int sock) {
opas_client_t *rv = malloc(sizeof(opas_client_t));
rv->sock = sock;
rv->in_len = 0;
rv->state = opas_state_header;
rv->st_hdr = 0;
rv->st_len = 4;
return rv;
}
opas_client_t *opas_open(char *server, int port) {
int sock;
struct hostent *host;
struct sockaddr_in addr;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return NULL;
if ((host = gethostbyname(server)) == NULL)
return NULL;
memcpy(&addr.sin_addr, host->h_addr_list[0], host->h_length);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
return NULL;
return _opas_new_client(sock);
}
void opas_set_dispatch(opas_client_t *client, dispatch_func func) {
client->dispatch = func;
}
static void _opas_init_header(opas_header_t *hdr,
uint32_t length,
uint32_t flags) {
hdr->bits = OPAS_VERSION;
hdr->bits |= length-4;
hdr->bits |= flags;
hdr->bits = htonl(hdr->bits);
}
void opas_send_req4(opas_client_t *client, uint32_t user_data, struct in_addr ipaddr) {
opas_request4_t req;
_opas_init_header(&req.header, sizeof(opas_request4_t), 0);
req.user_data = user_data;
req.ipaddr = ipaddr;
send(client->sock, &req, sizeof(opas_request4_t), 0);
}
void opas_send_ping(opas_client_t *client) {
opas_header_t hdr;
_opas_init_header(&hdr, sizeof(opas_header_t), BIT(14));
send(client->sock, &hdr, sizeof(opas_header_t), 0);
}
static int _opas_has_input(opas_client_t *client, int dowait) {
fd_set fds;
struct timeval tv;
FD_SET(client->sock, &fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(client->sock+1, &fds, NULL, NULL, dowait ? NULL : &tv) < 0)
return -1;
return FD_ISSET(client->sock, &fds);
}
static opas_header_t *_opas_cons_packet(opas_client_t *client) {
int len = 4 + (ntohl(client->st_hdr)&0xFF);
opas_header_t *rv = malloc(clamp(len, 0, client->in_len+4));
memcpy(4+(char*)rv, client->inbuf, client->in_len);
rv->bits = client->st_hdr;
client->st_hdr = 0;
return rv;
}
int opas_wait(opas_client_t *client) {
return _opas_has_input(client, 1);
}
int opas_dispatch(opas_client_t *client) {
while (_opas_has_input(client, 0) > 0) {
char buf[1024];
int i, len = recv(client->sock, buf, 1024, 0);
if (len < 0)
return -1;
else if (len == 0)
return 0;
for (i = 0; i < len; ++i) {
if (client->state == opas_state_header) {
client->st_hdr |= buf[i] << ((4 - client->st_len) << 3);
if (!--client->st_len) {
client->state = opas_state_packet;
client->st_len = ntohl(client->st_hdr) & 0xFF;
}
} else if (client->state == opas_state_packet) {
if (client->in_len < BUF_SIZE-1)
client->inbuf[client->in_len++] = buf[i];
if (!--client->st_len) {
client->dispatch(client, _opas_cons_packet(client));
client->state = opas_state_header;
client->st_len = 4;
}
}
}
}
return 1;
}
opas_packet_type_t opas_packet_type (opas_header_t *packet) {
int hdr = ntohl(packet->bits);
if (hdr & BIT(11) && hdr & BIT(10))
return opas_packet_reply_proxy;
else if (hdr & BIT(11))
return opas_packet_reply_neg;
else if (hdr & BIT(8))
return opas_packet_error;
else
return opas_invalid_packet;
}
int opas_packet_length (opas_header_t *packet) {
return ntohl(packet->bits) & 0xFF;
}