/*
* The olsr.org Optimized Link-State Routing daemon(olsrd)
* Copyright (c) 2005, Andreas Tønnesen(andreto@olsr.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of olsr.org, olsrd nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Visit http://www.olsr.org for more information.
*
* If you find this software useful feel free to make a donation
* to the project. For more information see the website or contact
* the copyright holders.
*
* $Id: main.c,v 1.28 2007/08/02 10:20:25 bernd67 Exp $
*/
/* olsrd host-switch daemon */
#include "olsr_host_switch.h"
#include "link_rules.h"
#include "ohs_cmd.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#ifdef WIN32
#undef errno
#define errno WSAGetLastError()
#undef strerror
#define strerror(x) StrError(x)
#define close(x) closesocket(x)
#else
#include <sys/wait.h>
#endif
static int srv_socket;
#define OHS_BUFSIZE 1500
static olsr_u8_t data_buffer[OHS_BUFSIZE];
struct ohs_connection *ohs_conns;
static int ip_version;
int ipsize;
static char ipv6_buf[100]; /* for address coversion */
olsr_u32_t logbits;
/* local functions */
static int
ohs_init_new_connection(int);
static int
ohs_route_data(struct ohs_connection *);
static int
ohs_init_connect_sockets(void);
static int
ohs_configure(void);
static void
ohs_listen_loop(void);
const char *
olsr_ip_to_string(const union olsr_ip_addr *addr)
{
static int index = 0;
static char buff[4][100];
const char *ret;
struct in_addr in;
if(ip_version == AF_INET)
{
in.s_addr=addr->v4;
ret = inet_ntoa(in);
}
else
{
/* IPv6 */
ret = inet_ntop(AF_INET6, &addr->v6, ipv6_buf, sizeof(ipv6_buf));
}
strncpy(buff[index], ret, 100);
ret = buff[index];
index = (index + 1) & 3;
return ret;
}
#ifdef WIN32
int __stdcall
ohs_close(unsigned long signal __attribute__((unused)))
#else
void
ohs_close(int signal __attribute__((unused)))
#endif
{
printf("OHS: exit\n");
close(srv_socket);
exit(0);
}
struct ohs_connection *
get_client_by_addr(union olsr_ip_addr *adr)
{
struct ohs_connection *oc = ohs_conns;
while(oc)
{
if(COMP_IP(adr, &oc->ip_addr))
return oc;
oc = oc->next;
}
return NULL;
}
static int
ohs_init_new_connection(int s)
{
struct ohs_connection *oc;
olsr_u8_t new_addr[4];
int i;
if(logbits & LOG_CONNECT)
printf("ohs_init_new_connection\n");
/* Create new client node */
oc = malloc(sizeof(struct ohs_connection));
if(!oc)
OHS_OUT_OF_MEMORY("New connection");
memset(oc, 0, sizeof(oc));
oc->socket = s;
oc->links = NULL;
oc->rx = 0;
oc->tx = 0;
oc->linkcnt = 0;
// hack alert: WSAEventSelect makes sockets non-blocking, so the
// recv() may return without having read anything on Windows; hence
// re-try for 2 seconds on Windows; shouldn't harm Linux et al.
/* Get "fake IP" */
for (i = 0; i < 20; i++)
{
if (recv(oc->socket, new_addr, 4, 0) == 4)
break;
#if defined WIN32
Sleep(100);
#endif
}
if (i == 20)
{
printf("Failed to fetch IP address! (%s)\n", strerror(errno));
return -1;
}
memcpy(&oc->ip_addr, new_addr, 4);
oc->ip_addr.v4 = ntohl(oc->ip_addr.v4);
if(logbits & LOG_CONNECT)
printf("IP: %s\n", olsr_ip_to_string(&oc->ip_addr));
if(get_client_by_addr(&oc->ip_addr))
{
if(logbits & LOG_CONNECT)
printf("IP: %s DUPLICATE! Disconecting client!\n", olsr_ip_to_string(&oc->ip_addr));
close(s);
return -1;
}
/* Queue */
oc->next = ohs_conns;
ohs_conns = oc;
return 1;
}
int
ohs_delete_connection(struct ohs_connection *oc)
{
if(!oc)
return -1;
/* Close the socket */
close(oc->socket);
if(logbits & LOG_CONNECT)
printf("Removing entry %s\n", olsr_ip_to_string(&oc->ip_addr));
/* De-queue */
if(oc == ohs_conns)
{
ohs_conns = ohs_conns->next;
}
else
{
struct ohs_connection *curr_entry, *prev_entry;
curr_entry = ohs_conns->next;
prev_entry = ohs_conns;
while(curr_entry)
{
if(curr_entry == oc)
{
prev_entry->next = curr_entry->next;
break;
}
prev_entry = curr_entry;
curr_entry = curr_entry->next;
}
}
ohs_delete_all_related_links(oc);
/* Free */
free(oc);
return 0;
}
static int
ohs_route_data(struct ohs_connection *oc)
{
struct ohs_connection *ohs_cs;
ssize_t len;
int cnt = 0;
oc->tx++;
/* Read data */
if((len = recv(oc->socket, data_buffer, OHS_BUFSIZE, 0)) <= 0)
return -1;
if(logbits & LOG_FORWARD)
printf("Received %d bytes from %s\n", (int)len, olsr_ip_to_string(&oc->ip_addr));
/* Loop trough clients */
for(ohs_cs = ohs_conns; ohs_cs; ohs_cs = ohs_cs->next)
{
/* Check that the link is active open */
if(ohs_check_link(oc, &ohs_cs->ip_addr) &&
oc->socket != ohs_cs->socket)
{
ssize_t sent;
/* Send link addr */
if(send(ohs_cs->socket, oc->ip_addr.v6.s6_addr, ipsize, 0) != ipsize)
{
printf("Error sending link address!\n");
}
/* Send data */
if(logbits & LOG_FORWARD)
printf("Sending %d bytes %s=>%s\n", (int)len,
olsr_ip_to_string(&oc->ip_addr),
olsr_ip_to_string(&ohs_cs->ip_addr));
if((sent = send(ohs_cs->socket, data_buffer, len, 0)) != len)
{
printf("Error sending(buf %d != sent %d)\n", (int)len, (int)sent);
}
ohs_cs->rx++;
cnt++;
}
}
return cnt;
}
static int
ohs_init_connect_sockets(void)
{
olsr_u32_t yes = 1;
struct sockaddr_in sin;
printf("Initiating socket TCP port %d\n", OHS_TCP_PORT);
if((srv_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Could not initialize socket(%d): %s\n", srv_socket, strerror(errno));
exit(0);
}
if(setsockopt(srv_socket, SOL_SOCKET, SO_REUSEADDR,
(char *)&yes, sizeof(yes)) < 0)
{
printf("SO_REUSEADDR failed for socket: %s\n", strerror(errno));
close(srv_socket);
exit(0);
}
/* complete the socket structure */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(OHS_TCP_PORT);
/* bind the socket to the port number */
if (bind(srv_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1)
{
printf("bind failed for socket: %s\n", strerror(errno));
close(srv_socket);
exit(0);
}
/* show that we are willing to listen */
if (listen(srv_socket, 5) == -1)
{
printf("listen failed for socket: %s\n", strerror(errno));
close(srv_socket);
exit(0);
}
return 1;
}
static int
ohs_configure(void)
{
return 1;
}
static void accept_handler(void)
{
struct sockaddr_in pin;
socklen_t addrlen = sizeof(pin);
int s;
memset(&pin, 0 , sizeof(pin));
if((s = accept(srv_socket, (struct sockaddr *)&pin, &addrlen)) < 0)
{
printf("accept failed socket: %s\n", strerror(errno));
}
else
{
/* Create new node */
ohs_init_new_connection(s);
}
}
static void stdin_handler(void)
{
ohs_parse_command();
}
static void read_handler(struct ohs_connection *con)
{
if (ohs_route_data(con) < 0)
ohs_delete_connection(con);
}
static void
ohs_listen_loop(void)
{
#if !defined WIN32
int n;
fd_set ibits;
int fn_stdin = fileno(stdin);
while(1)
{
int high;
struct ohs_connection *ohs_cs;
high = 0;
FD_ZERO(&ibits);
/* Add server socket */
high = srv_socket;
FD_SET(srv_socket, &ibits);
if(fn_stdin > high)
high = fn_stdin;
FD_SET(fn_stdin, &ibits);
/* Add clients */
for(ohs_cs = ohs_conns; ohs_cs; ohs_cs = ohs_cs->next)
{
if(ohs_cs->socket > high)
high = ohs_cs->socket;
FD_SET(ohs_cs->socket, &ibits);
}
/* block */
n = select(high + 1, &ibits, 0, 0, NULL);
if(n == 0)
continue;
/* Did somethig go wrong? */
if (n < 0)
{
if(errno == EINTR)
continue;
printf("Error select: %s", strerror(errno));
continue;
}
/* Check server socket */
if(FD_ISSET(srv_socket, &ibits))
accept_handler();
/* Loop trough clients */
ohs_cs = ohs_conns;
while(ohs_cs)
{
struct ohs_connection *ohs_tmp = ohs_cs;
ohs_cs = ohs_cs->next;
if(FD_ISSET(ohs_tmp->socket, &ibits))
read_handler(ohs_tmp);
}
if(FD_ISSET(fn_stdin, &ibits))
stdin_handler();
}
#else
HANDLE Objects[2];
WSANETWORKEVENTS NetEvents;
struct ohs_connection *Walker, *TmpWalker;
unsigned int Res;
Objects[0] = GetStdHandle(STD_INPUT_HANDLE);
Objects[1] = WSACreateEvent();
if (WSAEventSelect(srv_socket, Objects[1], FD_ACCEPT) == SOCKET_ERROR)
{
fprintf(stderr, "WSAEventSelect failed (1): %s\n", strerror(errno));
return;
}
while (1)
{
for (Walker = ohs_conns; Walker != NULL; Walker = Walker->next)
{
if (WSAEventSelect(Walker->socket, Objects[1], FD_READ | FD_CLOSE) == SOCKET_ERROR)
{
fprintf(stderr, "WSAEventSelect failed (2): %s\n", strerror(errno));
Sleep(1000);
continue;
}
}
Res = WaitForMultipleObjects(2, Objects, FALSE, INFINITE);
if (Res == WAIT_FAILED)
{
fprintf(stderr, "WaitForMultipleObjects failed: %s\n", strerror(GetLastError()));
Sleep(1000);
continue;
}
if (Res == WAIT_OBJECT_0)
stdin_handler();
else if (Res == WAIT_OBJECT_0 + 1)
{
if (WSAEnumNetworkEvents(srv_socket, Objects[1], &NetEvents) == SOCKET_ERROR)
fprintf(stderr, "WSAEnumNetworkEvents failed (1): %s\n", strerror(errno));
else
{
if ((NetEvents.lNetworkEvents & FD_ACCEPT) != 0)
accept_handler();
}
for (Walker = ohs_conns; Walker != NULL; Walker = TmpWalker)
{
TmpWalker = Walker->next;
if (WSAEnumNetworkEvents(Walker->socket, Objects[1], &NetEvents) == SOCKET_ERROR)
fprintf(stderr, "WSAEnumNetworkEvents failed (2): %s\n", strerror(errno));
else
{
if ((NetEvents.lNetworkEvents & (FD_READ | FD_CLOSE)) != 0)
read_handler(Walker);
}
}
}
}
#endif
}
int
main(void)
{
#ifdef WIN32
WSADATA WsaData;
if (WSAStartup(0x0202, &WsaData))
{
fprintf(stderr, "Could not initialize WinSock.\n");
exit(EXIT_FAILURE);
}
SetConsoleCtrlHandler(ohs_close, OLSR_TRUE);
#else
signal(SIGINT, ohs_close);
signal(SIGTERM, ohs_close);
/* Avoid zombie children */
signal(SIGCHLD, SIG_IGN);
#endif
printf("olsrd host-switch daemon version %s starting\n", OHS_VERSION);
logbits = LOG_DEFAULT;
ip_version = AF_INET;
ipsize = 4;
srand((unsigned int)time(NULL));
ohs_set_olsrd_path(OHS_DEFAULT_OLSRD_PATH);
ohs_init_connect_sockets();
ohs_configure();
printf("OHS command interpreter reading from STDIN\n");
printf("\n> ");
fflush(stdout);
ohs_listen_loop();
ohs_close(0);
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1