/* * * @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@ * */ /* File: shared_udp.c Contains: UDP Sockets implementation with shared ports */ #include #include #include #include #include #include #if defined(unix) #include #include #include #include #include #include #include #include #include #elif defined(win32) #include "WINSOCK.H" #include "regex.h" #elif defined(mac) #include #include "OpenTransport.h" #endif #include "shared_udp.h" #include "proxy_plat.h" #if DEBUG #define DEBUGPRINT(x) printf x #else #define DEBUGPRINT(x) #endif /**********************************************/ shok *gShokList = NULL; /**********************************************/ ipList *find_ip_in_list(ipList *list, int ip) { ipList *cur = list; DEBUGPRINT(( "-- -- looking for IP %x in IP list\n", ip)); while (cur) { DEBUGPRINT(("-- -- vs. %x\n", cur->ip)); if (cur->ip == ip) { DEBUGPRINT(("-- -- FOUND\n")); return cur; } cur = cur->next; } DEBUGPRINT(("-- -- NOT FOUND\n")); return NULL; } /**********************************************/ int add_ip_to_list(ipList **list, int ip) { ipList *newEl; newEl = (ipList*)malloc(sizeof(ipList)); if (!newEl) return false; newEl->ip = ip; newEl->what_to_do = NULL; newEl->what_to_do_it_with = NULL; newEl->next = *list; *list = newEl; return true; } /**********************************************/ int remove_ip_from_list(ipList **list, int ip) { ipList *last, *theEl = *list; if (theEl->ip == ip) { *list = theEl->next; free(theEl); return true; } last = theEl; theEl = theEl->next; while (theEl) { if (theEl->ip == ip) { last->next = theEl->next; free(theEl); return true; } last = theEl; theEl = theEl->next; } return false; } /**********************************************/ shok *find_available_shok(int fromIP, int toIP, int withSib) { shok *cur = gShokList; while (cur) { DEBUGPRINT(("-- looking for IP %x in shok %p\n", toIP, cur)); if (find_ip_in_list(cur->ips, toIP) == NULL) { if (withSib) { DEBUGPRINT(("-- looking for IP %x in SIB shok %p\n", toIP, cur->sib)); if (find_ip_in_list(cur->sib->ips, toIP) == NULL) return cur; } else return cur; } cur = cur->next; } return NULL; } /**********************************************/ int add_ips_to_shok(shok *theShok, int fromIP, int toIP, int withSib) { add_ip_to_list(&(theShok->ips), toIP); if (withSib) add_ip_to_list(&(theShok->sib->ips), toIP); return 1; } /**********************************************/ static int gUDPPortMin = 4000; static int gUDPPortMax = 65536; #define sInvalidPort -1 static int gNextPort = sInvalidPort; void set_udp_port_min_and_max(int min, int max) { gUDPPortMin = min; gUDPPortMax = max; if (gUDPPortMin & 0x1) gUDPPortMin++; } /**********************************************/ int remove_shok(shok *theShok, int withSib) { shok *cur = NULL, *last; cur = gShokList; if (cur == theShok) { gShokList = cur->next; goto doSib; } last = cur; cur = cur->next; while (cur) { if (cur == theShok) { last->next = cur->next; goto doSib; } last = cur; cur = cur->next; } return false; doSib: if (cur->sib) cur->sib->sib = NULL; if (withSib && cur->sib) remove_shok(cur->sib, false); { ipList *ipn, *ipl = cur->ips; while (ipl) { ipn = ipl->next; free(ipn); ipl = ipn; } } close_socket(cur->socket); free(cur); return true; } /**********************************************/ void remove_shok_ref(shok *theShok, int fromIP, int toIP, int withSib) { remove_ip_from_list(&(theShok->ips), toIP); if (withSib) remove_ip_from_list(&(theShok->sib->ips), toIP); if (theShok->sib->ips == NULL) remove_shok(theShok->sib, false); if (theShok->ips == NULL) remove_shok(theShok, false); } /**********************************************/ shok *make_new_shok(int fromIP, int toIP, int withSib) { shok *theShok1 = NULL, *theShok2 = NULL; int skt1 = INVALID_SOCKET, skt2 = INVALID_SOCKET; int port1 = sInvalidPort; int port2 = sInvalidPort; theShok1 = (shok*)malloc(sizeof(shok)); if (!theShok1) goto bail_error; if (withSib) { theShok2 = (shok*)malloc(sizeof(shok)); if (!theShok2) goto bail_error; } if (gNextPort == -1) gNextPort = gUDPPortMin; retry: if ((skt1 = new_socket_udp()) == SOCKET_ERROR) goto bail_error; if (skt1 == 0) { if (GetLastSocketError(skt1) == EINPROGRESS || GetLastSocketError(skt1) == EAGAIN) goto retry; else goto bail_error; } do { if ((gNextPort & 0x1) && withSib) gNextPort++; if (gNextPort > gUDPPortMax) gNextPort = gUDPPortMin; } while (bind_socket_to_address(skt1, fromIP, port1 = gNextPort++, false) != 0); if (withSib) { retry_rtcp: if ((skt2 = new_socket_udp()) == SOCKET_ERROR) goto bail_error; if (skt2 == 0) { if (GetLastSocketError(skt2) == EINPROGRESS || GetLastSocketError(skt2) == EAGAIN) goto retry_rtcp; else goto bail_error; } if (bind_socket_to_address(skt2, fromIP, port2 = gNextPort++, false) != 0) { close_socket(skt1); close_socket(skt2); skt1 = INVALID_SOCKET; skt2 = INVALID_SOCKET; goto retry; } } make_socket_nonblocking(skt1); theShok1->socket = skt1; theShok1->port = port1; theShok1->ips = NULL; if (withSib) { make_socket_nonblocking(skt2); theShok2->socket = skt2; theShok2->port = port2; theShok2->ips = NULL; theShok2->sib = theShok1; theShok1->sib = theShok2; theShok1->next = theShok2; theShok2->next = gShokList; } else { theShok1->sib = NULL; theShok1->next = gShokList; } add_ips_to_shok(theShok1, fromIP, toIP, withSib); gShokList = theShok1; return theShok1; bail_error: printf("make_new_shok bail_error\n"); close_socket(skt1); close_socket(skt2); if (theShok1 != NULL) free(theShok1); if (theShok2 != NULL) free(theShok2); return NULL; } /**********************************************/ int make_udp_port_pair(int fromIP, int toIP, shok **rtpSocket, shok **rtcpSocket) { shok *theShok; DEBUGPRINT(("MAKE_UDP_PORT_PAIR from %x to %x\n", fromIP, toIP)); DEBUGPRINT(("looking for available shok\n")); if ((theShok = find_available_shok(fromIP, toIP, true)) != NULL) { DEBUGPRINT(("found available shok : SOCKET [%d] PORT [%d]\n", theShok->socket, theShok->port)); add_ips_to_shok(theShok, fromIP, toIP, true); } else { theShok = make_new_shok(fromIP, toIP, true); DEBUGPRINT(("couldn't find shok - made new one : SOCKET [%d] PORT [%d]\n", theShok->socket, theShok->port)); } if (theShok && theShok->sib) { *rtpSocket = theShok; *rtcpSocket = theShok->sib; return 1; } else return -1; } /**********************************************/ int upon_receipt_from(shok *theShok, int fromIP, do_routine doThis, void *withThis) { ipList *listEl; DEBUGPRINT(( "UPON_RECEIPT_FROM %x do routine %p\n", fromIP, doThis)); listEl = find_ip_in_list(theShok->ips, fromIP); if (!listEl) return -1; listEl->what_to_do = doThis; listEl->what_to_do_it_with = withThis; return 0; } /**********************************************/ extern int gNeedsService; extern unsigned long gPacketsReceived; extern unsigned long gPacketsSent; extern unsigned long gBytesReceived; extern unsigned long gBytesSent; extern float gDropPercent; extern unsigned long gDropDelta; /**********************************************/ int service_shoks() { shok *cur; char buf[2048]; cur = gShokList; while (cur) { do_routine doit; int ret, fromPort, fromIP; again: doit = NULL; ret = recv_udp(cur->socket, buf, 2048, &fromIP, &fromPort); if (ret > 0) { ipList *ipl = NULL; DEBUGPRINT(("Got %d bytes for %x on port %d on socket %d\n", ret, fromIP, fromPort, cur->socket)); gBytesReceived += ret; gPacketsReceived++; ipl = find_ip_in_list(cur->ips, fromIP); if (ipl) doit = ipl->what_to_do; if (doit && ipl) { gNeedsService++; ret = (*doit)(ipl->what_to_do_it_with, buf, ret); if (ret == -1) { // what to do about termination, etc. DEBUGPRINT(("put returns error %d\n", errno)); } else if (ret == 0) { // client/server whatever died if (((trans_pb*)ipl->what_to_do_it_with)->status) *(((trans_pb*)ipl->what_to_do_it_with)->status) = 1; DEBUGPRINT(("client/server died\n")); } // what to do about incomplete packet transmission } goto again; } else if (ret < 0) { if (errno != 11) DEBUGPRINT(("recv_udp returns errno %d\n", errno)); // what to do about termination, etc. } cur = cur->next; } return 0; } /**********************************************/ int transfer_data(void *refCon, char *buf, int bufSize) { trans_pb *tpb = (trans_pb*)refCon; int ret; int isRTCP = 0; if (!tpb) return -1; if (strstr(tpb->socketName,"RTCP")) { //printf("shared_udp.c transfer_data %s\n",tpb->socketName); isRTCP = 1; } tpb->packetCount++; if (gDropPercent > 0.0 ) { int packetDropped = 0; //printf("transfer_data tpb->nextDropPacket=%qd tpb->packetCount=%qd\n",tpb->nextDropPacket, tpb->packetCount); if (tpb->packetCount == tpb->nextDropPacket) { tpb->droppedPacketCount ++; tpb->nextDropPacket = 0; //printf("transfer_data tpb->droppedPacketCount=%qd tpb->packetCount=%qd\n",tpb->droppedPacketCount, tpb->packetCount); packetDropped = 1; } if (tpb->nextDropPacket == 0) { int offset = 0; if (gDropPercent <= 50.0) offset = 100.0 / gDropPercent; // drop the percent packet else if (gDropPercent < 100.0 ) offset = 1 + ( ( (rand() % 49) <= ( gDropPercent - 47.0) ) ? 0 : 1);// drop random 1 to 2 packets 1 == next packet or 100% 2== every other 50% else offset = 1; // 100% = drop next packet tpb->nextDropPacket = tpb->packetCount + offset; } if (packetDropped) return bufSize; } ret = send_udp(tpb->send_from->socket, buf, bufSize, tpb->send_to_ip, tpb->send_to_port); DEBUGPRINT(("Sent %d bytes to %x on port %d on socket %d\n", ret, tpb->send_to_ip, tpb->send_to_port, tpb->send_from->socket)); if (ret > 0) { gBytesSent += ret; gPacketsSent++; } return ret; }