📄 iax.c
字号:
/* * libiax: An implementation of Inter-Asterisk eXchange * * Copyright (C) 2001, Linux Support Services, Inc. * * Mark Spencer <markster@linux-support.net> * * This program is free software, distributed under the terms of * the GNU Lesser (Library) General Public License */ #ifdef WIN32#include <string.h>#include <process.h>#include <windows.h>#include <winsock.h>#include <time.h>#include <stdlib.h>#include <malloc.h>#include <stdarg.h>#include <stdio.h>#include <fcntl.h>#include <io.h>#include <errno.h>#include <winpoop.h>#else#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/time.h>#include <stdlib.h>#include <string.h>#include <malloc.h>#include <stdarg.h>#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <error.h>#include <sys/select.h>#include <netinet/in.h>#include <arpa/inet.h>#include <time.h>#endif#include "frame.h" #include "iax-client.h"#include "iax.h"#include "md5.h"#ifdef SNOM_HACK/* The snom phone seems to improperly execute memset in some cases */#define MEMSET snom_memsetstatic void *snom_memset(void *s, int c, size_t n){ char *sc = s; size_t x; for (x=0;x<n;x++) sc[x] = c; return s;}#else#define MEMSET memset#endif#define IAX_EVENT_REREQUEST 999#define IAX_EVENT_TXREPLY 1000#define IAX_EVENT_TXREJECT 1001#define IAX_EVENT_TXACCEPT 1002#define IAX_EVENT_TXREADY 1003/* Define Voice Smoothing to try to make some judgements and adjust timestamps on incoming packets to what they "ought to be" */#define VOICE_SMOOTHING#undef VOICE_SMOOTHING/* Define Drop Whole Frames to make IAX shrink its jitter buffer by dropping entire frames rather than simply delivering them faster. Dropping encoded frames, before they're decoded, usually leads to better results than dropping decoded frames. */#define DROP_WHOLE_FRAMES#define MIN_RETRY_TIME 10#define MAX_RETRY_TIME 10000#define MEMORY_SIZE 100#define TRANSFER_NONE 0#define TRANSFER_BEGIN 1#define TRANSFER_READY 2/* No more than 4 seconds of jitter buffer */static int max_jitterbuffer = 4000;/* No more than 50 extra milliseconds of jitterbuffer than needed */static int max_extra_jitterbuffer = 50;/* To use or not to use the jitterbuffer */static int iax_use_jitterbuffer = 1;/* UDP Socket (file descriptor) */static int netfd = -1;/* Max timeouts */static int maxretries = 10;/* Dropcount (in per-MEMORY_SIZE) usually percent */static int iax_dropcount = 3;struct iax_session { /* Is voice quelched (e.g. hold) */ int quelch; /* Last received voice format */ int voiceformat; /* Last transmitted voice format */ int svoiceformat; /* Last received timestamp */ unsigned int last_ts; /* Last transmitted timestamp */ unsigned int lastsent; /* Last transmitted voice timestamp */ unsigned int lastvoicets; /* 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 last sent sequence number */ unsigned short oseqno; /* Our last received incoming sequence number */ unsigned short iseqno; /* Peer supported formats */ int peerformats; /* Time value that we base our transmission on */ struct timeval offset; /* Time value we base our delivery on */ struct timeval rxcore; /* History of lags */ int history[MEMORY_SIZE]; /* Current base jitterbuffer */ int jitterbuffer; /* Informational jitter */ int jitter; /* Measured lag */ int lag; /* Current link state */ int state; /* Peer name */ char peer[MAXSTRLEN]; /* Default Context */ char context[MAXSTRLEN]; /* Caller ID if available */ char callerid[MAXSTRLEN]; /* DNID */ char dnid[MAXSTRLEN]; /* Requested Extension */ char exten[MAXSTRLEN]; /* Expected Username */ char username[MAXSTRLEN]; /* Expected Secret */ char secret[MAXSTRLEN]; /* permitted authentication methods */ char methods[MAXSTRLEN]; /* MD5 challenge */ char challenge[12];#ifdef VOICE_SMOOTHING unsigned int lastts;#endif /* Refresh if applicable */ int refresh; /* Transfer stuff */ struct sockaddr_in transfer; int transferring; int transfercallno; /* For linking if there are multiple connections */ struct iax_session *next;};struct iax_frame { /* Information for where to send this */ struct iax_session *session; /* What to send */ void *data; /* How long is it */ int datalen; /* How many retries have we done? */ int retries; /* How long to wait for a retry? */ int retrytime; /* Is this a special transfer packet? */ int transferpacket; /* Easy Linking */ struct iax_frame *next;};#ifdef WIN32void gettimeofday(struct timeval *tv, struct timezone *tz);#define snprintf _snprintf#endifchar iax_errstr[256];static int sformats = 0;#define IAXERROR snprintf(iax_errstr, sizeof(iax_errstr), #ifdef DEBUG_SUPPORT#ifdef DEBUG_DEFAULTstatic int debug = 1;#elsestatic int debug = 0;#endif/* This is a little strange, but to debug you call DEBU(G "Hello World!\n"); */ \#ifdef WIN32#define G __FILE__, __LINE__,#else#define G __FILE__, __LINE__, __PRETTY_FUNCTION__, #endif#define DEBU __debug #ifdef WIN32static 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;}#elsestatic 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 */#ifdef WIN32#define DEBU#else#define DEBU(...)#endif#define G#endifstruct 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; /* Easy linking */ struct iax_sched *next;};#ifdef WIN32void bzero(void *b, size_t len){ MEMSET(b,0,len);}#endifstatic struct iax_sched *schedq = NULL;static struct iax_session *sessions = NULL;static int callnums = 1;static int iax_sched_event(struct iax_event *event, struct iax_frame *frame, 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) { DEBU(G "No event, no frame? what are we scheduling?\n"); return -1; } sched = (struct iax_sched*)malloc(sizeof(struct iax_sched)); bzero(sched, sizeof(struct iax_sched)); if (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; /* 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; }}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) { MEMSET(s, 0, sizeof(struct iax_session)); /* Initialize important fields */ s->voiceformat = -1; s->svoiceformat = -1; /* Default pingtime to 30 ms */ s->pingtime = 30; /* 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 = -1; s->next = sessions; 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;}static int calc_timestamp(struct iax_session *session, unsigned int ts){ int ms; struct timeval tv; /* 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) 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; /* Never send a packet with the same timestamp since timestamps can be used to acknowledge certain packets */ if ((unsigned) ms <= session->lastsent) ms = session->lastsent + 1; /* Record the last sent packet for future reference */ session->lastsent = ms; return ms;}#ifdef DEBUG_SUPPORTvoid showframe(struct iax_frame *f, struct iax_full_hdr *fhi, int rx){ char *frames[] = { "(0?)", "DTMF ", "VOICE ", "VIDEO ", "CONTROL", "NULL ", "IAX ", "TEXT ", "IMAGE ", "HTML "}; char *iaxs[] = { "(0?)", "NEW ", "PING ", "PONG ", "ACK ", "HANGUP ", "REJECT ", "ACCEPT ", "AUTHREQ", "AUTHREP", "INVAL ", "LAGRQ ", "LAGRP ", "REGREQ ", "REGAUTH", "REGACK ", "REGREJ ", "REGREL ", "VNAK ", "DPREQ ", "DPREP ", "DIAL ", "TXREQ ", "TXCNT ", "TXACC ", "TXREADY", "TXREL ", "TXREJ " }; char *cmds[] = { "(0?)", "HANGUP ", "RING ", "RINGING", "ANSWER ", "BUSY ",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -