rendered paste body#include <stdio.h> /* for printf() and fprintf() */#include <sys/socket.h> /* for socket(), bind(), and connect() */#include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */#include <stdlib.h> /* for atoi() and exit() */#include <string.h> /* for memset() */#include <unistd.h> /* for close() */#include <pthread.h>#include <sys/select.h>#include <sys/time.h>#include <sys/types.h>#include <signal.h>#include <netdb.h>#include "server.h"#define MAXPENDING 5 /* Maximum outstanding connection requests */#define MAX_THREADS 20 /*anzahl maximaler Nutzer */#define DOMAIN "bla.com"#define BEGRUSSUNG "220 " DOMAIN " moin\n"#define FALSCHE_REIHENFOLGE "503 Bad sequence of commands\n"#define FALSCHES_KOMMANDO "502 Command not implemented\n"#define FALSCHER_PARAMETER "504 Command parameter not implemented\n"#define HELO_OK "250 " DOMAIN " OK\n"#define STANDARD_OK "250 OK\n"#define DATA_BEGIN "354 Start mail input\n"#define PARAMETER_FALSCH "501 Syntax error in parameters or arguments\n"#define QUIT_OK "221 " DOMAIN " closing connection\n"#define BUFFER_SIZE 1024 // buffergroesse#define DATA_SIZE BUFFER_SIZE*10 //10 fache buffergroesse/* defintion der zustande */#define Z_VERBUNDEN 1#define Z_HELO 2#define Z_MAIL 3#define Z_RECEIPT 4#define Z_DATA 5#define Z_QUIT 6#define Z_RSET 7/* anzahl der zustande */#define BEF_ANZ 7#define SERVER_IP "mail.informatik.htw-dresden.de"#define SERVER_PORT 25int mask = 0;int servSock; /* Socket descriptor fur server */char join_thread[MAX_THREADS]; //feld aktiver threadspthread_t threads[MAX_THREADS]; //fehlt abgearbetieter threadspthread_mutex_t mx_join; //semeaphore für exkl. zugriff/* zu jedem zustand gehört ein befehl */struct zustaende{ int zustand; char *kommando;};struct zustaende befehle[]={ {Z_VERBUNDEN, " "}, {Z_HELO, "HELO"}, {Z_MAIL, "MAIL"}, {Z_RECEIPT, "RCPT"}, {Z_DATA, "DATA"}, {Z_QUIT, "QUIT"}, {Z_RSET, "RSET"}};struct speicher{ int data_size; char* mail_abs; char* mail_empf; char* mail_daten;};/* */int daten_lesen(int fd, char* buf, int max_len){ struct timeval tv; // struktur für zeitwert fd_set fds; int ret, len; FD_ZERO(&fds); FD_SET(fd, &fds); if(!mask) tv.tv_sec = 10; //10sekunden else tv.tv_sec = 600; //10minuten tv.tv_usec = 0; //plus 0 mikrosekunden ret = select(fd + 1, &fds, NULL, NULL, &tv); //wartet auf ereignis if(ret == 0) return 0; if(ret < 0) { perror("select"); return -1; } memset(buf, 0, max_len); //bufffer wird mit 0 initialisiert len = read(fd, buf, max_len); //lesen von socket und rückgabe anzahl gelesener bytes if(len <=0)return 0; buf[ max_len -1 ]='\0'; return len;}int daten_schreiben(int sock, char* buffer, int len){ int ret=0; ret = write(sock, buffer, len); return ret;}/* baut die verbindung korrekt ab */int verbindungsabbau(int sock){ int id = pthread_self(); //ermitteln der eigenen Thread ID int i, ret=0; close(sock); //schliessen des Sockets printf("beenden!\n"); /* locked */ pthread_mutex_lock( &mx_join ); for(i=0; i<MAX_THREADS; i++) { if(threads[i] == id) //wenn eintrag in der tabelle der ID entspricht break; } if(i == MAX_THREADS) { printf("ganz schlecht!\n"); exit(1); } join_thread[i] = 1; pthread_mutex_unlock( &mx_join ); /* unlocked */ pthread_exit(&ret); // thread (client) wird beendet return 0;}int mail_prufen(char *eing, int len, int from, struct speicher *zwischen){ char *ptr, *ptr2; char* copy = strdup(eing); if(!from && len < 13) return -1; if(from && len < 15) return -1; ptr = strsep(©, " "); len -= (copy-ptr); if(from) { if(strncasecmp(copy, "from:", 5) != 0) return -1; } else { if(strncasecmp(copy, "to:", 3) != 0) return -1; } ptr = strsep(©, ":"); len -= (copy-ptr); while(len > 0 && copy[0] == 0x20) { ptr = strsep(©, " "); len -= (copy-ptr); } if(len < 5) return -1; if(from) { if(zwischen->mail_abs) { free(zwischen->mail_abs); zwischen->mail_abs=NULL; } zwischen->mail_abs = (char*) malloc(len); strncpy(zwischen->mail_abs, copy, len); } else { if(zwischen->mail_empf) { free(zwischen->mail_empf); zwischen->mail_empf=NULL; } zwischen->mail_empf = (char*) malloc(len); strncpy(zwischen->mail_empf, copy, len); } //abschneiden bis zum @ ptr = strsep(©, "@"); if(ptr == NULL) //kein @ vorhanden return -1; if( (copy-ptr) == 1 ) //@ erstes zeichen return -1; len -= (copy-ptr); if(len <= 0) return -1; //prufen vor dem @ ptr2 = strsep(&ptr, "\"\\;:"); if(ptr != NULL) //illegales zeichen gefunden return -1; //prufen nach dem @ ptr = strsep(©, "@\"\\;:"); if(copy != NULL) //illegales zeichen gefunden return -1; copy = ptr; //abschneiden bis zum . ptr = strsep(©, "."); if(copy == NULL) //kein . vorhanden return -1; if( (copy-ptr) == 1 ) //. erstes zeichen nach @ return -1; len -= (copy-ptr); if(len <= 0) return -1; return 0;}int mail_senden(struct speicher *zwischen){ int sock; struct sockaddr_in server; struct in_addr a; struct hostent * host = gethostbyname(SERVER_IP); unsigned long addr; char buffer[BUFFER_SIZE]; int len=0; int size; char *ptr; if(host==NULL) { printf("falscher host\n"); return -1; } if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("smtp socket"); return -1; } memcpy((char*)&a, *host->h_addr_list, sizeof(a)); addr=inet_addr(inet_ntoa(a)); memcpy((char *)&server.sin_addr, &addr, sizeof(addr)); server.sin_family = AF_INET; server.sin_port = htons(SERVER_PORT); if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0) { perror("client: connect"); return -1; } //senden der daten //erst 220 verbunden memset(buffer, 0, BUFFER_SIZE); daten_lesen(sock, buffer, BUFFER_SIZE); if(atoi(buffer) != 220) { printf("Fehler anfang %d\n", atoi(buffer)); return -1; } //sende HELO memset(buffer, 0, BUFFER_SIZE); len = 7+strlen(DOMAIN)+1; snprintf(buffer, len, "HELO %s\r\n", DOMAIN); daten_schreiben(sock, buffer, len); //empfange 250 memset(buffer, 0, BUFFER_SIZE); daten_lesen(sock, buffer, BUFFER_SIZE); if(atoi(buffer) != 250) { printf("Fehler 2. %d\n", atoi(buffer)); return -1; } //sende MAIL FROM: memset(buffer, 0, BUFFER_SIZE); len = 12+strlen(zwischen->mail_abs)+1; snprintf(buffer, len, "MAIL FROM:%s\r\n", zwischen->mail_abs); daten_schreiben(sock, buffer, len); //empfange 250 memset(buffer, 0, BUFFER_SIZE); daten_lesen(sock, buffer, BUFFER_SIZE); if(atoi(buffer) != 250) { printf("Fehler 3. %d\n", atoi(buffer)); return -1; } //sende RCPT TO: memset(buffer, 0, BUFFER_SIZE); len = 10+strlen(zwischen->mail_abs)+1; snprintf(buffer, len, "RCPT TO:%s\r\n", zwischen->mail_empf); daten_schreiben(sock, buffer, len); //empfange 250 memset(buffer, 0, BUFFER_SIZE); daten_lesen(sock, buffer, BUFFER_SIZE); if(atoi(buffer) != 250) { printf("Fehler 4. %d\n", atoi(buffer)); return -1; } //sende DATA memset(buffer, 0, BUFFER_SIZE); len = 7; snprintf(buffer, len, "DATA\r\n"); daten_schreiben(sock, buffer, len); //empfange 354 memset(buffer, 0, BUFFER_SIZE); daten_lesen(sock, buffer, BUFFER_SIZE); if(atoi(buffer) != 354) { printf("Fehler 5. %d\n", atoi(buffer)); return -1; } size = zwischen->data_size; ptr = zwischen->mail_daten; while( size > 0 ) { len = (size > BUFFER_SIZE) ? BUFFER_SIZE : size; len = daten_schreiben(sock, ptr, len); ptr += len; size -= len; } //empfange 250 memset(buffer, 0, BUFFER_SIZE); daten_lesen(sock, buffer, BUFFER_SIZE); if(atoi(buffer) != 250) { printf("Fehler 6. %d\n", atoi(buffer)); return -1; } //sende QUIT memset(buffer, 0, BUFFER_SIZE); len = 7; snprintf(buffer, len, "QUIT\r\n"); daten_schreiben(sock, buffer, len); //empfange 221 memset(buffer, 0, BUFFER_SIZE); daten_lesen(sock, buffer, BUFFER_SIZE); if(atoi(buffer) != 221) { printf("Fehler 7.(ende) %d\n", atoi(buffer)); return -1; } return 0;}int daten_prufen(int sock, struct speicher *zwischen){ int ges_len = 0; int ges_speicher = 0; int len = 0; char eing[BUFFER_SIZE]; char *punkt; if(zwischen->mail_daten != NULL) { free(zwischen->mail_daten); zwischen->mail_daten = (char*) malloc(DATA_SIZE); ges_speicher = DATA_SIZE; memset(zwischen->mail_daten, 0, DATA_SIZE); } while( (len=daten_lesen(sock, eing, BUFFER_SIZE)) > 0 ) //so labge wir daten lesen { if( (ges_len+len) >= ges_speicher) { zwischen->mail_daten = realloc(zwischen->mail_daten, (ges_speicher + DATA_SIZE) ); if(zwischen->mail_daten == NULL) return -1; memset(zwischen->mail_daten + ges_speicher, 0, DATA_SIZE); ges_speicher = ges_speicher + DATA_SIZE; } strncpy(zwischen->mail_daten + ges_len, eing, len ); ges_len += len; punkt = strstr(zwischen->mail_daten, "\r\n.\r\n"); if( punkt != NULL ) { zwischen->data_size = ges_len; return 0; } } return -1;}/*uberpruft die empfangenen Kommandos eines Clients aus Syntax */int prufen(int sock, char* eing, int len, int *zustand, struct speicher *zwischen){ int i=0, bef_len=0, ret=0; for(i=0; i<BEF_ANZ; i++) { bef_len = strlen(befehle[i].kommando); if(len < bef_len) continue; if(strncasecmp(eing, befehle[i].kommando, bef_len) == 0) { break; } } if(i==BEF_ANZ) { daten_schreiben(sock, FALSCHES_KOMMANDO, strlen(FALSCHES_KOMMANDO)); return 0; } switch (befehle[i].zustand) { case Z_HELO: if(*zustand == Z_VERBUNDEN) { if(len < 6) { daten_schreiben(sock, PARAMETER_FALSCH, strlen(PARAMETER_FALSCH)); break; } for(i=5; i<len; i++) { if(eing[i] > 0x20) break; } if(i==len) { daten_schreiben(sock, PARAMETER_FALSCH, strlen(PARAMETER_FALSCH)); break; } daten_schreiben(sock, HELO_OK, strlen(HELO_OK));// printf("servus \n"); *zustand = Z_HELO; } else daten_schreiben(sock, FALSCHE_REIHENFOLGE, strlen(FALSCHE_REIHENFOLGE)); break; case Z_MAIL: if(*zustand == Z_HELO) { ret = mail_prufen(eing, len, 1, zwischen); if(ret < 0) { daten_schreiben(sock, PARAMETER_FALSCH, strlen(PARAMETER_FALSCH)); break; } daten_schreiben(sock, STANDARD_OK, strlen(STANDARD_OK));// printf("servus \n"); *zustand = Z_MAIL; } else daten_schreiben(sock, FALSCHE_REIHENFOLGE, strlen(FALSCHE_REIHENFOLGE)); break; case Z_RECEIPT: if(*zustand == Z_MAIL) { ret = mail_prufen(eing, len, 0, zwischen); if(ret < 0) { daten_schreiben(sock, PARAMETER_FALSCH, strlen(PARAMETER_FALSCH)); break; } daten_schreiben(sock, STANDARD_OK, strlen(STANDARD_OK));// printf("servus \n"); *zustand = Z_RECEIPT; } else daten_schreiben(sock, FALSCHE_REIHENFOLGE, strlen(FALSCHE_REIHENFOLGE)); break; case Z_DATA: if(*zustand == Z_RECEIPT) { daten_schreiben(sock, DATA_BEGIN, strlen(DATA_BEGIN));// printf("servus \n"); if(daten_prufen(sock, zwischen) == 0) { daten_schreiben(sock, STANDARD_OK, strlen(STANDARD_OK)); *zustand = Z_DATA; mail_senden(zwischen); } else daten_schreiben(sock, PARAMETER_FALSCH, strlen(PARAMETER_FALSCH)); } else daten_schreiben(sock, FALSCHE_REIHENFOLGE, strlen(FALSCHE_REIHENFOLGE)); break; case Z_QUIT: daten_schreiben(sock, QUIT_OK, strlen(QUIT_OK)); verbindungsabbau(sock); break; case Z_RSET: *zustand = Z_VERBUNDEN; daten_schreiben(sock, STANDARD_OK, strlen(STANDARD_OK)); break; default: break; } return 0;}int eingabemaske(int sock, struct speicher *zwischen){ char buffer[BUFFER_SIZE]; int len, ret, incorrect; char *list, *tmp; //absender einlesen daten_schreiben(sock, "Absender:\n", 11); len=0; strcpy(buffer, "MAIL FROM:"); while(len <= 0) len = daten_lesen(sock, buffer+10, BUFFER_SIZE-10);// printf("len: %d mailaddy: %s", len-2+10, buffer); ret = mail_prufen(buffer, len-2+10, 1, zwischen); if(ret != 0) { daten_schreiben(sock, "Emailadresse ungultig!\n", strlen("Emailadresse ungultig!\n")); return -1; } else daten_schreiben(sock, "Emailadresse korrekt.\n", strlen("Emailadresse korrekt.\n")); //empfanger einlesen daten_schreiben(sock, "Empfanger:\n", 12); do { incorrect = 0; len = 0; while(len <=0) len = daten_lesen(sock, buffer+8, BUFFER_SIZE-8); list = strdup(buffer); do { tmp = strsep(&list, ";"); memset(buffer, 0, BUFFER_SIZE); strcpy(buffer, "MAIL TO:"); strcat(buffer, tmp); ret = mail_prufen(buffer, strlen(buffer), 0, zwischen); if(ret) { daten_schreiben(sock, "Emailadresse ungultig!\n", strlen("Emailadresse ungultig!\n")); incorrect = 1; break; } } while(list); } while(incorrect); //daten einlesen daten_schreiben(sock, "Mailtext:\n", 12); ret = daten_prufen(sock, zwischen); if(ret == 0) daten_schreiben(sock, "daten gepruft\n", strlen("daten gepruft\n")); else daten_schreiben(sock, "daten falsch\n", strlen("daten falsch\n")); ret = mail_senden(zwischen); if(ret == 0) daten_schreiben(sock, "daten gesendet\n", strlen("daten gesendet\n")); else daten_schreiben(sock, "daten nicht gesendet\n", strlen("daten nicht gesendet\n")); return 0;}/*fangt das Signal Strg+C ab und beendet alles Verbindugen korrekt*/void sighandler(int sig){ if(sig == SIGINT) { printf("caught SIGINT!\n"); close(servSock); exit(1); }}/* jeder Thread bekommt diese Funktion übergeben */void handleClient(void* arg){ int sock = *((int*) arg); char buffer[BUFFER_SIZE]; int ret=0; int zustand=0; struct speicher zwischen; zwischen.mail_abs = NULL; zwischen.mail_empf = NULL; zwischen.mail_daten = NULL; if(!mask) ret = daten_schreiben(sock, BEGRUSSUNG, strlen(BEGRUSSUNG)); if(mask) { ret = eingabemaske(sock, &zwischen); } else { zustand = Z_VERBUNDEN; for (;;) /* endlos */ { ret=daten_lesen(sock, buffer, BUFFER_SIZE); if(ret==0) continue; if(ret<0) verbindungsabbau(sock); prufen(sock, buffer, ret, &zustand, &zwischen); //TODO:REMOVE// printf("erhalten: %s", buffer); } }}int main(int argc, char *argv[]){ int clntSock; /* Socket descriptor for server */ struct sockaddr_in smtpServAddr; /* Local address */ struct sockaddr_in smtpClntAddr; /* Client address */ unsigned short smtpServPort; /* Server port */ fd_set fds; struct timeval tv; int ret, i; unsigned int clntLen; void *value; signal(SIGINT, sighandler); //sighandler initialisierung pthread_mutex_init( &mx_join, NULL ); //semaphore initialisierung memset(join_thread, 0, sizeof(join_thread)); //initialisierung der thread tabelle if (argc != 3) /* test ob anzahl der argumente korrekt ist */ { fprintf(stderr, "Usage: %s <Server Port> <mask [0,1]>\n", argv[0]); return -1; } smtpServPort = atoi(argv[1]); /* erstes argument: server port */ mask = atoi(argv[2]); //maskenwert /* Socket fur eingehende Verbindungen starten */ if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("socket"); return -1; } /* Construct local address structure */ memset(&smtpServAddr, 0, sizeof(smtpServAddr)); /* Zero out structure */ smtpServAddr.sin_family = AF_INET; /* Internet address family */ smtpServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ smtpServAddr.sin_port = htons(smtpServPort); /* Local port */ /* Bind an lokale adresse */ if (bind(servSock, (struct sockaddr *) &smtpServAddr, sizeof(smtpServAddr)) < 0) { perror("bind() failed"); return -1; } /* socket wird zyklisch abgehort */ if (listen(servSock, MAXPENDING) < 0) { perror("listen() failed"); return -1; } for (;;) /* endloses warten auf Clienten */ { /* locked */ pthread_mutex_lock( &mx_join ); for(i=0; i<MAX_THREADS; i++) //maximal MAX_THREADS clienten können sich verbinden, danach warten die weiteren in einer warteschlange { if(join_thread[i] == 1) { pthread_join(threads[i], &value); threads[i] = 0; join_thread[i] = 0; } } pthread_mutex_unlock( &mx_join ); /* unlocked */ FD_ZERO(&fds); FD_SET(servSock, &fds); tv.tv_sec = 0; //zeit zyklus in sek tv.tv_usec = 50000; //in mikrosekunden //hier kommt select und createthread hin ret = select(servSock + 1, &fds, NULL, NULL, &tv); //wenn etwas auf den Serversocket ankommt if(ret == 0) continue; // bei 0 mach weiter (beendeter Thread) if(ret < 0) perror("select"); // bei negativen Werten ist ein Fehler aufgetreten for(i=0; i<MAX_THREADS; i++) // ist noch Platz für einen neuen Client ? if(threads[i] == 0 ) break; if(i == MAX_THREADS) // wenn nicht kurz warten { usleep(10000); continue; } // erzeugen des ClientSockets zur direkten Verbindung clntSock = accept(servSock, (struct sockaddr *) &smtpClntAddr, &clntLen); if (clntSock < 0) perror("accept() failed"); /* clntSock is connected to a client! */ printf("Handling client %s\n", inet_ntoa(smtpClntAddr.sin_addr)); ret = pthread_create(&threads[i], 0, (void*)handleClient, (void*) &clntSock); // jeder client bekommt einen Thread if( ret != 0) perror("pthread_create"); } close(servSock); /* wird nie erreicht */}