/* * libiax: An implementation of Inter-Asterisk eXchange * * Copyright (C) 2001, Linux Support Services, Inc. * * Mark Spencer * Frik Strecker * * This program is free software, distributed under the terms of * the GNU Lesser (Library) General Public License */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(WIN32) || defined(_WIN32_WCE) #undef __STRICT_ANSI__ //for strdup with ms #if defined(_WIN32_WCE) #define strdup _strdup #else #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #define snprintf _snprintf #if defined(_MSC_VER) #define close closesocket #if !defined(_WIN32_WCE) #define inline __inline #endif #endif #ifdef __cplusplus extern "C" { #endif void gettimeofday(struct timeval *tv, void /*struct timezone*/ *tz); #ifdef __cplusplus } #endif #else #include #include #include #include #include #ifdef __GNUC__ #ifndef __USE_SVID #define __USE_SVID #endif #endif #include #include #include #include #include #include #include #include #include #include #ifndef MACOSX #include #ifndef SOLARIS #include #endif #endif #endif #include "jitterbuf.h" #include "iax-client.h" #include "md5.h" /* Define socket options for IAX2 sockets, based on platform * availability of flags */ #if defined(WIN32) || defined(_WIN32_WCE) #define IAX_SOCKOPTS 0 #else #ifdef MACOSX #define IAX_SOCKOPTS MSG_DONTWAIT #else #ifdef SOLARIS #define IAX_SOCKOPTS MSG_DONTWAIT #else /* Linux and others */ #define IAX_SOCKOPTS MSG_DONTWAIT | MSG_NOSIGNAL #endif #endif #endif #ifdef SNOM_HACK /* The snom phone seems to improperly execute memset in some cases */ #include "../../snom_phonecore2/include/snom_memset.h" #endif /* Voice TS Prediction causes libiax2 to clean up the timestamps on * outgoing frames. It works best with either continuous voice, or * callers who call iax_send_cng to indicate DTX for silence */ #define USE_VOICE_TS_PREDICTION #define MIN_RETRY_TIME 10 #define MAX_RETRY_TIME 4000 #define MEMORY_SIZE 1000 #define TRANSFER_NONE 0 #define TRANSFER_BEGIN 1 #define TRANSFER_READY 2 #define TRANSFER_REL 3 /* Video frames bypass jitterbuffer */ static int video_bypass_jitterbuffer = 0; /* UDP Socket (file descriptor) */ static int netfd = -1; /* Max timeouts */ static const int maxretries = 10; /* configurable jitterbuffer options */ static long jb_target_extra = -1; /* external global networking replacements */ static iax_sendto_t iax_sendto = (iax_sendto_t) sendto; static iax_recvfrom_t iax_recvfrom = (iax_recvfrom_t) recvfrom; /* ping interval (seconds) */ static int ping_time = 10; static void send_ping(void *session); struct iax_session { /** Private data */ void *pvt; /** session-local Sendto function */ iax_sendto_t sendto; /** Is voice quelched (e.g. hold) */ int quelch; /** Codec Pref Order */ char codec_order[32]; /** Codec Pref Order Index*/ int codec_order_len; /** Last received voice format */ int voiceformat; /** Last transmitted voice format */ int svoiceformat; /** Last received video format */ int videoformat; /** Last transmitted video format */ int svideoformat; /** Per session capability */ int capability; /** Last received timestamp */ unsigned int last_ts; /** Last transmitted timestamp */ unsigned int lastsent; #ifdef USE_VOICE_TS_PREDICTION /** Next predicted voice ts */ unsigned int nextpred; /** True if the last voice we transmitted was not silence/CNG */ int notsilenttx; #endif /** Our last measured ping time */ unsigned int pingtime; /** Address of peer */ struct sockaddr_in peeraddr; /** Our call number */ int callno; /** Peer's call number */ int peercallno; /** Our next outgoing sequence number */ unsigned char oseqno; /** Next sequence number they have not yet acknowledged */ unsigned char rseqno; /** Our last received incoming sequence number */ unsigned char iseqno; /** Last acknowledged sequence number */ unsigned char aseqno; /** Last sequence number we VNAKd */ unsigned char lastvnak; /** Time value that we base our transmission on */ struct timeval offset; /** Time value we base our delivery on */ struct timeval rxcore; /** Current link state */ int state; /** Expected Username */ char username[MAXSTRLEN]; /** Expected Secret */ char secret[MAXSTRLEN]; /** Refresh if applicable */ int refresh; /** ping scheduler id */ int pingid; /** Transfer stuff */ struct sockaddr_in transfer; int transferring; int transfercallno; int transferid; int transferpeer; /** for attended transfer */ int transfer_moh; /** for music on hold while performing attended transfer */ jitterbuf *jb; struct iax_netstat remote_netstats; /** For linking if there are multiple connections */ struct iax_session *next; }; char iax_errstr[256]; #define IAXERROR snprintf(iax_errstr, sizeof(iax_errstr), #ifdef DEBUG_SUPPORT #ifdef DEBUG_DEFAULT static int debug = 1; #else static int debug = 0; #endif void iax_enable_debug(void) { debug = 1; } void iax_disable_debug(void) { debug = 0; } /* This is a little strange, but to debug you call DEBU(G "Hello World!\n"); */ #if defined(WIN32) || defined(_WIN32_WCE) #define G __FILE__, __LINE__, #else #define G __FILE__, __LINE__, __PRETTY_FUNCTION__, #endif #define DEBU __debug #if defined(WIN32) || defined(_WIN32_WCE) static int __debug(char *file, int lineno, char *fmt, ...) { va_list args; va_start(args, fmt); if (debug) { fprintf(stderr, "%s line %d: ", file, lineno); vfprintf(stderr, fmt, args); } va_end(args); return 0; } #else static int __debug(char *file, int lineno, char *func, char *fmt, ...) { va_list args; va_start(args, fmt); if (debug) { fprintf(stderr, "%s line %d in %s: ", file, lineno, func); vfprintf(stderr, fmt, args); } va_end(args); return 0; } #endif #else /* No debug support */ #if defined(WIN32) || defined(_WIN32_WCE) #define DEBU #else #define DEBU(fmt...) \ do {} while(0) #endif #define G #endif typedef void (*sched_func)(void *); struct iax_sched { /** These are scheduled things to be delivered */ struct timeval when; /** If event is non-NULL then we're delivering an event */ struct iax_event *event; /** If frame is non-NULL then we're transmitting a frame */ struct iax_frame *frame; /** If func is non-NULL then we should call it */ sched_func func; /** and pass it this argument */ void *arg; /** Easy linking */ struct iax_sched *next; }; static struct iax_sched *schedq = NULL; static struct iax_session *sessions = NULL; static int callnums = 1; static int transfer_id = 1; /** for attended transfer */ void iax_set_private(struct iax_session *s, void *ptr) { s->pvt = ptr; } void *iax_get_private(struct iax_session *s) { return s->pvt; } void iax_set_sendto(struct iax_session *s, iax_sendto_t ptr) { s->sendto = ptr; } unsigned int iax_session_get_capability(struct iax_session *s) { return s->capability; } static int inaddrcmp(struct sockaddr_in *sin1, struct sockaddr_in *sin2) { return (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) || (sin1->sin_port != sin2->sin_port); } static int iax_sched_add(struct iax_event *event, struct iax_frame *frame, sched_func func, void *arg, int ms) { /* Schedule event to be delivered to the client in ms milliseconds from now, or a reliable frame to be retransmitted */ struct iax_sched *sched, *cur, *prev = NULL; if (!event && !frame && !func) { DEBU(G "No event, no frame, no func? what are we scheduling?\n"); return -1; } //fprintf(stderr, "scheduling event %d ms from now\n", ms); sched = (struct iax_sched*)malloc(sizeof(struct iax_sched)); if (sched) { memset(sched, 0, sizeof(struct iax_sched)); gettimeofday(&sched->when, NULL); sched->when.tv_sec += (ms / 1000); ms = ms % 1000; sched->when.tv_usec += (ms * 1000); if (sched->when.tv_usec > 1000000) { sched->when.tv_usec -= 1000000; sched->when.tv_sec++; } sched->event = event; sched->frame = frame; sched->func = func; sched->arg = arg; /* Put it in the list, in order */ cur = schedq; while(cur && ((cur->when.tv_sec < sched->when.tv_sec) || ((cur->when.tv_usec <= sched->when.tv_usec) && (cur->when.tv_sec == sched->when.tv_sec)))) { prev = cur; cur = cur->next; } sched->next = cur; if (prev) { prev->next = sched; } else { schedq = sched; } return 0; } else { DEBU(G "Out of memory!\n"); return -1; } } static int iax_sched_del(struct iax_event *event, struct iax_frame *frame, sched_func func, void *arg, int all) { struct iax_sched *cur, *tmp, *prev = NULL; cur = schedq; while (cur) { if (cur->event == event && cur->frame == frame && cur->func == func && cur->arg == arg) { if (prev) prev->next = cur->next; else schedq = cur->next; tmp = cur; cur = cur->next; free(tmp); if (!all) return -1; } else { prev = cur; cur = cur->next; } } return 0; } int iax_time_to_next_event(void) { struct timeval tv; struct iax_sched *cur = schedq; int ms, min = 999999999; /* If there are no pending events, we don't need to timeout */ if (!cur) return -1; gettimeofday(&tv, NULL); while(cur) { ms = (cur->when.tv_sec - tv.tv_sec) * 1000 + (cur->when.tv_usec - tv.tv_usec) / 1000; if (ms < min) min = ms; cur = cur->next; } if (min < 0) min = 0; return min; } struct iax_session *iax_session_new(void) { struct iax_session *s; s = (struct iax_session *)malloc(sizeof(struct iax_session)); if (s) { jb_conf jbconf; memset(s, 0, sizeof(struct iax_session)); /* Initialize important fields */ s->voiceformat = -1; s->svoiceformat = -1; s->videoformat = -1; /* Default pingtime to 100 ms -- should cover most decent net connections */ s->pingtime = 100; /* XXX Not quite right -- make sure it's not in use, but that won't matter unless you've had at least 65k calls. XXX */ s->callno = callnums++; if (callnums > 32767) callnums = 1; s->peercallno = 0; s->lastvnak = -1; s->transferpeer = 0; /* for attended transfer */ s->next = sessions; s->sendto = iax_sendto; s->pingid = -1; s->jb = jb_new(); if ( !s->jb ) { free(s); return 0; } jbconf.max_jitterbuf = 0; jbconf.resync_threshold = 1000; jbconf.max_contig_interp = 0; jbconf.target_extra = jb_target_extra; jb_setconf(s->jb, &jbconf); sessions = s; } return s; } static int iax_session_valid(struct iax_session *session) { /* Return -1 on a valid iax session pointer, 0 on a failure */ struct iax_session *cur = sessions; while(cur) { if (session == cur) return -1; cur = cur->next; } return 0; } int iax_get_netstats(struct iax_session *session, int *rtt, struct iax_netstat *local, struct iax_netstat *remote) { jb_info stats; if(!iax_session_valid(session)) return -1; *rtt = session->pingtime; *remote = session->remote_netstats; jb_getinfo(session->jb, &stats); local->jitter = stats.jitter; /* XXX: should be short-term loss pct.. */ if(stats.frames_in == 0) stats.frames_in = 1; local->losspct = stats.losspct/1000; local->losscnt = stats.frames_lost; local->packets = stats.frames_in; local->delay = stats.current - stats.min; local->dropped = stats.frames_dropped; local->ooo = stats.frames_ooo; return 0; } #ifdef USE_VOICE_TS_PREDICTION static void add_ms(struct timeval *tv, int ms) { tv->tv_usec += ms * 1000; if(tv->tv_usec > 1000000) { tv->tv_usec -= 1000000; tv->tv_sec++; } if(tv->tv_usec < 0) { tv->tv_usec += 1000000; tv->tv_sec--; } } #endif static int calc_timestamp(struct iax_session *session, unsigned int ts, struct ast_frame *f) { int ms; struct timeval tv; int voice = 0; int video = 0; int genuine = 0; if ( f && f->frametype == AST_FRAME_VOICE ) { voice = 1; } else if ( f && f->frametype == AST_FRAME_VIDEO ) { video = 1; } else if (!f || f->frametype == AST_FRAME_IAX) { genuine = 1; } /* If this is the first packet we're sending, get our offset now. */ if (!session->offset.tv_sec && !session->offset.tv_usec) gettimeofday(&session->offset, NULL); /* If the timestamp is specified, just use their specified timestamp no matter what. Usually this is done for special cases. */ if (ts) { if ( f ) session->lastsent = ts; return ts; } /* Otherwise calculate the timestamp from the current time */ gettimeofday(&tv, NULL); /* Calculate the number of milliseconds since we sent the first packet */ ms = (tv.tv_sec - session->offset.tv_sec) * 1000 + (tv.tv_usec - session->offset.tv_usec) / 1000; if (ms < 0) ms = 0; if(voice) { #ifdef USE_VOICE_TS_PREDICTION /* If we haven't most recently sent silence, and we're * close in time, use predicted time */ if(session->notsilenttx && abs(ms - session->nextpred) <= 240) { /* Adjust our txcore, keeping voice and non-voice * synchronized */ add_ms(&session->offset, (int)(ms - session->nextpred)/10); if(!session->nextpred) session->nextpred = ms; ms = session->nextpred; } else { /* in this case, just use the actual time, since * we're either way off (shouldn't happen), or we're * ending a silent period -- and seed the next predicted * time. Also, round ms to the next multiple of * frame size (so our silent periods are multiples * of frame size too) */ int diff = ms % (f->samples / 8); if(diff) ms += f->samples/8 - diff; session->nextpred = ms; } session->notsilenttx = 1; #else if(ms <= session->lastsent) ms = session->lastsent + 3; #endif } else if (video) { if ((unsigned int)ms <= session->lastsent) ms = session->lastsent + 3; } else { /* On a dataframe, use last value + 3 (to accomodate jitter buffer shrinking) if appropriate unless it's a genuine frame */ if (genuine) { if ((unsigned int)ms <= session->lastsent) ms = session->lastsent + 3; } else if (abs(ms - session->lastsent) <= 240) { ms = session->lastsent + 3; } } /* Record the last sent packet for future reference */ /* unless an AST_FRAME_IAX */ if (!genuine) session->lastsent = ms; #ifdef USE_VOICE_TS_PREDICTION /* set next predicted ts based on 8khz samples */ if(voice) session->nextpred = session->nextpred + f->samples / 8; #endif return ms; } static unsigned char get_n_bits_at(unsigned char *data, int n, int bit) { int byte = bit / 8; /* byte containing first bit */ int rem = 8 - (bit % 8); /* remaining bits in first byte */ unsigned char ret = 0; if (n <= 0 || n > 8) return 0; if (rem < n) { ret = (data[byte] << (n - rem)); ret |= (data[byte + 1] >> (8 - n + rem)); } else { ret = (data[byte] >> (rem - n)); } return (ret & (0xff >> (8 - n))); } static int speex_get_wb_sz_at(unsigned char *data, int len, int bit) { static int SpeexWBSubModeSz[] = { 0, 36, 112, 192, 352, 0, 0, 0 }; int off = bit; unsigned char c; /* skip up to two wideband frames */ if (((len * 8 - off) >= 5) && get_n_bits_at(data, 1, off)) { c = get_n_bits_at(data, 3, off + 1); off += SpeexWBSubModeSz[c]; if (((len * 8 - off) >= 5) && get_n_bits_at(data, 1, off)) { c = get_n_bits_at(data, 3, off + 1); off += SpeexWBSubModeSz[c]; if (((len * 8 - off) >= 5) && get_n_bits_at(data, 1, off)) { /* too many in a row */ DEBU(G "\tCORRUPT too many wideband streams in a row\n"); return -1; } } } return off - bit; } static int speex_get_samples(unsigned char *data, int len) { static int SpeexSubModeSz[] = { 0, 43, 119, 160, 220, 300, 364, 492, 79, 0, 0, 0, 0, 0, 0, 0 }; static int SpeexInBandSz[] = { 1, 1, 4, 4, 4, 4, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 }; int bit = 0; int cnt = 0; int off = 0; unsigned char c; DEBU(G "speex_get_samples(%d)\n", len); while ((len * 8 - bit) >= 5) { /* skip wideband frames */ off = speex_get_wb_sz_at(data, len, bit); if (off < 0) { DEBU(G "\tERROR reading wideband frames\n"); break; } bit += off; if ((len * 8 - bit) < 5) { DEBU(G "\tERROR not enough bits left after wb\n"); break; } /* get control bits */ c = get_n_bits_at(data, 5, bit); DEBU(G "\tCONTROL: %d at %d\n", c, bit); bit += 5; if (c == 15) { DEBU(G "\tTERMINATOR\n"); break; } else if (c == 14) { /* in-band signal; next 4 bits contain signal id */ c = get_n_bits_at(data, 4, bit); bit += 4; DEBU(G "\tIN-BAND %d bits\n", SpeexInBandSz[c]); bit += SpeexInBandSz[c]; } else if (c == 13) { /* user in-band; next 5 bits contain msg len */ c = get_n_bits_at(data, 5, bit); bit += 5; DEBU(G "\tUSER-BAND %d bytes\n", c); bit += c * 8; } else if (c > 8) { DEBU(G "\tUNKNOWN sub-mode %d\n", c); break; } else { /* skip number bits for submode (less the 5 control bits) */ DEBU(G "\tSUBMODE %d %d bits\n", c, SpeexSubModeSz[c]); bit += SpeexSubModeSz[c] - 5; cnt += 160; /* new frame */ } } DEBU(G "\tSAMPLES: %d\n", cnt); return cnt; } static inline int get_interp_len(int format) { return (format == AST_FORMAT_ILBC) ? 30 : 20; } static int get_sample_cnt(struct iax_event *e) { int cnt = 0; /* * In the case of zero length frames, do not return a cnt of 0 */ if ( e->datalen == 0 ) { return get_interp_len( e->subclass ) * 8; } switch (e->subclass) { case AST_FORMAT_SPEEX: cnt = speex_get_samples(e->data, e->datalen); break; case AST_FORMAT_G723_1: cnt = 240; /* FIXME Not always the case */ break; case AST_FORMAT_ILBC: cnt = 240 * (e->datalen / 50); break; case AST_FORMAT_GSM: cnt = 160 * (e->datalen / 33); break; case AST_FORMAT_G729A: cnt = 160 * (e->datalen / 20); break; case AST_FORMAT_SLINEAR: cnt = e->datalen / 2; break; case AST_FORMAT_LPC10: cnt = 22 * 8 + (((char *)(e->data))[7] & 0x1) * 8; break; case AST_FORMAT_ULAW: case AST_FORMAT_ALAW: cnt = e->datalen; break; case AST_FORMAT_ADPCM: case AST_FORMAT_G726: cnt = e->datalen * 2; break; default: return 0; } return cnt; } static int iax_xmit_frame(struct iax_frame *f) { int res; #ifdef DEBUG_SUPPORT struct ast_iax2_full_hdr *h = (struct ast_iax2_full_hdr *)(f->data); if (ntohs(h->scallno) & IAX_FLAG_FULL) iax_showframe(f, NULL, 0, f->transfer ? &(f->session->transfer) : &(f->session->peeraddr), f->datalen - sizeof(struct ast_iax2_full_hdr)); #endif /* Send the frame raw */ res = f->session->sendto(netfd, (const char *) f->data, f->datalen, IAX_SOCKOPTS, f->transfer ? (struct sockaddr *)&(f->session->transfer) : (struct sockaddr *)&(f->session->peeraddr), sizeof(f->session->peeraddr)); return res; } static int iax_reliable_xmit(struct iax_frame *f) { struct iax_frame *fc; struct ast_iax2_full_hdr *fh; fh = (struct ast_iax2_full_hdr *) f->data; if (!fh->type) { return -2; } fc = (struct iax_frame *)malloc(sizeof(struct iax_frame)); if (fc) { /* Make a copy of the frame */ memcpy(fc, f, sizeof(struct iax_frame)); /* And a copy of the data if applicable */ if (!fc->data || !fc->datalen) { IAXERROR "No frame data?"); DEBU(G "No frame data?\n"); free(fc); return -1; } else { fc->data = (char *)malloc(fc->datalen); if (!fc->data) { DEBU(G "Out of memory\n"); IAXERROR "Out of memory\n"); free(fc); return -1; } memcpy(fc->data, f->data, f->datalen); iax_sched_add(NULL, fc, NULL, NULL, fc->retrytime); return iax_xmit_frame(fc); } } else return -1; } void iax_set_networking(iax_sendto_t st, iax_recvfrom_t rf) { iax_sendto = st; iax_recvfrom = rf; } void iax_set_jb_target_extra( long value ) { /* store in jb_target_extra, a static global */ jb_target_extra = value ; } int iax_init(int preferredportno) { int portno = preferredportno; if (iax_recvfrom == (iax_recvfrom_t)recvfrom) { struct sockaddr_in sin = {}; socklen_t sinlen; int flags; int bufsize = 256 * 1024; if (netfd > -1) { /* Okay, just don't do anything */ DEBU(G "Already initialized."); return 0; } netfd = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (netfd < 0) { DEBU(G "Unable to allocate UDP socket\n"); IAXERROR "Unable to allocate UDP socket\n"); return -1; } if (preferredportno == 0) preferredportno = IAX_DEFAULT_PORTNO; if (preferredportno < 0) preferredportno = 0; sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; sin.sin_port = htons((short)preferredportno); if (bind(netfd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { #if defined(WIN32) || defined(_WIN32_WCE) if (WSAGetLastError() == WSAEADDRINUSE) #else if (errno == EADDRINUSE) #endif { /*the port is already in use, so bind to a free port chosen by the IP stack*/ DEBU(G "Unable to bind to preferred port - port is in use. Trying to bind to a free one"); sin.sin_port = htons((short)0); if (bind(netfd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { IAXERROR "Unable to bind UDP socket\n"); return -1; } } else { IAXERROR "Unable to bind UDP socket\n"); return -1; } } sinlen = sizeof(sin); if (getsockname(netfd, (struct sockaddr *) &sin, &sinlen) < 0) { close(netfd); netfd = -1; DEBU(G "Unable to figure out what I'm bound to."); IAXERROR "Unable to determine bound port number."); return -1; } #if defined(WIN32) || defined(_WIN32_WCE) flags = 1; if (ioctlsocket(netfd,FIONBIO,(unsigned long *) &flags)) { closesocket(netfd); netfd = -1; DEBU(G "Unable to set non-blocking mode."); IAXERROR "Unable to set non-blocking mode."); return -1; } #else if ((flags = fcntl(netfd, F_GETFL)) < 0) { close(netfd); netfd = -1; DEBU(G "Unable to retrieve socket flags."); IAXERROR "Unable to retrieve socket flags."); return -1; } if (fcntl(netfd, F_SETFL, flags | O_NONBLOCK) < 0) { close(netfd); netfd = -1; DEBU(G "Unable to set non-blocking mode."); IAXERROR "Unable to set non-blocking mode."); return -1; } #endif /* Mihai: increase UDP socket buffers to avoid packet loss. */ if (setsockopt(netfd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) { DEBU(G "Unable to set buffer size."); IAXERROR "Unable to set buffer size."); } portno = ntohs(sin.sin_port); DEBU(G "Started on port %d\n", portno); } srand((unsigned int)time(0)); callnums = rand() % 32767 + 1; transfer_id = rand() % 32767 + 1; return portno; } static void destroy_session(struct iax_session *session); static void convert_reply(char *out, unsigned char *in) { int x; for (x=0;x<16;x++) out += sprintf(out, "%2.2x", (int)in[x]); } static unsigned char compress_subclass(int subclass) { int x; int power=-1; /* If it's 128 or smaller, just return it */ if (subclass < IAX_FLAG_SC_LOG) return subclass; /* Otherwise find its power */ for (x = 0; x < IAX_MAX_SHIFT; x++) { if (subclass & (1 << x)) { if (power > -1) { DEBU(G "Can't compress subclass %d\n", subclass); return 0; } else power = x; } } return power | IAX_FLAG_SC_LOG; } static int iax_send(struct iax_session *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final, int fullframe) { /* Queue a packet for delivery on a given private structure. Use "ts" for timestamp, or calculate if ts is 0. Send immediately without retransmission or delayed, with retransmission */ struct ast_iax2_full_hdr *fh; struct ast_iax2_mini_hdr *mh; struct ast_iax2_video_hdr *vh; //unsigned char buf[5120]; //fd: changed max packet size[5120]; unsigned char buf[32 * 1024]; //Mihai: let's see if this is where it crashes struct iax_frame *fr; int res; int sendmini=0; unsigned int lastsent; unsigned int fts; if (!pvt) { IAXERROR "No private structure for packet?\n"); return -1; } /* this must come before the next call to calc_timestamp() since calc_timestamp() will change lastsent to the returned value */ lastsent = pvt->lastsent; /* Calculate actual timestamp */ fts = calc_timestamp(pvt, ts, f); if (((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)) /* High two bits are the same on timestamp, or sending on a trunk */ && (f->frametype == AST_FRAME_VOICE) /* is a voice frame */ && (f->subclass == pvt->svoiceformat) /* is the same type */ ) { /* Force immediate rather than delayed transmission */ now = 1; /* Mark that mini-style frame is appropriate */ sendmini = 1; } /* Bitmask taken from chan_iax2.c... I must ask Mark Spencer for this? I think not... */ if ( f->frametype == AST_FRAME_VIDEO ) { if (((fts & 0xFFFF8000L) == (lastsent & 0xFFFF8000L)) /* High two bits are the same on timestamp, or sending on a trunk */ && ((f->subclass & ~0x01) == pvt->svideoformat) /* is the same type */ ) { /* Force immediate rather than delayed transmission */ now = 1; /* Mark that mini-style frame is appropriate */ sendmini = 1; } else { /* we want to send a fullframe and be able to retransmit it */ now = 0; sendmini = 0; } } /* if requested, force a full frame */ if ( fullframe ) { now = 0; sendmini = 0; } /* Allocate an iax_frame */ if (now) { fr = (struct iax_frame *) buf; } else { fr = iax_frame_new(DIRECTION_OUTGRESS, f->datalen); if ( fr == NULL ) { IAXERROR "Out of memory\n"); return -1; } } /* Copy our prospective frame into our immediate or retransmitted wrapper */ iax_frame_wrap(fr, f); fr->ts = fts; if (!fr->ts) { IAXERROR "timestamp is 0?\n"); if (!now) iax_frame_free(fr); return -1; } fr->callno = pvt->callno; fr->transfer = transfer; fr->final = final; fr->session = pvt; if (!sendmini) { /* We need a full frame */ if (seqno > -1) fr->oseqno = seqno; else fr->oseqno = pvt->oseqno++; fr->iseqno = pvt->iseqno; fh = (struct ast_iax2_full_hdr *)(((char *)fr->af.data) - sizeof(struct ast_iax2_full_hdr)); fh->scallno = htons(fr->callno | IAX_FLAG_FULL); fh->ts = htonl(fr->ts); fh->oseqno = fr->oseqno; if (transfer) { fh->iseqno = 0; } else fh->iseqno = fr->iseqno; /* Keep track of the last thing we've acknowledged */ pvt->aseqno = fr->iseqno; fh->type = fr->af.frametype & 0xFF; if (f->frametype == AST_FRAME_VIDEO) fh->csub = compress_subclass(fr->af.subclass & ~0x1) | ((fr->af.subclass & 0x1) << 6); else fh->csub = compress_subclass(fr->af.subclass); if (transfer) { fr->dcallno = pvt->transfercallno; } else fr->dcallno = pvt->peercallno; fh->dcallno = htons(fr->dcallno); fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_full_hdr); fr->data = fh; fr->retries = maxretries; /* Retry after 2x the ping time has passed */ fr->retrytime = pvt->pingtime * 2; if (fr->retrytime < MIN_RETRY_TIME) fr->retrytime = MIN_RETRY_TIME; if (fr->retrytime > MAX_RETRY_TIME) fr->retrytime = MAX_RETRY_TIME; /* Acks' don't get retried */ if ((f->frametype == AST_FRAME_IAX) && (f->subclass == IAX_COMMAND_ACK)) fr->retries = -1; if (f->frametype == AST_FRAME_VOICE) { pvt->svoiceformat = f->subclass; } else if (f->frametype == AST_FRAME_VIDEO) { pvt->svideoformat = f->subclass & ~0x1; } if (now) { res = iax_xmit_frame(fr); } else res = iax_reliable_xmit(fr); } else { if (fr->af.frametype == AST_FRAME_VIDEO) { /* Video frame have no sequence number */ fr->oseqno = -1; fr->iseqno = -1; vh = (struct ast_iax2_video_hdr *)(((char* )fr->af.data) - sizeof(struct ast_iax2_video_hdr)); vh->zeros = 0; vh->callno = htons(0x8000 | fr->callno); vh->ts = htons((fr->ts & 0x7FFF) | (fr->af.subclass & 0x1 ? 0x8000 : 0)); fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_video_hdr); fr->data = vh; fr->retries = -1; res = iax_xmit_frame(fr); } else { /* Mini-frames have no sequence number */ fr->oseqno = -1; fr->iseqno = -1; /* Mini frame will do */ mh = (struct ast_iax2_mini_hdr *)(((char *)fr->af.data) - sizeof(struct ast_iax2_mini_hdr)); mh->callno = htons(fr->callno); mh->ts = htons(fr->ts & 0xFFFF); fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_mini_hdr); fr->data = mh; fr->retries = -1; res = iax_xmit_frame(fr); } } if( !now ) iax_frame_free( fr ); return res; } #if 0 static int iax_predestroy(struct iax_session *pvt) { if (!pvt) { return -1; } if (!pvt->alreadygone) { /* No more pings or lagrq's */ if (pvt->pingid > -1) ast_sched_del(sched, pvt->pingid); if (pvt->lagid > -1) ast_sched_del(sched, pvt->lagid); if (pvt->autoid > -1) ast_sched_del(sched, pvt->autoid); if (pvt->initid > -1) ast_sched_del(sched, pvt->initid); pvt->pingid = -1; pvt->lagid = -1; pvt->autoid = -1; pvt->initid = -1; pvt->alreadygone = 1; } return 0; } #endif static int __send_command(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno, int now, int transfer, int final, int fullframe, int samples) { struct ast_frame f; f.frametype = type; f.subclass = command; f.datalen = datalen; f.samples = samples; f.mallocd = 0; f.offset = 0; #ifdef __GNUC__ f.src = (char *) __FUNCTION__; #else f.src = (char *) __FILE__; #endif f.data = data; return iax_send(i, &f, ts, seqno, now, transfer, final, fullframe); } static int send_command(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno) { return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0, 0, 0); } static int send_command_video(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno, int fullframe) { return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0, fullframe, 0); } static int send_command_final(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno) { #if 0 /* It is assumed that the callno has already been locked */ iax_predestroy(i); #endif int r; r = __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1, 0, 0); if (r >= 0) destroy_session(i); return r; } static int send_command_immediate(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno) { return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0, 0, 0); } static int send_command_transfer(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen) { return __send_command(i, type, command, ts, data, datalen, 0, 0, 1, 0, 0, 0); } static int send_command_samples(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno, int samples) { return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0, 0, samples); } int iax_transfer(struct iax_session *session, const char *number) { //static int res; //Return Code struct iax_ie_data ied; //IE Data Structure (Stuff To Send) // Clear The Memory Used For IE Buffer memset(&ied, 0, sizeof(ied)); // Copy The Transfer Destination Into The IE Structure iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, number); // Send The Transfer Command - Asterisk Will Handle The Rest! /*res =*/ send_command(session, AST_FRAME_IAX, IAX_COMMAND_TRANSFER, 0, ied.buf, ied.pos, -1); // Return Success return 0; } static void stop_transfer(struct iax_session *session) { struct iax_sched *sch; sch = schedq; while(sch) { if (sch->frame && (sch->frame->session == session)) sch->frame->retries = -1; sch = sch->next; } } /* stop_transfer */ static void complete_transfer(struct iax_session *session, int peercallno, int xfr2peer, int preserveSeq) { jb_frame frame; session->peercallno = peercallno; /* Change from transfer to session now */ if (xfr2peer) { memcpy(&session->peeraddr, &session->transfer, sizeof(session->peeraddr)); memset(&session->transfer, 0, sizeof(session->transfer)); session->transferring = TRANSFER_NONE; session->transferpeer = 0; session->transfer_moh = 0; /* Force retransmission of a real voice packet, and reset all timing */ session->svoiceformat = -1; session->voiceformat = 0; session->svideoformat = -1; session->videoformat = 0; } memset(&session->rxcore, 0, sizeof(session->rxcore)); memset(&session->offset, 0, sizeof(session->offset)); /* Reset jitterbuffer */ while(jb_getall(session->jb,&frame) == JB_OK) iax_event_free((struct iax_event *)frame.data); jb_reset(session->jb); if (! preserveSeq) { /* Reset sequence numbers */ session->aseqno = 0; session->oseqno = 0; session->iseqno = 0; } session->lastsent = 0; session->last_ts = 0; session->pingtime = 30; /* We have to dump anything we were going to (re)transmit now that we've been transferred since they're all invalid and for the old host. */ stop_transfer(session); } /* complete_transfer */ int iax_setup_transfer(struct iax_session *org_session, struct iax_session *new_session) { int res; struct iax_ie_data ied0; struct iax_ie_data ied1; struct iax_session *s0 = org_session; struct iax_session *s1 = new_session; memset(&ied0, 0, sizeof(ied0)); memset(&ied1, 0, sizeof(ied1)); /* reversed setup */ iax_ie_append_addr(&ied0, IAX_IE_APPARENT_ADDR, &s1->peeraddr); iax_ie_append_short(&ied0, IAX_IE_CALLNO, s1->peercallno); iax_ie_append_int(&ied0, IAX_IE_TRANSFERID, transfer_id); iax_ie_append_addr(&ied1, IAX_IE_APPARENT_ADDR, &s0->peeraddr); iax_ie_append_short(&ied1, IAX_IE_CALLNO, s0->peercallno); iax_ie_append_int(&ied1, IAX_IE_TRANSFERID, transfer_id); s0->transfer = s1->peeraddr; s1->transfer = s0->peeraddr; s0->transferid = transfer_id; s1->transferid = transfer_id; s0->transfercallno = s0->peercallno; s1->transfercallno = s1->peercallno; s0->transferring = TRANSFER_BEGIN; s1->transferring = TRANSFER_BEGIN; s0->transferpeer = s1->callno; s1->transferpeer = s0->callno; transfer_id++; if (transfer_id > 32767) transfer_id = 1; res = send_command(s0, AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied0.buf, ied0.pos, -1); if (res < 0) { return -1; } res = send_command(s1, AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied1.buf, ied1.pos, -1); if (res < 0) { return -1; } return 0; } static int iax_finish_transfer(struct iax_session *s, short new_peer) { int res; struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_short(&ied, IAX_IE_CALLNO, new_peer); res = send_command(s, AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied.buf, ied.pos, -1); complete_transfer(s, new_peer, 0, 1); return res; } static struct iax_session *iax_find_session2(short callno) { struct iax_session *cur = sessions; while(cur) { if (callno == cur->callno && callno != 0) { return cur; } cur = cur->next; } return NULL; } static int iax_handle_txready(struct iax_session *s) { struct iax_session *s0, *s1; short s0_org_peer, s1_org_peer; if (s->transfer_moh) { s->transfer_moh = 0; iax_unquelch(s); } complete_transfer(s, s->peercallno, 0, 1); s->transferring = TRANSFER_REL; s0 = s; s1 = iax_find_session2(s0->transferpeer); if (s1 != NULL && s1->callno == s0->transferpeer && s0->transferring == TRANSFER_REL && s1->transferring == TRANSFER_REL) { s0_org_peer = s0->peercallno; s1_org_peer = s1->peercallno; iax_finish_transfer(s0, s1_org_peer); iax_finish_transfer(s1, s0_org_peer); return 1; } return 0; } static void iax_handle_txreject(struct iax_session *s) { struct iax_session *s0, *s1; s0 = s; s1 = iax_find_session2(s0->transferpeer); if (s1 != NULL && s0->transferpeer == s1->callno && s1->transferring) { if (s1->transfer_moh) { s1->transfer_moh = 0; send_command_immediate(s1, AST_FRAME_IAX, IAX_COMMAND_UNQUELCH, 0, NULL, 0, s1->iseqno); } } if (s0->transfer_moh) { s0->transfer_moh = 0; send_command_immediate(s0, AST_FRAME_IAX, IAX_COMMAND_UNQUELCH, 0, NULL, 0, s0->iseqno); } memset(&s->transfer, 0, sizeof(s->transfer)); s->transferring = TRANSFER_NONE; s->transferpeer = 0; s->transfer_moh = 0; } static void destroy_session(struct iax_session *session) { struct iax_session *cur, *prev=NULL; struct iax_sched *curs, *prevs=NULL, *nexts=NULL; int loop_cnt=0; curs = schedq; while(curs) { nexts = curs->next; if (curs->frame && curs->frame->session == session) { /* Just mark these frames as if they've been sent */ curs->frame->retries = -1; } else if (curs->event && curs->event->session == session) { if (prevs) prevs->next = nexts; else schedq = nexts; if (curs->event) iax_event_free(curs->event); free(curs); } else { prevs = curs; } curs = nexts; loop_cnt++; } cur = sessions; while(cur) { if (cur == session) { jb_frame frame; if (prev) prev->next = session->next; else sessions = session->next; while(jb_getall(session->jb,&frame) == JB_OK) iax_event_free((struct iax_event *)frame.data); jb_destroy(session->jb); free(session); return; } prev = cur; cur = cur->next; } } static int iax_send_lagrp(struct iax_session *session, unsigned int ts); static int iax_send_pong(struct iax_session *session, unsigned int ts); static struct iax_event *handle_event(struct iax_event *event) { /* We have a candidate event to be delievered. Be sure the session still exists. */ if (event) { if ( event->etype == IAX_EVENT_NULL ) return event; if (iax_session_valid(event->session)) { /* Lag requests are never actually sent to the client, but other than that are handled as normal packets */ switch(event->etype) { /* the user on the outside may need to look at the session so we will not free it here anymore we will test for hangup event in iax_event_free and do it there. */ case IAX_EVENT_REJECT: case IAX_EVENT_HANGUP: /* Destroy this session -- it's no longer valid */ destroy_session(event->session); return event; case IAX_EVENT_LAGRQ: event->etype = IAX_EVENT_LAGRP; iax_send_lagrp(event->session, event->ts); iax_event_free(event); break; case IAX_EVENT_PING: event->etype = IAX_EVENT_PONG; iax_send_pong(event->session, event->ts); iax_event_free(event); break; case IAX_EVENT_POKE: event->etype = IAX_EVENT_PONG; iax_send_pong(event->session, event->ts); destroy_session(event->session); iax_event_free(event); break; default: return event; } } else iax_event_free(event); } return NULL; } static int iax2_vnak(struct iax_session *session) { /* send vnak just once for a given sequence number */ if ( (unsigned char)(session->lastvnak - session->iseqno) < 128 ) { return 0; } session->lastvnak = session->iseqno; return send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_VNAK, 0, NULL, 0, session->iseqno); } int iax_send_dtmf(struct iax_session *session, char digit) { return send_command(session, AST_FRAME_DTMF, digit, 0, NULL, 0, -1); } int iax_send_voice(struct iax_session *session, int format, unsigned char *data, int datalen, int samples) { /* Send a (possibly compressed) voice frame */ if (!session->quelch) return send_command_samples(session, AST_FRAME_VOICE, format, 0, data, datalen, -1, samples); return 0; } int iax_send_cng(struct iax_session *session, int level, unsigned char *data, int datalen) { #ifdef USE_VOICE_TS_PREDICTION session->notsilenttx = 0; #endif return send_command(session, AST_FRAME_CNG, level, 0, data, datalen, -1); } int iax_send_image(struct iax_session *session, int format, unsigned char *data, int datalen) { /* Send an image frame */ return send_command(session, AST_FRAME_IMAGE, format, 0, data, datalen, -1); } int iax_send_video(struct iax_session *session, int format, unsigned char *data, int datalen, int fullframe) { if (!session->quelch) { int res = send_command_video(session, AST_FRAME_VIDEO, format, 0, data, datalen, -1, fullframe); return res; } return 0; } int iax_send_video_trunk(struct iax_session *session, int format, char *data, int datalen, int fullframe, int ntrunk) { static int my_lastts = 0; if ( ntrunk == 0 ) my_lastts = calc_timestamp(session, 0, NULL); if ( !session->quelch ) { return send_command_video(session, AST_FRAME_VIDEO, format, my_lastts, (unsigned char *)data, datalen, -1, fullframe); } return 0; } int iax_video_bypass_jitter(struct iax_session *s, int mode) { video_bypass_jitterbuffer = mode; return 0; } int iax_register(struct iax_session *session, const char *server, const char *peer, const char *secret, int refresh) { /* Send a registration request */ char tmp[256]; char *p; int res; int portno = IAX_DEFAULT_PORTNO; struct iax_ie_data ied; struct hostent *hp; tmp[255] = '\0'; strncpy(tmp, server, sizeof(tmp) - 1); p = strchr(tmp, ':'); if (p) { *p = '\0'; portno = atoi(p+1); } memset(&ied, 0, sizeof(ied)); if (secret) strncpy(session->secret, secret, sizeof(session->secret) - 1); else strcpy(session->secret, ""); /* Connect first */ hp = gethostbyname(tmp); if (!hp) { snprintf(iax_errstr, sizeof(iax_errstr), "Invalid hostname: %s", tmp); return -1; } memcpy(&session->peeraddr.sin_addr, hp->h_addr, sizeof(session->peeraddr.sin_addr)); session->peeraddr.sin_port = htons(portno); session->peeraddr.sin_family = AF_INET; strncpy(session->username, peer, sizeof(session->username) - 1); session->refresh = refresh; iax_ie_append_str(&ied, IAX_IE_USERNAME, peer); iax_ie_append_short(&ied, IAX_IE_REFRESH, refresh); res = send_command(session, AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1); return res; } int iax_reject(struct iax_session *session, char *reason) { struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_str(&ied, IAX_IE_CAUSE, reason ? reason : "Unspecified"); return send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied.buf, ied.pos, -1); } int iax_hangup(struct iax_session *session, char *byemsg) { struct iax_ie_data ied; iax_sched_del(NULL, NULL, send_ping, (void *) session, 1); memset(&ied, 0, sizeof(ied)); iax_ie_append_str(&ied, IAX_IE_CAUSE, byemsg ? byemsg : "Normal clearing"); return send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1); } int iax_sendurl(struct iax_session *session, char *url) { return send_command(session, AST_FRAME_HTML, AST_HTML_URL, 0, (unsigned char *)url, (int)strlen(url), -1); } int iax_ring_announce(struct iax_session *session) { return send_command(session, AST_FRAME_CONTROL, AST_CONTROL_RINGING, 0, NULL, 0, -1); } int iax_lag_request(struct iax_session *session) { return send_command(session, AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1); } int iax_busy(struct iax_session *session) { return send_command(session, AST_FRAME_CONTROL, AST_CONTROL_BUSY, 0, NULL, 0, -1); } int iax_congestion(struct iax_session *session) { return send_command(session, AST_FRAME_CONTROL, AST_CONTROL_CONGESTION, 0, NULL, 0, -1); } int iax_accept(struct iax_session *session, int format) { struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_int(&ied, IAX_IE_FORMAT, format); return send_command(session, AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied.buf, ied.pos, -1); } int iax_answer(struct iax_session *session) { return send_command(session, AST_FRAME_CONTROL, AST_CONTROL_ANSWER, 0, NULL, 0, -1); } int iax_load_complete(struct iax_session *session) { return send_command(session, AST_FRAME_HTML, AST_HTML_LDCOMPLETE, 0, NULL, 0, -1); } int iax_send_url(struct iax_session *session, const char *url, int link) { return send_command(session, AST_FRAME_HTML, link ? AST_HTML_LINKURL : AST_HTML_URL, 0, (unsigned char *)url, (int)strlen(url), -1); } int iax_send_text(struct iax_session *session, const char *text) { return send_command(session, AST_FRAME_TEXT, 0, 0, (unsigned char *)text, (int)strlen(text) + 1, -1); } int iax_send_unlink(struct iax_session *session) { return send_command(session, AST_FRAME_HTML, AST_HTML_UNLINK, 0, NULL, 0, -1); } int iax_send_link_reject(struct iax_session *session) { return send_command(session, AST_FRAME_HTML, AST_HTML_LINKREJECT, 0, NULL, 0, -1); } static int iax_send_pong(struct iax_session *session, unsigned int ts) { struct iax_ie_data ied; jb_info stats; memset(&ied, 0, sizeof(ied)); jb_getinfo(session->jb, &stats); iax_ie_append_int(&ied,IAX_IE_RR_JITTER, stats.jitter); /* XXX: should be short-term loss pct.. */ if(stats.frames_in == 0) stats.frames_in = 1; iax_ie_append_int(&ied,IAX_IE_RR_LOSS, ((0xff & (stats.losspct/1000)) << 24 | (stats.frames_lost & 0x00ffffff))); iax_ie_append_int(&ied,IAX_IE_RR_PKTS, stats.frames_in); iax_ie_append_short(&ied,IAX_IE_RR_DELAY, (unsigned short)(stats.current - stats.min)); iax_ie_append_int(&ied,IAX_IE_RR_DROPPED, stats.frames_dropped); iax_ie_append_int(&ied,IAX_IE_RR_OOO, stats.frames_ooo); return send_command(session, AST_FRAME_IAX, IAX_COMMAND_PONG, ts, ied.buf, ied.pos, -1); } /* external API; deprecated since we send pings ourselves now (finally) */ int iax_send_ping(struct iax_session *session) { return send_command(session, AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1); } /* scheduled ping sender; sends ping, then reschedules */ static void send_ping(void *s) { struct iax_session *session = (struct iax_session *)s; /* important, eh? */ if(!iax_session_valid(session)) return; send_command(session, AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1); session->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)session, ping_time * 1000); return; } static int iax_send_lagrp(struct iax_session *session, unsigned int ts) { return send_command(session, AST_FRAME_IAX, IAX_COMMAND_LAGRP, ts, NULL, 0, -1); } static int iax_send_txcnt(struct iax_session *session) { struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_int(&ied, IAX_IE_TRANSFERID, session->transferid); return send_command_transfer(session, AST_FRAME_IAX, IAX_COMMAND_TXCNT, 0, ied.buf, ied.pos); } static int iax_send_txrej(struct iax_session *session) { struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_int(&ied, IAX_IE_TRANSFERID, session->transferid); return send_command_transfer(session, AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, ied.buf, ied.pos); } static int iax_send_txaccept(struct iax_session *session) { struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_int(&ied, IAX_IE_TRANSFERID, session->transferid); return send_command_transfer(session, AST_FRAME_IAX, IAX_COMMAND_TXACC, 0, ied.buf, ied.pos); } static int iax_send_txready(struct iax_session *session) { struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); /* see asterisk chan_iax2.c */ iax_ie_append_short(&ied, IAX_IE_CALLNO, session->callno); return send_command(session, AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied.buf, ied.pos, -1); } int iax_auth_reply(struct iax_session *session, char *password, char *challenge, int methods) { char reply[16]; struct IAX_MD5Context md5; char realreply[256]; struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); if ((methods & IAX_AUTH_MD5) && challenge) { IAX_MD5Init(&md5); IAX_MD5Update(&md5, (const unsigned char *) challenge, (unsigned int)strlen(challenge)); IAX_MD5Update(&md5, (const unsigned char *) password, (unsigned int)strlen(password)); IAX_MD5Final((unsigned char *) reply, &md5); memset(realreply, 0, sizeof(realreply)); convert_reply(realreply, (unsigned char *) reply); iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, realreply); } else { iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, password); } return send_command(session, AST_FRAME_IAX, IAX_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1); } static int iax_regauth_reply(struct iax_session *session, char *password, char *challenge, int methods) { char reply[16]; struct IAX_MD5Context md5; char realreply[256]; struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_str(&ied, IAX_IE_USERNAME, session->username); iax_ie_append_short(&ied, IAX_IE_REFRESH, session->refresh); if ((methods & IAX_AUTHMETHOD_MD5) && challenge) { IAX_MD5Init(&md5); IAX_MD5Update(&md5, (const unsigned char *) challenge, (unsigned int)strlen(challenge)); IAX_MD5Update(&md5, (const unsigned char *) password, (unsigned int)strlen(password)); IAX_MD5Final((unsigned char *) reply, &md5); memset(realreply, 0, sizeof(realreply)); convert_reply(realreply, (unsigned char *) reply); iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, realreply); } else { iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, password); } return send_command(session, AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1); } int iax_dial(struct iax_session *session, char *number) { struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, number); return send_command(session, AST_FRAME_IAX, IAX_COMMAND_DIAL, 0, ied.buf, ied.pos, -1); } int iax_quelch(struct iax_session *session) { return send_command(session, AST_FRAME_IAX, IAX_COMMAND_QUELCH, 0, NULL, 0, -1); } int iax_unquelch(struct iax_session *session) { return send_command(session, AST_FRAME_IAX, IAX_COMMAND_UNQUELCH, 0, NULL, 0, -1); } int iax_dialplan_request(struct iax_session *session, char *number) { struct iax_ie_data ied; memset(&ied, 0, sizeof(ied)); iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, number); return send_command(session, AST_FRAME_IAX, IAX_COMMAND_DPREQ, 0, ied.buf, ied.pos, -1); } static inline int which_bit(unsigned int i) { char x; for(x = 0; x < 32; x++) { if ((1U << x) == i) { return x + 1; } } return 0; } char iax_pref_codec_add(struct iax_session *session, unsigned int format) { int diff = (int) 'A'; session->codec_order[session->codec_order_len++] = (which_bit(format)) + diff; session->codec_order[session->codec_order_len] = '\0'; return session->codec_order[session->codec_order_len-1]; } void iax_pref_codec_del(struct iax_session *session, unsigned int format) { int diff = (int) 'A'; int x; char old[32]; char remove = which_bit(format) + diff; strncpy(old, session->codec_order, sizeof(old)); old[sizeof(old) - 1] = '\0'; session->codec_order_len = 0; for (x = 0; x < (int) strlen(old); x++) { if (old[x] != remove) { session->codec_order[session->codec_order_len++] = old[x]; } } session->codec_order[session->codec_order_len] = '\0'; } int iax_pref_codec_get(struct iax_session *session, unsigned int *array, int len) { int diff = (int) 'A'; int x; for (x = 0; x < session->codec_order_len && x < len; x++) { array[x] = (1 << (session->codec_order[x] - diff - 1)); } return x; } int iax_call(struct iax_session *session, const char *cidnum, const char *cidname, const char *ich, const char *lang, int wait, int formats, int capabilities) { char tmp[256]=""; char *part1, *part2; int res; int portno; char *username, *hostname, *secret, *context, *exten, *dnid; struct iax_ie_data ied; struct hostent *hp; /* We start by parsing up the temporary variable which is of the form of: [user@]peer[:portno][/exten[@context]] */ if (!ich) { IAXERROR "Invalid IAX Call Handle\n"); DEBU(G "Invalid IAX Call Handle\n"); return -1; } memset(&ied, 0, sizeof(ied)); strncpy(tmp, ich, sizeof(tmp) - 1); iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION); if (cidnum) iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, cidnum); if (cidname) iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, cidname); if (session->codec_order_len) { iax_ie_append_str(&ied, IAX_IE_CODEC_PREFS, session->codec_order); } session->capability = capabilities; session->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)session, 2 * 1000); /* XXX We should have a preferred format XXX */ iax_ie_append_int(&ied, IAX_IE_FORMAT, formats); iax_ie_append_int(&ied, IAX_IE_CAPABILITY, capabilities); if (lang) iax_ie_append_str(&ied, IAX_IE_LANGUAGE, lang); /* Part 1 is [user[:password]@]peer[:port] */ part1 = strtok(tmp, "/"); /* Part 2 is exten[@context] if it is anything all */ part2 = strtok(NULL, "/"); if (strchr(part1, '@')) { username = strtok(part1, "@"); hostname = strtok(NULL, "@"); } else { username = NULL; hostname = part1; } if (username && strchr(username, ':')) { username = strtok(username, ":"); secret = strtok(NULL, ":"); } else secret = NULL; if(username) strncpy(session->username, username, sizeof(session->username) - 1); if(secret) strncpy(session->secret, secret, sizeof(session->secret) - 1); if (strchr(hostname, ':')) { strtok(hostname, ":"); portno = atoi(strtok(NULL, ":")); } else { portno = IAX_DEFAULT_PORTNO; } if (part2) { exten = strtok(part2, "@"); dnid = exten; context = strtok(NULL, "@"); } else { exten = NULL; dnid = NULL; context = NULL; } if (username) iax_ie_append_str(&ied, IAX_IE_USERNAME, username); if (exten && strlen(exten)) iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, exten); if (dnid && strlen(dnid)) iax_ie_append_str(&ied, IAX_IE_DNID, dnid); if (context && strlen(context)) iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context); /* Setup host connection */ hp = gethostbyname(hostname); if (!hp) { snprintf(iax_errstr, sizeof(iax_errstr), "Invalid hostname: %s", hostname); return -1; } memcpy(&session->peeraddr.sin_addr, hp->h_addr, sizeof(session->peeraddr.sin_addr)); session->peeraddr.sin_port = htons(portno); session->peeraddr.sin_family = AF_INET; res = send_command(session, AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1); if (res < 0) return res; if (wait) { DEBU(G "Waiting not yet implemented\n"); return -1; } return res; } static int calc_rxstamp(struct iax_session *session) { struct timeval tv; int ms; if (!session->rxcore.tv_sec && !session->rxcore.tv_usec) { gettimeofday(&session->rxcore, NULL); } gettimeofday(&tv, NULL); ms = (tv.tv_sec - session->rxcore.tv_sec) * 1000 + (tv.tv_usec - session->rxcore.tv_usec) / 1000; return ms; } #ifdef notdef_cruft static int match(struct sockaddr_in *sin, short callno, short dcallno, struct iax_session *cur) { if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->peeraddr.sin_port == sin->sin_port)) { /* This is the main host */ if ((cur->peercallno == callno) || ((dcallno == cur->callno) && !cur->peercallno)) { /* That's us. Be sure we keep track of the peer call number */ cur->peercallno = callno; return 1; } } if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) { /* We're transferring */ if (dcallno == cur->callno) return 1; } return 0; } #endif /* splitted match into 2 passes otherwise causing problem of matching up the wrong session using the dcallno and the peercallno because during a transfer (2 IAX channels on the same client/system) the same peercallno (from two different asterisks) exist in more than one session. */ static int forward_match(struct sockaddr_in *sin, short callno, short dcallno, struct iax_session *cur) { if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) { /* We're transferring */ if (dcallno == cur->callno) { return 1; } } if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->peeraddr.sin_port == sin->sin_port)) { if (dcallno == cur->callno && dcallno != 0) { /* That's us. Be sure we keep track of the peer call number */ if (cur->peercallno == 0) { cur->peercallno = callno; } else if ( cur->peercallno != callno ) { // print a warning when the callno's don't match fprintf( stderr, "WARNING: peercallno does not match callno" ", peercallno => %d, callno => %d, dcallno => %d", cur->peercallno, callno, dcallno ) ; return 0 ; } return 1; } } return 0; } static int reverse_match(struct sockaddr_in *sin, short callno, struct iax_session *cur) { if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) { /* We're transferring */ if (callno == cur->peercallno) { return 1; } } if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->peeraddr.sin_port == sin->sin_port)) { if (callno == cur->peercallno) { return 1; } } return 0; } static struct iax_session *iax_find_session(struct sockaddr_in *sin, short callno, short dcallno, int makenew) { struct iax_session *cur = sessions; while(cur) { if (forward_match(sin, callno, dcallno, cur)) { return cur; } cur = cur->next; } cur = sessions; while(cur) { if (reverse_match(sin, callno, cur)) { return cur; } cur = cur->next; } if (makenew && !dcallno) { cur = iax_session_new(); cur->peercallno = callno; cur->peeraddr.sin_addr.s_addr = sin->sin_addr.s_addr; cur->peeraddr.sin_port = sin->sin_port; cur->peeraddr.sin_family = AF_INET; cur->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)cur, 2 * 1000); DEBU(G "Making new session, peer callno %d, our callno %d\n", callno, cur->callno); } else { DEBU(G "No session, peer = %d, us = %d\n", callno, dcallno); } return cur; } #ifdef EXTREME_DEBUG static int display_time(int ms) { static int oldms = -1; if (oldms < 0) { DEBU(G "First measure\n"); oldms = ms; return 0; } DEBU(G "Time from last frame is %d ms\n", ms - oldms); oldms = ms; return 0; } #endif /* From chan_iax2/steve davies: need to get permission from steve or digium, I guess */ static long unwrap_timestamp(long ts, long last) { int x; if ( (ts & 0xFFFF0000) == (last & 0xFFFF0000) ) { x = ts - last; if (x < -50000) { /* Sudden big jump backwards in timestamp: What likely happened here is that miniframe timestamp has circled but we haven't gotten the update from the main packet. We'll just pretend that we did, and update the timestamp appropriately. */ ts = ( (last & 0xFFFF0000) + 0x10000) | (ts & 0xFFFF); DEBU(G "schedule_delivery: pushed forward timestamp\n"); } if (x > 50000) { /* Sudden apparent big jump forwards in timestamp: What's likely happened is this is an old miniframe belonging to the previous top-16-bit timestamp that has turned up out of order. Adjust the timestamp appropriately. */ ts = ( (last & 0xFFFF0000) - 0x10000) | (ts & 0xFFFF); DEBU(G "schedule_delivery: pushed back timestamp\n"); } } else if ( (ts & 0xFFFF8000L) == (last & 0xFFFF8000L) ) { x = ts - last; if (x < -50000) { /* Sudden big jump backwards in timestamp: What likely happened here is that miniframe timestamp has circled but we haven't gotten the update from the main packet. We'll just pretend that we did, and update the timestamp appropriately. */ ts = ( (last & 0xFFFF8000L) + 0x10000) | (ts & 0xFFFF); DEBU(G "schedule_delivery: pushed forward timestamp\n"); } if (x > 50000) { /* Sudden apparent big jump forwards in timestamp: * What's likely happened is this is an old miniframe * belonging to the previous top-16-bit timestamp that * has turned up out of order. Adjust the timestamp * appropriately. */ ts = ( (last & 0xFFFF8000L) - 0x10000) | (ts & 0xFFFF); DEBU(G "schedule_delivery: pushed back timestamp\n"); } } return ts; } static struct iax_event *schedule_delivery(struct iax_event *e, unsigned int ts, int updatehistory) { /* * This is the core of the IAX jitterbuffer delivery mechanism: * Dynamically adjust the jitterbuffer and decide how long to wait * before delivering the packet. */ #ifdef EXTREME_DEBUG DEBU(G "[%p] We are at %d, packet is for %d\n", e->session, calc_rxstamp(e->session), ts); #endif /* insert into jitterbuffer */ /* TODO: Perhaps we could act immediately if it's not droppable and late */ if ( e->etype == IAX_EVENT_VIDEO && video_bypass_jitterbuffer ) { iax_sched_add(e, NULL, NULL, NULL, 0); return NULL; } else { int type = JB_TYPE_CONTROL; int len = 0; if(e->etype == IAX_EVENT_VOICE) { type = JB_TYPE_VOICE; /* The frame time only has an effect for voice */ len = get_sample_cnt(e) / 8; } else if(e->etype == IAX_EVENT_VIDEO) { type = JB_TYPE_VIDEO; } else if(e->etype == IAX_EVENT_CNG) { type = JB_TYPE_SILENCE; } /* unwrap timestamp */ ts = unwrap_timestamp(ts,e->session->last_ts); /* move forward last_ts if it's greater. We do this _after_ * unwrapping, because asterisk _still_ has cases where it * doesn't send full frames when it ought to */ if(ts > e->session->last_ts) { e->session->last_ts = ts; } if(jb_put(e->session->jb, e, type, len, ts, calc_rxstamp(e->session)) == JB_DROP) { iax_event_free(e); } } return NULL; } static int uncompress_subclass(unsigned char csub) { /* If the SC_LOG flag is set, return 2^csub otherwise csub */ if (csub & IAX_FLAG_SC_LOG) return 1 << (csub & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT); else return csub; } static void iax_handle_vnak(struct iax_session *session, struct ast_iax2_full_hdr *fh) { struct iax_sched *sch, *list, *l, *tmp; /* * According to the IAX2 02 draft, we MUST immediately retransmit all frames * with higher sequence number than the VNAK's iseqno * However, it seems that the right thing to do would be to retransmit * frames with sequence numbers higher OR EQUAL to VNAK's iseqno. */ sch = schedq; list = NULL; while ( sch != NULL ) { if ( sch->frame != NULL && sch->frame->session == session ) { /* * We want to check if our frame's oseqno is greater or equal than * the VNAK's iseqno, but we need to take into account sequence * number wrap-arounds * session->rseqno is our last acknowledged sequence number, so * we use that as a base */ if ( (unsigned char)(fh->iseqno - session->rseqno) <= (unsigned char)(sch->frame->oseqno - session->rseqno) ) { /* * We cannot retransmit immediately, since the frames are ordered by retransmit time * We need to collect them and orrange them in ascending order of their oseqno */ tmp = (struct iax_sched *)calloc(1, sizeof(struct iax_sched)); tmp->frame = sch->frame; if ( list == NULL || (list->frame->oseqno - session->rseqno) > (tmp->frame->oseqno - session->rseqno) ) { tmp->next = list; list = tmp; } else { l = list; while ( l != NULL ) { if ( l->next == NULL || (l->next->frame->oseqno - session->rseqno) > (tmp->frame->oseqno - session->rseqno) ) { tmp->next = l->next; l->next = tmp; break; } l = l->next; } } } } sch = sch->next; } /* Transmit collected frames and free the space */ while ( list != NULL ) { tmp = list; iax_xmit_frame(tmp->frame); list = list->next; free(tmp); } } static struct iax_event *iax_header_to_event(struct iax_session *session, struct ast_iax2_full_hdr *fh, int datalen, struct sockaddr_in *sin) { struct iax_event *e; struct iax_sched *sch; unsigned int ts; int subclass; int nowts; int updatehistory = 1; ts = ntohl(fh->ts); if (fh->type==AST_FRAME_VIDEO) subclass = uncompress_subclass(fh->csub & ~0x40) | ((fh->csub >> 6) & 0x1); else subclass = uncompress_subclass(fh->csub); /* don't run last_ts backwards; i.e. for retransmits and the like */ if (ts > session->last_ts && (fh->type == AST_FRAME_IAX && subclass != IAX_COMMAND_ACK && subclass != IAX_COMMAND_PONG && subclass != IAX_COMMAND_LAGRP)) { session->last_ts = ts; } #ifdef DEBUG_SUPPORT iax_showframe(NULL, fh, 1, sin, datalen); #endif /* Get things going with it, timestamp wise, if we haven't already. */ /* Handle implicit ACKing unless this is an INVAL, and only if this is from the real peer, not the transfer peer */ if ( !inaddrcmp(sin, &session->peeraddr) && ( subclass != IAX_COMMAND_INVAL || fh->type != AST_FRAME_IAX ) ) { unsigned char x; /* XXX This code is not very efficient. Surely there is a better way which still properly handles boundary conditions? XXX */ /* First we have to qualify that the ACKed value is within our window */ for (x=session->rseqno; x != session->oseqno; x++) if (fh->iseqno == x) break; if ((x != session->oseqno) || (session->oseqno == fh->iseqno)) { /* The acknowledgement is within our window. Time to acknowledge everything that it says to */ for (x=session->rseqno; x != fh->iseqno; x++) { /* Ack the packet with the given timestamp */ DEBU(G "Cancelling transmission of packet %d\n", x); sch = schedq; while(sch) { if ( sch->frame && sch->frame->session == session && sch->frame->oseqno == x ) sch->frame->retries = -1; sch = sch->next; } } /* Note how much we've received acknowledgement for */ session->rseqno = fh->iseqno; } else DEBU(G "Received iseqno %d not within window %d->%d\n", fh->iseqno, session->rseqno, session->oseqno); } /* Check where we are */ if ((ntohs(fh->dcallno) & IAX_FLAG_RETRANS) || ((fh->type != AST_FRAME_VOICE) && (fh->type != AST_FRAME_VIDEO))) updatehistory = 0; if ((session->iseqno != fh->oseqno) && (session->iseqno || ((subclass != IAX_COMMAND_TXREADY) && (subclass != IAX_COMMAND_TXREL) && (subclass != IAX_COMMAND_TXCNT) && (subclass != IAX_COMMAND_TXACC)) || (fh->type != AST_FRAME_IAX))) { if ( ((subclass != IAX_COMMAND_ACK) && (subclass != IAX_COMMAND_INVAL) && (subclass != IAX_COMMAND_TXREADY) && (subclass != IAX_COMMAND_TXREL) && (subclass != IAX_COMMAND_TXCNT) && (subclass != IAX_COMMAND_TXACC) && (subclass != IAX_COMMAND_VNAK)) || (fh->type != AST_FRAME_IAX)) { /* If it's not an ACK packet, it's out of order. */ DEBU(G "Packet arrived out of order (expecting %d, got %d) (frametype = %d, subclass = %d)\n", session->iseqno, fh->oseqno, fh->type, subclass); /* * Check if session->iseqno > fh->oseqno, accounting for possible wrap around * This is correct if the two values are not equal (which, in this case, is guaranteed) */ if ( (unsigned char)(session->iseqno - fh->oseqno) < 128 ) { /* If we've already seen it, ack it XXX There's a border condition here XXX */ if ((fh->type != AST_FRAME_IAX) || ((subclass != IAX_COMMAND_ACK) && (subclass != IAX_COMMAND_INVAL))) { DEBU(G "Acking anyway\n"); /* XXX Maybe we should handle its ack to us, but then again, it's probably outdated anyway, and if we have anything to send, we'll retransmit and get an ACK back anyway XXX */ send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0,fh->iseqno); } } else { /* Send a VNAK requesting retransmission */ iax2_vnak(session); } return NULL; } } else { /* Increment unless it's an ACK or VNAK */ if (((subclass != IAX_COMMAND_ACK) && (subclass != IAX_COMMAND_INVAL) && (subclass != IAX_COMMAND_TXCNT) && (subclass != IAX_COMMAND_TXACC) && (subclass != IAX_COMMAND_VNAK)) || (fh->type != AST_FRAME_IAX)) session->iseqno++; } e = (struct iax_event *)malloc(sizeof(struct iax_event) + datalen + 1); if (e) { memset(e, 0, sizeof(struct iax_event) + datalen); /* Set etype to some unknown value so do not inavertently sending IAX_EVENT_CONNECT event, which is 0 to application. */ e->etype = -1; e->session = session; switch(fh->type) { case AST_FRAME_DTMF: e->etype = IAX_EVENT_DTMF; e->subclass = subclass; /* We want the DTMF event deliver immediately so all I/O can be terminate quickly in an IVR system. e = schedule_delivery(e, ts, updatehistory); */ break; case AST_FRAME_VOICE: e->etype = IAX_EVENT_VOICE; e->subclass = subclass; e->ts = ts; session->voiceformat = subclass; if (datalen) { memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; } e = schedule_delivery(e, ts, updatehistory); break; case AST_FRAME_CNG: e->etype = IAX_EVENT_CNG; e->subclass = subclass; if (datalen) { memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; } e = schedule_delivery(e, ts, updatehistory); break; case AST_FRAME_IAX: /* Parse IE's */ if (datalen) { memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; } if (iax_parse_ies(&e->ies, e->data, e->datalen)) { IAXERROR "Unable to parse IE's"); free(e); e = NULL; break; } switch(subclass) { case IAX_COMMAND_NEW: /* This is a new, incoming call */ /* save the capability for validation */ session->capability = e->ies.capability; if (e->ies.codec_prefs) { strncpy(session->codec_order, e->ies.codec_prefs, sizeof(session->codec_order)); session->codec_order[sizeof(session->codec_order) - 1] = '\0'; session->codec_order_len = (int)strlen(session->codec_order); } e->etype = IAX_EVENT_CONNECT; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_AUTHREQ: /* This is a request for a call */ e->etype = IAX_EVENT_AUTHRQ; if (strlen(session->username) && !strcmp(e->ies.username, session->username) && strlen(session->secret)) { /* Hey, we already know this one */ iax_auth_reply(session, session->secret, e->ies.challenge, e->ies.authmethods); free(e); e = NULL; break; } e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_HANGUP: e->etype = IAX_EVENT_HANGUP; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_INVAL: e->etype = IAX_EVENT_HANGUP; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REJECT: e->etype = IAX_EVENT_REJECT; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_ACK: free(e); e = NULL; break; case IAX_COMMAND_VNAK: iax_handle_vnak(session, fh); free(e); e = NULL; break; case IAX_COMMAND_LAGRQ: /* Pass this along for later handling */ e->etype = IAX_EVENT_LAGRQ; e->ts = ts; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_POKE: e->etype = IAX_EVENT_POKE; e->ts = ts; break; case IAX_COMMAND_PING: /* PINGS and PONGS don't get scheduled; */ e->etype = IAX_EVENT_PING; e->ts = ts; break; case IAX_COMMAND_PONG: e->etype = IAX_EVENT_PONG; /* track weighted average of ping time */ session->pingtime = ((2 * session->pingtime) + (calc_timestamp(session,0,NULL) - ts)) / 3; session->remote_netstats.jitter = e->ies.rr_jitter; session->remote_netstats.losspct = e->ies.rr_loss >> 24;; session->remote_netstats.losscnt = e->ies.rr_loss & 0xffffff; session->remote_netstats.packets = e->ies.rr_pkts; session->remote_netstats.delay = e->ies.rr_delay; session->remote_netstats.dropped = e->ies.rr_dropped; session->remote_netstats.ooo = e->ies.rr_ooo; break; case IAX_COMMAND_ACCEPT: if (e->ies.format & session->capability) { e->etype = IAX_EVENT_ACCEPT; } else { struct iax_ie_data ied; /* Although this should not happen, we * added this to make sure the * negotiation protocol is enforced. * For lack of event to notify the * application we use the defined * REJECT event. */ memset(&ied, 0, sizeof(ied)); iax_ie_append_str(&ied, IAX_IE_CAUSE, "Unable to negotiate codec"); send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied.buf, ied.pos, -1); e->etype = IAX_EVENT_REJECT; } e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REGACK: e->etype = IAX_EVENT_REGACK; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REGAUTH: iax_regauth_reply(session, session->secret, e->ies.challenge, e->ies.authmethods); free(e); e = NULL; break; case IAX_COMMAND_REGREJ: e->etype = IAX_EVENT_REGREJ; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_LAGRP: e->etype = IAX_EVENT_LAGRP; nowts = calc_timestamp(session, 0, NULL); e->ts = nowts - ts; /* Can't call schedule_delivery since timestamp is non-normal */ break;; case IAX_COMMAND_TXREQ: /* added check for defensive programming * - in case the asterisk server * or another client does not send the * apparent transfer address */ if (e->ies.apparent_addr != NULL) { /* so a full voice frame is sent on the next voice output */ session->svoiceformat = -1; session->transfer = *e->ies.apparent_addr; session->transfer.sin_family = AF_INET; session->transfercallno = e->ies.callno; session->transferring = TRANSFER_BEGIN; session->transferid = e->ies.transferid; iax_send_txcnt(session); } free(e); e = NULL; break; case IAX_COMMAND_DPREP: /* Received dialplan reply */ e->etype = IAX_EVENT_DPREP; /* Return immediately, makes no sense to schedule */ break; case IAX_COMMAND_TXCNT: if (session->transferring) { session->transfer = *sin; iax_send_txaccept(session); } free(e); e = NULL; break; case IAX_COMMAND_TXACC: if (session->transferring) { stop_transfer(session); session->transferring = TRANSFER_READY; iax_send_txready(session); } free(e); e = NULL; break; case IAX_COMMAND_TXREL: /* Release the transfer */ send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0, fh->iseqno); if (session->transferring) { complete_transfer(session, e->ies.callno, 1, 0); } else { complete_transfer(session, session->peercallno, 0, 1); } e->etype = IAX_EVENT_TRANSFER; /* notify that asterisk no longer sitting between peers */ e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_QUELCH: e->etype = IAX_EVENT_QUELCH; session->quelch = 1; break; case IAX_COMMAND_UNQUELCH: e->etype = IAX_EVENT_UNQUELCH; session->quelch = 0; break; case IAX_COMMAND_TXREJ: e->etype = IAX_EVENT_TXREJECT; iax_handle_txreject(session); break; case IAX_COMMAND_TXREADY: send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0, fh->iseqno); if (iax_handle_txready(session)) { e->etype = IAX_EVENT_TXREADY; } else { free(e); e = NULL; } break; default: DEBU(G "Don't know what to do with IAX command %d\n", subclass); free(e); e = NULL; } break; case AST_FRAME_CONTROL: switch(subclass) { case AST_CONTROL_ANSWER: e->etype = IAX_EVENT_ANSWER; e = schedule_delivery(e, ts, updatehistory); break; case AST_CONTROL_CONGESTION: case AST_CONTROL_BUSY: e->etype = IAX_EVENT_BUSY; e = schedule_delivery(e, ts, updatehistory); break; case AST_CONTROL_RINGING: e->etype = IAX_EVENT_RINGA; e = schedule_delivery(e, ts, updatehistory); break; default: DEBU(G "Don't know what to do with AST control %d\n", subclass); free(e); return NULL; } break; case AST_FRAME_IMAGE: e->etype = IAX_EVENT_IMAGE; e->subclass = subclass; if (datalen) { memcpy(e->data, fh->iedata, datalen); } e = schedule_delivery(e, ts, updatehistory); break; case AST_FRAME_VIDEO: e->etype = IAX_EVENT_VIDEO; e->subclass = subclass; e->ts = ts; session->videoformat = e->subclass; memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; e = schedule_delivery(e, ts, updatehistory); break; case AST_FRAME_TEXT: e->etype = IAX_EVENT_TEXT; if (datalen) { memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; } e = schedule_delivery(e, ts, updatehistory); break; case AST_FRAME_HTML: switch(fh->csub) { case AST_HTML_LINKURL: e->etype = IAX_EVENT_LINKURL; /* Fall through */ case AST_HTML_URL: if (e->etype == -1) e->etype = IAX_EVENT_URL; e->subclass = fh->csub; e->datalen = datalen; if (datalen) { memcpy(e->data, fh->iedata, datalen); } e = schedule_delivery(e, ts, updatehistory); break; case AST_HTML_LDCOMPLETE: e->etype = IAX_EVENT_LDCOMPLETE; e = schedule_delivery(e, ts, updatehistory); break; case AST_HTML_UNLINK: e->etype = IAX_EVENT_UNLINK; e = schedule_delivery(e, ts, updatehistory); break; case AST_HTML_LINKREJECT: e->etype = IAX_EVENT_LINKREJECT; e = schedule_delivery(e, ts, updatehistory); break; default: DEBU(G "Don't know how to handle HTML type %d frames\n", fh->csub); free(e); return NULL; } break; default: DEBU(G "Don't know what to do with frame type %d\n", fh->type); free(e); return NULL; } } else DEBU(G "Out of memory\n"); /* Already ack'd iax frames */ if (session->aseqno != session->iseqno) { send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0, fh->iseqno); } return e; } /* Some parts taken from iax_miniheader_to_event and from from chan_iax2.c. We must inform Mark Spencer? */ static struct iax_event *iax_videoheader_to_event(struct iax_session *session, struct ast_iax2_video_hdr *vh, int datalen) { struct iax_event * e; if ( session->videoformat <= 0 ) { DEBU(G "No last video format received on session %d\n", session->callno); return 0; } e = (struct iax_event *)malloc(sizeof(struct iax_event) + datalen); if ( !e ) { DEBU(G "Out of memory\n"); return 0; } e->etype = IAX_EVENT_VIDEO; e->session = session; e->subclass = session->videoformat | (ntohs(vh->ts) & 0x8000 ? 1 : 0); e->datalen = datalen; memcpy(e->data, vh->data, e->datalen); e->ts = (session->last_ts & 0xFFFF8000L) | (ntohs(vh->ts) & 0x7fff); return schedule_delivery(e, e->ts, 1); } static struct iax_event *iax_miniheader_to_event(struct iax_session *session, struct ast_iax2_mini_hdr *mh, int datalen) { struct iax_event * e; if ( session->voiceformat <= 0 ) { DEBU(G "No last format received on session %d\n", session->callno); return 0; } e = (struct iax_event *)malloc(sizeof(struct iax_event) + datalen); if ( !e ) { DEBU(G "Out of memory\n"); return 0; } e->etype = IAX_EVENT_VOICE; e->session = session; e->subclass = session->voiceformat; e->datalen = datalen; memcpy(e->data, mh->data, datalen); e->ts = (session->last_ts & 0xFFFF0000) | ntohs(mh->ts); return schedule_delivery(e, e->ts, 1); } void iax_destroy(struct iax_session *session) { destroy_session(session); } static struct iax_event *iax_net_read(void) { unsigned char buf[65536]; int res; struct sockaddr_in sin; socklen_t sinlen; struct iax_event *event; sinlen = sizeof(sin); res = iax_recvfrom(netfd, (char *)buf, sizeof(buf), 0, (struct sockaddr *) &sin, &sinlen); if (res < 0) { #if defined(_WIN32_WCE) if (WSAGetLastError() != WSAEWOULDBLOCK) { DEBU(G "Error on read: %d\n", WSAGetLastError()); IAXERROR "Read error on network socket: ???"); } #elif defined(WIN32) || defined(_WIN32_WCE) if (WSAGetLastError() != WSAEWOULDBLOCK) { DEBU(G "Error on read: %d\n", WSAGetLastError()); IAXERROR "Read error on network socket: %s", strerror(errno)); } #else if (errno != EAGAIN) { DEBU(G "Error on read: %s\n", strerror(errno)); IAXERROR "Read error on network socket: %s", strerror(errno)); } #endif return NULL; } event = iax_net_process(buf, res, &sin); if ( event == NULL ) { // We have received a frame. The corresponding event is queued // We need to motify the entire stack of calling functions so they // don't go to sleep thinking there are no more frames to process // TODO: this is buttugly from a design point of view. Basically we // change libiax2 behavior to accomodate iaxclient. // There must be a way to do it better. event = (struct iax_event *)malloc(sizeof(struct iax_event)); if ( event != NULL ) event->etype = IAX_EVENT_NULL; } return event; } static struct iax_session *iax_txcnt_session(struct ast_iax2_full_hdr *fh, int datalen, struct sockaddr_in *sin, short callno, short dcallno) { int subclass = uncompress_subclass(fh->csub); unsigned char buf[ 65536 ]; /* allocated on stack with same size as iax_net_read() */ struct iax_ies ies; struct iax_session *cur; if ((fh->type != AST_FRAME_IAX) || (subclass != IAX_COMMAND_TXCNT) || (!datalen)) { return NULL; /* special handling for TXCNT only */ } memcpy(buf, fh->iedata, datalen); /* prepare local buf for iax_parse_ies() */ if (iax_parse_ies(&ies, buf, datalen)) { return NULL; /* Unable to parse IE's */ } if (!ies.transferid) { return NULL; /* TXCNT without proper IAX_IE_TRANSFERID */ } for( cur=sessions; cur; cur=cur->next ) { if ((cur->transferring) && (cur->transferid == (int) ies.transferid) && (cur->callno == dcallno) && (cur->transfercallno == callno)) { /* We're transferring --- * skip address/port checking which would fail while * remote peer behind symmetric NAT, verify * transferid instead */ cur->transfer.sin_addr.s_addr = sin->sin_addr.s_addr; /* setup for further handling */ cur->transfer.sin_port = sin->sin_port; break; } } return cur; } struct iax_event *iax_net_process(unsigned char *buf, int len, struct sockaddr_in *sin) { struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)buf; struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)buf; struct ast_iax2_video_hdr *vh = (struct ast_iax2_video_hdr *)buf; struct iax_session *session; if (ntohs(fh->scallno) & IAX_FLAG_FULL) { /* Full size header */ if (len < sizeof(struct ast_iax2_full_hdr)) { DEBU(G "Short header received from %s\n", inet_ntoa(sin->sin_addr)); IAXERROR "Short header received from %s\n", inet_ntoa(sin->sin_addr)); return NULL; } /* We have a full header, process appropriately */ session = iax_find_session(sin, ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, 1); if (!session) session = iax_txcnt_session(fh, len - sizeof(struct ast_iax2_full_hdr), sin, ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS); if (session) return iax_header_to_event(session, fh, len - sizeof(struct ast_iax2_full_hdr), sin); DEBU(G "No session?\n"); return NULL; } else { if (len < sizeof(struct ast_iax2_mini_hdr)) { DEBU(G "Short header received from %s\n", inet_ntoa(sin->sin_addr)); IAXERROR "Short header received from %s\n", inet_ntoa(sin->sin_addr)); return NULL; } /* Miniature, voice frame */ if ((vh->zeros == 0) && (ntohs(vh->callno) & 0x8000)) { session = iax_find_session(sin, ntohs(vh->callno) & ~0x8000, 0, 0); if (session) return iax_videoheader_to_event(session, vh, len - sizeof(struct ast_iax2_video_hdr)); } else { /* audio frame */ session = iax_find_session(sin, ntohs(fh->scallno), 0, 0); if (session) return iax_miniheader_to_event(session, mh, len - sizeof(struct ast_iax2_mini_hdr)); } DEBU(G "No session?\n"); return NULL; } } static struct iax_sched *iax_get_sched(struct timeval tv) { struct iax_sched *cur; cur = schedq; /* Check the event schedule first. */ while(cur) { if ((tv.tv_sec > cur->when.tv_sec) || ((tv.tv_sec == cur->when.tv_sec) && (tv.tv_usec >= cur->when.tv_usec))) { /* Take it out of the event queue */ schedq = cur->next; return cur; } cur = cur->next; } return NULL; } struct iax_event *iax_get_event(int blocking) { struct iax_event *event; struct iax_frame *frame; struct timeval tv; struct iax_sched *cur; struct iax_session *session; gettimeofday(&tv, NULL); while((cur = iax_get_sched(tv))) { event = cur->event; frame = cur->frame; if (event) { /* See if this is an event we need to handle */ event = handle_event(event); if (event) { free(cur); return event; } } else if(frame) { /* It's a frame, transmit it and schedule a retry */ if (frame->retries < 0) { /* It's been acked. No need to send it. Destroy the old frame. If final, destroy the session. */ if (frame->final) destroy_session(frame->session); if (frame->data) free(frame->data); free(frame); } else if (frame->retries == 0) { if (frame->transfer) { /* Send a transfer reject since we weren't able to connect */ iax_send_txrej(frame->session); if (frame->data) free(frame->data); free(frame); free(cur); break; } else { /* We haven't been able to get an ACK on this packet. If a final frame, destroy the session, otherwise, pass up timeout */ if (frame->final) { destroy_session(frame->session); if (frame->data) free(frame->data); free(frame); } else { event = (struct iax_event *)malloc(sizeof(struct iax_event)); if (event) { event->etype = IAX_EVENT_TIMEOUT; event->session = frame->session; if (frame->data) free(frame->data); free(frame); free(cur); return handle_event(event); } } } } else { struct ast_iax2_full_hdr *fh; /* Decrement remaining retries */ frame->retries--; /* Multiply next retry time by 4, not above MAX_RETRY_TIME though */ frame->retrytime *= 4; /* Keep under 1000 ms if this is a transfer packet */ if (!frame->transfer) { if (frame->retrytime > MAX_RETRY_TIME) frame->retrytime = MAX_RETRY_TIME; } else if (frame->retrytime > 1000) frame->retrytime = 1000; fh = (struct ast_iax2_full_hdr *)(frame->data); fh->dcallno = htons(IAX_FLAG_RETRANS | frame->dcallno); iax_xmit_frame(frame); /* Schedule another retransmission */ DEBU(G "Scheduling retransmission %d\n", frame->retries); iax_sched_add(NULL, frame, NULL, NULL, frame->retrytime); } } else if (cur->func) { cur->func(cur->arg); } free(cur); } /* get jitterbuffer-scheduled events */ for ( session = sessions; session; session = session->next ) { int ret; long now; long next; jb_frame frame; now = (tv.tv_sec - session->rxcore.tv_sec) * 1000 + (tv.tv_usec - session->rxcore.tv_usec) / 1000; if ( now <= (next = jb_next(session->jb)) ) continue; /* interp len no longer hardcoded, now determined by get_interp_len */ ret = jb_get(session->jb,&frame,now,get_interp_len(session->voiceformat)); switch(ret) { case JB_OK: event = (struct iax_event *)frame.data; event = handle_event(event); if (event) { return event; } break; case JB_INTERP: /* create an interpolation frame */ //fprintf(stderr, "Making Interpolation frame\n"); event = (struct iax_event *)malloc(sizeof(struct iax_event)); if (event) { event->etype = IAX_EVENT_VOICE; event->subclass = session->voiceformat; /* XXX: ??? applications probably ignore this anyway */ event->ts = now; event->session = session; event->datalen = 0; event = handle_event(event); if(event) return event; } break; case JB_DROP: iax_event_free((struct iax_event *)frame.data); break; case JB_NOFRAME: case JB_EMPTY: /* do nothing */ break; default: /* shouldn't happen */ break; } } /* Now look for networking events */ if (blocking) { /* Block until there is data if desired */ fd_set fds; int nextEventTime; FD_ZERO(&fds); FD_SET(netfd, &fds); nextEventTime = iax_time_to_next_event(); if(nextEventTime < 0) select(netfd + 1, &fds, NULL, NULL, NULL); else { struct timeval nextEvent; nextEvent.tv_sec = nextEventTime / 1000; nextEvent.tv_usec = (nextEventTime % 1000) * 1000; select(netfd + 1, &fds, NULL, NULL, &nextEvent); } } event = iax_net_read(); return handle_event(event); } struct sockaddr_in iax_get_peer_addr(struct iax_session *session) { return session->peeraddr; } void iax_session_destroy(struct iax_session **session) { destroy_session(*session); *session = NULL; } void iax_event_free(struct iax_event *event) { /* We gave the user a chance to play with the session now we need to * destroy it if you are not calling this function on every event you * read you are now going to leak sessions as well as events! */ switch(event->etype) { case IAX_EVENT_REJECT: case IAX_EVENT_HANGUP: /* Destroy this session -- it's no longer valid */ if (event->session) { /* maybe the user did it already */ destroy_session(event->session); } break; } free(event); } int iax_get_fd(void) { /* Return our network file descriptor. The client can select on this * (probably with other things, or can add it to a network add sort * of gtk_input_add for example */ return netfd; } int iax_quelch_moh(struct iax_session *session, int MOH) { struct iax_ie_data ied; //IE Data Structure (Stuff To Send) memset(&ied, 0, sizeof(ied)); // You can't quelch the quelched if (session->quelch == 1) return -1; if (MOH) { iax_ie_append(&ied, IAX_IE_MUSICONHOLD); session->transfer_moh = 1; } return send_command(session, AST_FRAME_IAX, IAX_COMMAND_QUELCH, 0, ied.buf, ied.pos, -1); }