SMTP AUTH Login via Winsock

daflowjoe

Mitglied
Hi Leute,

versuche gerade mit c++ eine WSA Connection zu einem SMTP Provider aufzubauen und so eine Mail zu schicken. Über Putti funktioniert alles wunderbar. Über meine Console aber nicht. Es bleibt beim read einfach hängen bzw. es kommt einfach keine Antwort vom Server, was aber nicht sein kann. Ich kann dieses Verhalten einfach nicht nachvollziehen. oO
Kann mir jemand auf die Sprünge helfen? Ich denke bei mir haberts da irgendwo grad am grundsätzlichen Verständnis.

Code:
// winsock tut.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <windows.h>
#include <winsock2.h>
#pragma comment( lib, "ws2_32.lib" )
using namespace std;

int startWinsock()
{
    //Initialsierung der WSA Startup WSA-Win Socket Api
    WSADATA wsa;
    return WSAStartup(MAKEWORD(2,0), &wsa);
}

bool readable(SOCKET socket)
{
    FD_SET fdSet;
    TIMEVAL timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    long status;

    FD_ZERO(&fdSet);
    FD_SET(socket,&fdSet);
    status = select(0,&fdSet,0,0,&timeout);
    if(status <= 0)
      {
          FD_ZERO(&fdSet);
      }
    if(!FD_ISSET(socket,&fdSet))
      {
          return false;
      }
    return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
    long rc;
    SOCKET s;
    SOCKADDR_IN addr;
    char buf[256];
    char buf2[256];
    rc = startWinsock();
    if(rc!=0)
    {
        cout << "Es ist ein Fehler beim Initialsieren der Windowsocket aufgetreten" << endl;
        return 1;
    }
    else
    {
        printf("Winsocket started!\n");
    }
    
    //Socket erstellen
    s = socket(AF_INET, SOCK_STREAM, 0);
    if(s == INVALID_SOCKET)
    {
        cout << "Error: Could not create socket, error: " << WSAGetLastError() << endl;
        return 1;
    }
    else
    {
        cout << "Socket creating successfull!" << endl;
    }

    // Alles auf 0 setzen, inklsuive sin_zero
    memset(&addr, 0, sizeof(SOCKADDR_IN));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(25); // Legt Portnummer fest, htons wandelt Short in Network byte um
    addr.sin_addr.s_addr = inet_addr("195.4.92.212");

    //Socketverbindung aufbauen
    rc = connect(s, (SOCKADDR*)&addr, sizeof(SOCKADDR));
    if( rc == SOCKET_ERROR)
    {
        cout << "Connection to socket failed." << WSAGetLastError() << endl;
        return 1;
    }
    else
    {
        cout << "Verbunden mit SMTP Freenet - Port 25... " << endl;
    }

    // Daten austauschen
    int ok = 1;
  while(rc!=SOCKET_ERROR)
  {
    printf("\nZeichenfolge eingeben [max 256]: ");
    gets(buf);
    send(s,buf,strlen(buf),0);
    rc=recv(s,buf,256,0); //>>>>>>>>>>>>>>> Hier die Stelle wo es hängt <<<<<<<<<<
    if(rc==0)
    {
      printf("Server hat die Verbindung getrennt..\n");
      break;
    }
    if(rc==SOCKET_ERROR)
    {
      printf("Fehler: recv, fehler code: %d\n",WSAGetLastError());
      break;
    }
    buf[rc]='\0';
    printf("\nServer antwortet: %s\n",buf);
  }
  closesocket(s);
  WSACleanup();
    return 0;
}
 
Danke für den Tip, so kommt zumindest schonmal eine Antwort zurück. Allerdings kommen dadurch scheinbar gleich mehrere Antworten gleichzeittig zurück. Außerdem verhält es sich immernoch anders wie eine Verbindung im Putty beispielsweise.
Was mache ich falsch:
Habe die Codeblöcke die das \r\n noch nicht enthielten folgendermaßen angepasst:
Code:
char helo[256] = "HELO localhost";
    helo[strlen(helo)] = '\r';
    helo[strlen(helo)+1] = '\n';
    cout << helo << endl;
    send(s, helo,strlen(buf),0);
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet: %s\n",buf);
    
    
    char auth[256] = "AUTH LOGIN";
    gets(auth);
    auth[strlen(auth)] = '\r';
    auth[strlen(auth)+1] = '\n';
    cout << auth << endl;
    send(s, auth,strlen(auth),0);

Mfg Joe
 
Mit dem SMTP bist du vertraut? Beim connecten sendet dir der Server "220 ein begrüssungstext", anschliessend kannst du dann EHLO oder HELO antworten und erhälst vom Server als Antwort "250 ein Text". Anschliessend kannst du dich authentifizieren.

Hier mal ein beispielhafter Ablauf (nicht kopieren, ist nur auf die Schnelle gemacht!!):
C++:
	long rc = connect(sk, (const sockaddr*)&sin, sizeof(sin)); // connecten
	if(rc == SOCKET_ERROR)
	{
		printf("Error: %u\n", WSAGetLastError());
		return false;
	}
	char buffer[65535];
	int x = recv(sk, buffer, 65535, 0); // hier die connect-antwort abfangen
	buffer[x] = 0;
	int opcode = atoi(buffer);
	if(opcode < 200 || opcode > 299) // die range 200-299 steht hier für Erfolg
	{
		printf("Error: '%s'\n", buffer);
		return false;
	}
	char reg[500];
	sprintf(reg, "EHLO localhost\r\n"); // Wir sagen hallo
	send(sk, reg, strlen(reg), 0);
	x = recv(sk, buffer, 65535, 0); // und erwarten auch ein Hallo vom Server
	buffer[x] = 0;
	opcode = atoi(buffer);
	if(opcode > 500) // von nun an sind opcodes höher als 500 für fehler
	{
		printf("Error: '%s'\n", buffer);
		return false;
	}
	sprintf(reg, "AUTH LOGIN\r\n"); // Wir senden nicht anonym, also senden wir die AUTH LOGIN anfrage
	send(sk, reg, strlen(reg), 0);
	x = recv(sk, buffer, 65535, 0);
	buffer[x] = 0;
	opcode = atoi(buffer);
	if(opcode > 500) // wird ja vielleicht vom server nicht unterstützt
	{
		printf("Error: '%s'\n", buffer);
		return false;
	}
	std::string user = from;
	user = base64_encode((const unsigned char*)user.c_str(), user.length()); // username wird base64 verschlüsselt (in meinem fall)
	sprintf(reg, "%s\r\n", user.c_str()); // base64 verschlüsselten username senden (ohne opcode)
	send(sk, reg, strlen(reg), 0);
	x = recv(sk, buffer, 65535, 0);
	buffer[x] = 0;
	opcode = atoi(buffer);
	if(opcode > 500) // das übliche halt
	{
		printf("Error: '%s'\n", buffer);
		return false;
	}
	std::string ps = pass;
	ps = base64_encode((const unsigned char*)ps.c_str(), ps.length()); // für das passwort das selbe
	sprintf(reg, "%s\r\n", ps.c_str());
	send(sk, reg, strlen(reg), 0);
	x = recv(sk, buffer, 65535, 0);
	buffer[x] = 0;
	opcode = atoi(buffer);
	if(opcode > 500) // fehler?
	{
		printf("Error: '%s'\n", buffer);
		return false;
	}

/edit:
So, jetzt noch mit Kommentaren!
 
Zuletzt bearbeitet:
Danke für die Mühen :) Das Prinzip habe ich schon erkannt. Auf ner normalen Konsole im Putty funktioniert das einwandfrei.

Sende ich jedoch ein AUTH LOGIN mit meinem Code, kommt ein "502 unimplemented" vom Server zurück, obwohl es wie gesagt beim normalen manuellen connecten funktioniert und ich verstehe einfach nicht was der Unterschied zwischen der Kommunikation mit dem Server im Putty mit der in meinem Programm ist. Die Funktionen send und read sollten doch genauso funktionieren?!

Der oben gepastete Code verbindet sich ja auch mit dem gmx Server. Das ehlo ist auch erfolgreich, erst beim "AUTH LOGIN" reagiert er "abnormal".

Hier nochmal der aktuelle Code:
Code:
// winsock tut.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <windows.h>
#include <winsock2.h>
#pragma comment( lib, "ws2_32.lib" )
using namespace std;

int startWinsock()
{
    //Initialsierung der WSA Startup WSA-Win Socket Api
    WSADATA wsa;
    return WSAStartup(MAKEWORD(2,0), &wsa);
}

bool readable(SOCKET socket)
{
    FD_SET fdSet;
    TIMEVAL timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    long status;

    FD_ZERO(&fdSet);
    FD_SET(socket,&fdSet);
    status = select(0,&fdSet,0,0,&timeout);
    if(status <= 0)
      {
          FD_ZERO(&fdSet);
      }
    if(!FD_ISSET(socket,&fdSet))
      {
          return false;
      }
    return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
    long rc;
    SOCKET s;
    SOCKADDR_IN addr;
    char buf[256];
    char buf2[256];
    rc = startWinsock();
    if(rc!=0)
    {
        cout << "Es ist ein Fehler beim Initialsieren der Windowsocket aufgetreten" << endl;
        return 1;
    }
    else
    {
        printf("Winsocket started!\n");
    }
    
    //Socket erstellen
    s = socket(AF_INET, SOCK_STREAM, 0);
    if(s == INVALID_SOCKET)
    {
        cout << "Error: Could not create socket, error: " << WSAGetLastError() << endl;
        return 1;
    }
    else
    {
        cout << "Socket creating successfull!" << endl;
    }

    // Alles auf 0 setzen, inklsuive sin_zero
    memset(&addr, 0, sizeof(SOCKADDR_IN));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(25); // Legt Portnummer fest, htons wandelt Short in Network byte um
    addr.sin_addr.s_addr = inet_addr("213.165.64.21");

    //Socketverbindung aufbauen
    rc = connect(s, (SOCKADDR*)&addr, sizeof(SOCKADDR));
    if( rc == SOCKET_ERROR)
    {
        cout << "Connection to socket failed." << WSAGetLastError() << endl;
        return 1;
    }
    else
    {
        cout << "Verbunden mit SMTP Freenet - Port 25... " << endl;
    }

    // Daten austauschen
    int ok = 1;
  while(rc!=SOCKET_ERROR)
  {
    char helo[256] = "HELO localhost";
    helo[strlen(helo)] = '\r';
    helo[strlen(helo)+1] = '\n';
    cout << helo << endl;
    send(s, helo,strlen(buf),0);
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet: %s\n",buf);
    
    
    char auth[256] = "AUTH LOGIN";
    gets(auth);
    auth[strlen(auth)] = '\r';
    auth[strlen(auth)+1] = '\n';
    cout << auth << endl;
    send(s, auth,strlen(auth),0);
    
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet: %s\n",buf);

    char user[256] = "c2ltb24ucGljdffda2VydEBnbXguZGU=";
    user[strlen(user)] = '\r';
    user[strlen(user)+1] = '\n';
    cout << user << endl;
    send(s, user,strlen(auth),0);
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet: %s\n",buf);

    char pass[256] = "YXNfkZjFhcsfss2Rm";
    pass[strlen(pass)] = '\r';
    pass[strlen(pass)+1] = '\n';
    cout << pass << endl;
    send(s, pass,strlen(auth),0);
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet: %s\n",buf);

    if(rc==0)
    {
      printf("Server hat die Verbindung getrennt..\n");
      break;
    }
    if(rc==SOCKET_ERROR)
    {
      printf("Fehler: recv, fehler code: %d\n",WSAGetLastError());
      break;
    }
    buf[rc]='\0';
    printf("\nServer antwortet: %s\n",buf);
  }
  closesocket(s);
  WSACleanup();
    return 0;
}
 
Was sendest du denn noch nach AUTH LOGIN? Also bei mir ist da nur AUTH LOGIN\r\n ohne noch ein String.

Mit Login wählst du ja die Auth-methode aus (PLAIN, LOGIN, CRAM-MD5, NTLM)
 
Ich sende nur AUTH LOGIN. Danachs ollte eigentlich dann der verschlüsselte loginname und das das passwort folgen... Was natürlich wirkenkunslos bleibt, wenn AUTH LOGIN nicht ankommt.

Das hier ist die Ausgabe auf der Console:

Code:
Winsocket started!
Socket creating successfull!
Verbunden mit SMTP GMX - Port 25...

Server antwortet: 220 mail.gmx.net GMX Mailservices ESMTP {mp055}

HELO localhost

Server antwortet: 250 mail.gmx.net GMX Mailservices {mp055}

AUTH LOGIN

Server antwortet: 502 5.5.2 Unimplemented {mp055}

c2ltb24ucGljdffda2VydEBnbXguZGU=

Server antwortet: 502 5.5.2 Unimplemented {mp055}

YXNfkZjFhcsfss2Rm
An der Stelle wo nach AUTH LOGIN das Unimplemented auftaucht kommt im Putty oder im Telnet ein erfolgreiches go ahead zurück...
Ich verzweifle oO
 
Zuletzt bearbeitet:
Ein Problem fällt mir da allerdings irgendwie ein bisschen auf:
auth[strlen(auth)] = '\r';
auth[strlen(auth)+1] = '\n';

Damit überschreibst du ja die terminierende 0!

Nimm doch strcat
 
C++:
#include <stdio.h>
#include <iostream>
#include <winsock2.h>
#include <windows.h>
#pragma comment( lib, "ws2_32.lib" )
using namespace std;

int startWinsock()
{
    //Initialsierung der WSA Startup WSA-Win Socket Api
    WSADATA wsa;
    return WSAStartup(MAKEWORD(2,0), &wsa);
}

bool readable(SOCKET socket)
{
    FD_SET fdSet;
    TIMEVAL timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    long status;

    FD_ZERO(&fdSet);
    FD_SET(socket,&fdSet);
    status = select(0,&fdSet,0,0,&timeout);
    if(status <= 0)
      {
          FD_ZERO(&fdSet);
      }
    if(!FD_ISSET(socket,&fdSet))
      {
          return false;
      }
    return true;
}

int main()
{
    long rc;
    SOCKET s;
    SOCKADDR_IN addr;
    char buf[256];
    char buf2[256];
    rc = startWinsock();
    if(rc!=0)
    {
        cout << "Es ist ein Fehler beim Initialsieren der Windowsocket aufgetreten" << endl;
        return 1;
    }
    else
    {
        printf("Winsocket started!\n");
    }
    
    //Socket erstellen
    s = socket(AF_INET, SOCK_STREAM, 0);
    if(s == INVALID_SOCKET)
    {
        cout << "Error: Could not create socket, error: " << WSAGetLastError() << endl;
        return 1;
    }
    else
    {
        cout << "Socket creating successfull!" << endl;
    }

    // Alles auf 0 setzen, inklsuive sin_zero
    memset(&addr, 0, sizeof(SOCKADDR_IN));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(25); // Legt Portnummer fest, htons wandelt Short in Network byte um
    addr.sin_addr.s_addr = inet_addr("213.165.64.21");

    //Socketverbindung aufbauen
    rc = connect(s, (SOCKADDR*)&addr, sizeof(SOCKADDR));
    if( rc == SOCKET_ERROR)
    {
        cout << "Connection to socket failed." << WSAGetLastError() << endl;
        return 1;
    }
    else
    {
        cout << "Verbunden mit SMTP Freenet - Port 25... " << endl;
    }

    // Daten austauschen
    int ok = 1;

  while(rc!=SOCKET_ERROR)
  {
    char helo[256] = "EHLO localhost\r\n";
    send(s, helo,strlen(helo),0);
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet(ehlo): %s\n",buf);
    
    
    char auth[256] = "AUTH LOGIN\r\n";
    send(s, auth,strlen(auth),0);
    
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet(auth): %s\n",buf);

    char user[256] = "c2ltb24ucGljdffda2VydEBnbXguZGU=\r\n";
    send(s, user,strlen(user),0);
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet(user): %s\n",buf);

    char pass[256] = "YXNfkZjFhcsfss2Rm\r\n";
    send(s, pass,strlen(pass),0);
    rc=recv(s,buf,256,0);
    buf[rc]='\0';
    printf("\nServer antwortet(pass): %s\n",buf);

	system("Pause");

    if(rc==0)
    {
      printf("Server hat die Verbindung getrennt..\n");
      break;
    }
    if(rc==SOCKET_ERROR)
    {
      printf("Fehler: recv, fehler code: %d\n",WSAGetLastError());
      break;
    }
    buf[rc]='\0';
    printf("\nServer antwortet: %s\n",buf);
  }
  closesocket(s);
  WSACleanup();
    return 0;
}

Ich habe deinen Code mal etwas geändert (ungeachtet, ob er sicher/performant u.ä. ist, einfach so, dass das geht, was du willst). Du hast ein paar Variabeln durcheinander gebracht, ausserdem war deine Version mit \r\n nicht vorteilhaft, da du die \0 überschrieben hast.
 
Zurück