In message <b02efe0454.Alex@xxxxxxxxxxxxxxxxxxxxxxxxxxxx> Alexander Ausserstorfer <bavariasound@xxxxxxxxxxxxxxx> wrote: >>>> Das könnte unsere Messenger Pro Probleme unter SSL beim Senden erklären! >>>> Sollte man Andrew vielleicht mal drauf ansetzen. >>> Ich bin mir nicht sicher, ob der von mir beschriebene Effekt damit zu >>> tun hat, weil man beim Senden das Protokoll SMTP verwendet. Mein >>> Programm holt aber die E-Mail vom Server und verwendet hierbei das >>> Protokoll POP3. >>> >>> Es kann höchstens sein, dass SMTP das gleich gestaltet wie POP3. >> >> Ja, CR LF . CR LF beendet DATA. DATA ist das SMTP Kommando für "jetzt >> will ich Dir die Mail schicken". >> http://de.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#Protokoll > > Ich gehe jetzt mal davon aus, dass dass bei jedem > (Telnet)-Terminalprogramm bei einer "Multiline-Response" (mehrzeiligen > Antwort) so funktioniert. Denn sonst könnte man sich ja nicht einfach > mit einem Terminalprogramm in einen POP3S-Server einloggen. Nein, ich muss diese Aussage bezüglich Telnet-Terminalprogramm leider zurücknehmen. Zumindest hier bei mir macht jedes Programm etwas anderes. So, wie es aussieht, habe ich jetzt den richtigen 'Algorithmus' beisammen. Es handelt sich um folgende Zeilen des kleinen Programmchens: if (ret > 0) { // printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { if ((int)buffer2[ii]==13 && m==0) {m++; d=1;} if ((int)buffer2[ii]==10 && m==1) {m++; d=1;} if ((int)buffer2[ii]==13 && m==3) {m++; d=1;} if ((int)buffer2[ii]!=13 && m==3) {m=0;} if ((int)buffer2[ii]==46 && m==2) {m++; d=1;} if ((int)buffer2[ii]==13 && m==2) {m=1; d=1;} if ((int)buffer2[ii]==10 && m==4) {m=0; ret=0;} if (m < 3) { printf("%c",(buffer2[ii])); } if (d==0) m=0; d=0; } } while (ret!=0); Warum das funktioniert? Ich habe ehrlich gesagt keine Ahnung. Als ich daran herumfummelte, ging es plötzlich. Das war eine Überraschung! Ich muss aber noch überprüfen, ob alles - Zeichen für Zeichen - passt. Bisher sieht es aber danach aus. Ich habe euch den Quellcode noch einmal angehängt. Wer will, kann diesen ja zur Übung mal durch den Compiler jagen und das Programm ausprobieren. Abrufen kann man damit bisher nur eine E-Mail nach Auswahl. Danach muss das Programm neu gestartet werden. Der nächste Schritt soll sein, dass automatisch alle vorhandenen E-Mails abgerufen und auf den Schirm ausgegeben werden. Danach soll's schon in Richtung RISC OS gehen, d. h. die E-Mails sollen dann in einzelne Dateien geschrieben werden (zur Weiterverarbeitung durch ein gesondertes Programm). Wer Fehler finden sollte, bitte bei mir melden! Ciao, Alex' -- http://home.chiemgau-net.de/ausserstorfer/ Sent wirelessly from RISC OS
#ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> // printf, scanf #include <stdlib.h> // Fehlercodes wie EXIT_FAILURE #include <sys/socket.h> // AF_INET, SOCK_STREAM, connect(...) #include <netdb.h> // struct hostent #include <netinet/in.h> // struct sockadd_in #include <gnutls/gnutls.h> // GnuTLS #include <gnutls/x509.h> // GnuTLS x.509-Zertifikate #include <string.h> // strcat(...) #define MAX_BUF 1024 // Groesse des Zwischenspeichers // fuer die verschluesselte // Uebertragung (mit Tunnelung, aber Daten // im Klartext) - POP3S #define MAXMSG 512 // Groesse des Zwischenspeichers // fuer die Klartextuebertragung // fuer die vom Server // uebermittelten Daten (ohne Tunnelung) // für StartTLS // Folgende Datei muss die zur Ueberprüfung // notwendigen Zertifikate (BASE64-codiert!) bereitstellen #define CAFILE "/usr/ssl/certs/E-Mail-Chain.crt" struct hostent *hostinfo; // enthaelt Zuordnung von Host- // Addressen und -namen struct sockaddr_in name; // enthaelt Verbindungsinfor- // mationen des verwendeten Sockets // (Prototyp) Funktion ueberprueft das vom Server geschickte // Zertifikat an Hand des Zertifikatsspeichers static int _verify_certificate_callback(gnutls_session_t session); // (Prototyp) Funktion ermittelt die binaere IP-Nummer aus dem // Hostnamen void IP_number(struct sockaddr_in *name, char *servername, uint16_t port); main() { int ret; // Rueckgabewert Handshake int ii; // Schleifenzaehler für buffer2[ii] int jj; // Innerer Schleifenzähler, damit restliche Zeichen ausgegeben werden. int m=0; // Schrittzähler für <CR><LF><46><CR><LF> -> marktiert Ende einer E-Mail! int d=0; // Merker für vorhergegangenes Zeichen // Folgende Struktur nimmt die fuer eine verschluesselte // Sitzung notwendigen Angaben auf gnutls_session_t session; // Folgende Struktur nimmt die fuer den Client notwendigen // Ausweisungsangaben ("Ausweispapiere") auf: // hier: Benutzername und Passwort // (englisch: Secure Remote Password authentication) gnutls_srp_client_credentials_t srp_cred; // Folgende Struktur speichert die spaeter vom Server // geschickten Ausweispapiere; in diesem Fall ein digitales // Zertifikat: gnutls_certificate_credentials_t cert_cred; char buffer2[MAX_BUF + 1]; // Puffer fuer verschluesselte // Uebertragung (empfangen) const char *err; // enthaelt beim Auftritt eines // Fehlers die Position in der // Zeichenkette; Verwendung // fuer // gnutls_priority_set_direct() // Eingabe der benoetigten Daten char server[30]; printf("Server: "); scanf("%s",server); char user[40]=""; printf("User: "); scanf("%s",user); char USER[50]="USER "; // Zusammenfuegen des POP- strcat(USER,user); // Befehls USER username strcat(USER,"\r\n"); // Anfuegen von <Return> char *password; password=(char *)getpass("Password: "); // Eingabe ohne // Bildschirm- // Ausgabe char PASSWORD[30]="PASS "; // Zusammenfuegen des POP- strcat(PASSWORD,password); // Befehls PASS passwort strcat(PASSWORD,"\r\n"); // Anfuegen von <Return> char buffer[MAXMSG]; // Puffer fuer Nachrichten // (empfangener Klartext) uint16_t port=995; // Portnummer POPS int sock=-1; // Fehlerwert fuer Socket // vordefinieren // size_t size; // IP_number() ermittelt die binaere IP-Nummer aus dem // Servername mittels DNS und schreibt diese in die Struktur // name vom Typ sockaddr_in, um sie fuer das Socket zur // Verfuegung zu stellen. Gleichzeitig wird die Port // Nummer dem Socket bereitgestellt. IP_number(&name, server, port); // Geeignetes Socket fuer Verbindung zum Server einrichten sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); exit (EXIT_FAILURE); } // Mit Host verbinden if (connect(sock, (struct sockaddr *) &name, sizeof (struct sockaddr_in)) < 0) { perror ("connect"); exit (EXIT_FAILURE); } // Servermeldung auslesen // int amount = read(sock, buffer, MAXMSG); // printf("%.*s", amount, buffer); /* Fähigkeiten des Servers abrufen (STARTTLS) write(sock,"CAPA\r\n",6); amount = read(sock, buffer, MAXMSG); printf("%.*s", amount, buffer); // verschluesselten Verbindungsaufbau mit dem Befehl // 'STLS' starten (STARTTLS) write(sock,"STLS\r\n",6); amount = read(sock, buffer, MAXMSG); printf("%.*s", amount, buffer); */ // GnuTLS aktivieren gnutls_global_init(); // Ausweisdatenspeicher des Clients initialisieren gnutls_srp_allocate_client_credentials (&srp_cred); // Ausweisdatenspeicher des Servers (Zertifikat) initialisieren gnutls_certificate_allocate_credentials (&cert_cred); // Zertifikate aus Datei in Zertifikatsspeicher laden // printf("\nAnzahl der verarbeiteten Zertifikate: %d\n", gnutls_certificate_set_x509_trust_file (cert_cred, CAFILE, GNUTLS_X509_FMT_PEM)); // Funktion und Speicher zur Ueberpruefung des vom Server // geschickten Zertifikats festlegen // Diese Funktion wird tatsaechlich erst aufgerufen, nachdem // das Server-Zertifikat empfangen worden ist // Aufruf erfolgt noch vor dem handshake // gnutls_certificate_set_verify_function (cert_cred,_verify_certificate_callback); // Folgende Funktion schreibt Benutzername und Passwort // des Clients in den dafuer eingerichteten Speicher: // gnutls_srp_set_client_credentials (srp_cred, user, password); // Folgende Funktion legt fest, dass das System, auf welchem // das Programm laueft, als CLIENT arbeiten soll. Es // initialisiert die Sitzung: gnutls_init (&session, GNUTLS_CLIENT); // Folgende Funktion schreibt Servername (Hostname) in die // Struktur session: gnutls_session_set_ptr (session, (void *) server); // Folgende Funktion schickt den als Hostname verwendeten // Distinguished Name vor dem handshake an den Server, // damit der Client das richtige Zertifikat erhaelt. // gnutls_server_name_set (session, GNUTLS_NAME_DNS, server, strlen(server)); // Grundeinstellungen der zu bevorzugenden chiper suite fuer // den handshake ret = gnutls_priority_set_direct (session, "NORMAL:+SRP:+SRP-RSA:+SRP-DSS", &err); if (ret < 0) { if (ret == GNUTLS_E_INVALID_REQUEST) { fprintf (stderr, "Fehler an der Stelle: %s\n", err); } exit(1); } // Folgende zwei Zeilen uebernehmen die Ausweisangaben von // Client und Server in die Struktur session: gnutls_credentials_set (session, GNUTLS_CRD_SRP, srp_cred); gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cert_cred); // Folgende Funktion stellt die Verbindung von eingerichtetem // Socket und der aktuellen TLS-Sitzung her: gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sock); // handshake durchfuehren; an dieser Stelle erfolgt der // verschluesselte Verbindungsaufbau do { ret = gnutls_handshake (session); } while (ret < 00 && gnutls_error_is_fatal (ret) == 0); // handshake erfolgreich? if (ret < 0) { fprintf (stderr, "*** Fehler bei der verschluesselten Verbindungsherstellung.\n"); gnutls_perror (ret); goto end; } else { printf ("Herstellung der verschluesselten Verbindung erfolgreich.\n"); } // Auslesen der verschluesselten Serverantwort ret = gnutls_record_recv(session,buffer2,MAX_BUF); if (gnutls_error_is_fatal (ret) != 0 || ret == 0) { if (ret == 0) { printf ("Kommunikationspartner hat die verschluesselte Verbindung abgebrochen.\n"); goto end; } else { fprintf (stderr, "**** Fehler: %s\n", gnutls_strerror(ret)); goto end; } } if (ret > 0) { printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { fputc (buffer2[ii], stdout); } fputs ("\n", stdout); } // Verschluesseltes Senden des POP-Befehls // USER username <Return> gnutls_record_send(session, USER,strlen(USER)); // Auslesen der verschluesselten Serverantwort ret = gnutls_record_recv(session,buffer2,MAX_BUF); if (gnutls_error_is_fatal (ret) != 0 || ret == 0) { if (ret == 0) { printf ("Kommunikationspartner hat die verschluesselte Verbindung abgebrochen.\n"); goto end; } else { fprintf (stderr, "**** Fehler: %s\n", gnutls_strerror(ret)); goto end; } } if (ret > 0) { printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { fputc (buffer2[ii], stdout); } fputs ("\n", stdout); } /* if (!strncmp(buffer2,"+OK",3)) { printf("USER OK\n"); } else { printf("USER LOGIN ERROR\n"); goto end; } */ // Verschluesseltes Senden des POP-Befehls // PASS password <Return> gnutls_record_send(session, PASSWORD,strlen(PASSWORD)); // Auslesen der verschluesselten Serverantwort ret = gnutls_record_recv(session,buffer2,MAX_BUF); if (gnutls_error_is_fatal (ret) != 0 || ret == 0) { if (ret == 0) { printf ("Kommunikationspartner hat die verschluesselte Verbindung abgebrochen.\n"); goto end; } else { fprintf (stderr, "**** Fehler: %s\n", gnutls_strerror(ret)); goto end; } } if (ret > 0) { printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { fputc (buffer2[ii], stdout); } fputs ("\n", stdout); } /* if (!strncmp(buffer2,"+OK",3)) { printf("PASSWORT LOGIN OK\n"); } else { printf("PASSWORT ERROR\n"); goto end; } */ // Verschluesseltes Senden des POP-Befehls // STAT <Return> // gibt Anzahl und Gesamtgröße aller E-Mails an gnutls_record_send(session, "STAT\r\n",6); // Auslesen der verschluesselten Serverantwort ret = gnutls_record_recv(session,buffer2,MAX_BUF); if (gnutls_error_is_fatal (ret) != 0 || ret == 0) { if (ret == 0) { printf ("Kommunikationspartner hat die verschluesselte Verbindung abgebrochen.\n"); goto end; } else { fprintf (stderr, "**** Fehler: %s\n", gnutls_strerror(ret)); goto end; } } if (ret > 0) { printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { fputc (buffer2[ii], stdout); } fputs ("\n", stdout); } /* if (!strncmp(buffer2,"+OK",3)) { printf("STAT OK\n"); } else { printf("STAT ERROR\n"); goto end; } */ // Verschluesseltes Senden des POP-Befehls // LIST <Return> // Führt E-Mails detailiert auf gnutls_record_send(session, "LIST\r\n",6); // Auslesen der verschluesselten Serverantwort ret = gnutls_record_recv(session,buffer2,MAX_BUF); if (gnutls_error_is_fatal (ret) != 0 || ret == 0) { if (ret == 0) { printf ("Kommunikationspartner hat die verschluesselte Verbindung abgebrochen.\n"); goto end; } else { fprintf (stderr, "**** Fehler: %s\n", gnutls_strerror(ret)); goto end; } } if (ret > 0) { printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { fputc (buffer2[ii], stdout); } fputs ("\n", stdout); } /* if (!strncmp(buffer2,"+OK",3)) { printf("LIST OK\n"); } else { printf("LIST ERROR\n"); goto end; } */ //E-Mail von Hand abfragen int x=0; // 1-te E-Mail int k=0; // Größe der Nachricht printf("n-te E-Mail abfragen (laufende Nummer n):\n"); // fflush(stdin); scanf("%d",&x); // Verschluesseltes Senden des POP-Befehls // RETR x <Return> // Holt E-Mails vom Server // int x=6; // l-te E-Mail // int k=4106; // Größe der Nachricht int i=0; // Bytezähler char comm_retr[20]; // Kommando RETR x sprintf(comm_retr,"RETR %d\r\n",x); // printf("Kommando lautet: %s",comm_retr); gnutls_record_send(session,comm_retr, strlen(comm_retr)); // Auslesen der verschluesselten Serverantwort // Holen von +OK? ret = gnutls_record_recv(session,buffer2,MAX_BUF); if (gnutls_error_is_fatal (ret) != 0 || ret == 0) { if (ret == 0) { printf ("Kommunikationspartner hat die verschluesselte Verbindung abgebrochen.\n"); goto end; } else { fprintf (stderr, "**** Fehler: %s\n", gnutls_strerror(ret)); goto end; } } if (ret > 0) { printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { fputc (buffer2[ii], stdout); } fputs ("\n", stdout); } // Falls kein +OK zurückkommt, abbrechen if (strncmp(buffer2,"+OK",3)) goto end; // Holen der E-Mail do { ret = gnutls_record_recv(session,buffer2,MAX_BUF); // printf("\n ------------ RET-WERT = %d ------------ \n",ret); if (gnutls_error_is_fatal (ret) != 0 || ret == 0) { if (ret == 0) { printf ("Kommunikationspartner hat die verschluesselte Verbindung abgebrochen.\n"); goto end; } else { fprintf (stderr, "**** Fehler: %s\n", gnutls_strerror(ret)); goto end; } } if (ret > 0) { // printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { if ((int)buffer2[ii]==13 && m==0) {m++; d=1;} // <CR> an 1. Stelle? -> Wenn ja, merken! if ((int)buffer2[ii]==10 && m==1) {m++; d=1;} // <LF> an 2. Stelle? -> Wenn ja, merken! if ((int)buffer2[ii]==13 && m==3) {m++; d=1;} // <CR> an 4. Stelle? -> Wenn ja, merken! if ((int)buffer2[ii]!=13 && m==3) {m=0;} // <46> an 4. Stelle? -> Wenn ja, Zähler rücksetzen! if ((int)buffer2[ii]==46 && m==2) {m++; d=1;} // <46> an 3. Stelle? -> Wenn ja, merken! if ((int)buffer2[ii]==13 && m==2) {m=1; d=1;} // <13> an 3. Stelle? -> Wenn ja, merken! Zähler auf 1. Stelle rücksetzen! Berücksichtigt endlose Folgen <CR><LF><CR><LF>...<CR><LF> if ((int)buffer2[ii]==10 && m==4) {m=0; ret=0;} // <10> an 5. Stelle? -> Wenn ja, wurde die Folge <CR><LF><46><CR><LF> durchlaufen, und das Ende der E-Mail ist erreicht. if (m < 3) { printf("%c",(buffer2[ii])); } // Zeichen ausgeben; Zeichen nur bis <CR><LF> ausgeben. if (d==0) m=0; // erfolgloser Durchlauf d=0; // Merker rücksetzen } } } while (ret!=0); /* // aktuelle E-Mail löschen? char key[2]; printf("Aktuelle E-Mail loeschen (j/n)?\n"); fflush(stdout); scanf("%s",key); // printf("Eingabe war: %s\n",key); if (key[0]=='j') { sprintf(comm_retr,"DEL %d\r\n",x); // printf("Kommando lautet: %s",comm_retr); gnutls_record_send(session,comm_retr, strlen(comm_retr)); // Auslesen der verschluesselten Serverantwort // Holen von +OK? ret = gnutls_record_recv(session,buffer2,MAX_BUF); if (gnutls_error_is_fatal (ret) != 0 || ret == 0) { if (ret == 0) { printf ("Kommunikationspartner hat die verschluesselte Verbindung abgebrochen.\n"); goto end; } else { fprintf (stderr, "**** Fehler: %s\n", gnutls_strerror(ret)); goto end; } } if (ret > 0) { printf ("%d Bytes empfangen: ", ret); for (ii = 0 ; ii < ret; ii++) { fputc (buffer2[ii], stdout); } fputs ("\n", stdout); } // Falls kein +OK zurückkommt, abbrechen if (strncmp(buffer2,"+OK",3)) goto end; } */ // Folgende Funktion teilt dem Kommunikationspartner (hier // POP-Server) mit, dass und wie die Verbindung // abgebrochen werden soll. Damit ist der Verbindungs- // abbruch von einer arglistigen Unterbrechung unter- // scheidbar. gnutls_bye (session, GNUTLS_SHUT_RDWR); end: // Marke // Eingerichtetes Socket schliessen (beenden) close(sock); // Sitzungsdaten loeschen und damit Speicher freigeben gnutls_deinit(session); // Ausweisspeicher des Clients freigeben gnutls_srp_free_client_credentials (srp_cred); // Ausweisspeicher des Servers freigeben gnutls_certificate_free_credentials (cert_cred); // GnuTLS beenden und damit Speicher freigeben gnutls_global_deinit(); } // IP_number(...) ermittelt die binaere IP-Adresse aus dem // gegebenen Hostnamen und wandelt diese in eine für Menschen // schreibbare Version um. Die ermittelte binaere IP-Adresse // wird ausserdem in die Struktur name vom Typ sockaddr_in // geschrieben, damit sie dem Socket zur Verfuegung steht. // Gleichzeitig wird mit Hilfe von htons(...) die Portnummer in // die vom System verwendete binaere Form umgewandelt, // damit sie in die Struktur name geschrieben werden kann. void IP_number(struct sockaddr_in *name, char *servername, uint16_t port) { char serverIP[16]; // Die Funktion // struct hostent * gethostbyname (const char *name) // ermittelt binaere IP-Nummer if(hostinfo=gethostbyname(servername)) { // inet_ntop() wandelt _binaere IP-Nummer in die // fuer Menschen lesbare Nummern-Punkt-Schreibweise // um. (Beispiel: 193.175.141.61). inet_ntop(PF_INET, hostinfo->h_addr_list[0], serverIP, 16); printf("IP: %s\n", serverIP); // Im Folgenden werden Adressformat, binaere // Portnummer und binaere IP-Adresse in die Struktur // name vom Typ sockaddr_in geschrieben, damit diese // Angaben dem Socket zum Verbindungsaufbau zur // Verfuegung stehen. name->sin_family = PF_INET; name->sin_port = htons(port); name->sin_addr = *(struct in_addr *) hostinfo->h_addr; } else { fprintf(stderr, "Unbekannter Host %s.\n", servername); exit (EXIT_FAILURE); } } // Unterprogramm zur Ueberprüfung des Server-Zertifikats static int _verify_certificate_callback (gnutls_session_t session) { unsigned int status; const gnutls_datum_t *cert_list; unsigned int cert_list_size; int ret; gnutls_x509_crt_t cert; const char *hostname; // Hostname auslesen hostname = gnutls_session_get_ptr (session); // Folgende Funktion ueberprueft das vom Server geschickte Zertifikat // an Hand der zuvor in den Zertifizierungsspeicher der Sitzung // geladenen Zertifikate ret = gnutls_certificate_verify_peers2 (session, &status); if (ret < 0) { printf ("Fehler. Zertifikat konnte nicht ueberprueft werden.\n"); return (GNUTLS_E_CERTIFICATE_ERROR); } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) printf ("Das Zertifikat hat keinen bekannten Aussteller.\n"); if (status & GNUTLS_CERT_REVOKED) printf ("Das Zertifikat wurde widerrufen.\n"); if (status & GNUTLS_CERT_EXPIRED) printf ("Das Zertifikat ist abgelaufen.\n"); if (status & GNUTLS_CERT_NOT_ACTIVATED) printf ("Das Zertifikat wurde bis jetzt nicht aktiviert.\n"); if (status & GNUTLS_CERT_INVALID) { printf ("Dem Zertifikat wird nicht vertraut.\n"); return (GNUTLS_E_CERTIFICATE_ERROR); } if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) return (GNUTLS_E_CERTIFICATE_ERROR); if (gnutls_x509_crt_init (&cert) < 0 ) { printf ("Initialisierungsfehler.\n"); return (GNUTLS_E_CERTIFICATE_ERROR); } cert_list = gnutls_certificate_get_peers (session, &cert_list_size); if (cert_list == NULL) { printf ("Kein Zertifikat gefunden!\n"); return (GNUTLS_E_CERTIFICATE_ERROR); } if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) { printf ("error parsing certificate\n"); return (GNUTLS_E_CERTIFICATE_ERROR); } if (!gnutls_x509_crt_check_hostname (cert, hostname)) { printf ("Zertifikatstraeger stimmt nicht mit Hostnamen ueberein. '%s'\n", hostname); return (GNUTLS_E_CERTIFICATE_ERROR); } gnutls_x509_crt_deinit (cert); // Normaler Programmablauf, d. h. Handshake durchfuehren. return (0); }