Socketprogrammierung FTP Client

Eburneolus

Grünschnabel

Schönen Guten Tag,
Ich hab die letzten Tage meine ersten Schritte in der Socketprogrammierung gemacht und dachte um das Wissen zu vertiefen wage ich mich an einen einfachen FTP - Clienten.
Ich bin dabei auf Probleme gestoßen, die evtl mehr Grundwissen über die Behandlung von Sockets im Betriebssystem oder das Management von Sockets für FTP - Clienten erfordern.
Folgendes Problem:
Ich bin in der Lage eine Verbindung zum Control - Port des FTP Servers herzustellen und kann mit diesem vorerst problemlos kommunizieren.
Auch ist es mir möglich eine Passiv - Verbindung zum Server herzustellen über Serverbefehl
"PASV\r\n" wird mir vom Server der Data - Port zur Verfügung gestellt, über welchem ich mich vorerst ohne Probleme verbinden kann.
Auch einfache Befehle wie "NLST\r\n" kann ich senden und die Antwort über den mit Data-Port verbundenen Socket empfangen.
Folgendes ist mir dabei aufgefallen: Jeder dieser Befehle erfordert stets ein erneuten Aufruf von "PASV\r\n", damit mir vorübergehend ein Port geöffnet wird.
Das heißt aber auch, dass jedesmal ein neuer Socket vom Betriebssystem zur Verfügung gestellt werden muss, dessen Ausgangsport nun nicht mehr um nur 1 höher ist als der Ausgangsport meines Sockets mit dem ich den Control - Port (21) anspreche.
Auszug Netstat:
netstatSRBP0.jpg


Meines Wissens nach muss der Data-Port genau um eins höher sein als der Control - Port des Clients.

Frage:
Gibt es Wege den Ausgangsport manuell zu bestimmen, ohne automatische Zuweisung des Betriebssystems?
Oder gibt es Wege einen Socket der zuvor connected war zu disconnecten und mit einem neuen Zielport zu verbinden, ohne, das er mit closesocket() freigegeben wird, denn jedesmal, wenn ich mit socket() einen Neuen erstelle ist der gewünschte Ausgangsport nicht mehr nur um 1 höher als der Ausgangsport des Sockets, der mit dem Controlport(21) verbunden ist, wie auch im Netstat erkenntlich.

Realisiert habe ich das folgendermaßen:
Selbstdefinierte Fkt:
C:
int sendCom(int sock, char* msg, int* status);

int connect_data(struct in_addr hostip, unsigned short ftpPort); /* retrieves the created Socked connected to the passive-port of the Server*/
struct in_addr getHostIp(char* hostName);
unsigned short passivePort(int ctrlSock);
unsigned short buildPort(char*);

main():
Wesentliches ab
/*Kommunikation mit Server ================================================== */
C:
int main()
{
	char key = 0;
	char buffer[BUF_LEN] = "";
	unsigned short ftpPort = 0;
	int status = 0;
	int bytes = 0;
	int sock = 0;
	int sock_data = 0;
	struct sockaddr_in addr; /* Control Port Server */	
	struct sockaddr_in addr_data; /* Passiver Datenport Server */
	struct hostent host; /* nötig um Informationen aus gethostbyname zu erhalen*/
	struct in_addr hostip; /* nötig um die IP - Adresse des Hosts aus "host.h_addr_list[0]" zu erhalten*/
	
	
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(1,1), &wsa) != 0)
	{
		printf("Failed to load windows specific DLL's for Sockets");
		return 1;
	}
	else
	{
		printf("%s\n", wsa.szDescription);
		printf("%s\n", wsa.szSystemStatus);
	}
	
	sock = socket(AF_INET, SOCK_STREAM, 0); /* Erstellen des Control - Port Sockets*/
	if (sock == -1)
	{
		printf("Failed to create Socket\n");
		return 1;
	}
	else
	{
		printf("Socket created...\n");
	}
        /* Hostaddresse aus durch gethostbyname() ermitteln*/
	host.h_length = 0;
	host = *gethostbyname("ftp.getintern.ge.ohost.de");
	if (host.h_length != 0)
	{
		printf("Hostename: %s\n", host.h_name);
		printf("Addresslaenge: %d\n", host.h_length);
		printf("Addresstyp: %d                 AF_INET: %d\n", host.h_addrtype, AF_INET);		
		hostip = *(struct in_addr*)host.h_addr_list[0];
		printf("Hostip: %ul  -- %s\n", hostip.s_addr, inet_ntoa(hostip));
	}
	else
	{
		printf("failed gethostbyname()\n");
	}
		
	addr.sin_family = AF_INET;
	addr.sin_addr   = hostip;
	addr.sin_port	= htons(21);

	/* Verbindung zu Controlport 21 des Servers herstellen, Ausgangsport vom Betriebssystem automatisch gewählt */
	if (connect(sock, (const sockaddr*)&addr, sizeof(addr)) != 0)
	{
		printf("Failed to connect...\n");
	}
	else
	{
		printf("Kommunikation mit Server startet...\n");
		
	/*Kommunikation mit Server ================================================== */

        /* Ne Menge Befehle um die Funktionalität des Controlports zu testen */

	sendCom(sock,"", &status);
		
	bytes = sendCom(sock, "USER blaaaaaa\r\n", &status);
	bytes = sendCom(sock, "PASS undblaaa\r\n", &status);	
	bytes = sendCom(sock, "OPTS UTF8 ON\r\n", &status);
	bytes = sendCom(sock, "HELP\r\n", &status);
	bytes = sendCom(sock, "SYST\r\n", &status);
	bytes = sendCom(sock, "PWD\r\n", &status);
	bytes = sendCom(sock, "MKD WhatDir\r\n", &status);
	
	

        	
	ftpPort = passivePort(sock); /* Senden von "PASV\r\n" und Ermittlung des geöffneten Ports*/
        /* Erstellen eines neuen Sockets und verbinden mit neu geöffneten passiven Dataport hier scheint das Problem zu liegen */
	sock_data = connect_data(getHostIp("ftp.getintern.ge.ohost.de"), ftpPort);	

	bytes = sendCom(sock, "TYPE A\r\n", &status);
	bytes = sendCom(sock, "NLST\r\n", &status);
	bytes = sendCom(sock_data, "", &status);
	bytes = sendCom(sock, "", &status);
	bytes = sendCom(sock, "PWD\r\n", &status);
        /* Bis hierhin keine Probleme, alle Daten über Control und Dataport konnten empfangen werden */

        /*erneuter Test der Verbindung mit passive Port
            auch hier wird wiederrum ein neuer Socket erstellt, dieser hat einen um 2 
            höheren Ausgangsport als der Control - port - socket,
            dennoch wird auch diesmal der Befehl "NLST\r\n" problemlos ausgeführt
            und es können beliebig Befehle über Controlport gesendet werden*/
	ftpPort = passivePort(sock);
	sock_data = connect_data(getHostIp("ftp.getintern.ge.ohost.de"), ftpPort);	

	bytes = sendCom(sock, "TYPE A\r\n", &status);
	bytes = sendCom(sock, "NLST\r\n", &status);
	bytes = sendCom(sock_data, "", &status);
	bytes = sendCom(sock, "", &status);
	bytes = sendCom(sock, "PWD\r\n", &status);


        /* Erneute passiv - Verbindung herstellen, diesmal aber um eine textdatei zu 
            erstellen, der Ausgangsport der passiver Verb ist diesmal um 3 höher als der des
            ControlSockets*/	
	ftpPort = passivePort(sock);
	sock_data = connect_data(getHostIp("ftp.getintern.ge.ohost.de"), ftpPort);	 
	bytes = sendCom(sock, "TYPE A\r\n", &status);
	bytes = sendCom(sock, "STOR Test.txt\r\n", &status);
	bytes = sendCom(sock_data, "BULLSHIT", &status);
       /*Diese Textdatei konnte problemlos erstellt werden JEDOCH ist es mir nich mehr 
          möglich Befehle über das Socket, welches mit dem Control Port verbunden ist zu  
          versenden
          All die folgenden Befehle sind erfolglos*/ 
	bytes = sendCom(sock, "PWD\r\n", &status);	
	bytes = sendCom(sock, "MKD WhatDir\r\n", &status);
	bytes = sendCom(sock, "QUIT\r\n", &status);	

       /*Dieses Problem würde auch auftreten, hätte ich die letzte Übertragung über das
          Data Socket ganz an den Anfang gesetzt, also es scheint probleme beim Upload zu 
          geben*/


		

	/*Kommunikation mit Server ================================================== */
	
	}
		
	closesocket(sock);
	closesocket(sock_data);
	printf("Sockets closed...\n");
	WSACleanup();
	
	key = getchar();	
	return 0;	
}


connect_data():
C:
int connect_data(struct in_addr hostip, unsigned short ftpPort)
{
	static int sock_data = -1;
	struct sockaddr_in addr_data;
	

        /*Hier wird jedesmal ein neues Socket erstellt, sobal ein neuer Passiver Port 
           bekannt ist, jedesmal steigt der Ausgangsport um 1 siehe Netstat*/
	if (sock_data != -1)
	{	
		closesocket(sock_data);
	}
	
		sock_data = socket(AF_INET, SOCK_STREAM, 0);
		if (sock_data == -1)
		{
			printf("Failed to create Datasocket\n");
			return 1;
		}
		else
		{
			printf("Datasocket created...\n");
		}
	
	addr_data.sin_family = AF_INET;
	addr_data.sin_addr   = hostip;
	addr_data.sin_port	= htons(ftpPort);
	
	if (connect(sock_data, (const sockaddr*)&addr_data, sizeof(addr_data)) != 0)
	{
		printf("Failed to connect FTP - DATA - PORT\n");
	}
	else
	{
		printf("Datentransfer startet...\n");
	}
	return sock_data;
	
}

sendCom():
C:
int sendCom(int sock, char* msg, int* status)
{
	int len = strlen(msg);
	int bytes = 0;
	int allBytes = 0;
	char buffer[BUF_LEN] = ""; /*BUF_LEN should be at least 4*/
	fd_set readSock;
	int selectRes = 1;
	
	struct timeval tv;

	if (msg[0] != 0)
	{
		bytes = send(sock, msg, len, 0);
		if (bytes <= 0)
		{
			printf("ERROR: Failed to send Message\n");
			return -1;
		}
	}
		
	while (selectRes)
	{
			tv.tv_sec = 3;
			tv.tv_usec = 0;
			FD_ZERO(&readSock);
			FD_SET(sock, &readSock);

			selectRes = select(sock + 1, &readSock, NULL, NULL, &tv);
			if (selectRes)
			{
				bytes = recv(sock, buffer, sizeof(buffer) - 1, 0);				
				if (bytes == SOCKET_ERROR)
				{
					printf("ERROR: Fehler bei der Uebertragung...\n");
					bytes = 0;
					return - 1;
				}
				else if (bytes == 0)
				{
					printf("ERROR: Connection has been closed\n");
					return - 1;
				}
				else if (bytes > 0)
				{
					buffer[bytes] = 0;
					printf("%s", buffer);
					if (bytes < sizeof(buffer) - 1)
						selectRes = 0;
					
					if (allBytes == 0)
					{
						buffer[4] = 0;
						(*status) = strToInt(buffer);
					}
					allBytes += bytes;					
				}			
			}
			else
			{
				printf("No Data to read for this Socket!\n");
			}
	}
	return allBytes;	
}

Nunja ich hoffe jemand tut sich dieses Leid hier an.
ich bin über jegliche Antworten dankbar.
Gruß

EDIT: Mir ist gerade aufgefallen, das ich erwähnte der Ausgangsport zum DataPort des Servers müsse um 1 größer sein als der Ausgangsport des Clienten zum Controlport des Servers, dabei MUSS er um 1 niedriger sein.
Nichtsdestotroz kommt eine Verbindung zustande und ich kann auch vom Server Daten empfangen und senden.
Das löst aber immernoch nicht das problem, das ich jedesmal, wenn ich einen neuen passiver Port vom Server bekomme ein neues Socket erstellen muss und nach einem Upload keine Kommunikation mit dem Server über den Controlport mehr möglich ist.
 
Zuletzt bearbeitet:
Hallo,

Hab mir deinen Code nicht genau angesehn. Um den Code etwas übersichtlicher zu gestalten, würd ich eine Klasse erstellen und die Funktionen dann im main aufrufen, z.b.:
C++:
ftp.send("NLST");//fügt \r\n and und sendet automatisch, funktioniert auch mit mehr Strings
cmd=fpt.recv();//empfängt den retcode und schreibt den Wert in cmd (int)

Letztlich baute ich mir noch eine download Funktion, die einen neue pasv Verbindung aufbaut, die entsprechende Daten runterlädt, in eine Datei speichert und den Datenport wieder schließt. Diese Funktion kann man dann so oft ausführen wie man es halt braucht.

Den Socket schließe ich mit closesocket(d_sock); und d_sock=INVALID_SOCKET;.

Der Filezilla Client macht es laut Socketsniffer genau so. Ich habe mir dazu keine Protokolle über FTP durchgelesen sonder hab die Packets gesnifft, und genau das selbe wie der Filezilla Client gemacht.

Ich hoffe ich konnte dir helfen.

Grüße Poseidon
 
Hab wohl den C - Tag vorm Topic vergessen.
Seis drum zu deinen Vorschlägen:
Die send und recv Funktion hab ich bereits realisiert.
Das Problem ist die Passive Verbindung.
Vor jedem Befehl, der eine Verbindung mit dem Datenport erfordert rufe ich "PASV" auf in Fkt
passivePort(sock), welche mir den Port zurückgibt mit dem ich dann in connect_data() das nötige Socket erstelle und mit dem Server verbinde.
Danach kann ich je nach Befehl Daten empfangen oder senden.
Nun ist es mir jedoch nicht mehr möglich, nachdem ich Daten z.B. die Textdatei geuploaded habe weitere Befehle an den ControlPort zu senden und ich versteh die Ursache nicht, zumal es in FileZilla ohn Probleme läuft.

PS: Wie kann man den C - Code farbig machen ?
 
Hi und Willkommen bei tutorials.de :)

welches Betriebssystem verwendest du eigentlich?

Zum farbigen C-Code: Statt CODE einfach CPP als Tag nehmen
[code=cpp]...[/code]

Gruß

PS: Korrektur
 
Zuletzt bearbeitet:
Momentan Win 7, aber der Code ist größtenteils portable, soll für mich dann auch einfacher auf Unix&Co implementierbar sein.
danke für den Hinweis.

Edit:
Hab die Lösung gefunden. Ich muss, nachdem ich Datenübertragung über den Datensocket beendet habe sofort die passive Verbindung beenden, d.h ich hab einfach das closesocket(sock_data) nach jeder Verbindung versäumt.
Jetzt kann ich auch wieder ohne Probleme auf den Controlport des Ftp - Servers zugreifen.
Soviel Geschreibe für so nen banalen Fehler, das war wohl das was auch posi90 erwähnt hatte, habs bloß nicht sofort erkannt.
Danke für die Hilfe, man sieht sich ;)
 
Zuletzt bearbeitet:
Zurück