SIP-Anruf auswerten

Hallo, ich habe diesen Sketch für einen SIP Anruf über ESP 8266 und Fritzbox.
Kann man da jetzt auswerten bzw abfragen ob der Anruf angekommen ist, angenommen wurde oder weggedrückt wurde??

// https://www.mikrocontroller.net/topic/444994?goto=new#new

/*====================================================================

   Copyright (c) 2018 Juerge Liegner  All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   1. Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the
      distribution.

   3. Neither the name of the author(s) nor the names of any contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS "AS IS" AND
   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   SUCH DAMAGE.

   ====================================================================*/

#include <ESP8266WiFi.h>
#include <WifiUdp.h>

//------------------------------------------------
// configuration with fix ip
//------------------------------------------------

// wlan param
const char *SSID = "";          
const char *WLANKEY = "";  

// sip params
const char *sipip = "192.168.0.100";
int sipport = 5060;
const char *sipuser = "Testanruf";   // angelegter Nutzername in der Fritzbox
const char *sippasswd = "Test3433";  // PW vom angelegter Nutzername

// dial params
//const char *sipdialnr = "**611";
const char *sipdialnr = "";
const char *sipdialtext = "Testanruf";  // Text auf Display

// network params
const char *ip = "192.168.0.154";  // ESP IP Adresse eintragen
const char *gw = "192.168.0.100";
const char *mask = "255.255.255.0";
const char *dns = "192.168.0.100";
//------------------------------------------------

#define DEBUGLOG
WiFiUDP Udptel;
static byte flag;
static unsigned long zeit;

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// hardware and api independent Sip class
//
/////////////////////////////////////////////////////////////////////////////////////////////////////

class Sip {
  char *pbuf;
  size_t lbuf;
  char caRead[256];

  const char *pSipIp;
  int iSipPort;
  const char *pSipUser;
  const char *pSipPassWd;
  const char *pMyIp;
  int iMyPort;
  const char *pDialNr;
  const char *pDialDesc;

  uint32_t callid;
  uint32_t tagid;
  uint32_t branchid;

  uint32_t iAuthCnt;  // original 'int'
  uint32_t iRingTime;
  uint32_t iMaxTime;
  uint32_t iDialRetries;  // original 'int'
  int iLastCSeq;
  void AddSipLine(const char *constFormat, ...);
  bool AddCopySipLine(const char *p, const char *psearch);
  bool ParseParameter(char *dest, int destlen, const char *name, const char *line, char cq = '\"');
  bool ParseReturnParams(const char *p);
  int GrepInteger(const char *p, const char *psearch);
  void Ack(const char *pIn);
  void Cancel(int seqn);
  void Bye(int cseq);
  void Ok(const char *pIn);
  void Invite(const char *pIn = 0);

  uint32_t Millis();
  uint32_t Random();
  int SendUdp();
  void MakeMd5Digest(char *pOutHex33, char *pIn);

public:
  Sip(char *pBuf, size_t lBuf);
  void Init(const char *SipIp, int SipPort, const char *MyIp, int MyPort, const char *SipUser, const char *SipPassWd, int MaxDialSec = 10);  //10
  void HandleUdpPacket(const char *p);
  bool Dial(const char *DialNr, const char *DialDesc = "");
  bool IsBusy() {
    return iRingTime != 0;
  }
};

Sip::Sip(char *pBuf, size_t lBuf) {
  pbuf = pBuf;
  lbuf = lBuf;
  pDialNr = "";
  pDialDesc = "";
}

bool Sip::Dial(const char *DialNr, const char *DialDesc) {
  if (iRingTime)
    return false;

  iDialRetries = 0;
  pDialNr = DialNr;
  pDialDesc = DialDesc;
  Invite();
  iDialRetries++;
  iRingTime = Millis();
  return true;
}

void Sip::Cancel(int cseq) {
  if (caRead[0] == 0)
    return;
  pbuf[0] = 0;
  AddSipLine("%s sip:%s@%s SIP/2.0", "CANCEL", pDialNr, pSipIp);
  AddSipLine("%s", caRead);
  AddSipLine("CSeq: %i %s", cseq, "CANCEL");
  AddSipLine("Max-Forwards: 70");
  AddSipLine("User-Agent: sip-client/0.0.1");
  AddSipLine("Content-Length: 0");
  AddSipLine("");
  SendUdp();
}

void Sip::Bye(int cseq) {
  if (caRead[0] == 0)
    return;
  pbuf[0] = 0;
  AddSipLine("%s sip:%s@%s SIP/2.0", "BYE", pDialNr, pSipIp);
  AddSipLine("%s", caRead);
  AddSipLine("CSeq: %i %s", cseq, "BYE");
  AddSipLine("Max-Forwards: 70");
  AddSipLine("User-Agent: sip-client/0.0.1");
  AddSipLine("Content-Length: 0");
  AddSipLine("");
  SendUdp();
}

void Sip::Ack(const char *p) {
  char ca[32];
  bool b = ParseParameter(ca, (int)sizeof(ca), "To: <", p, '>');
  if (!b)
    return;

  pbuf[0] = 0;
  AddSipLine("ACK %s SIP/2.0", ca);
  AddCopySipLine(p, "Call-ID: ");
  int cseq = GrepInteger(p, "\nCSeq: ");
  AddSipLine("CSeq: %i ACK", cseq);
  AddCopySipLine(p, "From: ");
  AddCopySipLine(p, "Via: ");
  AddCopySipLine(p, "To: ");
  AddSipLine("Content-Length: 0");
  AddSipLine("");
  SendUdp();
}

void Sip::Ok(const char *p) {
  pbuf[0] = 0;
  AddSipLine("SIP/2.0 200 OK");
  AddCopySipLine(p, "Call-ID: ");
  AddCopySipLine(p, "CSeq: ");
  AddCopySipLine(p, "From: ");
  AddCopySipLine(p, "Via: ");
  AddCopySipLine(p, "To: ");
  AddSipLine("Content-Length: 0");
  AddSipLine("");
  SendUdp();
}

void Sip::Init(const char *SipIp, int SipPort, const char *MyIp, int MyPort, const char *SipUser, const char *SipPassWd, int MaxDialSec) {
  caRead[0] = 0;
  pbuf[0] = 0;
  pSipIp = SipIp;
  iSipPort = SipPort;
  pSipUser = SipUser;
  pSipPassWd = SipPassWd;
  pMyIp = MyIp;
  iMyPort = MyPort;
  iAuthCnt = 0;
  iRingTime = 0;
  // iMaxTime = MaxDialSec * 2000;  // 1500
  iMaxTime = MaxDialSec * 1500;  // 1500 hier Klingelzeit einstellen
}

void Sip::AddSipLine(const char *constFormat, ...) {
  va_list arglist;
  va_start(arglist, constFormat);
  uint16_t l = (uint16_t)strlen(pbuf);
  char *p = pbuf + l;
  vsnprintf(p, lbuf - l, constFormat, arglist);
  va_end(arglist);
  l = (uint16_t)strlen(pbuf);
  if (l < (lbuf - 2)) {
    pbuf[l] = '\r';
    pbuf[l + 1] = '\n';
    pbuf[l + 2] = 0;
  }
}

// call invite without or with the response from peer
void Sip::Invite(const char *p) {
  // prevent loops
  if (p && iAuthCnt > 3)
    return;

  // using caRead for temp. store realm and nonce
  char *caRealm = caRead;
  char *caNonce = caRead + 128;

  char *haResp = 0;
  int cseq = 1;
  if (!p) {
    iAuthCnt = 0;
    if (iDialRetries == 0) {
      callid = Random();
      tagid = Random();
      branchid = Random();
    }
  } else {
    cseq = 2;
    if (ParseParameter(caRealm, 128, " realm=\"", p)
        && ParseParameter(caNonce, 128, " nonce=\"", p)) {
      // using output buffer to build the md5 hashes
      // store the md5 haResp to end of buffer
      char *ha1Hex = pbuf;
      char *ha2Hex = pbuf + 33;
      haResp = pbuf + lbuf - 34;
      char *pTemp = pbuf + 66;

      snprintf(pTemp, lbuf - 100, "%s:%s:%s", pSipUser, caRealm, pSipPassWd);
      MakeMd5Digest(ha1Hex, pTemp);

      snprintf(pTemp, lbuf - 100, "INVITE:sip:%s@%s", pDialNr, pSipIp);
      MakeMd5Digest(ha2Hex, pTemp);

      snprintf(pTemp, lbuf - 100, "%s:%s:%s", ha1Hex, caNonce, ha2Hex);
      MakeMd5Digest(haResp, pTemp);
    } else {
      caRead[0] = 0;
      return;
    }
  }
  pbuf[0] = 0;
  AddSipLine("INVITE sip:%s@%s SIP/2.0", pDialNr, pSipIp);
  AddSipLine("Call-ID: %010u@%s", callid, pMyIp);
  AddSipLine("CSeq: %i INVITE", cseq);
  AddSipLine("Max-Forwards: 70");
  // not needed for fritzbox
  // AddSipLine("User-Agent: sipdial by jl");
  AddSipLine("From: \"%s\"  <sip:%s@%s>;tag=%010u", pDialDesc, pSipUser, pSipIp, tagid);
  AddSipLine("Via: SIP/2.0/UDP %s:%i;branch=%010u;rport=%i", pMyIp, iMyPort, branchid, iMyPort);
  AddSipLine("To: <sip:%s@%s>", pDialNr, pSipIp);
  AddSipLine("Contact: \"%s\" <sip:%s@%s:%i;transport=udp>", pSipUser, pSipUser, pMyIp, iMyPort);
  if (p) {
    // authentication
    AddSipLine("Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"sip:%s@%s\", response=\"%s\"", pSipUser, caRealm, caNonce, pDialNr, pSipIp, haResp);
    iAuthCnt++;
  }
  AddSipLine("Content-Type: application/sdp");
  // not needed for fritzbox
  // AddSipLine("Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO");
  AddSipLine("Content-Length: 0");
  AddSipLine("");
  caRead[0] = 0;
  SendUdp();
}

// parse parameter value from http formated string
bool Sip::ParseParameter(char *dest, int destlen, const char *name, const char *line, char cq) {
  const char *qp;
  const char *r;
  if ((r = strstr(line, name)) != NULL) {
    r = r + strlen(name);
    qp = strchr(r, cq);
    int l = qp - r;
    if (l < destlen) {
      strncpy(dest, r, l);
      dest[l] = 0;
      return true;
    }
  }
  return false;
}

// search a line in response date (p) and append on
// pbuf
bool Sip::AddCopySipLine(const char *p, const char *psearch) {
  char *pa = strstr((char *)p, psearch);
  if (pa) {
    char *pe = strstr(pa, "\r");
    if (pe == 0)
      pe = strstr(pa, "\n");
    if (pe > pa) {
      char c = *pe;
      *pe = 0;
      AddSipLine("%s", pa);
      *pe = c;
      return true;
    }
  }
  return false;
}

int Sip::GrepInteger(const char *p, const char *psearch) {
  int param = -1;
  const char *pc = strstr(p, psearch);
  if (pc) {
    param = atoi(pc + strlen(psearch));
  }
  return param;
}

// copy Call-ID, From, Via and To from response
// to caRead
// using later for BYE or CANCEL the call
bool Sip::ParseReturnParams(const char *p) {
  pbuf[0] = 0;
  AddCopySipLine(p, "Call-ID: ");
  AddCopySipLine(p, "From: ");
  AddCopySipLine(p, "Via: ");
  AddCopySipLine(p, "To: ");
  if (strlen(pbuf) >= 2) {
    strcpy(caRead, pbuf);
    caRead[strlen(caRead) - 2] = 0;
  }
  return true;
}

void Sip::HandleUdpPacket(const char *p) {
  uint32_t iWorkTime = iRingTime ? (Millis() - iRingTime) : 0;
  if (iRingTime && iWorkTime > iMaxTime) {
    // Cancel(3);
    Bye(3);
    iRingTime = 0;
  }

  if (!p) {
    // max 5 dial retry when loos first invite packet
    //     int                int              uint32         int
    if (iAuthCnt == 0 && iDialRetries < 5 && (iWorkTime > (iDialRetries * 200))) {
      iDialRetries++;
      delay(30);
      Invite();
    }
    return;
  }

  if (strstr(p, "SIP/2.0 401 Unauthorized") == p) {
    Ack(p);
    // call Invite with response data (p) to build auth md5 hashes
    Invite(p);
  } else if (strstr(p, "BYE") == p) {
    Ok(p);
    iRingTime = 0;
  } else if (strstr(p, "SIP/2.0 200") == p)  // OK
  {
    ParseReturnParams(p);
    Ack(p);
  } else if (strstr(p, "SIP/2.0 183 ") == p      // Session Progress
             || strstr(p, "SIP/2.0 180 ") == p)  // Ringing
  {
    ParseReturnParams(p);
  } else if (strstr(p, "SIP/2.0 100 ") == p)  // Trying
  {
    ParseReturnParams(p);
    Ack(p);
  } else if (strstr(p, "SIP/2.0 486 ") == p      // Busy Here
             || strstr(p, "SIP/2.0 603 ") == p   // Decline
             || strstr(p, "SIP/2.0 487 ") == p)  // Request Terminatet
  {
    Ack(p);
    iRingTime = 0;
  } else if (strstr(p, "INFO") == p) {
    iLastCSeq = GrepInteger(p, "\nCSeq: ");
    Ok(p);
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// hardware dependent interface functions
//
/////////////////////////////////////////////////////////////////////////////////////////////////////

int Sip::SendUdp() {
  Udptel.beginPacket(pSipIp, iSipPort);
  Udptel.write(pbuf, strlen(pbuf));
  Udptel.endPacket();
#ifdef DEBUGLOG
  Serial.printf("\r\n----- send %i bytes -----------------------\r\n%s", strlen(pbuf), pbuf);
  Serial.printf("------------------------------------------------\r\n");
#endif
  return 0;
}

// generate a 30 bit random number
uint32_t Sip::Random() {
  // return ((((uint32_t)rand())&0x7fff)<<15) + ((((uint32_t)rand())&0x7fff));
  return secureRandom(0x3fffffff);
}

uint32_t Sip::Millis() {
  return (uint32_t)millis() + 1;
}

void Sip::MakeMd5Digest(char *pOutHex33, char *pIn) {
  MD5Builder aMd5;
  aMd5.begin();
  aMd5.add(pIn);
  aMd5.calculate();
  aMd5.getChars(pOutHex33);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Arduino setup() and loop()
//
/////////////////////////////////////////////////////////////////////////////////////////////////////

char caSipIn[2048];
char caSipOut[2048];
Sip aSip(caSipOut, sizeof(caSipOut));

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(false);
  delay(10);

  WiFi.disconnect(true);
  delay(10);
  WiFi.mode(WIFI_STA);
  delay(10);

  Serial.print("\r\n\r\n");
  Serial.printf("Connecting to %s\r\n", SSID);

  IPAddress Ip;
  IPAddress Gw;
  IPAddress Mask;
  IPAddress Dns;

  Ip.fromString(ip);
  Gw.fromString(gw);
  Mask.fromString(mask);
  Dns.fromString(dns);
  WiFi.config(Ip, Gw, Mask, Dns);
  Serial.print("Wifi begin...\r\n");
  WiFi.begin(SSID, WLANKEY);
  int i = 0;
  for (i = 0; i < 100; i++) {
    if (WiFi.status() == WL_CONNECTED)
      break;
    delay(100);
    Serial.print(".");
  }
  WiFi.persistent(true);
  Serial.printf("\r\nWiFi connected to: %s\r\n", WiFi.localIP().toString().c_str());
  Udptel.begin(sipport);
  aSip.Init(sipip, sipport, ip, sipport, sipuser, sippasswd, 15);
}

void loop(void) {
  if (flag == 0) {
    flag = 1;
    aSip.Dial(sipdialnr, sipdialtext);
    // delay(5000);
  }
  int packetSize = Udptel.parsePacket();
  if (packetSize > 0) {

    caSipIn[0] = 0;
    packetSize = Udptel.read(caSipIn, sizeof(caSipIn));
    if (packetSize > 0) {
      caSipIn[packetSize] = 0;
#ifdef DEBUGLOG
      IPAddress remoteIp = Udptel.remoteIP();
      Serial.printf("\r\n----- read %i bytes from: %s:%i ----\r\n", (int)packetSize, remoteIp.toString().c_str(), Udptel.remotePort());
      Serial.print(caSipIn);
      Serial.printf("----------------------------------------------------\r\n");
#endif
    }
  }
  aSip.HandleUdpPacket((packetSize > 0) ? caSipIn : 0);
}

Vorlage dürfte dafür

werden.
mit strstr(p ..... wird ja schon nach den Statuscodes gesucht.
Such Dir was aus, nach was Du suchen willst:

und reagiere da drauf.

Danke das wars, irgendwie bin ich da nicht draufgekommen....
Aber leider funktionieren manche Codes nur bei internen Anrufen, zb der Code für Anruf abgelehnt.

Supi und danke fürs rückmelden.

Dann übermittelt das Gateway die evtl. nicht vollständig.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.