📄 connection.c
字号:
/* Gnome-o-Phone - A program for internet telephony Copyright (C) 1999 Roland Dreier This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: connection.c 1.13 Sat, 11 Dec 1999 23:53:26 -0600 dreier $*/#ifdef HAVE_CONFIG_H#include <config.h>#endif#include "connection.h"#include <unistd.h>#include <stdlib.h>#include <sys/time.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <glib.h>#include <math.h>#include <string.h>#include <netdb.h>#include <sys/utsname.h>#include "rtp.h"#include "rtp-packet.h"#include "rtcp-packet.h"#include "listen.h"#include "thread.h"#include "sound.h"#include "request.h"#include "gphone.h"#include "gphone-lib.h"extern int Remote_Port;static const gchar App_Name[] = { /* Name for APP packets */ 'g', 'p', 'h', 'n'};static GHashTable *Source_Table;G_LOCK_DEFINE_STATIC(Source_Table);static int Data_Sock;static int Control_Sock;static int Send_Sock;static guint32 My_Ssrc;static guint16 My_Seq;static guint32 My_Timestamp;static struct sockaddr_in Send_Addr;static gchar *Dest_Hostname;static struct timeval Next_Report;static guint32 Rtp_Packets_Sent;static guint32 Rtp_Octets_Sent;static int Avg_Rtcp_Size;static int We_Called;static gintssrc_equal(gconstpointer a, gconstpointer b){ return *((const guint32 *) a) == *((const guint32 *) b);}static guintssrc_hash(gconstpointer key){ return (guint) *((const guint32 *) key);}static voidopen_sockets(int port){ struct sockaddr_in address; if ((Data_Sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { gphone_perror_exit("*** open_sockets : socket1", 1); } address.sin_family = AF_INET; address.sin_port = g_htons(port); address.sin_addr.s_addr = INADDR_ANY; memset(&address.sin_zero, 0, sizeof address.sin_zero); if (bind(Data_Sock, (struct sockaddr * ) &address, sizeof (struct sockaddr_in)) < 0) { gphone_perror_exit("bind", 1); } if ((Control_Sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { gphone_perror_exit("*** open_sockets : socket2", 1); } address.sin_family = AF_INET; address.sin_port = g_htons(port + 1); address.sin_addr.s_addr = INADDR_ANY; memset(&address.sin_zero, 0, sizeof address.sin_zero); if (bind(Control_Sock, (struct sockaddr * ) &address, sizeof (struct sockaddr_in)) < 0) { gphone_perror_exit("bind", 1); } if ((Send_Sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { gphone_perror_exit("*** open_sockets : socket3", 1); }}static voidset_next_report_time(int packetsize, int init){ double interval; struct timeval tv, now; /* some bad hardwired constants to be fixed here */ interval = rtcp_interval(2, 2, 33 * 50 * .05, 1, packetsize, &Avg_Rtcp_Size, init); tv.tv_sec = (time_t) floor(interval); tv.tv_usec = (time_t) ((interval - floor(interval)) * 1.0e6); if (gettimeofday(&now, 0) < 0) { gphone_perror_exit("*** set_next_report_time : gettimeofday", 1); } timeradd(&now, &tv, &Next_Report);}static gchar *make_cname(void){ gchar *cname; struct utsname un; if ((cname = getenv("GPHONE_CNAME")) != NULL) { return cname; } else { if (uname(&un) < 0) { gphone_perror_exit("*** make_cname : uname", 1); } cname = g_strconcat(g_get_user_name(), "@", un.nodename, NULL); return cname; }}static intsend_rtcp_sr(void){ Rtcp_Compound compound; int packetsize; int nsdes; Rtcp_Sdes_Type *type; char **value; gint8 *length; compound = rtcp_compound_new_allocate(RTP_MTU); rtcp_compound_add_sr(compound, My_Ssrc, My_Timestamp, Rtp_Packets_Sent, Rtp_Octets_Sent); nsdes = 1; type = g_new(Rtcp_Sdes_Type, nsdes); value = g_new(char *, nsdes); length = g_new(gint8, nsdes); type[0] = RTCP_SDES_CNAME; value[0] = make_cname(); length[0] = strlen(value[0]); rtcp_compound_add_sdes(compound, My_Ssrc, nsdes, type, value, length); Send_Addr.sin_port = g_htons(Remote_Port + 1); rtcp_compound_send(compound, Send_Sock, &Send_Addr); g_free(value[0]); g_free(type); g_free(value); g_free(length); packetsize = rtcp_compound_get_length(compound); rtcp_compound_free(compound); return packetsize;}voidsend_switch_packet(void){ Rtcp_Compound compound; guint32 message; compound = rtcp_compound_new_allocate(RTP_MTU); rtcp_compound_add_sr(compound, My_Ssrc, My_Timestamp, Rtp_Packets_Sent, Rtp_Octets_Sent); message = g_htonl(GPHONE_APP_SWITCH); rtcp_compound_add_app(compound, My_Ssrc, App_Name, &message, sizeof message); Send_Addr.sin_port = g_htons(Remote_Port + 1); rtcp_compound_send(compound, Send_Sock, &Send_Addr); rtcp_compound_free(compound);}voidmaybe_send_rtcp(void){ int packetsize; struct timeval tv; if (gettimeofday(&tv, 0) < 0) { gphone_perror_exit("*** maybe_send_rtcp : gettimeofday", 1); } if (timercmp(&tv, &Next_Report, >)) { packetsize = send_rtcp_sr(); /* 28 is UDP encapsulation */ set_next_report_time(packetsize + 28, 0); }}voidconnection_init(int port){ Source_Table = g_hash_table_new(ssrc_hash, ssrc_equal); Dest_Hostname = NULL; Rtp_Packets_Sent = 0; Rtp_Octets_Sent = 0; We_Called = 0; open_sockets(port);}voidconnection_call(gchar *hostname){ struct in_addr call_host; /* host info of computer to call */ if (find_host(hostname, &call_host)) { Send_Addr.sin_family = AF_INET; Send_Addr.sin_port = g_htons(Remote_Port); g_memmove(&Send_Addr.sin_addr.s_addr, &call_host, sizeof (struct in_addr)); My_Seq = random32(1); My_Ssrc = random32(2); My_Timestamp = random32(3); Dest_Hostname = g_strdup(hostname); if (get_sound_duplex() == HALF_DUPLEX) { set_status(STAT_TALK); } else { set_status(STAT_TALK_FD); } set_next_report_time(0, 1); We_Called = 1; } else { /* FIX: Raise error somehow */ }}gbooleanconnection_connected(void){ gboolean connected; connected = (Dest_Hostname != NULL) ? TRUE : FALSE; return connected;}gchar *connection_hostname(void){ if (connection_connected()) { return g_strdup(Dest_Hostname); } else { return NULL; }}rtp_source *find_member(guint32 src) { rtp_source *s; G_LOCK(Source_Table); s = (rtp_source *) g_hash_table_lookup(Source_Table, &src); G_UNLOCK(Source_Table); return s;}static rtp_source *add_member(guint32 src, guint16 seq, struct in_addr *addr){ const int MIN_SEQUENTIAL = 2; int i; guint32 *srcptr; rtp_source *s; struct hostent *h; s = g_new(rtp_source, 1); init_seq(s, seq); s -> probation = MIN_SEQUENTIAL; s -> ssrc = src; for (i = 0; i < RTCP_SDES_MAX; i++) { s -> sdes_len[i] = 0; } g_memmove(&s -> address, addr, sizeof (struct in_addr)); h = gethostbyaddr((char *) addr, sizeof (struct in_addr), AF_INET); if (h != NULL) { s -> hostname = g_strdup(h -> h_name); } else { s -> hostname = g_strdup(inet_ntoa(*addr)); } g_log("GPHONE", G_LOG_LEVEL_INFO, "New source: %4.4x %s", src, s -> hostname); G_LOCK(Source_Table); srcptr = g_new(guint32, 1); *srcptr = src; g_hash_table_insert(Source_Table, srcptr, s); if (g_hash_table_size(Source_Table) == 1) { /* added our first host */ if (We_Called) { if (memcmp(addr, &Send_Addr.sin_addr.s_addr, sizeof (struct in_addr))) { /* We got a packet from someone we didn't call */ g_warning("We didn't call THIS guy"); } } else { int seed = 3; Send_Addr.sin_family = AF_INET; g_memmove(&Send_Addr.sin_addr.s_addr, addr, sizeof(struct in_addr)); My_Seq = random32(1); My_Timestamp = random32(2); while ((My_Ssrc = random32(seed)) == src) { /* try to avoid collision */ seed++; } Dest_Hostname = g_strdup(s -> hostname); if (get_sound_duplex() == HALF_DUPLEX) { set_status(STAT_LISTEN); } else { set_status(STAT_TALK_FD); } set_next_report_time(0, 1); } } G_UNLOCK(Source_Table); return s;}voidmember_sdes(rtp_source *s, guint8 type, char *data, guint8 length){ if (s == NULL) { g_warning("SDES from unknown source."); return; } g_assert(type < RTCP_SDES_MAX); if (s -> sdes_len[type] != 0) { g_free(s -> sdes_data[type]); } s -> sdes_len[type] = length; s -> sdes_data[type] = g_malloc(length); g_memmove(s -> sdes_data[type], data, length); { int i; gchar *data; data = g_malloc(length + 1); for (i = 0; i < length; i++) { data[i] = s -> sdes_data[type][i]; } data[length] = '\0'; g_log("GPHONE", G_LOG_LEVEL_DEBUG, " type: %d length: %d %s", type, length, data); g_free(data); }}static gbooleancheck_from(struct sockaddr_in *fromaddr, rtp_source *s){ return (memcmp(&s -> address, &fromaddr -> sin_addr, sizeof (struct in_addr)) == 0) ? TRUE : FALSE;}voidrtp_send(gchar *buf, int nbytes, rtp_payload_t pt, guint32 nsamp){ Rtp_Packet packet; packet = rtp_packet_new_allocate(nbytes, 0, 0); rtp_packet_set_csrc_count(packet, 0); rtp_packet_set_extension(packet, 0); rtp_packet_set_padding(packet, 0); rtp_packet_set_version(packet, RTP_VERSION); rtp_packet_set_payload_type(packet, pt); rtp_packet_set_marker(packet, 0); rtp_packet_set_ssrc(packet, My_Ssrc); rtp_packet_set_seq(packet, My_Seq); rtp_packet_set_timestamp(packet, My_Timestamp); ++My_Seq; My_Timestamp += nsamp; g_memmove(rtp_packet_get_payload(packet), buf, nbytes); Send_Addr.sin_port = g_htons(Remote_Port); rtp_packet_send(packet, Send_Sock, &Send_Addr); ++Rtp_Packets_Sent; Rtp_Octets_Sent += rtp_packet_get_packet_len(packet); rtp_packet_free(packet);}static voidparse_rtp_packet(Rtp_Packet packet, struct sockaddr_in *fromaddr){ rtp_source *source; if (rtp_packet_get_version(packet) != RTP_VERSION) { g_warning("RTP packet version != %d", RTP_VERSION); } source = find_member(rtp_packet_get_ssrc(packet)); if (source == NULL) { source = add_member(rtp_packet_get_ssrc(packet), rtp_packet_get_seq(packet), &fromaddr -> sin_addr); } if (check_from(fromaddr, source)) { if (update_seq(source, rtp_packet_get_seq(packet))) { switch(rtp_packet_get_payload_type(packet)) { case PAYLOAD_GSM: /* really should do something with sequence/timestamp here */ play_gsm_data(rtp_packet_get_payload(packet), rtp_packet_get_payload_len(packet)); break; default: g_warning("Unsupported RTP payload type %d", rtp_packet_get_payload_type(packet)); break; } } } else { /* source doesn't match */ g_warning("Got RTP packet from new host %s, ssrc %x\n(old host %s, ssrc %x)", inet_ntoa(fromaddr -> sin_addr), rtp_packet_get_ssrc(packet), inet_ntoa(source -> address), source -> ssrc); }}static voidparse_rtcp_app_packet(Rtcp_Packet packet, struct sockaddr_in *fromaddr){ int match; gchar *name; gpointer data; Request req; name = rtcp_app_packet_get_name(packet); data = rtcp_app_packet_get_data(packet); match = (memcmp(name, App_Name, sizeof App_Name) == 0); if (match) { guint32 type = g_ntohl(*((guint32 *) data)); switch (type) { case GPHONE_APP_SWITCH: req = g_malloc(sizeof *req); req->type = REQUEST_SWITCH; req->data_len = 0; req->data = NULL; listen_add_request(req); g_log("GPHONE", G_LOG_LEVEL_DEBUG, " switch"); break; default: g_warning("Unrecognized gphn APP packet type %d\n", type); break; } } else { int i; gchar bad_name[4 + 1]; for (i = 0; i < 4; i++) { bad_name[i] = name[i]; } bad_name[4] = '\0'; g_warning("APP packet with unrecognized name '%s'", bad_name); }}static voidparse_rtcp_packet(Rtcp_Packet packet, struct sockaddr_in *fromaddr){ g_log("GPHONE", G_LOG_LEVEL_DEBUG, "read RTCP packet of length %d", rtcp_packet_get_length(packet)); switch (rtcp_packet_get_packet_type(packet)) { case RTCP_SR: g_log("GPHONE", G_LOG_LEVEL_DEBUG, " SR"); break; case RTCP_RR: g_log("GPHONE", G_LOG_LEVEL_DEBUG, " RR"); break; case RTCP_SDES: g_log("GPHONE", G_LOG_LEVEL_DEBUG, " SDES, count = %d", rtcp_packet_get_count(packet)); rtcp_read_sdes(packet, find_member, member_sdes); break; case RTCP_BYE: g_log("GPHONE", G_LOG_LEVEL_DEBUG, " BYE"); break; case RTCP_APP: g_log("GPHONE", G_LOG_LEVEL_DEBUG, " APP"); parse_rtcp_app_packet(packet, fromaddr); break; default: g_assert_not_reached(); break; }}static voidparse_rtcp_compound(Rtcp_Compound compound, struct sockaddr_in *fromaddr){ rtcp_compound_foreach(compound, (Rtcp_Foreach_Func) parse_rtcp_packet, fromaddr);}intconnection_listen(float timeout){ int fdmax; int rtp_read = 0; fd_set fds; struct timeval tv; struct sockaddr_in fromaddr; fdmax = MAX(Data_Sock, Control_Sock) + 1; FD_ZERO(&fds); FD_SET(Data_Sock, &fds); FD_SET(Control_Sock, &fds); tv.tv_sec = (time_t) floor(timeout); tv.tv_usec = (time_t) ((timeout - floor(timeout)) * 1.0e6); if (select(fdmax, &fds, NULL, NULL, &tv) < 0) { gphone_perror_exit("*** connection_listen : select", 1); } if (FD_ISSET(Data_Sock, &fds)) { /* read an RTP packet */ Rtp_Packet packet; packet = rtp_packet_read(Data_Sock, &fromaddr); parse_rtp_packet(packet, &fromaddr); rtp_packet_free(packet); rtp_read = 1; } if (FD_ISSET(Control_Sock, &fds)) { /* read and handle an RTCP packet */ Rtcp_Compound compound; compound = rtcp_compound_read(Control_Sock, &fromaddr); parse_rtcp_compound(compound, &fromaddr); rtcp_compound_free(compound); } return rtp_read;}/* * Local variables: * compile-command: "make -k gphone" * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -