/*******************************************************************************/
/*                                                                             */
/*  Copyright 2004 Pascal Gloor                                                */
/*                                                                             */
/*  Licensed under the Apache License, Version 2.0 (the "License");            */
/*  you may not use this file except in compliance with the License.           */
/*  You may obtain a copy of the License at                                    */
/*                                                                             */
/*     http://www.apache.org/licenses/LICENSE-2.0                              */
/*                                                                             */
/*  Unless required by applicable law or agreed to in writing, software        */
/*  distributed under the License is distributed on an "AS IS" BASIS,          */
/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/*  See the License for the specific language governing permissions and        */
/*  limitations under the License.                                             */
/*                                                                             */
/*******************************************************************************/


#include <stdio.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>


#include <p_defs.h>
#include <p_piranha.h>
#include <p_log.h>
#include <p_config.h>
#include <p_socket.h>
#include <p_dump.h>


/* init the global structures */
struct config_t config;
struct peer_t   peer[MAX_PEERS];
struct timeval  ts;


/* 00 BEGIN ;) */
int main(int argc, char *argv[])
{
	#ifdef DEBUG
	/* say hello */
	printf("Piranha v%s.%s.%s BGP Daemon, Copyright(c) 2004 Pascal Gloor\n",P_VER_MA,P_VER_MI,P_VER_PL);
	#endif

	/* set initial time */
	gettimeofday(&ts,NULL);

	{
		char logline[100];
		snprintf(logline, sizeof(logline), "Piranha v%s.%s.%s started.\n",P_VER_MA,P_VER_MI,P_VER_PL);
		p_log_add((time_t)ts.tv_sec, logline);
	}
	
	/* check the cmd line options */
	if ( argc != 2 ) { p_main_syntax(argv[0]); return -1; }

	/* init some stuff and load the config */
	config.file = argv[1];

	if ( p_config_load((struct config_t*)&config,(struct peer_t*)peer, (time_t)ts.tv_sec) == -1 )
	{ fprintf(stderr,"error while parsing configuration file %s\n", config.file); return -1; }

	/* set config reload for signal HUP */
	signal(SIGHUP, p_main_sighup);

	/* init the socket */
	if ( p_socket_start((struct config_t*)&config) == -1 )
	{
		fprintf(stderr,"socket error, aborting\n");
	 	return -1;
	}

	#ifndef DEBUG
	/* we dont use daemon() here, it doesnt exist on solaris/suncc ;-) */
	/* daemon(1,0); */
	if ( mydaemon(1,0) ) { fprintf(stderr,"daemonization error.\n"); }
	#endif

	/* log the pid */
	p_log_pid();

	while ( p_main_loop() == 0 )
	{
		#ifdef DEBUG
		printf("accept() loop\n");
		#endif

		/* we want to sessions to come up slowly */
		/* therefor we sleep a bit here. */
		sleep(1);

		p_log_status((struct config_t*)&config,(struct peer_t*)peer, (time_t)ts.tv_sec);

		/* we update a global var with the actual timestamp */
		gettimeofday(&ts,NULL);
	}

	#ifdef DEBUG
	printf("accept() failed, aborting\n");
	#endif

	return -1;
}

/*  check for accept() and start thread() */
int p_main_loop()
{
	int sock;

	if ( ( sock = p_socket_accept((struct config_t*)&config) ) == -1 )
	{
		return 0;
	}
	else
	{
		pthread_t thread;
		int *mysock;
		mysock = malloc(sizeof(sock));
		memcpy(mysock,&sock,sizeof(mysock));

		pthread_create(&thread, NULL, p_main_peer, (void *)mysock);
	}
	return 0;
}

/* peer thread */
void *p_main_peer(void *data)
{
	int sock;
	int a;
	int allow = 0;
	int peerid = -1;
	struct sockaddr_in sockaddr;
	unsigned int socklen = sizeof(sockaddr);

	memcpy(&sock,data,sizeof(sock));

	#ifdef DEBUG
	printf("I'm thready, my socket is %i\n",sock);
	#endif

	if ( getpeername(sock,(struct sockaddr*)&sockaddr, &socklen) == -1 )
	{
		#ifdef DEBUG
		printf("failed to get peeraddr!\n");
		#endif
		p_main_peer_exit(data,sock);
	}

	#ifdef DEBUG
	printf("peer ip is: %s\n",inet_ntoa(sockaddr.sin_addr));
	#endif

	/* does the peer exist? */
	for(a=0; a<MAX_PEERS; a++)
	{
		if ( peer[a].ip == sockaddr.sin_addr.s_addr && peer[a].allow == 1 )
		{
			char logline[100];
			if ( peer[a].status == 0 )
			{
				snprintf(logline,sizeof(logline), "%s connection (known)\n",inet_ntoa(sockaddr.sin_addr));
				p_log_add((time_t)ts.tv_sec, logline);
				#ifdef DEBUG
				printf("peer ip allowed id %i\n",a);
				#endif
				allow          = 1;
				peer[a].sock   = sock;
				peer[a].status = 1;
				peer[a].rhold  = BGP_DEFAULT_HOLD;
				peer[a].shold  = BGP_DEFAULT_HOLD;
				peer[a].ilen   = 0;
				peer[a].olen   = 0;
				peer[a].rts    = ts.tv_sec;
				peer[a].sts    = ts.tv_sec;
				peer[a].cts    = ts.tv_sec;
				peer[a].rmsg   = 0;
				peer[a].smsg   = 0;
				peer[a].filets = 0;
				peer[a].fh     = NULL;
				peer[a].ucount = 0;
				peerid         = a;

			}
			else
			{
				snprintf(logline, sizeof(logline), "%s connection (already connected)\n", inet_ntoa(sockaddr.sin_addr));
			}

			break;
		}
	}

	if ( allow == 0 )
	{
		char logline[100];
		snprintf(logline, sizeof(logline), "%s connection (unknown)\n",inet_ntoa(sockaddr.sin_addr));
		p_log_add((time_t)ts.tv_sec, logline);

		p_main_peer_exit(data,sock);
	}

	#ifdef DEBUG
	printf("peerid %i\n",peerid);
	#endif
	p_main_peer_loop(peerid);
	p_dump_add_close(peer, peerid, ts.tv_sec);

	/* peer disconnected, lets wait a bit to avoid direct reconnection */
	/* we'll wait DUMPINTERVAL time! */

	peer[peerid].status = 1;
	sleep(DUMPINTERVAL);
	peer[peerid].status = 0;

	p_main_peer_exit(data, sock);

	return NULL;
}

/* peer looop */
void p_main_peer_loop(int id)
{
	char logline[100];
	struct in_addr addr;
	char *ibuf;
	char *obuf;
	uint8_t marker[16];

	{ int a; for(a=0; a<sizeof(marker); a++) { marker[a] = 0xff; } }

	peer[id].ilen = 0;
	peer[id].olen = 0;


	addr.s_addr = peer[id].ip;


	ibuf = malloc(INPUT_BUFFER);
	obuf = malloc(OUTPUT_BUFFER);

	#ifdef DEBUG
	printf("peer status %u\n",peer[id].status);
	printf("starting peer loop\n");
	#endif

	p_main_peer_open(id, obuf);

	while((peer[id].status>0))
	{
		/* receiving new datas, sleep 1sec if nothing */
		int tlen = 0;
		int maxlen = INPUT_BUFFER - peer[id].ilen;

		if ( TEMP_BUFFER < maxlen ) { maxlen = TEMP_BUFFER; }

		tlen = recv(peer[id].sock, ibuf+peer[id].ilen, INPUT_BUFFER-peer[id].ilen, 0);

		if ( tlen == -1 )
		{
			p_dump_check_file(peer,id,ts.tv_sec);
			sleep(1);
		}
		else if ( tlen > 0 )
		{
			#ifdef DEBUG
			printf("got data\n");
			#endif
			peer[id].ilen += tlen;
		}
		else
		{
			snprintf(logline, sizeof(logline), "%s socket went down\n",inet_ntoa(addr));
			p_log_add((time_t)ts.tv_sec, logline);
			#ifdef DEBUG
			printf("something failed on recv()\n");
			#endif
			peer[id].status = 0;
		}


		/* working on datas */
		if ( peer[id].ilen > 0 )
		{
			p_main_peer_work(ibuf, obuf, id);
		}

		/* check if the peer timed out  (note: 0 == no keepalive!) */

		if ( peer[id].rhold != 0 && ( (ts.tv_sec - peer[id].rts) > peer[id].rhold ) )
		{
			/* timeout ;( */
			snprintf(logline, sizeof(logline), "%s holdtime expired\n",inet_ntoa(addr));
			p_log_add((time_t)ts.tv_sec, logline);
			peer[id].status = 0;
		}

		/* time to send a keepalive message ? (note: 0 == no keepalive!) */

		if ( peer[id].shold != 0 && ( (ts.tv_sec - peer[id].sts) > (peer[id].shold / 3) ) )
		{
			/* yeah */
			struct bgp_header r_header;
			memcpy(&r_header, marker, sizeof(marker));
			r_header.len  = htons(BGP_HEADER_LEN);
			r_header.type = 4;

			memcpy(obuf+peer[id].olen, &r_header, BGP_HEADER_LEN);
			peer[id].olen += BGP_HEADER_LEN;

			peer[id].sts = ts.tv_sec;
			peer[id].smsg++;

			p_main_peer_send(id, obuf);
		}


		/* sending */
		while((peer[id].olen>0 && peer[id].status))
		{
			peer[id].smsg++;
			p_main_peer_send(id, obuf);
			if ( peer[id].olen == -1 )
			{
				#ifdef DEBUG
				printf("failed to send!\n");
				#endif
				snprintf(logline, sizeof(logline), "%s failed to send\n",inet_ntoa(addr));
				p_log_add((time_t)ts.tv_sec, logline);
				peer[id].status = 0;
			}
		}

		if ( peer[id].olen == -1 )
		{
			printf("%u error while sending\n",peer[id].ip);
		}


	}

	free(ibuf);
	free(obuf);

	snprintf(logline, sizeof(logline), "%s down\n",inet_ntoa(addr));
	p_log_add((time_t)ts.tv_sec, logline);

	peer[id].cts = ts.tv_sec;

	#ifdef DEBUG
	printf("peer is gone\n");
	#endif
}

/* sending BGP open and first keepalive */
void p_main_peer_open(int id, char *obuf)
{
	/* reply with my open msg */
	struct bgp_header r_header;
	struct bgp_open   r_open;
	uint8_t marker[16];

	{ int a; for(a=0; a<sizeof(marker); a++) { marker[a] = 0xff; } }

	memcpy(r_header.marker, marker, sizeof(r_header.marker));

	r_header.len     = htons(BGP_HEADER_LEN+BGP_OPEN_LEN);
	r_header.type    = 1;
	r_open.version   = 4;
	r_open.as        = htons(config.as);
	r_open.holdtime  = htons(peer[id].shold);
	r_open.bgp_id    = htonl(config.routerid);
	r_open.param_len = 0;

	memcpy(obuf+peer[id].olen,&r_header, BGP_HEADER_LEN);
	peer[id].olen += BGP_HEADER_LEN;

	memcpy(obuf+peer[id].olen,&r_open, BGP_OPEN_LEN);
	peer[id].olen += BGP_OPEN_LEN;


	peer[id].smsg++;
	p_main_peer_send(id,obuf);

	/* we add directly the first keepalive msg */

	r_header.len  = htons(BGP_HEADER_LEN);
	r_header.type = 4;

	memcpy(obuf+peer[id].olen, &r_header, BGP_HEADER_LEN);
	peer[id].olen += BGP_HEADER_LEN;

	/* update the keepalive sent timestamp */
	peer[id].sts = ts.tv_sec;

	/* send the packet */

	peer[id].smsg++;
	p_main_peer_send(id,obuf);
}

/* bgp decoding stuff */
void p_main_peer_work(char *ibuf, char *obuf, int id)
{
	char logline[100];
	uint8_t marker[16];
	struct in_addr addr;

	{ int a; for(a=0; a<sizeof(marker); a++) { marker[a] = 0xff; } }

	addr.s_addr = peer[id].ip;

	/* show packet */
	#ifdef DEBUG
	{
		int a;
		for(a=0; a<peer[id].ilen; a++)
		{
			if ( !(a%8) ) { printf("\n"); }
			printf("%3u ",(uint8_t)*(ibuf+a));
		}
		printf("\n");
	}
	#endif

	while(1)
	{
		int pos = 0;
		struct bgp_header *header;

		if ( peer[id].ilen < sizeof(struct bgp_header) )
		{
			#ifdef DEBUG
			printf("not all header\n");
			#endif
			return;
		}

		header = (struct bgp_header*)ibuf;

		if ( memcmp(header->marker, marker, sizeof(marker)) != 0 )
		{
			snprintf(logline, sizeof(logline), "%s packet decoding error\n",inet_ntoa(addr));
			p_log_add((time_t)ts.tv_sec, logline);
			#ifdef DEBUG
			printf("invalid marker\n");
			#endif
			peer[id].status = 0;
			return;
		}

		#ifdef DEBUG
		printf("len: %u type: %u (buffer %u)\n",htons(header->len),header->type,peer[id].ilen);
		#endif

		if ( peer[id].ilen < htons(header->len) )
		{
			#ifdef DEBUG
			printf("bgp message not complete msg len %u, buffer len %u\n",htons(header->len),peer[id].ilen);
			#endif
			return;
		}

		pos += BGP_HEADER_LEN;
		peer[id].rmsg++;

		peer[id].rts = ts.tv_sec;

		if ( header->type == 1 && peer[id].status == 1 )
		{
			struct bgp_open *bopen;
			bopen = (struct bgp_open*) (ibuf + pos);

			if ( bopen->version != 4 )
			{
				snprintf(logline, sizeof(logline), "%s wrong bgp version (%u)\n",inet_ntoa(addr),bopen->version);
				p_log_add((time_t)ts.tv_sec, logline);
				#ifdef DEBUB
				printf("invalid BGP version %u\n",bopen->version);
				#endif
				peer[id].status = 0;
				return;
			}
			#ifdef DEBUG
			printf("BGP Version: %u\n",bopen->version);
			#endif

			if ( htons(bopen->as) != peer[id].as )
			{
				snprintf(logline, sizeof(logline), "%s wrong neighbor as (%u != %u)\n",inet_ntoa(addr),htons(bopen->as),peer[id].as);
				p_log_add((time_t)ts.tv_sec, logline);
				#ifdef DEBUG
				printf("invalid BGP neighbor AS %u\n",htons(bopen->as));
				#endif
				peer[id].status = 0;
				return;
			}

			peer[id].rhold = htons(bopen->holdtime);

			if ( peer[id].rhold < peer[id].shold )
			{
				peer[id].shold = peer[id].rhold;
			}

			if ( htons(header->len) != BGP_HEADER_LEN + BGP_OPEN_LEN + bopen->param_len )
			{
				snprintf(logline, sizeof(logline), "%s parameter parsing error)\n",inet_ntoa(addr));
				p_log_add((time_t)ts.tv_sec, logline);
				#ifdef DEBUG
				printf("size error in bgp open params, len %u, header %u open %u param %u\n",htons(header->len),BGP_HEADER_LEN,BGP_OPEN_LEN,bopen->param_len);
				#endif
				peer[id].status = 0;
				return;
			}

			pos += BGP_OPEN_LEN;

			while(pos<htons(header->len))
			{
				struct bgp_param *param;
				param = (struct bgp_param*) (ibuf+pos);
				#ifdef DEBUG
				printf("param code %u len %u\n",param->type,param->len);
				{ int a; for(a=0; a<param->len; a++) { printf("%u ",(uint8_t)param->param[a]); } printf("\n"); }
				#endif
				pos += 2;
				pos += param->len;
				if ( param->type > 4 )
				{
					#ifdef DEBUG
					printf("parameter rejected!\n");
					#endif
					peer[id].status = 0;
					return;
				}
			}

			snprintf(logline, sizeof(logline), "%s established\n",inet_ntoa(addr));
			p_log_add((time_t)ts.tv_sec, logline);

			peer[id].status = 2;

			p_dump_add_open(peer, id, ts.tv_sec);

		}
		else if ( header->type == 2 && peer[id].status == 2 )
		{
			/* BGP update */
			uint16_t wlen;
			uint16_t alen;
			void     *aspath = NULL;
			uint16_t aspathlen = 0;
			void     *community = NULL;
			uint16_t communitylen = 0;

			wlen = *(uint16_t *) (ibuf + pos);
			wlen = htons(wlen);
			pos += 2;

			#ifdef DEBUG
			printf("Withdrawn Length %u\n",wlen);
			#endif

			while(wlen>0)
			{
				uint8_t plen;
				uint32_t prefix;
				plen = *(uint8_t *) (ibuf + pos);
				pos++;
				wlen--;

				memcpy(&prefix, ibuf+pos, sizeof(prefix));

				prefix = ntohl(prefix);

				if ( plen > 24 )
				{
					pos+= 4;
					wlen-=4;
				}
				else if ( plen > 16 )
				{
					pos+= 3;
					wlen-=3;
					/* prefix = (prefix >> 8) << 8; */
				}
				else if ( plen > 8 )
				{
					pos+= 2;
					wlen-=2;
					/* prefix = (prefix >> 16) << 16; */
				}
				else if ( plen > 0 )
				{
					pos++;
					wlen--;
					/* prefix = (prefix >> 24) << 24; */
				}
				else
				{
					/* prefix = 0; */
				}

				{
					#ifdef DEBUG
					addr.s_addr = prefix;
					printf("withdrawn %s/%u\n",inet_ntoa(addr),plen);
					#endif
					p_dump_add_withdrawn(peer,id,ts.tv_sec,prefix,plen);
					peer[id].ucount++;
				}
			}

			alen = *(uint16_t *) (ibuf + pos);
			alen = htons(alen);

			#ifdef DEBUG
			printf("announce size: %u\n",alen);
			#endif

			pos+=2;

			while(alen>0)
			{
				uint8_t flags = *(uint8_t*) (ibuf+pos);
				uint8_t code  = *(uint8_t*) (ibuf+pos+1);
				uint16_t codelen;
				alen-=2;
				pos+=2;

				#ifdef DEBUG
				if ( flags & 0x80 ) { printf("attr is optional\n"); } else { printf("attr is well-known\n"); }
				if ( flags & 0x40 ) { printf("attr is transitive\n"); } else { printf("attr is non-transitive\n"); }
				if ( flags & 0x20 ) { printf("attr is partial\n"); } else { printf("attr is complete\n"); }
				if ( flags & 0x10 ) { printf("attr is 2 octet\n"); } else { printf("attr is 1 octets\n"); }

				printf("code: %x\n",code);
				#endif

				if ( flags & 0x10 )
				{
					uint16_t clen = *(uint16_t*) (ibuf+pos);
					clen = htons(clen);

					pos+= 2;
					alen-= 2;
					codelen = clen;
				}
				else
				{
					uint8_t clen = *(uint8_t*) (ibuf+pos);
					pos++;
					alen--;
					codelen = clen;
				}
				#ifdef DEBUG
				printf("length: %u\n",codelen);
				#endif

				if ( code == 2 )
				{
					uint8_t aspath_type = *(uint8_t*) (ibuf+pos);
					uint8_t aspath_len  = *(uint8_t*) (ibuf+pos+1);

					if ( codelen == 0 )
					{
						#ifdef DEBUG
						printf("Empty ASPATH (iBGP)\n");
						aspathlen = 0;
						#endif
					}
					else if ( aspath_type == 1 )
					{
						#ifdef DEBUG
						printf("AS_SET %u\n",aspath_len);
						#endif
					}
					else if ( aspath_type == 2 )
					{
						#ifdef DEBUG
						printf("AS_PATH %u\n",aspath_len);
						{
							int a;
							for(a=0; a<aspath_len; a++)
							{
								uint16_t toto = *(uint16_t*)(ibuf+pos+2+(a*2));
								printf("%5u ",ntohs(toto));
							}
							printf("\n");
						}
						#endif
						aspath = (void *)(ibuf+pos+2);
						aspathlen = aspath_len;
					}
					else
					{
						#ifdef DEBUG
						printf("error in aspath code, type %u unknown (len %u)\n",aspath_type,aspath_len);
						#endif

						snprintf(logline, sizeof(logline), "%s error in aspath code\n",inet_ntoa(addr));
						p_log_add((time_t)ts.tv_sec, logline);
						peer[id].status = 0;
						return;
					}
				}
				else if ( code == 8 )
				{
					if ( (codelen % 4) != 0 )
					{
						snprintf(logline, sizeof(logline), "%s error in community length\n",inet_ntoa(addr));
						p_log_add((time_t)ts.tv_sec, logline);
						peer[id].status = 0;
						return;
					}

					community = (void *) (ibuf+pos);
					communitylen = codelen / 4;

				}
				pos+=codelen;
				alen-=codelen;
			}

			while((htons(header->len) - pos))
			{
				uint8_t  plen   = *(uint8_t *)  (ibuf+pos);
				uint32_t prefix = *(uint32_t *) (ibuf+pos+1);
				pos++;

				prefix = ntohl(prefix);

				if ( plen > 24 )
				{
					pos+= 4;
				}
				else if ( plen > 16 )
				{
					pos+= 3;
					/* prefix &= 0xffffff00; */
				}
				else if ( plen > 8 )
				{
					pos+= 2;
					/* prefix &= 0xffff0000; */
				}
				else if ( plen > 0 )
				{
					pos++;
					/* prefix &= 0xff000000; */
				}
				else
				{
					prefix = 0;
				}

				{
					#ifdef DEBUG
					addr.s_addr = htonl(prefix);
					printf("announce %s/%u\n",inet_ntoa(addr),plen);
					#endif

					p_dump_add_announce(peer,id,ts.tv_sec,prefix,plen,aspath,aspathlen,community,communitylen);
					peer[id].ucount++;
				}
			}

		}
		else if ( header->type == 3 )
		{
			/*  bgp error msg */
			struct bgp_error *error;

			error = (struct bgp_error*) (ibuf + pos);

			#ifdef DEBUG
			printf("error code : %i/%i\n",error->code,error->subcode);
			#endif

			pos += BGP_ERROR_LEN;

			snprintf(logline, sizeof(logline), "%s notification received code %u/%u\n",inet_ntoa(addr),error->code,error->subcode);
			p_log_add((time_t)ts.tv_sec, logline);
			peer[id].status = 0;
		}
		else if ( header->type == 4 && peer[id].status == 2 )
		{
			/* keepalive packet */
			peer[id].rts = ts.tv_sec;
			p_dump_add_keepalive(peer, id, ts.tv_sec);
			#ifdef DEBUG
			printf("received keepalive\n");
			#endif
		}
		else
		{
			#ifdef DEBUG
			printf("invalid header type %u\n",header->type);
			#endif

			snprintf(logline, sizeof(logline), "%s invalid message type\n",inet_ntoa(addr));
			p_log_add((time_t)ts.tv_sec, logline);
			peer[id].status = 0;
			return;
		}

		if ( htons(header->len) != pos )
		{
			#ifdef DEBUG
			printf("something went wrong with the packet size\n");
			#endif

			snprintf(logline, sizeof(logline), "%s error in packet size\n",inet_ntoa(addr));
			p_log_add((time_t)ts.tv_sec, logline);
			peer[id].status = 0;
			return;
		}

		if ( peer[id].ilen <= htons(header->len) )
		{
			peer[id].ilen = 0;
			return;
		}

		#ifdef DEBUG
		printf("still got datas!\n");
		#endif

		memmove(ibuf, ibuf+htons(header->len), peer[id].ilen-htons(header->len));
		peer[id].ilen -= pos;
	}
}

/* send() */
void p_main_peer_send(int id, char *obuf)
{
	/* sending datas */
	if ( peer[id].olen > 0 )
	{
		int slen = 0;

		#ifdef DEBUG
		printf("something to send\n");
		#endif

		slen = send(peer[id].sock, obuf, peer[id].olen, 0);

		if ( slen == peer[id].olen )
		{
			#ifdef DEBUG
			printf("send ok\n");
			#endif
			peer[id].olen = 0;
		}
		else if ( slen == 0 )
		{
			#ifdef DEBUG
			printf("couldnt send anything\n");
			#endif
			peer[id].olen = -1;
		}
		else if ( slen == -1 )
		{
			#ifdef DEBUG
			printf("failed to send()\n");
			#endif
			peer[id].olen = -1;
		}
		else if ( slen < peer[id].olen )
		{
			#ifdef DEBUG
			printf("cound not send all\n");
			#endif
			memmove(obuf, obuf+slen, peer[id].olen-slen);
			peer[id].olen -= slen;
		}
		else
		{
			#ifdef DEBUG
			printf("impossible send() case!\n");
			#endif
			peer[id].olen = -1;
		}
	}
	return;
}

/* peer exit, thread exit */
void p_main_peer_exit(void *data, int sock)
{
	#ifdef DEBUG
	printf("dead thready with socket %i\n",sock);
	#endif

	close(sock);

	free(data);

	pthread_exit(NULL);
}

/*  syntax */
void p_main_syntax(char *prog)
{
	printf("syntax: %s <configuration file>\n",prog);
}

/* kill -HUP for config reload */
void p_main_sighup(int sig)
{
	if ( p_config_load((struct config_t*)&config,(struct peer_t*)peer, (time_t)ts.tv_sec) == -1 )
	{
		#ifdef DEBUG
		printf("failed to reload config!\n");
		#else
		p_log_add((time_t)ts.tv_sec, "failed to reload configuration\n");
		#endif
		exit(1);
	}

	p_log_add((time_t)ts.tv_sec, "configuration reloaded\n");
	signal(sig,p_main_sighup);
}

int mydaemon(int nochdir, int noclose)
{
	int fd;

	/* set new uid/gid */
	setuid(config.uid);
	seteuid(config.uid);
	setgid(config.gid);
	setegid(config.gid);

	switch (fork()) {
	case -1:
		return (-1);
	case 0:
		break;
	default:
		_exit(0);
	}

	if (setsid() == -1)
		return (-1);

	if (!nochdir)
		(void)chdir("/");

	if ( !noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1)
	{
		(void)dup2(fd, STDIN_FILENO);
		(void)dup2(fd, STDOUT_FILENO);
		(void)dup2(fd, STDERR_FILENO);
		if (fd > 2)
			(void)close(fd);
	}
	return(0);
}