/* 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; }