580 lines
14 KiB
C
580 lines
14 KiB
C
|
/*
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_START@
|
||
|
*
|
||
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original Code
|
||
|
* as defined in and that are subject to the Apple Public Source License
|
||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||
|
* compliance with the License. Please obtain a copy of the License at
|
||
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||
|
* file.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||
|
* Please see the License for the specific language governing rights and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_END@
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
proxy_unix.c
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
#include <unistd.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/time.h>
|
||
|
|
||
|
#include <netinet/in.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netdb.h>
|
||
|
|
||
|
#include <sys/resource.h>
|
||
|
#include <sys/signal.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <arpa/inet.h>
|
||
|
|
||
|
#include "util.h"
|
||
|
#include "proxy_plat.h"
|
||
|
|
||
|
#if USE_THREAD
|
||
|
#include "pthread.h"
|
||
|
#endif
|
||
|
|
||
|
#include "../defaultPaths.h"
|
||
|
|
||
|
char *gConfigFilePath = DEFAULTPATHS_ETC_DIR "streamingproxy.conf";
|
||
|
|
||
|
char *gOptionsString = "-c:-p:-d-D-v-h-s-x-i:";
|
||
|
char gOptionsChar = '-';
|
||
|
|
||
|
extern int gMaxPorts;
|
||
|
extern float gDropPercent;
|
||
|
|
||
|
/**********************************************/
|
||
|
int init_network()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
#define kKILL_THREAD -3
|
||
|
int term_network()
|
||
|
{
|
||
|
int send = kKILL_THREAD;
|
||
|
name_to_ip_num("", &send, true);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
typedef struct ghpb_rec {
|
||
|
char name[256];
|
||
|
int *result;
|
||
|
} ghpb_rec, *ghpb;
|
||
|
|
||
|
#if USE_THREAD
|
||
|
void *gethostthread(void *param)
|
||
|
{
|
||
|
struct hostent *hent;
|
||
|
ghpb pb = (ghpb)param;
|
||
|
int id;
|
||
|
pthread_t tid;
|
||
|
int tryCount = 0;
|
||
|
|
||
|
if (*pb->result == kKILL_THREAD)
|
||
|
exit(0);
|
||
|
tid = pthread_self();
|
||
|
again:
|
||
|
hent = gethostbyname(pb->name);
|
||
|
if (hent == NULL) do
|
||
|
{
|
||
|
tryCount ++;
|
||
|
if (h_errno == TRY_AGAIN)
|
||
|
{ if (tryCount < 10)
|
||
|
goto again;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
*pb->result = -1;
|
||
|
pthread_exit(NULL);
|
||
|
} while(0);
|
||
|
id = ntohl(((struct in_addr *)(hent->h_addr_list[0]))->s_addr);
|
||
|
*pb->result = id;
|
||
|
free(pb);
|
||
|
pthread_exit(NULL);
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
/**********************************************/
|
||
|
int name_to_ip_num(char *name, int *ip, int async)
|
||
|
{
|
||
|
int ret;
|
||
|
struct in_addr addr;
|
||
|
int tryAgain = 0;
|
||
|
#if USE_THREAD
|
||
|
ghpb pb = NULL;
|
||
|
pthread_t tid;
|
||
|
#endif
|
||
|
struct hostent *hent;
|
||
|
|
||
|
if (check_IP_cache(name, &ret) != -1)
|
||
|
{
|
||
|
*ip = ret;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if USE_THREAD
|
||
|
if (async)
|
||
|
{
|
||
|
*ip = kPENDING_ADDRESS;
|
||
|
pb = (ghpb)malloc(sizeof(ghpb_rec));
|
||
|
strcpy(pb->name, name);
|
||
|
pb->result = ip;
|
||
|
pthread_create(&tid, NULL, gethostthread, (void*)pb);
|
||
|
pthread_detach(tid);
|
||
|
return 1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
again:
|
||
|
|
||
|
tryAgain ++;
|
||
|
if ( inet_aton( name, &addr ) )
|
||
|
{ *ip = ntohl( addr.s_addr );
|
||
|
add_to_IP_cache(name, *ip );
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
hent = gethostbyname(name);
|
||
|
if (hent == NULL) {
|
||
|
if (h_errno == TRY_AGAIN)
|
||
|
if (tryAgain < 10)
|
||
|
goto again;
|
||
|
add_to_IP_cache(name, -1);
|
||
|
return -1;
|
||
|
}
|
||
|
*ip = ntohl(((struct in_addr *) (hent->h_addr_list[0]))->s_addr);
|
||
|
add_to_IP_cache(name, *ip);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**********************************************/
|
||
|
int get_remote_address(int skt, int *port)
|
||
|
{
|
||
|
#if !defined(sparc) && !defined(SGI) && !defined(WIN32)
|
||
|
unsigned
|
||
|
#endif
|
||
|
int nAddrSize = sizeof(struct sockaddr_in);
|
||
|
struct sockaddr_in remAddr;
|
||
|
int status;
|
||
|
remAddr.sin_addr.s_addr = INADDR_ANY;
|
||
|
status = getpeername(skt, (struct sockaddr*)&remAddr, &nAddrSize);
|
||
|
if (status >= 0)
|
||
|
{
|
||
|
if (port)
|
||
|
*port = ntohs(remAddr.sin_port);
|
||
|
return ntohl(remAddr.sin_addr.s_addr);
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int get_local_address(int skt, int *port) {
|
||
|
#if !defined(sparc) && !defined(SGI) && !defined(WIN32)
|
||
|
unsigned
|
||
|
#endif
|
||
|
int nAddrSize = sizeof(struct sockaddr_in);
|
||
|
struct sockaddr_in remAddr;
|
||
|
int status;
|
||
|
remAddr.sin_addr.s_addr = INADDR_ANY;
|
||
|
status = getsockname(skt, (struct sockaddr*)&remAddr, &nAddrSize);
|
||
|
if (status >= 0)
|
||
|
{
|
||
|
if (port)
|
||
|
*port = ntohs(remAddr.sin_port);
|
||
|
return ntohl(remAddr.sin_addr.s_addr);
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
static int __local_ip_address = -1;
|
||
|
|
||
|
int get_local_ip_address()
|
||
|
{
|
||
|
char buf[256];
|
||
|
struct hostent *hent;
|
||
|
int tryCount = 0;
|
||
|
|
||
|
if (__local_ip_address != -1)
|
||
|
return __local_ip_address;
|
||
|
|
||
|
if (gethostname(buf, 256) < 0)
|
||
|
return -1;
|
||
|
again:
|
||
|
tryCount ++;
|
||
|
hent = gethostbyname(buf);
|
||
|
if (hent == NULL)
|
||
|
{ if (h_errno == TRY_AGAIN)
|
||
|
{ if (tryCount < 10)
|
||
|
{
|
||
|
goto again;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
__local_ip_address = ntohl(((struct in_addr *)hent->h_addr)->s_addr);
|
||
|
return __local_ip_address;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void make_socket_nonblocking(int socket)
|
||
|
{
|
||
|
int flag;
|
||
|
flag = fcntl(socket, F_GETFL, 0);
|
||
|
fcntl(socket, F_SETFL, flag | O_NONBLOCK);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void sleep_milliseconds(int ms)
|
||
|
{
|
||
|
struct timeval tv;
|
||
|
|
||
|
tv.tv_sec = ms / 1000;
|
||
|
tv.tv_usec = (ms - (tv.tv_sec * 1000) ) * 1000;
|
||
|
select(0, NULL, NULL, NULL, &tv);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
time_t microseconds()
|
||
|
{
|
||
|
static bool us_initted = false;
|
||
|
static struct timeval us_time_zero;
|
||
|
struct timeval tv, t;
|
||
|
struct timezone tz;
|
||
|
|
||
|
gettimeofday(&tv, &tz);
|
||
|
if (us_initted == false)
|
||
|
{
|
||
|
us_initted = true;
|
||
|
us_time_zero = tv;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
timer_sub(tv, us_time_zero, t);
|
||
|
return (t.tv_sec * USEC_PER_SEC + t.tv_usec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
bool isReadable(int fd)
|
||
|
{
|
||
|
|
||
|
/* causes crash fd_set is wrong size if num users > 255
|
||
|
// not needed anyway
|
||
|
|
||
|
fd_set set;
|
||
|
struct timeval tv;
|
||
|
int err;
|
||
|
|
||
|
if (fd == INVALID_SOCKET)
|
||
|
return false;
|
||
|
|
||
|
tv.tv_sec = 0; tv.tv_usec = 0;
|
||
|
|
||
|
FD_ZERO(&set);
|
||
|
|
||
|
FD_SET(fd, &set);
|
||
|
|
||
|
err = select(fd+1, &set, NULL, NULL, &tv);
|
||
|
if (err > 0)
|
||
|
if (FD_ISSET(fd, &set))
|
||
|
return true;
|
||
|
return false;
|
||
|
*/
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
bool isWritable(int fd)
|
||
|
{
|
||
|
/* causes crash fd_set is wrong size if num users > 255
|
||
|
// not needed anyway
|
||
|
|
||
|
fd_set set;
|
||
|
struct timeval tv;
|
||
|
int err;
|
||
|
|
||
|
if (fd == INVALID_SOCKET)
|
||
|
return false;
|
||
|
tv.tv_sec = 0; tv.tv_usec = 0;
|
||
|
FD_ZERO(&set);
|
||
|
FD_SET(fd, &set);
|
||
|
err = select(fd+1, NULL, &set, NULL, &tv);
|
||
|
if (err > 0)
|
||
|
if (FD_ISSET(fd, &set))
|
||
|
return true;
|
||
|
return false;
|
||
|
*/
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int new_socket_udp(void)
|
||
|
{
|
||
|
int ret;
|
||
|
ret = socket(PF_INET, SOCK_DGRAM, 0);
|
||
|
gMaxPorts++;
|
||
|
set_socket_max_buf(ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int new_socket_tcp(int is_listener)
|
||
|
{
|
||
|
int ret;
|
||
|
ret = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||
|
gMaxPorts++;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void set_socket_reuse_address(int skt)
|
||
|
{
|
||
|
int i = 1;
|
||
|
setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (char*)&i, sizeof(i));
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void set_socket_max_buf(int skt)
|
||
|
{
|
||
|
int i = 1;
|
||
|
unsigned int len;
|
||
|
len = sizeof(i);
|
||
|
getsockopt(skt, SOL_SOCKET, SO_SNDBUF, (char*)&i, &len);
|
||
|
/*fprintf(stderr, "sndbuf for socket %d was %d\n", skt, i);*/
|
||
|
i *= 2;
|
||
|
setsockopt(skt, SOL_SOCKET, SO_SNDBUF, (char*)&i, len);
|
||
|
getsockopt(skt, SOL_SOCKET, SO_SNDBUF, (char*)&i, &len);
|
||
|
/*fprintf(stderr, "sndbuf for socket %d is now %d\n", skt, i);*/
|
||
|
getsockopt(skt, SOL_SOCKET, SO_RCVBUF, (char*)&i, &len);
|
||
|
/*fprintf(stderr, "rcvbuf for socket %d was %d\n", skt, i);*/
|
||
|
i *= 2;
|
||
|
setsockopt(skt, SOL_SOCKET, SO_RCVBUF, (char*)&i, len);
|
||
|
getsockopt(skt, SOL_SOCKET, SO_RCVBUF, (char*)&i, &len);
|
||
|
/*fprintf(stderr, "rcvbuf for socket %d is now %d\n", skt, i);*/
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int bind_socket_to_address(int skt, int address, int port, int is_listener)
|
||
|
{
|
||
|
struct sockaddr_in sin;
|
||
|
|
||
|
if (address == -1)
|
||
|
address = INADDR_ANY;
|
||
|
|
||
|
memset(&sin, 0, sizeof(sin));
|
||
|
sin.sin_family = AF_INET;
|
||
|
sin.sin_port = htons(port);
|
||
|
sin.sin_addr.s_addr = htonl(address);
|
||
|
return bind(skt, (struct sockaddr*)&sin, sizeof(sin));
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void close_socket(int skt)
|
||
|
{
|
||
|
if (skt != INVALID_SOCKET)
|
||
|
{
|
||
|
gMaxPorts--;
|
||
|
close(skt);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int listen_to_socket(int skt)
|
||
|
{
|
||
|
return listen(skt, 5);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int call_is_waiting(int skt, int *incoming_skt)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = isReadable(skt);
|
||
|
|
||
|
if (ret)
|
||
|
{
|
||
|
*incoming_skt = accept(skt, 0, 0);
|
||
|
|
||
|
if (*incoming_skt <= 0)
|
||
|
{
|
||
|
ret = 0;
|
||
|
}
|
||
|
else
|
||
|
gMaxPorts++;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int accept_connection(int from, int *to)
|
||
|
{
|
||
|
// return accept(from, 0, 0);
|
||
|
return *to;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int connect_to_address(int skt, int address, int port)
|
||
|
{
|
||
|
struct sockaddr_in sin;
|
||
|
|
||
|
memset(&sin, 0, sizeof(sin));
|
||
|
sin.sin_family = AF_INET;
|
||
|
sin.sin_port = htons(port);
|
||
|
sin.sin_addr.s_addr = htonl(address);;
|
||
|
return connect(skt, (struct sockaddr*)&sin, sizeof(sin));
|
||
|
}
|
||
|
|
||
|
int get_interface_addr(int skt)
|
||
|
{
|
||
|
int err = 0;
|
||
|
struct sockaddr_in localAddr;
|
||
|
unsigned int len = sizeof(localAddr);
|
||
|
memset(&localAddr, 0, sizeof(localAddr));
|
||
|
|
||
|
err = getsockname(skt, (struct sockaddr*)&localAddr, &len);
|
||
|
return ntohl(localAddr.sin_addr.s_addr);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int recv_udp(int socket, char *buf, int amt, int *fromip, int *fromport)
|
||
|
{
|
||
|
struct sockaddr_in sin;
|
||
|
int ret;
|
||
|
unsigned int len;
|
||
|
|
||
|
len = sizeof(sin);
|
||
|
memset(&sin, 0, sizeof(sin));
|
||
|
ret = recvfrom(socket, buf, (size_t) amt, 0, (struct sockaddr*)&sin, &len);
|
||
|
if (ret != -1) {
|
||
|
if (fromip)
|
||
|
*fromip = ntohl(sin.sin_addr.s_addr);
|
||
|
if (fromport)
|
||
|
*fromport = ntohs(sin.sin_port);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int send_udp(int skt, char *buf, int amt, int address, int port)
|
||
|
{
|
||
|
struct sockaddr_in sin;
|
||
|
|
||
|
memset(&sin, 0, sizeof(sin));
|
||
|
sin.sin_family = AF_INET;
|
||
|
sin.sin_port = htons(port);
|
||
|
sin.sin_addr.s_addr = htonl(address);
|
||
|
return sendto(skt, buf, (size_t) amt, 0, (struct sockaddr*)&sin, sizeof(sin));
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int recv_tcp(int socket, char *buf, int amt)
|
||
|
{
|
||
|
return read(socket, buf, (size_t) amt);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int send_tcp(int socket, char *buf, int amt)
|
||
|
{
|
||
|
return write(socket, buf, (size_t) amt);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int GetLastSocketError(int skt)
|
||
|
{
|
||
|
return errno;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int init_ui()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
int service_ui(int sleep_time)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void DoStats(stats_chunk *stats)
|
||
|
{
|
||
|
printf("\033[2J\033[H");
|
||
|
printf("Elapsed Time (seconds) : %lu\n", stats->elapsedSeconds);
|
||
|
printf("Number of clients : %lu\n", stats->numClients);
|
||
|
printf("bps Received : %lu\n", stats->bpsReceived);
|
||
|
printf("bps Sent : %lu\n", stats->bpsSent);
|
||
|
printf("Total Packets Received : %lu\n", stats->totalPacketsReceived);
|
||
|
printf("Total Packets Sent : %lu\n", stats->totalPacketsSent);
|
||
|
printf("pps Received : %lu\n", stats->ppsReceived);
|
||
|
printf("pps Sent : %lu\n", stats->ppsSent);
|
||
|
printf("number of ports used : %lu\n", stats->numPorts);
|
||
|
printf("packet loss percent : %f\n", stats->percentLostPackets);
|
||
|
printf("force drop percent : %f\n",gDropPercent);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void ErrorString(char *string)
|
||
|
{
|
||
|
fprintf(stderr, string);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void ErrorString1(char *string, int d)
|
||
|
{
|
||
|
fprintf(stderr, string, d);
|
||
|
}
|
||
|
|
||
|
/**********************************************/
|
||
|
void ErrorStringS(char *string, char *arg)
|
||
|
{
|
||
|
fprintf(stderr, string, arg);
|
||
|
}
|
||
|
|
||
|
void daemonize()
|
||
|
{
|
||
|
switch (fork()) {
|
||
|
case -1: /* error */
|
||
|
fprintf(stderr, "can't daemonize!\n");
|
||
|
case 0: /* child */
|
||
|
break;
|
||
|
default: /* parent */
|
||
|
exit(0);
|
||
|
}
|
||
|
}
|