Anruf abholen statt zustellen: Wie QC-Phone Plus eingehende SIP-Anrufe auf ein schlafendes iPhone bringt
Von Robert Brandt, QuadChroma
Veröffentlicht am . Stand: Entwurf. QC-Phone Plus ist in Entwicklung und noch nicht verfügbar.
Das Problem
QC-Phone ist eine SIP-Telefon-App für iPhone und iPad. Sie arbeitet mit dem SIP-Konto, das der Nutzer schon hat. Eine solche App kann im Hintergrund nicht mit dem SIP-Anbieter verbunden bleiben: iOS legt sie nach kurzer Zeit schlafen, und eine schlafende App empfängt kein SIP-INVITE.
Damit Anrufe ein schlafendes Telefon erreichen, muss etwas im Netz den Anruf stellvertretend empfangen und das Telefon wecken. Das übernimmt der optionale Dienst QC-Phone Plus.
Üblich ist, den Anruf zuzustellen. Ein Gateway empfängt ihn, weckt die App, wartet, bis sie sich neu verbunden oder angemeldet hat, und leitet den Anruf dann an die App weiter.
Wir bauen es umgekehrt. Unser Server stellt dem Telefon nie einen Anruf zu. Er hält den Anruf fest und sagt dem Telefon nur, wer anruft. Nimmt der Nutzer an, holt das Telefon den Anruf selbst ab.
Dieser Artikel beschreibt den Entwurf so genau, dass man ihn nachbauen kann. Wir veröffentlichen ihn, damit ihn alle nutzen können.
Die Beteiligten
- Der SIP-Anbieter, den der Nutzer schon hat. Dort ändert sich nichts.
- Der Plus-Server. Er hält das SIP-Konto angemeldet und hält eingehende Anrufe fest. Mit Ton hat er nie zu tun.
- Der Push-Dienst des Betriebssystems. Unter iOS sind das VoIP-Pushes über Apples Push-Dienst (PushKit).
- Optional zu Hause: eine dauerhafte Verbindung, die das Betriebssystem in vom Nutzer ausgewählten WLANs offen hält (unter iOS: Local Push Connectivity).
- Die App QC-Phone auf iPhone oder iPad.
Einrichtung
- Der Nutzer schaltet Plus in der App ein. Die App schickt dem Server über HTTPS:
- den Registrar des Anbieters und den SIP-Benutzernamen;
- einen Passwort-Hash (HA1), nie das Passwort selbst;
- ihren Push-Token;
- einen auf dem Gerät erzeugten öffentlichen Schlüssel.
- Der Server teilt der App einmalig mit, welchen Servernamen sie nutzen soll. Die App speichert ihn. Ausfallsicherheit gibt es über DNS unter demselben Namen.
- Der Server meldet das SIP-Konto beim Anbieter mit seiner eigenen Kontaktadresse an, über TLS, wo der Anbieter es unterstützt. Es gibt eine Anmeldung pro SIP-Konto, auch wenn das Abo auf mehreren Geräten genutzt wird.
- Die App hält keine Anmeldung und keine Verbindung zum Server. Das gilt im Vordergrund wie im Hintergrund. Die App teilt dem Server nie mit, ob sie im Vorder- oder Hintergrund ist.
- Im Plus-Modus meldet sich die App auch nicht selbst beim Anbieter an, der Anbieter sieht also immer nur eine Anmeldung. Ausgehende Anrufe gehen direkt von der App zum Anbieter und nie über den Server.
Ein eingehender Anruf, Schritt für Schritt
- Der Anbieter schickt ein INVITE an den Server. Der Server antwortet vorläufig (100 Trying, 180 Ringing), damit der Anrufer ein Freizeichen hört, und hält das INVITE im Arbeitsspeicher fest. Pro Konto hält er höchstens einen Anruf gleichzeitig. Ein zweiter Anruf in dieser Zeit bekommt 486 Busy Here.
- Der Server kündigt den Anruf jedem aktiven Gerät an, genau einmal pro Gerät. Die Ankündigung ist mit dem öffentlichen Schlüssel des Geräts Ende-zu-Ende-verschlüsselt (HPKE, RFC 9180). Sie enthält nur, was das Telefon zum Anzeigen des Anrufs braucht: Rufnummer und Anzeigenamen des Anrufers sowie den Sendezeitpunkt. Sie enthält keine Serveradresse, keine Kennung des Anrufs, keine Sitzungsbeschreibung und keine Zugangsdaten. Der Push-Dienst sieht nur, dass eine Nachricht an ein Gerät ging.
- Das Telefon klingelt. Unter iOS meldet es den Anruf sofort an CallKit. Bis der Nutzer annimmt, sendet die App nichts ins Netz.
- Der Nutzer tippt auf „Annehmen“. Erst jetzt verbindet sich die App über HTTPS mit dem Servernamen aus der Einrichtung und authentisiert sich mit ihrem Geräteschlüssel. Sie fragt nach dem Anruf, der für ihr Konto gehalten wird, erhält die Sitzungsbeschreibung des Anbieters (das SDP-Angebot) und schickt ihre SDP-Antwort zurück. Dafür braucht sie nichts aus der Ankündigung: Der Server findet den Anruf über das Konto.
- Der Server beantwortet das festgehaltene INVITE mit 200 OK und der SDP-Antwort des Telefons. Der Anbieter schickt ACK.
- Enthielt das INVITE des Anbieters kein SDP, schickt das Telefon stattdessen ein Angebot. Der Server setzt es ins 200 OK, und die Antwort des Anbieters aus dem ACK erreicht das Telefon über HTTPS.
- Der Ton fließt direkt zwischen Anbieter und Telefon, mit SRTP verschlüsselt, wo der Anbieter es unterstützt. Der Server reicht das SDP unverändert durch und leitet nie Medien weiter. Auf dem Telefon bleibt der Anruf von Anfang bis Ende ein eingehender Anruf.
- Zwischen Telefon und Server gibt es überhaupt kein SIP, nur HTTPS. Den SIP-Dialog mit dem Anbieter hält allein der Server.
Während des Gesprächs
- Nach dem Annehmen hält das Telefon eine HTTPS-Statusabfrage offen (Long-Poll). Sie meldet, wenn der Anbieter auflegt oder ein neues Angebot schickt.
- Das Telefon nutzt kurze HTTPS-Anfragen:
- zum Auflegen: Der Server schickt BYE an den Anbieter;
- für ein neues SDP-Angebot, wenn es den Anruf hält oder das Netz wechselt: Der Server schickt re-INVITE an den Anbieter und gibt die Antwort zurück;
- um ein neues Angebot des Anbieters zu beantworten;
- um DTMF bei Bedarf als SIP-INFO zu senden. DTMF im Ton (RFC 4733) läuft ohnehin mit dem Ton mit.
- Sitzungsauffrischungen gegenüber dem Anbieter übernimmt der Server.
- Bleibt die Statusabfrage des Telefons länger als 30 Sekunden weg, beendet der Server das Gespräch, damit kein Anruf hängen bleibt.
- Anfragen, die den Anruf erweitern würden, etwa REFER, lehnt der Server ab.
Wenn der Anruf nicht angenommen wird
- Ablehnen: Die App schickt eine HTTPS-Anfrage, und der Server lehnt das festgehaltene INVITE mit 603 Decline ab.
- Der Anrufer legt auf, ein anderes Gerät nimmt an oder die Zeit läuft ab: Der Server schickt jedem Gerät eine zweite, stille Nachricht ohne jede Kennung. Das Telefon hört auf zu klingeln und meldet sich nicht beim Server.
- Der Nutzer nimmt zu spät an: Der Server antwortet, dass für das Konto nichts gehalten wird, und das Telefon schließt die Anrufansicht.
- Keine Annahme: Der Server hält den Anruf, solange der Anbieter ihn klingeln lässt. Die Klingelzeit des Anbieters beendet ihn mit CANCEL, und dessen Mailbox oder Weiterleitung übernimmt. Eine eigene Frist setzt der Server nicht; ein Schutz-Timer von einigen Minuten räumt nur auf und lehnt den Anruf dann mit 480 ab.
- Der Push-Dienst meldet einen ungültigen Geräte-Token: Der Server lehnt den Anruf sofort ab, damit der Anrufer nicht unnötig wartet.
Mehrere Geräte
Alle aktiven Geräte eines Abos erfahren von dem Anruf. Das Gerät, auf dem zuerst angenommen wird, bekommt den Anruf. Die anderen Geräte erhalten die stille Nachricht und hören auf zu klingeln. Ein Abo kann gleichzeitig auf einer begrenzten Zahl von Geräten aktiv sein.
Zu Hause ohne Push-Dienst
In WLANs, die der Nutzer auswählt, zum Beispiel im Heimnetz, kommt das Telefon ohne den Push-Dienst aus:
- Eine Netzwerk-Erweiterung des Betriebssystems (unter iOS: Local Push Connectivity) hält eine dauerhafte, verschlüsselte Verbindung zum Server, solange das Telefon in diesem Netz ist.
- Der Server kündigt den Anruf über diese Verbindung an statt über den Push-Dienst, ebenfalls nur mit Anzeigedaten.
- Alles Weitere bleibt gleich: Das Telefon klingelt, und nach dem Annehmen holt es den Anruf über HTTPS ab.
- Pro Anruf und Gerät wird genau ein Weg genutzt: die dauerhafte Verbindung, wenn sie in dem Moment besteht, sonst der Push-Dienst. Nie beide.
In der App
Der vorhandene SIP-Stack der App kann unverändert bleiben. Ein kleiner Adapter in der App stellt ihm den abgeholten Anruf über die Loopback-Schnittstelle als normalen eingehenden SIP-Anruf dar. Der Adapter setzt Annehmen, Auflegen und neue Angebote des Stacks in die HTTPS-Anfragen oben um. Zum Server verlässt nur HTTPS das Telefon.
Sicherheit und Datenschutz
- Die Ankündigung ist Ende-zu-Ende-verschlüsselt, mit einem Schlüssel, den es nur auf dem Gerät gibt.
- Jede HTTPS-Anfrage ist mit dem Geräteschlüssel authentisiert. Der Servername stammt aus der Einrichtung, nie aus einer Nachricht.
- Die Signalisierung zum Anbieter läuft über TLS, der Ton über SRTP, jeweils wo der Anbieter es unterstützt.
- Der Server berührt nie Ton. Er hält alles nur im Arbeitsspeicher und schreibt keine Anruflisten und keine Logs mit Rufnummern. Geplante Neustarts übergeben den Zustand von Arbeitsspeicher zu Arbeitsspeicher, dabei geht nichts verloren und nichts wird auf Datenträger geschrieben. Nach einem unerwarteten Neustart sind alle Einrichtungen gelöscht; die App bemerkt das und richtet sich automatisch neu ein.
Vordergrund und Hintergrund sind gleich
Die App ist nie am Server angemeldet. Ein Anruf erreicht eine geöffnete App auf genau demselben Weg wie eine schlafende: Ankündigung, Klingeln, Annehmen, Abholen. Es gibt nur einen Weg, den man bauen, testen und am Laufen halten muss.
Warum das zuverlässig ist
- Im Ruhezustand gibt es keine Verbindung zwischen Telefon und Server, die am Leben gehalten werden muss, und keine, die unbemerkt abreißen kann. Die optionale Verbindung zu Hause hält das Betriebssystem.
- Das Telefon hat keine Anmeldung auf dem Server, die ablaufen oder veralten kann.
- Jeder eingehende Anruf nimmt denselben Weg, egal in welchem Zustand die App ist, und mit jedem Anbieter: Der Anbieter sieht nur einen ganz normalen angenommenen Anruf.
- Die Ankündigung trägt nichts, wovon der Anruf abhängt. Selbst wenn ihr Inhalt verloren ginge, könnte der Nutzer annehmen.
- Scheitert vor dem Annehmen etwas, endet der Anruf sauber beim Anbieter, mit Mailbox oder Weiterleitung, statt hängen zu bleiben.
Ablauf: eingehender Anruf über den Push-Dienst
Ablauf: zu Hause über die dauerhafte Verbindung
Varianten
Der Entwurf ist in allen folgenden Varianten Teil dieser Veröffentlichung, einzeln oder in jeder Kombination.
- Wann das Telefon abholt:
- wenn der Nutzer annimmt (Standard);
- gleich nach dem Aufwachen, wobei die Anfrage des Telefons in einem frühen Zustand bleibt und eine spätere Anfrage das Annehmen meldet;
- gleich nach dem Aufwachen mit abgeschlossenem Austausch, wobei der Server die Anbieterseite unbeantwortet lässt, bis das Telefon das Annehmen meldet;
- wobei der Server den Anruf hält, solange der Anbieter ihn klingeln lässt (Standard), oder nur bis zu einer festen eigenen Frist.
- Wie das Telefon abholt:
- HTTPS-Anfragen (Standard) oder WebSocket-Nachrichten;
- ein eigenes SIP-INVITE des Telefons über TLS, TCP, UDP oder WebSocket. Der Server beantwortet es und verbindet es als Back-to-Back User Agent nur für die Signalisierung mit dem festgehaltenen Anruf: Bei verzögertem Angebot des Telefons trägt das 200 OK des Servers das Angebot des Anbieters und das ACK des Telefons die Antwort;
- WebRTC-Signalisierung;
- mit oder ohne Adapter in der App, der die Nachrichten des SIP-Stacks der App in diese Anfragen umsetzt.
- Wie der Server den Anruf findet:
- über das authentisierte Konto oder Gerät (Standard);
- über einen Einmalwert, der in der Ankündigung steht oder mit einem geheimen Serverschlüssel aus den Kennungen des Anrufs abgeleitet wird;
- ein Wert für alle Geräte oder einer pro Gerät.
- Was die Ankündigung trägt:
- nur Anzeigedaten (Standard);
- zusätzlich die Sitzungsbeschreibung, einen Verweis auf den Anruf oder die Serveradresse;
- verschlüsselt mit HPKE oder einem anderen authentifizierten Verfahren, oder nur ein undurchsichtiger Verweis.
- Weg für die Ankündigung:
- der Push-Dienst des Betriebssystems (Standard), zum Beispiel ein Apple-VoIP-Push (PushKit), ein Apple-Hinweis- oder Hintergrund-Push, Firebase Cloud Messaging mit hoher Priorität oder jeder andere Push-Dienst eines Betriebssystems;
- eine dauerhafte Verbindung, die eine Netzwerk-Erweiterung des Betriebssystems in vom Nutzer ausgewählten Netzen hält, zum Beispiel im Heim-WLAN, in einem privaten Mobilfunknetz oder in einem Mobilfunkdienst für Einsatzkräfte;
- genau ein Weg pro Anruf und Gerät, je nachdem, ob die dauerhafte Verbindung gerade besteht.
- Wo das Angebot reist:
- beim Annehmen über HTTPS abgeholt (Standard);
- in der verschlüsselten Ankündigung;
- das Telefon macht das Angebot, und die Antwort des Anbieters erreicht das Telefon über HTTPS;
- Neuverhandlung jederzeit während des Gesprächs.
- Statuskanal:
- vor dem Annehmen keiner, Abbruch per zweiter, stiller Nachricht (Standard);
- HTTPS-Long-Poll, WebSocket, Server-Sent Events oder wiederholte Abfragen, vor oder nach dem Annehmen;
- SIP SUBSCRIBE und NOTIFY.
- Wie Anrufe zum Server kommen:
- der Server meldet das SIP-Konto mit eigener Kontaktadresse an (Standard);
- oder der Anbieter leitet Anrufe ohne Anmeldung an eine SIP-Adresse des Servers weiter (SIP-Trunk oder Rufweiterleitung);
- eine Anmeldung pro Konto für mehrere Geräte, mit einer Ankündigung pro Gerät.
- Mehrere Server:
- das Telefon nutzt einen bei der Einrichtung zugewiesenen Servernamen mit Ausfallsicherheit über DNS (Standard);
- oder die Ankündigung bzw. eine Abfrage nennt den Server, der den Anruf hält;
- oder jeder Server kann über gemeinsamen Arbeitsspeicher-Zustand antworten.
- Gerätegrenze: eine feste Zahl von Geräteplätzen pro Abo. Ein neues Gerät übernimmt den Platz eines Geräts, das der Nutzer auswählt, oder den des am längsten nicht aktiven Geräts.
- Rückruf statt Abholen: Der Server beendet den eingehenden Anruf sofort (zum Beispiel mit 480 oder 486), damit kein Anruf wartet, und schickt eine normale Mitteilung wie „X hat angerufen – zurückrufen“. Tippt der Nutzer darauf, ruft das Telefon den Anrufer mit einem neuen ausgehenden Anruf direkt über den Anbieter zurück, mit dessen üblicher Verschlüsselung. Der Server wird dabei nicht kontaktiert. Dieser Modus lässt sich pro Land oder pro Gerät einschalten, etwa als Ausweichlösung.
- Andere Plattformen: Derselbe Entwurf funktioniert unter Android mit Firebase Cloud Messaging und einem ConnectionService statt CallKit sowie auf Desktop-Systemen.
- Über Telefonate hinaus: Dasselbe Muster funktioniert für Videoanrufe, Push-to-Talk, Konferenzen und jede andere Sitzungsanfrage, die ein schlafendes Gerät annehmen soll.
Lizenz des Textes: CC BY 4.0.
Änderungen
- 27. Oktober 2026: Autorenangabe ergänzt. Der technische Inhalt ist unverändert.
- 30. Oktober 2026: Neues Design des Kopfes. Der Text ist unverändert.